Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Check-ins with non-propagating tags:
|
2026-10-31
| ||
| 19:46 | Note: Handoff: ollama-session 2026-10-31 technote: [ddc6461fb1] user: ollama-session tags: handoff | |
|
2026-10-30
| ||
| 00:09 | Note: Console boards technote: [e459e9b7bc] user: ollama-session tags: console-board | |
|
2026-10-06
| ||
| 23:48 |
Slice 13: queue and activity, a reviewer's queue and what has happened lately, from the record alone
Intent: Build docs/slices-parity.md slice 13, replacing the reference's console views and its reviewer: a reviewer sees the check-ins waiting for a verdict and the turns waiting on it, and everyone sees the newest check-ins with their ci and audited tags, posts and verdicts, derived from the repository alone, with no timer. Expression: `queue [--login LOGIN]` and `activity` (src/ocx/commands/views.cr) read the served repository, or a clone as it stands, so a reviewer's agent reads its own queue. The queue lists the newest check-ins that change files with no verdict whose tag's card is another login than the author, never the login's own, and on each record it took part in, the newest turn waiting on it. Who took a turn is who Fossil received it from on the served repository, else the card, as Turns.by now says for both (src/ocx/turns.cr). A viewer sees check-ins and verdicts with o or h, posts with 2, tickets with r. USAGE.md's sections for tools not yet built moved whole into docs/slices-parity.md, "Tools planned past parity", to make room, and USAGE.md points there. docs/interfaces.md, the module design and the survey say so. Demonstrates: spec/ocx/views_spec.cr (the queue holds the check-ins with no verdict but their author's, not a login's own, and the turn waiting on the reviewer on a ticket, read on the served repository and in a checkout, and in a thread it posted in through its clone; the activity lists a check-in with its branch, ci tag and verdict, a post and a verdict; what a viewer may not read is left out; refusals). crystal run ci.cr green: 254 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 30720aab0c user: opus-ocx2 tags: audited, ocx2 | |
| 23:32 |
Slice 11, part one: CI, a branch's newest check-in tested once and tagged ci green or red as the CI unit
Intent: Build the CI of docs/slices-parity.md slice 11, replacing the reference's command sessions and its ci-ocx tags: a CI unit tests each branch tip it has not tested, in its sandbox, and tags it as its own login; a red one is said on its record. Expression: `ci RECORD LOGIN CHECK [--branch NAME]` (src/ocx/commands/ci.cr) makes or finds the unit's workspace bound to the branch it tests; when the branch's newest check-in on the served repository has no ci tag, it pulls, updates the unit's tree to it, runs CHECK in a fresh sandbox, tags the check-in ci, green or red and the check's last line (else how it ended), and pushes, all as the unit in its sandbox; a red one is a report entry on the unit's record. A tree with changes tests nothing. A session whose runner is ci runs a pass each time it is woken, never twice for the same check-in unwoken (src/ocx/commands/serve.cr, src/ocx/commands/session.cr). Making or finding a workspace now reads the repository's public URL only for one made, finished or replaced: one that still syncs is found as its binding has it, so a session's later passes and turns need no remote (src/ocx/commands/workspace.cr). USAGE.md, docs/interfaces.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/ci_spec.cr (the branch tip tagged green with the check's last line, not tested again, a new check-in tagged red and reported on the ticket as the unit, a check that prints nothing tagged with how it ended; a CI session tests the tip when woken, with no remote named; a tree with changes tests nothing; refusals), spec/ocx/workspace_spec.cr, spec/ocx/session_spec.cr, spec/ocx/dry_run_spec.cr, spec/ocx/command_spec.cr, spec/ocx/styles_spec.cr. crystal run ci.cr green: 250 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 6efed12d06 user: opus-ocx2 tags: audited, ocx2 | |
| 23:19 |
post's dry run pulls nothing, and a recovering create is shown following no link a session left
Intent: Close codex-cloud-2's audit of d3cd11181a (slice 6), P2: ReadOn was a check that pulled the caller's clone, so post --after --dry-run imported artifacts, a change in a dry run. And add the recovery test its audit of 038d3a9606 (slice 1) asked for: the predictable binding.json.new it found was replaced in 4ae6d56d70 by a fresh file renamed into place, which its audit of 4ae6d56d70 confirmed, but no spec yet recovered a create through a planted link. Expression: post's pull is a change of its own, PullToReadOn, which a dry run lists and does not make; ReadOn reads on in the clone as it stands (src/ocx/commands/post.cr). docs/interfaces.md says so. Demonstrates: spec/ocx/post_spec.cr (post --after --dry-run lists the pull and reads on, and the person's reply the served repository holds is still absent from the clone; a pull that cannot reach its remote still says so), spec/ocx/workspace_spec.cr (a create cut off before its binding is finished with a link to a host file at binding.json.new, which stays as it was; a link in binding.json's place is refused as something else, and the file stays as it was). crystal run ci.cr green: 247 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: e7c3fde546 user: opus-ocx2 tags: audited, ocx2 | |
| 23:15 |
The hook wakes every session, two logins on one record among them, and a word is heard only when written whole
Intent: Close codex-cloud-2's audit of 6dc98e56b0 (slice 8's sessions). P1: Sessions.pipes kept one pipe per record, so of two logins' sessions on the same record the hook woke only one, and with no timer the other could stay idle indefinitely. P2: Sessions.deliver ignored the write's result, so a stop into a full pipe was reported heard. Expression: Sessions.pipes answers every session's pipe, each with its unit, LOGIN/RECORD, and the hook's log names the sessions it woke so (src/ocx/sessions.cr, src/ocx/hook.cr). deliver counts a word heard only when its whole line went into the pipe, which a full pipe takes none of. docs/interfaces.md says so. Demonstrates: spec/ocx/session_spec.cr (two logins' sessions on one ticket both read the hook's wake, and its log names both; a stop into a full pipe is not heard). crystal run ci.cr green: 246 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 0dcd5f0527 user: opus-ocx2 tags: audited, ocx2 | |
| 23:11 |
The git export serves only the repository it is fetched through, and a fetch git fails is a plain error
Intent: Close codex-cloud-2's audit of b62b49520d (slice 5). P1: the export authorized the viewer by the repository Fossil served the request through, but served any sibling NAME.fossil, so a private repository's export was readable through a public one. P2: a fetch git failed answered success headers and then a 500 inside the body. Expression: NAME in `<ext>/git/NAME.git` must be the served repository's own name, the only repository whose capabilities Fossil gives for the viewer; another's export is refused 404, whatever its file is named (src/ocx/git_export.cr). git upload-pack's answer is read whole before any header is written, so a fetch it fails is a plain 500. With only the served repository read, mirror's check that the repository file exists can never fail, and is gone. USAGE.md and docs/interfaces.md say so. Demonstrates: spec/ocx/git_export_spec.cr (fetches now go through the exported repository itself; its export through another repository is refused 404; a garbled fetch answers 500 with no success header; the clone, the push refusal and the other refusals as before). crystal run ci.cr green: 246 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 2950d18201 user: opus-ocx2 tags: audited, ocx2 | |
| 22:58 |
Slice 9: supervision, the node's units and their sessions listed, a stopped session restarted, and each release linked
Intent: Build docs/slices-parity.md slice 9, so a setup or admin viewer sees which units a node has and how their sessions stand, starts a stopped session again, after an accept among others, and sees each release's check-in and what next changes, replacing the console's Sessions, Workspaces and Daemon pages and its stop and restart actions. Slice 8 has no daemon, so nothing starts one and nothing needs one supervised. Expression: `units` (src/ocx/commands/supervise.cr) lists each workspace under the agents root, on every project, but those moved aside: its binding, its session as its run says while one runs, the release it runs, and its last turns. A session now logs one typed line a turn (src/ocx/sessions.cr, src/ocx/commands/serve.cr): when, the turn it answered, how the agent ended, the check-in, the post and the report, or why the turn did not close. `restart RECORD LOGIN` launches a stopped session again with the command its start kept beside the workspace (src/ocx/commands/session.cr), from the restarting program, so its release, as the login that restarts it. Each release on the web page is a link to its check-in, and next's changes from current a link to Fossil's diff (CheckinRef and Changes, src/ocx/elements.cr; src/ocx/markdown.cr; src/ocx/commands/release.cr). The cutover's lists in docs/slices-parity.md now say all ten slices landed, but slice 8's limit on a task's processes. USAGE.md, docs/interfaces.md, the module design and the survey say so. Demonstrates: spec/ocx/session_spec.cr (units lists a unit stopped and then running, with its last turns, and leaves out one moved aside; restart launches the kept command from this program as the restarting login, and refuses a running session, one never started, and no program or login; a session's log keeps each turn, a report among them), spec/ocx/styles_spec.cr (the status page links current to /info and next's changes to /vdiff), spec/ocx/release_commands_spec.cr, spec/ocx/command_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 246 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 15523617a5 user: opus-ocx2 tags: audited, ocx2 | |
| 22:38 |
Slice 8: a unit's bound tools, context and report, from its tree and over MCP
Intent: Build the MCP for a unit of docs/slices-parity.md slice 8, PLAN.md's bound surface ocx.context and ocx.report, in place of the reference's task --wait, --write and --mark: an agent asks what it works on and takes its turn on its own record without naming either, since its session, not the agent, drives the next turn. Expression: `context` and `report TEXT` (src/ocx/commands/bound.cr) read the binding beside the unit's tree and the record's turns in its clone. context says the record, login, branch and remote, and what waits on the unit; report takes the unit's turn as post takes one, a comment on its ticket, or in its thread a reply to the last turn that waits on it, else to the latest. Both are offered at the command line and over MCP; docs/prompt.md now names them to the agent. USAGE.md, docs/interfaces.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/bound_spec.cr (in a unit's tree on a ticket, context says the binding and the opening waiting, report comments as the unit, and context then says answered, over MCP too; in a thread, report replies to the post that waits, and with none waiting to the latest; both refused outside a unit's tree), spec/ocx/dry_run_spec.cr, spec/ocx/command_spec.cr, spec/ocx/styles_spec.cr. crystal run ci.cr green: 244 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 39e65be9c7 user: opus-ocx2 tags: audited, ocx2 | |
| 22:28 |
start gives the session every option it was given, its remote and branch among them
Intent: Let a session start where the served repository names no public URL. The live demonstration of 6dc98e56b0's staged release found a session started with --remote ending at once: serve makes or finds the workspace as create does, which reads the remote again, and start had not passed it on. Expression: start passes --branch and --remote to the session's serve, with --model, --program and --check (src/ocx/commands/session.cr). Demonstrates: spec/ocx/session_spec.cr (the session's command line carries the remote start was given). crystal run ci.cr green: 241 examples, 0 failures; coverage 100.0%; 1 executable builds. Live, with a binary built from this tree on a scratch repository with no email-url: started, the session answered the ticket's opening, the release's hook woke it after a person's turn and it answered, and stop ended it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 689407cb82 user: opus-ocx2 tags: audited, ocx2 | |
| 22:20 |
Slice 8: a unit's session, which stays and answers each turn that waits on it, started, woken and stopped
Intent: Build the sessions that stay of docs/slices-parity.md slice 8, so a unit answers each turn that waits on it on its record without anyone taking its turn by hand, replacing the reference's workspace serve, its wake pipes and the daemon that started its sessions; and give the web page and a record a way to a unit's turns, which turn would hold up. Expression: `start RECORD LOGIN RUNNER [turn's options]` (src/ocx/commands/session.cr) makes or finds the unit's workspace and, unless a session runs there, starts this program's `serve` detached under setsid (src/ocx/daemon.cr, its one setsid site) as the login that starts it, from the program's own file the context now carries (src/ocx/command.cr, src/ocx/entry.cr). `serve` (src/ocx/commands/serve.cr) takes the unit's turn whenever the served repository shows another login's waiting, as turn takes one, never twice for the same one unwoken, then blocks on its wake pipe: `wake` has it look again, and `stop`, which the `stop` tool writes, ends it. Its lock, wake pipe, run and log are beside the workspace, out of every sandbox (src/ocx/sessions.cr); whether one runs is read from its process's command line, taking no lock, and standing shows it (src/ocx/commands/standing.cr). The hook writes `wake` into every session of the served project after each receipt, without blocking, and logs which it woke (src/ocx/hook.cr). turn is withheld from the hook as from the page, since a turn would hold up both, and its close now counts the unit's own turn only once that turn is on the served repository (src/ocx/commands/turn.cr): a session spec found a unit that could not push looping on a turn it believed it had taken. USAGE.md, docs/interfaces.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/session_spec.cr (a session takes the turn that waits when it starts, looks again when woken, and ends on a stop, letting its lock and run go; it goes on after a turn that does not close and tries it again only when woken; a second session in a workspace is refused, start returns the one that runs, and standing shows it; start launches serve detached with the record, login, runner and options, as the login that starts it; stop is heard by a session that reads its pipe and by none otherwise; the hook wakes a session after a receipt and logs it; refusals), spec/ocx/turn_spec.cr (a call to turn recorded on a ticket is answered failed, and nothing waits), spec/ocx/dry_run_spec.cr, spec/ocx/command_spec.cr, spec/ocx/styles_spec.cr. crystal run ci.cr green: 241 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 6dc98e56b0 user: opus-ocx2 tags: audited, ocx2 | |
| 21:52 |
The web page says a tool is withheld before any other refusal
Intent: Answer a request for a tool the web page does not offer with why it is withheld, the most basic refusal, before asking for a POST or refusing a field the server sets. Slice 8's live demonstration found a GET of turn on ocx2-cgi-next answered "taken only by a POST from this site's own page", as deploy's had been since slice 0.5, though a POST would only be refused again. Expression: Commands.withheld names why an entry point does not offer a tool (src/ocx/commands.cr), and the CGI hands such a request to the tool at once, which answers "not offered here" and why before reading anything (src/ocx/cgi.cr). Demonstrates: spec/ocx/styles_spec.cr (a GET of turn with a field the server sets answers that the page does not offer it, and why), spec/ocx/harness_spec.cr. crystal run ci.cr green: 235 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: caba54c54e user: opus-ocx2 tags: audited, ocx2 | |
| 21:44 |
Slice 8: a sandbox's own accounts, so ocx2 runs inside it as its prompt says
Intent: Let an agent use ocx2 in its sandbox, as its prompt tells it to. The live demonstration of 4ae6d56d70's staged release found `ocx2 standing` dying there with "No such user: 1000": the program finds its home in the account database (precept C6, Explicit Arguments), and the host's names no user 1000, so any program in the sandbox that asks who it is failed the same way. Expression: the sandbox's written plan now carries an account database of its own (src/ocx/sandbox.cr, Sandbox::Plan): root, and the task's user `unit`, its workspace its home, which bubblewrap reads from its input (`--ro-bind-data 0 /etc/passwd`) in place of the host's /etc/passwd; the task's own input is then /dev/null, since bubblewrap takes its own. A tool's log shows the whole plan (src/ocx/commands/run.cr, src/ocx/commands/turn.cr), and tools/ocx2.cr says plainly when no account names its user. USAGE.md and docs/interfaces.md say so. Demonstrates: spec/ocx/run_spec.cr (inside, `id -un` answers unit, /etc/passwd names it with the workspace its home, and its input is empty), spec/ocx/turn_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 235 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 08cec23a30 user: opus-ocx2 tags: audited, ocx2 | |
| 21:34 |
Slice 8, part one: turn, a unit's turn on its record, committing only work its check verified, every fossil act for a unit in its sandbox
Intent: Build the turn of docs/slices-parity.md slice 8, so an agent answers what waits on its unit's record and its work is committed only once verified (codex's point 2 in stone's feedback 7f12302594), replacing the reference's workspace start and its runners for a turn that completes. A security fix rides along: a unit's clone, which a session can write, was pulled by fossil on the host, and a clone's ssh-command runs on a pull; every fossil act for a unit now runs in its sandbox. Expression: `turn RECORD LOGIN RUNNER [--check COMMAND] [--model MODEL] [--program PROGRAM]` (src/ocx/commands/turn.cr) makes or finds the unit's workspace as create does; when another login's turn waits on it, it writes the prompt (src/ocx/prompt.cr, from docs/prompt.md, compiled in) and runs the agent in the unit's sandbox within an hour's limits, its login's files bound in from AGENTS/LOGIN and the release's ocx2 at /ocx, first on its path (src/ocx/sandbox.cr). The claude and codex adapters (src/ocx/runner.cr, src/ocx/runners.cr, src/ocx/runners/claude/runner.cr, src/ocx/runners/codex/runner.cr) alone name their command lines, login files and answers (precept I1, Runner Isolation). OCX then closes the turn as the unit, in its sandbox (src/ocx/unit.cr): a pull; the tree's changes committed with --nosync on the unit's branch, then pushed, only once the agent exited 0 and the unit's check exited 0 in a fresh sandbox with 200 paths changed at most; otherwise a report entry, {"ocx": "report", "summary", "files"}, names the paths left (src/ocx/records.cr); and the agent's answer posted as the unit's turn unless it took one itself. OCX's own files in a workspace are written through a fresh file renamed into place and read only when plain (src/ocx/workspaces.cr), so a link a session leaves is never followed, and create's pull runs in the sandbox (src/ocx/commands/workspace.cr). An OCX entry addressed to a node is no turn (src/ocx/turns.cr); post takes a turn on a ticket as well as a thread (src/ocx/commands/post.cr, src/ocx/forum.cr, src/ocx/commands/record.cr); a runner's program is the node's own, set by the server (src/ocx/elements.cr, src/ocx/commands.cr, src/ocx/page.cr); fossil's masking and how a task ended are said in one place each (src/ocx/fossil.cr, src/ocx/commands/run.cr). A turn outlasts a web request, so the page records a call, which the hook takes. USAGE.md, docs/interfaces.md, docs/fossil.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/turn_spec.cr (a person's turn waiting: the agent run with the prompt, its work committed as the unit on task/ and the ticket's first ten hex digits once its check passed, the commit's Demonstrates line naming the check, and its answer posted as the unit; no agent run when nothing waits; a failing check, no check, an agent killed, 201 paths and an agent that answered nothing each reported with the paths left and nothing committed; an agent that answered itself, nothing more posted; on a thread, the answer a reply to the last post that waited; a commit Fossil refuses, the unit's branch started by a person meanwhile, reported; a unit that may not push, and a clone a session cut off, each stopping with why, its work left in the tree; the refusals, the dry run, the command line, and a call recorded on the ticket that the node's work takes), spec/runners/codex_conformance_spec.cr and spec/runners/claude_conformance_spec.cr (each adapter's command line and answer; tagged live, the real codex 0.156.1 and claude 2.1.289 taking those flags in OCX's sandbox, both passing here), spec/ocx/workspace_spec.cr (no link a session left is followed; the pull in the sandbox, so a clone's ssh-command runs only there), spec/ocx/post_spec.cr, spec/ocx/standing_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 235 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 4ae6d56d70 user: opus-ocx2 tags: audited, ocx2 | |
| 20:43 |
Slice 7: standing, where work stands on a ticket or thread for a login, from the order of its turns
Intent: Build docs/slices-parity.md slice 7, so the people and agents working on a record can ask what waits on a login, derived from the record alone, replacing the reference's task --status and what task --wait waited for. Expression: `standing RECORD [--login LOGIN]` (src/ocx/commands/standing.cr) reads a ticket's or a thread's turns in order (src/ocx/turns.cr): each ticket change that says something, the ticket's opening first even with only a title, or each post, with who took it: on the served repository the login Fossil received it from, in a clone the turn's card. Every turn after the login's own last waits on it, the record's first among them until it has taken one; the standing is pending while one waits, answered when its own turn is the latest. It only reads, and the web page answers only a viewer who may read the record, Fossil's `r` for a ticket and `2` for a forum, since the tool itself needs no capability. Decided here: what OCX parses is JSON (the structure precept), so standing is read from turn order and not from the text people write; a report or a question in text is for people to read, the report and note rows of docs/interfaces.md go to slice 8, which writes them, and open questions to slice 13's views. `post` now indexes its reply in the clone too (`fossil test-crosslink HASH`, which takes a name though its usage says a record id), so a clone reads its own turns as it reads others'. USAGE.md, docs/interfaces.md, docs/fossil.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/standing_spec.cr (on a ticket, a login with no turn waits on its opening, answered once its own comment is the latest, a change that says nothing no turn, pending again on another login's reply, and asked for another login; in a thread read in a unit's clone, the root waits until its post, then another login's answer, by the cards; the same read on the served repository; refusals for a viewer who may not read the ticket or the forum, for no login and no repository; the web page and the command line), spec/ocx/post_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 222 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: cd98157a5e user: opus-ocx2 tags: audited, ocx2 | |
| 20:32 |
Slice 6: post, a turn on a forum thread as the caller, from a clone by push, and reading on from a post
Intent: Build docs/slices-parity.md slice 6, so an agent's turns and milestone-sized work can live on a forum thread, as this branch's own work does, posted as the caller's own login like a ticket change in slice 2, replacing the reference's task --write on a thread and thread. Expression: `post POST TEXT [--after POST]` (src/ocx/commands/post.cr) replies to POST in its thread from the caller's clone. Fossil has no command that writes a forum post, only its web page, so the reply is composed as Fossil's forum artifact (src/ocx/forum.cr: D, G, I, N, U, W and Z cards, a card's argument writing a space as \s), put into the clone with `fossil test-content-put` in its checkout, and pushed; the served repository indexes it as a post received from the pushing login. A push needs Check-In (`i`), a forum post's too: Fossil refuses one without it with "Error: not authorized to write", which is reported, and a login without it posts in Fossil's own forum page. With --after it pulls and answers what others posted in the thread since that post, found by its own D card, so a post this clone put and has not indexed is read on from too, and written by others as each post's U card says, since a clone records everything it pulls as received from its own login. Decided here: OCX starts no thread (precept A5): a person starts one in Fossil's forum page, the web page leaves posting to it, and OCX Records stay on tickets while a thread carries turns. Found on the way: `fossil sql` reports an SQL error on standard error and exits 0, so Fossil.run now counts an Error there as a failure; and a repository has no forumpost table until its first post, so finding a post reads its artifact alone. docs/fossil.md records these; USAGE.md, docs/interfaces.md (the fossil CLI row names its one test command, and a turn on a thread is pinned), docs/slices-parity.md, the module design and the survey say so; the dry-run spec gives post a checkout and a post to reply to, so its dry run runs to its end like every tool's. Demonstrates: spec/ocx/post_spec.cr (a reply from a unit's clone held by the served repository as the unit's, in its thread, replying to the post named, its non-ASCII text whole; a reply to the clone's own unindexed post; reading on from a post: another login's reply and nothing of the caller's own, then nothing new; a push refused for a login without Check-In and one that cannot reach the server, and a pull that cannot; refusals for a name that is not hex, names nothing, names a check-in or a wiki page, an empty reply, the served repository and the web page; a dry run that posts nothing; an SQL error raised though fossil exits 0; the command line in a unit's tree), spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 218 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: d3cd11181a user: opus-ocx2 tags: audited, ocx2 | |
| 20:15 |
Slice 5: the git export, served by the one program as git, read-only over git's smart HTTP
Intent: Build docs/slices-parity.md slice 5, so the mirrors outside OCX can fetch from ocx2 once the cutover repoints the extension root's git link, and the ocx release's export is no longer needed. Expression: the program started as `git` (`git-next` from the release offered as next, and `ocx2-git` and `ocx2-git-next` too) is a fifth entry point (EntryPoint::Git). For `<ext>/git/NAME.git/…` it serves the git export of NAME.fossil, beside the served repository, from the directory its `last-git-export-repo` setting names, which `fossil git export` records (src/ocx/git_export.cr): `info/refs?service=git-upload-pack` advertises the refs and `git-upload-pack` sends a pack for what is wanted, a gzipped request accepted. A push is refused (403), and so is a viewer who may not read code (Fossil's `o` or `h`); a repository without an export is 404; a body over 50 MB, raw or expanded, is refused unread (413); a gzip that does not read is 400; an export git cannot read is 500. Git is started in one place, src/ocx/git.cr, with PATH alone in its environment, raising with what it said when it fails (precept I7). Ported from the reference's src/ocx/git_http.cr, with named statuses, named groups and a typed settings read. facts.json names the setting; USAGE.md, docs/interfaces.md (the export's row, pinned, and the one program's), docs/slices.md, docs/slices-parity.md, the module design and the survey say so. Its extension-root links are the maintainer's (scripts/start-fossil.cr): `ocx2-git` and `ocx2-git-next` let it be tried beside the ocx release's `git` before the cutover. Demonstrates: spec/ocx/git_export_spec.cr (a git client cloning a repository's export through it, by way of an HTTP server in the spec, and its push refused with 403; the advertised refs and a pack for a want, raw and gzipped, under each of its names; each refusal: a push by path and by query, a viewer without o or h, a path that names no repository, a repository with no export and a file that is not one, a path git's smart fetch does not use, a body announced over the limit and one expanding over it, a gzip that does not read, and an export git cannot read), spec/ocx/elements_spec.cr. crystal run ci.cr green: 214 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: b62b49520d user: opus-ocx2 tags: audited, ocx2 | |
| 20:04 |
Slice 4: audit, a verdict on a check-in by a second login, read back as the served repository holds it
Intent: Build docs/slices-parity.md slice 4, so ocx2 records verdicts itself and docs/invite.md and AGENTS.md can send reviewers to it once a release holding it is accepted (codex's third point), and a verdict Fossil did not keep is never reported as given. Expression: `audit CHECK-IN pass|fail NOTE` (src/ocx/commands/audit.cr) needs Fossil's Check-In (`i`) and refuses the check-in's author, since a verdict is a second login's. The check-in is a hash, a prefix of it or a name; a name that reads as an option is refused. It tags the check-in `audited` with `VERDICT: NOTE` as the caller, as `call` writes a record: in the caller's clone and pushed, a push reporting Error counting as not received, or on the served repository with `--user VIEWER`. Then it reads the verdict back as the served repository holds it: there, from its tags, with the login Fossil received it from; from a clone, by a second push that must send nothing. Tried with Fossil 2.27: a tag pushed by a login without Check-In is refused with "Error: not authorized to write" and the push still ends "Push done" (docs/fossil.md). docs/invite.md and AGENTS.md now name `ocx2 audit`, and the `ocx` release's until a release holding it is accepted. Found on the way: Crystal 1.18 runs, for every spec file defining a top-level private method of the same signature, the body of the file required first, so records_spec ran verdict_spec's `viewer`, which names no node, when the coverage run required them in another order; each spec's helpers now have names of their own, and the A1 guard fails on a helper two spec files define (spec/guards/checkin_spec.cr, refuted). USAGE.md, docs/interfaces.md, docs/slices-parity.md, the module design and the survey say so. Demonstrates: spec/ocx/verdict_spec.cr (a verdict tagged as the viewer on the served repository and read back with who gave it, then replaced by a later one; one from a unit's clone, pushed and held by the served repository; one by a login without Check-In refused by the server and reported as not given; a read-back whose push cannot reach the server, and one whose second push sends a tag again; refusals for the author, a login without Check-In, a name that names nothing, an option, a ticket change, an empty note and a verdict neither pass nor fail; a dry run tagging nothing; the web page and the command line in a unit's tree), spec/guards/checkin_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 211 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 53cf958b8c user: opus-ocx2 tags: audited, ocx2 | |
| 19:43 |
Slice 3, second part: the hook works through the node's OCX Records, its state on the record: claimed, then answered or failed once
Intent: Finish docs/slices.md slice 3 with codex's first point settled: each OCX Record naming the node runs as the login that delivered it and gets one final response, and a crash at any step, after a password is set or a task has run but before the response, leaves a state on the record that the next run reconciles without running anything again unasked. Expression: `work LOGIN` (src/ocx/commands/work.cr) needs setup and is what the hook runs after each receipt, given the node's own login (`ocx2-hook --log FILE --login LOGIN REPOSITORY`; `hook LOGIN` now installs that command and checks the login exists without admin or setup). It reads every ticket change holding an OCX entry with who delivered it, Fossil's rcvfrom (Records.written, one query), and takes each record naming this node with no final response. Under a lock of its own in ROOT/node/locks, it reads the record again; writes a claim `{"ocx": "claim", "record", "node", "attempt"}` as the node's login before anything else; runs the record's tool by record, whoever runs work, as the caller, whose Fossil capabilities decide; and writes the one final response as the node's login: answered with the end of the tool's answer, or failed and why, a task that does not exit 0 among them (RunCommand::Output#failure). Only the node's own claims count, and only a response from the node or the record's caller closes it, so no one else spends a record's attempts or answers it. A record claimed with no response, its lock free, was cut off: it runs again only when its tool resumes (Command RESUMES, true for create, whose directory says how far it got; false for a tool this release lacks), and is otherwise answered failed, interrupted, never run again; after three attempts it is answered failed. A claim's lease is its lock, held while the run that wrote it lives, so recovery waits on no clock. A record calling work is answered failed; deploy is withheld from the hook, as from the web page; a record gives no field the server sets (Commands.server_set, which the web page and call now share). The hook's first write waits out the backoffice's write transaction by Fossil's own busy timeout. The hook's log line keeps its fields and gains two optional ones, what it worked and why its work stopped. Records.append is the one way an entry is written on a ticket. USAGE.md, docs/interfaces.md (the request and the task, pinned), docs/slices.md, the module design and the survey say so. Demonstrates: spec/ocx/work_spec.cr (a record answered once, its claim and response written by the node's login, the workspace made, and nothing left the next time; records failed for a caller without the capability, a task that exits 3, a record calling work, a server-set field and an unknown tool, and another node's record left alone; a create cut off resumed as attempt 2, a run cut off answered failed without running its task, a record past three attempts failed, a claim by another login not counted, a response by the caller closing a record and one by another login not; a record held by another run's lock not taken; a dry run that writes nothing and takes no lock; the hook entry working through records and logging them, and refusing a node login with admin; the web page and the command line), spec/ocx/run_spec.cr (why a task failed, each way), spec/ocx/command_spec.cr (which tools resume), spec/ocx/hook_spec.cr, spec/ocx/records_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 204 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: de5331c21e user: opus-ocx2 tags: audited, ocx2 | |
| 19:25 |
Slice 3, first part: run, a task in a unit's sandbox, confined to its workspace and limited
Intent: Build the task half of docs/slices.md slice 3, before the hook runs OCX Records with it: running is a tool of its own, so the command line and the web page reach it too, and "The sandbox holds" is shown from inside a running task. Expression: `run RECORD LOGIN COMMAND` (src/ocx/commands/run.cr) needs admin or setup, as create does, and is not offered to MCP. It makes, finishes or finds the unit's workspace through create's own state machine, now CreateCommand.made, then runs the command with sh in the workspace's tree under src/ocx/sandbox.cr, the one place the sandbox starts (precept I6). The plan is written from the workspace's path each time and kept nowhere a task can write, unlike the reference's: bubblewrap with namespaces of its own and the network shared; the task as user 1000 holding no capability (--unshare-user and --cap-drop ALL; a root-run bubblewrap without its own user namespace kept every capability, tried here); the system's programs and libraries and named files of /etc read-only, since a file only root may read would be the task's to read; the workspace read-write at its own path and nothing else of the host. OCX's own script then sets soft and hard CPU seconds, memory, a file's size and a wall clock, which the task cannot raise, before sh runs the command. The answer says how the task ended: its exit status, or the signal that ended it and the limit behind it (cpu by SIGXCPU, time by SIGKILL, files by SIGXFSZ), and the end of what it printed, kept by a bounded tail. Without bubblewrap no task runs. A process-count limit is not set: Linux exempts a task whose host user is root, as the server's is, so it comes when tasks run as a host user of their own; docs/slices.md and the survey say so. Elements gain whole numbers (Int32, an exit status), read and shaped as integers. USAGE.md, docs/interfaces.md (the sandbox plan's row, pinned), the module design and the survey say so; the dry-run spec runs run's dry run. Demonstrates: spec/ocx/run_spec.cr (the workspace made on the way and found the next time, the command's exit and output; from inside, user 1000 with no capability, HOME the workspace, only its own unit under the agents root, nothing of the served repository's directory, /usr unwritable; the CPU limit, the wall clock and the file-size limit each named, a signal named, a hard limit the task cannot raise, the end of a long output kept; no task without the sandbox program; refusals for a login without admin, MCP and a unit login with setup; a dry run listing the plan and changing nothing; the web page with the server's agents root, and the command line), spec/ocx/elements_spec.cr, spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 196 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: e4f2f487b3 user: opus-ocx2 tags: audited, ocx2 | |
| 19:07 |
Slice 10: the hook, installed: hook and unhook on the served repository, and where the hook stands in status
Intent: Build docs/slices-parity.md slice 10 before slice 3 relies on it: install ocx2-hook as the served repository's after-receive hook, as the served release, so records reach the node; remove it; and let anyone see whether it is installed, when Fossil's backoffice runs it and its last run, which is how the backoffice firing it for a web post on the served host, not yet seen there, can be shown on the page. Expression: `hook` and `unhook` (src/ocx/commands/hook.cr) need setup, take --root as the release tools do, and act on the served repository (Context#served!, which create now shares); MCP is not offered them. `hook` makes the root's node directory (0700) and its link `ocx2-hook` to `current/ocx2`, so the hook follows every accept and rollback, and adds `ROOT/ocx2-hook --log ROOT/node/hook.log %R` to the repository's hooks: once, a hook that runs ocx2-hook with another command replaced, other hooks untouched; a root serving no release is refused, since the hook would run nothing. `unhook` deletes every hook that runs ocx2-hook, by its place in Fossil's list, the last first, and keeps the log. Each answers its outcome and reads back where the hook stands; a dry run lists its changes and reads nothing more. `status` shows that standing to any viewer (src/ocx/hook.cr): installed, other or absent; the programs other after-receive hooks run; when the backoffice runs hooks (queued, nodelay or disabled, the settings read as Fossil reads them, the repository's or else the account's global, through the new Fossil::Place#setting); and the hook's last run, its log's last line read back from the end. The hook's log line is now typed (Hook::Run) and keeps slice 0.5's fields (when, repository, artifacts, woke, took_ms): the served release's hook writes it and the page's release reads it, so no field is renamed or removed. Fossil.literal reads an SQL literal back whole, its doubled quotes undone, for config and for FindCall. The hook's spec runs its fossil server with --nojail, as the served host does: run as root, a server otherwise jails itself and its backoffice ran no hook; the other specs keep the jail, since a backoffice waiting out its lease outlives its server by up to a minute. The sourcing guard now also finds `setting` reads, and facts.json names hooks, backoffice-nodelay and backoffice-disable. docs/fossil.md records what was tried with Fossil 2.27: the backoffice starts only after a page that answers 200, so a form's post reaches the hook with the page after it; each start leases a minute, and a post made a second after a page reached the hook 61 seconds later; the jail; hooks named by place; settings as Fossil reads them. USAGE.md, docs/interfaces.md, the module design, the survey and the cutover list (landed so far: 1, 2 and 10) say so. The hooks list is a durable fact the repository owns, set by a person with setup; the hook's runtime record, its log, stays outside the repository in ROOT/node (precept C2). Demonstrates: spec/ocx/hook_spec.cr (installed as the root's link to the served release, once, then unchanged; one run with another command replaced; another hook left alone and named; removed, then absent; the backoffice queued, nodelay, and disabled by the account's global setting; the last run read back from a log whose last line is longer than a read; a fossil server's own backoffice running the installed command for a forum post made over HTTP, once a page answered 200; a root serving no release, a login without setup, MCP and no root refused; a dry run that changes nothing; the web page and the command line), spec/ocx/harness_spec.cr, spec/ocx/dry_run_spec.cr and spec/guards/sourcing_spec.cr. crystal run ci.cr green: 189 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: ffdbbb4b95 user: opus-ocx2 tags: audited, ocx2 | |
| 18:31 |
Slice 2: OCX Records and responses on a ticket, typed JSON written as the caller, from a clone or on the served repository
Intent: Build docs/slices.md slice 2, so every later path has a place to ask and to answer: an OCX Record naming a tool, its arguments and the node that runs it, and a response naming the record. The slice left open how a change is made as the caller; the harness settles it here, so slice 3 can take the caller from what Fossil records on receipt rather than from what a record says. Expression: `call TICKET TOOL [--arguments JSON] [--node CODE]` and `respond TICKET RECORD [--failed] [--note TEXT]` (src/ocx/commands/record.cr) need Fossil's `w` and append a fenced json block to an existing ticket's comment (src/ocx/records.cr): `{"ocx": "call", "tool", "arguments", "node"}` or `{"ocx": "response", "answers", "outcome", "note"}`. A person opens the ticket in Fossil's own page; OCX opens none (precept A5). In a clone's checkout the change is made as its default user and pushed; on the served repository it is made with `--user VIEWER`; either way Fossil records its receipt under that login, as the specs show. A push that reports `Error:` or makes no round trip is said not to have arrived (Fossil.unsynced, which workspace create's pull now shares). A call's arguments are checked against the tool's input when written (Commands.read!), a tool that only reads is refused, and the node is the served repository's server-code, which a clone must name. docs/fossil.md records the Fossil 2.27 facts slices 1 and 2 found; docs/interfaces.md pins the ticket-change, push and read rows and sends the report and note rows to slice 7; USAGE.md, the module design and the survey say so; the dry-run spec writes a call and gives call and respond real inputs; Fixture::Repo#thread posts a forum thread over HTTP for both specs. Demonstrates: spec/ocx/records_spec.cr (a call written on the served repository as the viewer, attributed to the viewer by its U card and its receipt, read back, answered and failed; one from a unit's clone made by slice 1, pushed as the unit's login, and one whose push does not get through, kept in the clone; every refusal; a dry run that writes nothing; the web page and the command line in a unit's tree), spec/ocx/workspace_spec.cr and spec/ocx/dry_run_spec.cr. crystal run ci.cr green: 183 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: a1aba83727 user: opus-ocx2 tags: audited, ocx2 | |
| 18:19 |
Slice 1: create a work unit's workspace, recoverably: a fresh password, a clone that alone remembers it, the binding written last
Intent: Build docs/slices.md slice 1, settled as codex's review asked (7f12302594, point 1): a create can be cut off at any step, and the same create may be asked again. Expression: `create RECORD LOGIN` (src/ocx/commands/workspace.cr) needs admin or setup and acts on the served repository named outright, as the web page names it or --repository on its host, never a checkout's clone; MCP does not offer it. It checks that the unit's login exists and holds neither admin nor setup, that the record is a ticket or a forum thread (by any of its posts), the remote (--remote, else the repository's email-url, now named in docs/design/facts.json), and how far the workspace's directory says a create got (src/ocx/workspaces.cr). It sets a fresh random password on the login, clones with --save-http-password so the clone alone remembers it, opens the tree on the branch where the clone has it, and writes binding.json last, through a rename. The same create again returns a workspace whose clone syncs, with no new password; finishes one cut off before its binding, or makes it again when its clone no longer syncs; and refuses one that no longer syncs, or is bound to something else, unless --replace moves it aside first. A pull counts as failing on Fossil 2.27's two silent failures, "Error: login failed" with exit 0, and no round trip with exit 0. A workspace is AGENTS/LOGIN/projects/PROJECT/RECORD, the ocx release's layout and binding, under ROOT/agents, an agents root the page shows as the server's and takes from no request. Entry.node reads server-code through the same config reader. docs/interfaces.md pins slice 1's rows; USAGE.md's create row and section, the module design and the survey say so; the dry-run spec gives create real inputs. Demonstrates: spec/ocx/workspace_spec.cr (created; existing, with no new password; completed; refused, then replaced and moved aside; a create cut off after its password set, run again; a sync that fails; every refusal; a dry run that changes nothing; a forum thread by a post; the command line, and the web page refusing an agents root from a request), spec/ocx/dry_run_spec.cr and spec/ocx/command_spec.cr. crystal run ci.cr green: 178 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 038d3a9606 user: opus-ocx2 tags: audited, ocx2 | |
| 17:59 |
The slices answer codex's review: durable states for slice 3, a settled create, verified commits, the cutover's two lists, invite.md's audit
Intent: stone relayed codex's review (7f12302594): continue the planned slices through and past ocx without another reset, and as they are built, (1) define slice 3's durable execution states and retry rules, since an unanswered record does not prove its task never ran, and settle the same create twice; (2) commit an agent's changes only on successful verification; (3) have docs/invite.md use the accepted ocx release's audit until slice 4; (4) treat retiring the old release after slices 1 to 10 as a cutover that first names which workflows are supported and which wait in slices 11 to 17. Expression: docs/slices.md: slice 1 says where a workspace lives (ROOT/agents, the ocx layout and binding) and what the same create twice or a create cut off does, binding.json being written last as its commit point; slice 3 takes only tasks that complete, writes a claim before any side effect as the node's own login, gives each record one final response, treats a claim past its lease as interrupted, resumes what is recoverable and otherwise answers it failed without running it again, caps attempts, and locks per record; the daemon moves to slice 8; milestone 1 claims only what slices 1 to 3 and 10 show, a killed run recovered among them; the spike, which docs/fossil.md already records, goes, its open question to slice 10. docs/slices-parity.md: slice 8 takes the daemon and commits only work whose named check passed in a fresh sandbox, leaving failed, interrupted or over-budget work for inspection with a failure report; slice 9 needs slice 8; the cutover milestone lists what slices 1 to 10 support and what stays the ocx release's until 11 to 17. docs/invite.md names the ocx release's audit, on the host and from a checkout of the ocx branch. docs/interfaces.md: the stop, session and served-session rows are pending for slice 8, the open question for slice 7. Demonstrates: spec/guards/interfaces_spec.cr (each row pending for a slice the documents define), spec/guards/documents_spec.cr (every link resolves) and spec/guards/size_spec.cr (docs/slices.md at 390 lines). crystal run ci.cr green: 170 examples, 0 failures; coverage 100.0%; 1 executable builds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LvS1YtczzW32vo9g9CkGv2 check-in: 092af61b66 user: opus-ocx2 tags: audited, ocx2 | |
|
2026-10-04
| ||
| 03:59 | Remove seal: an invite's password is handed over directly Intent: Act on stone's review (236e40d65b): seal had no identified use, since every invite hands its one-time password over directly and nothing needs a password to cross the record, and it reached below the standard library to libcrypto's own calls, under the virtue of Clarity and precept C1 (File Boundedness) in spirit: code without a use is weight. Expression: src/ocx/seal.cr, tools/seal.cr, spec/ocx/seal_spec.cr and the shard's seal target are removed, with the module and survey entries; USAGE.md, ARCHITECTURE.md, PLAN.md, docs/slices.md (its sealed-password test), docs/invite.md (its fallback section) and docs/interfaces.md now say the password is handed over directly and never put on the record; HISTORY.md adds decision 28, amending decision 27, and records that Crystal's standard library offers digests, HMAC, PBKDF2, symmetric ciphers and TLS but no public-key encryption. Demonstrates: /usr/lib/crystal/core/openssl holds algorithm, bio, cipher, digest, hmac, md5, pkcs5, sha1, ssl and x509 and no public-key API; no file outside HISTORY.md mentions seal; spec/ocx/deploy_spec.cr stages audit and deploy instead; crystal run ci.cr green, 111 examples, 100% coverage, 2 tools build. check-in: a9f51a7e08 user: opus-ocx tags: audited, ocx2 | |
| 02:02 | Hold an accepted release to the inventory it was staged with Intent: Fix the release-completeness defect codex-cloud-1 found on recheck (21b142edc7): acceptance compared the copy with whatever was left in the staged directory, so a release whose staged program had gone was accepted and served with only its gate note; and state in slice 1's design that only an allowlist of nonsecret build settings is recorded by value, under precept A4 (Staged Deployment) and HISTORY.md decision 21. Expression: OCX::Releases.inventory writes release.txt when a release is staged, each file and its SHA-256, and requires the gate note and at least one program; staging offers a release only if it matches its inventory, and acceptance requires the staged release to match it, copies exactly the listed files and the inventory into a fresh directory, checks the copy against the inventory, and only then renames it into place and moves current; an existing accepted copy is served only if it matches. OCX::Deploy writes the inventory before smoking. docs/slices.md's environment section names the build settings recorded by value, records any other CRYSTAL_ or SHARDS_ variable by name only, masks URL credentials, and says deploy does not record them yet. Demonstrates: spec/ocx/deploy_spec.cr keeps the working release current when a staged program has gone (codex-cloud-1's case), when a staged note was edited after staging and when a file was added, and refuses a missing, malformed, gateless or programless inventory; the staged release lists release.txt and its inventory names every file; crystal run ci.cr green, 113 examples, 100% coverage, 3 tools build. check-in: d67421aa37 user: opus-ocx tags: audited, ocx2 | |
| 01:50 | Add rendering GPCR, JSON and other formats in Fossil's views to the roadmap Intent: Record the roadmap item stone pointed to (4d74c29547), ec2-user's technote on rendering GPCR, JSON and other formats in Fossil's file view, where the roadmap keeps where Fossil is headed, under the virtue of Clarity. Expression: ROADMAP.md's Fossil section says the file and document views are to render GPCR, JSON and other formats as they render Markdown and Pikchr, links the technote, names the Fossil 2.27 settings to try first (mimetypes, th1-docs, th1-hooks, th1-setup), notes that this host's fossil cannot load its Tcl library, offers an extension page as the fallback, and notes the two setting names in the technote that Fossil 2.27 does not have. Demonstrates: fossil settings on this host lists mimetypes, th1-docs, th1-hooks, th1-setup, tcl and tcl-setup and neither mimetype-override nor mimetype-tile, and fossil version -v reports that no Tcl 8.x library could be loaded; spec/guards/documents_spec.cr renders ROADMAP.md; crystal run ci.cr green, 111 examples. check-in: 99287f70fe user: opus-ocx tags: audited, ocx2 | |
| 00:40 | Open a job's report without blocking, so a FIFO cannot stall its result Intent: Act on agent-sol's fail verdict on 8ef3cd9765 (685bc1cb29): Job.read_inside opened a report before checking that it was a regular file, so a background writer that swapped the chosen report for a FIFO made the open wait for a writer and the failed job never posted its result, under precept A1 (Visible Evidence). Expression: Job.read_inside opens the report with O_RDONLY, O_NONBLOCK and O_CLOEXEC, checks on that descriptor that it is a regular file whose real path lies inside the tree, and only then reads; an open that fails, or a tree that is gone, gives nil as before. Demonstrates: spec/ocx/job_spec.cr swaps the chosen report for a FIFO with no writer and reads nil at once, and reads nil for a tree that no longer exists; run against the previous read_inside the same spec blocked the whole process until it was killed; crystal run ci.cr green, 239 examples, coverage 96.9%, 10 tools build. Leaf check-in: a46d73f5bf user: opus-ocx tags: ocx, ci | |
|
2026-10-03
| ||
| 20:41 | Write down the invite as steps a person and an agent can follow today Intent: Give stone minimal steps, with only Fossil's web pages on his side, to let a cloud agent review this branch under a login of its own, and make them the pattern our own workspaces follow (0a85704e1b), under precept C3 (Stated Change) and the virtues of Clarity and Accountability. Expression: docs/invite.md gives the person's part (make the login on /setup_uedit with its terms in contact info and the capabilities g, o, 2, 4 and p, plus i only for verdicts; hand over the one-time password directly; clear the capabilities at the closing turn or the date) and the agent's part (replace the password on /login, clone with the remote remembered, open its own thread, reply, record a verdict with tools/audit.cr, close), and keeps the seal tool for a hand-off that must cross the record; AGENTS.md's section for an agent outside an OCX workspace points to it. Demonstrates: each curl and fossil step was run on 2026-10-03 against a local fossil server 2.27: an admin thread made through /forume1, the guest's password changed through /login with the old one then refused, a reply posted through /forume2 attributed to the guest, and a clone syncing with the new password remembered; spec/guards/documents_spec.cr renders docs/invite.md; crystal run ci.cr green, 95 examples. check-in: 102d703bdb user: opus-ocx tags: audited, ocx2 | |
| 11:34 | Restore the founding axioms and virtues to README Intent: Honor stone’s clarification that the plain-language function summary should retain the original philosophy in README. Expression: Restore the five axioms and four virtues, and explain how they relate to PLAN’s guarded precepts. Demonstrates: fossil test-markdown-render README.md renders the five axioms and four virtues as ordered lists; crystal run ci.cr green: 239 examples, 96.9% coverage, 10 tools build. check-in: b1f6f7229a user: agent-sol tags: ocx, ci | |
| 05:27 |
Correct the forum-session evidence citation
Intent: Repair the audited forum-session parity row so its direct HTTPS example identifies the session post. Expression: Replace the stone question citation with the opus-ocx sandbox post in PLAN.md. Demonstrates: fossil artifact on d20ac2297e, 7fcc414b25, b9400cb4bc, a6ccb4101d and 7d82cdb526 identifies the authors and contents; crystal run ci.cr green: 239 examples, 96.9% coverage, 10 tools build. check-in: a92173c1a7 user: agent-sol tags: ocx, audited, ci | |
| 05:23 | Record the live forum session in the parity contract Intent: Replace the forum-session row's stale specified status with evidence of a live served session, while leaving served-skin acceptance to the maintainer. Expression: Cite the console launch, direct HTTPS turn, and cross-thread listener and reply in PLAN.md; mark the row demonstrated. Demonstrates: Fossil forum artifacts d20ac2297e, 7fcc414b25, a6ccb4101d, and 7d82cdb526 exist in the clone; crystal run ci.cr green: 239 examples, 96.9% coverage, 10 tools build. check-in: 094cacaf84 user: agent-sol tags: ocx, audited, ci | |
| 05:03 | A job reads each report through one handle checked inside its tree Intent: Answer agent-sol's fail verdict on 877097e6b9 (48f324417a): reports were checked when chosen but read later by path, so a report replaced by a link out of the tree between the two was posted, as a background writer left by the job could do. Expression: Job.read_inside opens the report once, requires that open handle to be a regular file whose real path (read from /proc/self/fd) lies inside the tree's real path, and reads from that same handle; Job.result uses it and says a report it will not read is "no longer a file inside the tree" instead of posting it. Demonstrates: spec/ocx/job_spec.cr "reads a report through a handle checked inside the tree, so a later swap posts nothing from outside": with the previous implementation the posted result contained OUTSIDE_SECRET after the swap; now it does not, and read_inside refuses the swapped link, a climbing path and a vanished file; crystal run ci.cr green: 239 examples, coverage 96.9%, 10 tools build. check-in: 8ef3cd9765 user: opus-ocx tags: ocx, audited, ci | |
| 04:53 | A job's report paths are confined to its tree, links included Intent: Answer agent-sol's fail verdict on 18b8084c8f (6f04d6dc82): a job definition's report path such as `../outside.txt` made Job.reports return it, and Job.result read that file from outside the checkout and posted it to the record; a job's reports go onto a record others read. Expression: Job.reports keeps only files whose real path, after following links, lies inside the tree's own real path; a path that climbs out, a link to a file outside, and a path through a linked directory are passed over. Demonstrates: spec/ocx/job_spec.cr "reads no report file from outside the tree, by a path or a link": on the previous implementation it returned ["../outside.txt", "reports/inside.txt", "reports/link.txt", "up/outside.txt"], now only ["reports/inside.txt"]; the existing reports example still passes; crystal run ci.cr green: 238 examples, coverage 96.9%, 10 tools build (one earlier run reported the coverage ratchet broken and did not repeat). check-in: 877097e6b9 user: opus-ocx tags: ocx, audited, ci | |
| 04:15 | Accept the live CI mode that ci.cr advertises (cherry-pick of 873fc0fb59) Intent: Bring agent-sol's requested fix (5a769563af, e0e6a80629) for fdb2b84ecd to the ocx branch: `crystal run ci.cr -- live` was advertised but refused as an unknown mode, under precept C7 (Pinned Interfaces). Expression: Cherry-picks 873fc0fb59, audited pass by agent-sol on ocx-human-feedback: ci.cr accepts `live` and names it in its refusal; spec/guards/interfaces_spec.cr checks every mode ci.cr's usage advertises is accepted. Demonstrates: spec/guards/interfaces_spec.cr fails on the previous ci.cr with ["live"] and passes with this one (shown at 873fc0fb59); crystal run ci.cr on ocx with this change: green, 237 examples, coverage 96.9%, 10 tools build. check-in: 4a59d13ce6 user: opus-ocx tags: ocx, audited, ci | |
| 04:08 | Accept the live CI mode that ci.cr advertises, with a guard tying usage to the accepted modes Intent: Answer agent-sol's fail verdict on fdb2b84ecd (5a769563af): ci.cr's usage advertises `crystal run ci.cr -- live` and includes live specs for it, but the accepted-mode list omitted `live`, so the advertised command exited 1 with "unknown mode live", under precept C7 (Pinned Interfaces). Expression: ci.cr accepts `live` beside `host` and `merge-gate` and names it in its refusal; spec/guards/interfaces_spec.cr checks that every mode ci.cr's usage comment advertises is in its accepted list. Demonstrates: spec/guards/interfaces_spec.cr fails on the previous ci.cr with ["live"] unaccepted and passes with this one; `crystal run ci.cr -- live` now runs the live specs instead of refusing the mode (its Codex conformance example then failed, as the live tag expects when no logged-in Codex is available); crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. check-in: 873fc0fb59 user: opus-ocx tags: audited, ocx-human-feedback | |
| 04:03 | Describe persistent units and a live stream of each run's input and output Intent: Record stone's design (95605bfa78): "the daemon opens an sse channel that is a tee to stdin/stdout of persistent agents so that individual turns and tool calls can be observed. there are agent runs that should run to completion, but i find that holding some open provides some improved continuity", under precept C7 (Pinned Interfaces). Expression: ARCHITECTURE.md: a unit either completes (one run to its end) or stays (one agent process kept open, fed each new turn on its standard input); the daemon tees the child's standard input and output to a live server-sent-event stream for watching, while the record stays the account of the work; a new "Observable runs" function. USAGE.md adds "Watch a running unit" (CGI live stream, proposed `ocx watch`, no MCP form, with the reason) and says a unit's turns go to a fresh run or its open agent. README.md's summary drops "runs one confined agent turn". Demonstrates: spec/guards/size_spec.cr (ARCHITECTURE.md 212 lines, USAGE.md 171) and spec/guards/naming_spec.cr pass; crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. Where the stream is served stays open: a local fossil 2.27 server with --extroot held a CGI extension's output until it exited (three events written two seconds apart arrived together at six seconds), so it cannot pass through the console's /ext pages as they are. check-in: 1c0f488c1a user: opus-ocx tags: audited, ocx-human-feedback | |
| 04:00 | Settle the capability contract and label USAGE.md as the intended interface Intent: Answer agent-sol's fail verdict on 3195749108 (0e827589fb): ARCHITECTURE.md promised a CGI page, CLI command and MCP tool for every capability while USAGE.md leaves some absent, and USAGE.md presented `ocx` as an available command, under precept C7 (Pinned Interfaces). Expression: ARCHITECTURE.md says each capability has one definition reached through whichever of CGI, CLI and MCP its caller can use, with the reason stated where one is absent, and says it describes the system being built; USAGE.md opens by saying today's code has the CGI pages and separate CLI programs but not yet the `ocx` command or the MCP tools, and marks those two entry points as intended. Demonstrates: spec/guards/size_spec.cr (ARCHITECTURE.md 201 lines, USAGE.md 162) and spec/guards/naming_spec.cr pass; crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. check-in: c6f5c1399d user: opus-ocx tags: audited, ocx-human-feedback | |
| 04:00 | Bound Markdown in every project directory under precept C1 (File Boundedness) Intent: Answer agent-sol's fail verdict on c9094aa451 (0e827589fb): C1 promises every Markdown file, but the guard checked only the root and docs/, so a 401-line file under spec/fixtures passed, under precept C1 (File Boundedness). Expression: spec/guards/size_spec.cr scans Markdown in every directory of the project except the installed shards under lib/, and a new example refutes that boundary in a scratch tree. Demonstrates: spec/guards/size_spec.cr: 4 examples pass; the new example finds a 401-line spec/fixtures/long.md and passes over a 401-line lib/some_shard/README.md; a 401-line spec/fixtures file added to this checkout for the check made the guard fail, then was removed; crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. check-in: a35ebceced user: opus-ocx tags: audited, ocx-human-feedback | |
| 03:51 | Move OCX's function into ARCHITECTURE.md and describe its interfaces in USAGE.md Intent: Answer stone's asks on the Review ocx branch thread (23c9ccedd3: "most of the details need to move into an ARCHITECTURE.md and just describe the function of OCX in a way where the 4 causes are answered in a practical and simple and compelling way (without the philosophy)"; 32386fdd11: "a USAGE.md that provides a high-level view of the cgi, cli and mcp interfaces, then describes the functions in terms that apply to all three"), under precept C1 (File Boundedness) and precept C7 (Pinned Interfaces). Expression: ARCHITECTURE.md answers what OCX is for, what it is made of, how its parts are arranged (with README's diagrams and identity table) and what makes them act, then lists its functions; README.md keeps the objective, building blocks, axioms, virtues and history, with a short Functionality summary linking both documents; USAGE.md defines each function once, with its CGI page, CLI command and MCP tool or the reason one is absent, taking `ocx run` and the ocx.* tools from PLAN.md's work-unit contract and marking other CLI names as proposed; PLAN.md's authority lines and AGENTS.md's reading list name the new documents; spec/guards/naming_spec.cr also checks them. Demonstrates: spec/guards/size_spec.cr (every Markdown file within 400 lines: ARCHITECTURE.md 197, USAGE.md 157, README.md 154, PLAN.md 386) and spec/guards/naming_spec.cr pass; crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. check-in: 3195749108 user: opus-ocx tags: audited, ocx-human-feedback | |
| 03:47 | Bound Markdown files to 400 lines under precept C1 (File Boundedness) Intent: Keep each design document readable whole, as stone asked on the Review ocx branch thread (6c394893f3: "maybe md files should be under 400 lines as well?"), under precept C1 (File Boundedness). Expression: spec/guards/size_spec.cr adds an example over the project's own Markdown (top-level *.md and docs/**/*.md, not the shards under lib/); PLAN.md's C1 row names Markdown files. Demonstrates: spec/guards/size_spec.cr passes with every project Markdown file within 400 lines (PLAN.md is the longest at 385) and fails on a 401-line docs/ file added for the check and then removed; crystal run ci.cr keeps the three historical guard failures 9a9a7102b7 reports and no others. check-in: c9094aa451 user: opus-ocx tags: audited, ocx-human-feedback | |
|
2026-10-02
| ||
| 11:12 |
Propose one CGI owner for each person-facing CLI tool
Intent: Make the maintainer's request for CLI and CGI alignment reviewable before changing routes or permissions. Expression: docs/cli-cgi-alignment.md inventories the tool and page gaps, proposes an option-level contract and guard, and names the host-entrypoint boundary for review. Demonstrates: docs/cli-cgi-alignment.md records the current tool and page mapping; crystal run ci.cr is green with 236 examples, 96.9% coverage, and 10 tools built. check-in: f292e15dd6 user: sol-ocx tags: ocx, audited, ci | |
|
2026-10-01
| ||
| 22:41 |
A workspace the console makes clones the repository's public URL, with autosync on
Purpose: stone, 013bdbdf7c, choosing the https remote: "Each new workspace would then get `fossil remote` set to that URL, with its login and password remembered, and autosync on. The console would pass the URL in explicitly. If the URL is missing or doesn't resolve, a launch is refused with a message saying so; it never quietly falls back to the file." Since 393e0757a7 a workspace's remote was the repository file, which is outside the sandbox, so a session could not sync, pull or push during its turn. Shape: - Launch.public_url reads the repository's email-url. It refuses one that is unset, not http(s), or whose host does not resolve. - Add a workspace clones that URL; Preview and Add refuse first, before any login, thread or definition is made. - Launch.start refuses before anything is on the record when the workspace is still to be made and there is no usable URL. - The daemon passes `--remote URL` to `workspace serve` only for a workspace still to be made (Launch.prepare); a made workspace keeps its own remote. A refusal there is said on the record. The repository file is kept in the workspace as launch.repository, which an adopting daemon reads; sessions launched before fall back to their --remote. No new flag, so Restart on the previous release still works. - Workspace.create turns autosync on for a URL clone and leaves it off for a file clone. - The session prompt says autosync pushes each commit; AGENTS.md, interfaces.md, facts.json and the survey say the same. Intent: a session's commits and posts reach the record as it makes them, from inside its sandbox. Expression: src/ocx/launch.cr, src/ocx/daemon.cr, src/ocx/cgi.cr, src/ocx/workspace.cr, src/ocx/session.cr, AGENTS.md, docs/. Demonstrates: spec/ocx/public_url_spec.cr, spec/ocx/workspace_spec.cr, spec/ocx/daemon_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/session_spec.cr, crystal run ci.cr: - email-url unset, ftp, or not resolving is refused; a trailing slash is dropped; - a URL clone's own `fossil commit` is in the served repository at once; a file clone has autosync off; - with no email-url a launch is refused before posting, and the daemon's refusal is said on the record; - the daemon passes the URL and keeps the repository, and an adopting daemon finds it; - the console's Add refuses without a URL and, with one, makes a workspace whose remote is that URL, cloned over HTTP from a served fixture; a later launch passes no --remote; - CI green, 236 examples, coverage 96.9%, 10 tools build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: 0160971667 user: opus-ocx tags: ocx, audited, ci | |
| 21:20 |
AGENTS.md says how a session's clone syncs with a file remote and with a URL
Purpose: stone, on 58d537bd67: "please correct the documentation." AGENTS.md told every session to run `fossil sync -R ../clone` and said the task tool syncs. With the repository file as the remote (393e0757a7) neither works inside the sandbox, which led opus-ocx to report sessions as unable to sync. The host syncs the clone before and after each turn and posts kept turns from ~/outbox (Session.start, Outbox). Intent: the instructions a session reads match how its work reaches the repository, for both kinds of remote. Expression: AGENTS.md. Demonstrates: crystal run ci.cr: green, 235 examples, coverage 96.9%, 10 tools build. The behavior described is pinned by spec/ocx/session_spec.cr (d26240540f) and spec/ocx/workspace_spec.cr (file clone syncs from the host, kept turns posted once). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: eed22764dd user: opus-ocx tags: ocx, audited, ci | |
| 21:12 |
A session on the repository file is told the host pushes its commits, not `fossil sync`
Purpose: stone, on 58d537bd67: "i think you must be forgetting to/how to sync." The session prompt said "commit there, and `fossil sync` pushes it" for every workspace. A workspace made from the repository file (393e0757a7) has no repository in its sandbox, so `fossil sync` there fails; opus-ocx followed the prompt, saw it fail, and wrongly reported sessions as unable to sync. The host syncs the clone before each turn and pushes after it (Session.start). Intent: a session is told how its work actually reaches the repository. Expression: src/ocx/session.cr (Session.syncing), spec/ocx/session_spec.cr. Demonstrates: spec/ocx/session_spec.cr, crystal run ci.cr: a workspace on a served URL is still told `fossil sync` pushes; one on the repository file is told the host pushes when its turn ends and not to run `fossil sync`; CI green, 235 examples, coverage 96.9%, 10 tools build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: d26240540f user: opus-ocx tags: ocx, audited, ci | |
| 17:00 | Link jobs to their source design and show the session home listener Intent: Keep cross-project design links valid and identify the record a session always listens to. Expression: Derive the design repository from release source metadata and show the home record beside the count of additional listeners. Demonstrates: spec/ocx/console_workspaces_spec.cr; crystal run ci.cr (234 examples, coverage 96.9%, green). check-in: 2636052bf4 user: sol-ocx tags: ocx, audited, ci | |
| 16:52 | Show each job run's result on its page Intent: Let a reader distinguish a job's result from the lifecycle of its session. Expression: Derive the latest request and matching result from the job's record and show them beside Run. Demonstrates: spec/ocx/console_workspaces_spec.cr; crystal run ci.cr (234 examples, coverage 96.9%, green). check-in: fb54085999 user: sol-ocx tags: ocx, audited, ci | |
| 16:45 |
Give sessions and jobs their own action pages
Intent: Put each console action on the page for the type it affects and explain what a job run does. Expression: Add New session and Job pages, link to them from workspace and session pages, explain ref checkout and action effects, move render timing to the bottom, and reject Run without a job definition. Demonstrates: spec/ocx/console_workspaces_spec.cr; crystal run ci.cr (234 examples, coverage 96.8%, green). check-in: 3a973825af user: sol-ocx tags: ocx, audited, ci | |
| 15:07 |
A job request held during any run is run when it ends; the job's log says what it did
Purpose: stone, 2026-10-01: the first bq2 build "failed, falsely reported success, and provided a log that isn't very useful". The session the console started (a Start, which completes with nothing to do) ran while the request arrived. The daemon held the request "for the run going on". At that run's end it relaunched only a run that a request had started, so the request was dropped, though the Run form had said its result would be the reply. Shape: - At a run's end, a request held during it starts the job again, whatever started that run. - The job's log now says which request it runs, by whom, at which ref, and where the output goes, then the result's first line. - A session that completes says "completed: nothing waits on it". Origin: opus, on the maintainer's report. Intent: a request is never silently dropped, and the log shows what ran. Expression: src/ocx/daemon.cr, src/ocx/job.cr, src/ocx/serve.cr. Demonstrates: spec/ocx/job_spec.cr, crystal run ci.cr: - a request held during a run the console started (Server#launch) runs when that run ends; without the change it is dropped and the spec times out; - the job's log lines print; - CI green, 234 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: f03c7c279b user: opus tags: ocx, audited, ci | |
| 14:46 |
A workspace's fossil runs with the workspace as HOME, stated, not inherited
Purpose: stone, 2026-10-01: Add the workspace failed again, "fossil clone failed: cannot locate home directory". Fossil's server gives its CGI no HOME. Dropping the CGI variables (666c151f57) still left fossil depending on what it inherited; the maintainer: "environment variable dependencies rear their ugly head again ... we should manage them better". Shape: - Workspace.fossil now requires `home`. It always runs fossil in a clean environment (HOME and PATH only) whose HOME is the workspace itself, as its sandbox gives its sessions. - Workspace.create (clone, remote, settings), the remote and peer setup, and Workspace#sync all state it. - So do the command runner's and the job's update, info and tag. - WorkTree.fossil runs with the tree's workspace (its parent) as HOME. - A clone over HTTP names its admin user (-A LOGIN), as a file clone already did: a clean environment has no $USER to fall back on. - The same fossil commands now run the same way from the console, the daemon or a terminal. Origin: opus, on the maintainer's report. Intent: no fossil call that touches a workspace depends on its caller's environment. Expression: src/ocx/workspace.cr, src/ocx/worktree.cr, src/ocx/command_runner.cr, src/ocx/job.cr. Demonstrates: spec/ocx/workspace_spec.cr, crystal run ci.cr: - a workspace is created from a process with a CGI request's variables and no HOME, XDG_CONFIG_HOME or FOSSIL_HOME; - before this change the spec failed with the maintainer's error, first at the clone, then at the tree's `fossil open`; - CI green, 234 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: bbbd12aa26 user: opus tags: ocx, audited, ci | |
| 14:36 |
Terminate works on any workspace, whatever state its making left it in
Purpose: stone, 2026-10-01: the failed Add left "cruft", and the workspace list "doesn't provide a Delete button". Then: "a workspace clean-up is a workspace clean-up, be it made by an errant flow or not". The workspace page refused a directory with no binding, and so did terminate. Shape: - Terminate is one flow for every workspace. Each step applies to what the workspace has: - refused while a process runs in it; - synced when it is bound; - refused while its clone, if it has one, holds artifacts the repository lacks; - its end noted on its record, if it has one; - then removed. - The workspace page renders for any workspace directory. It shows its definition and a job's Run form when it has a record, its session or the start form, and always Terminate. Origin: opus, on the maintainer's report. Intent: whatever made a workspace, it can be cleaned up the same way. Expression: src/ocx/cgi.cr, src/ocx/cgi_workspaces.cr. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - a workspace directory with no binding is listed, its page offers Terminate, and Terminate removes it; - a second Terminate says there is none; - bound workspaces terminate as before; - CI green, 234 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6912141fbe user: opus tags: ocx, audited, ci | |
| 14:23 |
No fossil started by OCX inherits a CGI request's variables
Purpose: stone, 2026-10-01: Add the workspace failed with "addworkspace: fossil clone failed: no such file: clone". The console runs Workspace.create in its own process. Fossil, finding GATEWAY_INTERFACE, answered `fossil clone` as a CGI request for a file named "clone". Provisioning had the same failure once and was fixed at its own calls (home:); every other path still inherited the request. Shape: Fossil.run, the one place fossil is started, unsets the CGI variables (Fossil::CGI_ENV: GATEWAY_INTERFACE, REQUEST_METHOD, QUERY_STRING, PATH_INFO, SCRIPT_NAME, REQUEST_URI, CONTENT_LENGTH, CONTENT_TYPE) whenever it keeps this process's environment. With `home` it already started from a clean one. Origin: opus, on the maintainer's report. Intent: fossil always runs as a command, whoever starts it. Expression: src/ocx/fossil.cr. Demonstrates: spec/ocx/workspace_spec.cr, crystal run ci.cr: - a workspace is created from a process carrying a CGI request's variables; - without the fix the spec fails with the maintainer's exact error, "fossil clone failed: no such file: clone"; - CI green, 234 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 666c151f57 user: opus tags: ocx, audited, ci | |
| 14:12 |
Add a workspace gets a Preview button that shows the definition it would post
Purpose: the maintainer, 2026-10-01: "I want to see the built JSON structure before I click Add the workspace", and "a preview button is desired, especially since I don't get any preview of the contents of workspace/plain". No JavaScript. Shape: - The form has two buttons, Preview first, so Enter in a field previews. - Preview re-renders the form as sent and shows the workspace definition it would post, the template's sandbox included. It also says whether the login would be used or created, and whether the definition goes on a new thread (its title) or a named record. It writes nothing: no account, no thread. - The definition is built by one function, CGI.planned, for both Preview and Add. A record that no post or ticket starts with is refused before anything is created; Add used to refuse it only after. - The Add a workspace page moves to its own file, cgi_add_workspace.cr, keeping cgi_workspaces.cr under the size limit. Origin: opus, on the maintainer's terminal request. Intent: see exactly what Add the workspace will post before it does. Expression: src/ocx/cgi_add_workspace.cr, src/ocx/cgi.cr, src/ocx/cgi_workspaces.cr, docs/design/modules.json, docs/survey/functionality.json. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - the Preview button is on the form; - a preview with the claude template shows its definition with the workspace read-write, says it uses scout on a new thread "Build", and keeps the form; - a new login is "would create"; - a named record is shown, and a bad record is refused; - no thread and no login are created; - CI green, 233 examples, coverage 96.8%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 33d6466f64 user: opus tags: ocx, audited, ci | |
| 04:35 |
Jobs: a mechanical session that runs on request, for bq2 builds
Purpose: stone, 2b138b20de: "prepare the jobs design to be able to handle the need to run bq2 builds. on failures, the log should be saved to the thread"; the design 2a5f7eee4d, approved in 5dd397f158 with the report and the log's last 100 lines on the thread and the whole log kept in the workspace. Shape: - A session definition may say "on": "request" (a job) and name "report" paths in its tree; its runner is `job`. - A request is a post on the job's record, `Run: LOGIN at REF`, by anyone. The job's login is its name; a sentence's punctuation after REF is dropped. - The job's session takes the oldest request without a result of its login's. It updates the tree to REF, then runs the command in the sandbox with its output in jobs/REQUEST.log. - It replies to the request with `OCX result for REQUEST:`. - A pass is one line: the check-in and how long the run took. - A failure adds the exit, the last 100 lines of each file the run wrote under the report paths, and the log's last 100 lines, each fenced so nothing printed ends the fence. - A REF that cannot be checked out fails without running. - Every request gets a result, so none is taken twice. - The daemon starts a job's session on its request unless one runs. A request during a run waits, and the run's end starts it again only then, so a run stopped with requests unanswered is not restarted by itself. A run that ends well is not noted, since its result is its report. - Runner#work_waits? replaces Serve's CommandRunner checks, so serve treats a command and a job alike. - A job workspace's page has a Run form: a branch from the ten most recent, or any check-in. It posts the request as the job's login, on the viewer's behalf, and hands it to the daemon. - Design recorded in sessions.json, events.json and the session schema; the survey and modules list the job. Origin: opus, on 2b138b20de and 5dd397f158. Intent: a long build runs when asked, one at a time, and its failure is readable on the thread without the record keeping megabytes of log. Expression: src/ocx/job.cr, src/ocx/daemon.cr, src/ocx/serve.cr, src/ocx/runner.cr, src/ocx/command_runner.cr, src/ocx/runners.cr, src/ocx/definitions.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, tools/workspace.cr, docs/. Demonstrates: spec/ocx/job_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - requests are read and others' are ignored; - a failing run replies with its report file and log tail, keeps the whole log, and is answered once; - a bad ref fails without running; a pass is one line; - the daemon refuses a login with no workspace and a request off its record, starts one run, holds a request during it and runs it after, and does not run again unasked; - a foreign process makes a request wait; - the console's Run form posts the request; - CI green, 233 examples, coverage 96.8%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 18b8084c8f user: opus tags: ocx, audited, ci | |
| 00:31 |
The Daemon page says when the daemon runs the current release
Purpose: stone, 2cb14ff176: "the daemon page doesn't say it is current, but the version code matches". The line flagged only a release that differed, so a match read as nothing said. Shape: the daemon's line reads "release R, the current release" when it matches, and "release R, not the current release (C)" when it doesn't. Origin: opus, on 2cb14ff176. Intent: the page says outright whether the daemon is up to date. Expression: src/ocx/cgi_workspaces.cr. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: a daemon on the current release "one" says so; one on "zero" is flagged; CI green, 227 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 3e94c5df5e user: opus tags: ocx, audited, ci | |
|
2026-09-30
| ||
| 16:54 |
The daemon gets its own page with Stop and Start; the nav drops its numbers
Purpose: the maintainer, 2026-09-30: the daemon's line "should show on the main page or on it's own page and provide a stop/start, which it does not", and of the nav, "get rid of the numbers. they don't mean what you think". Shape: - A Daemon page (?view=daemon) in the setup nav shows what the daemon does, its status line and its log's end. It has one button: Stop while it runs, Start while it doesn't. The Sessions page no longer carries the line. - Stop sends the daemon TERM and leaves a mark beside its lock (Daemon.stop). No page or hook starts it again until Start, which clears the mark and starts it (Daemon.start). Its sessions run on, each in its own session. - A launch while it is stopped is refused, saying to start it on the Daemon page. Health and /state say whether it was stopped from the console. - A daemon that started before it wrote its release says "release unknown". - The nav reads Hook, Agent logins, Workspaces, Sessions, Daemon, without numbers, and the messages and docs that quoted them follow. Origin: opus, on the maintainer's terminal requests. Intent: the daemon is seen and controlled where it belongs, one page. Expression: src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, src/ocx/daemon.cr, src/ocx/launch.cr, src/ocx/host_state.cr, docs/. Demonstrates: spec/ocx/console_workspaces_spec.cr, spec/ocx/daemon_spec.cr, spec/ocx/agents_spec.cr, crystal run ci.cr: - the page shows not running with Start; - a stand-in holding the lock is stopped by Stop and ends on TERM; - then the page says stopped, and nothing starts it; - Start clears the mark; - a launch while stopped is refused with the way on; - a running daemon's line, and an older release's flag, move to the page; - CI green, 227 examples, coverage 96.5%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 398e42bdf8 user: opus tags: ocx, ci | |
| 16:30 |
Step 6: restart stopped sessions on a chosen release, and re-exec the daemon in place
Purpose: step 6 of the agreed build order (374b12e82e). The maintainer, 14d8477f84: no automatic relaunch, but "a CGI-based trigger to restart all stopped persistent session workers", with a release to choose so a bad one can be escaped. Accepting a release re-execs the daemon in place, keeping its pid and its children (027446fbe9). Shape: - Restart: - The Sessions page shows a Restart button when some of this project's stopped sessions, started from the console, have a definition that stays running. One choice goes with it: the current release, or the previous one. - The restart action puts a new launch note on each such session's record, naming the release when one is chosen, and refuses a release that is neither current nor previous. - Launch.start takes the release and ends the note with ", on release NAME"; the daemon then runs that release's `workspace`, or the current one when this host lacks it. - Re-exec: - Accept and Rollback hand the daemon `reexec`. bin/ocxd's callback execs the current release's program in place, keeping its pid. - On every start the daemon adopts the `workspace serve` processes it is the parent of, reading repository, login and record from their command lines, which Processes::Found now carries. - It reaps spawned and adopted children alike through the runtime's reaper (Crystal::System::SignalChildHandler.wait), and reaps once at start in case one ended during the exec. Children are kept by pid. - After a re-exec the daemon's standard output is already closed, so "ready" is not required then. - Definitions.latest_session? reads a refused definition as nil for a page listing many. Design and survey marked built. Origin: opus, on 374b12e82e and 14d8477f84. Intent: nothing restarts by itself, one button restarts what should stay, on a release you choose; a new release reaches the daemon without losing its sessions. Expression: src/ocx/daemon.cr, tools/ocxd.cr, src/ocx/launch.cr, src/ocx/sessions.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, src/ocx/processes.cr, src/ocx/definitions.cr. Demonstrates: spec/ocx/daemon_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/definitions_spec.cr, crystal run ci.cr: - re-exec calls what the program gives; - a launch naming release "two" runs two's program and says so; - a second daemon adopts that child and reaps its end onto the record; - the Restart button offers the previous release, restarts on it and on the current one, refuses an unknown one, and restarts nothing once the definition completes; - a refused session definition reads as nil; - CI green, 227 examples, coverage 96.5%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: bc12df02a0 user: opus tags: ocx, ci | |
| 16:16 |
Step 5: CI launched per check-in, one run at a time
Purpose: step 5 of the agreed build order (374b12e82e). CI completes per check-in (257b282d36, after stone's "ci should not stay, should it?", 3f6abf8cc4), with agent-sol's point (d38004971c) that green and red completion be guarded. Shape: - The daemon's `post` reads a check-in, as well as a post. For each workspace of the project listening to that check-in's branch (listening/branch-NAME), whose login has a kept password and a binding: - it launches the workspace's session, which completes: CommandRunner tests the newest untested check-in, tags it, and exits; - while its own run is going, it only says so, and the run's end looks again; - beside a process not its own, it says the branch waits. - When a check-in's run ends well, the daemon launches again only if a listened branch's newest check-in is untested and isn't the one just tested. A run that leaves its check-in untested therefore can't launch itself over and over. - A check-in's run reports with its ci tag: only a run that failed says its end on the record, so the CI thread isn't filled with notes. - Daemon::Server#untested reads the newest check-in of each listened branch with no ci tag of the login's, from the served repository. - Design and survey marked built. Needed on the host after acceptance (not done here): ci-ocx's session definition on f4054411ee and the session/ci template must say "completes", and the old `stays` ci-ocx serve must be stopped. Until then its process holds the workspace and the daemon lets trunk wait. Origin: opus, on 374b12e82e, 257b282d36 and d38004971c. Intent: no idle CI process; each check-in's run has its own start, result and end. Expression: src/ocx/daemon.cr, docs/design/events.json, docs/survey/functionality.json. Demonstrates: spec/ocx/daemon_spec.cr, crystal run ci.cr: - a check-in launches ci's session; - a second during the run launches nothing, and the run's good end launches the newer one, the same one never twice; - a good end says nothing; a failed one says "exit 1" and launches nothing; - a tagged check-in is not untested; - an unprovisioned listener is passed over, and a foreign process makes trunk wait; - another branch launches nothing; - CI green, 226 examples, coverage 96.4%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: fed6e1b31b user: opus tags: ocx, ci | |
| 16:08 |
Step 4: a session's lifetime, and its own record
Purpose: step 4 of the agreed build order (374b12e82e), on the lifetime design (257b282d36, recorded in a2743b4924). The maintainer asked to continue the plan to its end, to review then. Shape: - A `completes` session ends once its work is done (Serve.done?). A command's is done when nothing waits. A model's is done once its report is on the record, unless its own latest post is a question, which keeps it waiting until answered. `workspace serve` passes the definition's lifetime to Serve.run; a `stays` session waits as before. - A session works and reports on its own record: `serve` on another record than its workspace's works there by focus. The workspace stays bound to its own record, where its definition and branch listeners are read. Workspace#rebind is gone. - The workspace page's Start form takes the record to work on, by completion over the ten most recent, defaulting to the workspace's own. The launch resolves it and refuses a prefix naming none. - The Sessions list names the record the session works on, from its ~/run. A stopped `completes` session with a report shows "completed: SUMMARY". - The launch action moved to cgi_workspaces.cr, keeping cgi.cr within 400 lines. "end on its report" is built in events.json. Origin: opus, on 374b12e82e and 257b282d36. Intent: a short session costs one Start, works on the record it was started for, and ends by itself. Expression: src/ocx/serve.cr, tools/workspace.cr, src/ocx/workspace.cr, src/ocx/sessions.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi_agents.cr, src/ocx/cgi.cr. Demonstrates: spec/ocx/serve_spec.cr, spec/ocx/workspace_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - a completing session's report ends it without waiting; - its own open question makes it wait; - a staying session waits either way; - focus leaves the binding on disk; - a launch on another ticket puts its note there, and the workspace stays bound; a prefix naming nothing is refused; - the page shows "completed:" with the report; - CI green, 225 examples, coverage 96.4%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6c700187df user: opus tags: ocx, ci | |
| 15:44 |
The console reports the daemon's health
Purpose: the maintainer, 2026-09-30: "cgi should report daemon health". Shape: - Daemon.health: whether one runs (its lock), its pid, when it started, the release it runs, the sessions it is the parent of (from the process table), and the end of its log. - The daemon writes the release it started from into its lock (Daemon.claim), resolved through `current` when it starts, since its command line only says `current`. - The Sessions page starts with one line, e.g. "Daemon. Running since TIME (pid N), release R; the parent of K sessions". It flags a daemon on another release than the current one. When the daemon isn't running, it says each page starts it and shows the end of its log. - /state gains a `daemon` object, and its schema follows. Origin: opus, on the maintainer's request in the terminal. Intent: the daemon's state is seen where sessions are, and read by Stone. Expression: src/ocx/daemon.cr, tools/ocxd.cr, src/ocx/cgi_workspaces.cr, src/ocx/host_state.cr, docs/design/state.schema.json. Demonstrates: spec/ocx/daemon_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - not running, health says so and the page shows its log; - running, health gives this pid, release "one" and its child session, and the page gives the line; - a daemon on release "zero" is flagged as not the current release; - CI green, 224 examples, coverage 96.3%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6cd4d2edae user: opus tags: ocx, ci | |
| 15:06 |
Step 3: the daemon launches sessions as its children; Start and Stop go through it
Purpose: step 3 of the agreed build order (374b12e82e), on the daemon design (14d8477f84, recorded in aa3bbde87a). The console no longer spawns sessions: a launch is a post, and one parent sees every start and end. Shape: - src/ocx/daemon.cr and bin/ocxd: - one per agents root, by a lock in AGENTS/ocxd holding its pid, with its pipe beside it; - the console on every page, and the wake hook on every run, make sure it runs. `ensure` returns when the new daemon prints "ready" once its pipe is open, or when it ends because another holds the lock: an event, with no timer and no hang; - it reads `post REPOSITORY HASH` and `stop LOGIN KEY`. - A launch note by an agent login starts the release's `workspace serve` as the daemon's child in its own session (setsid, clean environment), once per note: - beside a process it did not start, it launches nothing and says so on the record; - its own session there is stopped first, and the new one starts when that end is reaped; - each end is reaped and said on the record ("the session ended: exit N" or "stopped by signal TERM"). - Launch.start posts the definitions and the note, then hands the note to the daemon itself (the hook may be held by Fossil's backoffice). It refuses when another login's session serves the record, or when no daemon reads its pipe. It returns the note's hash. - Launch.stop: a session the daemon started is stopped by the daemon (its process group gets TERM); any other, such as a terminal session, is sent TERM here. Nothing waits: launch.cr's timer (PATIENCE) is gone. - Terminate refuses while a process runs in the workspace, instead of waiting for it to stop. - launch.pid is gone: a `workspace serve` with no ~/run yet is "starting". ~/run is believed only while the process table shows its pid there. - The hook hands every received artifact to the daemon. setsid's one call site is daemon.cr. ocxd is a release target. Origin: opus, on 374b12e82e and 14d8477f84. Intent: every session has one parent that puts each start and end on the record; the console only posts and asks. Expression: src/ocx/daemon.cr, tools/ocxd.cr, src/ocx/launch.cr, src/ocx/cgi.cr, src/ocx/sessions.cr, src/ocx/serve.cr, tools/wake.cr, shard.yml, docs/. Demonstrates: spec/ocx/daemon_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/serve_spec.cr, spec/ocx/processes_spec.cr, crystal run ci.cr: - one lock per agents root, with its pid; ensure starts a daemon program and returns on "ready" or on its end; - from the console, a launch note starts the release's `workspace serve` as the daemon's child, with only HOME and PATH, once per note; - a person's post, or an agent's that isn't a launch note, starts nothing; - a second launch replaces its own session and says the first's end; - beside another process, the launch is refused on the record; - Stop goes through the daemon, and its end is said; - another login's session on the record refuses the launch, and with no daemon the launch says it won't start; - Terminate refuses while a session runs; - CI green, 224 examples, coverage 96.3%, 10 tools build. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 72ce3dca69 user: opus tags: ocx, ci | |
| 14:37 |
task --write --after posts, then shows what the writer wasn't shown
Purpose: the maintainer, c22a865bc6: "I don't know that it makes sense for your tool to hold you off when you have a response posted. I suppose you might like to know, so you have a chance to revise, but I suspect that lesser models could really be confused by that." 9a8e14a17d made --write --after refuse. Shape: `task --write --after POST` posts the turn, then prints what others posted after POST that the writer hadn't been shown, with the next cursor, so it can answer or revise. Nothing it posted over is buried unseen, and no session is held off. An unknown cursor is still refused before anything is written. Usage and the survey follow. Origin: opus, on c22a865bc6. Intent: the writer always learns of what it posted over, without a refusal to handle. Expression: tools/task.cr, docs/survey/functionality.json. Demonstrates: spec/ocx/turns_spec.cr (Dispatch.after, unchanged), crystal run ci.cr: green, 223 examples, coverage 96.1%. By hand on this thread: the reply 62bb0bae40, written --after 202ac563af, was posted, then showed c22a865bc6 as not yet shown, naming it as the next cursor. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 4c15fa8cb0 user: opus tags: ocx, ci | |
| 14:30 |
task --after POST: wait and write against the last post the caller was shown
Purpose: agent-sol's review (d38004971c) never reached opus. A post waits only until the login's next post, and opus posted 20 seconds later. The maintainer, 2026-09-30: respond "if any post is newer than a specific post and isn't the one you generated", keeping it synchronous, with no mechanism for detecting what was read. Shape: - Dispatch.after gives what others wrote on the record after a post named by hash prefix, refusing an unknown one. The caller carries the cursor (the last post it was shown), so nothing records what was read. - `task WS --wait --after POST` returns what others posted after POST, or blocks until something comes. - `task WS --write --after POST` refuses while others posted after POST: it prints them and names the next cursor. A session can no longer bury a post it wasn't shown. - Without --after, both behave as before. Usage and the survey follow. Origin: opus, on the maintainer's suggestion in the terminal, agreed there. Intent: nothing posted after what a session saw is buried by its own next post. Expression: src/ocx/turns.cr, tools/task.cr, docs/survey/functionality.json. Demonstrates: spec/ocx/turns_spec.cr, crystal run ci.cr: after the post a login saw, the three posts its own answer had hidden from `waiting` still come; none after the latest, or after its own answer; an unknown cursor is refused. By hand on this thread, --after 104c163383: the wait printed d7da4387c8 and e5f02d246e, the write refused naming them, and ffffff was refused. CI green, 223 examples, coverage 96.1%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 9a8e14a17d user: opus tags: ocx, ci | |
| 14:08 |
Terminal sessions read as terminal, logins list their workspaces, and spec stand-ins can't leak
Purpose: agent-sol's review (d38004971c; audit fails on c4687c2937 and a2743b4924), and the maintainer's nits on /state: "for logins, workspaces should list the workspace keys, rather than just provide a count. Also, I think some of the running states are wrong." Shape: - A live terminal session in a workspace that an earlier console launch note names is still "at a terminal". The live process decides, through its run's runner (Sessions::TERMINAL), not the historical note, so Status reads it from the record and its mark. - A session with no ~/run takes its activity and since-when from its own mark (`task --mark`), not a default "idle". The Sessions list and /state read the same mark Status does. - A lone `task --wait` is someone listening, not a session: Processes.leader skips it. - Agents::Standing.workspaces is the list of keys. The Agent logins page and /state name them; the schema follows. - Spec stand-ins die with their parent (PR_SET_PDEATHSIG, and exit if already orphaned). Six from failed runs were left pausing in this sandbox, and the same would happen in CI's. - Design (a2743b4924's gap): a mechanical `completes` session's report is its ci tag, green or red, on the check-in it tested, with a red one also said on its own record. "launch a session" happens once per launch note, or per check-in on a listened branch. Origin: opus, on d38004971c and the maintainer's nits. Intent: /state and the pages say what really runs, as what. Expression: src/ocx/sessions.cr, src/ocx/processes.cr, src/ocx/status.cr, src/ocx/agents.cr, src/ocx/cgi_agents.cr, spec/support/fixture.cr, docs/design/. Demonstrates: spec/ocx/status_spec.cr, spec/ocx/processes_spec.cr, spec/ocx/agents_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: - the reuse case: an old launch note plus a live terminal sandbox and mark "working" gives "at a terminal" and status "working"; - the Sessions list reads the mark; - a wait alone stands for no session but is listening; - standings and the logins page name workspace keys; - CI green, 223 examples, coverage 96.1%. A stand-in whose parent died, died with it (checked by hand). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 15847cb426 user: opus tags: ocx, ci | |
| 13:23 |
Step 2b: the host's state as one JSON document at /state
Purpose: step 2 of the agreed build order (374b12e82e). The maintainer, c4521cbb97: "Exposing JSON endpoints for the CGI polled status of hooks, agent logins, workspaces, sessions, and listeners, is still necessary, being very helpful to Stone". Shape: - HostState.json derives one pretty document when asked, from the record, the workspace directories and the process table: - releases: current, next, previous; - hooks: each repository of the login group, and nodelay; - logins: capabilities here, password kept, workspaces; - workspaces: state, listening, pids; - sessions: state, activity, since, how started, release and whether current, listening, sharing, pid; - listeners: thread, ticket or branch. - Its shape is docs/design/state.schema.json. Times are UTC: it is data. - GET <repo>/ext/ocx/state serves it to a viewer who may see the setup pages, 403 to anyone else, and 404 when the repository isn't there. - The handler lives in cgi_workspaces.cr, keeping cgi.cr within 400 lines. "serve status as JSON" is marked built in events.json. Origin: opus, on 374b12e82e and c4521cbb97. Intent: one readable source of what the host is doing, for Stone and the console. Expression: src/ocx/host_state.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, docs/design/state.schema.json. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: the document meets its schema; it names releases current, next and previous, the fixture's login, the workspace's two pids, both sessions sharing it with their release and listening, and branch, thread and ticket listeners; /state answers an admin, refuses a reader and misses an absent repository; CI green, 223 examples, coverage 96.1%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 047213fe41 user: opus tags: ocx, ci | |
| 12:57 |
Step 2a: whether a session is listening, and a note when a post reached nobody
Purpose: the maintainer's "Continue." (104c163383) reached nobody. The terminal session's watch had just failed to restart, while the console and Stone showed it idle, as if ready. Agreed in d7da4387c8: listening as a state from the process table, and the hook noting a post that reached nobody. Shape: - Processes recognizes `task WORKSPACE --wait` (an absolute workspace path) as a wait on the wake pipe. Processes.listening? is true while a `workspace serve` or a wait is in the workspace. A wait, like a sandbox, is part of a session, not one. - Each session carries `listening`. The Sessions page says ", not listening" for a running session without one. Stone's status JSON gains "listening" for a running session. - Wake.deliver tells a write apart from an unheard pipe (no reader: ENXIO) and a failure. The hook reports all three. - For an unheard post, the hook runs Unheard.note: the pipe's agent login notes on the record "posted while LOGIN was not listening: POST by AUTHOR waits on the record". It is a note, so the post still waits. Only a person's post is noted, never an agent's, so two deaf sessions can't answer each other's notes. - Design: a decision in sessions.json, and "note an unheard post" in events.json; modules and survey updated. Origin: opus, on d7da4387c8. Intent: a session that cannot hear looks different from one that is ready, on the console, in Stone, and on the record. Expression: src/ocx/processes.cr, src/ocx/sessions.cr, src/ocx/status.cr, src/ocx/cgi_workspaces.cr, src/ocx/wake.cr, src/ocx/unheard.cr, tools/wake.cr, docs/design/. Demonstrates: spec/ocx/processes_spec.cr, spec/ocx/unheard_spec.cr, spec/ocx/wake_spec.cr, spec/ocx/status_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: my waiter in the listing fixture makes my workspace listening, and agent-sol's terminal without one is not; an unopened pipe is unheard and a missing one failed; a person's unheard post gets the note and still waits on the login, and an agent's gets none; status says listening false for a lone sandbox; the Sessions page flags "not listening" once the serve is gone; CI green, 223 examples, coverage 96.0%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 51fe5d7282 user: opus tags: ocx, ci | |
| 03:31 |
Every session in a workspace is listed, not only one
Purpose: stone, 96315e341c: "looks like the session list only finds 1 per workspace." A workspace was one session: when two processes ran there at once (opus-crobots had two serves), only one was shown or stoppable. Shape: - Processes.sessions lists the processes that each are a session in a workspace (serve, run or start, not a sandbox), oldest first. Found carries the record key its command line names. - Sessions.list gives one session per such process. Each is on the record its command names, running, with its release; all of them carry how many share the workspace. - The Sessions page flags each as "one of N sessions in its workspace". Its link, and its page's Stop, name it by pid. - Launch.stop with a pid stops that session, refused unless the process table shows it in the workspace. The design allows one session per workspace at a time; this makes a breach visible and fixable. Origin: opus, on 96315e341c. Intent: what runs is what the page shows, one row per session. Expression: src/ocx/processes.cr, src/ocx/sessions.cr, src/ocx/launch.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, docs/precepts/exceptions.json. Demonstrates: spec/ocx/processes_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: two sessions in one workspace are both listed, each linked by pid and flagged; the second's page shows its pid and carries it to Stop; Stop by pid ends that one and leaves the other, and refuses a pid outside the workspace; CI green, 222 examples, coverage 96.0%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: a6fe1583ac user: opus tags: ocx, ci | |
| 03:06 |
Times shown in the viewer's timezone, else US Eastern
Purpose: the maintainer, 2026-09-30: "Please use the timezone of the presentation. If it isn't known, use US Eastern." The console showed times in UTC. Shape: src/ocx/clock.cr is the one place a time is formatted for a person. - On a page, a time is `<time datetime="UTC">`, written in US Eastern (EDT or EST). A script under Fossil's nonce, added to every console page, rewrites each into the browser's own timezone; without the script it stays Eastern. - This covers the page's "read at", each question's and each turn's time in the tables, and "working/idle since" on the Workspaces and Sessions pages. The table rows carry Time values, parsed from Fossil's UTC datetime(). - Text that isn't a page is written in US Eastern naming the zone: the console's text form, log lines (workspace serve, wake), the reviewer's report, and deploy's gate note. Thread choices name the day in Eastern. - Data for programs (the status JSON, artifacts) stays UTC. - A host without the zone's data falls back to UTC, and says so. Origin: opus, on the maintainer's instruction in the terminal. Intent: a person never converts a time. Expression: src/ocx/clock.cr, src/ocx/console.cr, src/ocx/cgi.cr, src/ocx/cgi_agents.cr, src/ocx/cgi_workspaces.cr, src/ocx/reviewer.cr, src/ocx/serve.cr, src/ocx/launch.cr, tools/workspace.cr, tools/wake.cr, tools/deploy.cr. Demonstrates: spec/ocx/clock_spec.cr, spec/ocx/console_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: 14:05 UTC in September is 10:05 EDT, in December 09:05 EST; a page's time element carries UTC and shows Eastern; console pages carry the script under the nonce; an unknown zone falls back to UTC; CI green, 222 examples, coverage 95.9%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: e04904d5ba user: opus tags: ocx, audited, ci | |
| 02:48 |
Stop reaches a session at a terminal; the Sessions page can list every project
Purpose: the maintainer, 179d5eda95: "Why doesn't clicking Stop ... actually stop it?" (agent-sol's `workspace run` at a terminal, pid 205951), and "Will you have a way to list *all* (all projects) active workspaces and sessions?". Stop looked only for ~/run, which a terminal session has none of, so it refused as "not running" while step 1 showed it running. Shape: - Launch.stop, with no ~/run, sends TERM to the process the process table shows in the workspace (Processes.leader): for a terminal session its `workspace run`, whose sandbox dies with it. A ~/run is still checked against its record as before. With `wait`, it watches that PID's /proc entry. - The Sessions page links "all projects" (?view=sessions&all=1), which lists every project's sessions on the host with a project column, and links back to this project's. Origin: opus, on 179d5eda95. Intent: what the page shows running, Stop can stop; and one page shows all that runs on the host. Expression: src/ocx/launch.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr. Demonstrates: spec/ocx/processes_spec.cr, spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: a stand-in `workspace run` with no ~/run is refused before it runs, then stopped by its PID and ends on the signal; the all-projects view names each session's project and links back; CI green, 220 examples, coverage 95.9%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 77108ae998 user: opus tags: ocx, audited, ci | |
| 02:24 |
Step 1: what runs in the workspaces, read from the process table
Purpose: the first step of the agreed build order (374b12e82e), on the daemon design (aa3bbde87a): find running sessions and active workspaces from the OS, whoever started them, so the Sessions page shows them all with their release, and gc never collects from under a running process (the maintainer, c4521cbb97 and 14d8477f84). Shape: - src/ocx/processes.cr reads /proc by command line. It recognizes `workspace serve|run|start KEY --root ROOT [--project P]`, a key given as a prefix included, and the sandbox's bind of the workspace (Sandbox::BIND); it gives each process's PID, parent, start time, kind and release. /proc/PID/cwd is never read. - Sessions: ~/run is believed only while the process table shows its PID in that workspace. A launch still making its workspace is starting. Otherwise the oldest process there stands for the session: at a terminal, or a serve without a launch note. The list now includes sessions with no launch note, each with its release. - The Sessions page has a release column: "current", "current when it started" (run through the `current` link), or NAME flagged as not the current release, with how to move it: Stop and Start from its workspace, or restart it at its terminal. - Status reads terminal sessions from the record, as before. - gc keeps a workspace a process runs in, before it syncs anything. - Sandbox.running_in's --chdir scan is gone; Sandbox names bwrap and its bind flag once, for both. - Specs: a stand-in `workspace` program (Fixture.stand_in, compiled with cc) gives a real `serve` command line. Origin: opus, on 374b12e82e. Intent: one reading of what runs where, for the console, Status and gc alike. Expression: src/ocx/processes.cr, src/ocx/sessions.cr, src/ocx/cgi_workspaces.cr, src/ocx/collect.cr, src/ocx/status.cr, src/ocx/sandbox.cr. Demonstrates: spec/ocx/processes_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/status_spec.cr, spec/ocx/serve_spec.cr, crystal run ci.cr: the maintainer's host listing as a fixture gives 5 workspaces with processes (terminal, orphaned serves on staged and old releases, a project workspace), and skips the server, other programs, gc, an unmatched prefix, a vanished process and non-PID entries; gc keeps a workspace a process runs in; the Sessions page flags a session on release "zero" and calls one on the current release "current"; a ~/run naming a process elsewhere reads as stopped; CI green, 219 examples, coverage 95.9%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: c4687c2937 user: opus tags: ocx, audited, ci | |
| 01:35 |
Design only: a session's lifetime, and CI launched per check-in
Purpose: the maintainer agreed the lifetime design (257b282d36, on 5d940f5510, after "ci should not stay, should it?", 3f6abf8cc4, and fe709740df). No code changes here. Shape: docs/design/sessions.json gains three decisions: - a `completes` session exits once its report is on its record (a question it asked keeps it running), and a `stays` one runs until `OCX stop`; - a session works and reports on its own record, and no longer rebinds the workspace; - CI doesn't stay: a check-in on a listened branch makes the daemon launch the mechanical `completes` session, a check-in during a run is refused as a launch, and the run's end launches again while an untested check-in remains. Each listener now says what it does: feeds a `stays` session (a thread or ticket) or launches a `completes` one (a branch). events.json registers "end on its report" and "launch for a waiting check-in", and marks CI's current `stays` form as not built as agreed. Origin: opus, on 257b282d36. Intent: the agreed lifetime recorded, before any code. Expression: docs/design/sessions.json, docs/design/sessions.schema.json, docs/design/events.json. Demonstrates: spec/guards/deliberation_spec.cr, spec/guards/causation_spec.cr, crystal run ci.cr: the design holds to its schemas and to events.json, its evidence exists on the record, and every behavior has one cause; CI green, 219 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: a2743b4924 user: opus tags: ocx, audited, ci | |
|
2026-09-29
| ||
| 15:13 |
Design only: the OCX daemon that parents every session
Purpose: the maintainer asked for more thought on finding sessions through the OS and on how they are launched and parented, then agreed the revised design (14d8477f84, on 027446fbe9, after c4521cbb97 and 1b2388982d). No code changes here. Shape: docs/design/sessions.json gains a `daemon` section and five decisions, and events.json registers the behaviors the design needs: - `ocxd`, one per agents root under Fossil's sandbox, started idempotently by the console and the hook (a flock on AGENTS/ocxd.lock); - a launch is a post, which the hook hands to the daemon to start as its child; - sessions and active workspaces are found from the process table by command line, including terminal sessions and orphans, and gc never collects an active workspace; - no automatic relaunch, and the console flags sessions on old releases; - JSON status for Stone and the console. The layout drops run.json. Every new behavior is marked "not built" with the agreement cited. The sessions schema describes the daemon apart from the five parts. Origin: opus, on 14d8477f84. Intent: the agreed design recorded, before any code. Expression: docs/design/sessions.json, docs/design/sessions.schema.json, docs/design/events.json. Demonstrates: spec/guards/deliberation_spec.cr, spec/guards/causation_spec.cr, crystal run ci.cr: the design holds to its schemas and to events.json, its evidence exists on the record, and every behavior has one cause; CI green, 219 examples. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: aa3bbde87a user: opus tags: ocx, ci | |
| 14:19 |
An open question links to the post that asks it
Purpose: the maintainer, 2026-09-29: "the link to the question just goes to the top-post on the forum thread, not the actual question post." The console linked every open question in a thread to the thread's first post. Shape: Questions::Open carries the post that asks it (a thread's latest post, the one the verdict reads), and the console links forumpost/POST; a ticket's question still links its ticket. The short-hash length is named (Questions::SHORT), which lowers questions.cr's unnamed literals from 2 to 1. Origin: opus, on the maintainer's report in the terminal. Intent: a question's link opens the question. Expression: src/ocx/questions.cr, src/ocx/console.cr, docs/precepts/exceptions.json. Demonstrates: spec/ocx/questions_threads_spec.cr, spec/ocx/console_spec.cr, crystal run ci.cr: a question that starts its thread names that post; one asked in a reply further down names the reply; CI green, 219 examples, coverage 95.8%. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 4582f04a91 user: opus tags: ocx, ci | |
|
2026-09-28
| ||
| 12:36 |
CI type-checks every tool, and its last line says what passed
Purpose: two gaps in the CI session's result (e5fd51e1b2): its ci tag read "green: green", and 0d7c78f68c was tagged green although tools/task.cr no longer built; staging found it. The maintainer: "close the ci gaps". Shape: ci.cr gains a tools step: every tools/*.cr is type-checked (crystal build --no-codegen), in parallel, about 3 s. One that does not build is named with the end of its error, and the run is red. A green run ends with a line counting what passed ("219 examples, 0 failures; coverage 95.8%; 9 tools build"); a red one ends with every failure on one line. The command runner tags a check-in with that last line, so the tag now reads "green: 219 examples, ...". Origin: opus, on e5fd51e1b2. Intent: a green tag means every program builds, and says what it counted. Expression: ci.cr, docs/survey/functionality.json. Demonstrates: spec/guards/ledger_spec.cr, crystal run ci.cr: green, ending "219 examples, 0 failures; coverage 95.8%; 9 tools build"; with a tool that does not type-check added for one run, red, naming that file, then removed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 112e7f4832 user: opus tags: ocx, ci | |
| 00:55 |
4. Sessions: the console lists this project's sessions
Purpose: stone, ebd6d19fcd: "i still need a sessions page". A session was reached only through its workspace. Shape: a fourth step in the console's nav, "4. Sessions" (?view=sessions): a short table of this project's sessions, each "LOGIN on RECORD" linked to its own page, with where it stands (working or idle since, starting, stopped). With none, it points to the workspaces. Parts: src/ocx/cgi_workspaces.cr (sessions_html), src/ocx/cgi.cr (the view and the nav), spec/ocx/console_workspaces_spec.cr, docs/design/sessions.json (pages), docs/survey/functionality.json. Origin: opus, on ebd6d19fcd. Intent: every session found from one page. Expression: src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: none yet; a stopped session linked to its page; an idle one shown idle since; CI green, coverage 95.8%, ratchet holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: ffe79a930a user: opus tags: ocx, ci | |
|
2026-09-27
| ||
| 22:21 |
A refused definition no longer takes the workspace page down
Purpose: stone, be7d6abb7a, after accepting 6dfa469c6a: "the console failed: OCX::Definitions::Invalid: the definition states no sandbox". A record's workspace definition from before the sandbox raised while the workspace page rendered, and a view has no rescue of its own. Shape: Definitions.shown gives the latest definition of a kind as the console shows it: its text, or "(its latest workspace definition, by LOGIN, is refused: WHY)". The workspace page uses it; the Start box is left empty when the latest session definition is refused. The four ollama records' workspace definitions were posted again with a sandbox (b09cc6adf1, 89f946b7a3, 3415083607, 39769f8685), and the workspace/claude, codex and plain templates carry one. Origin: opus, on be7d6abb7a. Intent: one bad definition is shown where it is, never a failed console. Expression: src/ocx/definitions.cr, src/ocx/cgi_workspaces.cr. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: an old-style workspace definition and a session of an unknown kind on the record; the workspace page renders, names the refusal, and leaves the Start box empty; CI green, coverage 95.8%, ratchet holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: b464dc8b88 user: opus tags: ocx, ci | |
| 22:07 |
bin/task's CLI runners take the record's sandbox too
Purpose: 0d7c78f68c left tools/task.cr building the claude and codex runners from --tools and --home, which no longer compiles; staging stopped there. Shape: `task WORKSPACE --cli claude|codex` takes the record's workspace definition's sandbox, and --tools and --home only when the record has no workspace definition, as `workspace` does. Origin: opus, on 0d7c78f68c. Intent: one source for a session's confinement, whichever program starts it. Expression: tools/task.cr. Demonstrates: spec/ocx/sandbox_spec.cr, spec/ocx/definitions_spec.cr, crystal run ci.cr (green), and every tools/*.cr type-checks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6dfa469c6a user: opus tags: ocx, ci | |
| 22:03 |
The workspace definition states its whole sandbox
Purpose: stone, 43ce0ee965: "the workspace record entry should include the full bwrap details, except what is filled in by the session"; the shape asked in 46913d970d and answered in 60b14a4324: "Yes, as shown. The workspace templates carry a full sandbox too." Shape: Sandbox::Policy (src/ocx/sandbox.cr) is what a workspace's processes see of the host: network, read_only, read_write, homes and path. A workspace definition carries it as "sandbox", replacing tools and homes; a definition without one is refused, not read as the old shape. bwrap is built only from the policy, plus what is the same for every workspace (/proc, /dev, a fresh /tmp, the workspace read-write as HOME, its peers) and the program the session runs. Add a workspace takes the template's sandbox and adds the workspace's own directory read-write. Runners, the session start, the command runner and `workspace run/start/serve` take the policy. --tools and --home are used only when a record has no workspace definition. Parts: Crystal (sandbox.cr, definitions.cr, cgi.cr, cgi_workspaces.cr, session.cr, cli_runner.cr, command_runner.cr, runners.cr, the claude and codex runners, tools/workspace.cr, specs); JSON and docs (workspace-definition.schema.json, sessions.json: the example, a decision and the migration step, interfaces.md, the survey). Origin: opus, on 43ce0ee965, 46913d970d and 60b14a4324. Intent: every session's confinement stated on the record, where anyone can read it. Expression: src/ocx/sandbox.cr, src/ocx/definitions.cr, src/ocx/cgi.cr, tools/workspace.cr, docs/design/workspace-definition.schema.json. Demonstrates: spec/ocx/sandbox_spec.cr, spec/ocx/definitions_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/session_spec.cr, crystal run ci.cr: a stated policy is planned exactly (no network, each path, PATH, the workspace bound once); a definition round-trips through its schema, and one with no sandbox, no path, no network or a non-path is refused; Add a workspace posts the template's sandbox with the workspace's directory, and refuses a template without one; CI 219 examples green, coverage 95.8%, ratchet holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 0d7c78f68c user: opus tags: ocx, ci | |
| 19:25 |
Listeners get their own console page
Purpose: stone, 43ce0ee965: "the listeners feel like they should have their own console page ... i didn't even see them on the sessions page". Shape: a session's page now shows "Listeners (N)", linking ?view=listeners&login=LOGIN&workspace=KEY. That page lists what the session listens to, with Stop listening, and one form to add a listener (a kind pull-down, then completion). Listen and unlisten return to it. An unknown session says so and links all workspaces. Parts: src/ocx/cgi_workspaces.cr (listeners_page_html; the session page's link), src/ocx/cgi.cr (the view), spec/ocx/console_workspaces_spec.cr, docs/design/sessions.json (pages), docs/survey/functionality.json. Origin: opus, on 43ce0ee965. Intent: one focused page per thing; listeners found from the session. Expression: src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, docs/design/sessions.json. Demonstrates: spec/ocx/console_workspaces_spec.cr, crystal run ci.cr: the link and its count on the session page; branch, thread and ticket listeners added and removed from the listeners page; CI 218 examples green, coverage 95.8%, ratchet holds. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: f4b8c82188 user: opus tags: ocx, ci | |
| 16:49 |
Workspace and session definitions replace grants; the console's workspace pages
Purpose: the maintainer, bdeef0ccd8: "design sounds right. start by replacing the old grants on the forum, then complete the design. no need to support old design." The four threads that had grants now carry workspace and session definitions (posted by opus, 7d428de197). Shape: - src/ocx/definitions.cr reads and writes the two JSON definitions: - a workspace definition: login, project, branch, tools, homes; - a session definition: mechanical with a command, or llm with a runner, model and provider; and a lifetime. The latest of each on a record is in force. Templates are wiki pages workspace/NAME and session/NAME, found across the login group. - Dispatch.turn calls both a Definition, not an answer. - grant.cr, the grant notation and `bin/workspace grant` are removed. - `workspace serve` takes its runner, command, model, provider and mounts from the record's definitions; Workspace.create takes the branch from the workspace definition. - A launch order carries the definitions and posts them. - The console's steps each have a focused page (cgi_workspaces.cr): 1. Hook; 2. Agent logins; 3. Workspaces, a short list for this project. From the list: - Add a workspace: login pull-down, branch completion, mounts from a template, and a record by completion or a new thread. It posts the definition as the agent and clones the repository file, one per login and project. - A workspace: its definition, its session or a form that starts one from a session template (filled by a script), and Terminate. - A session: as before. - Terminate syncs verily and refuses while the clone holds an artifact the repository lacks. Fossil's unsent list is cleared even by a sync that reached nothing, so it isn't trusted. - Repository#hashes and Workspace#sync(verily) support that; the Start and Sessions views are gone. Parts: Crystal (definitions.cr, cgi.cr, cgi_agents.cr, cgi_workspaces.cr, launch.cr, workspace.cr, dispatch.cr, turns.cr, repository.cr, sessions.cr, runners.cr, the workspace tool, specs); JSON and docs (modules, survey, interfaces.md, exceptions, events, coverage ratchet). Origin: opus, on 26b7cdcdfd, 3fa96ac171 and bdeef0ccd8. Intent: one place states a workspace's bindings and one what its session runs; one focused page per step. Expression: src/ocx/definitions.cr, src/ocx/cgi_workspaces.cr, src/ocx/cgi.cr, src/ocx/cgi_agents.cr, src/ocx/launch.cr, src/ocx/workspace.cr, tools/workspace.cr, docs/. Demonstrates: spec/ocx/definitions_spec.cr, spec/ocx/console_workspaces_spec.cr, spec/ocx/console_setup_spec.cr, spec/ocx/serve_spec.cr, spec/ocx/agents_spec.cr, crystal run ci.cr: - the definitions round-trip through their schemas, and are refused when they aren't definitions; - a workspace is added on a new thread and on a ticket; - a session starts from a template, or written with a model and provider; - a command session listens to branches, an llm session to threads and tickets; - terminate refuses unsent work, then sends it and removes the workspace, stopping a running session; - CI: 218 examples, green; coverage 95.7%, the ratchet lowered to 112 missed; within pace. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 377760081f user: opus tags: ocx, ci | |
| 16:06 |
Design: workspace and session definitions replace the grant; one focused page per step
Purpose: the maintainer, 26b7cdcdfd: the grant seems wrong, and the biggest gap is that there isn't a place where the workspace bindings are clearly defined. His steps are hook, agent login, workspace, session, listener; an agent is mechanical or llm. Agreed in 3fa96ac171: "sounds right. keep the individual console pages focused." Shape: docs/design/sessions.json now has five parts: hook, agent login, workspace, session, listener. Each says where it lives and which behavior makes and removes it. - A workspace is stated by a workspace definition, the first entry on the record it was added on (docs/design/workspace-definition.schema.json). - A session is stated by a session definition on the workspace's record: mechanical or llm, and whether it completes or stays (docs/design/session-definition.schema.json). - The grant is retired: the migration reads a grant as the two definitions until records have them. - `pages` lays out one focused console page per step. - events.json gains "withdraw an agent login" (built), and "add a workspace" and "terminate a workspace" (not built). - The deliberation guard now checks each definition's example against its schema, and a part on the record against the host's layout. Parts: JSON (sessions.json and its schema, the two definition schemas, events.json); Crystal (deliberation guard). Origin: opus, on 26b7cdcdfd and 3fa96ac171. Intent: one place states a workspace's bindings; the design before the code. Expression: docs/design/sessions.json, docs/design/sessions.schema.json, docs/design/workspace-definition.schema.json, docs/design/session-definition.schema.json, docs/design/events.json, spec/guards/deliberation_spec.cr. Demonstrates: spec/guards/deliberation_spec.cr, crystal run ci.cr: the design holds against its schema, events.json and the record, and each definition's example against its schema; CI 216 examples, green; ratchet holds; within pace. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: c07c9dbe4e user: opus tags: ocx, ci | |
|
2026-09-26
| ||
| 20:23 |
Template fills the grant box; no pull-down over ten; idle sessions say idle
Purpose: the maintainer, 87d697dffd: choosing a grant template should simply populate the grant box, no copying; "never make a plan that generates a drop-down with over 10 entries"; one pull-down for the listener's kind and completion for which. stone, 6defa7d950: an idle session was listed as running. Shape: - The CGI reads FOSSIL_NONCE, which Fossil gives an ext program so its scripts run under the page's content security policy (the explicitness guard allows it). Choosing a template on Start fills the grant box by a script under that nonce. What the box holds is the grant; a template counts only when the box is empty. The details previews are gone. - "What it is for" is a box with completion from the ten most recent threads and tickets, and empty means a new thread (Launch::MOST_OFFERED = 10; CGI::SHORT). - A session's page has one Listen: a pull-down of the kinds it takes (branch for a command session; thread or ticket for a model's) and a box whose completion offers that kind's ten most recent (Repository#recent_branches), switched by the kind. - The listen action takes kind and target. - A running session is listed as working or idle, with since when. Parts: Crystal (cgi.cr, cgi_agents.cr, launch.cr, checkins.cr, the CGI tool, environment_spec.cr, agents_spec.cr, serve_spec.cr); JSON (exceptions, coverage ratchet). Origin: opus, on 87d697dffd and 6defa7d950. Intent: pick, don't paste; never a long pull-down. Expression: src/ocx/cgi.cr, src/ocx/cgi_agents.cr, src/ocx/launch.cr, src/ocx/checkins.cr, tools/cgi.cr, spec/guards/environment_spec.cr. Demonstrates: spec/ocx/agents_spec.cr, spec/ocx/serve_spec.cr, crystal run ci.cr: with a nonce the Start page carries the templates and fills the box, without one no script; an empty box takes the template; the records are completions by short hash; a command session's page offers the branch kind with trunk, a model's thread and ticket; listen by kind=branch&target=trunk, and a missing branch is refused; an idle session reads idle since; CI 216 examples, green; coverage 95.1%, ratchet lowered; within pace. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: df915eb2d7 user: opus tags: ocx, ci | |
| 19:39 |
A page per session; the Sessions list is short; one Listen per kind of session
Purpose: stone, 78bea0bd5a and 6503cbc7d9: finding a session and its listeners on a phone meant scrolling right and down a crowded table; "let's try to limit the number of elements on a page"; a CI session was set listening to a thread by the wrong one of two Listen forms. Shape: 3. Sessions is a two-column list, each session linked to its own page (view=session&login=…&workspace=…), which holds its record, where it stands with its pid, Stop, what it listens to, and the end of its log. Stop, Listen and Stop listening return to that page. A command session (CI, a review) is offered branches to listen to, and a model's session threads and tickets; never both. Parts: Crystal (cgi.cr, cgi_agents.cr, agents_spec.cr, serve_spec.cr); JSON (survey, coverage ratchet). Origin: opus, on 78bea0bd5a and 6503cbc7d9. Intent: one session per page, one choice per question. Expression: src/ocx/cgi.cr, src/ocx/cgi_agents.cr, spec/ocx/agents_spec.cr, spec/ocx/serve_spec.cr, docs/survey/functionality.json. Demonstrates: spec/ocx/agents_spec.cr, spec/ocx/serve_spec.cr, crystal run ci.cr: the list links scout on its record to its page; the page shows who started it, its log, Stop with the pid while running, and starting while it makes its workspace; a command session's page offers branches and no records, a model's no branches; listen and unlisten return to the page; CI 216 examples, green; coverage 95.0%, ratchet lowered; within pace. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 2df7f7414c user: opus tags: ocx, ci | |
|
2026-09-24
| ||
| 22:33 | Pin the portable question parser contract Intent: give OCX and Stone a shared body corpus with expected parsing results. Expression: add eight versioned JSON cases, test OCX against them, and list the contract in the interface inventory. Demonstrates: crystal spec spec/ocx/question_contract_spec.cr; crystal run ci.cr check-in: 863712863d user: agent-sol tags: ocx, audited | |
| 17:26 |
A terminal session's status for Stone, read from the record; agents kept off trunk by convention
Intent: the maintainer, 2026-09-24 (883bf7bb6d): "update your status info for me so i know you are busy in the app"; a session at a terminal left no ~/run, so its thread answered 404. And his decision on the push boundary (7261dfa269, d6cc5ea6e9): Fossil cannot refuse a push, so protection is by convention, with audits. Expression: Status.json answers, after a running console session, a session at a terminal: a workspace bound to the thread whose sandbox is in the host's process list, working while the thread's latest post is someone else's and idle once it has answered (Repository#forum_latest_other, from the index). PLAN.md's parity preface and backlog record the push test on Fossil 2.27 and the decision. Demonstrates: spec/ocx/status_spec.cr (no sandbox, then working, then idle after the agent's reply); crystal run ci.cr (140 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: ebd07b02d1 user: opus tags: ocx, audited | |
| 14:01 |
Start refuses to replace a session that outlives TERM
Intent: agent-sol's audit of ecd90ccf1e (fail): Launch.stop gave up waiting after ten seconds and Launch.start went on, so a session that ignored TERM kept running beside the new one on the same record. Expression: Launch.stop takes how long to wait and raises Refused when the process is still alive after it; Launch.start waits its patience (ten seconds by default) for each session it replaces, before writing anything on the record or starting anything. Demonstrates: spec/ocx/serve_spec.cr (a session that traps TERM is not replaced: the start is refused, no note is written and no launch is started); crystal run ci.cr (139 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: e6df96db08 user: opus tags: ocx, audited | |
| 13:54 | Audit tool addresses the checkout repository explicitly Intent: record review verdicts from the workspace checkout. Expression: pass the checkout repository path to Fossil tag and sync. Demonstrates: crystal run ci.cr; fossil tag add -R ../clone -n audited ecd90ccf1e fail:dry-run check-in: 61bf1e9561 user: agent-sol tags: ocx, audited | |
| 12:59 |
PLAN.md: the parity contract as of today, with rows for console sessions and the git export
Intent: the maintainer, 2026-09-24 (29cc1794fa): work toward the merge, first refreshing the parity contract to today's facts and adding the git export row. The contract still said serving the console was his step to come, had no row for the git export he wants reproduced before the merge, and listed sessions as long-lived processes among the retired. Expression: the contract's preface says where the console is served, that live runs on public repositories now happen, and that the push boundary is the next slice. Two rows are added, both demonstrated: sessions from the console with their status for Stone (ecd90ccf1e), and the read-only git export (ac215eda96). The retired list says what replaced the daemon's sessions (decision 23). Demonstrates: crystal run ci.cr (green; the parity gate under -- merge-gate still fails, no row being shown). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: d560aab062 user: opus tags: ocx, audited | |
| 04:36 |
Start a session makes a new login and replaces the session running there
Intent: the maintainer, 2026-09-24 (59f6a4a464): the Stone session should run as its own login, opus-stone, and "the provisioning of sessions and status checks are really clunky right now"; switching it took Agent logins, Stop and Start in turn. Expression: the launch action provisions the typed login on the served repository first, so a new name makes a new agent login with a kept password; the Start form's login is a text field with the known logins offered. Launch.start stops a session the console started on the record, as any login, before starting (Launch.stop, TERM and waited for, which Sessions.stop now uses), and the launch note ends ", in place of LOGIN". Demonstrates: spec/ocx/agents_spec.cr (a new login typed at Start is made with its account and kept password, an invalid name refused); spec/ocx/serve_spec.cr (another login's running session is stopped and the note names it); crystal run ci.cr (139 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: ecd90ccf1e user: opus tags: ocx, audited | |
| 04:30 |
A thread's agent status for Stone: ocx/status?thread=ROOT, from the state the session writes
Intent: the request from the Stone session (ollama forum 36b942fc75; contract docs/ocx-status-endpoint.md in stone, chosen by the maintainer at stone 9c88a45eda): Stone's conversation view shows whether the agent on a thread is working, idle or stopped. Expression: Serve.step marks ~/run working as a turn starts and idle as it ends (Run gains state and since; Serve.mark), and Serve.run starts idle. Status.json finds the console-started session on the thread (Sessions.list, by the thread root of its record) and answers login, state, since and last_post; starting is working, a gone or dead ~/run is stopped. The CGI answers the /status sub-path (PATH_INFO, now in Request.path) with that JSON for a forum reader, 403 otherwise, 404 with {} for a thread with no such session. Demonstrates: spec/ocx/status_spec.cr (no session, stopped, working with its since and last post; the CGI's 200, 404 and 403); spec/ocx/serve_spec.cr (a turn runs with ~/run saying working and leaves it idle); crystal run ci.cr (139 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6e24311e50 user: opus tags: ocx, audited | |
| 04:30 |
Sessions reads a starting session's launch pid once
Intent: 6dadb2569d read the launch pid twice, once for the state and again for the run, so a launch that ended between the reads was starting with no pid and the Sessions view failed with a NilAssertionError; spec/ocx/agents_spec.cr:201 failed whenever it ran alone. Expression: Sessions.list reads Launch.starting once and derives both the state and the run from that one reading. Demonstrates: spec/ocx/agents_spec.cr (the launch-then-Sessions case, three runs alone and in the suite); crystal run ci.cr (green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: c5dda6f274 user: opus tags: ocx, audited | |
| 01:50 |
Sessions lists every console-started session on the host, on any project
Intent: the maintainer, 2026-09-24: ollama's Sessions view showed nothing while the Stone session ran on stone; "list every ocx-originated session (not terminal sessions) on the host". Expression: Sessions.list reads every workspace on the host; a workspace's repository is its binding's project served beside this one (this one while the workspace is being made), and it is a session when its record carries a launch note by an agent login (Sessions.launch_note, paging from the thread's root). Sessions without a note, which a terminal leaves, are not listed. The view gains a project column and links a record on another project at its own address. Demonstrates: spec/ocx/serve_spec.cr (a session on another project listed with its address, a workspace with no launch note not listed, the earlier running, starting and paging cases); spec/ocx/agents_spec.cr; crystal run ci.cr (138 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6dadb2569d user: opus tags: ocx, audited | |
| 01:40 |
A session whose work waits on the maintainer ends its turn with the question
Intent: the maintainer, 2026-09-24 (thread adfc87fba2, 8890f53742): the Stone session's fix needed a merge into trunk, but its turn ended with a report, so nothing reached the console's open questions; the writing rules said how to ask, not when a session must. Expression: one rule in Conventions.text, after the question shape: when the work cannot go further without the maintainer (a merge into trunk, a release to accept, a choice between designs), the turn's last post is that question, in the question shape. Demonstrates: spec/ocx/conventions_spec.cr (the rule is in every session's prompt); crystal run ci.cr (green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: c75737deca user: opus tags: ocx, audited | |
| 01:03 |
Git requests are bounded before they are read; each release links on its own repository
Intent: agent-sol's audits of dbff1ddacc and a0c80294fb (both fail): the CGI allocated CONTENT_LENGTH before any bound, and MAX_BODY applied only to gzip-expanded bodies; and the Releases table used one source for every row, so a release from another repository linked to the wrong one and was compared across repositories. Expression: GitHTTP.body reads a POST body only within MAX_BODY and returns nil past it, which tools/cgi.cr uses for every request; GitHTTP.respond refuses a nil body with 413, and gunzip refuses raw bodies past MAX_BODY as well as expanded ones. releases_html links each release on its own source.txt, this page on the release it is, and offers "what changed" only when next and current recorded the same repository. Demonstrates: spec/ocx/git_http_spec.cr (an oversized announced body unread, oversized raw and gzip-expanded bodies 413); spec/ocx/cgi_spec.cr (a release with no source, one on another repository, and one on the same); crystal run ci.cr (138 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: ac215eda96 user: opus tags: ocx, audited | |
|
2026-09-23
| ||
| 18:40 |
Releases link to their check-ins; git-next serves the staged release's git exports
Intent: the maintainer, 2026-09-23: the Releases table should link each release's check-in so he knows what to look for when accepting; and the git CGI (dbff1ddacc) needs a way to be tried before it is accepted. Expression: deploy writes source.txt beside gate.txt, the checkout's remote without its login; Releases.release reads it; releases_html links every release name to SOURCE/info/NAME and gives next a "what changed" link to SOURCE/timeline?from=CURRENT&to=NEXT. tools/cgi.cr serves git exports under git-next as under git, as the console is served under ocx-next. Demonstrates: spec/ocx/cgi_spec.cr (a release with source.txt linked to its check-in, next linked to the timeline from current); by hand, the built CGI answered as git-next with the ref advertisement and as another name with the console; crystal run ci.cr (138 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: a0c80294fb user: opus tags: ocx, audited | |
| 18:29 |
The CGI serves each repository's git export, read-only, as ext/git
Intent: the maintainer, 2026-09-23: the git export at .../ext/git/stone.git is served by trunk's binary (GitHTTPHandler), and that must be reproduced before trunk can be retired. The export itself is each repository's after-receive hook (fossil git export, then git update-server-info), which needs nothing of trunk. Expression: tools/cgi.cr, served under the name git, hands the request to OCX::GitHTTP, which finds NAME.fossil beside the served repository, serves the directory its last-git-export-repo setting names (recorded by fossil git export itself) with git upload-pack --stateless-rpc, accepts a gzipped request, refuses git-receive-pack, and needs o or h; OCX::Git is the one place git is started (I7), with PATH alone. C6 admits PATH_INFO, Fossil's way of telling an ext program which sub-path was asked for (the maintainer allowed it where it is the clear channel between Fossil and the CGI). Demonstrates: spec/ocx/git_http_spec.cr (advertisement, a fetch that returns a pack, a gzipped fetch, pushes and viewers without o or h refused, unknown names 404); by hand, git clone through fossil server --extroot of the whole ocx export (434 branches, 1.1 s) and a push refused with 403; crystal run ci.cr (138 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: dbff1ddacc user: opus tags: ocx, audited | |
| 17:13 |
Merge agent-sol's reviewer queue (agent/20260923T-reviewer-watcher) into ocx
Intent: the maintainer, 2026-09-23: "why hasn't agent/20260923T-reviewer-watcher been merged?"; every check-in on it carries an independent audit (pass, by opus). Expression: a clean merge of the branch tip d2cefd5fc6: tools/reviewer.cr and src/ocx/reviewer.cr, with its changes to checkins, dispatch, questions, turns and tickets. Demonstrates: spec/ocx/reviewer_spec.cr; crystal run ci.cr (137 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 5fe2abe75a user: opus tags: ocx, audited | |
| 17:09 |
Workspaces names and links every record, whatever its project
Intent: the maintainer, 2026-09-23: in the Workspaces view two records were resolved and three were not; only workspaces on the repository being viewed were named and linked. Expression: workspaces_html reads the record's title from the workspace's own clone (else, while it has none, from this repository when it is its) and links it on the workspace's remote, or relatively for this repository. Demonstrates: spec/ocx/agents_spec.cr (a workspace on another project named from its clone and linked there); crystal run ci.cr (green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: d9776c672c user: opus tags: ocx, audited | |
| 17:09 |
Sessions finds a launch note however many replies followed it
Intent: agent-sol's audit of 679ed48a98 (fail): Sessions read only an agent's latest 20 posts or ticket changes per record, so a launch note followed by 21 replies dropped the session from the view. Expression: Sessions.launch_notes reads each record's agent entries a page at a time (PAGE, 20) until one is a launch note or the pages end; forum_posts_by and ticket_comments_by take an offset, and ticket_comments_by returns one entry per change (empty text when it appended no comment) so a page is short only at its end. Demonstrates: spec/ocx/serve_spec.cr (a launch note on a ticket and on a thread, each followed by 21 replies, is listed); crystal run ci.cr (green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 08e133e0ec user: opus tags: ocx, audited | |
| 17:03 |
A session reads and answers nothing while its clone cannot sync
Intent: agent-sol's audit of 0a002aa92f (fail): with a refused login, workspace serve went on to write a grant, build the runner and answer from a stale clone, and Serve.step dispatched after a failed sync. Expression: Serve.step returns Unsynced without reading the record when the sync fails, and the loop says so and waits; workspace serve syncs with Serve.synced! at startup and after writing a grant, and stops on Unsynced without writing a failure note with a login that may not work; the run and start verbs refuse a grant read from an unsynced clone. Demonstrates: spec/ocx/serve_spec.cr (a refused sync answers nothing and dispatches no turn; after the password is taken it answers); crystal run ci.cr (136 examples, green). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 33e4530bba user: opus tags: ocx, audited | |
| 16:51 |
docs/interfaces.md: the console row names the Workspaces view and how it finds a running sandbox
Intent: the Workspaces view (d21ce3e812) and its sandbox detection (b1eebcb578) were not in the interface inventory: both edits of the row failed to match and nothing checked them. Expression: the console row in docs/interfaces.md. Demonstrates: crystal run ci.cr (the interfaces guard reads the row). Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 6c310118c5 user: opus tags: ocx, audited | |
| 16:50 |
The Workspaces view finds a session running at a terminal by its sandbox, with no pid reported by hand
Intent: the maintainer asked opus and agent-sol to report their pids to their workspaces, and guessed there is a more automated way to find the processes in the right bwrap (thread adfc87fba2). A pid read inside a sandbox is the sandbox's own: here pid 1 is bwrap and nine processes are visible, so a reported pid would name the wrong process on the host. The host sees every sandbox, and each names its workspace's tree as its working directory (--chdir), so the console finds it there: a workspace with no ~/run and no launch in progress, but a sandbox running in it, shows as running at a terminal with its host pid. Expression: Sandbox.running_in (the one file that names the sandbox program); Sessions.workspaces uses it; the Workspaces view says "running at a terminal"; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/sandbox_spec.cr (a stand-in process list: the sandbox of a workspace found, another program with the same directory and another workspace's sandbox not taken); crystal run ci.cr; tried inside this sandbox, where it finds its own bwrap for this workspace and nothing for another directory. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: b1eebcb578 user: opus tags: ocx, audited | |
| 16:44 |
Require independent author for an audit tag
Intent: Keep a check-in visible for review when its own author tagged it audited. Expression: Match the active audited tag's source artifact user against the check-in author in the audit queue query. Demonstrates: spec/ocx/reviewer_spec.cr (a self-tag remains pending while a different login's tag clears its check-in); crystal run ci.cr (135 examples, zero failures). Leaf check-in: d2cefd5fc6 user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 16:15 |
Count status-only ticket changes as reviewer participation
Intent: Include a reviewer in a ticket's scope when they changed its status without writing a comment. Expression: Query the ticket change index for the reviewer's changes and keep Dispatch.waiting responsible for conversational entries. Demonstrates: spec/ocx/reviewer_spec.cr (a status-only reviewer change followed by a waiting comment); crystal run ci.cr (135 examples, zero failures); crystal build tools/reviewer.cr. check-in: 1a58b2f9e3 user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 16:08 |
Integrate reviewer queue with current OCX APIs
Intent: Keep the reviewer queue mergeable with current OCX interfaces and limit each poll's conversation search. Expression: Merge the current ocx tip; use its per-record forum and ticket queries; inspect only the latest COUNT threads and tickets; resolve ticket prefixes with an indexed query. Demonstrates: spec/ocx/reviewer_spec.cr (thread and ticket scope); crystal run ci.cr (135 examples, zero failures); crystal build tools/reviewer.cr; live --since now queue returned empty sections with the cutoff note. check-in: 8d3987011c user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 16:05 |
One password per agent login, taken by every session; a session failure is written on the record; a refused login is not a successful sync
Intent: the maintainer: "If you cannot see the stone session, then you designed it wrong" (thread adfc87fba2). One agent login had two passwords, the kept one on the host and a copy in each workspace, with nothing keeping them one, so this workspace could not read stone as opus while stone held the kept one. And a session that failed left its reason only in a log on the host. Now workspace serve, which runs on the host and can read the kept password, gives it to its workspace each time it starts (identity file and clone login), and any failure after that is written on the thread as a note by the agent, where anyone reading the record sees it. Found on the way: fossil can end a sync with success after the server refused its push, so a drifted workspace believed it was in sync while its work stayed unsent; Workspace#sync now reads fossil's words for a refused login. Expression: Workspace#adopt_password; workspace serve takes the kept password and writes "OCX note: the session stopped: ..." on failure; Workspace#sync treats a refused login as failure. Demonstrates: crystal spec spec/ocx/workspace_spec.cr (an agent whose server password changed: its check-in does not reach the server and its sync reports failure; after taking the new password it syncs and the check-in arrives); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 0a002aa92f user: opus tags: ocx, audited | |
| 15:39 |
A Workspaces view on the console: every workspace on the host
Intent: the maintainer asked for a view listing workspaces (thread adfc87fba2, 2e7e490347). It lists every workspace directory on the host, newest activity first, with its agent login, the project it is bound to, the thread or ticket (named and linked when it is this repository), its branch, whether a session runs or is starting in it, and when its clone last changed; a directory whose workspace is still being made says so. It reads the workspace directories alone (binding, ~/run, launch pid), the state decision 25 keeps there, and is for setup and admin viewers. Expression: Sessions.workspaces and Place; CGI.workspaces_html and the workspaces view and its link; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/agents_spec.cr (a workspace bound to this repository ticket named and linked with its branch and state; one being made; not shown to a reader); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: d21ce3e812 user: opus tags: ocx, audited | |
| 15:34 |
Scope reviewer conversations to relevant waiting records
Intent: Keep the project-wide review queue focused on code audits and conversations this reviewer can answer. Expression: Share Dispatch.waiting across threads and tickets; include conversations only when the reviewer participated or is addressed, and use the indexed latest thread post. Demonstrates: spec/ocx/reviewer_spec.cr (unrelated, addressed, participated, resolved, and protocol cases); crystal run ci.cr (135 examples, zero failures). check-in: 04783fd7a5 user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 15:29 |
Sessions off the first view; launch notes looked for only on the listed records
Intent: ollama/ext/ocx-next timed out for the maintainer when logged in, and he pointed out that the Sessions list was the kind of search he had asked to move off the first page (thread adfc87fba2). It was: the first view still derived it, and it read every post and ticket change any agent login ever wrote across the repository, thousands on ollama from trunk agents, with every change of each ticket they touched parsed. Sessions is now a view of its own, and the first view is the questions and, for setup and admin viewers, the releases. The launch notes are looked for per listed thread or ticket, among the agents latest few posts or comments there, newest first, stopping at the first. Expression: the sessions view in src/ocx/cgi.cr and its navigation link; the launch and stop forms return to it; Repository#forum_posts_by per thread and #ticket_comments_by per ticket, parsing only those changes; Sessions.launch_notes over the listed choices; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/agents_spec.cr (the first view has no Sessions; the sessions view lists them from their launch notes), spec/ocx/serve_spec.cr; crystal run ci.cr; timed by hand on a copy of the ollama repository with its trunk agent logins present, release build: first view 40 ms, sessions 377 ms, work 95 ms. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 679ed48a98 user: opus tags: ocx, audited | |
| 15:22 |
Add explicit UTC date and now cutoffs to reviewer queue
Intent: Let a reviewer start from a clean timeline horizon without marking older code or forum records as reviewed. Expression: Parse --since YYYY-MM-DD at UTC midnight or --since now at startup; state in output that earlier candidates are hidden, not cleared. Demonstrates: spec/ocx/reviewer_spec.cr; live bin/reviewer --since now returned empty queues while preserving the cutoff note; crystal run ci.cr (135 examples, zero failures). check-in: 245791256b user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 15:17 |
The console renders in about a tenth of a second: its first view is only the questions, the rest a click away
Intent: the maintainer asked for a render under a second, with most searches behind a click (thread adfc87fba2). Timed on a copy of this repository: the open questions in threads took 3.1 s, because each of 50 threads had every post parsed when the verdict reads only the latest, and each thread root was parsed for its title. Threads now come from the index alone, with their latest post and starter and the title from the root timeline entry, and only the latest post is parsed (29 ms). The page is split into views: the first holds the questions, and for setup and admin viewers the sessions and the releases; work, check-ins, posts and audits, starting a session and the agent logins are each a link, and each derives only its own sections. Every view renders in 110 ms or less on the same copy. Expression: Repository#forum_threads reads the index (latest, starter, title), Questions.in_threads parses the latest post only; Console::VIEWS and the view parameter of sections and render; the CGI view choice and navigation line, the admin HTML split into sessions, start and agent views with their links; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (the first view links the others and derives neither work nor audits; each view shows its own), spec/ocx/agents_spec.cr (sessions, starting and logins in their views), spec/ocx/questions_threads_spec.cr and spec/ocx/console_spec.cr; crystal run ci.cr; timed by hand on a copy of the ollama repository, release build: home 109 ms, work 95, start 89, posts 43, check-ins 3, audits 1, agents 1. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: f78a73325f user: opus tags: ocx, audited | |
| 15:14 |
Let reviewers bound the recent Fossil review queue
Intent: The review queue must make its recent-item cap visible and adjustable for a repository with a large backlog. Expression: Add --limit and print the limit in each section heading. Demonstrates: spec/ocx/reviewer_spec.cr; crystal run ci.cr (135 examples, zero failures). check-in: d96072535f user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 15:11 |
Add a project-wide reviewer queue
Intent: Give a reviewer one read-only queue for file-changing check-ins that lack an audit and substantive forum posts that have not received the reviewer’s reply. Expression: Derive candidates from the Fossil clone; sync and wait in bin/reviewer without a local cursor or model invocation. Demonstrates: spec/ocx/reviewer_spec.cr; crystal run ci.cr (135 examples, zero failures); build tools/reviewer.cr and read the live project queue. check-in: 0e497acc6a user: agent-sol tags: audited, agent/20260923T-reviewer-watcher | |
| 15:04 |
A launch makes the agent able to log in first, and its session makes its workspace in the launch directory
Intent: the maintainer launched on stone with 26dc2b8e3c accepted (thread adfc87fba2) and got "is not a workspace (OCX::Workspace::Invalid)": the launch now makes the workspace directory first for its log, and workspace serve took any existing directory for a made workspace. Running the same steps here as opus then showed the next failure: cloning stone as opus is refused with "login failed" (traced: the second sync round replies "error login failed"), on stone, badge-launcher and zepto alike, while ollama accepts the same password. So the password the group members hold for opus is not the one this workspace uses, and a session would stop at its clone. The launch now provisions the agent on this repository alone before anything else: the account made if missing, and set to the kept password, which the session itself pushes with. Expression: workspace serve opens a workspace only where its binding is, else creates it (Workspace.create already accepts a directory holding only launch files); Launch.start calls Agents.provision for this repository before writing on the record. Demonstrates: crystal spec spec/ocx/workspace_spec.cr (a workspace is made in a directory holding a launch log), spec/ocx/serve_spec.cr (the launch succeeds when the account password has drifted from the kept one, and fails with "login failed" when the provisioning step is removed); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: d9070b7c75 user: opus tags: ocx, audited | |
| 14:50 |
The Sessions list reads only the agents' posts: the console page in milliseconds, not seconds
Intent: the maintainer: "stone/ext/ocx fetch took 12.07s" (thread adfc87fba2). Timed here on a copy of this repository: the Sessions list took 10 s, because it read every post and ticket change of 57 records to find launch notes; everything else on the page took under 150 ms. The index says which posts and ticket changes are by agent logins, so only those are parsed now: 97 ms on the same copy. Expression: Repository#forum_posts_by and #tickets_changed_by; Sessions.launch_notes gathers the latest launch note per record from them. Demonstrates: crystal spec spec/ocx/serve_spec.cr and spec/ocx/agents_spec.cr (sessions still listed from their launch notes, on tickets and on threads); crystal run ci.cr; timed by hand on a copy of the ollama repository, 9999 ms before, 97 ms after. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 26dc2b8e3c user: opus tags: ocx, audited | |
| 14:34 |
task --write can name the post it answers
Intent: the maintainer found replies hard to find in the thread tree (thread adfc87fba2): an answer attached to the newest post by someone else at the moment of writing, so an answer to one post landed under a later one that had arrived meanwhile. The writer may now name the post it answers; the latest post by anyone else stays the default. Expression: Dispatch.write reply_to, by hash prefix within the thread, refusing a post not in it; --reply-to on task --write. Demonstrates: crystal spec spec/ocx/turns_spec.cr (a turn written with reply_to a post replies to it; an unknown post is refused); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 7addcf7685 user: opus tags: ocx, audited | |
| 14:26 |
Sessions are told how to ask the maintainer a good question
Intent: the maintainer (thread adfc87fba2, dea860e05a) pointed to "How To Ask Questions The Smart Way" and summarized it: clarify the goal, attempt a solution first, detail what was tried and expected, explain the actual outcome, define success; not excessive initiative, but research enough that the reply can be a simple confirmation; always by the virtues of Clarity, Interchangeability and Accountability. Every session prompt now carries this, beside the earlier conventions, so no session needs telling again. Expression: two lines in src/ocx/conventions.cr. Demonstrates: crystal spec spec/ocx/conventions_spec.cr; crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 760913b6dc user: opus tags: ocx, audited | |
| 05:30 |
Define a portable parser contract for questions shared with Stone
Intent: Stone and OCX need the same concrete question body cases so their independent readers can be compared without translating prose expectations. Expression: Add a versioned language-neutral JSON fixture for valid and invalid question bodies, pin OCX parsing against its expected fields, and list the fixture in the interface inventory. Demonstrates: crystal spec spec/ocx/question_contract_spec.cr (8 shared cases); CRYSTAL_CACHE_DIR=/tmp/ocx-crystal-cache crystal run ci.cr (136 examples, zero failures). Leaf check-in: d9c308ace4 user: agent-sol tags: audited, agent/20260923T0529-portable-question-contract | |
| 05:26 |
The console keeps its state on the record; the public URL is the repository email-url; a thread started as the agent is answered
Intent: the maintainer (thread adfc87fba2): "Only the workspaces should be getting state from the filesystem ... Fossil is our abstraction to the storage wherever it can handle it", and "the email-url config property can be requested to be configured on each repository". Three launches had guessed the public URL from the request wrongly; the console kept launch logs, pids, a grant hand-off file and a saved grant per runner beside the workspaces. Found while planning this: a thread the console starts is posted as the agent, so its session counted the first post as its own turn and saw nothing to answer. Expression: Launch.start writes the grant (when the record has none) and the note "OCX note: started from the console by VIEWER: RUNNER as LOGIN" on the record as the agent, pushed to the public URL, and keeps its log and pid inside the session workspace directory (Workspace.create accepts a directory holding only those); Sessions are derived from the launch notes on the record, their state from the workspace; the default grant is the latest on the repository; the saved grants, the grant file and the request URL and scheme reading are gone, and the C6 guard names six variables again. Dispatch.waiting: the login asks, notes and grants are not answers, and the record own post waits until the login answers even when the login wrote it; a stop the session already acknowledged is neither obeyed again nor answered. PLAN.md decision 25; docs/interfaces.md rows. Demonstrates: crystal spec spec/ocx/serve_spec.cr (the launch writes grant and note on the record as the agent and starts serve with no grant file; sessions listed from the notes, running, starting and stopped), spec/ocx/agents_spec.cr (refused without email-url; launched against a served fixture with it; the Sessions row from the record), spec/ocx/turns_spec.cr (a thread started as the agent waits through its grant and note until answered); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: f3f89358ad user: opus tags: ocx, audited | |
| 05:14 |
A session runs its CLI from the granted path, not the sandbox path
Intent: the stone session launched from the console answered "OCX failed: the session ended with exit 1 / bwrap: execvp claude: No such file or directory" (stone forum post 50a6351787, reported in thread adfc87fba2). The grant it was given, taken from this workspace, binds claude's binary as a home and names no command, so the runner ran a bare "claude", which is not on the sandbox path. When no program is named, a granted home named as the runner is now the program, run by that host path, which the session binds read-only as it already does for a command's absolute path. Expression: Runners.program, used by workspace serve, which also logs which program it runs. Demonstrates: crystal spec spec/ocx/runners_spec.cr (a home named claude is its program; a command's absolute first word wins; none for a runner no home names); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: de5fa5e974 user: opus tags: ocx, audited | |
| 05:02 |
Every session is told how to write on the record; the console tells starting from stopped, and a terminal session from both
Intent: the maintainer: "no process for remembering proper pre-prompting", as the new stone session repeated the bare-reference mistake; and "the console reports this session as stopped, but it does so for all the sessions" (thread adfc87fba2). The corrections given to sessions so far are now part of every prompt, so no session has to be corrected again: references as Markdown links in full, built from the workspace remote and branch, the design text linked; a question as its own post in the question shape, a blank line before each heading, one at a time; the result first, plainly; times in US Eastern. And the Sessions list was wrong in two ways. A workspace started at a terminal, which has no launch log, was called stopped. A launched session still cloning its repository had no ~/run yet and was called stopped too. The launch now keeps its pid beside the log, and the session logs each step with its output unbuffered, so the log fills as it goes. Expression: src/ocx/conventions.cr, included by Session.prompt and the native runner system message; Launch.pid_file and Launch.starting, refusing a second launch while one is starting; the Starting and NotFromConsole states; STDOUT.sync and progress lines in workspace serve; docs/interfaces.md rows. Demonstrates: crystal spec spec/ocx/conventions_spec.cr and spec/ocx/session_spec.cr (the prompt carries the conventions, with links built from the remote); spec/ocx/serve_spec.cr (a launch whose process lives and has no ~/run yet is starting, then stopped when it ends; a workspace with no launch log is not started from the console); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: cf0220e169 user: opus tags: ocx, audited | |
| 04:48 |
Keep forum count checks stable while the record grows; keep local no-port URLs local
Intent: Audit found that a live forum post can arrive after a thread count is read, and the new public-host URL rule also treats localhost without a port as https. Expression: Compare the later post count against the earlier lower bound; preserve HTTP for localhost and loopback with no port; document and specify the exception. Demonstrates: crystal run ci.cr (132 examples, zero failures); spec/ocx/forum_spec.cr and spec/ocx/cgi_spec.cr check-in: 0680cee54f user: agent-sol tags: ocx, audited | |
| 04:43 |
The session remote is https for a public host, whatever the request says
Intent: with 3cf7ddaa0d previewed the launch still connected to port 80 (thread adfc87fba2). The request evidently states http: Fossil on the host is handed the request after TLS has ended, and a stated scheme won in the last rule. The public name answers 200 on https and refuses on port 80 (checked from a workspace here). A host named without a port is now always https; a host naming its own port is reached as the request says, else http. Expression: OCX::CGI.scheme; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (a public host is https even when the request says http and HTTPS is off; a host with a port follows the request, else http); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 90114c0ff8 user: opus tags: ocx, audited | |
| 04:37 |
The session remote is https for a host named without a port
Intent: with b66630b162 previewed on the host the launch still connected to port 80 (thread adfc87fba2): the ext program there is told neither REQUEST_SCHEME nor HTTPS, nor port 443, nor an https Referer, so both guesses gave http. A host named without a port, as the public one is, is now taken as its https address; http only when the request says so or the host names its own port, as a local server does. Expression: OCX::CGI.scheme takes REQUEST_SCHEME, HTTPS and HTTP_HOST; SERVER_PORT and HTTP_REFERER are no longer read, and the C6 guard names the four URL variables only; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (no scheme variables and a host without a port give https; a host with a port gives http; REQUEST_SCHEME and HTTPS win when given); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 3cf7ddaa0d user: opus tags: ocx, audited | |
| 04:33 |
Merge the concurrent HTTPS console change with the workspace watcher
Intent: Keep the ocx branch on one head with both the console scheme correction and the detached watcher repairs. Expression: Merge b66630b162 into c4f5970711; the changes apply cleanly in separate files. Demonstrates: CRYSTAL_CACHE_DIR=/tmp/ocx-crystal-cache crystal run ci.cr (132 examples, zero failures; 19 precepts, zero gaps). check-in: ce66d24df9 user: agent-sol tags: ocx, audited | |
| 04:32 |
Let a detached Codex session recover and answer its forum record
Intent: A workspace watcher should stay idle without model calls, then reliably launch the installed Codex CLI for a new forum post and report CLI failures truthfully. Expression: Use current Codex approval and sandbox flags in its adapter; keep Fossil stderr out of machine-readable output; preserve a human post across a mechanical self-ask; report printed nonzero exits as failed and clear a stale Codex answer before a new run. Document the runner outcome contract and pin the recovery cases in specs. Demonstrates: crystal run ci.cr (131 examples, zero failures); spec/ocx/turns_spec.cr; spec/ocx/cli_runner_spec.cr; spec/ocx/worktree_spec.cr; spec/runners/codex_conformance_spec.cr. check-in: c4f5970711 user: agent-sol tags: ocx, audited | |
| 04:32 |
The session remote is https when the port or the browser says so: Fossil's TLS server sets no scheme for an ext program
Intent: the next launch on the host said "launch: failed: Socket::ConnectError: Error connecting to ollama.openbeagle.org:80" (thread adfc87fba2): the console built the repository URL with http, since Fossil's own server serving TLS gives an ext program neither REQUEST_SCHEME nor HTTPS. The port it answers on (443) and the Referer the browser sends with the console's own form both say https; plain http is taken only when nothing says otherwise. Expression: OCX::CGI.scheme, called by tools/cgi.cr with REQUEST_SCHEME, HTTPS, SERVER_PORT and HTTP_REFERER; the C6 guard names the two new variables. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (no scheme variables but port 443, or an https Referer, gives https; a stated REQUEST_SCHEME wins; the repository URL is cut at /ext/); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: b66630b162 user: opus tags: ocx, audited | |
| 04:24 |
The console always answers; fossil never runs with its standard streams closed; a workspace opened by a relative path still syncs
Intent: the maintainer pressed Start the session and got a 0-byte download (thread adfc87fba2): a CGI that dies prints nothing, and Fossil serves nothing as a download, so an exception no action named escaped. The same launch run here with the built CGI in a CGI environment, for an existing thread and for a new one posted to a local server, answered as the console, so the host condition is not reproduced; the console now names it instead. While merging the check-in 832108d3a2 by agent-sol, which found that sync failed with standard input closed, the cause was found in my OCX::Fossil wrapper (2af75b63aa): it closed all three streams, so fossil opened the repository on a low descriptor (SQLite warns "attempt to open ... as file descriptor 0"), failed to sync, and could have printed into the database. The clone passes integrity_check. And since that fix names the clone with -R from inside the tree, a workspace opened by a relative path could not sync; it is now made absolute. Expression: CGI.act names any other error as "ACTION: failed: CLASS: MESSAGE" and CGI.respond answers any error as a plain page; OCX::Fossil.run gives fossil an empty input and discards its output unless told otherwise, never closing a stream; Workspace.open makes its directory absolute. Demonstrates: crystal spec spec/ocx/agents_spec.cr (a launch whose new thread cannot be posted answers "launch: failed: Socket::ConnectError" on a page) and spec/ocx/workspace_spec.cr (a workspace opened by a relative path syncs); crystal run ci.cr; by hand, task --wait on this workspace syncs again. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 495a0f9f84 user: opus tags: ocx, audited | |
| 04:03 |
Name the clone in the manual workspace sync instruction
Intent: Give an agent a reliable command for refreshing the forum record when inspecting it manually. Expression: Use `fossil sync -R ../clone` in AGENTS.md, matching the workspace tool's explicit clone selection. Demonstrates: AGENTS.md; spec/ocx/workspace_spec.cr. check-in: 281777e74c user: agent-sol tags: ocx, audited | |
| 04:02 |
Document the agent workspace conversation and make its sync reliable
Intent: An agent in a new workspace needs a clear path to the binding, contribution rules, and forum thread, and the task tool must sync that workspace's clone before it waits for posts. Expression: Add AGENTS.md with the workspace and review workflow; pass the bound clone explicitly to Fossil sync; document and pin that contract in docs/interfaces.md and the workspace spec. Demonstrates: crystal run ci.cr (129 examples, zero failures); spec/ocx/workspace_spec.cr; spec/ocx/turns_spec.cr. check-in: 832108d3a2 user: agent-sol tags: ocx, audited | |
| 03:38 |
The console's sessions, step by step: what runs, how to start one, which agents; the URL from the request
Intent: the maintainer could not tell the phases apart on the page (provision an agent, pick a thread or ticket, define the workspace, start the session), did not know whether a session had started on stone, and found that three grant boxes left no real defaults when he had only suggested each runner's defaults differ. He then saw why nothing started: "this repository has no URL recorded yet". I had assumed, without checking, that Fossil records each base URL in the config table; on the host it does not. Fossil gives an ext program the request's own URL (REQUEST_SCHEME or HTTPS, HTTP_HOST, REQUEST_URI; www/serverext.wiki), and the repository's URL is that, cut at /ext/. Expression: src/ocx/sessions.cr derives the repository's sessions from the agents directory (running, stopped, never started, with the end of each log), stops one by TERM to the pid its ~/run names once its command line is seen to name the record, and gives each runner's default grant (the one last launched with it on this host, else the latest on this repository, else one on a workspace's record). src/ocx/cgi_agents.cr lays the page out as Sessions, Start a session (1. as which agent, 2. on what, 3. with which runner, chosen by a link so its grant fills the one box, 4. its workspace), then Agent logins. A withdrawn account can be set up again. workspace serve leaves no ~/run behind when stopped. Repository#base_url is gone; the CGI reads the four URL variables, and the C6 guard names them. Demonstrates: crystal spec spec/ocx/agents_spec.cr (the sections and steps; a runner's grant by ?runner=; a launch refused when the request names no URL, and started from it otherwise; a new thread's session listed as never started when the program exits at once, its grant kept for next time) and spec/ocx/serve_spec.cr (sessions of this repository listed with their log, another's left out; Stop ends the process and the session shows stopped); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 62a4920217 user: opus tags: ocx, audited | |
| 03:22 |
Provisioning creates the account only where it is asked; withdraw an agent from one repository
Intent: the maintainer pressed Create the account here for opus on stone and got "provisioned opus on stone, ollama, badge-launcher, ... interactor", 25 repositories, and asked whether that was right. It was not. The code walked the login group's member list itself and created the account, with check-in and forum rights, wherever it was missing, when the only purpose of touching the group was to keep one password. Now the account is created only on the repository whose console asked; the other members get the password only where the account already exists. And since Fossil never deletes a user, a row button withdraws an agent's capabilities on the repository being viewed, which is how the accounts made by mistake are taken back. Expression: Agents.provision creates on the first repository only and returns what it set; Agents.withdraw; the withdraw action and a Withdraw it here button per row with capabilities; the provision notice names where the password was only kept in step; PLAN.md decision 24 and docs/interfaces.md say so. Demonstrates: crystal spec spec/ocx/agents_spec.cr (provisioning with a two-member group creates the account on the first only; an account made by hand on the other gets the one password and keeps its capabilities; withdraw empties the capabilities on that repository alone and refuses a person's login); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 61e2e98873 user: opus tags: ocx, audited | |
| 03:15 |
One button per agent login: create its account here with the kept password
Intent: on stone's preview the maintainer saw opus with no account there but its password kept, and asked why there was no button to set the account up. The password now lives in one place for the host, the agents directory, not per project, so a project needs only its account created. Each row of the Agents table offers the one step that fits: create the account here (the agent capabilities, the kept password); keep the password the login's workspaces already use, when none is kept; or, asked for plainly, reset an existing account's password and keep the new one when OCX holds none. Expression: the fifth column of the Agents table and provision_button in src/ocx/cgi.cr, each posting the existing provision action. Demonstrates: crystal spec spec/ocx/agents_spec.cr (a kept login with no account here gets Create the account here, and pressing it creates the account with capabilities oi234 and leaves the kept password as it was; an account whose workspaces hold a password gets Keep the password its workspaces use); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: ac8c43a1b8 user: opus tags: ocx, audited | |
| 03:07 |
Start a session by picking its thread or ticket, or on a new thread; a grant per runner
Intent: the maintainer found the hash and model fields error-prone and asked for a picker, or at least a way to create the thread the session is for (thread adfc87fba2); then that the homes differ by runner. The form now lists the recently active threads and the tickets not closed, and offers a new thread whose title and first post are the viewer's, posted as the agent login (the console cannot post as the viewer) with a line naming who started it. The model is a text field suggesting the aliases claude --model takes (fable, opus, sonnet), the runner's default when empty. Each runner has its own grant, pre-filled from the latest grant on this repository for that runner, and a grant now says which runner it was for (`runner:`). Host paths stay on the record, never in src/ (I2). The console needs the repository's URL, and now takes a plain http one when no https one was recorded. Expression: Launch.choices and Launch.new_thread; the picker, the new-thread fields, the model suggestions and one grant field per runner in src/ocx/cgi.cr; Grant#runner and Grant.recent(repository, runner); Repository#base_url prefers https; the A5 guard names the console's new thread as a prompt, not an outcome record. Demonstrates: crystal spec spec/ocx/agents_spec.cr (open tickets and threads offered, a closed ticket not; a grant per runner pre-filled; a launch on a new thread posts it as the agent with its starter named, over HTTP to a served fixture, and starts the session on it); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: a7e922cf00 user: opus tags: ocx, audited | |
| 02:45 |
fossil started in one place and never in the console's environment; the Agents list tells the truth
Intent: the maintainer accepted the release and found two faults (thread adfc87fba2). First, "provision: fossil user failed: no such file: user": the console ran fossil in the environment of the request it was answering, and fossil given GATEWAY_INTERFACE answers as a CGI, taking `user` for a file to serve (reproduced here). A session the console launched would have failed the same way. Second, the Agents section said "none provisioned" when this project has agent logins: it listed only logins with a kept password file, and none had one yet. Worse, the failed attempt had already written the password file before fossil ran. The maintainer also restated a standing rule: any external program is started from exactly one place in src/, the function that wraps it being where another implementation goes. fossil was started from four places, only one of which the first fix would have covered. Expression: src/ocx/fossil.cr is the one place fossil is started, with clean_env (HOME and PATH only) for the console and what it starts; Workspace, WorkTree and the process transport go through it; provisioning runs fossil clean, writes the password file only once a repository holds the password, and refuses to reset an existing account whose password OCX does not hold unless reset is ticked; the launch starts with the clean environment; the Agents section lists every agent login the host has a directory for, with its account here, whether its password is kept, and its workspaces. Precept I7 with its guard, which lists every Process call in src/ and fails on a new one or a second one; docs/interfaces.md rows. Demonstrates: crystal spec spec/ocx/agents_spec.cr (provisioning from a process carrying GATEWAY_INTERFACE, REQUEST_METHOD and QUERY_STRING succeeds, and fails with the fix removed; the listing; the refusal and reset), spec/ocx/serve_spec.cr (the launched program sees only HOME and PATH), spec/guards/external_programs_spec.cr; crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 2af75b63aa user: opus tags: ocx, audited | |
| 02:27 |
Sessions launched from the console: workspace serve, detached, answering each new post
Intent: PLAN.md decision 23, slice 2 of the order posted in thread adfc87fba2: a setup or admin viewer starts a session on a thread or ticket from the console, choosing the provisioned agent login, the runner (claude, codex or native), the model and the provider, and the grant to put on the record when it has none (the form offers the latest grant on this repository's threads). The CGI does not run the agent: it starts its own release's `workspace serve` detached under setsid, logging beside the workspace, and answers at once. `workspace serve` makes the workspace with the login's kept password when there is none, then sleeps on the record as `task --wait` does and has the runner answer whatever waits on the login, until a post `OCX stop`, which it acknowledges with a note. So that a session can hold a conversation: the new posts go into each prompt (CLI and native runners); a CLI runner writes its answer whenever the session did not answer the record itself during that turn (it used to stop after the first answer ever); claude continues its conversation after the first turn; the native runner asks once. A runner is picked by name in one place. Expression: src/ocx/serve.cr, src/ocx/launch.cr, src/ocx/runners.cr; `serve` in tools/workspace.cr; the launch action and form in src/ocx/cgi.cr; Session.prompt, CLIRunner.dispatch and turns, Claude --continue, NativeRunner ask-once and turns; Grant.recent; Repository#base_url (the newest baseurl Fossil recorded); three docs/interfaces.md rows. Demonstrates: crystal spec spec/ocx/serve_spec.cr (a thread answered, the next post in the prompt and answered, OCX stop ends the loop with a note and removes ~/run; the launcher's command line and refusals), spec/ocx/agents_spec.cr (the console launches from its release's program, refuses without a recorded https URL or a provisioned login), spec/ocx/cli_runner_spec.cr (a second turn answered, --continue); crystal run ci.cr. Not yet run live: the served CGI starting a real session, which needs this release accepted. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: cab4b663c4 user: opus tags: ocx, audited | |
| 02:17 |
Agent logins provisioned from the console, on every repository of the login group
Intent: the maintainer approved sessions launched from the console with each agent login provisioned once and its password kept per login (thread adfc87fba2, 7b2a643b0b), one password across the login group. This is the first slice: provisioning. Tried on scratch repositories and confirmed by Fossil's own document (www/caps/login-groups.md): each repository of a group keeps its own user table, and a change made with `fossil user` reaches only the repository it names, so each member is set in turn. Capabilities are overwritten, not merged, when Fossil applies a change to a group, so an existing login keeps its own and only a new one gets the agent's oi234. The password is the kept file's, else the one a running workspace of that login already pushes with (so provisioning opus does not cut off this session), else a new random one. Expression: src/ocx/agents.cr (root, logins, group, provision); Repository#configs; the console's provision action and Agents section for setup and admin viewers, the form decoded; docs/interfaces.md row; PLAN.md decision 24, which amends decision 20. Demonstrates: crystal spec spec/ocx/agents_spec.cr (two repositories in a login group both get the login and one kept password; re-provisioning keeps it and leaves hand-set capabilities; a running workspace's password adopted; a person's login, a bad name and admin capabilities refused; only a setup viewer with the token provisions) and crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 2c51338982 user: opus tags: ocx, audited | |
| 01:42 |
A question renders each heading as a paragraph of its own
Intent: the maintainer saw "Options:" sitting at the end of the line before it (thread adfc87fba2, d70c384f96): the question shape put each heading on the next line with no blank line, so Markdown ran it into the paragraph above. The canonical body now opens every heading with a blank line and sets each list apart. The parser already skipped blank lines, so bodies in either form parse the same. Expression: OCX::Question#to_s in src/ocx/question.cr. Demonstrates: crystal spec spec/guards/question_spec.cr (each heading after a blank line, the body re-parses to the same question) and spec/ocx/session_spec.cr; crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: a2fe0cda09 user: opus tags: ocx, audited | |
| 01:22 |
PLAN.md decision 23: sessions are launched from the console, not by a timer
Intent: the maintainer decided (thread adfc87fba2, 931a705dd9) that the CGI is where a session is launched, because the manual process was error-prone and he will not be tied to a console session; timers are for chores such as garbage collection, not session creation. Recorded so the branch carries its own reason, before the slice that builds it. Expression: PLAN.md decision 23; the wake entry in the backlog marked as superseded in part. Demonstrates: crystal run ci.cr Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: 72b0614f8c user: opus tags: ocx, audited | |
| 00:31 |
A turn written in a thread replies to the post it answers
Intent: the maintainer, posting from the Stone app, saw no answer and asked whether agents reply to posts made there (thread adfc87fba2, ce363cb15b). task --write had attached every turn to the thread root, so a threaded reader showed the answer under the root and not under the post it answered. A turn in a thread now replies to the latest post by anyone else, the one being answered, and falls back to the record's own post when there is none (an ask on a fresh thread is unchanged). Expression: OCX::Dispatch.write in src/ocx/dispatch.cr chooses the reply-to. Demonstrates: crystal spec spec/ocx/turns_spec.cr (two turns after another login's note both reply to that note) and spec/ocx/dispatch_spec.cr (the ask on a fresh thread still replies to its root); crystal run ci.cr. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: c7a8b10cd5 user: opus tags: ocx, audited | |
|
2026-09-22
| ||
| 22:31 |
task --wait: a session sleeps on its record until something waits on its login
Intent: the maintainer asked (thread adfc87fba2, e1e793cee7) that the polling a session uses to hear its thread be mechanized where he can see it, rather than held in the session as an ad hoc shell loop, and without thrashing the server. What waits on a login is derived from the record alone: the entries by anyone else since the login's own latest entry, so the waiter keeps no mark of what it has seen and writing any turn clears it. bin/task WORKSPACE --wait syncs the workspace clone every SECONDS (60 by default; one round trip of about 3 KB, 80 ms, measured today), and when something waits it prints those entries and exits, so a session can sleep on it and answer when it returns. Expression: OCX::Dispatch.waiting in src/ocx/turns.cr; --wait and --every in tools/task.cr and its usage; the wake entry in the PLAN.md backlog says which half now exists. Demonstrates: crystal spec spec/ocx/turns_spec.cr (nothing waits after the login's own note; two posts by another login wait in order; the login's reply clears them) and crystal run ci.cr. Tried live on this thread: bin/task on this workspace with --wait returned the maintainer's unanswered post at once. Co-Authored-By: Claude Opus 5.5 (1M context), noreply@anthropic.com Claude-Session: https://claude.ai/code/session_01UrDxZLxg3rJ9AQNcwDvo3n check-in: d4524f4a3d user: opus tags: ocx, audited | |
| 21:33 |
docs/agent-login.md moved to the wiki page "New Agent Workspace"
Intent: the maintainer wants the new-agent steps as a wiki page, which can be corrected in the browser without a check-in; one copy only, so the checked-in file is removed. The page was created with `fossil wiki create` from this file's text and synced. Expression: docs/agent-login.md removed; wiki page "New Agent Workspace". Demonstrates: crystal run ci.cr Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: d006b6ac80 user: fable tags: ocx, audited | |
| 21:26 |
docs/agent-login.md: a new agent login, its thread and its workspace, by commands only
Intent: the maintainer asked for an agent login of its own for this session's contributions, with the steps documented as command-line operations only, since the web UI's user page does not say which checkbox is which capability letter. The steps set names once, make an owner-only password file, create the login with capabilities oi234 (why each, and never s or a), start its thread as the login with bin/thread, create the workspace on branch ocx, write the grant on the thread, and open a shell in the sandbox with run. Expression: docs/agent-login.md. Demonstrates: crystal run ci.cr. The guide has not yet been run on the host; its command blocks parse with bash -n, and fossil user new and fossil user capabilities were tried on a scratch repository. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: 1c09aa45f2 user: fable tags: ocx, audited | |
| 20:46 |
scripts/start-fossil.cr and scripts/sync-fossil.cr: trunk's two scripts, translated; a release carries only the tools/ targets
Intent: the maintainer asked again for the fossil server and backup scripts as Crystal programs, after the first attempt was backed out (thread cde80fe75b, 5797348fd9), with scripts/ as their place and kept out of releases. Each is a direct translation: the host names, paths and repository list are written in the file, nothing else is read, fossil and getcap are run only where the scripts ran them, and the base directory is found from the program's own place in a checkout as the scripts found theirs. The header tells agents not to change the file without the maintainer's approval, and no other file is involved. A release copied every executable in bin/, so the staged release 5349ffa94069 carried the backed-out start-fossil and sync-fossil binaries; it now copies only the shard.yml targets built from tools/. Expression: scripts/start-fossil.cr and scripts/sync-fossil.cr as shards targets; OCX::Deploy.release takes the programs by name and refuses one not built; tools/deploy.cr names the tools/ targets; PLAN.md decision 22; the release-root row of docs/interfaces.md. Demonstrates: crystal spec spec/ocx/deploy_spec.cr (a stale executable in bin/ is not carried; a named program not built is refused) and crystal run ci.cr Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> check-in: 401fa676f5 user: fable tags: ocx, audited | |
| 18:27 |
Backed out whole: the start-fossil and sync-fossil tools of 4723e4b203
Intent: the maintainer's review (thread cde80fe75b, 5797348fd9): the code stated a premise about who runs what and where that it had no business holding; it put fossil process invocations into src/ for reuse against the agreed direction of one interchangeable seam for external tools; a file named host.cr mixed a settings reader with unrelated actions; and the restriction he set was written on the two tool files but not on the settings files that carry his repository list and paths, where a change would actually be made. He asked for them backed out completely. They are, whole, with nothing kept; the accept-smoke fix of 1b438467db stays. Expression: src/ocx/host.cr, tools/start-fossil.cr, tools/sync-fossil.cr, spec/ocx/host_spec.cr and docs/settings removed; the shards targets and the interfaces row removed. Demonstrates: crystal run ci.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 5349ffa940 user: fable tags: ocx, audited | |
| 17:52 |
Accepting a release smokes the copy as a plain request; a short body is not a fault
Intent: the maintainer pressed Accept update and got "did not answer as the console: End of file reached": the smoke ran the copy with the accepting request's own environment, so the copy read REQUEST_METHOD and CONTENT_LENGTH, found no body, and died. The copy is now smoked with a cleared environment and closed input, as a plain request, and the CGI takes a body shorter than announced as it is. Expression: Deploy.smoke with clear_env and closed input; tools/cgi.cr rescuing end of file on the body; the deploy spec's stub refuses to answer when it sees the caller's environment or any input. Demonstrates: crystal spec spec/ocx/deploy_spec.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1b438467db user: fable tags: ocx, audited | |
| 17:35 |
PLAN.md: the maintainer's second round on the gap inventory, as backlog
Intent: the maintainer answered the remaining rows of the trunk gap inventory with decisions; each is carried into the backlog with his reason so the next slices are chosen from the record, and the replies stand beside his words on the wiki page. Expression: PLAN.md backlog, one entry naming every decision of the round. Demonstrates: crystal run ci.cr (the plan's precept table still ledgered by spec/guards/ledger_spec.cr) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: e1e8eb65fe user: fable tags: ocx, audited | |
| 17:33 |
The host's two jobs as tools: start-fossil and sync-fossil, pinned to trunk's scripts
Intent: the maintainer approved (thread cde80fe75b, 4c905a7a75) re-implementing start-fossil.sh and scripts/sync-fossil.sh as Crystal tools that reproduce them exactly and then never change without his explicit approval on the record. Each takes its host's settings from a file he names (code carries no host paths, precept I2; nothing comes from the environment, C6), can print exactly what it would run, and is pinned argument for argument by a spec against expectation files kept as data under docs/settings; the standing instruction to agents is in each file's header. start-fossil replaces its process with fossil server, so a service unit supervises fossil itself; sync-fossil runs every step and reports each failure. The CGI now states its own derivation time, since the page footer times the server; the text console ends with the viewer's questions. Expression: OCX::Host (settings, Server.argv, Sync.plan) in src/ocx/host.cr; tools/start-fossil.cr and tools/sync-fossil.cr with --dry-run; docs/settings/*.conf and *.expected for the public server and the maintainer's workstation; shards targets; docs/interfaces.md; Console.render's timing and render_text's order. Demonstrates: crystal spec spec/ocx/host_spec.cr (each documented host's fossil server line equal to trunk's; sync in an existing checkout and clone otherwise; the 24 repositories; refusals) and bin/start-fossil docs/settings/start-fossil.ollama.conf --dry-run, whose one line equals start-fossil.sh's for this host Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 4723e4b203 user: fable tags: ocx, audited | |
| 15:04 |
PLAN.md: precept I6 and five backlog items from the maintainer's decisions on the gap inventory
Intent: the maintainer answered ten rows of the trunk gap inventory. Confinement is to be interchangeable, so precept I6 states it: a written plan and one replaceable call site, bwrap named in one file and started in one place, guarded. His blocking reason for trunk's daemon (long runs watched turn by turn, stopped cleanly, listable) becomes a design in the backlog without a coordinator; the sweep of trunk's session branches, peers as grant and prompt and ask, health checks in the console, and an MCP server as one adapter over the same tools are backlog items with his reasons. Expression: PLAN.md precept I6 and backlog; spec/guards/confinement_spec.cr. Demonstrates: crystal spec spec/guards/confinement_spec.cr (bwrap in src/ocx/sandbox.cr only, one start) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 2deb5a0de5 user: fable tags: ocx, audited | |
| 14:10 |
Phase F.5: one console derivation, rendered for the page and for a shell; a question in a thread is for its starter
Intent: the maintainer asked to see what is pending for others and for a tool the agent can use to poll for its questions, without a second code path for the same feature. The console is now one derivation into sections and two renderings: HTML for the served page and text for `bin/console CLONE --for LOGIN`, which is how an agent at a shell asks what is waiting on it. Posts in threads count in In work and Done recently as ticket turns do. And a question in a thread that names no one is for the thread's starter, since the first such poll found the maintainer's question on the agent's own thread filed as work in progress rather than as his. Expression: Console.sections, Console.render and Console.render_text in src/ocx/console.cr; Activity::Item#path and #stamp (Fossil's own time, so turns from tickets and threads merge without ties at the second); Questions::Open#owner and #for?; bin/console CLONE [--for LOGIN] [--html [--url BASE]]; tools/deploy.cr says which links are in view inside a workspace; docs/interfaces.md. Demonstrates: crystal spec spec/ocx/console_spec.cr (the same rows in both renderings; a question in the viewer's thread naming no one is theirs; a thread's ask under In work), and this workspace's own poll on 2026-09-22, which found the maintainer's question Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 8fdddfa2ee user: fable tags: ocx, audited | |
| 13:52 |
The grant explained, and every interface documented and pinned (precept C7)
Intent: the maintainer could not tell program from command in a grant, and asked that interface contracts be documented and tested for regressions. program is gone: command is what a session runs, and a first word that is an absolute path is bound read-only into the sandbox, which is all program was for; each grant line is now explained where the notation is defined. docs/interfaces.md lists every contract OCX has with the record, with Fossil, with the workspace and with serving, each with the spec that pins it, and a new precept C7 guards that every row names a spec that exists and that every notation head the code writes is listed. Expression: OCX::Grant without program; docs/interfaces.md; spec/guards/interfaces_spec.cr; PLAN.md precept C7. Demonstrates: crystal spec spec/guards/interfaces_spec.cr and crystal spec spec/ocx/grant_spec.cr (unknown lines ignored, the command bound by its path) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: dcce209bdb user: fable tags: ocx, audited | |
| 13:27 |
Phase D.6: what a workspace is given is written on its record
Intent: the maintainer's point: bindings on the command line leave no trail, and the database should hold everything needed to make and start a workspace. A grant is now a turn on the record in OCX's notation (OCX workspace: then branch, tools, homes, program, command), written by a login, so who granted what and when is on the record; create takes the branch from it, run and start take the tools, homes and command from it, and each refuses the same settings on the command line when the record decides them. Only the login and its password, which are not for the record, are given at creation. A grant is a turn that answers nothing. Expression: OCX::Grant (parse, to_text, latest, resolved_command) in src/ocx/grant.cr; Dispatch::Turn::Grant; Workspace.create honouring the record's branch; bin/workspace grant, and run and start reading the grant after a sync. Demonstrates: crystal spec spec/ocx/grant_spec.cr (the notation whole; a grant is no answer; the latest grant on a thread names the branch a workspace is created on and refuses another; under bubblewrap a session started from the grant sees its home bound) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 9f2257d401 user: fable tags: ocx, audited | |
| 13:23 |
Phase G.5: whoever stages a release cannot choose what runs
Intent: the maintainer's objection: if the agent offers next and he selects what is active, the agent must not keep the means to set it. The release root now has two areas. staged/ is the agent's: releases are written there and next is offered there, and it is the only path a workspace that builds releases is given. The root is the server's: current, previous, the token and every accepted release, each a copy taken from staged/ at acceptance and smoked before current moves, so what runs cannot be altered afterwards by whoever staged it. The first install is the root owner's, once. Expression: OCX::Releases (staging, stage, install, accept with a check on the copy, rollback, token in the root); Deploy.release writes into staged/ and offers, --install takes as current; the CGI finds the root from either area, and its accept smokes the copy; decision 21 in PLAN.md and the grant item retired from the backlog. Demonstrates: crystal spec spec/ocx/releases_spec.cr (offer, accept as a copy the staged area cannot alter, rollback both ways, a failed check changing nothing, the token in the root), spec/ocx/deploy_spec.cr (into staged/, offered not served, the served links to current and staged/next) and spec/ocx/cgi_spec.cr (the preview program in staged/ finds the root; accept copies and withdraws the offer) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: f9ba37e31a user: fable tags: ocx, audited | |
| 13:12 |
Phase D.5: workspace garbage collection
Intent: a workspace lives as long as its record needs it, and nothing a person might still want is removed: one bound to a ticket is done when the ticket is Closed, one bound to a thread when the thread has been quiet longer than a stated age; any with uncommitted changes in its tree or check-ins its clone has not sent is kept and says why. Verdicts are derived from the record after a sync. The first slice made from inside a workspace of its own, the deploy agent's, bound to thread cde80fe75b. Expression: OCX::Collector (sweep, judge, collect) in src/ocx/collect.cr; bin/workspace gc --root DIR [--quiet-days N] [--dry-run]. Demonstrates: crystal spec spec/ocx/collect_spec.cr (four workspaces: an open ticket kept, a closed one removed, one with uncommitted work kept and named, a thread removed once quiet; a dry run removing nothing) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 6c97bae76c user: fable tags: ocx, audited | |
| 12:53 |
A shell in a workspace is nothing special: run keeps the caller's streams
Intent: the maintainer asked why a shell needed special casing. It did not: the terminal is whatever the caller handed the process, and the sandbox's job is confinement, not the terminal. The plan no longer detaches the process from its terminal (a command whose output is piped has no terminal to protect, and one run from a terminal was given it on purpose), the terminal switch and the shell verb of the last two check-ins are gone, and bin/workspace run inherits this process's streams, so `run … -- bash -i` works as expected and takes --home like start. Expression: Sandbox.plan without --new-session or a terminal parameter; Session.start unchanged in meaning; the run verb of tools/workspace.cr resolves its program, takes --home, and inherits standard streams; no shell verb. Demonstrates: crystal spec spec/ocx/sandbox_spec.cr (the plan takes no new session) and spec/ocx/session_spec.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 0693253da5 user: fable tags: ocx, audited | |
| 12:51 |
The sandbox keeps the terminal for a person or an interactive agent
Intent: the maintainer opened a shell in his workspace with bin/workspace start and got "cannot set terminal process group: Not a tty". The plan gave every process a new session (bubblewrap's --new-session), right for a runner nobody watches, since a confined process sharing the terminal could type into it, and wrong for a person at a shell or an agent run interactively. The terminal is now a stated choice in the plan. Expression: Sandbox.plan and .write take `terminal`; Session.start keeps the terminal when its input is this process's; the shell verb always does; run never does. Demonstrates: crystal spec spec/ocx/sandbox_spec.cr (--new-session present by default, absent with terminal) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 4bdc24b5cb user: fable tags: ocx, audited | |
| 12:49 |
bin/workspace shell: a person looks around a workspace under the same confinement
Intent: the maintainer asked how to open a shell in a workspace to check it out. start would write an ask onto the record and close turns afterwards; run buffers output and closes standard input. A shell is the same confinement with nothing asked and nothing closed, on this terminal. Expression: the shell verb in tools/workspace.cr: writes the sandbox with the tools and homes given, runs COMMAND (sh by default) with standard streams inherited, exits with its status. Demonstrates: crystal build tools/workspace.cr; bin/workspace shell RECORD --root DIR opened on the deploy agent's workspace on 2026-09-22 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 694b495c15 user: fable tags: ocx, audited | |
| 12:36 |
PLAN.md: the deploy agent's record, and four backlog items from the maintainer's questions
Intent: the session that builds this branch now has a record and a place: thread cde80fe75b on this repository, the wiki page "Deploy Agent Memories" for what it must remember, and a workspace bound to that thread on branch ocx with the current release as its tools. The maintainer's questions about invoking a session through Fossil, hidden state, garbage collection and who may build releases are answered in the backlog as work not yet done: a wake for bound sessions, workspace garbage collection, confining the console's own actions, and naming the deploy agent's grant in its binding. Expression: PLAN.md backlog, four items. Demonstrates: crystal run ci.cr (the backlog is part of PLAN.md, whose precept table spec/guards/ledger_spec.cr still ledgers); the thread cde80fe75b and the wiki page "Deploy Agent Memories" on this repository Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: e21560ea02 user: fable tags: ocx, audited | |
| 12:33 |
bin/thread: a session's record started from the command line
Intent: a session bound to a forum thread needs the thread to exist first, started by the agent's own login; Fossil's CLI cannot post to the forum, so OCX's own composer and sync client do it, the same path every other turn takes. Expression: tools/thread.cr (--clone, --login, --password-file, --title, the body on standard input; prints the post's hash); a shards target. Demonstrates: crystal build tools/thread.cr, then the thread cde80fe75b started on this repository on 2026-09-22 for the deploy agent's workspace Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 59f52bea75 user: fable tags: ocx, audited | |
| 12:16 |
Phase G.4: the console reads the served repository Fossil names, for every project
Intent: the maintainer found /stone/ext/ocx empty: the console read only clones named in a sources file. Fossil already names the served repository in every ext request, and the console is the server's own ext program, part of serving and not an agent, so it now reads that repository directly, query-only and with Fossil's busy timeout, as Fossil's pages do: every served project has a console, nothing is configured, nothing is synced, nothing is stale. Precept I4 is refined to admit the programs the server spawns to answer a request; the nine corruptions that gave I4 its reason came from writers and WAL, never from a reader. Expression: CGI.respond builds its Source from FOSSIL_REPOSITORY with links relative to the project (../tktview, ../forumpost, ../info); the sources file, its copy in a release and CGI.sources are gone; the I4 host guard admits a holder whose parent is the fossil server; PLAN.md I4 and decision 20. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (a question on the served repository the request names, linked relative to the project; a repository that is not there answered plainly) and crystal spec spec/ocx/deploy_spec.cr (a release without a sources file) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: c4c5434c1e user: fable tags: ocx, audited | |
| 10:59 |
Phase G.3: releases staged behind the gate, accepted or rolled back from the console
Intent: the maintainer wants good checks before a deploy without being the bottleneck at a shell, and easy rollbacks. So the deploy step gates on ci.cr and refuses when red, releases a copy named for its check-in with the gate's result beside it, smokes it, and stages it as next rather than serving it; the console shows a setup or admin viewer what is current, what is staged (with a preview link served as ocx-next) and where a rollback goes, with Accept update and Roll back buttons that move only the links among the releases. Each is a POST carrying the token kept owner-only in the release root, refused otherwise and refused to any other viewer. The served links, made once, never move again. Expression: OCX::Releases (state, stage, accept, rollback, token, relink) in src/ocx/releases.cr; Deploy.release writes the gate note, stages, and makes LINK and LINK-next; tools/deploy.cr runs ci.cr first (--no-gate to skip) and --current for a first install; CGI::Request carries method, query and body, CGI.act performs the action, CGI.releases_html shows the table; a name ending in -page renders bare, any other in the skin; the C6 guard names the three request variables added; decision 19. Demonstrates: crystal spec spec/ocx/releases_spec.cr (first as current, later as next; accept; rollback both ways; token owner-only), spec/ocx/deploy_spec.cr (gate note, staging, the served links made once, a failed smoke staging nothing) and spec/ocx/cgi_spec.cr (releases shown to a setup viewer only; accept refused to a reader, to a GET and to a wrong token; accept and rollback moving current) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 4cb97aedb7 user: fable tags: ocx, audited | |
| 10:46 |
Phase F.4: the console inside the Fossil skin, leading with what is for the viewer, then what agents are doing
Intent: the maintainer's correction: the first console threw away months of learning. Trunk put its console inside the Fossil chrome so a logged-in person keeps every feature of the Fossil web server and, in the same place, sees what needs them and what agents are doing. That is restored: the view is the same program, wrapped by Fossil when served under the name ocx (the fossil-doc form the server documents), bare under any other name, and it reads: For you (questions addressed to the viewer), Open questions, In work and Done recently (each open ticket's latest turn, sorted by OCX's notation), Check-ins on agent branches (task/, and trunk's agent/ and session/), Posts in threads, Audits. All derived from the record; nothing stored. Setup and admin capabilities read everything, as in Fossil. Expression: OCX::Activity (ticket_turns, posts, task_checkins, audits, in_work, done) in src/ocx/activity.cr; Console.render(source, viewer, tickets, threads, chrome) in the section order above; CGI::Request carries the served name and chrome?; bin/ocx-cgi passes its own link name; decision 18 in PLAN.md. Demonstrates: crystal spec spec/ocx/activity_spec.cr (turns, a post, an agent-branch check-in and an audit derived from a seeded repository; work sorted from reports), spec/ocx/console_spec.cr (the viewer's questions first; the skin form; the empty case) and spec/ocx/cgi_spec.cr (skin under ocx, bare under another name, setup reads all); rendered against the console's clone of this repository on 2026-09-22 with twenty agent-branch check-ins, twenty posts and eight audits Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 93aad63d53 user: fable tags: ocx, audited | |
| 10:35 |
The fixture amends a comment with flags every Fossil has; the maintainer's failing audit of 1484fa3062 answered
Intent: the maintainer's Fossil 2.23 has no --no-verify-comment on amend, so the check-ins spec could not run on his machine (audit tag on 1484fa3062). The flag was added for Fossil 2.27 here without checking older versions, and it was never needed: 2.27 amends a two-line comment non-interactively without it, standard input closed, as tested. Fixture code must use only what both Fossils accept; the maintainer's version is the floor. Expression: Fixture#amend passes only the hash and -m. No other fixture or tool flag is version-specific as far as is known; commit --nosync and changes --extra are older than 2.23. Demonstrates: crystal spec spec/ocx/checkins_spec.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 045ad34081 user: fable tags: ocx, audited | |
| 10:29 |
Commits never sync by themselves; the maintainer's failing audit of H.2 answered
Intent: the maintainer's run of ci.cr failed (audit tag on 8ba73050a5): on his machine Fossil autosyncs by default, so a commit in a workspace pulls first and, with the fixture's server already gone, refuses; and a fixture clone's commit had already pushed what a spec expected to find unsent. Here autosync was off globally, which hid it. Syncing is a named step in this design (Workspace#sync, Dispatch.push_unsent), never a side effect of a commit, so it is now off on every clone OCX makes and every commit OCX makes says so. Expression: Workspace.create sets autosync off on the workspace's clone; WorkTree#commit passes --nosync; the fixture's clones set autosync off; the one spec that commits by hand passes --nosync. Reproduced here by setting autosync on globally for the run (three failures, the maintainer's), green after, the setting restored. Demonstrates: crystal spec spec/ocx/dispatch_spec.cr spec/ocx/native_runner_spec.cr with `fossil settings autosync on --global` in force Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1484fa3062 user: fable tags: ocx, audited | |
| 10:23 |
Phase H.2: served beside trunk, and the served repositories back in rollback mode
Intent: the maintainer's ruling: the old executable is not replaced; if the contract is good the two run in parallel until he takes one down. So precept A4 judges OCX's own served link and nothing of trunk's, and the host guards can be green while trunk is still served. The three served repositories running in WAL mode were migrated in place to rollback mode with Fossil's own tool, as the maintainer directed, and the I5 guard now passes. Expression: spec/host/deploy_spec.cr judges the link named ocx under the ext root (absent: pending, saying so; present: resolves into a release, never a checkout); PLAN.md precept A4 reworded and decision 17. Demonstrates: crystal run ci.cr -- host (I5 green after the migration; A4 pending until OCX is served here) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 8ba73050a5 user: fable tags: ocx, audited | |
| 02:13 |
Phase H.1: the parity contract filled in, and what the gate still waits for
Intent: the branch says where it stands against the contract it set out with, row by row, each citing one check-in the gate can resolve and the demonstration a reader can run, and it says plainly what is not yet shown and why: serving the console on this host and any live run on the public repository are the maintainer's steps, the second waiting for the push boundary the backlog names. The merge gate fails today on purpose. Expression: PLAN.md's parity contract: five rows demonstrated, one specified (the thread as session log has its specs and no live run in a thread); the status vocabulary gains `specified`; the deploy command and the served path are written down. Demonstrates: crystal run ci.cr -- merge-gate (fails, naming every row not yet shown) and crystal run ci.cr (green) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 61878c8f0a user: fable tags: ocx, audited | |
| 02:11 |
Phase G.2: the served console shows one project, as far as Fossil lets the viewer read
Intent: trunk gated its console on a login (docs/fossil-auth-cgi.md) for two reasons: that console could act, and a viewer must see only the projects they may see. This console cannot act, so the first reason is retired; the second still binds and is met with Fossil's own means: the CGI takes from the request exactly what Fossil says about it (which repository it is for, which user, which capabilities), shows that one project among its sources, and shows its tickets and its threads only when the capabilities read them, as the project's own pages would. Precept C6 keeps its guard, now naming those three variables as the one thing a program may take from the environment, and nothing else. Expression: OCX::CGI::Request (project, tickets?, threads?) and CGI.respond choosing the source and what to show, in src/ocx/cgi.cr; Console.render's tickets and threads switches naming what is withheld; tools/cgi.cr reading only FOSSIL_REPOSITORY, FOSSIL_USER and FOSSIL_CAPABILITIES; the C6 guard admitting exactly that file and exactly those names; PLAN.md precept C6 and decision 16. Demonstrates: crystal spec spec/ocx/cgi_spec.cr (a viewer who reads tickets and forum sees the project the request is for and no other; one without forum capability is told threads are withheld; one with neither is told so; an unknown repository and a request naming none are answered plainly) and crystal spec spec/guards/environment_spec.cr (the exception is one file and three names) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 4ca6b05629 user: fable tags: ocx, audited | |
| 02:07 |
Phase G.1: build, release and serve as named steps, and the console as the served program
Intent: build produces programs; serving them is a separate, named step (precept A4). A release is a copy of the built programs under a root, named for the check-in they came from, smoked by running the served program on that copy; only then are the root's `current` link and the served link under the Fossil server's ext root moved to it, and a failed smoke moves nothing, so the served link never resolves into a checkout (host guard A4). The served program is the console as a CGI that reads no request and no environment: it answers every request with the console page for the sources named in a file deployed beside it. Expression: shard.yml targets for task, workspace, audit, console and ocx-cgi (`shards build`); OCX::CGI.respond and .sources in src/ocx/cgi.cr, bin/ocx-cgi finding console.sources beside its own real path; OCX::Deploy.release, .smoke and the link swap in src/ocx/deploy.cr; `crystal run tools/deploy.cr -- --root DIR [--served LINK]`. Fixture.with_dir for specs that need no repository. Demonstrates: crystal spec spec/ocx/deploy_spec.cr (a release from stub programs: the copy, the smoke, the links, a name used once; a failed smoke leaving the links as they were) and crystal spec spec/ocx/cgi_spec.cr (the response for named sources; the answer when none are named; a malformed line refused) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: a132df1a8d user: fable tags: ocx, audited | |
| 02:04 |
Phase F.3: the console, a view over derived queries
Intent: the console shows the maintainer what the record implies and nothing it stores: for each served project, the open questions on its tickets (F.1's report) and in its threads (F.2's definition), every row linking to the project's own served page, where the reply is made. It reads clones, never served files, and remembers nothing. Expression: OCX::Console.render over Sources (a project's name, the clone read, the served URL) in src/ocx/console.cr, plain HTML with every value escaped; bin/console NAME=CLONE=URL... prints the page. Repository#table? is public so the forum and ticket readers answer empty for a young repository that lacks those tables. Demonstrates: crystal spec spec/ocx/console_spec.cr (a ticket question and a thread question rendered with their links and escaped titles; a project with none says so) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: d035b425dd user: fable tags: ocx, audited | |
| 02:01 |
Phase F.2: open questions in threads, by code held to one shared definition
Intent: in a forum thread an open question is the latest post when it is in the question shape, for the login its For: line names; any later post closes it. Threads have no report table, so this is code, in OCX and in Stone alike; drift is prevented not by one implementation but by one functional definition: a versioned file of sample threads with the verdict each must get, which every implementation runs as its own spec. Expression: src/ocx/reports/open-questions-threads.json (the definition and seven cases: open, closed by a reply, closed by the asker's own note, the latest of two questions, no For: line, prose that mentions a question, text before the shape); Questions.verdict over a thread's posts and Questions.in_threads over a clone, in src/ocx/questions.cr. Demonstrates: crystal spec spec/ocx/questions_threads_spec.cr (every case of the definition; then three threads in a real repository, one of them open) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 5e5b6adda9 user: fable tags: ocx, audited | |
| 02:00 |
Phase F.1: open questions on tickets, by one report the server and OCX both run
Intent: an open question is derived from the record, never stored: on tickets it is the latest comment appended to an open ticket when that comment is in the question shape, and whom it is for is its For: line. The definition is one SQL file; installed as the ticket report of the same title on a served repository it is run by the Fossil server for the console and pulled by Stone with the ticket configuration; embedded in OCX it is run against a clone. One definition, so three readers cannot drift. Expression: src/ocx/reports/open-questions.sql; OCX::Questions.on_tickets and Repository#report in src/ocx/questions.cr and repository.cr; the fixture installs a report. Compose.ticket_change now records an appended comment also as the change's own comment with its login and mimetype (the ticketchng table's columns, as Fossil's ticket page fills them), so a report over ticket changes sees each turn by itself; Fossil filled those columns with nothing for OCX's earlier appends. Demonstrates: crystal spec spec/ocx/questions_spec.cr (four tickets: a question latest, a question answered after, a question on a closed ticket, a note; the reader lists the one open question and the server's own report page shows the same) and crystal spec spec/ocx/compose_spec.cr (the change's own comment cards) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 43a3e7d68e user: fable tags: ocx, audited | |
| 01:55 |
Phase E.3: the codex runner by shape, bin/task --cli, and one ticket through two runners
Intent: the first comparison the plan promised: one ticket answered by more than one runner with evidence of the same shape, each in its own workspace, as its own login, on its own branch. The codex CLI joins as an adapter whose command is specified and whose live conformance run waits for a host that may launch it. Two faults the comparison exposed are fixed: a ticket's task was built from its accumulated comment, so every ask after the first was malformed; and a branch made by the session itself made OCX's own commit fail. Expression: OCX::Runners::Codex in src/ocx/runners/codex/runner.cr (full-auto mode inside OCX's sandbox, rooted in the tree, last message to ~/answer). bin/task WORKSPACE --cli claude|codex [--program] [--home]... [--tools] [--model]. Task.from takes a ticket's comment as last set whole (decision 15). WorkTree#commit moves the checkout onto the branch when it already exists; #branches. CLIRunner keeps the session's output at ~/printed. PLAN.md: parity row 1 demonstrated for native and claude with the live evidence; decisions 14 and 15; backlog: a runner's login can push anywhere (seen live: the model committed on the scratch trunk through crystal -e), to be bounded on the served repository with Fossil's xfer-push-script; the codex live run. Demonstrates: crystal spec spec/runners/codex_conformance_spec.cr (the command's shape and the answer file; live case tagged), crystal spec spec/ocx/dispatch_spec.cr (the task unchanged by what is appended to the ticket), crystal spec spec/ocx/worktree_spec.cr (a commit landing on the branch after the checkout was moved off it); live on 2026-09-21 as PLAN.md's parity row 1 records Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: d79b37bfa6 user: fable tags: ocx, audited | |
| 01:39 |
Phase E.2: the claude runner, conformant through the real CLI
Intent: the claude CLI, on the Pro login it keeps under its home, answers a workspace's record as one runner among others: started as a session in the sandbox with its login directories bound under the workspace, asked to print its answer as text and exit, in its default permission mode with its tools named (bypassing permissions is refused to the sandbox's user, which is root), the program bound read-only at its resolved path when it lives outside the system paths. Its answer, in report notation or not, reaches the record with the check-in named whether the agent committed on the branch itself or left the tree changed for OCX to commit. Expression: OCX::Runners::Claude in src/ocx/runners/claude/runner.cr (the only file that may name the CLI's flags, precept I1). Sandbox.plan binds `programs` read-only at their own paths and Session.resolve names an absolute program by its real path. CLIRunner#turn names the check-in when the branch tip moved during the session, filling it into an agent's own report that lacks one. ci.cr's `-- live` runs the conformance specs through the real CLIs. Demonstrates: crystal spec spec/runners/claude_conformance_spec.cr (the command's shape; and, tagged live, a ticket answered through the real CLI in a workspace: hello.txt made and committed on the workspace's branch, the report on the record naming that check-in. Run five times on 2026-09-21: two failures before the check-in was filled in from the branch, one after it whose evidence was not captured, two passes; the spec now carries the record, branch and tree in its failure message) and crystal spec spec/ocx/cli_runner_spec.cr (an agent's own commit named in its report) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 500ecf7473 user: fable tags: ocx, audited | |
| 01:27 |
Phase E.1: one runner interface, and a runner that is a command-line agent
Intent: the native runner and any command-line agent answer a workspace's record behind one interface, so a task can be run through more than one of them and compared. A CLI runner is a session in the workspace's sandbox (D.4) whose printed answer, or the file it leaves, becomes the turn on the record: as it is when the agent answered in report notation or changed nothing; headed by a report naming the check-in OCX makes of the tree's changes otherwise, the agent's words kept after; a report of failure when it ends saying nothing; nothing at all when the agent answered the record itself with `task ~ --write`. Expression: OCX::Runner (name, dispatch(workspace)) in src/ocx/runner.cr; NativeRunner is one. OCX::CLIRunner in src/ocx/cli_runner.cr (tools, homes, abstract command(workspace) with the prompt placeholder, answer(workspace, printed), dispatch). WorkTree#changes lists managed and unmanaged changes and commit adds the unmanaged. ci.cr excludes the tag `live` unless run as `-- live`, for the conformance specs that drive the real CLIs. Demonstrates: crystal spec spec/ocx/cli_runner_spec.cr (a shell script standing in for an agent: a report printed is the answer; a changed tree is committed on the workspace's branch and the words follow the report; a silent exit is a failure; a record answered from elsewhere is left alone) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1d590ec03e user: fable tags: ocx, audited | |
| 01:21 |
Phase D.4: bin/workspace start, a session born in the sandbox
Intent: a command-line agent, or a person's shell, begins its work already inside a workspace: the workspace is its home, so the binding is ~/binding.json and the tree ~/tree; OCX's prompt (the ask in the question shape, then where things are and how to answer) is the argument the command names and is also written at ~/prompt; the ask is on the record before the session starts and is not repeated; when the session ends the handler closes the turns the record holds and the standing is printed. A CLI's own login directory is the one host path bound under that home, read-write, by its basename. Expression: OCX::Session (prompt, command, start) in src/ocx/session.cr; Sandbox.plan's extra paths become homes bound under the workspace, and Sandbox.start runs a command with given standard streams; bin/workspace start RECORD --root DIR [--tools DIR] [--home PATH]... -- COMMAND; bin/task takes a workspace directory as well as a hash prefix, so `task ~` works inside, and --write puts a turn from standard input on the record as the workspace's identity. Decision 13 in PLAN.md. Demonstrates: crystal spec spec/ocx/session_spec.cr (the prompt composed from the ask and put in the placeholder's place; under bubblewrap, a shell session that reads its binding, writes through its bound login directory, sees the prompt, and writes in its tree; the ask made once across two starts; a request written from elsewhere as the login closed by the handler when the session ends) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 5014eb0c34 user: fable tags: ocx, audited | |
| 01:15 |
Phase D.3: the handler and the native runner inside a workspace
Intent: a task is a workspace and a prompt. The handler runs a request from the record in the addressee's own workspace, confined by its written sandbox, and writes the result as that workspace's identity; the native runner answers in a workspace's tree with its commands confined the same way, writes the ask and the answer as the workspace's identity, and syncs the clone so the check-in goes out and the record comes in. Nothing works in a temporary place any more: what a runner did is where it lives. Expression: Workspace#run (an allowed command under the written sandbox when bubblewrap is present, else in the tree) and Workspace#sync. Dispatch.handle(workspace) and NativeRunner#answer(workspace) and #dispatch(workspace) take the workspace and nothing else. bin/task names a workspace under --root instead of a clone, a login and a password. The spec fixture's serve returns the block's value. Demonstrates: crystal spec spec/ocx/turns_spec.cr (a request run in the addressee's workspace, under bubblewrap where it exists, its result written as the addressee over the clone's own remote) and crystal spec spec/ocx/native_runner_spec.cr (every case of C.4 in a workspace bound to a served ticket, the check-in reaching the server by the workspace's sync) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 279454b13f user: fable tags: ocx, audited | |
| 01:08 |
Phase D.2: the sandbox, written down, and bin/workspace
Intent: any process that works in a workspace, a session, a handler's command, a native runner, must be confined the same way and started the same way, today or later. So the confinement is generated from the workspace directory and written into it as the exact invocation, and one tool creates a workspace and runs a command under it. Expression: src/ocx/sandbox.cr (Sandbox.plan: the workspace read-write at its own path, its peer views read-only, the system's programs and trust roots read-only, the network open, the environment cleared but for HOME and PATH, an optional tools directory on the path; .write puts the plan in the workspace one argument per line; .run starts a command under it). tools/workspace.cr (bin/workspace create RECORD --root --remote --login --password-file; run RECORD --root -- COMMAND; show). Workspace.create now takes the record's hash and learns from its own clone whether it is a ticket or a post. Demonstrates: crystal spec spec/ocx/sandbox_spec.cr (the plan's binds and their order, written and read back; under bwrap: the tree writable and the host outside the workspace invisible, HOME and the working directory as planned, an environment of three variables at most; a peer view readable and not writable) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 2910c478df user: fable tags: ocx, audited | |
| 01:05 |
Phase D.1: the workspace directory
Intent: a runner lives somewhere for as long as its record is open, and a session on another machine or a later day must be able to start there the same way: everything it needs on disk, in one directory, and nothing remembered elsewhere. The tree a turn runs in, built as C.2, becomes one component of that directory. Expression: src/ocx/workspace.cr (Workspace.create clones the server as the login with the password remembered by Fossil's own `remote add`, opens the tree on the branch, keeps the password in an owner-only file, writes the binding, and materialises read-only clones of the peers the tree's .peers/peers.json names at their tracks; Workspace.open reads the binding back; #tree, #task, #identity, #peers). The directory is named by the record's hash. src/ocx/worktree.cr is the C.2 class under its own name, WorkTree, used unchanged by the handler and the native runner. PLAN.md gains Phase D, workspaces, in four slices, and the later phases move down a letter. Demonstrates: crystal spec spec/ocx/workspace_spec.cr (a workspace created against a served fixture, its permissions, its identity and task read back, a commit in its tree pushed by a bare fossil sync, the workspace reopened from the directory, the refusals; a peer declared in the manifest materialised at its track as a read-only view) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 39f7f0ef31 user: fable tags: ocx, audited | |
|
2026-09-21
| ||
| 23:13 |
bin/task exits by the standing it printed
Intent: the live run of C.5 printed the standing as answered and then exited 1, because the exit code was taken from the standing read before the clone had synced. The exit code must follow what was shown. Expression: tools/task.cr keeps the standing read after the sync and exits by it. Demonstrates: crystal run ci.cr; bin/task on a dispatched record exits 0 when the standing it prints is answered and done Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 27952eadb5 user: fable tags: ocx, audited | |
| 23:12 |
Phase C.5: the tool, and a live run
Intent: the phase ends with something a person runs: one command that dispatches a record to the native runner, or runs the handler for an answer arriving from elsewhere, or reads where a record stands; and a live run against a real local model, repeatable. Expression: tools/task.cr (bin/task RECORD with --provider and --model for the native runner; --handle --for ADDRESSEE for the handler; --status --for ADDRESSEE to read the standing; the clone synced before the record is read back, with the failure said plainly). Dispatch.push_unsent sends a task branch's artifacts to the record. PLAN.md marks parity rows two and three demonstrated, citing C.4 and C.3. Demonstrates: the live run of 2026-09-21 on the scratch served repository: bin/task ran ticket 5f2b9319d6 with codex-crystal-gemma4, the check-in landed on its task branch, the ask and the report on the ticket, and bin/task --status read the standing back as answered by ocx; crystal spec spec/ocx/dispatch_spec.cr for push_unsent Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 60276ed418 user: fable tags: ocx, audited | |
| 23:10 |
Phase C.4: the native runner
Intent: the in-process way of answering: a chat protocol driven through tools until the model finishes or the step budget ends. Its tool turns run in the task's workspace in memory, the way the handler runs a request from the record, and only the ask and the answer reach the record, so a reader sees the same two entries whether the answer came from this process or another. Every way the loop can go wrong is reported, not hidden. Expression: src/ocx/provider.cr (Provider with a Protocol instance; Message, ToolCall, ToolSpec). src/ocx/chat_protocol.cr (the /api/chat shape, tool calls structured or as the JSON text local models write). src/ocx/native_runner.cr (NativeRunner#answer: the mechanical framing, the loop, list, read, write, run and finish performed in the workspace, a commit in this project's own format; #dispatch: ask, answer, write back, standing read from the record). spec/support/fake_model.cr is a scripted model server. Demonstrates: crystal spec spec/ocx/native_runner_spec.cr (one dispatch leaving the ask and the report on the ticket and the check-in on the task branch; the budget running out; prose with no tool; a malformed call and a path outside the checkout; a second answer continuing the branch; an unreachable model) and crystal spec spec/ocx/chat_protocol_spec.cr (the wire shape, the textual tool-call shapes, an unreachable provider named) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 882d08c908 user: fable tags: ocx, audited | |
| 23:08 |
Phase C.3: turns and the handler
Intent: an answer may take several turns and arrive later from another process, so the loop between an ask and its answer must close on the record. Entries by the addressee after the ask are turns: a request asks OCX to run a command in the task's workspace, and OCX's handler runs it and writes the result back, paired to the request; a note is progress and closes nothing; anything else is the answer, in report notation or a person's prose, and fixes the standing as answered. The same handler serves every way a prompt may be produced. Expression: src/ocx/report.cr (Report in OCX's notation, written and read back whole). src/ocx/dispatch.cr grows the standing to answered, classifies entries as request, note, result or answer, and reads the answer and its report from the record. src/ocx/turns.cr (Dispatch.handle: every unanswered request by the addressee after the latest ask is run in the workspace, in order, and its result written back naming the request). Workspace.open reuses a directory that already holds a checkout of the clone, so the handler can be called again and again on one workspace; an entry's text is read without the whitespace OCX puts around a ticket comment. Demonstrates: crystal spec spec/ocx/turns_spec.cr (the remote side played by a second login: a request run and its result written back with the toolchain's output, a note that changes nothing, a refused request answered with a failed result, the answer in report notation collected with its commands; a prose answer in a thread kept whole; a report round-tripped through its notation) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 5ffc6e5d71 user: fable tags: ocx, audited | |
| 23:06 |
Phase C.2: the workspace
Intent: a turn's command and a runner's changes need somewhere confined to happen: an isolated checkout of the agent's own clone on the task's branch, never a served repository. Paths stay inside it, only a short list of programs may run, each run is bounded in time, and a commit creates the branch on first use and continues it after. Expression: src/ocx/workspace.cr (Workspace.open at the branch's tip when it exists or at trunk otherwise, as the given login; read and write with confined paths; files; run limited to crystal and shards with a ten-minute bound; commit that creates or continues the branch and returns the check-in). The fossil command is used as a person would use it. Demonstrates: crystal spec spec/ocx/workspace_spec.cr (files written, committed on the branch, attributed to the login, read back through the tree; a second workspace continues the same branch; refusals for paths outside, an empty path, the checkout database and a program off the list; an allowed program run) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: f7ea98c325 user: fable tags: ocx, audited | |
| 23:05 |
Phase C.1: the ask and the standing
Intent: dispatch state lives on the record. To dispatch a task is to write the ask onto its ticket or thread, in OCX's notation, naming the login expected to answer; where a task stands is read back from the record by finding that ask. Nothing is remembered anywhere else, so any process, on any machine, can read the same standing. Expression: src/ocx/task.cr (Origin; Task, with Task.from reading the prompt verbatim from a ticket's title and comment or a post's body, and the branch defaulting to task/<record>). src/ocx/dispatch.cr (the ask framed as a Question with the record and the branch tip as references and the addressee on its For: line; written and pushed as the pipeline's identity; the standing not asked or pending; the entries of a record, oldest first, which later slices read for turns and answers). Demonstrates: crystal spec spec/ocx/dispatch_spec.cr (a ticket: task read, not asked, ask written and pushed, pending for the addressee and still not asked for anyone else, the ask read back whole; a thread: task read from the root post, the ask a reply with the branch tip as a reference) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 9070b5b85c user: fable tags: ocx, audited | |
| 23:04 |
Phase C removed whole; the plan restated from what it taught
Intent: the first build of Phase C (61cc0e3e96 to e4c1bc64f2) grew by files and then by alterations, so no single check-in of it is a slice a reader can take in and run. The maintainer asked for a clean slate at the end of Phase B and a rebuild in readable, functional slices. This check-in is the slate; the plan carries what the first build taught: the dispatch state goes on the record, an answer may take several asynchronous turns from any kind of runner, and a mechanized handler closes the loop. Expression: removed src/ocx/task.cr, runner.cr, provider.cr, chat_protocol.cr, native_runner.cr, workspace.cr, pipeline.cr, tools/task.cr and their specs and the fake model server. Phase B keeps Repository#unsent and Sync.push_repo in identity.cr. The question shape keeps its For: line, which belongs to the ask. PLAN.md's Phase C names five slices, each runnable and specified from the record inward, with a person as one of many ways a prompt may be produced rather than a special case; the two parity rows return to open. Demonstrates: crystal run ci.cr (Phases A and B whole, green, with nothing of Phase C in the tree) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 9078d7a94a user: fable tags: ocx, audited | |
| 22:37 |
A clone remembers its password with fossil remote add; the whole path shown live
Intent: the previous check-in stated that a clone made without an interactive session cannot remember its sync password. The maintainer pointed out that `fossil remote` saves it: `fossil remote add NAME URL` with the login and password in the URL, then `fossil remote NAME`, after which a bare `fossil sync` works and nothing remains on a command line. The limit was mine, not Fossil's. Expression: PLAN.md's runtime layout says how a clone is provisioned once; the backlog item "OCX pulls for itself" keeps only its real reason, the second copy of the password; tools/task.cr's note says the same. Demonstrates: bin/task f4b4f2f002 ... on the scratch clone provisioned that way: dispatch answered, the clone synced, and on the record: answered (answered by ocx); crystal run ci.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: e4c1bc64f2 user: fable tags: ocx, audited | |
| 22:21 |
The tool reads the record back only after the clone has pulled, and can collect alone
Intent: slice 3's live run printed "not asked" after a successful task because collect read the clone, which never pulls what the pipeline pushes; the served repository is the record and the clone is a cache of it. A clone made non-interactively cannot remember its sync password, and this host has no pseudo-terminal to provision one, so the limit is stated rather than hidden. Expression: tools/task.cr prints what dispatch returned, syncs the clone with `fossil sync` when the clone remembers its password and says so when it cannot, and gains --collect, which only reads where a record stands for a login. PLAN.md's runtime layout states that a clone learns what others wrote only by pulling, and the backlog gains "OCX pulls for itself". Demonstrates: bin/task f4b4f2f002 --collect --login ocx on the scratch clone after one pull: on the record: answered (answered by ocx), with the report read back; crystal run ci.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: a0fb4d6d14 user: fable tags: ocx, audited | |
| 22:18 |
Phase C, re-cut, slice 3: the tool on the pipeline, shown live again
Intent: the tool had carried its own record-to-task and write-back logic; on the pipeline it is a thin caller, and the live run is repeatable rather than a one-off. Expression: tools/task.cr resolves RECORD to a ticket or a post in the clone, dispatches it through the pipeline to the native runner, pushes the clone's unsent artifacts, and prints the standing read back from the record. PLAN.md describes Phase C as re-cut and points the two demonstrated parity rows at the slices. Demonstrates: crystal run ci.cr, and the live run of 2026-09-21: bin/task ran a second ticket on the scratch served repository with codex-crystal-gemma4; the ask and the report are on the ticket and the check-in on its task branch. Amended: the run printed the standing as not asked, because collect read the local clone, which does not pull what the pipeline pushes; the next check-in corrects the tool. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: fdf58b4626 user: fable tags: ocx, audited | |
| 22:17 |
Phase C, re-cut, slice 2: the native runner's loop, with its failure modes
Intent: the loop between prompt and response is the native runner's own concern and was specified only on its happy path. This slice states it whole: what the model sees, what it may do, and how each way it can go wrong is reported rather than hidden. Expression: spec/ocx/native_runner_spec.cr covers the happy path (the mechanical framing seen by the model, list, write, an allowed command, a refused command, finish, the check-in attributed to the login), the step budget running out (failed, but what was written is committed), prose with no tool (failed, nothing committed), a malformed call and a path outside the checkout (told to the model, nothing escapes), a second dispatch continuing the branch, and an unreachable model. src/ocx/native_runner.cr marks an exhausted budget as failure; src/ocx/workspace.cr refuses an empty path. Demonstrates: crystal spec spec/ocx/native_runner_spec.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 8f1d648eb2 user: fable tags: ocx, audited | |
| 22:16 |
Phase C, re-cut, slice 1: record → ask → runner → answer → record, with the record as the only state
Intent: the runner interface assumed an answer in the same call, and a person or a session on another machine answers later; the maintainer's principle is that the record is the state and notation is OCX's. This slice replaces C.3 to C.6's middle with one pipeline: dispatch reads the task from its ticket or post, writes the ask in OCX's notation naming the login expected to answer, and hands the task to the runner, which answers now or later; collect derives where the task stands from the record alone. Expression: src/ocx/pipeline.cr (task from a record; dispatch; collect; push_unsent; the framing). src/ocx/runner.cr: AgentRunner is name, login and dispatch(task) : Report?. src/ocx/task.cr: Report parses back from its notation; Standing and Status. Question gains an optional For: line. src/ocx/chat_protocol.cr replaces model.cr with the protocol alone; errors are Protocol::Error. NativeRunner opens its own checkout and answers from dispatch. Removed: human_runner.cr, reporting.cr, the Model wrapper, Sync.push_from, and their specs. The fixture can add a second login. tools/task.cr is a stub until slice 3. Demonstrates: crystal spec spec/ocx/pipeline_spec.cr (a ticket asked, pending on a second login, answered by that login's own comment and collected; the same in a forum thread; an in-process answer recorded at once and read back whole; a clone's check-ins pushed to its origin) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: d21666abc8 user: fable tags: ocx, audited | |
| 21:09 |
Notation is OCX's: the human runner frames a raw task, it never requires one already shaped
Intent: the maintainer's principle: the pre-prompt is mechanical and OCX's, the raw prompt and answer live in Fossil's own records, and the answer returns over the same ticket, thread or check-in; a person and an agent differ only by login and by notation OCX added. The human runner had required the task itself to be in the question shape, which made the person or the caller type OCX's notation. Expression: HumanRunner#frame composes the question from the task verbatim with the references OCX knows (the origin and the branch tip); Reporting.ask posts it with the same framing Reporting uses for reports; Question's stakes and options are optional content, and its text no longer has to end in a question mark. NativeRunner's user message starts with the mechanical part (where the task lives) before the task verbatim. PLAN.md restates C4 and adds the rule that notation is OCX's. Demonstrates: crystal spec spec/ocx/human_runner_spec.cr (a raw task framed, posted and read back as a question with the ticket and the branch tip as references) and crystal spec spec/guards/question_spec.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 49d461a5b6 user: fable tags: ocx, audited | |
| 20:22 |
The reader shows an amended comment, as Fossil's timeline does
Intent: amending fe6acef679's comment left guard A1 reading the old text, because the reader took event.comment while Fossil keeps an amendment in ecomment (and an amended user in euser) and displays whichever is set. The reader must show what the timeline shows. Expression: src/ocx/checkins.cr reads coalesce(euser, user) and coalesce(ecomment, comment); the fixture can amend a comment; spec/ocx/checkins_spec.cr amends one and reads the new wording back with its line breaks. A stale comment in spec/ocx/runner_spec.cr no longer says "seam". Demonstrates: crystal spec spec/ocx/checkins_spec.cr, and crystal run ci.cr (guard A1 green again on the amended check-in) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 625f97f0d8 user: fable tags: ocx, audited | |
| 20:19 |
Phase C.6: specific names for specific things; a runner opens what it needs
Intent: "seam" names a property (precept I1: each part substitutable at one point), and I had made it a file that bundled specific abstractions under a generic word. The maintainer asked that specific intents carry specific names and that anything generic be treated generically or not named at all. Also: a runner should open what it needs, so callers substitute runners without knowing their needs, which the human runner had exposed by receiving a workspace it could not use. Expression: src/ocx/seam.cr is gone. src/ocx/task.cr holds Origin, Task, Outcome and Report; src/ocx/runner.cr holds AgentRunner (run(task) : Report) and the Workspace a file-changing runner may use; src/ocx/provider.cr holds Provider, its Protocol, and the Message, ToolCall and ToolSpec a protocol carries. NativeRunner is built with its workspace; HumanRunner needs none and is kept as the proof that the interface holds for a person, not as a feature. Renaming spec/ocx/seam_spec.cr exposed a weakness in guard A1, which judged every check-in's demonstration against today's tree; it now reads the tree of the check-in itself (Repository#tree, from the manifest's baseline and F cards). PLAN.md: the formal cause and parity row one no longer say "seam"; a working rule that a unit is a slice, never a file, with the first unit of a phase the thinnest end-to-end slice, since Phase C's early check-ins were file-shaped; Phase E defines drift as a missing or unenforced functional definition and specifies the question shape by a versioned fixture that OCX and Stone must both pass. This comment was amended: the check-in was first committed under the previous message because the commit step wrote its message after the CI step in a run that had failed. Demonstrates: crystal run ci.cr (every spec of Phase C on the renamed types; spec/ocx/runner_spec.cr and spec/ocx/human_runner_spec.cr on run(task); the A1 guard passing on the two check-ins whose demonstration was renamed) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: fe6acef679 user: fable tags: ocx, audited | |
| 20:05 |
Phase C.5: the seam restated: protocol instances, runners as instances, the person as a runner
Intent: the maintainer read 61cc0e3e96 and found the seam unclear on three points, all real. An enum named wire protocols after products and Model#chat branched on it, which is the caller deciding the protocol; a runner carried an id string from the old design's lookup-by-string; and the person was outside the seam although prompting him is the same act in the other direction. Expression: src/ocx/seam.cr now holds the seam whole: Message, ToolCall and ToolSpec; an abstract Protocol that a Provider carries as an instance; AgentRunner with `name` for attribution only; Outcome::Pending. src/ocx/model.cr becomes ChatProtocol (the /api/chat shape, structured or textual tool calls) and a thin Model that delegates to its provider's protocol. src/ocx/human_runner.cr is the person as a runner: a task must have the question shape, is posted onto the prompting ticket or thread as the agent's identity, and reports Pending until the reply lands there. tools/task.cr loses its --api flag. Demonstrates: crystal spec spec/ocx/human_runner_spec.cr (a question-shaped task appended to its ticket and reported pending; a non-question refused with nothing posted), crystal spec spec/ocx/seam_spec.cr and spec/ocx/model_spec.cr on the restated types Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1b739685fb user: fable tags: ocx, audited | |
| 20:01 |
Phase C.4: tools/task.cr runs a ticket end to end, shown live with a local model
Intent: the parity contract's second and third rows need a live demonstration, not only a scripted one: a ticket run through the native runner by a real local model, its branch pushed, its report written back to the ticket. Doing that surfaced four things the specs could not: local models write tool calls as JSON text, a checkout can record a relative repository path, a rerun must continue its task branch, and a fresh clone has no remembered password for Fossil's own sync. Expression: tools/task.cr (TICKET --provider --model --login --password-file; reads the ticket from the clone, runs the native runner in a fresh checkout on task/<ticket>, pushes the branch's unsent artifacts and the report through OCX's sync client). src/ocx/model.cr recognises a tool call written as JSON text in the shapes local models use, only for a known tool. src/ocx/checkout.cr resolves a relative repository path against the checkout. src/ocx/workspace.cr opens at the task branch's tip when it exists. src/ocx/reporting.cr adds Repository#unsent and Sync.push_unsent, and starts an appended ticket comment on its own line. The fixture can clone itself. PLAN.md marks rows two and three demonstrated and defines demonstrated versus shown. Demonstrates: crystal spec spec/ocx/push_unsent_spec.cr (a clone's check-in pushed to its origin by OCX's client and read back there), crystal spec spec/ocx/model_spec.cr (the textual tool-call shapes), and the live run of 2026-09-21: codex-crystal-gemma4 created hello.txt on task/3665395777 in a scratch served repository, seven artifacts pushed, the report appended to ticket 3665395777 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: e9102c35c6 user: fable tags: ocx, audited | |
| 19:55 |
Phase C.3, the files: workspace, native runner, reporting, and their specs
Intent: the previous check-in, 00bc3203de, carries the Phase C.3 message but not the five files it names: my commit step ran `fossil add` after CI, and in the runs where CI failed the add never happened, so the final run committed only the edits to already-versioned files. This check-in adds the files that message describes; the message stands as the description of both. Expression: src/ocx/workspace.cr, src/ocx/native_runner.cr, src/ocx/reporting.cr, spec/ocx/workspace_spec.cr, spec/ocx/native_runner_spec.cr, exactly as 00bc3203de's message describes them. The commit step now refuses to run while `fossil extras` lists anything. Demonstrates: crystal spec spec/ocx/native_runner_spec.cr and crystal spec spec/ocx/workspace_spec.cr, now present in the tree they are run from Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: c88ca3b31e user: fable tags: ocx, audited | |
| 19:55 |
Phase C.2: a Model that speaks the local chat API, proven against a fake server
Intent: the native runner needs one completion call with tools; the provider owns the wire protocol so no caller ever branches on an API style. Proven without a real model, against a server that answers with scripted replies and records what it was asked. Expression: src/ocx/model.cr adds Message, ToolCall, ToolSpec and Model#chat for the Ollama-style /api/chat protocol (messages, tools, tool calls back), with errors named by provider; the OpenAI style is declared and refused until built. spec/support/fake_model.cr is the scripted server. Demonstrates: crystal spec spec/ocx/model_spec.cr (request shape, tool calls read back, a tool result sent on, an unreachable provider named) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 00bc3203de user: fable tags: ocx, audited | |
| 19:50 |
Phase C.2: a Model that speaks the local chat API, proven against a fake server
Intent: the native runner needs one completion call with tools; the provider owns the wire protocol so no caller ever branches on an API style. Proven without a real model, against a server that answers with scripted replies and records what it was asked. Expression: src/ocx/model.cr adds Message, ToolCall, ToolSpec and Model#chat for the Ollama-style /api/chat protocol (messages, tools, tool calls back), with errors named by provider; the OpenAI style is declared and refused until built. spec/support/fake_model.cr is the scripted server. Demonstrates: crystal spec spec/ocx/model_spec.cr (request shape, tool calls read back, a tool result sent on, an unreachable provider named) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 6197834a65 user: fable tags: ocx, audited | |
| 19:50 |
Phase C.1: the Task, AgentRunner and Model seam
Intent: the formal cause names one seam between a task and the runner that performs it; this check-in states it as types so that swapping a runner or a provider is a substitution here and nowhere else (precept I1), and so a report is evidence a reader can check. Expression: src/ocx/seam.cr defines Origin (the ticket or forum post a task's outcome returns to, precept A5), Task, Provider (one endpoint, no notion of use), Report (outcome, summary, check-in, files, commands, and its text form), the abstract AgentRunner and the abstract Workspace a runner may touch. Demonstrates: crystal spec spec/ocx/seam_spec.cr (a stand-in runner and an in-memory workspace run a task through the seam and report) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 61cc0e3e96 user: fable tags: ocx, audited | |
| 19:41 |
PLAN.md: a backlog, starting with an interchangeable path for tools/audit.cr
Intent: the maintainer deferred one item with a reason; the plan needs a place for that which is not a ticket and not a phase. Expression: a Backlog section in PLAN.md, with the rule that an item is promoted only when it becomes the next thing to show, and its first entry: once writes are fully debugged, tools/audit.cr should deliver through OCX::Compose and OCX::Sync as an alternative to the fossil CLI, at one seam. Demonstrates: crystal run ci.cr (the plan still parses for the ledger and parity guards) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 735191eab2 user: fable tags: ocx, audited | |
| 19:37 |
The reader waits on a busy repository as long as Fossil does
Intent: the maintainer's audit of 1e499cf2ce (fail) shows the sync specs on his machine raising "database is locked": after `fossil http` replies, Fossil's backoffice holds the repository for a moment, and the reader gave up at once where Fossil waits 15 seconds. Same timeout, same design. Expression: src/ocx/repository.cr and src/ocx/checkout.cr open one connection each (DB.connect, not a pool, so every query provably carries the pragmas) with busy_timeout 15000 and query_only. BUSY_TIMEOUT_MS names Fossil's value. Demonstrates: crystal run ci.cr, and crystal spec spec/ocx/sync_spec.cr repeated; the maintainer's re-run on his machine is the real evidence Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 0c9ab41cc8 user: fable tags: ocx, audited | |
| 19:34 |
tools/audit.cr defaults to the checkout's current check-in
Intent: auditing the check-in you have just read and run should not require copying its hash. Asked by the maintainer. Expression: tools/audit.cr takes [CHECK-IN] pass|fail [NOTE-FILE]; with no check-in it reads the checkout's current one from .fslckout through OCX::Checkout, and it says so when run outside a checkout. PLAN.md and the wiki page tag/audited show the new form and that the note may come from a heredoc. Demonstrates: crystal run tools/audit.cr -- pass with an empty note (resolves the current check-in, then refuses the empty note) and crystal run ci.cr Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1e499cf2ce user: fable tags: ocx, audited | |
| 19:31 |
Host and gate guards move under spec/, selected by tag
Intent: host/ and gate/ were top-level only to keep them out of a bare `crystal spec`; a layout chosen to dodge the runner is not a reason. Crystal's spec tags select exactly which examples run, so every spec lives under spec/ and ci.cr excludes the host and gate tags unless asked. Asked by the maintainer. Expression: spec/host/ (tagged host) and spec/gate/ (tagged gate) replace host/ and gate/; ci.cr runs `crystal spec --tag ~host --tag ~gate` by default and drops an exclusion per mode; the guards and the format check no longer list the two directories; PLAN.md points at the new paths. Demonstrates: crystal run ci.cr (default excludes both tags, green) and crystal run ci.cr -- host (the two host failures still reported) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1373984fa9 user: fable tags: ocx, audited | |
| 19:30 |
tools/audit.cr: the audit tool in Crystal, and tools/ under the guards
Intent: tools are Crystal, not shell (maintainer's rule, precept "Crystal-first" in spirit). The audit tool is rewritten, and tools/ joins the format check and the size, naming, repository-neutral and no-environment guards so a tool is held to the same rules as the code. Expression: tools/audit.cr replaces tools/audit; it reads the note from a file or stdin, runs `fossil tag add audited CHECK-IN "verdict: note"` and `fossil sync` as the person running it. ci.cr formats tools/; four guards scan it; PLAN.md and the wiki page tag/audited name the new invocation. Demonstrates: crystal run tools/audit.cr -- (prints usage and exits non-zero) and crystal run ci.cr (tools/ formatted and guarded) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 989deeaf32 user: fable tags: ocx, audited | |
| 19:28 |
Audits carry a verdict; tools/audit records one with a long note
Intent: the maintainer asked for a simple way to add an audited tag with a long note, and whether audits can be negative. They can: the tag records a verdict about the check-in, pass or fail, with the note after it; a ticket records any work arising. The meaning is written where Fossil links it, the wiki page tag/audited. Expression: tools/audit CHECK-IN pass|fail [NOTE-FILE] adds the tag (value "verdict: note", note from a file or stdin) and syncs. spec/spec_helper.cr reads an Audit (author, verdict, note) from the tag; the A2 guard prints coverage with pass, fail and no-verdict counts. PLAN.md restates A2 and the audit rule. The wiki page tag/audited is created in the same sync. Demonstrates: crystal run ci.cr (the audit coverage line now shows verdicts) and tools/audit itself, whose first use is the maintainer's Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 6afba36104 user: fable tags: ocx, audited | |
| 19:19 |
ci.cr installs the locked shards when lib/ does not match the lock
Intent: the one CI command must work on a fresh clone; it printed shard.lock but never checked that lib/ satisfied it, so the first run failed on a missing require instead of saying what to do. Asked by the maintainer. Expression: ci.cr runs `shards check` after the ledger and, when lib/ does not match, `shards install --frozen`, which installs exactly the locked versions and refuses to proceed without a lock; the outcome is printed as part of the ledger. Demonstrates: crystal run ci.cr after removing lib/ (installs the locked shards, then runs green); a second run reports lib/ matches the lock Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 0d0d4ebccb user: fable tags: ocx, audited | |
| 19:15 |
Phase B.3: an agent's writing identity, and a push that needs no other input
Intent: with a clone per agent under its own login, a push should need only who is writing and what: the project code and the server come from the clone itself, and the password from an owner-only file beside it, never from a repository or the environment (decision 3, precept C6). Expression: src/ocx/identity.cr adds Identity (login and password, read from a file that must be owner-only or it is refused), Repository#remote (the clone's last-sync-url with any typed credentials stripped) and Sync.push_from(checkout, identity, artifacts). The fixture can record a remote. PLAN.md marks Phase B landed. Demonstrates: crystal spec spec/ocx/identity_spec.cr (an owner-only file accepted, a group-readable one refused, and a tag pushed to the fixture's own remote over HTTP and read back with its author) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: ac4bfbf36e user: fable tags: ocx, audited | |
| 19:13 |
Phase B.2: push artifacts over Fossil's sync protocol as a login
Intent: an agent writes to a served repository the way a human's `fossil push` does, as its own login, through the sync protocol, so the server validates every artifact and no process but the server touches the served file (precept I4; the transport is a seam, precept I1). Expression: src/ocx/sync.cr composes an xfer request (login card with the nonce and signature Fossil checks, a push card, file cards framed exactly as Fossil's sender frames them), sends it through a Transport seam (HTTPTransport for a server, ProcessTransport driving `fossil http` for specs), and raises Rejected with the server's own reason. The fixture now exposes the admin password `fossil new` printed and can serve itself on a free local port. Demonstrates: crystal spec spec/ocx/sync_spec.cr (a tag pushed and read back with its author; a ticket change and a two-post thread pushed and read back; the server's refusals; the same push over real HTTP to a local fossil server) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 9f0bf0dfee user: fable tags: ocx, audited | |
| 19:13 |
Phase B.1: compose artifacts in Fossil's card format
Intent: writing to Fossil means producing artifacts Fossil itself will accept: tag (control) artifacts, ticket changes and forum posts, with the cards sorted, strings escaped, the Z card carrying the MD5 of the text before it, and the artifact named by the SHA3-256 of the whole. Expression: src/ocx/compose.cr adds Compose.artifact, .encode, .time_card and the typed builders .tags, .ticket_change and .forum_post (root with a title, or reply with thread and reply-to), returning a Composed with text and hash. Demonstrates: crystal spec spec/ocx/compose_spec.cr (card order, escapes, Z and name shape, each builder re-read by the Phase A parser, and the refusals) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 426c35c92c user: fable tags: ocx, audited | |
| 18:50 |
Tag author for propagated tags; the previous check-in was red
Intent: the previous check-in shipped with a failing suite because my commit step read a piped grep's status instead of ci.cr's; a propagated tag (a branch tag inherited down the branch) has no source artifact, so its event row was missing and the reader raised. Both are corrected, and the commit step now gates on ci.cr's own exit status. Expression: src/ocx/checkins.cr resolves a tag's author and time from its source artifact when it has one and from its originating check-in otherwise, falling back to the check-in's own time. Demonstrates: crystal run ci.cr (green again; audit coverage read for every check-in on this branch) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: c78570862f user: fable tags: ocx, audited | |
| 18:49 |
An audit tag's author is the tag artifact's user, not a typed value
Intent: Fossil already records who added a tag and when, in the control artifact that carried it; asking the auditor to type their login into the tag's value duplicated a fact Fossil owns. Use the existing mechanism. Expression: src/ocx/checkins.cr adds Tag and Repository#tag_records (name, value, user and time from the tag artifact's event row); #tags is now derived from it. The audit guard and PLAN.md read the auditor from the tag's author; the audit is `fossil tag add audited <check-in>` with no value. The fixture's tag helper takes an optional value. Demonstrates: crystal spec spec/ocx/checkins_spec.cr (a tag added by the fixture user carries that user and no value; a valued tag keeps its value) and crystal run ci.cr (audit coverage read the same way) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: f527bdbab3 user: fable tags: ocx, audited | |
| 18:42 |
The compiler pin gates the merge, not the maintainer's own run
Intent: the maintainer's workstation runs Crystal 1.21.0 and the pin is 1.18.2; failing his every run would turn "green" into a value he has to mentally override. The pin exists to catch a gate machine on the wrong compiler, so that is where it refuses. Expression: ci.cr reports a compiler that differs from the pin and marks the result "not a gate result", failing only in merge-gate mode; the exact-match assertion moves from spec/guards/dependency_ledger_spec.cr to gate/compiler_gate_spec.cr; PLAN.md restates I3 accordingly. Demonstrates: crystal run ci.cr (green on the pinned compiler; on another it prints the difference and still runs format and specs) and crystal run ci.cr -- merge-gate (refuses a compiler other than the pin) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: dd7e65efe7 user: fable tags: ocx, audited | |
| 18:31 |
Phase A.7: logins and capabilities from the user table
Intent: with a clone per agent under its own login, the user table is the authority on what each agent may do; the workbench reads logins and Fossil's capability letters, and never the password column. Expression: src/ocx/users.cr adds User (login, capabilities, info, last change) with #can?, and Repository#users and #user. Demonstrates: crystal spec spec/ocx/users_spec.cr (the fixture's admin login with its s capability, the built-in anonymous login, ordering, and a missing login) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 6dcdf884e0 user: fable tags: ocx, audited | |
| 18:31 |
Phase A.6: forum threads and posts from their artifacts
Intent: the forum thread is the durable session log in the parity contract, so the workbench reads threads and every post in them from the artifacts themselves: root, reply-to, edit lineage, title, user, time, mimetype and body. Expression: src/ocx/forum.cr adds ForumPost and ForumThread, Repository#forum_threads (most recently active first, title from the root's H card), #forum_posts (a thread oldest first) and #forum_post by prefix, all through the card parser and the content reader. Demonstrates: crystal spec spec/ocx/forum_spec.cr, which reads the forum of whatever repository the checkout belongs to (this branch's repository has thousands of posts) and is pending in a repository without a forum, since Fossil offers no command-line way to write one. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 1357aba281 user: fable tags: ocx, audited | |
| 18:30 |
Phase A.5: artifact cards, and tickets with their change history
Intent: a ticket is the artifact that prompts most work, so the workbench reads tickets as the repository defines them, whatever fields its schema chose, and reads each change from the ticket-change artifact itself rather than trusting a cached column. That needs a parser for Fossil's card format, which forum posts and wiki pages share. Expression: src/ocx/artifact.cr parses cards (letter, arguments, the byte-counted W body), decodes card string escapes and D-card times. src/ocx/tickets.cr adds Ticket and TicketChange, Repository#ticket_fields (from the ticket table's own columns), #tickets (all or by status), #ticket by prefix, and #ticket_changes (J cards, with + appends kept apart from assignments). Demonstrates: crystal spec spec/ocx/artifact_spec.cr (a forum post's cards and body, escapes, two refusals) and crystal spec spec/ocx/tickets_spec.cr (fields, listing, status filter, prefix lookup, and a change read back from its artifact) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 23d1aa6daf user: fable tags: ocx, audited | |
| 18:28 |
Phase A.4: artifact content through zlib and Fossil's delta chain
Intent: tickets, forum posts, wiki pages and files are artifacts, and an artifact's bytes are what every later reader needs; Fossil stores them zlib-compressed, often as a delta against another artifact, so reading them means implementing Fossil's delta format rather than shelling out. Expression: src/ocx/delta.cr applies Fossil's delta format (header size, copy and insert segments, base-64 integers most significant digit first, 32-bit wrapping checksum) with every malformed case refused. src/ocx/content.cr adds Repository#content and #text (four-byte length, zlib, delta chain followed to a plain blob), #file (a file's blob hash in a check-in) and #files. The fixture commits with --hash so a same-size edit in the same second is still a change. Weakness: A.1 and A.2 were committed with explicit file lists while the tree already depended on A.3, so those two check-ins do not compile standalone; from here every check-in is the whole tree. Demonstrates: crystal spec spec/ocx/delta_spec.cr (a vector made by `fossil test-delta-create`, the checksum it wrote, the zero-length copy rule, four refusals) and crystal spec spec/ocx/content_spec.cr (three versions of one file read back through real deltas; a manifest read as text) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 7301079d57 user: fable tags: ocx, audited | |
| 18:23 |
Phase A.3: the checkout file, and guards that read through OCX's own reader
Intent: a guard that judges history should use the workbench's own reader, not the fossil CLI, so the reader is exercised on every run and the guards need no external program. A checkout knows its repository and its current check-in; both are read from .fslckout. Expression: src/ocx/checkout.cr reads repository path, check-in hash and branch from the checkout file. spec/spec_helper.cr drops the fossil CLI: branch_checkins, artifact_exists? and audited_by go through OCX::Checkout and OCX::Repository, and guards are pending when the tree is not a checkout. The fixture takes full 64-character hashes from the checkout file, since `fossil info` truncates to the hash-digits setting. Demonstrates: crystal spec spec/ocx/checkout_spec.cr, and crystal run ci.cr (the check-in and audit guards now judge this branch through the reader) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 51e25dc182 user: fable tags: ocx, audited | |
| 18:23 |
Phase A.2: check-ins, branches and tags from the repository database
Intent: the timeline is the project's source of truth, so the workbench reads check-ins as Fossil stores them: the event row, its blob, its branch tag, and any tags on it, with multi-line comments intact. Expression: src/ocx/checkins.cr adds Checkin (hash, UTC time from Fossil's Julian day, user, comment, branch) and Repository#checkins (newest first, optionally one branch), #checkin by prefix, #branches, #files_changed? (mlink), and #tags (name => value, sym- prefix kept). Demonstrates: crystal spec spec/ocx/checkins_spec.cr (order, branch filter, branch list, prefix lookup, a tag set by `fossil tag add`, and that a branch-creation check-in changed no file) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 9c19150f02 user: fable tags: ocx, audited | |
| 18:23 |
Phase A.1: open a Fossil repository read-only from Crystal
Intent: the first thing the workbench must do is read a repository the caller names, directly from its SQLite file, without the fossil CLI and without the ability to write (precepts I2, I4). The repository is a parameter; the served copy is never touched. Expression: src/ocx/repository.cr opens a path query-only, refuses anything that is not a Fossil repository, and reads config values, the project code, and full artifact hashes by unique prefix. shard.yml pins the sqlite3 shard and shard.lock is versioned; ci.cr prints the lock as the rest of the dependency ledger. spec/support/fixture.cr builds a throwaway repository and checkout with the fossil CLI for specs that need real history. Demonstrates: crystal spec spec/ocx/repository_spec.cr (refuses non-repositories, reads project code and config, resolves a prefix, and proves a write attempt is refused) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: 735539354f user: fable tags: ocx, audited | |
| 18:06 |
Version the spec helper; guards read check-in comments from the database
Intent: the first check-in omitted spec/spec_helper.cr (re-created at a path the same check-in deleted, so `add` skipped it), and its check-in guard read comments through the timeline command, which flattens lines. Both are corrected here rather than hidden by amending history. Expression: spec/spec_helper.cr is now versioned. It reads each branch check-in's hash and hex-encoded comment with one SQL query over event, blob, tagxref and mlink, replacing the timeline parse; the branch name comes from `fossil branch current`, never a constant. Demonstrates: crystal run ci.cr (spec/guards/checkin_spec.cr judges both check-ins on this branch; spec/guards/audit_spec.cr reports coverage over them) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: e765fc4018 user: fable tags: ocx, audited | |
| 18:03 |
OCX: plan, precepts as guarded conditionals, parity contract, one CI command
Intent: begin the parallel rebuild line decided on 2026-09-21. State the four causes and three axioms, derive sixteen precepts as conditionals with one guard spec each, fix the parity contract with trunk, and reset the tree so nothing from trunk's source carries over unstated. Trunk is untouched; the timeline keeps its history. Expression: PLAN.md (causes, axioms, precepts, parity contract, phases, working rules, decisions), README.md, shard.yml with an exact compiler pin, ci.cr as the one CI command with explicit host and merge-gate modes, src/ocx/question.cr (the question shape put to the maintainer), spec/guards/ (thirteen tree guards), host/ (three guards that inspect the running Fossil server, discovered from its own command line), gate/ (the parity gate). 337 trunk files removed on this branch. Demonstrates: crystal run ci.cr (16 precepts, 0 gaps; 25 examples green). crystal run ci.cr -- host fails today on I5 (three served repositories in WAL mode) and A4 (served CGI links resolve into a checkout), which are operator steps on the maintainer's word. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QBEovXRBLmZ2rts65raJT7 check-in: fdb2b84ecd user: fable tags: ocx, audited | |
| 18:01 | Create new branch named "ocx" check-in: 853d7bbb65 user: fable tags: ocx, audited | |
| 14:58 | Review list of projects in sync check-in: 1f0ac30c5b user: jkridner tags: trunk, ollama-codex | |