Commit Graph
100 Commits
Author SHA1 Message Date
kyleandClaude Opus 5.5 bd4cf9fb7f review: a key split by an invisible character is still a secret
has_secret masked each character a character rule matches to a space, so one
zero-width space inside a key cut it into two runs too short to match. The
secret rules now also run on the text with those characters deleted, and the
text is a secret if either matches.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:12:12 -07:00
kyleandClaude Opus 5.5 3d266df9de review: bxctl memory sync shows what the Honcho side waits on
A shared status, kept by the worker and the walk and read by the report under
its own leaf lock, holds the workspaces whose key was refused, when an outage
of Honcho began, and when brokerd stopped answering. The report adds a line
for each while it lasts, and is unchanged when all is well. The runbook's
Honcho entries and broker-state-unavailable now say what sync shows.

Tuning and Wake move to honcho/wake.rs (re-exported from worker) to keep
worker.rs under 500 lines. honcho_sync.rs's serve test now allows the outage
line: the write wakes the worker, which finds Honcho unreachable.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:11:31 -07:00
kyleandClaude Opus 5.5 3dd95c5791 review: a memory write wakes the Honcho worker
A write, an undo or an accepted write decided into the ledger waited out the
worker's 30 s idle sleep. Mirror::journalled now says whether it added a line,
and memoryd wakes the worker when it did. The Wake lock is a leaf, taken under
the index lock as before.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:07:38 -07:00
kyleandClaude Opus 5.5 7c207f6ec6 review: an undo concludes again only what it had deleted
Undoing a consolidate concluded its moved entries a second time, and a later
removal deleted only the newer conclusion. An undo's added entry is now
concluded only when the ledger decided a Conclude for it and then an
Unconclude: an entry still concluded, or never concluded (a secret session's,
one from before sync), gets nothing. Ledger::concluded and unconcluded answer
from the decisions, in a module of their own to keep ledger.rs under 500 lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:06:28 -07:00
kyleandClaude Opus 5.5 b392af9ca4 review: memory writes from a secret session are not written to Honcho
The journal now records each call's class (absent on the owner's undo and on
older lines, which still read). The mirror turns everything a secret write
would conclude or send to the fleet into one Skip, after its deletions, which
still go; a note whose own header says secret is skipped the same way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 14:04:42 -07:00
kyleandornith-1.5-35b-a3b 5f4e0d9997 memoryd: sync begins when Honcho is turned on, and conclusions are matched by peer
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 13:40:48 -07:00
kyleandornith-1.5-35b-a3b fb2afa1979 bxctl memory sync: the Honcho ledger's counts and failed items
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 13:22:26 -07:00
kyleandornith-1.5-35b-a3b fb05b26462 memoryd: the Honcho worker sends conclusions, their deletions and fleet notes
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 13:11:51 -07:00
kyleandornith-1.5-35b-a3b 4ebfc36c1a memoryd: memory writes decided into the Honcho ledger as they are journalled
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 12:46:18 -07:00
kyleandornith-1.5-35b-a3b b37eaef390 memoryd: the Honcho worker sends decided turns, waits out outages, retries undecided turns
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 12:30:49 -07:00
kyleandornith-1.5-35b-a3b aabfbc2f0a memoryd: a Honcho client per workspace, and its workspace, peers and sessions made sure of
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 12:13:48 -07:00
kyleandornith-1.5-35b-a3b 28df2a0e3b memoryd: a reported turn decided after loopd has its answer, from brokerd and the log
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 11:59:34 -07:00
kyleandornith-1.5-35b-a3b a339eb58b6 memoryd: where a finished turn goes in Honcho, decided from brokerd and the log
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 11:41:38 -07:00
kyleandornith-1.5-35b-a3b 8aaeb74f36 memoryd: the Honcho ledger, decided, sent and failed, read through at start
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 11:33:49 -07:00
kyleandornith-1.5-35b-a3b 129a9eadc3 memoryd: [honcho] and its keys checked at start, and brokerd's state socket
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 11:20:10 -07:00
kyleandornith-1.5-35b-a3b 9b4e9807b2 brokerd: state.sock answers memoryd's question about a session's state
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 11:05:42 -07:00
kyleandornith-1.5-35b-a3b 5199bd7149 bxctl: chat --speaker NAME, the speaker on every turn of the chat
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 10:57:11 -07:00
kyleandornith-1.5-35b-a3b fae12c3fe2 gatewayd: a turn's speaker, the username of the one person who wrote its burst
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 10:51:17 -07:00
kyleandornith-1.5-35b-a3b 4866353ac9 loopd: a turn's speaker in the log, its number in SessionUpdated
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 10:33:36 -07:00
kyleandornith-1.5-35b-a3b c798714af1 bxcore: a turn's speaker, SessionUpdated's turn, the session-state messages
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-28 10:27:28 -07:00
kyleandClaude Opus 5.5 027146f359 M5c-1 plan: README, tasks 07 to 15, the owner's decisions, the known flaky test; M5b finding 8 closed
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 10:00:59 -07:00
kyleandClaude Opus 5.5 d8952ca77d M5c-1 plan: task 15, sync from when Honcho is on, conclusions matched by peer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:58:26 -07:00
kyleandClaude Opus 5.5 69370f03c6 docs: M5c spec: roncho's features are ["tls"], as task 00 set them
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:39:53 -07:00
kyleandClaude Opus 5.5 44d597524b M5c-1 plan: task 14, bxctl memory sync
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:38:50 -07:00
kyleandClaude Opus 5.5 687d07a9a7 M5c-1 plan: task 13, the Honcho worker sends memory writes
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:27:51 -07:00
kyleandClaude Opus 5.5 d3d37c955a M5c-1 plan: task 12, memory writes decided for Honcho as they are journalled
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 09:08:56 -07:00
kyleandClaude Opus 5.5 5a0cee42ec docs: M5c spec: conclusions go to <id>_memory; no retraction, notes cannot be undone
A memory write is made during a turn, before that turn's Honcho session exists, and a session
whose turns are never synced has none, so each Boxmaker session's conclusions go to its own
<id>_memory session, holding the agent's peer and the owner's. `bxctl memory undo` refuses notes
(they are append-only), so the undo-of-a-fleet-note row and its retraction go. Owner, 2026-09-28.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 08:53:56 -07:00
kyleandClaude Opus 5.5 67ddda514b M5c-1 plan: task 11, the Honcho worker that sends turns
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 08:45:55 -07:00
kyleandClaude Opus 5.5 a359f34a73 M5c-1 plan: task 10, the Honcho clients and the ensure steps in memoryd
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 08:05:57 -07:00
kyleandClaude Opus 5.5 2926031b4d docs: M5c spec: sends are at least once, skip in decided lines, sync from when Honcho is on
The ledger cannot promise an item is never sent twice: a crash between a send and its sent line,
or an owner removing that line, sends it again. A decided line holds a skip and its reason, as
task 07's ledger does. Turns from before sync began are skipped (owner's decision, 2026-09-28).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 07:59:30 -07:00
kyleandClaude Opus 5.5 47d846eb77 M5c-1 plan: task 09, turns decided on SessionUpdated after loopd has its answer
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 07:42:23 -07:00
kyleandClaude Opus 5.5 991545c778 M5c-1 plan: task 08, the turn decision in memoryd
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 06:57:08 -07:00
kyleandClaude Opus 5.5 aef6525cc7 M5c-1 plan: task 07, the Honcho ledger in memoryd
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 06:43:05 -07:00
kyleandClaude Opus 5.5 0e1e070fc3 M5c-1 plan: task 06, memoryd checks [honcho] and its keys at start
memoryd.toml gains an optional [honcho] section, [[honcho.read]] and the
Honcho keys in [secrets.<name>], checked at start into
memoryd::honcho::config::HonchoConfig (13 reasons, runbook entry
honcho-config-invalid given as a fragment), and [sockets] state for
brokerd's state.sock. Keys are checked, never read; no [honcho] leaves
memoryd as it was. memoryd enables bxcore's feature secrets.

