Repository navigation
Conversation
The permission dialog only unmounts after the server's `permission.replied`
event comes back, so every key held down across that round trip re-POSTed
`/permission/{id}/reply` for the same request. A run in XiaomiMiMo#2565 logged 32
replies for one id and 141 for another; the storm eventually wedged
`setRawMode` with EIO and left Enter, Esc and Ctrl+C dead for the rest of
the session, recoverable only with SIGKILL.
The server ignores a duplicate reply, so the extra POSTs were harmless
individually and invisible in behaviour -- only the storm itself hurt.
Latch the reply per request rather than per prompt mount. Per prompt is not
enough: the `always` confirmation answers and then drops back to the first
stage, so a second Enter lands on a fresh prompt that would answer again
with `once`.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Fixes #2565
Problem
The permission dialog is only torn down once the server's
permission.repliedevent comes back. Until then the prompt keeps handling keys, and every one of them re-POSTed the same reply:The report logged 32 replies for one
requestIDover ~8 minutes, and 141 in another session. The storm eventually wedged@opentui/core'ssetRawModewith EIO, after which Enter, Esc and Ctrl+C were all dead for the rest of the session — onlykill -9recovered it:Cause
PermissionRequestPromptcallssdk.client.permission.reply()directly from three places — theonce/rejectpath, thealwaysconfirmation, and the reject-reason stage — with no guard. The server already drops a duplicate reply, so each extra POST was individually harmless; the storm was the whole problem.Fix
Latch the reply per request:
Per request rather than per prompt mount, because a confirmation answers and then drops back to the first stage:
A per-prompt latch is not sufficient here — the
alwaysstage sends its reply and returns to thepermissionstage, so a second Enter lands on a freshly mounted prompt that would answer again withonce. Holding Enter through a confirmation therefore producedalwaysthenoncefor one request. The per-request latch covers all three call sites at once.This is a behaviour fix only. Which reply wins is unchanged for every single-keystroke interaction, and no dialog, stage or keybinding changes.
Validation
Four regression tests in
test/cli/tui/permission-bash-delete.test.tsx, on the existingmountPermissionharness which records every/permission/*/replyPOST:PermissionPrompt answers a held RETURN onceonce, three Enters in one mountPermissionPrompt answers a held ESCAPE oncereject, two Escapesalways confirmation answers a held RETURN oncealways, two Enters on the confirmation stagereject stage answers a held RETURN oncerejectviaRejectPrompt(harness gained an optionalparentIDso the request routes through the reject-reason stage)Before / after, on the same tree:
Also run:
bun test test/cli/tui— 279 pass, 2 fail. Both failures are insession-list-visibility.test.ts, which scans source text for an absolutefile:///…import and does not match this checkout's path. They fail identically on pristinemain(275 pass, 2 fail), which is the same run plus these 4 tests.bun run typecheck(tsgo --noEmit) — clean.bunx oxlinton both changed files — 0 errors; the 2 warnings inpermission.tsx(lines 56-57) are identical on pristinemain.permission-bash-delete.test.tsxis not prettier-clean onmaineither, so I left the surrounding formatting alone rather than reformatting the file inside this diff; the added tests follow the style already in it.🤖 Prepared with AI assistance.