Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
217 lines
12 KiB
Markdown
217 lines
12 KiB
Markdown
# M5c-2 task 08: `recall`'s Honcho part, every source at once with its own limit
|
|
|
|
**Branch:** `m5c2`. Check it: `git branch --show-current` prints `m5c2` and `git status --short`
|
|
is empty; otherwise stop. Never switch branch.
|
|
**Commit subject:** `memoryd: recall's Honcho part, every source at once with its own limit`
|
|
**Log row:** the Task cell is `M5c2/08-memoryd-honcho-search`.
|
|
|
|
## Goal
|
|
|
|
With `[honcho]`, `recall` searches other agents' Honcho workspaces and the fleet's beside the
|
|
local index (M5c spec, section 8). Tasks 05 to 07 gave the readers, the items and the local
|
|
side. This task writes the Honcho part itself, as a new module
|
|
`crates/memoryd/src/honcho/search.rs`, and the answer made from both parts. Task 09 wires it into
|
|
`memoryd`'s `recall`, outside the state lock; until then nothing calls it.
|
|
|
|
- **`search(readers, owner, args, wait, log) -> Searched`**:
|
|
- **A query holding a secret asks nothing.** If `decide::has_secret(&args.query)` (M5c-1's
|
|
known-prefix secret test, with its masked and deleted forms), no source is asked:
|
|
`Searched::Secret`.
|
|
- **Every source at once.** Each source of `readers.recall_sources()` (the `[[honcho.read]]`
|
|
workspaces in the order written, then the fleet's; never the agent's own) is asked on a thread
|
|
of its own.
|
|
- **A source's calls**, one after another on its thread: `search_workspace` with `limit = k`;
|
|
then, for another agent's workspace only, `query_conclusions` with `top_k = k` for that
|
|
agent's peer, then for the owner's peer (observer and observed the same peer, as `memoryd`
|
|
writes conclusions). The fleet's is the one search. A source has answered only when all its
|
|
calls have; its kept items are task 06's `items`. A read agent's three calls share the
|
|
source's one limit, so a slow rift makes the whole source unavailable, not only its last
|
|
call.
|
|
- **Each source has `wait`** (5 s in `memoryd`, `SOURCE_WAIT`; the tests give less), counted
|
|
from when the Honcho part starts, not added up. A source that failed, or has not answered by
|
|
then, is unavailable. A late thread is left to finish on its own, holding no lock; what it
|
|
finds is dropped.
|
|
- **Logging**: each unavailable source is one line, `memoryd: recall: {name} unavailable:
|
|
{error}`, where the error is `HonchoError`'s kind and status (`Honcho is unreachable (server,
|
|
HTTP 503)`) or `no answer in {wait} ms`. Never the query, never any text. A Honcho that is
|
|
down gives one line per source per `recall`, and the model calls `recall` a few times a turn
|
|
at most.
|
|
- The answer, `Searched::Asked { found, unavailable }`: each source's kept items in
|
|
`recall_sources()` order (empty for one that did not answer), and the unavailable sources'
|
|
names, in the same order.
|
|
- **`answer(local, query, k, searched) -> Recalled`**: the local items (task 07's
|
|
`recall::Local`), then Honcho's, interleaved up to `k` in all and numbered after the local
|
|
ones (task 06's `interleave` and `render`); `(no matches for "…")` when there is neither; then
|
|
the `unavailable` lines, or, for `Secret`, the line `honcho: not asked; the query looks like it
|
|
holds a secret`. The label is the stricter of the local part's and the shown Honcho items'
|
|
(task 06's `label`): the higher class, and untrusted when either is.
|
|
- **The fake Honcho** of the tests (`crates/memoryd/tests/support/honcho.rs`, a given file) gets a
|
|
new version: it answers a workspace search from fixture 30 and a conclusion query from fixture
|
|
32 (about `kyle`) or 33 (about anyone else), can answer as a workspace that does not exist
|
|
(`Mode::Missing`: fixtures 34, 35 and 36), and can take a mode for one workspace
|
|
(`set_workspace`). Everything it did before is unchanged.
|
|
|
|
## Every place that changes
|
|
|
|
This is the complete list.
|
|
|
|
| Place | What happens there |
|
|
|---|---|
|
|
| `crates/memoryd/src/honcho/search.rs` | new: a skeleton you copy, then fill 2 bodies |
|
|
| `crates/memoryd/src/honcho/mod.rs` | `pub mod search;` (edit H1) |
|
|
| `crates/memoryd/tests/honcho_search.rs` | new, given |
|
|
| `crates/memoryd/tests/support/honcho.rs` | new version, given |
|
|
| everything else | **no change**: not `serve.rs` (task 09), not `recall.rs`, no `Cargo.toml` |
|
|
|
|
## Files
|
|
|
|
- Copy, from `docs/plans/M5c2/files/` (the same path under it): the new test
|
|
`crates/memoryd/tests/honcho_search.rs`; a **new version** of
|
|
`crates/memoryd/tests/support/honcho.rs` (it replaces the file in the tree): yours is the given
|
|
`honcho.rs-task08`, since the given `honcho.rs` is task 10's; and the skeleton
|
|
`crates/memoryd/src/honcho/search.rs`.
|
|
- Modify: `crates/memoryd/src/honcho/mod.rs`, `docs/implementer-log.md`.
|
|
- Do not touch: any other file, and in the skeleton anything but its two `todo!()` bodies.
|
|
|
|
## Facts you need
|
|
|
|
- **The skeleton** is written except two bodies: `source` (one source's calls) and `answer`.
|
|
`conclusions` (one conclusion query), `search` (the threads, the limit, the log) and the types
|
|
are written; read them before you fill the bodies. `search` calls `source` on each thread.
|
|
- **`roncho`'s calls and types** (this table is complete; `roncho`'s source, in the Cargo
|
|
registry, is outside the repository and off limits):
|
|
|
|
| Item | What it does |
|
|
|---|---|
|
|
| `Client::search_workspace(&self, &MessageSearch) -> Result<Vec<Message>, roncho::blocking::Error>` | `POST /v3/workspaces/{w}/search` |
|
|
| `MessageSearch { query: String, filters, limit: Option<u32>, scope }`, `MessageSearch::new(query)` | all fields public: `MessageSearch { limit: Some(k), ..MessageSearch::new(query) }` sets the limit |
|
|
| `Client::query_conclusions`, `ConclusionQuery` | used in `conclusions`, written |
|
|
| `roncho::Message`, `roncho::Conclusion` | what `items` (task 06) takes |
|
|
|
|
- **From earlier tasks**: `classify(&roncho::blocking::Error) -> HonchoError` (M5c-1, in
|
|
`client.rs`); `Reader::search() -> &Client`, `Reader::source`, `Reader::label` (task 05);
|
|
`items`, `interleave`, `render`, `label`, `unavailable` (task 06, `recalled.rs`); `Local {
|
|
items, class, untrusted }`, `Recalled { text, class, untrusted }` (task 07, `recall.rs`).
|
|
- **A name clash** in `answer`: `Searched::Asked`'s field `unavailable` has the name of task 06's
|
|
function `unavailable`. Bind the field under another name (`unavailable: missing`), as the
|
|
comment says, or the function is hidden inside that arm.
|
|
- **The given test** `honcho_search.rs` (12 tests), against the fake Honcho:
|
|
- the limit and the secret line;
|
|
- every source searched with its calls, their paths and bodies (limit, `top_k`, the peers);
|
|
- the agent's own workspaces never searched;
|
|
- each source's hits in the answer with their tags, numbered after a local item;
|
|
- a fleet hit making the answer untrusted, and a trusted read workspace's hit not (T58);
|
|
- a hanging source: one `unavailable` line after the limit (1.5 s), while the others answer;
|
|
- every source hanging, answered after one limit, not one per source;
|
|
- a failing source (503, 422): `unavailable`, logged by kind and status only;
|
|
- a missing workspace: no hits, not unavailable;
|
|
- a query holding a secret: no request at all, and the secret line;
|
|
- nothing found: `(no matches for "zebra")`, then any `unavailable` line;
|
|
- the local hits filling `k`, and the two labels joined.
|
|
|
|
A key in it is built from pieces; the waits have limits (the whole file takes about 3 s).
|
|
- The calls the bodies use: `MessageSearch::new`, `Client::search_workspace`,
|
|
`Result::map_err`, `classify`, `conclusions` (written), `items`; `usize::try_from`,
|
|
`interleave`, `unavailable`, `label`, `render`, `DataClass::max`, `slice::join`.
|
|
|
|
## The edits, exactly
|
|
|
|
### `crates/memoryd/src/honcho/mod.rs`
|
|
|
|
**H1.** Add one line. The line
|
|
|
|
```rust
|
|
pub mod report;
|
|
```
|
|
|
|
becomes
|
|
|
|
```rust
|
|
pub mod report;
|
|
pub mod search;
|
|
```
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Copy the given tests.** From the repository root, two commands:
|
|
|
|
```sh
|
|
cp docs/plans/M5c2/files/crates/memoryd/tests/honcho_search.rs crates/memoryd/tests/
|
|
cp docs/plans/M5c2/files/crates/memoryd/tests/support/honcho.rs-task08 \
|
|
crates/memoryd/tests/support/honcho.rs
|
|
```
|
|
|
|
Then `git status --short` lists `crates/memoryd/tests/support/honcho.rs` as modified and
|
|
`honcho_search.rs` as new.
|
|
- [ ] **2. See them fail.** `cargo build --tests --keep-going -p memoryd`, with a shell timeout of
|
|
600000 ms: one test target, `honcho_search`, does not compile: `E0432`, ``unresolved import
|
|
`memoryd::honcho::search` ``. Every other target compiles.
|
|
- [ ] **3. The skeleton and the edit.** Copy the skeleton, then make edit H1:
|
|
|
|
```sh
|
|
cp docs/plans/M5c2/files/crates/memoryd/src/honcho/search.rs crates/memoryd/src/honcho/
|
|
```
|
|
|
|
`cargo check -p memoryd`: no error, and 12 warnings, all from the unfilled bodies: unused
|
|
imports (`MessageSearch`, `Source`, and `interleave`, `items`, `label`, `render`,
|
|
`unavailable`), unused variables (`reader`, `owner`, `query`, `k`, `local`, `searched`), and
|
|
the function `conclusions`, never used.
|
|
- [ ] **4. Fill the bodies**, one at a time, from their comments, with `cargo check -p memoryd`
|
|
after each: `source`, then `answer`. Delete each instruction comment and keep the doc comment
|
|
above the function. After the last, `cargo check -p memoryd` prints no warning.
|
|
- [ ] **5. The tests.** With a shell timeout of 600000 ms, every test passes: 13 in
|
|
`honcho_client`, 3 in `honcho_keys` (and 1 ignored, as before), 8 in `honcho_read`, 12 in
|
|
`honcho_search`, 12 in `honcho_worker`:
|
|
|
|
```sh
|
|
cargo test -p memoryd --test honcho_search --test honcho_read --test honcho_client \
|
|
--test honcho_keys --test honcho_worker
|
|
```
|
|
- [ ] **6. Check.** `git diff --stat` lists exactly `crates/memoryd/src/honcho/mod.rs` and
|
|
`crates/memoryd/tests/support/honcho.rs`, and `git status --short` shows `search.rs` and
|
|
`honcho_search.rs` as new.
|
|
- [ ] **7. Run the gate.** `cargo fmt --all`, then `make gate`, with a shell timeout of 600000 ms.
|
|
Expected last line: `gate: ok`. The test count is the passed total of `cargo test --workspace`
|
|
plus one, for the ignored `end_to_end` test that the gate runs on its own: **1430** after this
|
|
task, 1418 before it; the 12 new ones are the tests of `honcho_search.rs`. To count, add up
|
|
the `N passed` of every `test result:` line the gate's `cargo test --workspace` printed, 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 is
|
|
`M5c2/08-memoryd-honcho-search`. Stage with
|
|
`git add crates/memoryd docs/implementer-log.md Cargo.lock`, then commit as `AGENTS.md` says.
|
|
`Cargo.lock` does not change in this task; the `git add` is harmless.
|
|
|
|
## Done when
|
|
|
|
- `make gate` prints `gate: ok` with 1430 tests.
|
|
- `git diff HEAD~1 --stat` lists only `mod.rs`, `search.rs`, `honcho_search.rs`,
|
|
`support/honcho.rs` and `docs/implementer-log.md`.
|
|
|
|
## Stop and report if
|
|
|
|
- The branch check at the top fails.
|
|
- The "before" line of H1 is not in the file exactly once.
|
|
- Step 2 or 3 shows a different result: other targets failing, other errors, or warnings about
|
|
anything but the unfilled bodies.
|
|
- `cargo check` reports 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, the
|
|
fake 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.rs`, `read.rs`,
|
|
`recalled.rs`, a new dependency. It does not; stop and say why you think so.
|
|
- A test outside the given files fails 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 1430.
|
|
- 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 (19 tried, all
|
|
caught): the secret check skipped; the search without its limit, or a conclusion query without
|
|
`top_k` or with another observer; the conclusions about the agent's peer or the owner's left
|
|
out, or asked of the fleet; a failed or a late source not reported unavailable; no limit on the
|
|
wait; the sources asked one after another; the local items left out, or Honcho's numbered from
|
|
1 or interleaved as if there were none; no `(no matches …)` line; the `unavailable` lines left
|
|
out; either part's label ignored; the secret line's words.
|