Skip to content

Transcript: stop rendering an answered approval twice, and name what it was for - #46

Merged
duolahypercho merged 1 commit into
mainfrom
transcript-approval-rendering
Aug 18, 2026
Merged

Transcript: stop rendering an answered approval twice, and name what it was for#46
duolahypercho merged 1 commit into
mainfrom
transcript-approval-rendering

Conversation

@duolahypercho

Copy link
Copy Markdown
Owner

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 duplicate, and the card that kept asking

The fix is in the state machine, not the CSS. settled now 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 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 — "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_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 the card shows it and the row reads Approved file_write · plumber-site/index.html.

argumentsWorthShowing gates the JSON block. It was printing a literal null for 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, and null ?? nowMs made its age permanently zero. Those cards never aged out at all. Tick now stamps them on first sight.

Around it

One CALICODE eyebrow 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; 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.

Tests changed, deliberately

Four assertions pinned copy this PR rewrites, and are updated in the same commit:

  • the always-allow scope names the tool rather than "it" — ambiguous once the row also names a path
  • the deny row carries its path
  • the settled sibling asserts why it settled, plus a new assertion that the clicked card left no second card behind
  • two lapsed cards assert the new title instead of the phrase no longer answerable, which the title now carries

Plus 8 new tests covering the visibility filter, the eviction-clock fix, and both display helpers.

Verification

tsc -b clean · 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, with pageerror/console wired and zero errors. The scroll button was re-shot cropped at deviceScaleFactor: 4 in both themes.

Built off main rather than assets-library-scoping, which has its own open PR (#45). Core is untouched.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GDe8EbQXQxLdcpttjFfPWU

…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
duolahypercho force-pushed the transcript-approval-rendering branch from 9969c50 to ac81c3d Compare August 18, 2026 01:30
@duolahypercho
duolahypercho merged commit 6471735 into main Aug 18, 2026
4 checks passed
@duolahypercho
duolahypercho deleted the transcript-approval-rendering branch August 18, 2026 01:38
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