Checks: a reference at ref 05 + this task passes make gate with 1188
tests (1171 + 17); /tmp unchanged around the gate; the given tests fail
before as the task says (two targets do not compile, 4 start tests
fail); 49 one-line mutations of the reference, all caught (one gap, a
URL with a user and no password, found and closed); the new tests 40
times under CPU load, no failure; a replay from the task text on a fresh
ref-05 worktree with its own target is byte-identical and passes the
gate, and every before block was found once; given files pass fmt and
the 500-line limit; no chat-template tag in any file.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 02:48:16 -07:00
kyleandClaude Opus 5.5 ba8267c7dc M5c-1 plan: task 05, brokerd answers memoryd's session-state question on state.sock
brokerd serves a third socket, [sockets] state (default
<home>/run/memory-broker/state.sock), bound by the same listen (dir 0700,
stale socket removed, socket 0600), one thread per connection, one
question per connection. The answer is StateStore::read: the stored taint
and flag, the store's default (private, trusted) for a session with no
file. A damaged state is an internal error with the store's text, never an
answer; other kinds are forbidden; malformed frames get read_request's
error. A new tests/serve.rs waits for an answer before reading the
startup notices (a race that failed about one run in ten under load,
before this task too).

Checks: every before block found once at ref 04 (20 edits); the new file
in the task is byte-identical to the reference; the given tests fail
before as the task says (config and state_socket do not compile;
serve_state 5 of 5 fail); gate ok with 1171 tests on the reference and on
a replay from the task text in a fresh ref-04 worktree with its own build
dir (byte-identical to the reference; removed); /tmp unchanged around the
gate; 25 mutations, 24 caught, the one missed (state bound last) changes
no tested behaviour; the socket tests 25 runs under load, 0 failures;
no task-text line over 100 characters outside tables and code.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 01:52:06 -07:00
kyleandClaude Opus 5.5 5454d7ea1b M5c-1 plan: task 04, bxctl chat --speaker NAME, opt-in, no default
The speaker goes on every turn of the chat: interactive, --say and --say's retry. Without the
flag it is None. A name that is empty or holds whitespace or a control character (Unicode), or a
second --speaker, is a usage error: the usage text on stderr and exit status 2. The usage text
gains the flag and one line explaining it. Given: chat_speaker.rs (new) and new versions of
chat.rs, chat_approvals.rs and cli.rs. Task 01 now copies chat.rs-task01, the older chat.rs.

Checks: every before block found once at ref 03; replayed from the task text on a fresh ref-03
worktree with its own build directory, byte-identical to the reference, gate ok with 1158 tests
(1148 + 10); /tmp unchanged around the reference's and the replay's gate runs; 20 of 20
mutations caught; the new tests passed 40 of 40 runs under CPU load; given files pass fmt and
the 500-line check; no chat-template tag in the plan or the given files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 01:04:27 -07:00
kyleandClaude Opus 5.5 5ca50257d4 docs: roncho requirements: name no other agent's workspace
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 00:38:43 -07:00
kyleandClaude Opus 5.5 5465946d98 M5c-1 plan: task 03, gatewayd sets the speaker from the burst's one author
A batch carries the Mattermost user id of each message; the turn's speaker
is the username of the one user who wrote all of it (Client::username,
kept in a map on the event loop), and None for two or more people or a
failed lookup. The turn's text is unchanged.

