Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
| Comment: | 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 |
|---|---|
| Downloads: | Tarball | ZIP archive |
| Timelines: | family | ancestors | descendants | both | agent-tools |
| Files: | files | file ages | folders |
| SHA3-256: |
a4cceed15735cd8c7c369d3b875f766c |
| User & Date: | opus-ocx2 2026-10-26 04:47:27.889 |
|
2026-10-26
| ||
| 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 | |
|
2026-10-23
| ||
| 15:06 |
The node's check ends what a gone watcher's run left once the process it launched has gone too, and each sandbox a process of the run started in a session of its own; a run's lock names the host's boot, and a lock from another boot names nothing the check ends
Intent: an adversarial review of e06e736748 (HISTORY.md decision 119) found two older defects in Node.end_left, the node's check's end of what a gone watcher's run left, each shown by a stand-in watcher built from OCX's own code, killed once its program ran, at 30 places a run's processes are: under no timeout, GNU's or BusyBox's, at a terminal or not, unconfined or in a sandbox, in a session of its own or not, ending with the node or not; 12 of the 30 left processes past the check. (1) It returned at once once the process launched had gone (`return unless watcher && launched && stat`), though its session or a group still held the run's processes: at a terminal always, since the watcher holds the terminal's near end, whose last close hangs it up and ends the session's leader before the check, so a job in a group of its own ran on, and a sandbox that does not end with the node; not at a terminal, an unconfined program that had ended left its child and its job, and a sandboxed one its sandbox's init, in the watcher's group or GNU timeout's (9 of 15 such places left processes). (2) A sandbox in a session of its own that does not end with the node, not at a terminal, ran on past the check and its wall: bubblewrap's --new-session makes the sandbox's init lead a session of its own, in no group the check ended, so it ended bubblewrap's outer process, and GNU timeout, alone. A run from a user holding R alone ends with the node and was never touched. Expression: src/ocx/care.cr: Node.take_run names the host's boot after the watcher, "PID boot:BOOT_ID" (BOOTED; Node.boot reads BOOT, /proc/sys/kernel/random/boot_id), so a lock's words are the watcher, its boot, then each program launched with its start, which pids never reads as numbers. end_left reads the lock's boot, none in a lock an older release wrote, which is read as before, and ends nothing for a lock naming another boot, every process of which has ended. With the process launched still there its branches are as before; gone, in the lock's boot, it ends each group numbered as that process or the watcher was, and each group of the session numbered as that process was, that holds a process started since the lock says it did (groups_of, given the groups and the start besides the session), as Linux gives no new process a number in use as a group's or a session's. Every branch ends its groups through Node.ended, never the group the checking process is in, which a stop would hold for ever: it sends STOP to each group, so no process of it waits for a child, which then keeps its number, reads /proc once (listed), sends KILL to each child of a process of those groups that is in a pid namespace below the host's, a sandbox's init and with it every process of its sandbox, and in its ensure KILL to each group, so none is left stopped. stat_of reads through proc_file, which rescues a process gone as stat_of did. src/ocx/sandbox.cr: Sandbox.namespaced?, what a sandbox's process is by its NSpid line, which Sandbox.ending_left and Node.ended share. docs/protocol-v1-fossil.md, section 5, and docs/protocol-v1-failures.md, cases 34 and 36, say what the check ends, and what remains; docs/linux.md: bubblewrap's --new-session init leads a session of its own, a terminal's hang-up as its near end's last close ends the session's leader, and a process's start tells it from another within one boot, as boot_id names each; HISTORY.md decision 124, amending decisions 118 and 119; docs/backlog.md: two items, below; docs/design/modules.json and docs/design/modules.md (generated). What remains, in decision 124 and the backlog: a sandbox in a session of its own that does not end with the node whose bubblewrap outer process has ended before the check, at its program's end or past its wall, which nothing its run's lock names leads to (3 of 15 places with the program ended before the check); and one timing dependency, a number taken again within one boot: once nothing the run left is in a group or session numbered as the process launched or the watcher was, Linux may give that number to a new process, whose group, or its session's groups, holding a process started since the run's, the check would end; that needs Linux to go round every free number first, up to 4,194,304 on the server's host, and the lock's boot rules it out across a reboot. Review: Gemma (gemma4:31b) read the change twice (scratchpad gemma-end-left-1.txt and -2.txt, requests and replies in gemma-reviews/end-left and end-left-2), the second asked, besides, for every order of the stop, the read of /proc and the KILLs against a process of the groups exiting, forking or reaped, a lock with and without a boot against each branch, a session whose last program was named without a start, whether the check can stop itself or leave a process stopped, whether each spec would pass with its part of the fix reverted, and whether the documents say anything the code does not do. Held: none. Misread, a clarity finding, the code changed and asked again: the first read groups_of's start comparison as comparing nil with an Int64; the start is now read into a variable of its own first, 0 for a process whose start /proc does not give, and the comment says so. Wrong: that a group gone before the stop is sent KILL in the ensure as well: both signals fail, which is no failure, as Node.ended says; and that a lock whose start is missing or malformed skips the gone branch: that is meant, as with no start nothing tells the process launched from one that took its number, which Gemma itself then said. The second read every order, both lock forms, the start-less session, the own group and the documents right and found nothing; it took the stop and the KILL of a namespaced child as shown together by the matrix, which shows the KILL alone: the stop guards a member reaping a child between the read and the KILL, an order no spec forces. Demonstrates: spec/ocx/care_spec.cr, six new examples, "ends every process of a gone watcher's run, and nothing else", under no timeout, GNU timeout's stand-in (spec/support/stand_in_timeout.cr, built with cc; pending where it is not installed) and BusyBox timeout (pending where it is not installed): a stand-in watcher in Python, leading a session of its own, launches as the watcher does, at each place, a program Launch.plan plans, leaving a child in its group and a job in a group of its own, and the spec writes the run's lock as the watcher does; every stand-in is killed, and the check ends every process of every run, at the 10 places, while the process launched runs, or at a terminal once the hang-up has ended it, and at the 4 places not at a terminal but a sandbox in a session of its own that does not end with the node, the program ended of itself before the check, while a process in a session of its own and one in the spec's own group run on; each example's runs run at once, and the file's 14 examples take 2.4 s. With end_left as it was, BOOTED and Node.boot added so the spec compiled, all six failed, listing 4 places each, at a terminal unconfined or in a sandbox not ending with the node and, not at a terminal, a sandbox in a session of its own not ending with the node; and, with the program ended, 2 each, unconfined and a sandbox in no session of its own not ending with the node (tried). "ends what a gone watcher's program left once that program has gone, only in the boot its run's lock names and started since it": a lock naming another boot ends nothing, the program running or gone, nor one with no boot word, nor one in this boot whose start is after the job's; with the program's start the job ends; with end_left as it was, the first check ended the job (tried). "never ends the process group of the process that checks": a lock naming the spec's group as the watcher's, its program in it, ends nothing; with the exclusion removed, the spec stopped itself and stood stopped (tried, in a session of its own). The examples there before pass, with locks written by hand naming no boot. spec/ocx/program_spec.cr and spec/ocx/watcher_spec.cr wait for a lock of four words, and spec/ocx/program_spec.cr finds the second the host's boot. spec/ocx/sandbox_spec.cr, spec/ocx/ending_spec.cr and spec/ocx/tasks_spec.cr pass with Sandbox.namespaced? shared. src/ocx/care.cr and src/ocx/sandbox.cr hold the change; spec/guards/size_spec.cr holds every file within 400 lines, spec/ocx/watcher_spec.cr at 400; spec/guards/derivation_spec.cr finds docs/design/modules.md generated; spec/guards/documents_spec.cr renders HISTORY.md and the documents changed; the coverage ratchet finds every statement of src/ocx/care.cr and src/ocx/sandbox.cr run. crystal run ci.cr green: 520 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: 32ed3cbef7 user: opus-ocx2 tags: agent-tools | |