Skip to content

Fix stale mention accept during overlay reopen - #764

Merged
TheGreatAxios merged 4 commits into
mainfrom
cl-6718-gate-mention-accept-on-live-parse-and-clear-stale-accept
Sep 4, 2026
Merged

TheGreatAxios merged 4 commits into
mainfrom
cl-6718-gate-mention-accept-on-live-parse-and-clear-stale-accept

Conversation

@TheGreatAxios

Copy link
Copy Markdown
Collaborator

Summary

  • Mention overlay Enter splices only when the mention generation is current and a live @ token is under the cursor.
  • Enter that fails those checks dismisses the popup and bumps generation so an in-flight lookup cannot reopen or splice a stale completion.
  • Dismiss clears the mention accept handler.

Verification

  • bun run check exits 0 (5912 pass, 0 fail)
  • bun test ./src/tui/mention-popup.test.ts ./src/tui/prompt-features.test.ts exits 0 (39 pass)

Fixes CL-6718

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

linear-code Bot commented Sep 3, 2026

Copy link
Copy Markdown

CL-6718

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 through closeMentionPopup, not Esc/closeInsetOverlay.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 uses closeMentionPopup, not operator Esc (closeInsetOverlay at src/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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neckbeard · Comment

Hygiene only; not blocking.

  • src/tui/shell.ts:5155 and src/tui/shell.ts:5115mentionAcceptIsLive and onAccept repeat the same three predicates.
  • docs/TUI.md:553 — "accept handler" names a WeakMap delete, not the overlay callback.
  • src/tui/mention-popup.test.ts:280describe title includes the ticket id.
  • src/tui/mention-popup.test.ts:324 — three await Promise.resolve() as a flush, copied from type().

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

Copy link
Copy Markdown
Collaborator Author

Addressed review findings: Esc/closeInsetOverlay during an in-flight lookup now bumps generation; accept requires the same live @token the lookup started on; tests cover Esc, vanished token, and a different @token.

Accept splices only the @token the overlay opened on. Cursor on a
different live @token dismisses instead of completing into the wrong token.

@TheGreatAxios TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TheGreatAxios left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neckbeard · Comment

Hygiene only; not blocking.

  • src/tui/shell.ts:5141 — splice path hand-rolls the three assignments editPromptAt already does (src/tui/shell.ts:5196).
  • src/tui/shell.ts:5180mentionAcceptIsLive is only liveMentionAccept(shell) !== null and 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 calls closeInsetOverlay, not an Escape key.
  • src/tui/mention-popup.test.ts:358 — closed-state asserts .not.toBe("mentions") instead of overlayKind === null.

Splice goes through editPromptAt. Tests name the functions they call,
assert a closed overlay, and cover Enter during a no-match re-query.
@TheGreatAxios
TheGreatAxios merged commit 52279c4 into main Sep 4, 2026
5 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