Checks: reference replayed from the task text on a fresh ref-02 worktree
with its own build dir, byte-identical; make gate ok with 1148 tests
(1138 + 10); /tmp unchanged around both gate runs; 21 mutations, all
caught; new tests stressed 40 times under load, no failure; every before
block found once at ref 02; no chat-template tags.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 00:30:24 -07:00
kyleandClaude Opus 5.5 4e823a76e6 docs: decisions: one toolchain, Rust 1.98.1
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 00:04:38 -07:00
kyleandClaude Opus 5.5 d2e6cfa51f flake: Rust 1.98.1 from rust-overlay; rust-version 1.98
The owner's hosts (titan, talos) run 1.98.1; nixos-26.05's Rust is
1.95.0, so builds and lints differed by host. The flake gains a second
input, oxalica's rust-overlay (following our nixpkgs, so host configs
can still follow theirs), and both the package build and the dev shell
use its stable 1.98.1 with clippy and rustfmt. The workspace's
rust-version rises to 1.98 to match; AGENTS.md, docs/dependencies.md
and the M5c-1 plan follow.

Checked on straylight: nix build gives all seven binaries; the dev
shell has rustc, cargo and clippy 1.98.1, rustfmt 1.9.0, cargo-deny
0.19.6; make gate in it passes with 1122 tests, as on titan.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-28 00:04:38 -07:00
kyleandClaude Opus 5.5 0a59dc0b22 M5c-1 plan: task 02, loopd logs the speaker and sends the turn's number
loopd copies the Turn's speaker onto the User record it logs, as it
came (not checked, not neutralised), and never into a request; after a
finished turn it sends SessionUpdated with the turn's number, the count
of User records in the session's log (Session::turns), so the first
turn is 1, a failed turn that was logged counts, and the count goes on
after a reopen or a restart. Failed turns are still not told.

Given: speaker_turn.rs (8 tests) and new versions of loopd's memory.rs,
turn_broker.rs, end_to_end.rs and support/turn.rs (run_turn gains
speaker, MemorySource::updated gains turn). Task 01 now copies
memory.rs-task01, its own version.

Checks: the reference passes make gate with 1138 tests (1130 + 8); the
task replayed from its text on a fresh ref-01 worktree with its own
target is byte-identical to the reference and passes the gate; /tmp
unchanged around both gate runs; 15 one-line mutations (speaker not
logged or dropped by the channel or neutralised, speaker in the request
at log time or at replay, turn 0-based or +1, counting Assistant
records, all records or only speakers' turns, a global or per-process
counter, told before logging or on failure, MemoryPort sending 0) all
caught; speaker_turn run 60 times under load, no failure; no
chat-template tag in the plan or given files; task lines within 100
characters but tables.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 23:49:35 -07:00
kyleandClaude Opus 5.5 82b7898e84 M5c-1 plan: task 01, the speaker, SessionUpdated's turn, the session-state messages
Task 01 changes bxcore's wire and log types for M5c-1: Turn.speaker and
LogRecord::User.speaker (optional, skipped when None, so old frames and logs
keep their bytes), SessionUpdated.turn (required, refused when missing), and
the new SessionState / SessionStateReply messages. The other crates only
construct the new fields (speaker: None, turn: 0) and brokerd's kind() names
the two new kinds; later tasks give them values. 20 given files.

Checks, against a reference at 7b40686 with its own build directory:
- every "before" block found exactly once at 7b40686 (and at master, which
  changed no crate since);
- step 2's failures as the task names them: two strict tests, fifteen test
  targets with E0559/E0560/E0432/E0599 only;
- make gate: gate: ok, 1130 tests (1122 + 8); /tmp unchanged around it;
- replay from the task text in a fresh worktree of 7b40686 with its own
  target/: crates/ byte-identical to the reference, gate ok, 1130; removed;
- 23 mutations: 21 caught; the two that drop `default` from an Option
  field cannot change behaviour (serde reads a missing Option as None);
- no chat-template tag whole in the task or the given files; lines over
  100 characters only in tables, fixtures and verbatim strings.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 23:22:37 -07:00
