Task file and 13 given files (2 new: the wire fixture with a detail and
brokerd/tests/invalid_detail.rs; 11 new versions of existing tests, most
only adding `detail: None`).
Checked against a reference in a scratch worktree with its own target/,
on titan (rustc 1.98.1, clippy 0.1.98): make gate prints gate: ok with
956 tests (944 + 12 new); /tmp has no new entries after the gate. All 21
quoted before-blocks occur once in master, and applying the edits to
master gives the reference byte for byte. The given files are the
reference's, formatted, none over 500 lines; no chat-template tag in
the plan.
Before the edits, strict's envelopes_reject_unknown_keys_at_every_depth
fails and ten test targets do not compile, as step 2 lists.
Mutations: 21 one-line breaks of the reference; 20 caught by the given
tests. The one not caught (dropping `default` from the detail attribute)
cannot change behaviour: serde reads a missing Option field as None.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Code constraints: `proto` became `bxcore` in M5b task 00 and holds the
code roles share as well as the shared types. Recorded in
docs/decisions.md with the M5b spec.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Done the way the owner asked and the spike mapped: the crate and the
manifests renamed, then what the build reported fixed. Layers: 138
"proto unresolved" errors across 169 files; cargo fmt reordering
imports; six fixture paths built as strings in brokerd's and bxctl's
tests, which the compiler cannot see, and comments naming
crates/proto/; the crate-dependency check and its self-test. One more
the spike did not reach: a `\nproto =` key inside a printf format in
the self-test, which a word-boundary replacement skips because `n` and
`p` are both word characters.
make gate: gate: ok, 944 tests; no new entry in /tmp.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 00 (proto -> bxcore) by the design model before the run; fourteen
Ornith tasks: the invalid-arguments detail, the secrets move, the
sanitizer in three tasks, memoryd, loopd's neutralising and self-test,
TurnDone.untrusted, gatewayd's links, bxctl sanitize check, the instance
lock in two tasks, and the runbook. Global constraints, five review-focus
inputs each owned by a task, and the interfaces between tasks. The task
files and the reference come next, through docs/plans/CHECKLIST.md.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every daemon removes a leftover socket before binding, which also lets a
second copy silently take a running one's sockets. Owner's decision:
refuse to start instead. Each daemon takes an exclusive std File lock
(stable since 1.89, checked on 1.95) on <home>/run/<role>.lock, or
<socket>.lock for inferproxy, before touching its sockets; the system
releases it however the process ends. A runbook entry, a security note
and a test per daemon.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
About 10,000 test leftovers had built up in titan's /tmp. loopd's Home
and FakeServer, its fake broker's socket, bxctl's temp sockets, fake
loopd and command homes, inferproxy's socket, and a few brokerd and
toolkit tests created uniquely named directories or sockets and never
removed them, or removed them by hand at the end, which a failing test
skips. Each is now a guard removed on drop (bxctl gains
tests/support/temp.rs). Two races also left gatewayd homes behind: the
test Running only signalled its server to stop, and two Stage structs
declared the home first, so it was removed while the server could still
write into it. Running's drop now waits for the thread (with a 5-second
limit), and every home is declared last.
Checked: make gate twice, then the gate and three rounds of gatewayd and
loopd tests; no new entry in /tmp; 944 tests pass.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Code spans stop both the link preview and the server's image fetch;
Markdown links are not previewed; images are fetched by the server at
posting time. Emoji refusal accepted, phrases move to flag, Hermes'
patterns ported at plan time.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
bxcore (compiler-driven rename, with the spike's four layers named); the
invalid-arguments reason on the wire; the secret store moved into
bxcore behind a feature loopd never enables; bxcore::sanitize with
refuse, neutralise and flag, its lists, hand-written normalisation, two
corpora, a rule-change procedure and `bxctl sanitize check`; memoryd
refusing and flagging (a flag keeps the text as an untrusted note);
loopd neutralising tags on append, with a tokenizer self-test and the
session's trust on TurnDone; gatewayd escaping images and, in untrusted
sessions, links; a table of every link between the roles; runbook
entries; attack tests per trust boundary and the device checks. Four
points left for the owner in section 14.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner's decisions, 2026-09-26. The second row lists what was adopted,
which driver guards are trials for M5b's run, and what was left out.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the combined Ornith guidance (§6). Each session runs in its own
process group and is stopped after SESSION_LIMIT_MIN minutes (default
90), killed by group so the cargo it started goes too, never with pkill
(titan has no `timeout`, so the limit is plain shell). A task that logs
`stopped` gets its own message with the row. Two guards for a trial run:
RESUME=1 resumes a session once when it leaves work uncommitted, and
SLOTS_URL stops a session when no llama slot has been busy for
IDLE_LIMIT_MIN minutes (default 10).
Tested against a stub opencode outside a throwaway repository: a clean
task, a stopped row, a wrong trailer, an uncommitted session with and
without RESUME, a hang past a 1-minute limit (its child process killed
with it), and a session with no busy slot for 1 minute.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
One list for the design model to work through per task, gathering the
task-writing tips that apply before hand-over (T18, T19, T24 to T29,
T32 to T35, T39, T40) and three new ones from the combined Ornith
guidance: walk each given test by hand against the task's rules (T41),
grep earlier given files for anything the task changes (T42), and run
the gate's own checks on the given files (T43).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the combined Ornith guidance (§8.2), checked against OpenCode
1.18.30's schema and permission docs (last matching rule wins). Reads
and edits outside the repository are denied, except OpenCode's own
tool-output and temporary directories; web access, questions, subagents
and doom-loop prompts are denied; edits to AGENTS.md, CLAUDE.md,
opencode.json, the design documents, specs, plans and tools/ are denied;
shell commands are allowed except push, reset, rebase, amend, switch,
checkout, find /, locate, sudo, rm -rf / and curl/wget. A denied call
continues the loop (experimental.continue_loop_on_deny, untested with
Ornith). steps = 400 is a backstop. No custom prompt: OpenCode already
reads AGENTS.md, and a prompt may replace its tool guidance.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
From the combined Ornith guidance (~/projects/ornith/GUIDANCE.md, §4):
experiments go under the ignored .state/scratch/, never /tmp (which
reconciles I16 with crossbar's in-repo experiments); a refused tool call
does not end the turn; todo!() bodies are filled one at a time with
cargo check between; the gate runs with a 600000 ms shell timeout;
Cargo.lock is staged when it changed; commit messages are written to a
file and committed with -F, since quotes broke -m in crossbar.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner's rule. The implementer often cannot know which model it is, so
run-plan.sh derives the name from OPENCODE_MODEL (the part after the
provider), gives the exact trailer and the Model column's value in the
session prompt, and stops the run when a task's commit lacks that exact
line. AGENTS.md replaces the old Implemented-By trailer and the "owner
fills in the model" rule.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner's decision. The hardening half (sanitizer, tag neutralisation,
image links escaped, the invalid-arguments reason, and the secret store
available to every role) needs neither roncho nor Honcho and can be
specified now. Honcho waits on roncho's tagged release. Earlier
documents keep the old numbers; P24 says so.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
What Boxmaker checked of v0.1.0 and the fixes on top of it: the
blocking library now builds and its tests pass, but the quoted
blocking-only test command does not compile (ungated async test
files), and nothing is committed or tagged yet. Five MUST items and
nine SHOULD items remain, a queue-status operation among them. New
requirements R23 to R27 prepare for rift turning on API keys: 401/403
as their own error, an empty key refused, one key per client, keys
wiped on drop, and the live suite able to run with a key. Choices
roncho made that Boxmaker accepts are listed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
For the owner's combined assessment across three repositories: 113
Ornith tasks from M2a to M5a, first gate 73/113, 74/84 without
intervention where recorded; how it was run; its strengths and failure
modes with each occurrence; the task format that works and why; where
the defects came from; and the gaps in the records. Compiled from the
implementer log, the lessons, the plan READMEs and the run logs.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
roncho moved twice while the requirements were written. Line numbers are
gone; each divergence is described by what the code does, rechecked at
a9708f1, which added the Workspaces API: O1 and O7 now name the async
functions, and workspace delete does not tell the 409 conflict apart.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The owner chose roncho over a client of memoryd's own. The requirements
for its developer: a blocking client whose dependency graph fits
Boxmaker's rules (no async runtime, licences, no duplicate versions,
Rust 1.95), no default server and no hidden environment settings or
redirects, caller-set idle timeouts and body caps, thirteen operations
checked against rift, the search and paging fixes found reading the
code, and eight questions about Honcho 3.2 to answer from its docs and
rift. References are to roncho at f24fc4a.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Two review entries in the implementer log: tasks 01 to 15 (the header
forgery, review ids, the runbook entries, log rows written from memory)
and tasks 16 to 22 with the device check on straylight (the ledger
raising state by the grant's label, the note answers, the stale target/
that stalled task 21, the clippy 1.95 stop, the fake podman race). Two
short rules go into AGENTS.md: stop after one check when a test outside
the task fails, and write the log row from the diff.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found in the M5a review: task 13 renamed core-memory-unreadable to
memory-unavailable but kept its body, which still described
memory/core.md, and appended a memory-index-damaged entry of its own that
names a pointer memoryd never prints and leaves out `bxctl reindex`. Its
log row says both fragments were copied. Both entries are now the plan's
fragments, verbatim.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
M5a task 21. The work is Ornith's, from the stopped run; the design
model checked it against the task file and committed it after finding
that the gate's failure came from its own reference worktree's stale
test binaries in the shared target/ (fixed with cargo clean; 944 tests).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
21: the ledger raises a session's state by the stricter of the grant's
label and the result's own, so a clean session that recalls a
memory-tainted note becomes untrusted and cannot write it into
memory.md (found on straylight, 2026-09-25). 22: the answers to a note
name notes.md, and for an untrusted add the file that was not changed.
The new given tests are ledger_label.rs and new versions of memoryd's
edit.rs and serve.rs; the old copies are kept as -task06 and -task10. On
a scratch tree at the end of task 20: the named tests fail and then pass,
5 and 4 mutations are caught, and the gate passes with 944 tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found on the device (2026-09-25): the ledger raised a session's state by
the grant's label, so a clean session that recalled a memory-tainted note
stayed trusted and could add it to memory.md. The spec's "as today" was
wrong; section 8 now says the state is raised by the stricter of the
grant's label and the result's, and the recall section says the session
becomes untrusted. The note answers (section 3) name notes.md and, for an
untrusted add, the file that was not changed: the model read the old ones
as success. Tasks 21 and 22.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Crossbar (main, 4c64158) cannot serve Boxmaker as deployed; agents
connect straight to a host's llama-server until crossbar adds a default
route, per-route stickiness, side calls outside its limiter and
llama-server's error shape. Queued-looks-idle stays open.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The fake wrote each argument with its own printf. On a loaded machine
(titan, serving Ornith while the gate runs) the 300 ms limit fired while
`run` was still recording, and the `kill` call's lines landed inside
`run`'s: `a_tool_past_its_time_limit_is_killed_removed_and_failed`
failed about one run in five. The fake now writes each call to a file of
its own and appends it with one `cat` (bash's printf writes line by
line), and the two time-limit tests check the calls other than `run`,
which may be killed before it records itself. Test-only; 20 of 20 runs
pass, and both tests pass with a forced one-second start.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
M5a task 16. The work is Ornith's, from the stopped run; the design
model checked it against the task file and committed it once the
memoryd lint that stopped the gate was fixed (44bdcbd).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Clippy 1.95's collapsible_match flags the nested `if` in the
`LogRecord::User` arm of `index_record`, and the workspace pins
rust-version 1.95, so `make gate` failed on titan. An empty user message
now falls to the `_ => {}` arm, which indexes nothing, as before.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The three task changes (.cargo/config.toml, copied toolkit shell test, and the three read_checked edits in gatewayd/secrets.rs) are done and all gatewayd/toolkit/brokerd tests pass. make gate does not reach them: it fails to compile memoryd on clippy collapsible_match at crates/memoryd/src/index.rs:224, which fails identically on the original committed code and is outside this task's scope. Per AGENTS.md point 4, committed only this log row.
Titan serves ornith-1.5-35b-a3b itself, so OpenCode there uses
titan/ornith-1.5-35b-a3b. The harness's device checks still use straylight.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The work moved to titan (macOS), where the gate failed for reasons older
than M5a: sockets under macOS's long TMPDIR pass its 104-byte limit,
gatewayd reads /proc/self for its uid, and /tmp is /private/tmp. Task 16
sets TMPDIR=/tmp for every process Cargo runs (.cargo/config.toml [env]),
has gatewayd take its uid from the owner of a pipe it makes (no dependency,
no unsafe), and hands over a toolkit shell.rs that compares resolved paths.
On a scratch tree the gate passes on titan and on talos with 936 tests, and
a wrong uid fails 7 gatewayd tests. Tasks 16 to 19 are renumbered 17 to 20.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
review::submit counted only the writes still waiting, so a write staged
after an accepted one got that one's id and was lost: never listed, never
acceptable. It now counts every Waiting line of the day. On a scratch tree
with task 16's line applied, both new tests fail and then pass, the old code
and a count of every line are both caught, and the gate passes (938 there;
941 after task 18).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
entry_problem refuses any line that starts with the separator, and a noted
header with an unknown last part is not a header. The new given tests are
forgery.rs and new versions of memory_args.rs and notes.rs; the old copies
are kept as -task02 and -task06. On a scratch tree at the end of task 17,
the three tests named in the task fail and then pass, four mutations of the
fix are caught, and the gate passes with 939 tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>