Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
12 KiB
M5c-2 task 06: Honcho's results as recall items
Branch: m5c2. Check it: git branch --show-current prints m5c2 and git status --short
is empty; otherwise stop. Never switch branch.
Commit subject: memoryd: Honcho's results as recall items, kept, labelled and interleaved
Log row: the Task cell is M5c2/06-memoryd-honcho-recalled.
Goal
From task 08 on, recall searches other agents' Honcho workspaces and the fleet's beside the
local index (M5c spec, section 8). This task writes the pure part: what recall makes of
Honcho's results. A new module, crates/memoryd/src/honcho/recalled.rs, turns one source's
results into items, interleaves the sources' items after the local hits, and labels the answer.
It does no I/O; nothing calls it until task 08, and recall uses it from task 09.
- The inputs, per source: its
Label(task 05); the messages Honcho's workspace search returned (roncho::Message); and for another agent's workspace, the conclusions of two queries, about that agent's peer and then about the owner's (roncho::Conclusion). The fleet has no conclusions: both lists are empty. - Relevance. Honcho's search has no relevance cutoff: a query that matches nothing still gets
the nearest messages (recorded from rift, fixture 31). So a result is kept only when it shares
a whole word with the query (spec 8, amended), as the local index's full-text match requires.
Words are runs of alphanumeric characters (
char::is_alphanumeric), lowercased;teadoes not matchsteamorteatime. - Duplicates. Within one source, a result whose cleaned text equals an earlier kept one's is dropped. Fixture 30 holds the same message twice.
- Each item: a header in the local format,
[n] {tag} · {from} · {class}, then its text.tagis the source's (memorywhen the owner trusts it, elsememory-tainted),classits class, written as the local index writes one (index::class_name).fromis{name}: {peer} said, {when}for a message and{name}: a conclusion about {peer}, {when}for a conclusion.{name}is the source's name (the agent's, orfleet), so a fleet hit readsfleet: boxmaker2 said, …, naming the peer that wrote it.{peer}is the message'speer_idor the conclusion'sobserved_id, and{when}itscreated_atas a local hit shows a time (2026-09-28 23:19 UTC,recall::when).- The text is
neutralised, every control character (line breaks among them) and Unicode line or paragraph separator becomes a space, and it is cut to its first 600 characters (recall::SNIPPET), as a local snippet is cut: no mark is added. - The text goes on the line after the header, indented by two spaces. So every line that
starts with
[is a real header: a text shaped like a header ([1] memory · …, or a line break and then one) can never begin a line (tip T37). Local items keep their own format.
- Order within a source: messages, then the conclusions about the agent's peer, then those about the owner's, each in Honcho's order.
- Interleaving to
k. The local index's hits come first, as today. Honcho's items fill what is left ofk: each source's first, inrecall_sources()order, then each source's second, and so on. If the local hits alone fillk, no Honcho item is shown: this is the spec's "local index first". Honcho's items are numbered after the local ones. - The label of the answer's Honcho part: the highest class of the items shown,
privateat the least, and untrusted when any shown item is. An item dropped as irrelevant, as a duplicate, or becausekwas full labels nothing (T58). {name}: unavailable: one line per source that did not answer, after the items. Task 08 decides which did not.
Every place that changes
This is the complete list.
| Place | What happens there |
|---|---|
crates/memoryd/src/honcho/recalled.rs |
new: a skeleton you copy, then fill 3 bodies |
crates/memoryd/src/honcho/mod.rs |
pub mod recalled; (edit H1) |
crates/memoryd/src/recall.rs |
when becomes pub(crate), for recalled.rs (edit R1) |
crates/memoryd/tests/honcho_recalled.rs |
new, given |
| everything else | no change: not serve.rs, not recall's own function, no Cargo.toml |
Files
- Copy, from
docs/plans/M5c2/files/(the same path under it): the new testcrates/memoryd/tests/honcho_recalled.rs, and the skeletoncrates/memoryd/src/honcho/recalled.rs. - Modify:
crates/memoryd/src/honcho/mod.rs,crates/memoryd/src/recall.rs,docs/implementer-log.md. - Do not touch: any other file, and in the skeleton anything but its three
todo!()bodies.
Facts you need
-
The skeleton is written except three bodies:
items,interleaveandlabel. Their comments say what to write. Everything else in it (Item,Item::render,words,relevant,clean,message_from,conclusion_from,render,unavailable) is written; read it before you fill the bodies. -
Why
wordsis new. The local index'srecall::termssplits a query the same way but returns the FTS5 expression SQLite reads (quoted terms joined byOR), not the words, and does not lowercase them: SQLite's tokenizer folds case itself.wordsgives the words, lowercased, so that both sides of a comparison are. -
roncho's types, as the bodies use them (this table and the next are complete;roncho's source, in the Cargo registry, is outside the repository and off limits):pub struct Message { pub id: String, pub content: String, pub peer_id: String, pub session_id: String, pub created_at: String, /* more */ } pub struct Conclusion { pub id: String, pub content: String, pub observer_id: String, pub observed_id: String, pub created_at: String, /* more */ }Both are
roncho::Messageandroncho::Conclusion, deserialised from Honcho's JSON; the given test reads them from the recorded responses incrates/memoryd/tests/fixtures/honcho/. -
Label(task 05,crate::honcho::read::Label):name: String,trust,class: DataClass;tag() -> &'static stranduntrusted() -> bool. -
DataClassis orderedPublic < Private < Secret;DataClass::max(a, b)is the higher. -
The calls the bodies use:
Item What it does words(&str) -> Vec<String>,relevant(&[String], &str) -> bool,clean(&str) -> Stringin the skeleton message_from(&Label, &Message) -> String,conclusion_from(&Label, &Conclusion) -> Stringin the skeleton slice::iter,Iterator::map,Iterator::chain,Iterator::any,Vec::pushitemsVec::into_iter,std::vec::IntoIter<Item>,Iterator::next,usize::saturating_subinterleaveIterator::fold(DataClass::Private, DataClass::max),Iterator::anylabel -
The given test
honcho_recalled.rs(11 tests): words and whole-word relevance; fixture 31 keeps nothing; fixture 30 keeps its two different messages once each, with their exact items and a rendered one; the order messages, agent's conclusions, owner's (fixtures 30, 33, 32); a duplicate across a message and a conclusion; a fleet hit, tainted and naming its writer; neutralising, one line, the 600-character cap; forged headers kept inside their items (T37); interleaving round-robin after the local hits,kfull, and numbering; the label from shown items only, with a taintedsecretitem pastkand then within it (T58); theunavailablelines. A chat-template tag in it is built from pieces, and no assertion prints one.
The edits, exactly
Nothing else in these files changes. Each quoted "before" block occurs exactly once in its file.
crates/memoryd/src/honcho/mod.rs
H1. Add one line. The line
pub mod read;
becomes
pub mod read;
pub mod recalled;
crates/memoryd/src/recall.rs
R1. The lines
/// `2026-09-20 14:02 UTC` from `2026-09-20T14:02:00.000Z` or `2026-09-20T14:02Z`.
fn when(time: &str) -> String {
become
/// `2026-09-20 14:02 UTC` from `2026-09-20T14:02:00.000Z` or `2026-09-20T14:02Z`.
pub(crate) fn when(time: &str) -> String {
Steps
-
1. Copy the given test. From the repository root, one command:
cp docs/plans/M5c2/files/crates/memoryd/tests/honcho_recalled.rs crates/memoryd/tests/ -
2. See it fail.
cargo build --tests --keep-going -p memoryd, with a shell timeout of 600000 ms: one test target,honcho_recalled, does not compile:E0432,unresolved import `memoryd::honcho::recalled`. Every other target compiles. -
3. The skeleton and the edits. Copy the skeleton, then make edits H1 and R1:
cp docs/plans/M5c2/files/crates/memoryd/src/honcho/recalled.rs crates/memoryd/src/honcho/cargo check -p memoryd: no error, and 11 warnings, all from the unfilled bodies: unused variableslabel,query,messages,agent,owner,local,k,sourcesanditems, and the functionsmessage_fromandconclusion_from, never used. -
4. Fill the bodies, one at a time, from their comments, with
cargo check -p memorydafter each:items, theninterleave, thenlabel. Delete each instruction comment and keep the doc comment above the function. After the last,cargo check -p memorydprints no warning. -
5. The tests. With a shell timeout of 600000 ms, every test passes: 11 in
honcho_recalled, and 6 inrecall, as before:cargo test -p memoryd --test honcho_recalled --test recall -
6. Check.
git diff --statlists exactlycrates/memoryd/src/honcho/mod.rsandcrates/memoryd/src/recall.rs(1 line each), andgit status --shortshowsrecalled.rsandhoncho_recalled.rsas new. -
7. Run the gate.
cargo fmt --all, thenmake gate, with a shell timeout of 600000 ms. Expected last line:gate: ok. The test count is the passed total ofcargo test --workspaceplus one, for the ignoredend_to_endtest that the gate runs on its own: 1414 after this task, 1403 before it; the 11 new ones are the tests ofhoncho_recalled.rs. To count, add up theN passedof everytest result:line the gate'scargo test --workspaceprinted, and add 1. Never run the gate again only to count. -
8. Log and commit. Add your row to
docs/implementer-log.md; its Task cell isM5c2/06-memoryd-honcho-recalled. Stage withgit add crates/memoryd docs/implementer-log.md Cargo.lock, then commit asAGENTS.mdsays.Cargo.lockdoes not change in this task; thegit addis harmless.
Done when
make gateprintsgate: okwith 1414 tests.git diff HEAD~1 --statlists onlymod.rs,recall.rs,recalled.rs,honcho_recalled.rsanddocs/implementer-log.md.
Stop and report if
- The branch check at the top fails.
- A "before" block is not in its file exactly as shown, or is there more than once.
- Step 2 or 3 shows a different result: other targets failing, other errors, or warnings about anything but the unfilled bodies.
cargo checkreports an error after a body, and its comment does not tell you how to fix it. Quote the error, and stop.- After step 4 a test fails although each body follows its comment. Do not change a test or the written parts of the skeleton: stop, and give the failing tests' names and their messages.
- The fix seems to need a change anywhere else:
serve.rs,recall's own function,read.rs, a new dependency. It does not; stop and say why you think so. - A test outside
honcho_recalled.rsfails in the gate: check once whether it fails without your change (git stash -u, run it,git stash pop), then stop and report both results. - The gate passes with a count other than 1414.
- A comment in the skeleton does not fit its signature. Quote it and stop.
What the given tests pin
Each of these changes to the finished code makes at least one given test fail (22 tried, all
caught): words not lowercased; a substring match instead of whole words; no relevance check; no
duplicate check; no neutralise; a line break kept; the text not indented; the cap moved by
one; the owner's conclusions before the agent's, or the conclusions left out; a source's tag,
trust or class ignored; the wrong peer in from; interleaving that ignores the local hits,
takes one source's items all at once, or stops at the first empty source; numbering from 1
instead of after the local hits; the label's floor public, or untrusted only when every item
is; the unavailable line's wording.