fix(tui): a finished tool card in a running turn opens its raw detail

CW-7087 (secondary finding of the reproduction). Inside a turn that is
still running, Alt+V or a click on a tool card that had already finished
opened a rendered "Message" view (no tool id, input or retained output)
instead of "Raw detail". The same card opened raw detail once the turn
ended.

Cause: tool_detail_record_for_cell matched an active-cell detail only
through tool_cells, and tool_routing removes a tool's tool_cells entry when
it completes. ActiveCell keeps the tool-to-entry binding until the group
flushes (flush_active_cell already relies on it), so the lookup now also
matches history.len() + active.entry_index_for_tool(tool_id).

Verified with a real TUI repro (tmux 90x30, --yolo, local mock model, no
provider traffic): run a phase of quick commands, then a phase of
`sleep 4; echo newer-NN` calls, and press Alt+V 14 s in, while newer-01 has
finished inside the running turn. Same script, two builds of this tree:
baseline opens "Message ... run done · sleep 4; echo newer-01"; fixed opens
"Raw detail — bash" with Tool ID, input {"command": "sleep 4; echo
newer-01"} and output newer-01. Artifacts:
CW/artifacts/cw7087-20261018/shots-ab/.
scripts/dev-test.sh tui detail activity tool_routing active_cell: 234 run,
234 passed. CI-form clippy -p codewhale-tui --all-targets --all-features
-D warnings clean; cargo fmt --check clean.

Not changed here: with no selection, Alt+V targets the first visible card
with details, so newer cards scrolling in move the target (the primary
CW-7087 finding). That rule is f7264f7d72's deliberate choice; changing it
is recorded on CW-7087 for a decision.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NBKbhx19BRbZtvWK8rVKjz
This commit is contained in:
CodeWhale BotandClaude Opus 5.5 committed 2026-10-18 02:26:34 -07:00
1 parent 91c3ac2083
commit 1865cb2770
1 file changed
+10 -3
+10 -3
View File
@@ -5586,9 +5586,16 @@ impl App {
if let Some(detail) = self.tool_details_by_cell.get(&index) {
return Some(detail);
}
self.active_tool_details
.values()
.find(|detail| self.tool_cells.get(&detail.tool_id).copied() == Some(index))
let active_entry = |tool_id: &str| {
self.active_cell
.as_ref()
.and_then(|active| active.entry_index_for_tool(tool_id))
.map(|entry| self.history.len() + entry)
};
self.active_tool_details.values().find(|detail| {
self.tool_cells.get(&detail.tool_id).copied() == Some(index)
|| active_entry(&detail.tool_id) == Some(index)
})
}
/// Whether a virtual transcript cell can open a meaningful `v` detail