Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
M5c-2 implementation plan: Honcho, the read 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: the agent reads what the fleet knows. recall searches the other agents' trusted
workspaces and the fleet workspace alongside the local index, each source on its own limit, and
a new core tool, ask_memory, asks Honcho's dialectic about one or two workspaces. What comes back
is labelled, and taints the session, as a local recall hit does.
Architecture: bxcore gains ask_memory's arguments; brokerd runs it as a built-in memory
tool with its own wait; loopd declares it as a core tool with its own wait; memoryd gains
read-only Honcho clients, turns Honcho's messages and conclusions into recall items, runs its
Honcho sources and chats outside its state lock, and answers ask_memory.
Tech stack: Rust 1.98.1, edition 2024; roncho 1.0.0 (already a dependency of memoryd,
features = ["tls"]); no new crate.
Spec: docs/specs/2026-09-27-m5c-honcho.md, sections 3, 8, 9, 10 and 13, as amended on
2026-09-28 (8e56261).
The owner's decisions (2026-09-28)
- Queries from any session.
recallsearches Honcho, andask_memoryasks it, from a session of any class,secretincluded: Honcho stores no query, and on rift a query reaches only the owner's hosts. It is an assumption about rift's configuration, written into the spec (section 8) anddocs/egress.md. A query holding a known-prefix secret is refused before anything is sent. - Two workspaces per
ask_memorycall, both asked at once, so both answer insidebrokerd's wait; the model asks again for more. - New recordings from rift for the fake server (task 00), in a throwaway workspace deleted
afterwards, as in M5c-1. They changed two things (spec,
891f413):ask_memoryalways asks atminimal, since alowchat on rift returned raw tool-call markup; and a Honcho hit is kept only when it shares a term with the query, since search has no relevance cutoff.
What the code already has, and what it lacks
- A session keeps the tools array it was created with (
loopd's baseline is saved at the start and sent on every request):ask_memoryappears in sessions started after deployment, and no epoch code is needed. brokerdwaits 30 s for any memory tool (memory_port.rs,MEMORY_WAIT), andloopd120 s for any tool ([broker] timeout_ms). Neither has a per-tool wait;ask_memoryneeds 170 s and 180 s (spec 9).memorydholds its one state lock for the whole of arecall. Honcho searches (5 s) and chats (150 s) must run outside it, or everymemorycall waits behind them.- M5c-1's
Clientsare the sync worker's, for writes, and move into its thread. Reading needs its own clients: one per[[honcho.read]]workspace, the fleet, and the agent's own two (forask_memory). roncho's blocking client has what is needed:search_workspace,query_conclusions(it requires the observer and the observed) andworkspace_chat.
Global constraints
- Roles depend on
bxcoreonly;memorydalso onroncho. Nounsafe, no async runtime. - Every
ronchoclient is built with.without_config_file(); keys come frombxcore::secretsand are never logged; errors are logged by kind and status only. - Text from Honcho is neutralised on arrival and capped like a local
recallsnippet (600 characters); a result's labels raise the session as a local hit's do. - No Honcho call runs under
memoryd's state lock. - Without a
[honcho]section,recallis exactly M5b's andask_memoryanswers that Honcho is not configured. - No file the implementer reads holds a chat-template tag whole (T33), nor a fake key whole.
- Every path a given test creates is removed by a guard (T44); every process a test starts has a deadline.
- Each task gives its baseline and its gate count with what the count includes (T50, T53); the
branch line is a check, never
git switch(T51); import changes are single added lines (T54); whitespace-checked literals are marked on every edit (T55); the task's table ofroncho's API is complete, and the Cargo registry is off limits (T56).
Tasks (outline, for the owner's review)
| # | Delivers | Crates |
|---|---|---|
| 00 | (done by the design model, 718e66d) recordings from rift: workspace search with a limit and with no match, conclusion queries for two peers, and what a missing workspace answers |
memoryd fixtures |
| 01 | Wire: AskMemoryArgs { question, workspaces }, its name and its checks (question at most 2,000 characters; one or two workspace names, no repeats, each 1 to 512 characters as memoryd's name rule) |
bxcore |
| 02 | brokerd: ask_memory as a built-in memory tool (name, parsing, the grant builtin-ask-memory, owner grants naming it refused, routing to memoryd); its answer's labels raise the session |
brokerd |
| 03 | brokerd: a per-tool wait on memoryd, 170 s for ask_memory (ASK_MEMORY_WAIT) and 30 s for the others, chosen by MemoryPort::wait_for |
brokerd |
| 04 | loopd: ask_memory's schema as a core tool in a new registry; a per-tool wait on brokerd, 180 s for ask_memory and [broker] timeout_ms for the others; the system prompt's line on memory tools |
loopd |
| 05 | memoryd: read clients, one per [[honcho.read]] workspace, the fleet and the agent's own two, with a 5 s limit for searches and 150 s for chat |
memoryd |
| 06 | memoryd, pure: Honcho messages and conclusions as recall items (tag, source name, class, neutralised, capped), kept only when they share a term with the query, interleaved with the local hits to k, the local index first |
memoryd |
| 07 | memoryd: recall's Honcho sources, each on its own thread with its own limit, after the local search and outside the lock; <source>: unavailable; a query holding a secret sends nothing |
memoryd |
| 08 | memoryd: ask_memory: workspace names checked against [honcho], both chats at once, each answer tagged and capped, labels, <workspace>: unavailable; a question holding a secret sends nothing |
memoryd |
| 09 | memoryd serves ask_memory beside recall and memory, outside the lock; bxctl's usage and the runbook where a new message needs a pointer |
memoryd, bxctl |
After task 09, the design model runs the M5c-2 device check (spec 13) on straylight, rift and the
owner's Mattermost: a second memoryd under its own name writes a fleet note that recall finds,
tagged memory-tainted, turning the session untrusted; ask_memory answers from the agent's own
trusted workspace within its limit; with rift down, recall answers from the local index with
unavailable lines. The baseline is measured again against the 3,000-token budget
(make verify-device).
Counts, as tasks are written
The gate count is the passed total of cargo test --workspace plus one, for the ignored
end_to_end test the gate runs on its own. Before task 01 it is 1364 (talos, rustc 1.98.1).
| Task | Gate count after | Mutations tried, caught |
|---|---|---|
| 01 | 1372 | 23, 23 |
| 02 | 1381 | 17, 17 |
| 03 | 1384 | 10, 10 |
| 04 | 1394 | 19, 19 |
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 whole workspace stressed under load
(T52); the reference moved to straylight as a bundle before the run. Task files name their log
row (T47). For every class or trust rule, each path that reads from Honcho gets a test at the
forbidden label (T58).
Running it
git switch -c m5c2 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/M5c2 01