Login
Check-in [ef83839803]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:One daemon per machine pulls each clone whole on each chat message and hands each open run to a watcher, which keeps the run's state under its lock, posts through Fossil's pages and gives each new post once

Intent: the maintainer's "go" (2026-10-26) on the design page "How a post reaches an agent", check-in 2 of the 4 of its build plan (Facts, Daemon, Remove, Docs). That page has one program on each agent machine, the daemon, do for each clone "over and over" its five steps, Wait, Pull, Find what is new, Decide and Remember, deciding nothing "until the pull is done"; and a watcher for each run that "takes a lock on a small file named for the run", "posts "started", starts the agent in a sandbox, and gives the agent each new post in the run's thread, once", and, when the agent finishes, "posts "ended". The file then says "done", so the run never starts again." The rule this check-in serves is the page's "A restart is safe. A restarted daemon reads everything once more; each run's file still says what already happened, so nothing starts twice", with its "Nothing is missed" and "Nothing runs twice", and the maintainer's rules that a restart may only cause a pull and an evaluation and must never start a run twice, and that OCX never writes as a person. The case the maintainer asked to settle: a run addressed to this node that ended before the daemon, with no run file, does not start again, as its ended, which OCX's read-only receipt page says the node user sent, counts as done; an ended whose card names the node but which another user sent does not.

Expression: src/ocx/daemon.cr, Daemon::Served#round: `fossil remote off`, then `fossil pull ocx-server`, killed past 10 minutes, complete only when Fossil saved the default remote again (Incomplete otherwise, and the daemon exits for its service manager to start again, which pulls first); the clone's highest blob.rcvid written by rename as its completion number, ROOT/node/completed/SERVER-CODE; on the first round every message read up to it, after that what arrived since the last number handled, which starts at 0 in each process, so a restart reads everything once; then, under the node's lock, each run for the node user and clone not settled: its state done, settled; its lock held, skipped; otherwise Decisions.decide with senders from the receipt page, Nothing settled (so a run that ended before the daemon starts no watcher), Wait kept, and anything else handed to a watcher, again at each round until settled, a run handed twice taking one watcher by its lock; Daemon::Served#wait holds /chat-poll at the highest id answered before the last complete pull; Daemon.serve runs one fiber per clone and returns once the release root's current link names another release; one JSON line per round in ROOT/node/daemon.log. src/ocx/commands/daemon.cr, `ocx2 daemon NODES`, withheld from the web page and MCP: one daemon per machine (ROOT/node/daemon.lock, taken without waiting, never in a dry run); poll's joined moved here, an entry whose login is not approved yet passed over and named (Unapproved, src/ocx/commands/register.cr); SignIn refuses a node user holding admin or setup, lacking g, o, C, 4 or w, or whose ticket changes a moderator holds; PrepareClone, logged, names the node user as the clone's default user (Fossil's pull needs it after remote off, docs/fossil.md, "What the daemon relies on") and the remote ocx-server from the default. src/ocx/fossil/pulls.cr: name_remote, pull_whole, highest_receipt, said_between, checkins_between, push_to and sync_unversioned_to, the last two leaving the default remote alone; src/ocx/fossil.cr's private run gains a bound that kills fossil past it and reads no exit status (the one Process call stays one, precept I7, Single Process Site); src/ocx/fossil/said.cr's said and said_in read up to a completion number. src/ocx/server/pages.cr: Server#post, Here writing into its repository as before, Asked posting through /forume2 or /tktedit (csrf, same-origin Referer, mutype=Markdown) and raising Unposted without Fossil's redirect to the stored post; Asked#wake and Asked#held. src/ocx/completion.cr: Completion.write and read, and Completion::Watch, an inotify descriptor per waiter on the completed directory (docs/linux.md). src/ocx/watcher/state.cr: taken, ending and done beside the run's lock, written by rename and fsync only under it, a state that does not read counting as taken. src/ocx/watcher.cr and src/ocx/watcher/reports.cr: on taking the run's lock the words the earlier watcher left are kept, and ended (Node.end_left) where the run is answered lost; done, nothing; ending, the held ended posted, then done; taken, lost; no state, decided as before, Nothing writing done; taken written right before started is posted, the program launched only once Fossil redirected to the stored started; a post with no redirect, on a clone with completion numbers, waits for the next one on a watch of its own and counts as made when the same text, replying to the same post, sent by the node user, is there (Fossil 2.27 stores a forum post without its last newline), and a stop that counts meanwhile makes it post ended stopped and never launch; with no completion numbers (the server's own repository, or a clone poll pulls) a post raises at once, and decisions and posts that cannot wait keep the node's lock as before; the watcher starts in a stated environment holding the account's home (precept C6, Explicit Arguments). src/ocx/watcher/giving.cr: on a clone the watcher gives each new post in its run's thread and each check-in once, in the server's order, at each completion number, never what came before its started, replies for its own session to a person's post it held back naming the filter, ends the program for a stop that counts, and tries a receipt-page failure again at the next number without ending the run. src/ocx/watcher/process.cr holds the launching code moved out of watcher.cr, unchanged but for Time.utc in place of Time.monotonic, as docs/crystal.md has OCX time a run. src/ocx/node.cr: Node.round gives nothing and tags nothing on a clone; the node user's own ended, received from the server, counts as on the server's own repository; src/ocx/node/view.cr reads every message on a clone, up to a completion number, and holds only what the server has received; src/ocx/decisions.cr refuses a run asking a verdict when the node user lacks i; verdicts push with `fossil push --once ocx-server` (src/ocx/server.cr) and shares with `fossil uv sync ocx-server` (src/ocx/node/sharing.cr); Logins::ASKED becomes goC4w (src/ocx/commands/logins.cr), and poll names its remote too (src/ocx/commands/poll.cr), so poll and the hook's node keep working until check-in 3; src/ocx/care.cr, src/ocx/hook.cr, src/ocx/commands.cr and src/ocx/fossil/scratch.cr follow. docs/design/modules.json and modules.md, docs/survey/functionality.json ("Run a node: the daemon"), docs/interfaces.md (the row check-in 1 left, precept C7, Pinned Interfaces) and docs/linux.md (inotify). Every file within 400 lines (precept C1, File Boundedness).

