Files
boxmaker/docs/plans/M5c2/README.md
T
2026-09-28 21:07:18 -07:00

9.3 KiB

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 is tools/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)

  1. Queries from any session. recall searches Honcho, and ask_memory asks it, from a session of any class, secret included: 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) and docs/egress.md. A query holding a known-prefix secret is refused before anything is sent.
  2. Two workspaces per ask_memory call, both asked at once, so both answer inside brokerd's wait; the model asks again for more.
  3. 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_memory always asks at minimal, since a low chat 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_memory appears in sessions started after deployment, and no epoch code is needed.
  • brokerd waits 30 s for any memory tool (memory_port.rs, MEMORY_WAIT), and loopd 120 s for any tool ([broker] timeout_ms). Neither has a per-tool wait; ask_memory needs 170 s and 180 s (spec 9).
  • memoryd holds its one state lock for the whole of a recall. Honcho searches (5 s) and chats (150 s) must run outside it, or every memory call waits behind them.
  • M5c-1's Clients are 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 (for ask_memory).
  • roncho's blocking client has what is needed: search_workspace, query_conclusions (it requires the observer and the observed) and workspace_chat.

Global constraints

  • Roles depend on bxcore only; memoryd also on roncho. No unsafe, no async runtime.
  • Every roncho client is built with .without_config_file(); keys come from bxcore::secrets and are never logged; errors are logged by kind and status only.
  • Text from Honcho is neutralised on arrival and capped like a local recall snippet (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, recall is exactly M5b's and ask_memory answers 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 of roncho'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; each source's label; recall_sources and resolve; own, untrusted and fleet reserved as read agents' names 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: local recall items in task 06's form, one indented line each, through one shared snippet and item; recall::local gives the items, their count and their label apart (T37) memoryd
08 memoryd: recall's Honcho part (honcho/search.rs): every source of recall_sources at once, each on its own thread with a limit of 5 s from the start; a read agent's workspace is a search and two conclusion queries; <source>: unavailable; a query holding a secret asks nothing; the answer from the local items and Honcho's, with the stricter label memoryd
09 memoryd: recall with [honcho] runs the local part under the state lock and the Honcho part outside it; the readers taken at start; without [honcho], recall as before memoryd
10 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
11 memoryd, making room: reindex and admin move unchanged from serve.rs to serve/admin.rs, with tests for four of their behaviours no test pinned memoryd
12 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 12, 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
05 1403 24, 24
06 1414 22, 22
07 1418 15, 15
08 1430 19, 19
09 1434 8, 8
10 1444 18, 18
11 1448 8, 8

A flaky test, fixed before the run: bxctl's chat_approvals test json_prints_only_json_and_never_asks_brokerd failed once in 5 loaded runs of the whole workspace while task 06 was checked: its helper wrote to the standard input of a bxctl chat that does not read it and may already have exited, and unwrapped the broken pipe. The helper now accepts a broken pipe on that write (master, after task 06); 0 failures in 100 loaded runs.

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