Transcript: stop rendering an answered approval twice, and name what it was for - #46
Merged
Merged
Conversation
…it was for
A finished turn read as a wall of unanswered prompts. Every approval was on
screen in two shapes at once — a compact "Approved file_write" row, and a
full-size "Approve file_write?" card still asking the question, with its
outcome demoted to grey text underneath. Both described the same click.
The fix is in the state machine, not the CSS. `settled` now records who
produced the answer. A decision this window made is already written to the
transcript as a permanent row, so its card drops out of `visibleApprovals` —
out of the view, not out of the map, so a redelivered `Arrived` still finds it
finished and cannot resurrect an answered prompt. An always-allow clearance is
the one settle that stays up, because nobody clicked that card and it has no
row of its own; it now says exactly that instead of claiming the user approved
it.
Finished cards also stopped asking. Titles carry their own tense: "Approved
file_write", "Denied file_write", "Approval for browser_downloads lapsed".
And a card now says what it is about. Two `file_write` prompts were the same
four words twice, and the rows they left behind were indistinguishable;
`approvalTarget` pulls the path, command or url out of the arguments so both
the card and the row name it. `argumentsWorthShowing` gates the JSON block,
which was printing a literal "null" for argument-less tools and repeating a
single-key `{ path }` directly under the target line that already said it.
Found on the way: an entry that finished with nothing in flight got
`finishedAtMs: null`, and `null ?? nowMs` made its age permanently zero — those
cards never aged out at all. `Tick` stamps them on first sight.
Around it, the transcript's own reading: one CALICODE eyebrow per speaker turn
rather than one per block, shield icons for grant and refusal rows in place of
the filled square that was the heaviest mark on screen, one stroke weight
across the icon set, `formatDuration` on the busy clock so "331s" and "5m 31s"
stop appearing together, a base gap tight enough that tool steps read as one
run, and a halo on the scroll-to-latest button so it floats over content
instead of landing on a card's border.
Four assertions changed with the copy they pinned, deliberately: the
always-allow scope now names the tool rather than "it", the deny row carries
its path, the settled sibling asserts why it settled rather than a bare
"Approved.", and two lapsed cards assert the new title instead of the phrase
"no longer answerable" that the title now carries.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GDe8EbQXQxLdcpttjFfPWU
duolahypercho
force-pushed
the
transcript-approval-rendering
branch
from
August 18, 2026 01:30
9969c50 to
ac81c3d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A finished turn read as a wall of unanswered prompts. Every approval was on screen in two shapes at once — a compact
Approved file_writerow, and a full-sizeApprove file_write?card still asking the question, with its outcome demoted to grey text underneath. Both described the same click.The duplicate, and the card that kept asking
The fix is in the state machine, not the CSS.
settlednow records who produced the answer (SettledVia). A decision this window made is already written to the transcript as a permanent row, so its card drops out ofvisibleApprovals— out of the view, not out of the map, so a redeliveredArrivedstill finds it finished and cannot resurrect an answered prompt.An always-allow clearance is the one settle that stays up, because nobody clicked that card and it has no row of its own. It now says exactly that — "Covered by the permission you just granted — this one was never asked" — instead of a bare "Approved." that would credit the user with a decision they were never shown.
Finished cards also stopped asking. Titles carry their own tense:
Approved file_write,Denied file_write,Approval for browser_downloads lapsed.Naming what a request is about
Two
file_writeprompts were the same four words twice, and the rows they left behind were indistinguishable.approvalTargetpulls the path, command or url out of the arguments, so the card shows it and the row readsApproved file_write · plumber-site/index.html.argumentsWorthShowinggates the JSON block. It was printing a literalnullfor argument-less tools, and — caught only from a screenshot — repeating a single-key{ path }directly beneath the target line that already said it.Found on the way
An entry that finished with nothing in flight got
finishedAtMs: null, andnull ?? nowMsmade its age permanently zero. Those cards never aged out at all.Ticknow stamps them on first sight.Around it
One
CALICODEeyebrow per speaker turn rather than one per block; shield icons for grant/refusal rows in place of the filled square that was the heaviest mark on screen; one stroke weight (~1.7–1.9) across the icon set;formatDurationon the busy clock so331sand5m 31sstop appearing together; a base gap tight enough that tool steps read as one run; and a halo on the scroll-to-latest button so it floats over content instead of landing on a card's border.Tests changed, deliberately
Four assertions pinned copy this PR rewrites, and are updated in the same commit:
"it"— ambiguous once the row also names a pathno longer answerable, which the title now carriesPlus 8 new tests covering the visibility filter, the eviction-clock fix, and both display helpers.
Verification
tsc -bclean · 909/909 unit on this branch · driven headlessly through every approval state (live with args, live without, always-allow clearance, lapsed, post-approve) in both themes at 1440px and 520px, withpageerror/consolewired and zero errors. The scroll button was re-shot cropped atdeviceScaleFactor: 4in both themes.Built off
mainrather thanassets-library-scoping, which has its own open PR (#45). Core is untouched.🤖 Generated with Claude Code
https://claude.ai/code/session_01GDe8EbQXQxLdcpttjFfPWU