Fix stale mention accept during overlay reopen - #764
TheGreatAxios merged 4 commits into
Conversation
Enter can still splice a completion from a prior overlay frame while a mention lookup is in flight, using a cursor captured before the await. Accept now requires the current generation and a live @token. Enter that fails those checks dismisses the popup so the in-flight lookup cannot reopen it.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Review · Approve
Mention overlay Enter only splices a live @ token on the current generation; stale Enter dismisses so in-flight lookups cannot reopen.
No findings.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Review · Comment
Mention overlay Enter only splices a live @ token on the current generation; stale Enter dismisses so in-flight lookups cannot reopen.
No blocking findings. GitHub forbids self-approve on this PR.
Notes
src/tui/mention-popup.test.ts:384— in-flight dismiss coverage goes throughcloseMentionPopup, not Esc/closeInsetOverlay.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Critic · Comment
Accept is gated on current generation plus a live @ token; dismiss bumps generation and drops accept state. No blocking product defect.
Findings
src/tui/mention-popup.test.ts:384— should-fix coverage: dismiss-during-lookup usescloseMentionPopup, not operator Esc (closeInsetOverlayatsrc/tui/shell.ts:4079).src/tui/mention-popup.test.ts:361— failed-reopen then Enter hits the permissions overlay, so it does not uniquely prove the mention live-gate.src/tui/shell.ts:5084-5112— file-for-later: first lookup does not re-parse@after await before opening the popup; Enter still refuses a stale splice.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Greybeard · Comment
Hold. Splice coordinates belong at accept time; mentionGenerations already owns lookup currency; dismiss including non-live Enter must bump that same token.
acceptOverlaySelection classifying stale Enter as dismiss is the right host special-case (src/tui/shell.ts:4455-4458). Gates only in onAccept would echo a choice and leave an in-flight lookup able to reopen. mentionAcceptIsLive and onAccept are two decisions with close in between, not dual ownership.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Neckbeard · Comment
Hygiene only; not blocking.
src/tui/shell.ts:5155andsrc/tui/shell.ts:5115—mentionAcceptIsLiveandonAcceptrepeat the same three predicates.docs/TUI.md:553— "accept handler" names a WeakMap delete, not the overlay callback.src/tui/mention-popup.test.ts:280—describetitle includes the ticket id.src/tui/mention-popup.test.ts:324— threeawait Promise.resolve()as a flush, copied fromtype().
Dismiss, including Esc, bumps mention generation so an in-flight lookup cannot reopen. Accept requires the current generation and the same live @token the lookup started on. Enter that fails those checks dismisses.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Review · Comment
Mention overlay Enter splices only the live @ token the current lookup armed; stale Enter dismisses so an in-flight lookup cannot reopen.
No blocking findings. GitHub forbids self-approve on this PR.
CI is green on HEAD (f617263e).
TheGreatAxios
left a comment
There was a problem hiding this comment.
Critic · Comment
Dismiss clears accept state and bumps generation. Accept requires current generation plus a live parse of the same @. Enter during an in-flight lookup dismisses instead of splicing.
No blocking product defect.
Findings
src/tui/mention-popup.test.ts:339— should-fix coverage: there is no hangable case for Enter while a no-match re-query is in flight (resolveNext([])after dismiss). Same generation bump as the mid-reopen test; the issue asked for both races as permanent coverage.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Greybeard · Comment
Proceed. Failed mention Enter as dismiss belongs at acceptOverlaySelection; generation owns lookup currency; live parseAtState owns splice coords.
The host gate is scoped to the @ popup claim (kind === "mentions" && mentionPopups.has(shell)). Successful accept deletes that claim before closeInsetOverlay so the close is not a dismiss bump. Do not add a third mention-kind check in the host.
TheGreatAxios
left a comment
There was a problem hiding this comment.
Neckbeard · Comment
Hygiene only; not blocking.
src/tui/shell.ts:5141— splice path hand-rolls the three assignmentseditPromptAtalready does (src/tui/shell.ts:5196).src/tui/shell.ts:5180—mentionAcceptIsLiveis onlyliveMentionAccept(shell) !== nulland has one call site.src/tui/mention-popup.test.ts:339— “mid-reopen” names the old close-then-reopen path; the hot path is in-place refresh.src/tui/mention-popup.test.ts:463— “Esc” test callscloseInsetOverlay, not an Escape key.src/tui/mention-popup.test.ts:358— closed-state asserts.not.toBe("mentions")instead ofoverlayKind === null.
Splice goes through editPromptAt. Tests name the functions they call, assert a closed overlay, and cover Enter during a no-match re-query.
Summary
@token is under the cursor.Verification
bun run checkexits 0 (5912 pass, 0 fail)bun test ./src/tui/mention-popup.test.ts ./src/tui/prompt-features.test.tsexits 0 (39 pass)Fixes CL-6718