fix: prevent model picker race condition when switching agent types#807
fix: prevent model picker race condition when switching agent types#807pedramamini merged 2 commits intorcfrom
Conversation
The model/effort pill useEffect in MainPanel had no cleanup, causing stale async responses from a previous agent to overwrite the current agent's model list. For example, switching from OpenCode (slow subprocess discovery) to Claude (fast file read) could result in OpenCode's models appearing in a Claude session. Added a stale flag with cleanup function so late-arriving responses from superseded effect invocations are silently discarded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughImplements a stale-flag guard in MainPanel to prevent late-resolving agent model-discovery promises from overwriting state after switching Changes
Sequence Diagram(s)sequenceDiagram
participant MainPanel
participant MaestroAgents as window.maestro.agents
participant InputArea
Note over MainPanel,MaestroAgents: Start model discovery for OpenCode (deferred)
MainPanel->>MaestroAgents: getModels('opencode') -> deferred Promise
Note over MainPanel: User switches activeSession.toolType -> 'claude-code'
MainPanel->>MaestroAgents: getModels('claude-code') -> resolves immediately
MaestroAgents-->>MainPanel: resolves with claude models
alt effect not stale
MainPanel->>MainPanel: setPillModels/efforts with Claude models
MainPanel->>InputArea: render data-available-models = Claude models
end
Note over MaestroAgents,MainPanel: Later, deferred OpenCode promise resolves
MaestroAgents-->>MainPanel: resolves with opencode models
alt effect is stale
MainPanel--xMaestroAgents: ignore response (stale guard prevents setState)
else
MainPanel->>InputArea: (would overwrite models) but guarded
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~18 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds a
Confidence Score: 5/5Safe to merge — the production fix is correct and the only remaining finding is a P2 test-assertion gap. The stale-flag implementation in MainPanel.tsx is correct and covers all three async calls. The sole finding is that the new test's assertions don't actually verify the race-condition behavior, which is a quality-of-test concern but not a production defect. src/tests/renderer/components/MainPanel.test.tsx — assertions should be strengthened to actually catch a regression if the stale guard is removed. Important Files Changed
Sequence DiagramsequenceDiagram
participant U as User
participant MP as MainPanel useEffect
participant OC as opencode getModels (slow)
participant CC as claude-code getModels (fast)
U->>MP: Render with opencode session
MP->>OC: getModels('opencode') [stale=false]
Note over OC: subprocess running…
U->>MP: Switch to claude-code session
MP-->>MP: cleanup() → stale=true (opencode effect)
MP->>CC: getModels('claude-code') [new stale=false]
CC-->>MP: ['sonnet','opus','haiku'] → setPillModels ✓
OC-->>MP: ['gpt-5-mini','llama3'] (arrives late)
MP-->>MP: stale=true → DISCARD, setPillModels NOT called ✓
Reviews (1): Last reviewed commit: "fix: prevent model picker race condition..." | Re-trigger Greptile |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/__tests__/renderer/components/MainPanel.test.tsx`:
- Around line 3542-3556: The test currently only checks both getModels calls
happened but doesn't verify Claude's models weren't overwritten by the late
opencode response; after calling resolveOpenCodeModels(openCodeModels) (the late
resolver), add an assertion that the component/state still reflects the Claude
models (e.g. assert rendered model names or the internal model list equals the
expected claudeModels) and assert that the opencode payload was not applied (for
example, ensure no subsequent call applied opencode models or that UI does not
contain any openCode model names); use the existing resolveOpenCodeModels,
openCodeModels and the mocked window.maestro.agents.getModels/response helpers
to locate where to add these assertions.
In `@src/renderer/components/MainPanel/MainPanel.tsx`:
- Around line 266-291: The three .catch handlers for the async calls to
window.maestro.agents (the getModels / getConfigOptions / getConfig promise
chains that call setPillModels, setPillEfforts, setAgentDefaultModel and
setAgentDefaultEffort) currently swallow errors; update each catch to report the
error via the Sentry utilities (import captureException or captureMessage from
src/utils/sentry.ts) with contextual info (agentId and which call: models,
effort/options, or config), then retain the existing fallback state updates
(e.g., setPillModels([]), setPillEfforts([]), setAgentDefaultModel(''),
setAgentDefaultEffort('')) so UI degrades while telemetry is recorded.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: ba047a35-3cba-4de4-bffd-c243a8389fdb
📒 Files selected for processing (2)
src/__tests__/renderer/components/MainPanel.test.tsxsrc/renderer/components/MainPanel/MainPanel.tsx
Update InputArea mock to expose availableModels as a data attribute, then assert the actual model state after the stale response resolves. This ensures the test fails if the stale-flag guard is removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…unMaestro#807) * fix: prevent model picker race condition when switching agent types The model/effort pill useEffect in MainPanel had no cleanup, causing stale async responses from a previous agent to overwrite the current agent's model list. For example, switching from OpenCode (slow subprocess discovery) to Claude (fast file read) could result in OpenCode's models appearing in a Claude session. Added a stale flag with cleanup function so late-arriving responses from superseded effect invocations are silently discarded. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * fix: strengthen test assertions for model picker race condition Update InputArea mock to expose availableModels as a data attribute, then assert the actual model state after the stale response resolves. This ensures the test fails if the stale-flag guard is removed. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
MainPanel.tsxmodel/effort pill fetching where stale async responses from a previous agent could overwrite the current agent's model listuseEffectthat fetches models, effort options, and agent defaultsRoot Cause
When switching between agents (e.g., OpenCode → Claude), the
useEffectfired async calls for both. OpenCode's model discovery (opencode modelssubprocess) is slower than Claude's (local file read), so the stale OpenCode response could resolve last and overwrite Claude's model list — causing OpenCode models likegithub-copilot/gpt-5-minito appear in a Claude-only session.Test plan
Model/effort pill race condition > should discard stale model responses when switching agent types🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Tests