Skip to content

Chat transcript fidelity: show what the terminal shows - #317

Merged
xerhab merged 1 commit into
mainfrom
chat-transcript-fidelity
Jul 27, 2026
Merged

Chat transcript fidelity: show what the terminal shows#317
xerhab merged 1 commit into
mainfrom
chat-transcript-fidelity

Conversation

@xerhab

@xerhab xerhab commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Goal

The chat page should be complete enough that reviewing/verifying a session never requires flipping to terminal mode. This PR closes the information gaps found by replaying all 559 stored Claude transcripts on this host through the block pipeline and comparing what the chat renders against what the raw transcript (and the TUI) contains.

What was being lost

Gap Corpus count Before After
Edit calls 2,381 card showed only file_path edit {old,new,replaceAll} → rendered as a −/+ diff
Write calls 434 only file_path content (the file body) on the card
ExitPlanMode 3 input JSON dump cut at 1000 chars plan rendered as prose, open by default (it's the thing being approved)
description args 6,759 dropped desc shown beside the raw argument (Bash/Agent/Monitor/…)
AskUserQuestion 76 raw questions-JSON dump card titled with the question text(s); skill/subject join the salient-arg keys
Compactions every compact_boundary invisible in chat centred marker: "↺ Context compacted (auto) — 123.4k → 5.9k tokens"
PRs opened 1,091 pr-link entries invisible inline linked "↗ Opened PR #N — repo" marker where it landed; consecutive duplicates fold

pr-link entries carry no uuid, so both feeds synthesize a stable id (_entry_id/entryId) — the client's id-keyed merge drops id-less entries.

Eval (iterated before finalizing)

  • No-regression: old vs new _entry_blocks over all 59,225 corpus entries — identical output apart from the intended additions (0 unexplained diffs).
  • Mirror parity: hub-agent.py vs tunnel-agent.js over the 37,553 block-producing entries — 0 new divergences; the 13 that exist are pre-existing on main (JS UTF-16 vs Python codepoint clip length on emoji-bearing entries, truncation-point-only, harmless).
  • Real-browser exercise (verify-skill recipe): booted the hub, rendered pipeline-generated entries through TurmaChat.renderStatic in Chromium over CDP; probed the DOM (diff old/new backgrounds, plan open+prose, desc, question title, marker texts, PR href, dedupe 2→1) and screenshotted light/dark/1180px/390px. One bug found and fixed that unit tests couldn't catch: .tool-plan needed white-space: pre-wrap.

Tests

  • TestEntryBlocks +9 cases (agent, 793 green) · tunnel mirror cases (64 green) · chat.test.js +5 render cases (turma, 615 green).
  • Android decodes the new shapes safely today (UnknownBlock / ignored keys); the render gap is recorded in android/PARITY.md per XERK-30.

Known follow-ups (out of scope)

  • The archive stays text-only, so ended-session views don't get diffs/markers — terminal mode doesn't exist for ended sessions, so the live chat + /history was the target.
  • The other reason to open the terminal — a permission prompt / plan-approval dialog waiting in the TUI with no transcript trace — is a pane-scrape feature, not a transcript-fidelity one; worth its own ticket.

The chat view flattened every tool call to its one salient argument, so
reviewing an agent's actual work (what an Edit changed, what a Write wrote,
the plan behind an ExitPlanMode) still required opening the raw terminal.
Close the gaps found by replaying all 559 stored transcripts through the
block pipeline:

- Edit tool_use blocks carry the change itself (edit {old, new, replaceAll}),
  rendered as a -/+ diff on the action card (2,381 calls in the corpus were
  showing only a file path).
- Write blocks carry the file body (content); ExitPlanMode carries its plan,
  rendered as prose and open by default (it is the thing being approved).
- Any tool's human description arg rides as desc beside the raw argument
  (6,759 calls); AskUserQuestion cards are titled with the question text
  instead of an input-JSON dump.
- system/compact_boundary entries become a centred status marker with the
  trigger and pre/post token counts - a compaction was invisible in chat.
- pr-link entries become an inline linked "Opened PR #N" marker where the
  PR landed; they carry no uuid, so the feeds synthesize a stable id
  (_entry_id/entryId) to survive the client's id-keyed merge, and
  consecutive duplicates fold.

hub-agent.py and tunnel-agent.js stay line-for-line mirrors (verified over
the full corpus: 0 divergences beyond the 13 pre-existing emoji clip-length
ones present on main). Android decodes the new shapes to UnknownBlock /
ignored keys (safe degrade) - gap recorded in android/PARITY.md.

Verified: full agent (793) + tunnel (64) + turma (615) suites green, plus a
real-browser render of pipeline-generated entries via the verify recipe
(diff colors, plan open-by-default, markers, 390px wrap all checked).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant