The release root: staged/<check-in>, staged/next and staged/gate.log are the agent's, the log holding all that a stage's gate printed, written as it runs, so a long gate can be followed, and a gate that fails refused with its last line, the first five examples that failed with how many in all, and the log; current, previous and accepted copies are the server's; a release holds the shard.yml targets built from tools/ and nothing else in bin/, with gate.txt and source.txt (the checkout's remote, without its login, from which the page links each tool to the line that declares it at the release's own check-in) |
spec/ocx/staging_spec.cr, spec/ocx/release_commands_spec.cr, spec/guards/provenance_spec.cr, spec/ocx/styles_spec.cr, spec/ocx/command_spec.cr |
The CGI: reads FOSSIL_REPOSITORY, FOSSIL_USER, FOSSIL_CAPABILITIES, FOSSIL_NONCE, FOSSIL_URI, REQUEST_METHOD, QUERY_STRING, PATH_INFO, CONTENT_LENGTH, HTTPS, HTTP_HOST and HTTP_REFERER by value, the build settings by value, and only the names of the rest of the environment; served as ocx2-cgi, the active release candidate, or ocx2-cgi-next, the pre-release candidate, saying which, inside Fossil's skin, or JSON with json; answered as Markdown that Fossil renders in its skin; with no tool named, the list, each tool a card: what it does, a "(src)" link to the file and line that declare it in the release's source at its own check-in, the capabilities it needs marked held or not for the viewer, what a changing tool would do now (its dry run, an operation a line), and its form, which is its input (dry_run among its switches, json the page's own, each described in its tooltip), left out when a POST of it would be refused: the viewer lacks what it needs, or the form has nothing for the viewer to fill in and what the tool would do now stops (a plan made with no input cannot judge a form with fields, such as create's); with a tool named, a button back to the list, the tool's answer and its own card, which beside a dry run leaves out what the tool would do now; the context's facts in a strip, as set by Fossil and the server, and a release root refused from the request; a style block taking its colours from the skin; stage not offered, saying why; a tool that needs a capability taken only by a POST whose Referer is Fossil's base URL or a page under it, as Fossil checks its own forms (the base https when Fossil's HTTPS is on, as fossil server --https sets it behind a proxy that ends TLS, whatever REQUEST_SCHEME says), since Fossil passes no Sec-Fetch-Site |
spec/guards/environment_spec.cr, spec/ocx/styles_spec.cr, spec/ocx/harness_spec.cr |
Git exports: the program served under the name git, or git-next from the release offered as next (ocx2-git and ocx2-git-next too), (<ext>/git/NAME.git/…), serves the git export of the repository Fossil serves the request through, NAME its file's name without its extension, read-only over git's smart HTTP (info/refs?service=git-upload-pack and git-upload-pack, a gzipped request accepted, one whose gzip does not read refused with 400; a request body over 50 MB, raw or expanded, refused with 413, and one announced over it never read), from the directory its last-git-export-repo setting names, which fossil git export records (OCX's hook and export keep it current, slice 40); a push is refused (403), a viewer without o or h gets 403, another repository's export, whatever its file is named, 404, since Fossil gives the viewer's capabilities for the served repository alone; a repository without an export 404, an export git cannot read or a fetch git fails 500, git's answer read whole before any header is written; git is started only by OCX::Git, with PATH alone in its environment |
spec/ocx/git_export_spec.cr |
The git export kept current: export, setup, on the served repository named outright, refused in a checkout and withheld from MCP, runs fossil git export into the directory the repository's last-git-export-repo setting names (refused when it names no git repository), one run at a time: a run first leaves ocx-export-again in the export's .git, then tries the flock on ocx-export-lock there, and the holder exports while that mark is left; the run is kept as ocx-export-last.json there, {"when", "took_ms", "runs", "added", "failure"}, added what Fossil said each export added; a failed export refuses with what Fossil said, and the next run exports what awaits; the output is the run and the state fossil git status gives (mirror, awaiting, last_export, autopush, main_branch) with the last run, and a dry run lists the export with awaiting, autopush and the last failure; where Fossil cannot say, as after an export killed before it kept anything, awaiting is absent, unreadable says why, and the export runs and mends it; nothing sets an autopush |
spec/ocx/export_spec.cr, spec/ocx/dry_run_spec.cr |
Importing a git repository: import UPSTREAM NAME [--mirrors DIR], setup, on the served repository named outright, withheld from MCP; UPSTREAM a git URL or a directory on the served host, bound read-only into the sandbox; in a sandbox within half an hour, a bare clone (git -c safe.directory=* clone --bare --no-local), its branches alone as git's fast-export with marks, the upstream's own branch (its HEAD) named trunk, so each check-in keeps its branch, a checkout opens on the upstream's own, and the tags stay git's, and fossil import --git with Fossil's marks, the caller its admin; the repository placed beside the served one as NAME with the served one's extension, its export made of the clone in DIR/NAME (DIR by default where the served repository's export is), its .mirror_state seeded with each check-in's commit and the newest check-in's day; exported once, recording it, the export's git configuration keeping the upstream's URL; the hook added with this release's command where it is installed on the served repository; its public URL set as its email-url for its administrator, the caller, the served one's with NAME for its last segment, so a run's clone is made from it with no step by hand (the maintainer, 2026-10-09: "be ready to use the new repo without other steps"), a fact of the repository set once when it is made, not OCX's runtime state (precept C2), and left unset where the served one has none; a name not of letters, digits, ., _ and -, a repository or export already there, or no export to go beside, refused; it joins the served repository's login group last, made of the two and named for the served one where there is none, so its admin, the caller, signs in to it as to the served one (the maintainer, 2026-10-08); a run that fails removes what it made, the new repository leaving the login group first, and the directory it works in, .NAME.import beside the served repository, goes when it ends |
spec/ocx/import_spec.cr, spec/ocx/dry_run_spec.cr |
An imported repository brought up to its upstream: what the upstream added since, from the export's marks, onto each branch Fossil has not moved past and onto upstream/BRANCH where it has |
pending: slice 39 |
Onboarding a repository: the hook installed, email-url from the served repository's, the person onboarding signing in by Fossil's login group, the web page in its menu |
pending: slice 37 |
| A run's user: a Fossil user made for runs by a person, in Fossil's user pages, holding neither admin nor setup and not a node's user, or the node refuses the run; its password set fresh and random by the watcher each time it prepares a run, used to clone and kept only in the clone (HISTORY.md decision 29); OCX keeps no password file |
spec/ocx/watcher_spec.cr, spec/ocx/decisions_spec.cr |
A tool run at the command line with no repository named takes the repository of the checkout it is run in, found as Fossil finds it, by its checkout file (.fslckout, or the older _FOSSIL_) from the working directory up, read directly; one named with --repository always wins, and outside a checkout there is none. A tool that acts on the served repository takes a checkout's only where this release's hook is installed in its repository, which marks the served one, since a hook's settings are never cloned; elsewhere it refuses, naming the checkout's repository (the maintainer, 2026-10-07, after the use-fslckout branch's example) |
spec/ocx/command_spec.cr, spec/ocx/hook_spec.cr, spec/ocx/fossil_spec.cr |
Who sent an artifact, for protocol v1 (docs/slices-v1.md, slice v1.4): receipt [--artifacts 'HASH ...'] [--capabilities-of USER], needing no capability, on the served repository named outright, refused in a checkout; each hash in full (SHA1 or SHA3-256, in hex), any other refused; for each, in the order asked, whether the served repository received it (a phantom it has only heard of is not received), the login its receipt names, that login's capabilities as Fossil grants them, inherited ones included, the receipt's number (rcvid) and the artifact's row number (rid), which orders the artifacts of one receipt; for --capabilities-of, that user's capabilities, absent for one it lacks; to a caller without Read (o), and for a private artifact to one without x, every artifact not received and no capabilities; never the receipt's address |
spec/ocx/receipt_spec.cr |
A node on a clone, for protocol v1 (docs/protocol-v1.md, section 10; HISTORY.md decision 43): node SERVER CREDENTIALS [--check], needing setup on the clone, from the command line alone, on the clone named with --repository in a release root, refused for a repository whose hook this root installed (the server's); it reads the file CREDENTIALS, {"user", "password", "runs": {USER: PASSWORD}}, refused unless only its owner may read it, signs in to SERVER (https, or http on this host alone; no user, query or fragment) as user with a POST of u and p to /login, keeping the cookie Fossil sets, refused without one, and refuses a node user the server lacks or that holds admin or setup there; it pulls the clone as the user it remembers, refused when the pull reports Error: or makes no round trip, and acts on what the pull brought (its new receipts, in order) as the server's node acts on what its hook is given, or with --check on every run for the node: who sent each artifact, that user's capabilities, and the server's order are the answers of GET /ext/ocx2-cgi/receipt?json&... there, asked once each, a batch of 64 at a time, within ten seconds and a mebibyte; an artifact the server has not received decides nothing and is given to no session; a run whose user it holds no password for is refused; each report it writes as the node user it pushes at once, and goes on only once the server says it received it from the node user; each run to start is handed to a watcher through the root's link, made as hook makes it, with --server SERVER --credentials CREDENTIALS, which clones SERVER as the run's user with the password the file holds, and keeps the file apart from every path the run binds; on every node, a run is refused whose user works for another node, the node of the first run an admin or setup sent naming that user, in the server's order |
spec/ocx/remote_node_spec.cr, spec/ocx/decisions_spec.cr, spec/ocx/dry_run_spec.cr |
The one program: ocx2 chooses its entry point by the name it is started under (ocx2, ocx2-cgi, ocx2-mcp, ocx2-hook, git), and ocx2 --mcp is the MCP server too, as a program's own configuration starts it, its tools/list giving the tools MCP offers; each tool is defined once by its typed input and answer and reached from every entry point, refused by the caller's capabilities; every answer is {tool, dry_run, log, result, stopped, took_ms}, its log the typed operations performed, the result only when the tool ran to the end, and --json prints it; a dry run is the same answer, changing nothing; with no tool it runs list, each tool with the derived schema of its input, which MCP's tools/list renders; help adds the output, context and answer schemas; status shows the context and names the environment present but unread |
spec/ocx/command_spec.cr, spec/ocx/elements_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/styles_spec.cr |