fix(tools): make code result cards authoritative

This commit is contained in:
Tianyi Cui
2026-07-21 18:38:22 +08:00
parent f9b938a4a8
commit 449e3cf298
19 changed files with 1526 additions and 374 deletions

View File

@@ -6,7 +6,7 @@ English | [中文](2026-07-20-code-mode-result-card-completeness.zh.md)
## Problem
The outer `run_code` tool persisted complete rendered content, but its editor presenter ignored that content and rebuilt the card body from a logs-only `presentationMeta` projection. A result-only run appeared correct because an empty presenter body let ACP and TUI fall back to `tool/result.content`. Once the program emitted a log, the presenter supplied non-empty content, that fallback stopped, and the returned value disappeared from the completed card. Failure text and a spill policy's final head/tail preview were vulnerable to the same split ownership.
The outer `run_code` tool persisted complete rendered content, but its editor presenter ignored that content and rebuilt the card body from a logs-only `presentationMeta` projection. A result-only run appeared correct because an empty presenter body let ACP and TUI fall back to `tool/result.content`. Once the program emitted a log, the presenter supplied non-empty content, that fallback stopped, and the returned value disappeared from the completed card. A spill policy's final head/tail preview was vulnerable to the same split ownership whenever captured logs made the stale projection non-empty.
Nested Code calls never owned cards, so producing metadata for the outer call solely to reconstruct one incomplete card also obscured the intended one-card boundary.
@@ -14,13 +14,13 @@ Nested Code calls never owned cards, so producing metadata for the outer call so
The `run_code` output renderer remains the single owner of model-facing outer content. It renders captured logs followed by the return value, the explicit no-output marker, or the failure content produced by the canonical tool pipeline. Post-execute policy and spill may replace that content before it is persisted.
`run_code.presentResult` now forwards the final `result.content` into one generic result card. It deliberately omits the title so the pending card retains the program text. The existing logs metadata remains in `tool/result` for transcript compatibility, but the presenter no longer treats it as a second content source: `tool/result.content` is the durable, replayable, post-policy projection.
`run_code.presentResult` now forwards the final `result.content` into one generic result card. It deliberately omits the title so the pending card retains the program text. The redundant logs-only `presentationMeta` projection is removed: `tool/result.content` is the durable, replayable, post-policy projection and the card's only result-content source.
Nested dispatch remains unchanged. Calls marked by `exec.parent` emit bounded `tool/code-dispatch` diagnostics but no `tool/call` or `tool/result` surface cards, so one outer `run_code` invocation still produces exactly one card.
## Testing
Presenter unit coverage pins logs-only, result-only, logs-plus-result, no-output, failure, and spilled-result content. Every case proves stale metadata cannot replace the final content.
Presenter unit coverage pins logs-only, result-only, logs-plus-result, no-output, and spilled-result content. A separate integration-shaped unit drives a real runtime failure through the canonical registry result before presenting it. The successful cases prove stale metadata cannot replace final content; the failure case guards complete forwarding without claiming it reproduced the original metadata-triggered defect.
The keyless ACP and TUI Code Mode snapshots execute one outer program that performs two nested bash calls, logs `captured output`, and returns `CODE_ONE+CODE_TWO`. Both surfaces show one completed outer card containing both lines and no nested cards.
@@ -34,4 +34,4 @@ The keyless ACP and TUI Code Mode snapshots execute one outer program that perfo
## Consequences
ACP and TUI now display the same complete content the model receives and replay persists, including post-policy spill previews. The change adds or removes no event fields and requires no session-format bump. Existing and future replay records remain valid because the presenter ignores logs metadata when choosing card content and reads their durable rendered content.
ACP and TUI now display the same complete content the model receives and replay persists, including post-policy spill previews. New `run_code` results no longer carry the optional logs metadata, but this requires no session-format bump: existing records remain valid because the presenter ignores that field and reads their durable rendered content.