session_slot and the scheduler tests held a temporary JSON Value across statements (E0716). Cron now pins DOM and DOW as AND, which the given tests had not. Gate counts from task 04 on move up by one. Co-Authored-By: Grok 4.6 <noreply@x.ai>
142 lines
5.5 KiB
Markdown
142 lines
5.5 KiB
Markdown
# M5d-2 task 09: index `ToolSummary` as kind `summary`
|
|
|
|
**Branch:** `m5d2`. Check it: `git branch --show-current` prints `m5d2` and `git status --short`
|
|
is empty; otherwise stop. Never switch branch.
|
|
**Commit subject:** `memoryd: index ToolSummary as kind summary`
|
|
**Log row:** the Task cell is `M5d2/09-memoryd-summary-index`.
|
|
|
|
## Goal
|
|
|
|
`memoryd` indexes `LogRecord::ToolSummary` content as kind `summary`, with that record's
|
|
`class` and `untrusted` (spec 4.3, P26). `recall` finds it and tags `memory` or
|
|
`memory-tainted` as for any other session hit. Raw `ToolResult` bodies stay unindexed.
|
|
|
|
`index_record` today has `_ => {}`. It compiles. This task adds the arm.
|
|
|
|
## Every place that changes
|
|
|
|
| Place | What happens there |
|
|
|---|---|
|
|
| `crates/memoryd/src/index.rs` | `ToolSummary` arm (edit I1); module doc |
|
|
| `crates/memoryd/tests/tool_summary_index.rs` | new, given |
|
|
| everything else | **no change**: not `recall.rs` headers (role `"summary"` uses the existing "the assistant said" branch) |
|
|
|
|
## Files
|
|
|
|
- Copy, from `docs/plans/M5d2/files/` (the same path under it): the new test
|
|
`crates/memoryd/tests/tool_summary_index.rs`.
|
|
- Modify: `crates/memoryd/src/index.rs`, `docs/implementer-log.md`.
|
|
- Do not touch: any other file. Do not edit `tests/support/logs.rs`.
|
|
|
|
## Facts you need
|
|
|
|
- **Kind `summary`.** Set `kind: "summary".to_string()` on the `Source`, not the log's
|
|
`"session"`. Role `"summary"`. Time from the record. `class` / `untrusted` from the
|
|
`ToolSummary`, not from `base` (the rolling session label). Skip when
|
|
`summary.class > self.max_class`, even if `base.class` is still `Private`. Skip when
|
|
`content.trim()` is empty.
|
|
- **The existing `if base.class > self.max_class` stays.** A secret `ToolResult` raises the
|
|
rolling label before the next record, so a following secret summary is also skipped that
|
|
way. Still check the summary's own class.
|
|
- **Do not add `ToolSummary` to the rolling-label update** in `index_log` (that loop still
|
|
looks at `ToolResult` only). The result already carried the same labels.
|
|
- **Recall headers.** `kind` `"summary"` is not `moved` / `dropped` / `noted`, so the tag is
|
|
`memory` or `memory-tainted`. Role `"summary"` is not `owner` or `call`, so the line says
|
|
`the assistant said`. The given tests look for `memory · session mm-a` and
|
|
`memory-tainted`.
|
|
- The items the edits use. This table is complete.
|
|
|
|
| Item | What it does |
|
|
|---|---|
|
|
| `self.add` | FTS row plus `sources` |
|
|
| `Source` | kind / role / class / untrusted |
|
|
|
|
## The edits, exactly
|
|
|
|
Each quoted "before" block occurs exactly once.
|
|
|
|
### `crates/memoryd/src/index.rs`
|
|
|
|
**I1.** After the `Assistant` arm, before `_ => {}`. The lines
|
|
|
|
```rust
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
become
|
|
|
|
```rust
|
|
}
|
|
LogRecord::ToolSummary {
|
|
time,
|
|
content,
|
|
class,
|
|
untrusted,
|
|
..
|
|
} if !content.trim().is_empty() && *class <= self.max_class => {
|
|
let src = Source {
|
|
kind: "summary".to_string(),
|
|
time: time.to_rfc3339(),
|
|
role: "summary".to_string(),
|
|
class: *class,
|
|
untrusted: *untrusted,
|
|
..base.clone()
|
|
};
|
|
self.add(content, &src)?;
|
|
}
|
|
_ => {}
|
|
}
|
|
Ok(())
|
|
}
|
|
```
|
|
|
|
`DataClass` already implements `Ord` (`Public < Private < Secret`). If `<=` does not
|
|
compile, stop.
|
|
|
|
Optionally update the module doc "never a tool's result" to say summaries of opted-in
|
|
results are indexed. A comment that does not fit its code: stop.
|
|
|
|
## Steps
|
|
|
|
- [ ] **1. Copy the given test.**
|
|
|
|
```sh
|
|
cp docs/plans/M5d2/files/crates/memoryd/tests/tool_summary_index.rs crates/memoryd/tests/
|
|
```
|
|
|
|
- [ ] **2. See it fail.** `cargo test -p memoryd --test tool_summary_index`, timeout 600000
|
|
ms: the tests compile (`ToolSummary` exists) and `a_tool_summary_is_found_as_kind_summary`
|
|
fails: `(no matches for "tea")` or the unique phrase is missing. `the_raw_tool_result_is_still_not_indexed`
|
|
may pass (results were already unindexed).
|
|
- [ ] **3. The edit.** I1. `cargo check -p memoryd` prints no error and no warning.
|
|
- [ ] **4. The tests.** `cargo test -p memoryd --test tool_summary_index --test recall`: every
|
|
test passes, 5 in `tool_summary_index`.
|
|
- [ ] **5. Check.** `git diff --stat` lists exactly `crates/memoryd/src/index.rs`.
|
|
- [ ] **6. Run the gate.** `cargo fmt --all`, then `make gate`, timeout 600000 ms. Last line
|
|
`gate: ok`. Test count **1568** (1563 before, plus 5).
|
|
- [ ] **7. Log and commit.** Task cell `M5d2/09-memoryd-summary-index`. Stage with
|
|
`git add crates/memoryd docs/implementer-log.md Cargo.lock`, then commit as `AGENTS.md`
|
|
says.
|
|
|
|
## Done when
|
|
|
|
- `make gate` prints `gate: ok` with 1568 tests.
|
|
- `git diff HEAD~1 --stat` lists only `crates/memoryd/src/index.rs`,
|
|
`crates/memoryd/tests/tool_summary_index.rs` and the log.
|
|
|
|
## Stop and report if
|
|
|
|
- The branch check fails; a "before" block is not found exactly once; step 2 differs; `cargo
|
|
check` warns; a test fails; the gate count is not 1568; you index `ToolResult` content; you
|
|
leave `kind` as `"session"`; a secret summary is searchable on a private index.
|
|
|
|
## What the given tests pin
|
|
|
|
A summary's content is found and tagged `memory · session …`; the raw tool body is not; an
|
|
untrusted summary taints the recall (`memory-tainted`); a trusted summary does not taint; a
|
|
secret summary is skipped when `max_class` is `Private`.
|