kyleandClaude Opus 5.5 c151a7516d direnv: enter the flake's dev shell on hosts with Nix
`.envrc` runs `use flake` when Nix is present, so on the NixOS hosts
(programs.direnv.enable brings direnv and nix-direnv) cd'ing into a
clone gives the pinned Rust toolchain, clippy, rustfmt, cargo-deny and
make; on titan, which has no Nix, it does nothing. nix-direnv's cache,
.direnv/, is ignored. Checked on straylight in a throwaway copy:
cargo 1.95.0, cargo-deny 0.19.6, rustc from the Nix store.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 23:12:55 -07:00
kyleandClaude Opus 5.5 b8ebfd45ed M5c-1 plan: Honcho responses recorded from rift (task 00)
29 request/response pairs, each request exactly as roncho 1.0.0's
blocking client sends it, recorded in a throwaway workspace
(boxmaker-plan-probe) created for it and deleted afterwards; no other
workspace was touched. For the fake Honcho server of task 10. Found:
creation is get-or-create (201, then 200); adding messages to a missing
session creates it; a 422 echoes the whole input; a deleted conclusion
answers 204 with an empty body, and 404 the second time.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:59:04 -07:00
kyleandClaude Opus 5.5 7b40686def memoryd: depend on roncho 1.0.0 (M5c-1 task 00)
The owner's Honcho client, blocking, with TLS: default features off,
feature tls. It adds only itself to the lock file (checksum a5ccccc2…,
as checked against crates.io in review). cargo deny passes. The
dependency and the egress to rift are documented; the egress table also
lists the design model's probe of rift in a throwaway workspace.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:53:19 -07:00
kyleandClaude Opus 5.5 76a655062e M5c-1 plan: the owner's answers (speaker opt-in; rift probe workspace only)
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:45:50 -07:00
kyleandClaude Opus 5.5 339de0fdc2 M5c-1 plan: outline for the owner's review
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:34:43 -07:00
kyleandClaude Opus 5.5 bc4eb3f271 docs: milestones: M5c as the approved spec has it
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:33:18 -07:00
kyleandClaude Opus 5.5 ee50adf78c docs: decisions: P23 applied as amended; the M5c design
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:33:18 -07:00
kyleandClaude Opus 5.5 cdedf26987 design: Honcho as the M5c spec settles it (P23, amended)
Memory items 4 and 5: two workspaces per agent and one fleet workspace
for untrusted notes; the private ceiling, secret sessions never synced;
trusted memory writes mirrored as conclusions; the agent's peer not
observed; no rebuild, corrections by contra-fact or deleting our own
conclusion; the fleet workspace always untrusted. Recorded in
docs/decisions.md.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:32:32 -07:00
kyleandClaude Opus 5.5 4abed5bdd9 M5c spec: approved
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:32:12 -07:00
kyleandClaude Opus 5.5 fdc03cf8ba M5c spec: the owner's answers
A turn without complete speaker information is not sent; every peer in
the fleet workspace is a member, so fleet notes are messages that one
search finds, with a retraction message for an undone note (Honcho
cannot delete a message); the fleet workspace derives nothing
(reasoning, summaries and dreaming off); API keys per workspace are
first-class, with a runbook entry for a refused key.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:25:20 -07:00
kyleandClaude Opus 5.5 23c9ebec12 M5c spec: Honcho, draft for the owner's review
Designed with the owner on 2026-09-27: per agent a trusted and an
untrusted workspace plus one fleet workspace for untrusted notes;
private ceiling, secret sessions never synced; no rebuild (contra-facts,
or deleting our own conclusion); the owner and other people observed,
the agent not; every trusted memory write mirrored as a conclusion; a
decision ledger that doubles as the outbox; recall over the local index
and the fleet; ask_memory by workspace chat. Grounded in roncho 1.0.0
and Honcho 3.2.0 (source read at v3.2.0) and rift's live metrics.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 22:11:31 -07:00
kyleandClaude Opus 5.5 4d1120032a docs: M5b landed; CLAUDE.md and decisions follow
decisions: M5b settles the invalid-arguments reason, tag
neutralisation and the shared secret store (their open rows go), and
the verdict on the run's two driver trials. CLAUDE.md's current state:
M5b done, M5c (Honcho, roncho 1.0.0) next; straylight's clone is
boxmaker-m5b.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:15:09 -07:00
kyleandClaude Opus 5.5 d423d87115 docs: the M5b device check, passed on straylight
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 6f243ad779 docs: M5b review finding 10, the tag check against inferproxy; tip T49
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 5a2464d81d loopd: the tag check asks /tokenize twice, not once per tag
Found in the M5b device check on straylight: the self-test asked about
each of the 33 tags and each neutralised form, 66 requests, one
connection each, and inferproxy's token bucket (burst 10) refused the
ninth, so loopd would not start. Now it asks about all the tags at once,
one per line, and then all their neutralised forms: the answer to the
first must hold each tag's recorded token in order, the second none of
them. Each tag is a special token, split out before anything else, so
the lines between cannot change it; and a neutralised form is only
dangerous as a tag's own token, so that is what the second answer must
not hold. The errors name a tag by its id, as before.

