You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Make native-USB deployment fail safe and recoverable when Windows retains a stale/phantom COM devnode.
The concrete RP2040 failure is not missing CDC deployment support: fbuild can flash RP2040 UF2 firmware and can use a healthy Pico CDC endpoint. The bug is that Windows health discovered during enumeration is discarded before target selection, allowing a historical COM12 record to be returned as usable after it has become CM_PROB_PHANTOM.
This meta tracks the data-model fix, RP2040 state-machine fix, and an optional scoped UAC recovery path.
Confirmed evidence
Observed on a real RP2040:
runtime USB: VID:PID 2E8A:000A;
serial: 5303284720C4641C;
historical runtime endpoint: COM12;
BOOTSEL volume: RPI-RP2 on G:;
fbuild successfully copied the UF2 and observed the BOOTSEL volume eject;
the post-flash Windows devnode USB\VID_2E8A&PID_000A\5303284720C4641C was CM_PROB_PHANTOM;
serial open timed out;
the deployment path still returned COM12.
Related completed work already provides useful pieces but does not solve this:
Close this meta only after all three PRs are merged to main and the end-to-end criteria below are met.
Cross-platform scanning parity remains tracked by #1091. Link it in PRs where relevant, but do not expand this Windows recovery meta into untestable macOS/Linux implementation work.
Recovery ladder
For an RP2040 deploy, the final behavior is:
Enumerate all candidates with identity and health.
Show unhealthy devices diagnostically.
Select only a non-ambiguous candidate that is not known-unhealthy.
If healthy runtime CDC is openable, perform the existing 1200-baud BOOTSEL touch.
Otherwise use the existing manual BOOTSEL-volume path.
Copy UF2 with current identity/ambiguity safeguards.
Do not accept a fix that merely remembers the unique pre-flash COM name. The known bad patch shape is:
manual BOOTSEL + one historical runtime candidate
-> remember COM12
-> UF2 succeeds
-> return COM12 by serial match
That is unsafe unless the preflight record was healthy/openable and the post-flash endpoint is independently rediscovered, healthy, and openable.
Privileged recovery boundary
The normal CLI owns UAC policy. The normal daemon owns deployment state. The elevated process is the already-shipped fbuild.exe in a hidden helper mode and performs no daemon/cache/build work.
normal daemon detects exact unhealthy target
-> typed recovery request
-> normal CLI applies --admin/--no-admin policy
-> one-shot elevated helper revalidates identity and repairs PnP
-> helper exits
-> normal daemon rescans/reopens
There is no ?administrator daemon mode.?
Agent handoff procedure
A smaller model should follow this without redesigning the project:
Read this meta and the selected child completely.
Work on one child per PR from current origin/main; do not combine all three into one large patch.
Read repository CLAUDE.md and crates/CLAUDE.md.
Use the repository's required soldr-prefixed Rust commands exactly as shown below.
Only #1146, #1148, and #1147 are children of this meta. References to #962, #1095, #1074, #605, #1087, #1080, and #1091 are history, dependencies, or follow-up context; do not close, reopen, or add them to this burn-down. #1091 remains an explicitly separate macOS/Linux parity follow-up.
Execute strictly in this order: #1146 merged and validated -> #1148 merged and validated by fakes -> #1147 merged and hardware-validated -> close this meta. Do not start a later child from an earlier child's branch, and do not merge all children in one PR. One child has one focused PR unless a documented review fix requires a follow-up PR.
For every child: branch from fresh origin/main; use Closes #<child> in its PR; run the child's focused tests plus its listed clippy command; validate again after merge against current main; then close/tick only that child. Do not tick a checkbox or close an issue because code merely exists on a local branch or CI is green.
If a source fact disagrees with this roadmap, preserve the locked safety invariants and add a concise issue comment naming the file/symbol and discrepancy. Do not substitute a simpler but lossy design, relax health/openability checks, or turn a diagnostic record into an eligible deploy target merely to keep moving.
Required handoff record per child
The final PR/issue comment must state: merged PR URL and merge SHA; exact targeted commands and result; source callers migrated or intentionally left raw with rationale; whether hardware was touched; and the precise remaining next child. Hardware claims must name the board serial and health state actually observed. No simulated or historic run counts as the final #1147 hardware proof.
Goal
Make native-USB deployment fail safe and recoverable when Windows retains a stale/phantom COM devnode.
The concrete RP2040 failure is not missing CDC deployment support: fbuild can flash RP2040 UF2 firmware and can use a healthy Pico CDC endpoint. The bug is that Windows health discovered during enumeration is discarded before target selection, allowing a historical
COM12record to be returned as usable after it has becomeCM_PROB_PHANTOM.This meta tracks the data-model fix, RP2040 state-machine fix, and an optional scoped UAC recovery path.
Confirmed evidence
Observed on a real RP2040:
2E8A:000A;5303284720C4641C;COM12;RPI-RP2onG:;USB\VID_2E8A&PID_000A\5303284720C4641CwasCM_PROB_PHANTOM;COM12.Related completed work already provides useful pieces but does not solve this:
The missing invariant is: diagnostic visibility is not deploy eligibility.
Locked system invariants
Unknownhealth preserves current behavior; do not fabricate Windows semantics.Child issues and required order
Recommended close order:
DetectedPort/health semantics and prevents new consumers from receiving lossy records.mainand the end-to-end criteria below are met.Cross-platform scanning parity remains tracked by #1091. Link it in PRs where relevant, but do not expand this Windows recovery meta into untestable macOS/Linux implementation work.
Recovery ladder
For an RP2040 deploy, the final behavior is:
--no-admin: print exact direct-port and BOOT+RESET recovery instructions;--admin: run the feat(windows): add scoped elevated USB PnP recovery helper #1148 helper once, then repeat step 8;Architecture map
Health source
crates/fbuild-serial/src/ports.rsowns the enriched record and Windows classification.Health must survive through:
Any conversion back to name/VID/PID-only data before selection is a blocker.
RP2040 consumer
Primary files:
crates/fbuild-deploy/src/rp2040.rscrates/fbuild-deploy/src/rp2040_target.rscrates/fbuild-daemon/src/handlers/operations/deploy.rsDo not accept a fix that merely remembers the unique pre-flash COM name. The known bad patch shape is:
That is unsafe unless the preflight record was healthy/openable and the post-flash endpoint is independently rediscovered, healthy, and openable.
Privileged recovery boundary
The normal CLI owns UAC policy. The normal daemon owns deployment state. The elevated process is the already-shipped
fbuild.exein a hidden helper mode and performs no daemon/cache/build work.There is no ?administrator daemon mode.?
Agent handoff procedure
A smaller model should follow this without redesigning the project:
origin/main; do not combine all three into one large patch.CLAUDE.mdandcrates/CLAUDE.md.Fixes #<child>;Required automated validation
At minimum, across the three PRs:
Required deterministic matrix:
Required hardware closeout
Use fbuild through FastLED AutoResearch; do not substitute raw pyserial, picotool, or manual copy commands.
Use an isolated five-digit dev daemon port so another fbuild worktree cannot be stomped:
Capture:
fbuild port scanbefore deployment;fbuild-daemon.exestayed normal privilege and kept the same endpoint/PID.An already-flashed board is sufficient. Do not require or consume another factory-fresh board.
Meta completion criteria
fbuild port scanidentifies phantom/problem endpoints without hiding them.--adminperforms at most one scoped helper operation;--no-adminnever prompts.main.Non-goals
Related history
Canonical execution contract (read before acting)
Only #1146, #1148, and #1147 are children of this meta. References to #962, #1095, #1074, #605, #1087, #1080, and #1091 are history, dependencies, or follow-up context; do not close, reopen, or add them to this burn-down. #1091 remains an explicitly separate macOS/Linux parity follow-up.
Execute strictly in this order:
#1146 merged and validated->#1148 merged and validated by fakes->#1147 merged and hardware-validated-> close this meta. Do not start a later child from an earlier child's branch, and do not merge all children in one PR. One child has one focused PR unless a documented review fix requires a follow-up PR.For every child: branch from fresh
origin/main; useCloses #<child>in its PR; run the child's focused tests plus its listed clippy command; validate again after merge against currentmain; then close/tick only that child. Do not tick a checkbox or close an issue because code merely exists on a local branch or CI is green.If a source fact disagrees with this roadmap, preserve the locked safety invariants and add a concise issue comment naming the file/symbol and discrepancy. Do not substitute a simpler but lossy design, relax health/openability checks, or turn a diagnostic record into an eligible deploy target merely to keep moving.
Required handoff record per child
The final PR/issue comment must state: merged PR URL and merge SHA; exact targeted commands and result; source callers migrated or intentionally left raw with rationale; whether hardware was touched; and the precise remaining next child. Hardware claims must name the board serial and health state actually observed. No simulated or historic run counts as the final #1147 hardware proof.