Skip to content

feat(windows): orchestrate one-shot RP2040 USB recovery through deploy --admin#1156

Merged
zackees merged 1 commit into
mainfrom
feat/1152-usb-recovery-orchestration
Jul 23, 2026
Merged

feat(windows): orchestrate one-shot RP2040 USB recovery through deploy --admin#1156
zackees merged 1 commit into
mainfrom
feat/1152-usb-recovery-orchestration

Conversation

@zackees

@zackees zackees commented Jul 23, 2026

Copy link
Copy Markdown
Member

Closes #1152 (tracked by meta #1149; consumes the merged #1148 helper foundation and the merged #1147 fail-closed state machine, PR #1153).

What changed

The Windows recovery design is now complete in practice: a deploy that detects its exact target as a known-unhealthy devnode produces a typed UsbRecoveryRequest, and explicit interactive --admin drives the one-shot elevated helper, then resumes deployment on freshly enumerated transports only.

fbuild-core

  • UsbRecoveryOperation::RestartVerifiedParent — the one narrow allowlist extension the issue anticipated: a present problematic USB interface devnode (...&MI_xx\..., e.g. the BOOTSEL PICOBOOT function at CM_PROB_FAILED_INSTALL) may only restart its live-verified healthy parent composite; restarting the interface alone cannot re-initialize sibling interfaces or remount the stale synthetic BOOTSEL volume (the observed ERROR_FILE_INVALID 1006 case).
  • UNCLASSED_DEVICE_CLASS sentinel: an absent Windows device class (driverless devnode) becomes an exact-match identity fact, never a wildcard.

fbuild-serial

  • execute_recovery: new PresentProblem-interface arm — parent re-inspected live (allow_phantom=false), required HealthyPresent, instance-ID matched, VID/PID equal to the child, else parent-not-live/missing-verified-parent fail-closed. Plain device targets keep the original exact-child RestartTarget.
  • Windows backend maps a missing Device_Class (CR_NO_SUCH_VALUE) to the sentinel instead of failing inspection; restart_verified_parent reuses the bounded disable/enable with rollback.
  • UsbProblemDevice gains parent_instance_id + device_class (Config-Manager-proved facts needed to compose an exact-device request).

fbuild-daemon

  • OperationResponse.usb_recovery: Option<UsbRecoveryRequest> (skip_serializing_if, backward-compatible; CLI mirror uses serde(default)).
  • Deploy handler: when platform is RaspberryPi on Windows and the deploy either failed or flashed without a recovered CDC endpoint, run a fresh device refresh + present_usb_problem_devices() scan and call compose_rp2040_recovery_request (new recovery_request.rs): BOOTSEL problem interface (bootloader-UF2 identity, &MI_, proved parent) preferred over the phantom runtime CDC record; records without canonical instance/parent facts are skipped. flash_completed distinguishes the two flows. The daemon never elevates and never acts on the request.

fbuild-cli

  • run_recovery_for_typed_request — like the merged launch_once_for_typed_request but actually consumes the helper's result file before the rendezvous drops, validating the rendezvous nonce and operation ID (a foreign/mismatched result fails closed).
  • run_deploy orchestration (maybe_recover_and_retry): on a response carrying usb_recovery
    • Default policy → prints the --admin suggestion + physical BOOTSEL guidance, never elevates; --no-admin → never prompts; CI/non-interactive/non-Windows → refuse with a note.
    • --admin → helper launched exactly once (UAC cancel is expected control flow), then:
      • flash_completed == falseone deploy retry with skip_build = true and usb_recovery_policy = DenyAdmin (never rebuilds, can never prompt or recover twice);
      • flash_completed == true → never reflashes; bounded 10 s rescan via fresh fbuild-serial enumerations requiring health eligibility + a bounded open probe before any name is reported. No stale COM name or volume handle is ever reused.

Deterministic tests

  • serial: interface→verified-parent restart happy path; unhealthy parent, mismatched parent VID/PID, missing parent fact each fail closed; sentinel class is exact-match, not wildcard (13 total in usb_recovery.rs).
  • daemon (recovery_request.rs): BOOTSEL interface preferred over phantom CDC; phantom COM12 for serial 5303284720C4641C yields the exact typed request; healthy/unrelated devices compose nothing; records without canonical identity or parent are skipped; parent-serial extraction requires matching VID/PID.
  • CLI: completed helper result validated & returned; mismatched nonce fails closed; UAC cancel is control flow; Default/--no-admin never reach the launcher.
  • serde: usb_recovery absent in old daemon responses parses (mirror serde(default)).

Validation