The fake tokenizer reads text with several tags in it; the tests cover
each tag as ordinary tokens, as another listed or unlisted token, a
duplicated token in the middle and at the end, a form holding each tag,
and both requests failing. Spec section 8 and the runbook entry follow.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 1fb0ec8a06 docs: the M5b review, and tips T45 to T48
The review of tasks 01 to 14 in the implementer log (task 14's row
renamed to its file's name), and four tips for writing tasks: what to
do with a skeleton's comments, golden files for scanners, naming the
log row, and running the implementer without plugins.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 1fda5bfdb9 bxcore, bxctl: drop the skeletons' instruction comments; usage indent
M5b review, findings 3 and 4: the instruction comments tasks 03 and 04
left inside filled bodies, and a space of indent task 11 added to four
lines of bxctl's usage text. No behaviour changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 27aeab48e1 gatewayd: links: url_len and closes as their comments say
M5b review, findings 1 and 2. url_len ended a clean reply's URL at any
`<` outside parentheses, not at a `<` before white space, a bracket,
`<`, `>` or the end; closes used a count of characters as a byte
offset, so a closer indented with a no-break space did not close its
fence. Both bodies, and fence_end's (an index replaced by `get`), are
the reference's again. tests/links_review.rs pins both cases with the
reference's output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 27a6354a80 docs: runbook: sanitizer-rules, how to change a sanitizer rule
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 7cbc1d7342 brokerd, memoryd, gatewayd: a lock file keeps each to one instance
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b fd0437a032 bxcore, loopd, inferproxy: a lock file keeps each to one instance
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 54f102bbe1 bxctl: sanitize check shows which sanitizer rules a text breaks
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 648469cd67 gatewayd: images, and links in untrusted replies, are posted as code
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 0d3048c3c4 bxcore, loopd: TurnDone says whether the session has read untrusted content
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 91992b9743 loopd: the self-test checks the model's tags against the tokenizer
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b b4d631e249 loopd: neutralise user messages, tool results and the memory block
Text from outside the model (the user's message, every tool result, and the
memory block) is neutralised the moment it is first logged, so every later
request replays the same bytes and a tag in that text cannot reach the model as
one of its own control tokens. The model's own output, replay, and the system
prompt are left byte-for-byte.

Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b ca8b50cd69 memoryd: refuse and flag memory writes through bxcore::sanitize
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b b3e9af7586 bxcore: sanitize::flag, text shaped like an attack
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 41d425f73e bxcore: sanitize::refuse, what a memory write may never hold
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b 1817e7f117 bxcore: the model's tags and sanitize::neutralise
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b f594e0d9d4 bxcore, gatewayd, loopd: the secret store moves to bxcore::secrets
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandornith-1.5-35b-a3b de60c9e13a bxcore, brokerd, loopd: an invalid-arguments denial says why
Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
2026-09-27 19:08:42 -07:00
kyleandClaude Opus 5.5 89a50bc3c6 tools: run-plan: pure sessions, resume after the limit, row named
Three changes from the M5b run (implementer lessons T47, T48):
- each session runs `opencode run --pure` with
  OPENCODE_DISABLE_CLAUDE_CODE=1, so no global plugin or borrowed rules
  file reaches the implementer; PLUGINS=1 restores both;
- with RESUME=1, a session stopped at SESSION_LIMIT_MIN with work left
  uncommitted is resumed once, as one that ended by itself already was;
  an idle stop is still not resumed;
- the prompt names the log row's Task cell, so a task's heading cannot
  mislead the row the driver then looks for.

Checked against a stub opencode in scratch repositories (17 checks:
the flags and variable, PLUGINS=1, a resume after the limit, no resume
without work or without RESUME).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-27 18:33:11 -07:00
kyleandClaude Opus 5.5 34ca1ef191 docs: decisions: a session's trust must survive compaction
Open item from M5b task 09: loopd reads a session's trust from its
epoch-0 log, so compaction must carry it into the new epoch.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 17:30:44 -07:00
kyleandClaude Opus 5.5 ba3c89791f M5b plan: README brought up to the written tasks
The task table follows the files (03 tags, 04 refuse, 05 flag) and
gives each task's gate count and mutations; the interfaces are the ones
the tasks built (task 02's role argument and SecretSpec, Refusal.at,
Flag, TurnDone.untrusted required, disarm, LockError::line); the review
focus names the tasks that pin each item; which task adds each runbook
entry, and the -taskNN convention for replaced given copies.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 17:30:44 -07:00
kyleandClaude Opus 5.5 81d849d4ba M5b plan: task 14, the runbook entry sanitizer-rules
The last runbook entry, the rule-change procedure of spec 6.5; the
entries memory-write-refused and memory-write-flagged now send the
reader to it instead of to the spec. Tasks 06, 08 and 12 added the
other five, because the gate needs an entry for every pointer.

Checks: the edits, applied from the task's text to ref 13, give a
runbook byte-identical to the reference; each before block found once;
the gate passes on the reference with 1118 tests; /private/tmp unchanged.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 17:29:50 -07:00
kyleandClaude Opus 5.5 5f48ba65b0 M5b plan: task 13, instance lock in brokerd, memoryd and gatewayd
Each locks <home>/run/<role>.lock in main (gatewayd after its secret,
so nothing is made before the secret is read). brokerd's old
second-instance test in serve.rs expected the audit lock's message,
so serve.rs has a new version; task 01 now copies serve.rs-task01.

Checks: given tests fail at ref 12 as the task says (2 each in
brokerd, memoryd, gatewayd; serve.rs passes); the task's quoted edits
applied to ref 12 reproduce ref 13's tree exactly; replay gate ok with
1118 tests (1113 - 1 + 6), /tmp unchanged; 14 mutations, all caught;
the six instance test files stressed 25 runs under 16 busy loops and
20 under 32, no failure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 17:21:14 -07:00
kyleandClaude Opus 5.5 291d82f21b M5b plan: task 12, instance lock in bxcore, loopd and inferproxy
bxcore::instance::lock (File::try_lock, file 0600, missing directory
0700), LockError::line for the two lines the daemons print, and the
already-running runbook fragment; loopd locks <home>/run/loopd.lock
before its socket and self-test, inferproxy <socket>.lock.

Checks: given tests fail at ref 11 as the task says (bxcore E0432;
loopd 3 and inferproxy 3 fail); the task's quoted edits applied to
ref 11 reproduce ref 12 byte for byte; replay gate ok with 1113 tests
(1099 + 14), /tmp unchanged; 25 mutations, 24 caught (the uncaught
one maps TryLockError::Error to Held, not reachable from a test);
given files rustfmt-clean, under 500 lines; no chat-template tags.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 17:21:14 -07:00
kyleandClaude Opus 5.5 ef2add03f0 M5b plan: task 11, bxctl sanitize check
The task file, the skeleton crates/bxctl/src/sanitize.rs (five todo!()
bodies: parse_sanitize, code_point, token, prefix, tag_count) and two
given tests: sanitize_check.rs (8, runs the binary) and
sanitize_report.rs (10, the library).

Decisions: the one argument after `check` is the text, whatever it
looks like; none means standard input. Output: refuse line (first rule
only; a character as U+XXXX, a tag as its token id, a secret as its
prefix and an ellipsis), flag line (rule only), neutralise line (every
occurrence of every tag). Exit 0 nothing fires, 1 something fires, 2
usage error or unreadable text. Nothing of the text is printed.

Checks: every given test fails before the task as the task says (the
report file does not compile: one E0432, two E0599; the binary tests
exit 2); the skeleton state gives 10 and 8 not-yet-implemented
failures; the reference passes make gate with 1099 tests (1081 + 18);
the task's edits applied to a fresh ref-10 copy with its own build
directory give a tree byte-identical to the reference; 21 mutations,
each caught; 40 runs under load, no failure; /private/tmp unchanged
around a gate run; rustfmt and the line limit on the given files; no
tag or key-shaped string in the plan or given files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 10:18:55 -07:00
kyleandClaude Opus 5.5 7eb65a276b M5b plan: task 10, gatewayd posts images and untrusted links as code
Task 10 (spec 9, the owner's option C): a new module gatewayd::links with
disarm(text, untrusted) -> Disarmed { text, links }, given as a skeleton
with 16 small todo!() helpers, and deliver posting each answer through
answer_posts: disarm, split_answer, disarm each part again, NOTICE once
at the end of the last post when a link was put in a code span.

Checks, against a reference kept off master:
- Replayed from ref 09 in a scratch worktree with its own target/: the
  task's edits (applied from the task text) plus the given files plus the
  reference bodies are byte-identical to the reference. make gate on
  rustc/clippy 1.98.1: gate: ok, 1081 tests (1058 + 7 + 7 + 9). /tmp
  holds nothing new after the gate.
- Step 2 (E0432 only) and step 3 (6/1, 7, 9 failing, all "not yet
  implemented"; warnings as the task lists) seen in the replay.
- 71 one-line breaks of the reference, each caught by the given tests.
- Differential fuzz of generated Markdown through Mattermost's marked
  fork (mattermost/marked c211e4c, the webapp's options) and CommonMark:
  on the final code, 600k replies in each mode, half with inline LaTeX
  on, render no image and (untrusted) no link.
- disarm(disarm(x)) == disarm(x) on 200k generated replies, both modes.
- Given files pass rustfmt; skeleton comments read beside their
  signatures; no chat-template tag in the plan or given files;
  deliver_links 40 runs under CPU load, no failure.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 09:21:57 -07:00
kyleandClaude Opus 5.5 8658af58b7 M5b plan: task 09, TurnDone.untrusted from the session's log
bxcore's TurnDone gains untrusted: bool, always written and required
when read (no serde default: a missing field would read as a clean
session, the wrong way to fail). loopd's new Session::untrusted is true
once any ToolResult in the log is labelled untrusted, and channel::handle
puts it in the TurnDone; the log is the one source, so it holds across
resumes and restarts. gatewayd, bxctl and brokerd sources are unchanged;
their tests that build a TurnDone get new versions. Task 01's broker.rs
is kept as broker.rs-task01, and task 01 now copies it.

Checks: the given tests fail at ref 08 as the task says (strict's
turn_done.json, and 13 test targets with E0560/E0609 only); the task's
edits applied from its text to a fresh ref 08 tree are byte-identical to
the reference; the gate passes with 1058 tests; 12 mutations caught;
/private/tmp and TMPDIR unchanged around the runs; no tag in plan or
given files.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 06:37:03 -07:00
kyleandClaude Opus 5.5 d4b7f42b56 M5b plan: task 08, the self-test checks the tags against the tokenizer
loopd's self-test gains a check, run second (after "server matches
config", before any prompt): each tag in bxcore::sanitize::TAGS must be
one token from /tokenize, its recorded id, and its neutralised form
more than one. A failure names the tag by its id, never its text, and
points at a new runbook entry, loopd-tags-unexpected, given as a
fragment; SelfTestError::pointer picks the entry per failure. One
request per text (66): /tokenize has no batch form. The fake server
gains respond and a fake tokenizer; selftest.rs, serve.rs, pointers.rs
and support/mod.rs get M5b copies.

Checks, on a reference at ref 08 (titan, rustc 1.98.1, own target/):
- /tokenize fields from llama.cpp tools/server/README.md at b10964
  (b29c606) and its handler; on straylight all 33 tags gave their ids
  and every neutralised form 7 to 11 tokens, 66 requests in ~0.3 s
- given tests before the change: 4 fail as the task lists, selftest_tags
  does not compile (7 E0599); pointers passes
- make gate: gate: ok, 1050 tests (1042 + 8); /tmp and $TMPDIR
  unchanged around it; the four suites 25 runs under load, no failure
- 20 mutations, each caught: id not compared; neutralised check
  dropped; > 1 as >= 1, as == 1 missing 0, as > 2; first or last tag
  skipped; tag asked twice; pointer dropped or wrong (all or Tokenize);
  check skipped in serve; check run last; parse_special false;
  add_special left out; id missing from the message; the tag's text in
  the message; tokenize error as Infer; wrong count; wrong other id
- the task's edits applied to a fresh ref-07 copy give files
  byte-identical to the reference; every before block found once
- no chat-template tag whole in the task or given files; task lines
  within 100 characters except tables and verbatim lines; given files
  under 500 lines

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 06:14:58 -07:00
kyleandClaude Opus 5.5 7998f0be17 M5b plan: task 07, loopd neutralises text when it is first logged
loopd neutralises the user's message (the User record), every text
appended as a tool result (results, recall's, denials, failures and
loopd's own answers share the one append in run_turn), after the cap,
and the memory block in Baseline::assemble_with. The model's output,
system.md and replay are untouched: an old log with a raw tag is
replayed byte for byte.

Checks, on a reference at ref 07 (titan, rustc 1.98.1, own target/):
- given tests before the change: 7 of 9 fail as the task lists; the
  two that guard what must not change pass before and after
- make gate: gate: ok, 1042 tests (1033 + 9); /tmp unchanged around it;
  the two new suites 20 runs, no failure
- 14 mutations, each caught: user not neutralised; neutralised before
  the cap; recall path missed; only untrusted results; truncated from
  the final length; block not neutralised; model content, reasoning or
  tool arguments neutralised; replay re-neutralises user, tool or
  system; open rewrites records; system.md neutralised too
- the task's edits applied to a fresh ref-06 copy give files
  byte-identical to the reference; every before block found once
- no chat-template tag whole in the task or given files; task lines
  within 100 characters except tables; given files under 500 lines

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 05:42:35 -07:00
kyleandClaude Opus 5.5 10d2e41f43 M5b plan: task 06, memoryd refuses and flags through sanitize
memoryd's writes go through bxcore::sanitize::refuse, then flag; check.rs
and its test go. A refused write answers with the rule's sentence and is
logged with the rule's name; a flagged identity write becomes untrusted
notes with the rule in the answer, the journal (new optional field `flag`)
and the log, and is never staged for review; the block leaves out an
entry that breaks a refuse rule and logs the rule. The three runbook
entries the new pointers need are given here as fragments, because the
gate's runbook check fails on a pointer without its entry.

Checks, on titan (rustc 1.98.1, clippy 0.1.98):
- given tests fail before as the task says: 7 failing in edit, serve and
  serve_sanitize; files and sanitize do not compile (E0609; E0432, E0609,
  E0615)
- the task's edits, parsed from the task file and applied to a fresh
  ref-05 worktree with its own target/, give a tree byte-identical to the
  reference; its gate: ok, 1033 tests; /private/tmp unchanged
- 26 one-line mutations of the reference, each caught by the given tests
- new tests stressed under load: 30 runs, 0 failures
- no chat-template tag and no whole fake key in the task or given files

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 05:14:10 -07:00
kyleandClaude Opus 5.5 9ee215337f docs: dependencies: credit Hermes for the ported flag rules
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 04:32:14 -07:00
kyleandClaude Opus 5.5 ad60f84a65 M5b spec: the flag port as planned in task 05
Records what porting Hermes' patterns decided: seven rules kept,
"dump the conversation" narrowed, agent-configuration changes left out
(Boxmaker's agent loads no such files), about fifty look-alikes,
apostrophes folded, and a flag in bxctl names only its rule. A false
positive also marks a session that recalls the note untrusted.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 04:32:14 -07:00
kyleandClaude Opus 5.5 0c2946c99e M5b plan: task 05, narrower flag rules
The design model's review of task 05: a recalled flagged note taints the
reading session, so a false positive on a code-work note costs a session.

- dump-conversation: the object must end its clause (a new part, Ends,
  and a function to write, ends_clause): punctuation after it, the last
  word, or one of so far, above, to, into, in, with, verbatim next. "include
  the conversation id in each log line" and CLAUDE.md's "spike output that
  contains conversation content" now pass (code-work corpus).
- persist-agent-config dropped: Boxmaker's agent never loads AGENTS.md,
  CLAUDE.md, .cursorrules or .clinerules as its instructions. Listed under
  not ported; its notes are code-work cases, its attack a decided edge.
- normalise folds the typographic apostrophes U+2018 and U+2019 to '.
- The corpus is split: other scripts and decided edges move to
  sanitize_flag_edges.rs, keeping each file under 500 lines.

Checks: make gate on the reference (ref 05, 818dbc7), rustc 1.98.1: gate
ok, 1023 tests (22 + 2 + 2 new); the task applied to a fresh ref-04 copy
is byte-identical to it; 60 mutations, each caught; /tmp unchanged around
the gate; no chat-template tag in the task or given files; given files
pass rustfmt and the 500-line limit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 04:30:59 -07:00
kyleandClaude Opus 5.5 e087ebb0ff M5b plan: task 05, sanitize::normalise and sanitize::flag
Task 05 adds normalise (case, full width, white space and zero-width,
leetspeak, fifty Cyrillic and Greek look-alikes from UTS #39
confusables 18.0.0) and flag: nine rules, eight ported from Hermes
Agent's tools/threat_patterns.py (NousResearch/hermes-agent at d0288be,
MIT) as word sequences with bounded gaps, and M5a's six phrases. Given:
two skeletons (eleven small todo!()s, tables whole) and two test files
(21 unit tests, 4 corpus tests including Russian and Greek notes and a
table of decided edges).

Checks: the given tests fail before for unresolved imports only (E0432);
the reference (ref 05) passes make gate on rustc 1.98.1, 1022 tests;
the task applied to a fresh ref-04 copy is byte-identical to it; 45
one-line mutations, each caught; /tmp unchanged around the gate; no
chat-template tag in the task or given files; given files pass rustfmt
and the 500-line limit; every todo!() comment read beside its signature.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 04:16:27 -07:00
kyleandClaude Opus 5.5 a28ba8792d M5b plan: task 04, PGP private key blocks too
The private-key rule also refuses a line that starts `-----BEGIN` and
ends `PRIVATE KEY BLOCK-----`, OpenPGP's armor header (RFC 9580
section 6.2.1; RFC 4880 section 6.2). The two endings are a given
table, PRIVATE_KEY_ENDS; the skeleton's private_key comment names it.
A PGP private key block joins the attack corpus, a PGP public key
block and a signature the code-work corpus, and the edge test gains an
indented PGP header (refused), a PGP public header and a PGP header
without its closing dashes (both pass).

Checks: the reference passes the gate on titan, 997 tests (unchanged:
the cases are inside existing tests), /tmp unchanged around the run;
the task's edits applied to a fresh ref-03 copy are byte-identical to
the reference; 8 private-key mutations, 4 of them new for PGP, all
caught; tag and private-key-header greps over the task and given files
are empty; given files pass rustfmt and the 500-line limit.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 03:41:52 -07:00
kyleandClaude Opus 5.5 ebadc8250e M5b spec: refuse PGP private key blocks too
The private-key rule named only PEM's ending, so an armored PGP
private key passed. Public-key blocks still pass.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 03:37:31 -07:00
kyleandClaude Opus 5.5 ed4cef9ec5 M5b plan: task 04, sanitize::refuse
The refuse half of spec 6.1 in bxcore: M5a's control and invisible
rules and tables, the tag rule narrowed to task 03's exact matching,
and new rules for private use, noncharacters and known-prefix secrets
(GitHub, GitLab, sk-, Slack, AWS, private-key lines). A skeleton with
eight small todo!()s; the rule table, sentences and secret tables are
given filled in. memoryd is untouched until task 06.

Checks: given tests fail before (E0432, then 16 not yet implemented);
the reference passes the gate on titan, 997 tests (981 + 16), /tmp
unchanged around the run; the task's edits applied to a fresh ref-03
copy are byte-identical to the reference; 36 mutations, all caught
(one needed a new case: lower case after AKIA); tag grep over the task
and given files is empty; given files pass rustfmt and the 500-line
limit; every todo!() comment read beside its signature.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 03:34:59 -07:00
kyleandClaude Opus 5.5 fb3c483b50 M5b plan: task 03, the model's tags and neutralise
Task 03 is now the tag list and sanitize::neutralise; refuse moves to 04 and flag
to 05. The list is the 33 control and user-defined entries of the model file
straylight's router loads (Ornith-1.5-35B-Q4_K_M.gguf, snapshot 12393612fd4f,
llama-server b10964-b29c606), each checked with /tokenize on 2026-09-26 to be one
token with its id. Review focus 1 is decided by the same measurement: a tag
inside a word is still the token and is neutralised; upper or mixed case, a
zero-width, space or line-feed split, and full-width brackets are not, and are
left as is. Each shape is pinned by a given test.

Checks: the given tests fail before (E0432, no bxcore::sanitize); the reference
passes make gate on titan (rustc and clippy 1.98.1) with 981 tests (963 + 18);
16 mutations of tags.rs, all caught; no failure message prints a tag; /tmp
unchanged around the gate; the task's edits applied to a fresh ref 02 tree give
a tree byte-identical to the reference; no plan or given file holds a tag whole;
given files pass rustfmt and are under 500 lines.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 03:11:31 -07:00
kyleandClaude Opus 5.5 0f1de0d6d4 M5b plan: task 02, the secret store moves to bxcore
The task moves gatewayd's secret store to bxcore::secrets behind the
Cargo feature `secrets` (spec, section 5). The moved module is given
whole; SecretSpec and SecretSource move with it, so gatewayd's config
keeps its [secrets.<name>] table with the per-secret type from bxcore.
load and read_checked take the role's name first, so the three
sentences that named gatewayd literally now name the role; gatewayd
passes "gatewayd" and its bytes are unchanged (its tests/main.rs pins
them and is not touched). A given loopd test parses the three
manifests (loopd's, the workspace entry, bxcore's features) and checks
the cfg line in bxcore's lib.rs; the task also checks `cargo tree -p
loopd -e features` and builds loopd on its own.

Checked: the reference, replayed from the task text applied
mechanically to the start tree (master plus task 01), is byte for byte
the reference; every before block found exactly once there. Step 2's
failures recorded from that tree: ten test targets with E0432 on
`bxcore::secrets`, two of four no_secrets tests. make gate on titan's
toolchain: gate: ok, 963 tests (956 + 3 + 4), clippy clean, /tmp
unchanged across the run. 23 one-line mutations of the reference, all
caught; removing the zeroize wrapper is not observable and not counted. Given
files pass fmt and the line limit; no chat-template tags.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 02:56:26 -07:00
kyleandClaude Opus 5.5 fa5bcae503 M5b plan: task 01, the invalid-arguments detail
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>
2026-09-26 02:35:35 -07:00
kyleandClaude Opus 5.5 56b969d55b M5b plan: task 00 done; task 14 is the runbook only
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 01:49:47 -07:00
kyleandClaude Opus 5.5 67985023c9 design: the shared crate is bxcore (decision of 2026-09-26)
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>
2026-09-26 01:49:21 -07:00
kyleandClaude Opus 5.5 9672cb5e53 AGENTS.md, CLAUDE.md, dependencies: the shared crate is bxcore
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-09-26 01:49:21 -07:00
kyleandClaude Opus 5.5 2b3ae080b4 proto becomes bxcore, the one shared crate (M5b task 00)
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>
2026-09-26 01:48:38 -07:00