Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
M5c-1 implementation plan: Honcho, the write path
For the implementer: each task file is one closed task for one fresh session. Read
AGENTS.md, then the task file, and do exactly that task. The driver istools/run-plan.sh.
Goal: finished turns and memory writes reach Honcho: turns to the agent's trusted or untrusted
workspace by the session's state at the turn's end (never a secret session's), trusted memory
writes mirrored as conclusions at once, untrusted notes to the fleet workspace as messages, all
through an append-only ledger that is also the outbox.
Architecture: bxcore gains the speaker on a turn, the turn number on SessionUpdated, and a
session-state question; loopd, gatewayd and bxctl chat carry the speaker; brokerd answers
the question on a new socket; memoryd gains a honcho module (configuration, ledger, decision,
roncho clients, a sync worker, mirrored memory writes) and bxctl memory sync shows its state.
Tech stack: Rust 1.98, edition 2024; roncho 1.0.0 (default-features = false, features = ["blocking"]), added by the design model in task 00; no other new crate.
Spec: docs/specs/2026-09-27-m5c-honcho.md (approved 2026-09-27), sections 3 to 7 and 10 to
13. The read path (recall over the fleet, ask_memory) is M5c-2, a later plan.
Global constraints
- Roles depend on
bxcoreonly;memorydalso onroncho(task 00). Nounsafe, no async runtime. - Every
ronchoclient is built with.without_config_file()(spec 6). - Nothing is logged that holds memory text or a key:
ronchoerrors are logged by kind and status only (spec 6). - Nothing above
privateis written, and never asecretsession's turn (spec 3). - No file the implementer reads holds a chat-template tag whole (T33); no fake key whole.
- Every path a given test creates is removed by a guard (T44); every process a test starts has a deadline.
- Every fail-closed state has a runbook entry, added by the task that adds its pointer (T49's lesson from M5b: the gate needs the entry with the pointer).
- Commit trailer from the driver; gate with a 600000 ms timeout.
Tasks (outline, for the owner's review)
| # | Delivers | Crates |
|---|---|---|
| 00 | (the design model, before the run) roncho added: workspace dependency, docs/dependencies.md, docs/egress.md (rift), cargo deny, the Nix build on straylight; saved Honcho responses for the fake server (below) |
all |
| 01 | Wire: Turn.speaker, LogRecord::User.speaker (optional, absent in old logs), SessionUpdated.turn, SessionState → SessionStateReply; fixtures |
bxcore |
| 02 | loopd logs the speaker on the User record and sends the turn number in SessionUpdated |
loopd |
| 03 | gatewayd sets the speaker: the Mattermost username when every post of the burst is from one person, else none |
gatewayd |
| 04 | bxctl chat --speaker NAME, opt-in, no default |
bxctl |
| 05 | brokerd serves run/memory-broker/state.sock from its state store; brokerd-listener-lost and socket-forbidden name it |
brokerd |
| 06 | memoryd's [honcho] section and its keys, checked at start; runbook honcho-config-invalid |
memoryd |
| 07 | The ledger, memory/honcho.jsonl: its records, append, read-through, and the outbox |
memoryd |
| 08 | The decision for a turn (a pure function: state, labels, speaker, text) | memoryd |
| 09 | On SessionUpdated, after loopd has its answer: ask brokerd, read the log, decide, record; undecided turns retried at the next report of any session; the ledger read through at start; runbook broker-state-unavailable |
memoryd |
| 10 | roncho clients per workspace, and creating workspaces, peers and sessions (names, the agent unobserved, the fleet with reasoning, summaries and dreaming off, the -2 sessions); the fake Honcho server |
memoryd |
| 11 | The sync worker for turns: messages in parts of 25,000, neutralised; backoff; outage logged once; refused sends and keys; runbooks honcho-unreachable, honcho-send-refused, honcho-key-refused |
memoryd |
| 12 | Memory writes mirrored: each journalled edit decided by spec 4's table (conclusions, fleet messages, retractions, undo) | memoryd |
| 13 | The sync worker for memory writes: conclusions created and deleted, fleet messages | memoryd |
| 14 | bxctl memory sync: decided, sent, waiting, failed |
memoryd, bxctl |
After task 14, the design model runs the M5c-1 device check (spec 13) on straylight, rift and the owner's Mattermost.
The owner's answers (2026-09-27)
bxctl chat's speaker is opt-in:--speaker NAME, with no default. Console turns are mostly tests, and Honcho can neither forget them nor be shared without them; abxctl chatturn without the flag has no speaker and is not synced (spec 4). Task 04.- Saved Honcho responses for the fake server: the design model records real responses from
rift in task 00, in a throwaway workspace,
boxmaker-plan-probe, that it creates and deletes. It touches no other workspace on rift, in any way (not even a read): rift has no keys yet, so the rule is ours to keep.
Before hand-over
Every task is worked through docs/plans/CHECKLIST.md with a reference in a scratch worktree
with its own build directory, replayed from its task text; mutations; /tmp compared around a
gate run; scratch files in the worktree's .state/; the reference moved to straylight as a bundle
before the run. Task files name their log row (T47); a scanner-like function gets a golden file
(T46); a request through another role is counted against its limits (T49).
Running it
git switch -c m5c1 master
OPENCODE_MODEL=straylight/ornith-1.5-35b-a3b RESUME=1 \
SLOTS_URL='http://straylight:11434/slots?model=ornith-1.5-35b-a3b' \
tools/run-plan.sh docs/plans/M5c1 01
(Or titan's Ornith, titan/… and http://127.0.0.1:8081/…, once its GPU is free.)