Fix double transcript rows on permission and operator approvals - #382
Merged
TheGreatAxios merged 2 commits intoAug 8, 2026
Merged
Conversation
Accepting a permission overlay writes both the generic overlay-accept echo and gate-wire's own recordDecision row, so a screen of approvals reads as twice as many requests as actually happened. Esc/deny on either overlay writes zero rows instead of one, since neither path records anything today.
recordDecision already writes the authoritative transcript row for a permission decision, carrying the full subject and chosen scope, but the generic overlay-accept echo fired alongside it because neither overlay open passed echoChoice: false. The operator overlay had no equivalent recorder at all, so suppressing its echo the same way would have gone silent instead of de-duplicated — it now records its own decision (option picked, typed answer, or cancel) the same way. Esc/deny on both overlays previously wrote nothing; it now records the decision too, so every terminal path leaves exactly one row.
TheGreatAxios
force-pushed
the
cl-5644-every-approval-double-prints-its-outcome-in-the-transcript
branch
from
August 8, 2026 16:25
41e23c7 to
96aa968
Compare
This was referenced Aug 8, 2026
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.
Summary
recordDecision, whose own doc comment says it exists specifically instead of that echo. NeitheropenPermissionsOverlaynoropenOperatorOverlayever passedechoChoice: false, so both fired.recordDecision.recordDecisionequivalent at all. Suppressing its generic echo would have made it silent rather than de-duplicated, so a newrecordOperatorDecisionhelper now records accept, typed answer, and cancel. A separate, previously-unconditional echo on the free-text-answer path (answered: ${text}inhandleOverlayAnswerKey) was also gated on the same flag — it wasn't covered by the accept-path fix and would have kept double-printing typed answers.echoChoice: falseis passed from gate-wire.ts's two overlay opens rather than hardcoded insideopenPermissionsOverlay/openOperatorOverlaythemselves. Those two openers are also called directly (with no recorder) from the standalonedemo.tsfixture; hardcoding the suppression there would have silently zeroed the demo's transcript rows too.echoChoicedefaults to the existing on-by-default behavior (shell.ts), so any caller that doesn't opt out still gets a row.Operator path
onOperatorhad no equivalent torecordDecision, so suppressing its generic echo would have dropped the record entirely instead of de-duplicating it. AddedrecordOperatorDecisionand wired it into all three operator terminal paths (accept, typed answer, cancel) so the operator gate keeps the same one-row-per-decision guarantee as the permission gate.Regression note (CL-4868 / CL-5607)
CL-4868 ("no duplicate command dumps") shipped this exact guarantee pre-cutover and was marked Done. The existing gate-wire test (
"gate content reaches the transcript only after the operator decides") only asserted on content (toContain), never on row count, so it kept passing at 2 rows and never caught the regression when the OpenTUI shell landed. That's the gap this PR closes with count-delta assertions instead.Test plan
bun run test— 4088/4089 pass; the one failure (lsp-availability.test.ts) is pre-existing, environment-dependent (checks for a local language server binary), and unrelated to this changebun run typecheck— pre-existing unrelated failures insrc/tui/tool-formatter.ts,src/util/list-dir.ts, andvendor/intx-inference(arktype narrowing), none in files touched by this branchbun run build— passes