Files
boxmaker/docs/plans/M5d2/02-bxcore-tool-summary.md
T
kyleandGrok 4.6 3726ae585f docs: M5d-1 and M5d-2 implementation plans for Ornith
Eight closed tasks for epochs and compaction, then ten for the scheduler,
ToolSummary, and tiers. Given tests first. Gate 1456 → 1507 → 1572.

Co-Authored-By: Grok 4.6 <noreply@x.ai>
2026-10-02 21:54:37 -07:00

166 lines
6.0 KiB
Markdown

# M5d-2 task 02: `LogRecord::ToolSummary`
**Branch:** `m5d2`. Check it: `git branch --show-current` prints `m5d2` and `git status --short`
is empty; otherwise stop. Never switch branch.
**Commit subject:** `bxcore: ToolSummary log record; messages ignore it`
**Log row:** the Task cell is `M5d2/02-bxcore-tool-summary`.
## Goal
A tool that opts in gets a second record, for the index only (spec 4.3):
```text
LogRecord::ToolSummary { time, call, tool_call_id, content, class, untrusted }
```
JSON type `tool_summary`. `messages()` / `messages_with_carry` ignore it, so the epoch's prefix
does not change. `support/turn.rs` `types` names it (T19: this task ships its own copy).
Nothing summarises yet. That is task 08.
## Every place that changes
| Place | What happens there |
|---|---|
| `crates/bxcore/src/log.rs` | the variant, after `ToolResult` (edit L1) |
| `crates/loopd/src/baseline.rs` | ignore arm (edit B1) |
| `crates/loopd/tests/support/turn.rs` | replaced, given |
| `crates/bxcore/tests/tool_summary.rs` | new, given |
| `crates/loopd/tests/tool_summary_messages.rs` | new, given |
| everything else | **no change**: not `index.rs` (task 09), not `turn.rs` |
`memoryd/src/index.rs` `index_record` already has `_ => {}`. It compiles. Summaries are skipped
until task 09.
## Files
- Copy, from `docs/plans/M5d2/files/` (the same path under it): the new tests
`crates/bxcore/tests/tool_summary.rs` and
`crates/loopd/tests/tool_summary_messages.rs`, and the replacement
`crates/loopd/tests/support/turn.rs`.
- Modify: `crates/bxcore/src/log.rs`, `crates/loopd/src/baseline.rs`,
`docs/implementer-log.md`.
- Do not touch: any other file.
## Facts you need
- **Field order is the wire format.** `time`, `call`, `tool_call_id`, `content`, `class`,
`untrusted`. No `truncated`: a summary is already capped.
- **Unknown fields are errors**, as on every `LogRecord` variant (`deny_unknown_fields` is on
the enum).
- **`types` is exhaustive.** Adding the variant without the replacement `support/turn.rs` fails
to compile `turn` / `limits` tests. Copy the given file over the existing one. The only
intended change is the `ToolSummary => "tool_summary"` arm. `Setup::turn` still returns
`(Result<TurnOutcome, TurnError>, Vec<TurnEvent>)`.
- **`messages` names every variant.** The new arm is empty, like `Usage` / `CacheLoss` /
`EpochEnd`.
- The items the edits use. This table is complete.
| Item | What it does |
|---|---|
| `CallId` | already imported in `log.rs` |
| `DataClass` | already imported in `log.rs` |
## The edits, exactly
Each quoted "before" block occurs exactly once after task 01.
### `crates/bxcore/src/log.rs`
**L1.** After the `ToolResult` variant (the lines)
```rust
truncated: bool,
},
Usage {
```
become
```rust
truncated: bool,
},
ToolSummary {
time: Timestamp,
call: CallId,
tool_call_id: String,
content: String,
class: DataClass,
untrusted: bool,
},
Usage {
```
Keep the fields in that order.
### `crates/loopd/src/baseline.rs`
**B1.** After the `ToolResult` arm (the lines)
```rust
} => out.push(ChatMessage::Tool {
tool_call_id: tool_call_id.clone(),
content: content.clone(),
}),
LogRecord::Usage { .. } => {}
```
become
```rust
} => out.push(ChatMessage::Tool {
tool_call_id: tool_call_id.clone(),
content: content.clone(),
}),
LogRecord::ToolSummary { .. } => {}
LogRecord::Usage { .. } => {}
```
## Steps
- [ ] **1. Copy the given files.** From the repository root:
```sh
cp docs/plans/M5d2/files/crates/bxcore/tests/tool_summary.rs crates/bxcore/tests/
cp docs/plans/M5d2/files/crates/loopd/tests/tool_summary_messages.rs crates/loopd/tests/
cp docs/plans/M5d2/files/crates/loopd/tests/support/turn.rs crates/loopd/tests/support/turn.rs
```
- [ ] **2. See them fail.** `cargo build --tests --keep-going -p bxcore -p loopd`, timeout
600000 ms: `tool_summary` and `tool_summary_messages` do not compile (`E0599` no variant
`ToolSummary`). `support/turn.rs` does not compile for the same reason until L1. After L1
and before B1, `baseline.rs` fails (`non-exhaustive patterns`). Do L1 then B1 in step 3;
this step is the first `cargo build` against the given tests only, which is E0599.
- [ ] **3. The edits.** Make L1 then B1. `cargo check -p bxcore -p loopd` prints no error and
no warning.
- [ ] **4. The tests.** `cargo test -p bxcore --test tool_summary --test records` and
`cargo test -p loopd --test tool_summary_messages --test turn --test baseline --test
session_slot`: every test passes, 3 in `tool_summary`, 3 in `tool_summary_messages`.
- [ ] **5. Check.** `git diff --stat` lists `crates/bxcore/src/log.rs`,
`crates/loopd/src/baseline.rs`, `crates/loopd/tests/support/turn.rs`.
- [ ] **6. Run the gate.** `cargo fmt --all`, then `make gate`, timeout 600000 ms. Last line
`gate: ok`. Test count **1517** (1511 before, plus 6).
- [ ] **7. Log and commit.** Task cell `M5d2/02-bxcore-tool-summary`. Stage with
`git add crates/bxcore crates/loopd docs/implementer-log.md Cargo.lock`, then commit as
`AGENTS.md` says.
## Done when
- `make gate` prints `gate: ok` with 1517 tests.
- `git diff HEAD~1 --stat` lists only `crates/bxcore/src/log.rs`,
`crates/bxcore/tests/tool_summary.rs`, `crates/loopd/src/baseline.rs`,
`crates/loopd/tests/tool_summary_messages.rs`, `crates/loopd/tests/support/turn.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 1517; you replay a summary as a `User` or
`Tool` message; you add `truncated`; you change `Setup::turn`'s return type.
## What the given tests pin
JSON type is `tool_summary`; unknown fields are refused; field order is the struct order;
`messages()` with and without a summary are equal; a summary is not a tool message; inserting a
summary between a result and a later user does not break prefix extension.