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>
The M5a review found that a note's text could hold a line shaped like a
notes.md header. Read back, the line started an entry of its own: an
untrusted session could plant a 'moved from memory.md' header, and recall
showed what followed it as identity, not untrusted. Entries and notes now
refuse any line that starts with the separator, and a header must match one
of its three forms exactly.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 16: memoryd takes the day and the month of a time with str::get, never a
slice. Task 17: repeated error lines in memoryd, proto, brokerd and bxctl
written once. Neither changes behaviour. Both were applied to a scratch tree
from 5f82260: the gate passes with 936 tests, and four mutations of the new
helpers are caught by the existing tests. The README runs with OPENCODE_MODEL.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Every row from M2a to M5a that read ? or Ornith, from the reviews' own
records of who ran each milestone. The DEBUG-HANDOFF row stays ? : no record
says which model it was.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
M5a task 04 stopped twice on the chat-template tags the check refuses.
The first time, the model read them in a file and took them for its own
template. The second time, it spelled them out in its own reasoning.
Measured on straylight: the think and tool-call tags are one token
whatever the tokenizer's flag. Inside any chat message, the turn
markers become control tokens, so a tool result can forge a system
turn.
Adds the rule to AGENTS.md, and proposes neutralizing the tags in
loopd as an open decision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The session did the task and stopped on make verify-device. The cause
was not the server: run-plan.sh and the device tests both read
BOXMAKER_MODEL, and the run's value (OpenCode's provider-prefixed id)
reached llama-server as the model name. With it unset, verify-device
passes 6 of 6. The copied files are identical to the plan's, and the
gate prints gate: ok, 936 tests.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 04's second session stopped too. It checked its code against the
markup test cases in its reasoning, and the tag it spelled out ended
its reasoning. Building tags from pieces keeps them out of files, not
out of the model's thoughts.
check.rs is now given whole, with the tags from pieces, and task 04
fills only entries.rs. The three later tests that used a tag only as
something the check refuses now use an instruction phrase or an
invisible character. They pass against the reference.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 04's session read the given test and the check.rs skeleton, both
holding the tags the check refuses, and Ornith took them for its own
template. It lost track of the file and made no change. The tags are
now built with concat! and format! in check.rs and in the four given
tests that held them, and the spec describes them in words. The
rewritten tests pass against the reference, are rustfmt-clean, and
still catch the case-folding mutation.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Follows the owner's other projects (sift, tether): nixpkgs pinned to
nixos-26.05, x86_64-linux, the version from the commit. `packages.default`
builds all six binaries with rustPlatform (tests stay in `make gate`),
`packages.tools-image` is deploy/tools-image.nix, and `devShells.default`
has Rust 1.95.0, clippy, rustfmt, cargo-deny and make. No NixOS module
yet: that is M8 (P22).
Checked on straylight: `nix flake check` passes; `nix build` gives
brokerd, bxctl, gatewayd, inferproxy, loopd and toolkit, each printing
its usage; `nix build .#tools-image` and `nix develop` work.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Owner's direction, 2026-09-25: one agent per host, each with its own
identity, memory and credentials, sharing fleet memory through Honcho;
inference from a configured llama-server-compatible endpoint; a last
milestone that deploys to NixOS hosts and other Linux hosts. Not applied
to the brief yet.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
memoryd (files, the check, edits, journal, index, recall, serving),
the memory tools and built-in grants in brokerd, the memory block in
loopd, bxctl reindex and memory, and the device checks with memoryd.
Each task was built as a reference and gated. Each was replayed on its
own at its end state (854 to 936 tests) and mutation-checked. Each
skeleton compiles and fails its tests. Every todo comment was read
beside its signature.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The memory socket in brokerd.toml is optional, and bxctl's flag is
--memory-socket. A note's header carries its class. Built-in grants are
kept apart from the owner's, and builtin- ids are reserved. The session
state is carried through Decision and RunSpec, labels through
run_labelled. loopd's assemble_with and MemorySource. Baseline
measured at 881 and 2,432 tokens.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
nobody_else_and_nothing_else_answers_it read five replies after
waiting only for the first, so it failed whenever the other four were
not posted yet. It failed once in a full `make gate` run, when many
test binaries ran at once. The helper now waits for as many posts as
the test reads.
The M4b plan's copy is left as it was handed over.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The research feeds M5b's sanitizer. One finding is a bug in the M5a
draft: category Cf misses variation selectors, the combining grapheme
joiner, and the Hangul and Braille fillers.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Writes from sessions that have read untrusted content go to the
archive, labelled untrusted, and never reach the prompt. Every write is
checked, announced in its thread, journalled and can be undone.
Session-scoped approvals for other tools become an open decision.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The owner's review of 2026-09-24. P17: the memory tool gains
consolidate, which moves entries out of memory.md into
memory/archive.md, and from M5b into Honcho, instead of losing them.
P18: memoryd owns memory/ and the SQLite index, so every memory tool
runs in one place.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The owner's direction of 2026-09-24:
- Memory is three local files, as Hermes has them: a soul written by
the owner, and memory and user files the agent edits.
- Honcho is the fleet's memory. Each Boxmaker writes only to its own
workspace, and can read other workspaces at a trust level the owner
sets.
- A new role, memoryd, does the syncing, because brokerd never holds
conversation history and loopd has no network.
- M5 splits into M5a (files and local search), M5b (memoryd and
Honcho) and M5c (the scheduler).
The brief is not changed until the owner approves P17 to P20.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The two posts caught up at restart are two top-level posts, so two
sessions, and their turns run on their own threads. gatewayd starts
them in order, but which one reaches loopd first is up to the
scheduler. Under load the test failed about 1 run in 60; task 18's
session hit it once. mm_json.rs already checks the order of catch-up
(reversing the sort fails there), so this test now compares the two
turns as a set. It passed 100 of 100 runs under load.
The M4a plan's copy is left as it was handed over.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The review of tasks 01 to 15 goes in the implementer log, with the
device check against the owner's Mattermost. Three follow-ups:
- 16: the refusal without a runner says why.
- 17: the texts that say who asked.
- 18: approvals from non-approvers in direct or group messages go to
an approver.
Task 14's given serve_approvals.rs becomes serve_approvals.rs-task14,
since task 18 hands over a new version. Tip T31 is added.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 15's session found the defect in serve/approvals.rs, a file it did not list, and stopped.
The comment in task 14 used one name for two values; task 14's tests could not reach it. Lesson T30.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Skeletons for new files only; existing files change by exact edits, so no task overwrites an
earlier one's work. Each task's tests were run against a reference built in order from master,
with the gate at each step and mutations checked; each skeleton compiles and fails its tests, and
every todo!() comment was read beside its signature. The reference is kept off this machine.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
approval_reply and GatewayOutcome (proto already has an ApprovalAnswer); the bot's edit permission
confirmed on the owner's server; a restart replaces a dead post's text; the reason escaped as
inline code; the part size bound; the reply for an approval too long to show.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The owner's decisions of 2026-09-24: commands only, no reactions; an approvers list that falls
back to allow.users; approvals in the asking thread; on a gatewayd restart, old posts are marked
dead and brokerd's courier posts pending approvals again.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sixteen tasks by Ornith through the plan driver, the review's fixes, and task 16 for its one low
finding. Checked end to end against the owner's Mattermost.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The test swaps the file between the check and the open through a `between` hook. A reference fix
passed it and the gate (769 tests) in the working tree, caught two mutations, and was removed.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A status line splits on single spaces only, and every chunk line must end in CRLF. The bounded
`as` casts in http.rs, handshake.rs and proto's sha1.rs become try_from and from.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A script that rewrote the skeleton's comments put `now_ms`'s text on `post`. Task 14's second
session saw the contradiction and deliberated until cut off. Both comments are fixed, each
todo!() comment in tasks 14 and 15 was read beside its signature, and both tasks now say to stop
and quote a comment that does not fit. Lesson T29.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Task 14's session read the crate to learn the APIs and planned `connect` in prose until it was
cut off. `connect`, `Gateway::new` and `catch_up` are now written; the task lists every call with
its signature and makes the copy and the failing test the first actions. Task 15's comment is
rewritten one step per item. Both checked to fail, then pass when filled from their comments.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>