# 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, Vec)`. - **`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 **1518** (1512 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 1518 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 1518; 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.