Skip to content

Fix double transcript rows on permission and operator approvals - #382

Merged
TheGreatAxios merged 2 commits into
mainfrom
cl-5644-every-approval-double-prints-its-outcome-in-the-transcript
Aug 8, 2026
Merged

Fix double transcript rows on permission and operator approvals#382
TheGreatAxios merged 2 commits into
mainfrom
cl-5644-every-approval-double-prints-its-outcome-in-the-transcript

Conversation

@TheGreatAxios

@TheGreatAxios TheGreatAxios commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Accepting a permission overlay wrote two transcript rows: the generic overlay-accept echo and gate-wire's recordDecision, whose own doc comment says it exists specifically instead of that echo. Neither openPermissionsOverlay nor openOperatorOverlay ever passed echoChoice: false, so both fired.
  • Esc/deny on either overlay previously wrote zero rows; auto-deny (timeout/abort) also wrote zero. All three now write exactly one, through recordDecision.
  • The operator gate had no recordDecision equivalent at all. Suppressing its generic echo would have made it silent rather than de-duplicated, so a new recordOperatorDecision helper now records accept, typed answer, and cancel. A separate, previously-unconditional echo on the free-text-answer path (answered: ${text} in handleOverlayAnswerKey) 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: false is passed from gate-wire.ts's two overlay opens rather than hardcoded inside openPermissionsOverlay/openOperatorOverlay themselves. Those two openers are also called directly (with no recorder) from the standalone demo.ts fixture; hardcoding the suppression there would have silently zeroed the demo's transcript rows too. echoChoice defaults to the existing on-by-default behavior (shell.ts), so any caller that doesn't opt out still gets a row.

Operator path

onOperator had no equivalent to recordDecision, so suppressing its generic echo would have dropped the record entirely instead of de-duplicating it. Added recordOperatorDecision and 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

  • Failing test committed first, confirmed red against the pre-fix code
  • 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 change
  • bun run typecheck — pre-existing unrelated failures in src/tui/tool-formatter.ts, src/util/list-dir.ts, and vendor/intx-inference (arktype narrowing), none in files touched by this branch
  • bun run build — passes

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.
@linear-code

linear-code Bot commented Aug 8, 2026

Copy link
Copy Markdown

CL-5644

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
TheGreatAxios force-pushed the cl-5644-every-approval-double-prints-its-outcome-in-the-transcript branch from 41e23c7 to 96aa968 Compare August 8, 2026 16:25
@TheGreatAxios
TheGreatAxios merged commit 036e6f5 into main Aug 8, 2026
3 checks passed
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