feat: promote tool-derived diffs to first-class chat cards - #5012
Conversation
UX Review (Fable 5) — 🟡 CONCERNSUX-level review of UX-Verdict: CONCERNS Solid promotion with fail-safe degradation, but the new density controls are ephemeral and override the user's stated collapse preference with no lasting relief. Watch
Suggestions
[UX-REVIEWED] 79fe604 |
GPT 5.6 Review — ✅ no blocking findingsGPT 5.6 completed its review of This comment is updated in place on each push. Review detailsNo findings. False positive or not applicable? A repository writer can comment: |
Design Review (Fable 5) — ✅ PASSDesign-level review of Design-Verdict: PASS Server-side runtime selection with every ambiguity failing toward the hard mandate makes the risky half of this — relaxing a display-critical prompt rule — safely directional. The one residual model-judgment surface (tool-vs-shell on the dashboard) is named in the RFC, its failure is backstopped by the untouched [DESIGN-REVIEWED] 79fe604 |
ceef4a6 to
1b93228
Compare
First Principles Review (Fable 5) — 🟡 CONCERNSPremise-level review of I've verified the key claims against the repo: the First-Principles-Verdict: CONCERNS The header-path regex rewrite in What this change shipsIntent: show file changes as first-class diff cards built from tool data instead of model-restated ```diff prose — an ADDITION.
Watch
Subtractions
[FIRST-PRINCIPLES-REVIEWED] 79fe604 |
1b93228 to
78eba5d
Compare
Disposition — UX Review (lane: ux-review)Finding 1 (over-cap edits lose their only visible diff): ADOPTED, fixed in 78eba5d. Finding 2 (cards exempt from collapseAll with no override): ADOPTED, fixed in 78eba5d. |
78eba5d to
8038325
Compare
Disposition — Design Review (lane: design-review)Finding 1 (channel regression rides on per-turn model judgment): ADOPTED, fixed in 8038325. Implemented your suggestion: the runtime clause is now decided SERVER-SIDE. Finding 2 (over-cap edits lose their primary display): ADOPTED, fixed in 78eba5d (also raised by UX Review). Over-cap edits degrade to a summary chip (filename, −N/+M, expands the details panel) instead of nothing. Finding 3 (temp-screenshots committed at repo top level): NOT ADOPTED. Committed QA media under |
Disposition — First Principles Review (lane: first-principles-review)Watch (RFC ships self-contradicting): ADOPTED, fixed in 8038325. Front matter now Watch (RFC §3.1/§5 contradict the shipped code): ADOPTED, fixed in 8038325. §3.1 rewritten to the actual architecture (presentation renders inside ToolCallLine — one site covers ChatPage, ChatPane, and app-sdk surfaces — plus the summary-chip degradation); the §5 Subtraction (un-export |
8038325 to
76ac00f
Compare
Disposition — Design Review round 2 (lane: design-review)Finding 1 (edits outside the built-in tools lose their trace under the relaxed rule): ADOPTED, fixed in 76ac00f. The dashboard rule now scopes the don't-repeat clause to "the BUILT-IN file-editing tools" and explicitly lists "an MCP tool that writes files" in the must-emit set ( Finding 2 (6 KB Suggestion (compare raw source, not localized display): ADOPTED, fixed in 76ac00f. Resolution extracted to |
76ac00f to
e5c5f31
Compare
Disposition — UX Review round 2 (lane: ux-review)Finding 1 (denied edits look applied): ADOPTED, fixed in e5c5f31. A rejected or auto-denied call never promotes: Finding 2 (collapseAll loses the visible change record): ADOPTED, fixed in e5c5f31, exactly as you suggested. Under "collapse all working steps" every diff row demotes to the one-line −N/+M summary chip (new |
Disposition — First Principles Review round 2 (lane: first-principles-review)Subtraction (inline +/− loop duplicates Subtraction ( Subtraction (one-member Watch (prompt.md still ships the model-judged runtime clause, three spellings to sync): ADOPTED, fixed in e5c5f31. Clause (b) is deleted from both bundled prompts; the paragraph now defers to the runtime-selected critical rules as authoritative and keeps only the tool-vs-shell/MCP distinction (the one thing the model can observe). One authoritative spelling per surface, selected server-side. Watch (AGENTS.md: new markdown file needs explicit instruction): CONFIRMED-AS-INSTRUCTED. The RFC was explicitly requested by the repository owner in the originating session ("写一个 RFC 把方案定下来" — "write an RFC to settle the design"), not convention-inherited. |
e5c5f31 to
820bf32
Compare
Disposition — UX Review round 3 (lane: ux-review)Finding 1 (truncated diffs look complete): ADOPTED, fixed in 820bf32. Finding 2 (edit-kind row without a unified diff leaves no trace): ADOPTED, fixed in 820bf32. A |
Disposition — Design Review round 3 (lane: design-review)Finding (cross-surface sessions outrun the session-start rule selection): ADOPTED, fixed in 820bf32, using your asymmetric suggestion. The per-turn runtime refresh in |
820bf32 to
3bef27a
Compare
Response — GPT 5.6 blocking finding (fixed in 6950ee5)Malformed edit args abort the turn: FIXED. |
6950ee5 to
a05adb6
Compare
a05adb6 to
7284819
Compare
|
Disposition — First Principles CONCERNS (on 6950ee5):
|
|
Fix note for the blocking finding (head 7284819):
|
kiro-cli's edit tools already carry a structured diff on the ACP tool_call event; the backend renders it to a unified-diff string on tool_input, but the dashboard hid it inside the collapsed tool-call details panel while a prompt rule ordered the model to hand-write the same diff as a ```diff block in prose - paying tokens on every edit and depending on format compliance. Make the structured channel the primary display, per docs/request-for-change/rfc-tool-derived-diff-cards.md: - ToolCallLine renders an always-visible DiffBlock card below the pill when the tool kind is "edit" and its input is a unified diff (new toolDiff.ts detection: kind gate first so shell commands with diff-shaped input never promote; 400-line cap so whole-file creates keep the collapsed rendering). - _tool_meta persists the ACP tool kind so historical rows gate identically to live ones; pre-existing rows never promote. - TurnBlock excludes diff-card rows from the "Worked through N steps" collapse, same family as workflow_run / spawn_run / MCP-app rows. - The prompt mandate (prompt.md, prompt-orchestrator.md, _CRITICAL_RULES) becomes runtime-conditional: ```diff blocks are required only for edits made outside file-editing tools or on non-dashboard runtimes (Slack/Discord/Telegram render no tool cards, so message text stays their only display). Verified on an isolated dev gateway: edit-tool turn renders the card (light+dark screenshots under temp-screenshots/tool-diff-cards/), shell turns do not; tsc, vitest (22747 passed; the one failing file passes standalone and pre-dates this change), pytest (60119 passed; 21 failures reproduce on pristine origin/main), flake8, mypy green.
7284819 to
79fe604
Compare
|
Fix note for the blocking finding (head 79fe604):
|
Opus 4.8 Review — ✅ no blocking findingsReviewed Review detailsBased on my analysis of the diff and the candidate list: CANDIDATE 1 ( CANDIDATE 2 (insert hunk off-by-one): The candidate itself concedes the effect is cosmetic gutter numbering and that it could not confirm kiro-cli's CANDIDATE 3 (new RFC markdown file): CANDIDATE 4 ( Step 2 review of the introduced code: the No findings. [OPUS-REVIEWED] 79fe604 Verdict parsed from the review's SHA-scoped output markers for commit False positive or not applicable? A repository writer can comment: |
…v#5012) kiro-cli's edit tools already carry a structured diff on the ACP tool_call event; the backend renders it to a unified-diff string on tool_input, but the dashboard hid it inside the collapsed tool-call details panel while a prompt rule ordered the model to hand-write the same diff as a ```diff block in prose - paying tokens on every edit and depending on format compliance. Make the structured channel the primary display, per docs/request-for-change/rfc-tool-derived-diff-cards.md: - ToolCallLine renders an always-visible DiffBlock card below the pill when the tool kind is "edit" and its input is a unified diff (new toolDiff.ts detection: kind gate first so shell commands with diff-shaped input never promote; 400-line cap so whole-file creates keep the collapsed rendering). - _tool_meta persists the ACP tool kind so historical rows gate identically to live ones; pre-existing rows never promote. - TurnBlock excludes diff-card rows from the "Worked through N steps" collapse, same family as workflow_run / spawn_run / MCP-app rows. - The prompt mandate (prompt.md, prompt-orchestrator.md, _CRITICAL_RULES) becomes runtime-conditional: ```diff blocks are required only for edits made outside file-editing tools or on non-dashboard runtimes (Slack/Discord/Telegram render no tool cards, so message text stays their only display). Verified on an isolated dev gateway: edit-tool turn renders the card (light+dark screenshots under temp-screenshots/tool-diff-cards/), shell turns do not; tsc, vitest (22747 passed; the one failing file passes standalone and pre-dates this change), pytest (60119 passed; 21 failures reproduce on pristine origin/main), flake8, mypy green. Co-authored-by: t <t@t>
Summary
Adopts the structured-diff display model (RFC:
docs/request-for-change/rfc-tool-derived-diff-cards.md, included in this PR): kiro-cli's edit tools already deliver a structured diff on the ACPtool_callevent, and the backend already renders it to a unified-diff string ontool_input— but the dashboard hid it inside the collapsed tool-call details panel while the prompt ordered the model to hand-write the same diff as a ```diff block, on every file change, no exceptions.This PR makes the tool-derived diff the primary display and relaxes the prompt mandate to a runtime-conditional fallback.
Changes
ToolCallLinerenders an always-visibleDiffBlockcard below the pill when the tool kind iseditand its input is a unified diff. Newpages/chat/toolDiff.tsowns detection: kind gate first (a shell command with diff-shaped input never promotes), 400-line cap (whole-file creates keep the collapsed rendering).TurnBlockexcludes diff-card rows from the "Worked through N steps" collapse — same predicate family as workflow_run / spawn_run / MCP-app rows.make_unified_diff's transport cap is raised from 6 000 chars to 64 KiB (sized so the full-card range is never cut; ~11x larger persistedmeta.input/ WS payloads for large edits, within the existing 1 MB tool-field ceiling), with line-boundary truncation and a machine-detectable annotation.derive_edit_diffturns bare-JSON edit args (create / insert / strReplace) into a unified diff server-side, so everytool_inputconsumer sees the same diff._tool_metapersists the ACP toolkindso historical rows gate identically to live ones. Rows persisted before the field never promote (fail-safe).prompt.md,prompt-orchestrator.md, and_CRITICAL_RULESbecomes conditional — ```diff blocks are required only when (a) the change was made outside file-editing tools (sed, scripted bulk edits, git apply), or (b) the runtime is not the dashboard. Slack/Discord/Telegram renderers never seetool_input(verified: `messaging/driver.py` copies four fields onto OutputEvent), so message text remains their only file-change display and the mandate stays for them.Evidence
Isolated dev-gateway session showing an edit turn (fs_write, kind=edit): the card renders and stays visible with the turn collapsed. The shell negative case (kind=execute never promotes, even with diff-shaped input) is pinned by automated tests in toolDiff.test.tsx and TurnBlock.test.tsx.
Tested
toolDiff.test.tsx(13 cases: kind gating, shell false-positive rejection, line cap, historical-meta path, card render),TurnBlock.test.tsxdiff-card visibility x3, backendtest_tool_meta_persists_kind.CliPanelCoverage, passes standalone and pre-dates this change — full-run cross-file pollution), pytest 60119 passed (21 failures reproduce identically on pristineorigin/main— host-environment class), flake8, mypy, docs-lint, brand gate all green. isort failure onvalidation.pyalso reproduces on pristine main.Not in scope
Extending messaging
OutputEventwithtool_inputso channel renderers could show diffs (would let the conditional prompt rule retire entirely) — recorded in the RFC as future work.Behaviors a reader should expect (beyond the headline)