Files
boxmaker/docs/plans/M5c1

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 is tools/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 bxcore only; memoryd also on roncho (task 00). No unsafe, no async runtime.
  • Every roncho client is built with .without_config_file() (spec 6).
  • Nothing is logged that holds memory text or a key: roncho errors are logged by kind and status only (spec 6).
  • Nothing above private is written, and never a secret session'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; runbook broker-state-unavailable 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: ask brokerd, read the labels, decide, record; undecided turns retried 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)

  1. 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; a bxctl chat turn without the flag has no speaker and is not synced (spec 4). Task 04.
  2. 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.)