Skip to content

fix(cdp): fast-fail cdp_status when the Dev Client picker blocks the bundle (closes #184)#187

Merged
Lykhoyda merged 1 commit into
mainfrom
fix/issue-184-picker-blocking-probe
May 30, 2026
Merged

fix(cdp): fast-fail cdp_status when the Dev Client picker blocks the bundle (closes #184)#187
Lykhoyda merged 1 commit into
mainfrom
fix/issue-184-picker-blocking-probe

Conversation

@Lykhoyda

Copy link
Copy Markdown
Owner

Closes #184.

Problem

cdp_status({platform:'ios'}) with the Expo Dev Client "Development servers" picker up and Metro running hung ~33.5s, logged React not ready after 30000ms, then returned a misleading ok:true. Root cause: stale C++ targets pass filterValidTargets, the 1+1 pre-flight probe passes (the C++ connection answers), and performSetup's waitForReact(REACT_READY_TIMEOUT_MS=30_000) then burns its full 30s because the picker blocks the bundle so React never readies.

Fix

Thread a connect intent ('default' | 'status') through autoConnect → discoverAndConnect → connectToTarget. For a status-scoped connect against a non-Hermes target, run a bounded React-reachability probe (default 5s, RN_PICKER_PROBE_BUDGET_MS-overridable) before setup()'s 30s waitForReact. If React isn't reachable within the budget, throw the typed PickerBlockingBundleError — which bypasses both the connectToTarget retry loop and the discoverAndConnect candidate loop — and status.ts maps it to a fast failResult with code PICKER_BLOCKING and an actionable message:

Dev Client picker appears to be blocking the bundle … select your Metro server, then retry cdp_status.

A working Bridgeless app's target is also a [C++ connection], but its React is reachable, so the probe succeeds and the connect proceeds normally — only a genuinely blocked/never-ready target throws.

What it deliberately does NOT do (the two regressions #184 called out)

  • Doesn't shorten the global REACT_READY_TIMEOUT_MS — it's shared by every connect path and would regress legitimate slow first-builds.
  • Doesn't add a pre-autoConnect target-shape heuristic — /json/list returns 0 Hermes targets transiently during healthy reloads, so that would false-positive.
  • Hermes targets skip the probe entirely (a slow Hermes first-build keeps the full budget); non-status connect paths (cdp_connect, feature-dev, reconnect) are completely unchanged.

Files (~5, as the issue estimated)

cdp/connect.ts (intent threading, PickerBlockingBundleError, shouldRunPickerProbe, probe + both catch sites), cdp/setup.ts (probeReactReachable), cdp-client.ts (intent param), tools/status.ts ('status' intent + mapping), types.ts (PICKER_BLOCKING code).

Testing

  • 5 new unit tests for the extracted pure helpers (probeReactReachable, shouldRunPickerProbe, PickerBlockingBundleError), written failing-first — the connect layer opens a real WebSocket, so the decision logic is extracted into pure helpers per the repo's existing formatConnectFailureMessage/stickyPlatformFilters convention.
  • Full suite: 1563 pass; tsc --noEmit clean; existing connect tests unaffected.
  • The end-to-end picker-up assertion (cdp_status returns < ~6s) belongs to the deferred Dev Client harness CI (DC-Task 9 / Multi-device routing + dev-client picker hangs (combined session feedback) #136), per the issue.

🤖 Generated with Claude Code

…bundle (closes #184)

cdp_status with the Expo Dev Client "Development servers" picker up AND Metro
running hung ~33.5s: stale C++ targets pass filterValidTargets, the 1+1 probe
passes, and performSetup's waitForReact then burns its full 30s before returning
a misleading ok:true.

Fix: thread a connect intent ('default' | 'status') through
autoConnect -> discoverAndConnect -> connectToTarget. For a status-scoped connect
against a non-Hermes target, run a bounded React-reachability probe (default 5s,
RN_PICKER_PROBE_BUDGET_MS-overridable) BEFORE setup()'s 30s waitForReact. If
React isn't reachable, throw the typed PickerBlockingBundleError, which bypasses
both the connectToTarget retry loop and the discoverAndConnect candidate loop;
status.ts maps it to a fast failResult with code PICKER_BLOCKING and an
actionable message ("select your Metro server, then retry cdp_status").

Deliberately NOT changing the global REACT_READY_TIMEOUT_MS (shared by every
connect path) and NOT adding a pre-autoConnect target-shape heuristic (which
false-positives on healthy reloads). Hermes targets skip the probe entirely, so
legitimate slow first-builds keep the full budget; non-status connect paths are
unchanged.

New pure helpers (tested without a real WebSocket, per the repo's
formatConnectFailureMessage/stickyPlatformFilters convention):
- probeReactReachable(evaluate, budgetMs, pollMs) -> boolean (setup.ts)
- shouldRunPickerProbe(intent, target), PickerBlockingBundleError (connect.ts)

Full suite 1563 pass; tsc clean. End-to-end picker-up assertion belongs to the
deferred Dev Client harness CI (DC-Task 9 / #136).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Lykhoyda Lykhoyda merged commit 891de57 into main May 30, 2026
7 checks passed
Lykhoyda added a commit that referenced this pull request May 31, 2026
…to-dismiss (#196)

* docs(#136): design spec for dev-client picker dismiss tool + launch wiring (sub-3)

Approach B: cdp_dismiss_dev_client_picker MCP tool (Android-as-is) + iOS guard
+ best-effort device_deeplink dismissal, routed through one shared helper.
Sub-1/sub-2 already shipped (#174/#187); this covers the remaining sub-3.



* docs(#136): implementation plan for dev-client picker dismiss (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(#136): add guarded clearDevClientPickerIfPresent seam (sub-3)

Wraps the unchanged Android handleDevClientPicker(); iOS short-circuits
with an actionable message and never touches the legacy agent-device path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(#136): map picker outcome to ToolResult in dismiss handler (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(#136): register cdp_dismiss_dev_client_picker MCP tool (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(#136): best-effort Dev Client picker dismiss after Android deep links (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(#136): document cdp_dismiss_dev_client_picker; bump tool count 75->76 (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore(#136): add changeset (patch) for dev-client picker dismiss tool

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* build(#136): compile dist for dev-client picker dismiss tool (sub-3)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
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.

fix: cdp_status hangs ~33.5s when Dev Client picker blocks the bundle (#136 sub-2)

1 participant