Files

8.7 KiB

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 = ["tls"], which brings 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

The gate count is make gate's tests after the task. Mutations are one-line breaks of the reference, each run against the task's given tests (checklist, T28); those not caught cannot change behaviour, or are checked by the task's own greps instead, as the task says.

# Delivers Crates Gate Mutations
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 1130
02 loopd logs the speaker on the User record and sends the turn number in SessionUpdated loopd 1138
03 gatewayd sets the speaker: the Mattermost username when every post of the burst is from one person, else none gatewayd 1148
04 bxctl chat --speaker NAME, opt-in, no default bxctl 1158
05 brokerd serves run/memory-broker/state.sock from its state store; brokerd-listener-lost and socket-forbidden name it brokerd 1171
06 memoryd's [honcho] section and its keys, checked at start; runbook honcho-config-invalid memoryd 1188
07 The ledger, memory/honcho.jsonl: its records, append, read-through, and the outbox; runbook honcho-ledger-damaged memoryd 1212 45: 41 caught; 3 (no sync, two writes) checked by the task's greps; 1 cannot change behaviour
08 The decision for a turn (a pure function: state, labels, speaker, text) memoryd 1230 36: all caught
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 1257 42: all caught
10 roncho clients per workspace, and making sure of workspaces, peers and sessions (the agent unobserved, the fleet with reasoning, summaries and dreaming off, the -2 sessions); the fake Honcho server memoryd 1278 44: 43 caught, 1 cannot change behaviour
11 The worker for turns: messages in parts of 25,000, neutralised, one batch; backoff; outage logged once; refused sends and keys; runbooks honcho-unreachable, honcho-send-refused, honcho-key-refused memoryd 1300 36: 35 caught, 1 cannot change behaviour
12 Memory writes decided as they are journalled, by spec 4's table (conclusions, their deletion, fleet messages, undo); the ledger's own lock memoryd 1314 21: 17 caught, 4 cannot change behaviour
13 The worker for memory writes: conclusions made in <id>_memory and deleted, fleet messages memoryd 1324 23: all caught
14 bxctl memory sync: the ledger's counts and its failed items memoryd, bxctl, bxcore 1332 14: all caught
15 Sync begins when Honcho is turned on (began, and a skip for older turns); conclusions matched by peer memoryd 1345 15: all caught

A later task that changes a given file gives the earlier task its own copy, <name>-taskNN (T19), and that task's copy command names it.

After task 15, 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.

The owner's decisions (2026-09-28)

  1. Sync starts when Honcho is turned on. The ledger's first line records when sync began, and a turn from before it is skipped: turning on [honcho] never sends earlier conversations. Task 15. The first report of a session that ran before then decides a skip for each of its earlier turns; bxctl memory sync counts them.
  2. Conclusions go to <id>_memory, the Boxmaker session's own Honcho session, holding the agent's and the owner's peers, made on first use whether or not a turn of the session is synced. An undo's conclusions carry the undone edit's session. Tasks 12, 13.
  3. No retraction. A note cannot be undone (notes are append-only), so nothing is retracted, and the row went from spec section 4. Action::Retract stays in the ledger's schema, unused; the worker leaves one waiting. Tasks 12, 13.
  4. Sends are at least once. The worker records sent after Honcho answers: a crash between the two, or an owner removing a sent line, sends the item again (Honcho cannot delete a message). Tasks 07, 11.

Changes to the outline

  • The runbook entry broker-state-unavailable moved from task 05, which did not add it, to task 09, which adds the pointer (the rule: the entry comes with the task that writes its pointer).
  • Task 12 decides only; task 13 sends. Task 15 was added for the owner's decision 1 and for the matching of conclusions by peer found while writing task 13.

Known issue

gatewayd's gateway_sock test brokerd_closing_first_ends_it_as_no_longer_pending fails now and then on a loaded machine (2 of 10 runs without any M5c-1 change, at a load average near 10, on talos): after Ended arrives, the handler can still hold the answer's receiver for a moment. It is a timing race in the test, not in memoryd. Tasks 13 to 15 tell the implementer: if it is the one test that fails, run the gate once more. To be fixed after M5c-1.

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.)