Skip to content

fix(preflight): do not pin empty WSL/local agent detection (#8366)#8391

Open
bbingz wants to merge 4 commits into
stablyai:mainfrom
bbingz:fix/wsl-agent-detect-nonsticky-empty
Open

fix(preflight): do not pin empty WSL/local agent detection (#8366)#8391
bbingz wants to merge 4 commits into
stablyai:mainfrom
bbingz:fix/wsl-agent-detect-nonsticky-empty

Conversation

@bbingz

@bbingz bbingz commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

WSL cold-start / probe failures return an empty agent list, and the renderer treated that empty [] as a successful cache hit (if (existing) is true for arrays). The launcher then permanently hid installed agents (including Grok) until a context switch. This also adds one cold-start retry for WSL ETIMEDOUT / killed probes, and wires the local launch surfaces to re-probe once on remount after an empty result (same contract as SSH/runtime).

Closes #8366

Root cause

  1. Renderer pin: ensureDetectedAgents short-circuited on any non-null detectedAgentIds, including []. Remote/runtime already used existing?.length (Detect agents in the serve user's package-manager bins; don't pin empty remote-detect results #6029); local did not.
  2. Resolved empty promise: even after a re-entry, a resolved detectPromise for the same context key could still short-circuit.
  3. WSL soft-fail: detectWslCommandsOnPath swallowed timeouts into new Set() with no retry, feeding the sticky empty cache.
  4. Hook gap (follow-up in this PR): store non-sticky empty alone was not enough — useDetectedAgents only called ensureLocal when detectedIds === null, so TabBar/QuickLaunch never re-entered after []. SSH/runtime already had one empty remount retry; local did not.

Fix

  • Local cache short-circuit: existing?.length && detectedContextKey === contextKey (mirror remote/runtime).
  • Clear detectPromise when the detection/refresh result is empty so the next ensure re-probes.
  • Retry WSL agent detection once on ETIMEDOUT / killed only; hard shell failures still fail immediately to empty.
  • useDetectedAgents local path: one empty remount retry per mounted surface (emptyRetryKey = 'local'), same shape as SSH/runtime.

Related PRs

Test plan

  • pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/store/slices/detected-agents.test.ts src/main/ipc/preflight-wsl-agent-detection.test.ts src/renderer/src/hooks/useDetectedAgents.test.tsx
  • Unit: empty local detection re-probes on next ensure (store)
  • Unit: local launch surface remount retries empty once and does not thrash on same mount
  • Unit: WSL ETIMEDOUT / killed retries once and returns second result
  • Unit: non-timeout probe failures do not retry
  • Manual (Windows+WSL): cold-start Orca with Grok on PATH in WSL; open launcher twice without switching projects — Grok should appear after a warm probe

@coderabbitai

coderabbitai Bot commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@bbingz, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 25dc4505-8dfe-45b5-afcd-c2958645cca2

📥 Commits

Reviewing files that changed from the base of the PR and between 3557906 and d869edb.

📒 Files selected for processing (6)
  • src/main/ipc/preflight-wsl-agent-detection.test.ts
  • src/main/ipc/preflight-wsl-agent-detection.ts
  • src/renderer/src/hooks/useDetectedAgents.test.tsx
  • src/renderer/src/hooks/useDetectedAgents.ts
  • src/renderer/src/store/slices/detected-agents.test.ts
  • src/renderer/src/store/slices/detected-agents.ts
📝 Walkthrough

Walkthrough

Adds one retry for WSL detection command failures caused by timeouts or killed child processes, while leaving other failures non-retriable. Expands tests for retry counts and returned results. Updates detected-agent state handling so empty detection or refresh results do not remain cached, allowing subsequent detection calls to run again. Adds local hook retry behavior and coverage for empty results, remounts, and later detected agents.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers summary, root cause, fix, and tests, but it omits required Screenshots, AI Review Report, Security Audit, and Notes sections. Add the missing template sections, including 'No visual change' if applicable, an AI review summary, a security audit, and any platform-specific notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and matches the main change: fixing empty WSL/local agent detection pinning.
Linked Issues check ✅ Passed The changes directly address #8366 by fixing empty WSL/local detection caching and the related retry behavior needed for Grok to appear again.
Out of Scope Changes check ✅ Passed The modified tests and logic stay focused on WSL/local agent detection and remount retries, with no clear unrelated scope creep.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/main/ipc/preflight-wsl-agent-detection.test.ts (1)

99-135: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider adding a retry exhaustion test.

The three new tests cover single-retry success and non-retriable failure, but don't verify behavior when both attempts timeout (retry exhaustion). A test confirming detectWslCommandsOnPath returns an empty Set and execFileAsyncMock is called exactly twice after two consecutive ETIMEDOUT rejections would close the gap.

🧪 Suggested test for retry exhaustion
  it('returns an empty set when both attempts timeout', async () => {
    const timeoutError = Object.assign(new Error('Timed out running wsl.exe'), {
      code: 'ETIMEDOUT'
    })
    execFileAsyncMock.mockRejectedValue(timeoutError)

    const found = await detectWslCommandsOnPath({ distro: 'Ubuntu' }, ['grok'])

    expect(found).toEqual(new Set())
    expect(execFileAsyncMock).toHaveBeenCalledTimes(2)
  })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: d1b358ea-54b0-4880-a68a-aef5c16402c7

📥 Commits

Reviewing files that changed from the base of the PR and between 26934b1 and 8663d45.

📒 Files selected for processing (4)
  • src/main/ipc/preflight-wsl-agent-detection.test.ts
  • src/main/ipc/preflight-wsl-agent-detection.ts
  • src/renderer/src/store/slices/detected-agents.test.ts
  • src/renderer/src/store/slices/detected-agents.ts

bbingz added a commit to bbingz/orca that referenced this pull request Jul 12, 2026
Store non-sticky empty was not enough: useDetectedAgents only called
ensureLocal when detectedIds was null, so TabBar/QuickLaunch never
re-probed after a WSL cold-start []. Mirror SSH/runtime one-shot empty
remount retry for local surfaces (stablyai#8366 / stablyai#8391).
@bbingz

bbingz commented Jul 13, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

bbingz added a commit to bbingz/orca that referenced this pull request Jul 14, 2026
Store non-sticky empty was not enough: useDetectedAgents only called
ensureLocal when detectedIds was null, so TabBar/QuickLaunch never
re-probed after a WSL cold-start []. Mirror SSH/runtime one-shot empty
remount retry for local surfaces (stablyai#8366 / stablyai#8391).
@bbingz
bbingz force-pushed the fix/wsl-agent-detect-nonsticky-empty branch from b01b463 to 3557906 Compare July 14, 2026 00:37
bbingz added a commit to bbingz/orca that referenced this pull request Jul 14, 2026
Store non-sticky empty was not enough: useDetectedAgents only called
ensureLocal when detectedIds was null, so TabBar/QuickLaunch never
re-probed after a WSL cold-start []. Mirror SSH/runtime one-shot empty
remount retry for local surfaces (stablyai#8366 / stablyai#8391).
@bbingz
bbingz force-pushed the fix/wsl-agent-detect-nonsticky-empty branch 2 times, most recently from d869edb to a499c6b Compare July 18, 2026 14:56
@bbingz

bbingz commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Rebased onto current main (was ~241 commits behind). Focused tests still pass:

  • preflight-wsl-agent-detection
  • useDetectedAgents
  • detected-agents

Ready for re-review / CI.

@OrcaWin

OrcaWin commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator

Taking a look!

@OrcaWin

OrcaWin commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

Did a deep review of this cold-start empty-agent-list fix (#8366) — correctness, cross-platform, SSH/runtime, and perf/regression angles — plus ran the full test suite for the touched files.

Verdict: solid. The three-part approach is correct and minimal, and it reuses the already-shipped non-sticky-empty pattern (ensureRemoteDetectedAgents / ensureRuntimeDetectedAgents, #6029) rather than inventing a new caching scheme:

  • Store: existing?.length guard + nulling detectPromise on empty resolves (and in refreshDetectedAgents) so an empty [] can't be re-cached as a hit — while non-empty still short-circuits.
  • Hook: one fresh probe per launch-surface remount on [], bounded by the per-mount retriedEmptyTargetRef guard, mirroring the ssh/runtime branches.
  • WSL probe: one bounded ETIMEDOUT/killed retry to absorb a single cold-start miss.

Things I specifically checked and confirmed are not problems: no re-probe/render loop (per-mount ref guard + module-scoped detectPromise dedup means at most one in-flight wsl.exe probe even under rapid reopen); warm path is byte-for-byte unchanged (no perf regression for installed agents); the two effect callers (StatusBar, useComposerState) depend on the stable action ref, not detectedAgentIds, so non-sticky-empty doesn't make them loop; retry bound is exactly one; SSH/runtime branches untouched.

One change I pushed (commit 35fe8b0): corrected the comment on isWslAgentDetectionTimeoutError. It claimed "Node's execFile timeout sets code=ETIMEDOUT" — I verified empirically that Node's execFile timeout actually kills the child with code=null, killed=true (SIGTERM); the ETIMEDOUT code comes only from the in-file Promise.race wrapper. The predicate itself was already correct (it checks both) — but the comment misexplained the exact mechanism, which could lead a future maintainer to drop the killed check and silently break the execFile-timeout retry path. Comment-only, no behavior change.

Validation: 30/30 tests pass across the three touched test files, tsc --noEmit (node project) clean on the changed files, oxlint clean. LGTM to merge.

@bbingz

bbingz commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@OrcaWin Thanks for the deep review and “solid” verdict on the cold-start empty agent-list fix (#8366).

Still MERGEABLE on current main. Happy to take any final nits; otherwise this is ready to land.

bbingz and others added 3 commits July 23, 2026 15:02
…8366)

Empty [] is truthy, so a cold-start soft-fail permanently hid agents from
the launcher until a context switch. Match remote/runtime non-sticky empty
caching, clear empty detectPromise snapshots, and retry one WSL ETIMEDOUT.
Store non-sticky empty was not enough: useDetectedAgents only called
ensureLocal when detectedIds was null, so TabBar/QuickLaunch never
re-probed after a WSL cold-start []. Mirror SSH/runtime one-shot empty
remount retry for local surfaces (stablyai#8366 / stablyai#8391).
@bbingz
bbingz force-pushed the fix/wsl-agent-detect-nonsticky-empty branch from 35fe8b0 to 8910a7f Compare July 23, 2026 07:06
@bbingz

bbingz commented Jul 23, 2026

Copy link
Copy Markdown
Contributor Author

Restack note

Rebased onto latest main (was DIRTY). Conflicts in useDetectedAgents:

Ready for upstream review; community author cannot merge.

Use a per-mount retry key so the first empty result does not double-fire
ensureDetectedAgents, while a remounted launch surface still re-probes.
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.

[windows] Sometimes Grok CLI is not detected in Orca but works in system terminal

3 participants