soldr cargo test -p fbuild-core usb        -> 32 passed
soldr cargo test -p fbuild-serial          -> 163 + 7 doc passed
soldr cargo test -p fbuild-daemon --lib    -> 208 passed
soldr cargo test -p fbuild-cli             -> all suites ok (187 lib + bins)
soldr cargo test -p fbuild-deploy          -> 241 passed
soldr cargo clippy (5 crates) --all-targets -- -D warnings  -> clean
tree-wide pinned rustfmt --check           -> clean

Hardware

Not yet touched by this PR — the attached board (serial 5303284720C4641C) remains preserved in the exact reproduced wedge (BOOTSEL RPI-RP2 on G:, phantom COM12, PICOBOOT MI_01 problem 28). The fbuild-only hardware closeout (fbuild port scan pre/post, deploy --admin, bash autoresearch rp2040 --rpc-smoke --timeout 120s --skip-lint on an isolated five-digit FBUILD_DAEMON_PORT) runs immediately after merge, with evidence posted to #1152/#1147/#1149 per the meta handoff contract.

🤖 Generated with Claude Code

…y --admin

Wires the merged #1148 helper foundation and #1147 fail-closed state
machine into an end-to-end recovery flow:

- fbuild-core: UsbRecoveryOperation::RestartVerifiedParent — a present
  problematic composite-interface devnode (e.g. the BOOTSEL PICOBOOT
  function at CM_PROB_FAILED_INSTALL) may only restart its live-verified
  healthy parent composite, the one action that can remount the stale
  synthetic BOOTSEL volume; UNCLASSED_DEVICE_CLASS sentinel makes an
  absent Windows device class an exact-match fact
- fbuild-serial: execute_recovery grows the interface->verified-parent
  arm (identity + VID/PID re-proved live, fail-closed otherwise); the
  Windows backend tolerates missing Device_Class via the sentinel;
  UsbProblemDevice now carries parent_instance_id and device_class
- fbuild-daemon: on an RP2040 deploy that failed or flashed without a
  recovered CDC endpoint, compose a typed UsbRecoveryRequest from FRESH
  scan facts (BOOTSEL problem interface preferred over the phantom
  runtime CDC record) and attach it to OperationResponse.usb_recovery;
  the daemon never elevates and never acts on the request itself
- fbuild-cli: run_recovery_for_typed_request consumes and validates the
  helper's nonce-bound result before the rendezvous drops; deploy
  orchestration launches the helper at most once under explicit
  interactive --admin, then either retries the deployment exactly once
  (skip_build, DenyAdmin — no rebuild, no second prompt) when the
  transfer never succeeded, or rescans for a health-eligible openable
  endpoint when the flash was already confirmed; Default/--no-admin/CI
  never elevate

Closes #1152

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Warning

Review limit reached

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

Next review available in: 1 minute

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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c4a0fbf7-11e2-48f8-91e8-e80007cfd34a

📥 Commits

Reviewing files that changed from the base of the PR and between 618001d and a45bc7b.

📒 Files selected for processing (18)
  • crates/fbuild-cli/src/cli/deploy.rs
  • crates/fbuild-cli/src/cli/port_scan.rs
  • crates/fbuild-cli/src/cli/usb_recovery.rs
  • crates/fbuild-cli/src/daemon_client.rs
  • crates/fbuild-cli/src/daemon_client/types.rs
  • crates/fbuild-core/src/usb/mod.rs
  • crates/fbuild-core/src/usb/recovery.rs
  • crates/fbuild-daemon/src/handlers/emulator/avr8js_deploy.rs
  • crates/fbuild-daemon/src/handlers/emulator/qemu_deploy.rs
  • crates/fbuild-daemon/src/handlers/emulator/select.rs
  • crates/fbuild-daemon/src/handlers/operations/build.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs
  • crates/fbuild-daemon/src/handlers/operations/deploy_port.rs
  • crates/fbuild-daemon/src/handlers/operations/mod.rs
  • crates/fbuild-daemon/src/handlers/operations/recovery_request.rs
  • crates/fbuild-daemon/src/models.rs
  • crates/fbuild-serial/src/ports.rs
  • crates/fbuild-serial/src/usb_recovery.rs
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/1152-usb-recovery-orchestration

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.

@zackees
zackees merged commit 9284e60 into main Jul 23, 2026
89 of 93 checks passed
@zackees
zackees deleted the feat/1152-usb-recovery-orchestration branch July 23, 2026 11:28
@fastled-project-sync fastled-project-sync Bot moved this to Triage in FastLED Tracker Jul 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Triage

Development

Successfully merging this pull request may close these issues.

feat(windows): recover RP2040 phantom/BOOTSEL USB state through scoped admin PnP flow

1 participant