fix: defer startup plugin checks until prompt idle#436
fix: defer startup plugin checks until prompt idle#436vltansky wants to merge 3 commits intoGitlawb:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR aims to prevent post-launch prompt input freezes by deferring performStartupChecks() (plugin startup work) until the REPL is locally idle, rather than running immediately on mount.
Changes:
- Added a small gate helper (
shouldRunStartupChecks) to centralize the startup-check conditions. - Updated
REPL.tsxto delay startup checks with an idle window (viasetTimeout) and re-check the gate before running. - Added a focused unit test for the new gate helper.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
src/screens/replStartupGates.ts |
Adds a helper predicate to decide if startup checks should run. |
src/screens/replStartupGates.test.ts |
Adds unit tests validating the gate logic. |
src/screens/REPL.tsx |
Defers performStartupChecks() until local prompt idle, with a delay + re-check. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
gnanam1990
left a comment
There was a problem hiding this comment.
Thanks for working on this. The approach makes sense, but I see one blocking issue in the current implementation:
src/screens/REPL.tsx:823
The newuseEffectdependency array referencespromptTypingSuppressionActive, but that variable is declared later in the component. Since the dependency array is evaluated during render, this will throw aReferenceErrorat runtime before the component finishes rendering.
Could you please move this effect below the promptTypingSuppressionActive declaration (or move that state/derived value earlier) and then re-run the checks?
There’s also a small follow-up cleanup opportunity to reuse PROMPT_SUPPRESSION_MS instead of another hardcoded 1500, but the runtime ordering issue is the blocking one.
Relocated the useEffect that gates startup checks so it sits after both `promptTypingSuppressionActive` and `startupChecksStartedRef` declarations, preventing a temporal dead-zone ReferenceError on the dependency array. Also replaced the magic `1500` with the existing `PROMPT_SUPPRESSION_MS` constant to keep the idle-delay value in a single place.
|
done |
Vasanthdev2004
left a comment
There was a problem hiding this comment.
Rechecked the latest head 5e8487415a685134620d99ac5b29833c259df2d7 against current origin/main.
The earlier runtime ordering bug is fixed on this revision, but I still can't approve because the core startup-check gating regression is still present.
src/screens/REPL.tsx:1331-1366 now delays performStartupChecks() behind promptTypingSuppressionActive, and src/screens/replInputSuppression.ts:1-5 still defines that flag as:
isPromptInputActive || inputValue.trim().length > 0So startup checks do not wait for the prompt to become idle; they wait for the prompt to become empty. If the session starts with a prefilled draft, or the user types part of a message and pauses without clearing it, shouldRunStartupChecks(...) stays false forever and performStartupChecks() never starts.
That is a behavioral regression from "run once the prompt is idle" to "run only when there is no draft text at all", and it can delay or completely skip startup plugin checks for the whole session.
The new focused test is also currently asserting that same regressed behavior:
src/screens/replStartupGates.test.ts:5-7says startup checks should be blocked while the prompt is "actively typing or seeded"
That means the branch is not just missing coverage for the regression; it is codifying it as the expected outcome.
What I rechecked on this head:
- direct code-path review of
src/screens/REPL.tsx,src/screens/replStartupGates.ts, andsrc/screens/replInputSuppression.ts bun test src/screens/replStartupGates.test.ts-> 4 passbun run build-> successbun run smoke-> success
So the latest head is stable enough to build, but it still changes startup checks from an idle gate to an empty-input gate.
shouldRunStartupChecks now checks isPromptInputActive (clears after PROMPT_SUPPRESSION_MS of idle) instead of promptTypingSuppressionActive (stays true while any draft text exists). This prevents a regression where startup checks would never fire if the prompt had a prefilled draft or the user paused mid-message without clearing the input.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| useEffect(() => { | ||
| if ( | ||
| !shouldRunStartupChecks( | ||
| isRemoteSession, | ||
| startupChecksStartedRef.current, | ||
| isPromptInputActive, | ||
| ) |
There was a problem hiding this comment.
Startup-check gating only considers isPromptInputActive, but isPromptInputActive is not set when the prompt is seeded with buffered early input (consumeEarlyInput()), so startup checks can still run while inputValue already contains user text. This undermines the goal of deferring plugin startup work until the prompt is truly idle; consider gating on promptTypingSuppressionActive (or inputValue.trim().length === 0) instead of isPromptInputActive alone, and update shouldRunStartupChecks accordingly.
| ): boolean { | ||
| return !isRemoteSession && !hasStarted && !isPromptInputActive |
There was a problem hiding this comment.
shouldRunStartupChecks only gates on isPromptInputActive, which does not capture the "seeded with buffered early input" case (where inputValue.trim().length > 0 but the typing flag is still false). If the intended condition is "prompt is idle and empty", consider changing the API to accept a broader suppression/idle flag (or include inputValue/hasBufferedInput) so the helper matches the actual startup-check eligibility rules.
| ): boolean { | |
| return !isRemoteSession && !hasStarted && !isPromptInputActive | |
| inputValue: string, | |
| ): boolean { | |
| return ( | |
| !isRemoteSession && | |
| !hasStarted && | |
| !isPromptInputActive && | |
| inputValue.trim().length === 0 | |
| ) |
Summary
performStartupChecks()until the REPL is locally idle instead of firing it immediately on mountWhy
Current builds already include the earlier keyboard-freeze fixes (#266, #285) and the startup dialog suppression fix (#423), but there are still reports in #363 where:
--bareworksThe strongest remaining code path is immediate plugin startup work from
performStartupChecks(setAppState)landing during initial prompt interaction.Test Plan
bun test src/screens/replStartupGates.test.tsbun run buildbun run smokeNote
Generated with Codex from a local reproduction, and the resulting fix did work for the reporter locally before opening this PR.
Closes #435