Review: Codex (codex-cli 0.162.1, gpt-6.1-sol at high reasoning, in its workspace-write sandbox, which blocks sockets, so it ran only the specs that serve nothing; the reviewer ran every spec after each pass) wrote every line, in seven passes (codex-build-8.log to -14.log beside the build plan); the reviewer's only tree edit is `fossil add` of the new files. Review 1, 30 findings: of the new specs 4 of 26 failed, and of the old ones 8 failed and remote_node_spec hung, its watcher waiting for a completion number no daemon writes; Fossil stores a post without its last newline, so a stored started was posted twice; Node.round's clone line, the tool list, the dry run's daemon sample, the node user's letters (goC4w, as posting through pages needs) and poll's remote; unnamed limits; handed runs settled for ever, so a run whose watcher waited was missed; lock tries outside the node's lock in the round and in the watcher's decision, which can fail a take; the decision and started on the server's node no longer in one hold, letting a started follow a round's look past a stop; end_left after the lock's words were replaced, ending nothing; giving replying to every post before the run, using another rule for whom it never tells, stopping the run on one receipt failure, reading every check-in ever received, and sharing one inotify descriptor with a post's retry; an equal later reply taken as already posted; the watcher's home; the verdict row's letters; matching an error's text for an unapproved login; comments; the interfaces row; watchers logging into daemon.log; giving's spec copying the repository file instead of pulling. Review 2, 533 examples, 1 failure, 1 error, and watcher_spec and turned_away_spec hung, spec/support/held_watcher.cr overriding the old signatures; 10 findings, among them comments citing the build plan, which is not in the tree, setup writes outside the log (now PrepareClone), and the 2.23 floor claimed for commands pinned on 2.27. Pass 3 ended "Selected model is at capacity" before its report; the reviewer checked its changes. Review 3, 548 examples, 1 failure: a dry run's log, as Registration asks nothing in one. Gemma (gemma4:31b) read the change after pass 4 four times, whole and in three parts, and the changed parts again after passes 5 and 6 (scratchpad gemma-run-daemon-1, -a, -b, -c, -a2, -b2, -b3). Held: a bounded fossil whose wait raises would leave the wait after its KILL unbounded; the fiber now sends what it raised (pass 5). Misread, a clarity finding, the code changed and asked again: a run that waits taken as keeping its lock; `unchecked` taken as letting a pull that exited 0 count (the flag is gone: a bounded run never reads its exit status); the lock's words kept past the block taken as a race; the clone's own-ended check's removal taken as letting another user's ended count, twice (comments now say each, passes 5 and 6). Wrong: SignIn comparing against ASKED's text (it tests each letter with allows?); poll's home nil (Context#home is a String); an empty ask asking the server (it asks nothing); taken to be undone after a failed started (a watcher dying between taken and ending ends its run lost, by the design page's rule, so an unconfirmed started never launches twice); the OOM killer stopping one fiber; a named remote's name taken for a URL; said's bound bypassed by a join (both are inner joins on blob); the old take_run line read from the diff as current; Run#process nil (it is not nilable, and row 2 refuses a run that does not read); recovered not ending what the lock names (it does); write_state failing silently (it raises Refused); the watcher's bound a stale number (it is the newest complete pull). The third read's delivery point cites `counted` as not shown, as its body is outside the diff; the comment beside the check says what it checks. The gate then broke the coverage ratchet: 27 statements of the new code never ran (7075 of 7102); pass 7 added specs that run them (a serve of two clones, one failing, raising that failure once the other's wake is closed; a check-in given to a session; a held ending Fossil had already stored, not posted again; a ticket run's reports through /tktedit; a receipt failure while a post is retried; a giving closed while its reply waits; a completion number and a state that cannot be written) and removed what cannot run (the daemon's refusal fallback for the withheld entry points, a check for an end of file inotify never gives, two repeated checks), and coverage reached 7093 of 7093. Gemma read the changes made after review 2 once more: that a daemon whose pull keeps failing restarts in a tight loop is wrong: its service manager starts it again 10 s later, and a pull raising on its exit status would end the daemon the same way. Not in this check-in: removing the hook's node and poll (check-in 3), and protocol v1's new refusal row 11 and the documents that describe them (check-in 4).

Demonstrates: spec/ocx/daemon_spec.cr, spec/ocx/daemon_pages_spec.cr and spec/ocx/daemon_setup_spec.cr (an incomplete pull writes no completion number and hands nothing; the completion number after each complete pull and each run for its node user and clone handed, again while its lock is free, skipped while held; a done run handed by no restart; a run whose ended lab-1 posted settled with no watcher while mallory's forged ended is handed; a wake within 5 s of a post; a pull after an empty answer brings an approved run; a return once the root serves another release; refusals of admin, setup, a missing C, 4 or w and moderated tickets; the clone made, prepared and named once approved, an unapproved entry named, a dry run changing nothing; a second daemon refused at once; a verdict refused without i; a serve of two clones raising the first failure; a completion number that cannot be written refused), spec/ocx/run_states_spec.cr and spec/ocx/run_pages_spec.cr (states read, unreadable as taken; taken ended lost and ending posted unchanged, then done; done for an ended the node user sent, none for a forged one; what an old lock names ended for taken and for a lost decision, not for done; a held ending already stored finished without a second post; a state that cannot be written refused, the previous stage kept; taken before started, a launch only after the stored started, a retry at the next completion number posting none twice; a stopped ended and no launch for a stop before the redirect, or for one the first giving reads after a stored started; /forume2 and /tktedit with mutype; a ticket run's reports through /tktedit; an unconfirmed ticket change refused), spec/ocx/giving_spec.cr (each new post once in the server's order through real pulls, nothing before started, a reply naming the filter, none to another run's user, a receipt failure retried, the node user's own ended given, equal replies kept apart, a check-in on a branch the session hears given once, a stop from mallory ignored and stone's obeyed, a giving joined while its held-back reply waits), spec/ocx/pulls_spec.cr, spec/ocx/completion_spec.cr and spec/ocx/server_pages_spec.cr, with spec/support/daemon_node.cr, spec/support/pages_proxy.cr and spec/support/fossil_pull.cr; spec/ocx/remote_node_spec.cr, spec/ocx/logins_spec.cr, spec/ocx/ends_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/command_spec.cr, spec/ocx/turned_away_spec.cr and spec/support/held_watcher.cr follow the change; the sources src/ocx/daemon.cr, src/ocx/commands/daemon.cr, src/ocx/completion.cr, src/ocx/fossil/pulls.cr, src/ocx/server/pages.cr, src/ocx/watcher/state.cr, src/ocx/watcher/reports.cr, src/ocx/watcher/giving.cr, src/ocx/watcher/process.cr, src/ocx/watcher.cr, src/ocx/node.cr, src/ocx/node/view.cr, src/ocx/node/sharing.cr, src/ocx/decisions.cr, src/ocx/care.cr, src/ocx/server.cr, src/ocx/fossil.cr, src/ocx/fossil/said.cr, src/ocx/fossil/scratch.cr, src/ocx/commands/poll.cr, src/ocx/commands/logins.cr, src/ocx/commands/register.cr, src/ocx/commands.cr and src/ocx/hook.cr; spec/guards/size_spec.cr, spec/guards/measure_spec.cr, spec/guards/causation_spec.cr, spec/guards/external_programs_spec.cr, spec/guards/survey_spec.cr and spec/guards/interfaces_spec.cr hold them. crystal run ci.cr green: 566 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs

Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | agent-tools
Files: files | file ages | folders
SHA3-256: ef83839803f53430c038be9ab56629d1aca26d78f04c01e970fad963b4bfe81c
User & Date: opus-ocx2 2026-10-26 08:34:38.742
Context
2026-10-26
12:31
Nothing schedules run-work on the server or from a hook: the server's node, the hook's rounds and decisions, the backoffice start after an ended and poll go, and the after-receive hook is Fossil's own git export

Intent: the maintainer's "go" (2026-10-26) on the design page "How a post reaches an agent", check-in 3 of the 4 of its build plan (Facts, Daemon, Remove, Docs). That page has the public server keep the record and nothing more: "When new posts or check-ins arrive, Fossil stores them. OCX runs nothing then, and OCX never writes on the server"; "One Fossil hook remains: Fossil's own fossil git export, which keeps the git mirror current. It is not OCX code"; and among what OCX never does, "Run anything on the public server when something arrives". The rules this check-in serves are the maintainer's: no hook schedules run-work; the git mirror is Fossil's own after-receive hook running Fossil's own fossil git export; OCX's read-only CGI pages stay on the server; a restart may only cause a pull and an evaluation and must never start a run twice; nothing missed, nothing given twice; and "I'm happy to throw away code that we can do without". The case the maintainer asked to settle stays true with the server's node gone: a run addressed to this node that ended long ago, before the daemon, with no run file, does not start again, as its ended, which OCX's read-only receipt page says the node user sent, counts as done; an ended whose card names the node but which another user sent does not.

Expression: the hook's node goes. src/ocx/hook.cr keeps only Hook::COMMAND, "%F git export --if-mirrored -R %R", the command Fossil 2.27's `fossil all git export` runs (allrepo.c 265; %F and %R, hook.c 95-96; --if-mirrored, export.c 1860; the export's write transaction, export.c 1460, with the backoffice running one hook at a time, so no lock of OCX's), OURS (that command and any old ocx2-hook command), State, Backoffice, Registered and Standing (state, command, others, backoffice, chat_user from chat-timeline-user); Hook.run, its export and answer, check, act, the watcher's hand-on, Run, Acted, the release root's ocx2-hook link and its log go, and with them EntryPoint::Hook (src/ocx/entry.cr, src/ocx/command.cr), whose identification of the served repository now reads its hooks for Hook::COMMAND alone. src/ocx/commands/hook.cr: `hook` takes no root and no login, installs Hook::COMMAND once, replacing any ocx2-hook command and leaving other hooks alone; `unhook` removes both; CheckServing, CheckNodeLogin, CheckRuns and PlaceHook go. src/ocx/node.cr loses round, check, mend, decide, give, listed?, own? and Round, keeping the decisions and deliveries the daemon and each watcher read, with ROOT/node, made 0700, as Node::DIRECTORY; src/ocx/node/view.cr always reads every message, up to a completion number, its read by tags (hold_tagged, listed, tag_untagged) gone, Node.untagged moved into src/ocx/commands/tag_messages.cr, and View is generic over its server's and host's types; src/ocx/decisions.cr counts a report only when the receipt page says the node user sent it (Held#own goes) and passwords is an Array(String); src/ocx/care.cr loses polling and stop_program; src/ocx/server.cr's Here keeps only its reads, for status and audit, while post, password and published are Server::Writer's, which Asked is and the watcher, Launch.steps and Node.verdict hold by type (src/ocx/server/pages.cr, src/ocx/launch.cr, src/ocx/node/sharing.cr), send_left and Asked.from going; src/ocx/fossil/message_tags.cr loses said_tagged_open, said_tagged_about and OpenRuns, src/ocx/fossil/repository.cr start_backoffice, src/ocx/fossil/forum.cr index_stored_posts, src/ocx/fossil/said.cr last_receipt and received_after, src/ocx/fossil.cr the re-entrant turn only the mend took, src/ocx/writes.cr act_at_once and ActOnRun, and src/ocx/commands/create.cr and src/ocx/commands/stop.cr their acted output, the write's chat message waking the daemon; a run must name its node. poll goes (src/ocx/commands/poll.cr deleted, its SignIn, CloneServer and PrepareClone already in src/ocx/commands/daemon.cr, which now places no hook link). src/ocx/watcher.cr: `following` goes, a run that waited being handed by a later daemon round, which the ended's chat message or the chat poll's timeout wakes; Watcher.hand starts ROOT/current/ocx2, resolved at each start, as `watch RUN SERVER CREDENTIALS --repository CLONE [--by-hand]`; every watcher works at completion numbers, Completion.latest (src/ocx/completion.cr) refusing a clone with no server-code or no complete pull yet, so the no-completion mode that posted at once under the node's lock goes from src/ocx/watcher.cr, src/ocx/watcher/reports.cr, src/ocx/watcher/process.cr and src/ocx/watcher/giving.cr, and taken is written before started is posted, as a comment there now says why. src/ocx/commands/watch.cr, new, `watch`, the command line alone, needing setup on the clone, whose default user is the node user and its admin. src/ocx/commands/run.cr moves `run` to the node's machine: `run RUN NODES --repository CLONE` reads the clone's entry in the nodes file, finds and decides the run by hand with the server's receipts up to the clone's completion number (FindRun and ReadCompletion, src/ocx/served.cr) and hands it to `watch --by-hand`. src/ocx/commands/status.cr and src/ocx/commands/console.cr, with src/ocx/console_page.cr and src/ocx/console_text.cr: on the served repository, the record's runs for every node user a run an admin or setup sent names, with no care; given the nodes file and --repository CLONE, the clone's runs up to its completion number with their care and processes, signed in as a logged check, and status's offer (node user, server-code, the limits it enforces); status shows the hook's standing and the daemon's last round from ROOT/node/daemon.log, read by Logged.last (src/ocx/log.cr, was Hook.last, also read by src/ocx/imports.cr). src/ocx/imports_detached.cr, src/ocx/commands/import.cr, src/ocx/commands/import_operations.cr (Hook::COMMAND), src/ocx/commands/attach.cr, src/ocx/commands/audit.cr, src/ocx/daemon.cr, src/ocx/fossil/pulls.cr, src/ocx/mirror.cr, src/ocx/session.cr, src/ocx/workspaces.cr (Workspaces.agents, which only the hook called, goes) and src/ocx/commands.cr follow. Specs: spec/ocx/remote_node_spec.cr is deleted, its behaviors in the daemon's specs, its verdict and share example in spec/ocx/daemon_spec.cr and its refusals in spec/ocx/daemon_refusals_spec.cr; spec/support/local_server.cr, new, a Server::Writer whose post writes straight into a fixture's repository as the node user, so in-process watchers stay fast, each spec giving by writing a completion number the watcher's own Giving reads; Node.round and Node.check in the other specs become a watcher's answers or its giving at a written completion number. Data: docs/design/modules.json and modules.md (commands/poll.cr goes, commands/watch.cr comes, the purposes rewritten), docs/survey/functionality.json ("Log a hook run" and "Be a node on another host" go; the export takes Fossil's own hook), docs/design/facts.json ("chat for timeline events", chat-timeline-user, read by src/ocx/hook.cr), docs/interfaces.md, SCENARIOS.md and docs/backlog.md (the plan's three open points). Every file within 400 lines (precept C1, File Boundedness); nothing added to docs/precepts/exceptions.json; spec/coverage/ratchet.json keeps "missed": [].

Review: Codex (codex-cli 0.162.1, gpt-6.1-sol at high reasoning, in its workspace-write sandbox, which blocks sockets, so it ran only the specs that serve nothing; the reviewer ran every spec, network ones included, and the coverage ratchet after each pass) wrote every line, in seven runs (codex-build-15.log to -21.log beside the build plan); the reviewer's only tree edits are fossil add and fossil rm of the files it made and deleted. Pass 1 stopped with "Selected model is at capacity" after its first large edit, and was resumed in the same session. Review 1, 548 examples, 3 failures (one, spec/guards/outlived_spec.cr's "refutes a fossil program left naming a fixture that is gone", fails by itself on the parent check-in too, twice in two runs, and passed in later runs here), coverage 6704 of 6732: 13 findings: the CLI parser let a required argument take a global flag's place, so `send_runs alice --user stone` refused alice (restored, `watch` and `run` taking bare words, the regression pinned); the daemon setup example and the talk scenario expecting what is gone; 28 statements no spec ran, most of them the refusals remote_node_spec had shown (moved to spec/ocx/daemon_refusals_spec.cr, Workspaces.agents deleted); the no-completion mode, which only the server's node and poll used (removed, and the spec harness's second giving loop and reopened Watcher.watch with it); `run` deciding past the last complete pull (bounded, with spec/ocx/daemon_reads_spec.cr); `.as(Server::Writer)` casts and a Here#post that only refused (post moved to Writer, View generic); watch and run needing nothing while overriding changes? and refusal (now setup, overrides gone); module comments and modules.json purposes cut to a line, stale wording about this release's hook, the spec names that still named the hook; a second copy of a decision's note in console; unrelated fixture changes (reverted); status signing in outside the log; Logged.last's example in the hook's spec. Review 2, 554 examples, 1 failure, coverage 6714 of 6721: `run`'s dry run refused for want of a completion number, which hid attach's; a clone read's ended run; Watcher.hand's check for a missing program, which the daemon and run, themselves that program, never meet (deleted); an unused assignment in watch. Review 3: 554 examples, 0 failures, the ratchet holding. Gemma (gemma4:31b) read the change's sources in four parts and its main specs in a fifth, then the changed parts three more times, after passes 4, 5 and 6 (scratchpad gemma-remove-server-runs-a to -h). Held: create's help sent a person to status for a clone's server-code, which status no longer showed, and Node::ENFORCES was read nowhere; status on the node's machine shows the node's offer again (pass 4). Misread, a clarity finding, the code changed and asked again: Asked#arguments taken as missing watch's flags (its comment says they are watch's SERVER and CREDENTIALS words); View's "only a view holding a Writer can post" taken as unenforced (it says the compiler refuses it); Giving's server-code taken as possibly nil (it says Watcher.watch refuses first, and Completion.latest names a clone with no server-code apart from one with no complete pull) (pass 4); taken read as written after started is posted, so a killed watcher could launch twice (the order was right; a comment now says why it matters) (pass 5); run's and watch's examples read as naming a root (they name the nodes and credentials files) (pass 6). Wrong: a Giving made with no completion number, a post's wait on a clone never pulled, `run` reading past its bound, a release root whose current link is not updated (each refused or bounded before it); Here naming no sender for a local write (it reads the local receipt's user); started_here? turning another clone's started into lost (it answers nothing, as that clone may hold the process); status with a nodes file and no root failing with a type error (it refuses, saying so); console falling back when its sign-in fails (an explicit nodes file is refused); `hook` replacing any export hook (only its own command and old ocx2-hook commands); the imports directory made without its parent's mode (Dir.mkdir_p applies it to each); audit losing a report Held#own counted (own never read the receipt page; the local receipt's user is the sender); the share example reading a local checkout (AtHost.place is the served repository); Giving made from a view of Here (Watcher.watch takes a Writer, Giving a view with a host); a post's wait unbounded with the daemon down (the retry is the next complete pull, by design S3, which the service manager's restart brings); run's NODES not taken from the command line (it is its second bare word); run's of_clone? call read as passing its block wrongly (the fix it gives is the line as written); DecideRun's completion not a field (it is a getter). Not in this check-in: the documents that describe what was removed (USAGE, protocol v1, GLOSSARY, HISTORY decision 125; check-in 4), and protocol v1's refusal row 11.

Demonstrates: spec/ocx/daemon_spec.cr (a run that waited for its user's other run is handed by a later round once that run has ended, with no state written by the watcher that waited; a build's verdict pushed once to the named remote and checked on the receipt page, a share only with y, the default remote left as it was; the round's watch command line; a run ended before the daemon by lab-1 settled and one with mallory's forged ended handed, unchanged), spec/ocx/daemon_reads_spec.cr (run and watch refused before a complete pull; mid-pull reports hidden from run, status and console; a run present mid-pull not found), spec/ocx/daemon_refusals_spec.cr (insecure or decorated URLs, unreadable or shared credentials, an unreachable server, receipt answers malformed, oversized, failed or refused, a bad login, a missing run password, a verdict unsent or received from another user), spec/ocx/daemon_pages_spec.cr, spec/ocx/watch_spec.cr (the current release's watch command with its words, following an accept; the node directory 0700; watch needing a root and a clone; a run answered through Fossil's pages; withheld from CGI and MCP), spec/ocx/hook_spec.cr (Fossil's own git export installed once, replacing ocx2-hook, other hooks left; run by Fossil's backoffice after a sync, bringing the export up to date; backoffice standing and the chat user; refusals, a dry run and the entry points), spec/ocx/run_spec.cr (a manual run decided with receipts and handed to watch --by-hand; served status for every node user an admin's run names, never reading server locks as care; a clone's care, processes and offer with its nodes file; the daemon's last round; stop writing as its caller), spec/ocx/run_states_spec.cr, spec/ocx/run_pages_spec.cr, spec/ocx/giving_spec.cr, spec/ocx/completion_spec.cr, spec/ocx/command_spec.cr (send_runs alice --user stone keeping stone as the acting user), spec/ocx/node_spec.cr, spec/ocx/watcher_spec.cr, spec/ocx/turned_away_spec.cr, spec/ocx/decisions_spec.cr, spec/ocx/console_spec.cr, spec/ocx/create_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/asked_spec.cr, spec/ocx/attach_spec.cr, spec/ocx/care_spec.cr, spec/ocx/elements_spec.cr, spec/ocx/ending_spec.cr, spec/ocx/ends_spec.cr, spec/ocx/export_spec.cr, spec/ocx/harness_spec.cr, spec/ocx/import_spec.cr, spec/ocx/imports_detached_spec.cr, spec/ocx/logins_spec.cr, spec/ocx/mcp_spec.cr, spec/ocx/message_tags_spec.cr, spec/ocx/program_spec.cr, spec/ocx/release_bounds_spec.cr, spec/ocx/steps_spec.cr, spec/ocx/terminal_spec.cr, spec/ocx/writers_spec.cr, spec/scenarios/audit_spec.cr, spec/scenarios/build_spec.cr, spec/scenarios/ci_spec.cr, spec/scenarios/question_spec.cr and spec/scenarios/talk_spec.cr, with spec/support/local_server.cr, spec/support/at_host.cr, spec/support/daemon_node.cr, spec/support/fixture.cr and spec/support/stand_in_fossil.cr; spec/guards/external_programs_spec.cr (src/ocx/session.cr starting a run's watcher or session) and spec/guards/outlived_spec.cr (the fixture server's backoffice alone) follow; the sources src/ocx/hook.cr, src/ocx/commands/hook.cr, src/ocx/commands/watch.cr, src/ocx/commands/run.cr, src/ocx/commands/status.cr, src/ocx/commands/console.cr, src/ocx/console_page.cr, src/ocx/console_text.cr, src/ocx/watcher.cr, src/ocx/watcher/reports.cr, src/ocx/watcher/giving.cr, src/ocx/watcher/process.cr, src/ocx/completion.cr, src/ocx/daemon.cr, src/ocx/commands/daemon.cr, src/ocx/node.cr, src/ocx/node/view.cr, src/ocx/node/sharing.cr, src/ocx/decisions.cr, src/ocx/care.cr, src/ocx/server.cr, src/ocx/server/pages.cr, src/ocx/launch.cr, src/ocx/served.cr, src/ocx/entry.cr, src/ocx/command.cr, src/ocx/commands.cr, src/ocx/commands/create.cr, src/ocx/commands/stop.cr, src/ocx/writes.cr, src/ocx/fossil.cr, src/ocx/fossil/forum.cr, src/ocx/fossil/message_tags.cr, src/ocx/fossil/pulls.cr, src/ocx/fossil/repository.cr, src/ocx/fossil/said.cr, src/ocx/log.cr, src/ocx/imports.cr, src/ocx/imports_detached.cr, src/ocx/commands/import.cr, src/ocx/commands/import_operations.cr, src/ocx/commands/tag_messages.cr, src/ocx/commands/attach.cr, src/ocx/commands/audit.cr, src/ocx/mirror.cr, src/ocx/session.cr and src/ocx/workspaces.cr; spec/guards/size_spec.cr, spec/guards/causation_spec.cr, spec/guards/survey_spec.cr, spec/guards/interfaces_spec.cr, spec/guards/derivation_spec.cr and spec/guards/sourcing_spec.cr hold them. crystal run ci.cr green: 555 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: 8cac96ce10 user: opus-ocx2 tags: agent-tools

08:34
One daemon per machine pulls each clone whole on each chat message and hands each open run to a watcher, which keeps the run's state under its lock, posts through Fossil's pages and gives each new post once

Intent: the maintainer's "go" (2026-10-26) on the design page "How a post reaches an agent", check-in 2 of the 4 of its build plan (Facts, Daemon, Remove, Docs). That page has one program on each agent machine, the daemon, do for each clone "over and over" its five steps, Wait, Pull, Find what is new, Decide and Remember, deciding nothing "until the pull is done"; and a watcher for each run that "takes a lock on a small file named for the run", "posts "started", starts the agent in a sandbox, and gives the agent each new post in the run's thread, once", and, when the agent finishes, "posts "ended". The file then says "done", so the run never starts again." The rule this check-in serves is the page's "A restart is safe. A restarted daemon reads everything once more; each run's file still says what already happened, so nothing starts twice", with its "Nothing is missed" and "Nothing runs twice", and the maintainer's rules that a restart may only cause a pull and an evaluation and must never start a run twice, and that OCX never writes as a person. The case the maintainer asked to settle: a run addressed to this node that ended before the daemon, with no run file, does not start again, as its ended, which OCX's read-only receipt page says the node user sent, counts as done; an ended whose card names the node but which another user sent does not.

Expression: src/ocx/daemon.cr, Daemon::Served#round: `fossil remote off`, then `fossil pull ocx-server`, killed past 10 minutes, complete only when Fossil saved the default remote again (Incomplete otherwise, and the daemon exits for its service manager to start again, which pulls first); the clone's highest blob.rcvid written by rename as its completion number, ROOT/node/completed/SERVER-CODE; on the first round every message read up to it, after that what arrived since the last number handled, which starts at 0 in each process, so a restart reads everything once; then, under the node's lock, each run for the node user and clone not settled: its state done, settled; its lock held, skipped; otherwise Decisions.decide with senders from the receipt page, Nothing settled (so a run that ended before the daemon starts no watcher), Wait kept, and anything else handed to a watcher, again at each round until settled, a run handed twice taking one watcher by its lock; Daemon::Served#wait holds /chat-poll at the highest id answered before the last complete pull; Daemon.serve runs one fiber per clone and returns once the release root's current link names another release; one JSON line per round in ROOT/node/daemon.log. src/ocx/commands/daemon.cr, `ocx2 daemon NODES`, withheld from the web page and MCP: one daemon per machine (ROOT/node/daemon.lock, taken without waiting, never in a dry run); poll's joined moved here, an entry whose login is not approved yet passed over and named (Unapproved, src/ocx/commands/register.cr); SignIn refuses a node user holding admin or setup, lacking g, o, C, 4 or w, or whose ticket changes a moderator holds; PrepareClone, logged, names the node user as the clone's default user (Fossil's pull needs it after remote off, docs/fossil.md, "What the daemon relies on") and the remote ocx-server from the default. src/ocx/fossil/pulls.cr: name_remote, pull_whole, highest_receipt, said_between, checkins_between, push_to and sync_unversioned_to, the last two leaving the default remote alone; src/ocx/fossil.cr's private run gains a bound that kills fossil past it and reads no exit status (the one Process call stays one, precept I7, Single Process Site); src/ocx/fossil/said.cr's said and said_in read up to a completion number. src/ocx/server/pages.cr: Server#post, Here writing into its repository as before, Asked posting through /forume2 or /tktedit (csrf, same-origin Referer, mutype=Markdown) and raising Unposted without Fossil's redirect to the stored post; Asked#wake and Asked#held. src/ocx/completion.cr: Completion.write and read, and Completion::Watch, an inotify descriptor per waiter on the completed directory (docs/linux.md). src/ocx/watcher/state.cr: taken, ending and done beside the run's lock, written by rename and fsync only under it, a state that does not read counting as taken. src/ocx/watcher.cr and src/ocx/watcher/reports.cr: on taking the run's lock the words the earlier watcher left are kept, and ended (Node.end_left) where the run is answered lost; done, nothing; ending, the held ended posted, then done; taken, lost; no state, decided as before, Nothing writing done; taken written right before started is posted, the program launched only once Fossil redirected to the stored started; a post with no redirect, on a clone with completion numbers, waits for the next one on a watch of its own and counts as made when the same text, replying to the same post, sent by the node user, is there (Fossil 2.27 stores a forum post without its last newline), and a stop that counts meanwhile makes it post ended stopped and never launch; with no completion numbers (the server's own repository, or a clone poll pulls) a post raises at once, and decisions and posts that cannot wait keep the node's lock as before; the watcher starts in a stated environment holding the account's home (precept C6, Explicit Arguments). src/ocx/watcher/giving.cr: on a clone the watcher gives each new post in its run's thread and each check-in once, in the server's order, at each completion number, never what came before its started, replies for its own session to a person's post it held back naming the filter, ends the program for a stop that counts, and tries a receipt-page failure again at the next number without ending the run. src/ocx/watcher/process.cr holds the launching code moved out of watcher.cr, unchanged but for Time.utc in place of Time.monotonic, as docs/crystal.md has OCX time a run. src/ocx/node.cr: Node.round gives nothing and tags nothing on a clone; the node user's own ended, received from the server, counts as on the server's own repository; src/ocx/node/view.cr reads every message on a clone, up to a completion number, and holds only what the server has received; src/ocx/decisions.cr refuses a run asking a verdict when the node user lacks i; verdicts push with `fossil push --once ocx-server` (src/ocx/server.cr) and shares with `fossil uv sync ocx-server` (src/ocx/node/sharing.cr); Logins::ASKED becomes goC4w (src/ocx/commands/logins.cr), and poll names its remote too (src/ocx/commands/poll.cr), so poll and the hook's node keep working until check-in 3; src/ocx/care.cr, src/ocx/hook.cr, src/ocx/commands.cr and src/ocx/fossil/scratch.cr follow. docs/design/modules.json and modules.md, docs/survey/functionality.json ("Run a node: the daemon"), docs/interfaces.md (the row check-in 1 left, precept C7, Pinned Interfaces) and docs/linux.md (inotify). Every file within 400 lines (precept C1, File Boundedness).

Review: Codex (codex-cli 0.162.1, gpt-6.1-sol at high reasoning, in its workspace-write sandbox, which blocks sockets, so it ran only the specs that serve nothing; the reviewer ran every spec after each pass) wrote every line, in seven passes (codex-build-8.log to -14.log beside the build plan); the reviewer's only tree edit is `fossil add` of the new files. Review 1, 30 findings: of the new specs 4 of 26 failed, and of the old ones 8 failed and remote_node_spec hung, its watcher waiting for a completion number no daemon writes; Fossil stores a post without its last newline, so a stored started was posted twice; Node.round's clone line, the tool list, the dry run's daemon sample, the node user's letters (goC4w, as posting through pages needs) and poll's remote; unnamed limits; handed runs settled for ever, so a run whose watcher waited was missed; lock tries outside the node's lock in the round and in the watcher's decision, which can fail a take; the decision and started on the server's node no longer in one hold, letting a started follow a round's look past a stop; end_left after the lock's words were replaced, ending nothing; giving replying to every post before the run, using another rule for whom it never tells, stopping the run on one receipt failure, reading every check-in ever received, and sharing one inotify descriptor with a post's retry; an equal later reply taken as already posted; the watcher's home; the verdict row's letters; matching an error's text for an unapproved login; comments; the interfaces row; watchers logging into daemon.log; giving's spec copying the repository file instead of pulling. Review 2, 533 examples, 1 failure, 1 error, and watcher_spec and turned_away_spec hung, spec/support/held_watcher.cr overriding the old signatures; 10 findings, among them comments citing the build plan, which is not in the tree, setup writes outside the log (now PrepareClone), and the 2.23 floor claimed for commands pinned on 2.27. Pass 3 ended "Selected model is at capacity" before its report; the reviewer checked its changes. Review 3, 548 examples, 1 failure: a dry run's log, as Registration asks nothing in one. Gemma (gemma4:31b) read the change after pass 4 four times, whole and in three parts, and the changed parts again after passes 5 and 6 (scratchpad gemma-run-daemon-1, -a, -b, -c, -a2, -b2, -b3). Held: a bounded fossil whose wait raises would leave the wait after its KILL unbounded; the fiber now sends what it raised (pass 5). Misread, a clarity finding, the code changed and asked again: a run that waits taken as keeping its lock; `unchecked` taken as letting a pull that exited 0 count (the flag is gone: a bounded run never reads its exit status); the lock's words kept past the block taken as a race; the clone's own-ended check's removal taken as letting another user's ended count, twice (comments now say each, passes 5 and 6). Wrong: SignIn comparing against ASKED's text (it tests each letter with allows?); poll's home nil (Context#home is a String); an empty ask asking the server (it asks nothing); taken to be undone after a failed started (a watcher dying between taken and ending ends its run lost, by the design page's rule, so an unconfirmed started never launches twice); the OOM killer stopping one fiber; a named remote's name taken for a URL; said's bound bypassed by a join (both are inner joins on blob); the old take_run line read from the diff as current; Run#process nil (it is not nilable, and row 2 refuses a run that does not read); recovered not ending what the lock names (it does); write_state failing silently (it raises Refused); the watcher's bound a stale number (it is the newest complete pull). The third read's delivery point cites `counted` as not shown, as its body is outside the diff; the comment beside the check says what it checks. The gate then broke the coverage ratchet: 27 statements of the new code never ran (7075 of 7102); pass 7 added specs that run them (a serve of two clones, one failing, raising that failure once the other's wake is closed; a check-in given to a session; a held ending Fossil had already stored, not posted again; a ticket run's reports through /tktedit; a receipt failure while a post is retried; a giving closed while its reply waits; a completion number and a state that cannot be written) and removed what cannot run (the daemon's refusal fallback for the withheld entry points, a check for an end of file inotify never gives, two repeated checks), and coverage reached 7093 of 7093. Gemma read the changes made after review 2 once more: that a daemon whose pull keeps failing restarts in a tight loop is wrong: its service manager starts it again 10 s later, and a pull raising on its exit status would end the daemon the same way. Not in this check-in: removing the hook's node and poll (check-in 3), and protocol v1's new refusal row 11 and the documents that describe them (check-in 4).

Demonstrates: spec/ocx/daemon_spec.cr, spec/ocx/daemon_pages_spec.cr and spec/ocx/daemon_setup_spec.cr (an incomplete pull writes no completion number and hands nothing; the completion number after each complete pull and each run for its node user and clone handed, again while its lock is free, skipped while held; a done run handed by no restart; a run whose ended lab-1 posted settled with no watcher while mallory's forged ended is handed; a wake within 5 s of a post; a pull after an empty answer brings an approved run; a return once the root serves another release; refusals of admin, setup, a missing C, 4 or w and moderated tickets; the clone made, prepared and named once approved, an unapproved entry named, a dry run changing nothing; a second daemon refused at once; a verdict refused without i; a serve of two clones raising the first failure; a completion number that cannot be written refused), spec/ocx/run_states_spec.cr and spec/ocx/run_pages_spec.cr (states read, unreadable as taken; taken ended lost and ending posted unchanged, then done; done for an ended the node user sent, none for a forged one; what an old lock names ended for taken and for a lost decision, not for done; a held ending already stored finished without a second post; a state that cannot be written refused, the previous stage kept; taken before started, a launch only after the stored started, a retry at the next completion number posting none twice; a stopped ended and no launch for a stop before the redirect, or for one the first giving reads after a stored started; /forume2 and /tktedit with mutype; a ticket run's reports through /tktedit; an unconfirmed ticket change refused), spec/ocx/giving_spec.cr (each new post once in the server's order through real pulls, nothing before started, a reply naming the filter, none to another run's user, a receipt failure retried, the node user's own ended given, equal replies kept apart, a check-in on a branch the session hears given once, a stop from mallory ignored and stone's obeyed, a giving joined while its held-back reply waits), spec/ocx/pulls_spec.cr, spec/ocx/completion_spec.cr and spec/ocx/server_pages_spec.cr, with spec/support/daemon_node.cr, spec/support/pages_proxy.cr and spec/support/fossil_pull.cr; spec/ocx/remote_node_spec.cr, spec/ocx/logins_spec.cr, spec/ocx/ends_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/command_spec.cr, spec/ocx/turned_away_spec.cr and spec/support/held_watcher.cr follow the change; the sources src/ocx/daemon.cr, src/ocx/commands/daemon.cr, src/ocx/completion.cr, src/ocx/fossil/pulls.cr, src/ocx/server/pages.cr, src/ocx/watcher/state.cr, src/ocx/watcher/reports.cr, src/ocx/watcher/giving.cr, src/ocx/watcher/process.cr, src/ocx/watcher.cr, src/ocx/node.cr, src/ocx/node/view.cr, src/ocx/node/sharing.cr, src/ocx/decisions.cr, src/ocx/care.cr, src/ocx/server.cr, src/ocx/fossil.cr, src/ocx/fossil/said.cr, src/ocx/fossil/scratch.cr, src/ocx/commands/poll.cr, src/ocx/commands/logins.cr, src/ocx/commands/register.cr, src/ocx/commands.cr and src/ocx/hook.cr; spec/guards/size_spec.cr, spec/guards/measure_spec.cr, spec/guards/causation_spec.cr, spec/guards/external_programs_spec.cr, spec/guards/survey_spec.cr and spec/guards/interfaces_spec.cr hold them. crystal run ci.cr green: 566 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: ef83839803 user: opus-ocx2 tags: agent-tools

04:47
The behaviours of Fossil 2.27 the daemon relies on are pinned by specs that fail if an upgrade changes them

Intent: the maintainer's "go" (2026-10-26) on the design page "How a post reaches an agent", check-in 1 of the 4 of its build plan (Facts, Daemon, Remove, Docs). That page's "Why nothing is missed or done twice" rests on what Fossil does: "Nothing is missed: every pull's artifacts carry its receipt number, and the daemon handles every number above the last it handled", and the daemon "decides nothing until the pull is done", told by Fossil saving "the server's address as the clone's remote only after a pull with no errors". So, as the merged design page "Run-work design" lists for its first next step ("Make each 'tried' fact a test failing if Fossil changes it"), each behaviour of Fossil 2.27 the daemon and the watcher will rely on gets a spec that fails if a Fossil upgrade changes it, before any code relies on it. No src/ change.

Expression: spec/facts/fossil_spec.cr, a new directory, built by ci.cr with every spec (Parts.files(["spec"])) and outside the survey's spec/ocx and spec/runners: describe "Fossil 2.27, as the daemon relies on it", six examples on a fixture served by fossil server (ServedNode.serve for OCX's receipt page in example 5), users lab-1, the node user, holding exactly goC4w, stone holding s and mallory holding i3, chat-timeline-user ocx-timeline and chat-poll-timeout 3; fossil runs with a cleared environment and Fossil.env (precept C6, Explicit Arguments), a URL holding a password reaches it on standard input (--args -), every wait is bounded by AtHost.wait or the poll's own timeout, never a fixed delay, and Fossil's default permissions are left as fossil new makes them. (1) The first /chat-poll makes the chat table, before which a post adds no message; then a post through /forume1, a check-in pushed from a clone and a post written in a clone (Place#new_thread) and pushed each add exactly one message from ocx-timeline, approving a held post adds none, and an id is never given again after the table is emptied (alerts.c 158-215; chat.c 273-285). (2) A poll held at the newest id answers the next post's message within 2.5 s of the post's return, a poll with nothing new answers {"msgs":[]} after 2 to 6 s, and, still signed in as lab-1, a poll at name=0 without a same-origin Referer answers HTTP 400, "Database error: not authorized", as observed (chat.c 713-787; main.c 2197; cgi.c 706-731). (3) Three posts sent in three syncs reach the clone in one fossil pull of 2 or more round trips, under one receipt number above the clone's earlier highest (content.c 472-482). (4) fossil remote add ocx-server default copies the URL and a password; after fossil remote off, a pull in a cleared environment finds its user only from the clone's default user, so on a clone as Place#clone makes it fossil pull ocx-server exits 1, "cannot determine user" (user.c 556-589), which check-in 2's daemon meets by naming the node user as the clone's default user; with it, a pull with no error exits 0 and saves the default remote, and one that cannot connect ("cannot connect to host 127.0.0.1:1", "Round-trips: 0") or whose password is wrong ("Error: login failed") exits 0 and leaves it off (sync.c 71-72, 397-411, 659-668, 700-717). (5) /rcvfromlist and /rcvfrom send lab-1 to the login page and answer the fixture's admin; OCX's receipt page, asked by Server::Asked signed in as lab-1, answers a post stone made through Fossil's page with sender stone, stone's capabilities, a receipt number and a row (shun.c 355-366, 468-475). (6) lab-1, holding goC4w, stores a reply through /forume2 (302 to /forumpost/PREFIX; public; its receipt naming lab-1; without csrf no redirect and nothing stored) and a ticket change through /tktedit (302 to /tktview/TICKET; one ticketchng row with the text in icomment, mimetype text/x-markdown and login lab-1), each form needing its csrf and a same-origin Referer; Fossil's default ticket edit page reads mutype, not a posted mimetype (tktsetup.c 678-693), which check-in 2's comment must send; given i and y, lab-1 sends a tag by fossil push --once ocx-server and a file by fossil uv sync ocx-server, each without error, and the default remote stays off, while a plain fossil push ocx-server saves it (forum.c 1274-1279, 1404-1411; login.c 1726, 1737-1739; tkt.c 1131; sync.c 263, 524-530). docs/fossil.md: a section "What the daemon relies on" (anchor daemon), one bullet per fact, naming its example and its Fossil 2.27 source lines, the file at 400 lines (precept C1, File Boundedness). Not in this check-in: a row in docs/interfaces.md for these contracts (precept C7, Pinned Interfaces), left for check-in 2, whose code relies on them.

Review: Codex (codex-cli 0.162.1, in its workspace-write sandbox, which blocks sockets, so it ran only the specs that serve nothing; the reviewer ran the rest) wrote every line, in seven passes (scratchpad codex-build-1.log to -7.log, beside the build plan). Pass 1 wrote the spec and the section. Review 1, running the six examples: three failed or errored; twelve findings: a pull after remote off in a cleared environment exits 1 "cannot determine user", to be pinned and met by the clone's default user; the change to nobody's and anonymous's capabilities removed, as Fossil's default permissions are never changed, the password's copy shown another way; a checkout opened by a Place naming its repository, which fossil refuses; a poll after the approval that waited 3 s for nothing; the wake's bound taken from the post's return, so the post's own time under load is outside it; Place#last_receipt, which check-in 3 deletes, replaced by SQL; the pushes made as stone, a person holding setup, made instead as the node user given i and y, each sending a tag or a file, so a push with an error cannot pass, and a plain push shown to save the remote; the ticket form's mutype; every setup command's result asserted; each failed pull's words pinned as observed; the section in docs/fossil.md's own form; a header comment. Review 2: two failed: obscure() salts each stored password (encode.c 868-881), so both are checked present and of one length, the push in example 6 through the copied remote, which nobody may not make, showing it signs lab-1 in; fossil uv list's name is the last word of its line; and the example's name kept near the plan's, a callback dropped, one tag query as a helper, two of the section's sentences rewritten. Review 3: all six pass; one SQL read rewritten, and pass 5 corrected the reviewer's own suggestion for it, as fossil sql prints a text value quoted. Gemma (gemma4:31b) read the change three times (scratchpad gemma-fossil-facts-1.txt to -3.txt, requests and replies in gemma-reviews/fossil-facts, -2 and -3). Held: none. Misread, a clarity finding, the code changed and asked again: the first read took the poll without a Referer, still signed in, as perhaps let through by its cookie, and taking the post's time after it returns as a mistake; comments now say that the cookie is kept so the missing Referer alone denies the messages, and why the bound runs from the post's return (pass 6); the second took the timing of the poll with nothing new as that of the poll in the spawned fiber; its names are now asked and waited, with a comment, and the bound's comment says a poll delayed past the post would answer at once (pass 7). Wrong: that fossil pull may exit non-zero on a wrong password: Fossil 2.27's pull ignores its error count for its exit status (sync.c 397-411), the example passes, and a trial by hand printed exit 0 for an unreachable host and for a wrong password. The third read raised the timeout's check, then read it right itself and found nothing. What remains: example 2's poll is held before the post because its one request reaches Fossil before the post's three; nothing forces that order, and in the other the poll answers at once, which the bound also allows.

Demonstrates: spec/facts/fossil_spec.cr, its six examples passing on Fossil 2.27 in about 12 s: "adds one chat message from chat-timeline-user for each timeline event once the chat table exists, local or by sync", "answers /chat-poll within about a second of a newer message, empty at its timeout, and not without a same-origin Referer", "stamps every artifact one pull brings with one receipt number, across its round trips", "saves the default remote only after a pull with no error, and exits 0 either way, once the clone has a default user", "keeps its own receipt pages for Admin, while OCX's receipt page answers the node user", and "stores a reply through /forume2 and a ticket change through /tktedit from a user holding goC4w, public, and redirects only once stored; push --once and uv sync leave the default remote as it was"; spec/guards/size_spec.cr holds docs/fossil.md at 400 lines and the spec at 363; spec/guards/documents_spec.cr and spec/guards/delineation_spec.cr render docs/fossil.md and resolve its links and the anchor the spec's header links to. crystal run ci.cr green: 526 examples, 0 failures.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QVpmCsBwVF3d4zhMHHDAxs check-in: a4cceed157 user: opus-ocx2 tags: agent-tools

Changes
Unified Diff Side-by-Side Diff Patch
Changes to docs/design/modules.json.
Changes to docs/design/modules.md.
Changes to docs/interfaces.md.
Changes to docs/linux.md.
Changes to docs/survey/functionality.json.
Changes to spec/ocx/command_spec.cr.
Added spec/ocx/completion_spec.cr.
Added spec/ocx/daemon_pages_spec.cr.
Added spec/ocx/daemon_setup_spec.cr.
Added spec/ocx/daemon_spec.cr.
Changes to spec/ocx/dry_run_spec.cr.
Changes to spec/ocx/ends_spec.cr.
Added spec/ocx/giving_spec.cr.
Changes to spec/ocx/logins_spec.cr.
Added spec/ocx/pulls_spec.cr.
Changes to spec/ocx/remote_node_spec.cr.
Added spec/ocx/run_pages_spec.cr.
Added spec/ocx/run_states_spec.cr.
Added spec/ocx/server_pages_spec.cr.
Changes to spec/ocx/turned_away_spec.cr.
Added spec/support/daemon_node.cr.
Added spec/support/fossil_pull.cr.
Changes to spec/support/held_watcher.cr.
Added spec/support/pages_proxy.cr.
Changes to src/ocx/care.cr.
Changes to src/ocx/commands.cr.
Added src/ocx/commands/daemon.cr.
Changes to src/ocx/commands/logins.cr.
Changes to src/ocx/commands/poll.cr.
Changes to src/ocx/commands/register.cr.
Added src/ocx/completion.cr.
Added src/ocx/daemon.cr.
Changes to src/ocx/decisions.cr.
Changes to src/ocx/fossil.cr.
Added src/ocx/fossil/pulls.cr.
Changes to src/ocx/fossil/said.cr.
Changes to src/ocx/fossil/scratch.cr.
Changes to src/ocx/hook.cr.
Changes to src/ocx/node.cr.
Changes to src/ocx/node/sharing.cr.
Changes to src/ocx/node/view.cr.
Changes to src/ocx/server.cr.
Added src/ocx/server/pages.cr.
Changes to src/ocx/watcher.cr.
Added src/ocx/watcher/giving.cr.
Added src/ocx/watcher/process.cr.
Added src/ocx/watcher/reports.cr.
Added src/ocx/watcher/state.cr.