From 0730183f35c0009041272ca6aef8be786d10848a Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 25 Apr 2026 06:06:32 +0900 Subject: [PATCH] =?UTF-8?q?roadmap:=20#202=20filed=20=E2=80=94=20sanitize?= =?UTF-8?q?=5Ftool=5Fmessage=5Fpairing=20silent=20drop,=20no=20tool=5Fmess?= =?UTF-8?q?age=5Fdropped=20event=20(Jobdori=20cycle=20#135)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ROADMAP.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/ROADMAP.md b/ROADMAP.md index 6dbcbfe..a8cf771 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -12342,3 +12342,41 @@ grep -rE 'kind:.*=' src/ | grep -v test | wc -l **Status:** Open. No code changed. Filed 2026-04-25 05:02 KST. Branch: feat/jobdori-168c-emission-routing. HEAD: b780c80. 🪨 + +--- + +## Pinpoint #202 — `sanitize_tool_message_pairing` silent drop: orphaned tool messages removed with no event, no log, no diagnostic visibility (Jobdori, cycle #135) + +**Observed:** In `rust/crates/api/src/providers/openai_compat.rs`, `sanitize_tool_message_pairing()` (called at line ~868) silently drops any `role:"tool"` message whose `tool_call_id` has no matching preceding `assistant` turn with a `tool_calls[].id`. The drop is intentional (prevents 400s from OpenAI-compat backends) but produces zero structured signal: no event, no log entry, no field in the request envelope indicating N messages were removed. + +**Gap:** +- A session with compaction, editing, or resume can arrive at the request boundary with orphaned tool messages +- These are quietly dropped; the provider receives a request with fewer messages than the session history claims +- No `tool_message_dropped` event or `history_sanitized` field is emitted +- `claw doctor`, the event log, and downstream observers cannot distinguish "all tool messages sent" from "N tool messages silently omitted" +- Debugging mismatch between session history and what the provider actually received requires source-level tracing + +**Repro:** +``` +# Craft a session where a tool result has no matching assistant tool_calls entry +# (e.g. resume after compaction that dropped the assistant turn but kept the result) +# sanitize_tool_message_pairing() drops the orphan silently +# Event log shows no drop event +# Provider receives history minus the orphaned message; caller sees no indication +``` + +**Expected:** +- On any drop, emit structured event: `{ "kind": "tool_message_dropped", "tool_call_id": "...", "count": N, "reason": "no_paired_assistant_turn" }` +- Optionally: include `{ "history_sanitized": { "dropped_tool_messages": N } }` in request metadata +- `claw doctor` can surface sessions where tool message sanitization occurred +- Clawability: agents replaying or resuming sessions can detect the gap and re-issue the tool call or warn + +**Fix sketch:** +1. Change `sanitize_tool_message_pairing` to return `(Vec, Vec)` or emit events via a callback/channel +2. At call site (line ~868), if any drops occurred, emit `tool_message_dropped` event(s) before sending request +3. Add `dropped_tool_messages` count to request diagnostic envelope if non-zero +4. Add to classifier's recognized event taxonomy + +**Status:** Open. No code changed. Filed 2026-04-25 06:09 KST. Branch: feat/jobdori-168c-emission-routing. HEAD: 5e0228d. + +🪨