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 port health a first-class part of fbuild's serial-device model. Windows already discovers whether a COM devnode is live, phantom, or in a Config Manager problem state; this issue stops that information from being discarded before device selection.
This is the foundation for #1147. It must land before RP2040 deployment tries to reject stale CDC endpoints.
Confirmed failure
fbuild_serial::ports::available_ports() currently inspects Windows PnP state and intentionally retains unusual/non-present USB COM records for diagnostic compatibility with #962. It then returns only serialport::SerialPortInfo.
That return type loses:
whether the devnode is present in the live tree;
Config Manager status and problem code;
canonical device-instance ID;
parent/topology identity already discovered during the PnP walk.
A stale RP2040 entry can therefore look identical to a live COM12 later in deployment. This is a data-model bug, not an RP2040 timeout bug.
Required data model
Add a public record in crates/fbuild-serial/src/ports.rs (names may vary, semantics may not):
structDetectedPort{info: serialport::SerialPortInfo,health:PortHealth,instance_id:Option<String>,parent_instance_id:Option<String>,// Reuse existing topology/location data when available.}enumPortHealth{HealthyPresent,PresentProblem{problem_code:u32,status:Option<u32>},Phantom{problem_code:Option<u32>,status:Option<u32>},Unknown,}
Classification rules:
HealthyPresent: Windows says the devnode is present and its problem code is zero.
PresentProblem: present is true and the problem code is non-zero.
Phantom: present is false, regardless of whether Windows also reports a problem code.
Unknown: the host does not expose equivalent metadata (normal on non-Windows), the endpoint is non-USB, or the Windows metadata query failed.
Preserve the canonical Windows instance ID exactly as returned by PnP. Display escaping is a rendering concern; matching must use the canonical value.
Add helpers such as is_known_unhealthy(). Unknown is not the same as unhealthy; existing non-Windows behavior must continue and consumers can use an openability probe.
Implementation directions
Refactor crates/fbuild-serial/src/ports.rs so the blessed enumeration API returns DetectedPort, not a lossy SerialPortInfo.
Do not derive health by joining only against present_usb_problem_devices(): that helper uses a present-only Windows scan and cannot represent a phantom devnode. Build the health record during the same PnP walk that currently computes present.
If a lossy compatibility adapter is temporarily unavoidable, name it explicitly (for example available_port_infos_lossy()) and do not use it in deploy, monitor, daemon inventory, or auto-selection.
Migrate crates/fbuild-cli/src/cli/port_scan.rs to render health, problem code, and instance ID. A phantom endpoint should be visible and clearly marked as not selectable.
Migrate the daemon device inventory so cached device records retain health instead of caching only port name/VID/PID.
Audit every caller with:
rg -n "available_ports\(" crates
At minimum classify the direct calls in CLI serial probing, daemon device management, fbuild-serial board/bootloader watching, Teensy discovery, RP2040 discovery, and platform deployers. Migrate any caller that chooses a user device. Tool-specific probes may remain raw only when the PR explains why health metadata is irrelevant.
Keep selection policy out of the low-level enumerator. The enumerator reports facts; family deployers decide how known-unhealthy devices affect their recovery state machine.
Required diagnostics
A Windows port-scan row must distinguish at least:
healthy and present;
present with problem code;
phantom/non-present;
unknown health.
When known, include the canonical instance ID and Config Manager problem code. Do not report a phantom record as merely ?available.?
Tests
Tests must not depend on the contributor's real Device Manager state. Extract classification/merge logic behind testable functions and use fixtures for:
This child is a model-and-propagation change, not a selection-policy change. Keep DetectedPort public and value-like (Clone, Debug, equality where practical), with public info, health, instance_id, and parent_instance_id fields or equivalent accessors. On non-Windows, wrap the existing serialport result as Unknown; do not return raw SerialPortInfo from the blessed API on one platform and enriched records on another.
Implement one pure classification seam (for example PnpObservation -> PortHealth) and test it without Windows hardware. CM_Get_DevNode_Status success plus problem 0 is healthy; successful status with non-zero problem is PresentProblem; a non-present/phantom observation is Phantom; failure or unavailable metadata is Unknown. Keep the canonical instance ID unmodified for matching. A UI renderer may escape it, but must not write the escaped form back into the record.
Caller decision table
Migrate these callers to the enriched API in this PR because they list, cache, monitor, match, or select user devices:
Caller
Required treatment
fbuild-cli/src/cli/port_scan.rs
render health, problem code, and instance ID; annotate known-unhealthy rows as selectable=no without hiding them
fbuild-cli/src/cli/deploy.rs and serial_probe.rs
retain health through monitor/list/find decisions; never implicitly select a known-unhealthy endpoint
fbuild-daemon/src/device_manager.rs
retain health and identities in discovered and cached device state
fbuild-serial/src/bootloader_watcher.rs and boards.rs
consume enriched records; exclude only at the policy point if opening/selecting a known-unhealthy port
fbuild-deploy/src/rp2040.rs and teensy/port_discovery.rs
carry enriched facts forward; RP2040's actual rejection/recovery behavior remains #1147
Audit remaining direct serialport::available_ports() calls individually. probe_rs, LPC, ESP32-native, or similarly tool-specific probes may stay raw only if the PR lists the exact function and explains why it does not select a CDC endpoint or cannot use PnP health. Never silently leave a direct user-device selection path raw.
Do-not-merge checks
No DetectedPort -> SerialPortInfo conversion may occur before daemon cache, monitor selection, or family deployer candidate cataloguing.
A phantom composite/Teensy record must still render in scan output.
Outcome
Make port health a first-class part of fbuild's serial-device model. Windows already discovers whether a COM devnode is live, phantom, or in a Config Manager problem state; this issue stops that information from being discarded before device selection.
This is the foundation for #1147. It must land before RP2040 deployment tries to reject stale CDC endpoints.
Confirmed failure
fbuild_serial::ports::available_ports()currently inspects Windows PnP state and intentionally retains unusual/non-present USB COM records for diagnostic compatibility with #962. It then returns onlyserialport::SerialPortInfo.That return type loses:
A stale RP2040 entry can therefore look identical to a live
COM12later in deployment. This is a data-model bug, not an RP2040 timeout bug.Required data model
Add a public record in
crates/fbuild-serial/src/ports.rs(names may vary, semantics may not):Classification rules:
HealthyPresent: Windows says the devnode is present and its problem code is zero.PresentProblem: present is true and the problem code is non-zero.Phantom: present is false, regardless of whether Windows also reports a problem code.Unknown: the host does not expose equivalent metadata (normal on non-Windows), the endpoint is non-USB, or the Windows metadata query failed.is_known_unhealthy().Unknownis not the same as unhealthy; existing non-Windows behavior must continue and consumers can use an openability probe.Implementation directions
crates/fbuild-serial/src/ports.rsso the blessed enumeration API returnsDetectedPort, not a lossySerialPortInfo.present_usb_problem_devices(): that helper uses a present-only Windows scan and cannot represent a phantom devnode. Build the health record during the same PnP walk that currently computespresent.available_port_infos_lossy()) and do not use it in deploy, monitor, daemon inventory, or auto-selection.crates/fbuild-cli/src/cli/port_scan.rsto render health, problem code, and instance ID. A phantom endpoint should be visible and clearly marked as not selectable.fbuild-serialboard/bootloader watching, Teensy discovery, RP2040 discovery, and platform deployers. Migrate any caller that chooses a user device. Tool-specific probes may remain raw only when the PR explains why health metadata is irrelevant.Required diagnostics
A Windows port-scan row must distinguish at least:
When known, include the canonical instance ID and Config Manager problem code. Do not report a phantom record as merely ?available.?
Tests
Tests must not depend on the contributor's real Device Manager state. Extract classification/merge logic behind testable functions and use fixtures for:
Unknownbehavior;Run:
If a name filter matches no tests, run the whole package and record that command instead.
Acceptance criteria
Unknownplus existing openability checks.rg -n "available_ports\(" cratesand explains every selection path left on raw/lossy enumeration.Non-goals
Related issues
Implementation handoff: exact migration boundary
This child is a model-and-propagation change, not a selection-policy change. Keep
DetectedPortpublic and value-like (Clone,Debug, equality where practical), with publicinfo,health,instance_id, andparent_instance_idfields or equivalent accessors. On non-Windows, wrap the existing serialport result asUnknown; do not return rawSerialPortInfofrom the blessed API on one platform and enriched records on another.Implement one pure classification seam (for example
PnpObservation -> PortHealth) and test it without Windows hardware.CM_Get_DevNode_Statussuccess plus problem0is healthy; successful status with non-zero problem isPresentProblem; a non-present/phantom observation isPhantom; failure or unavailable metadata isUnknown. Keep the canonical instance ID unmodified for matching. A UI renderer may escape it, but must not write the escaped form back into the record.Caller decision table
Migrate these callers to the enriched API in this PR because they list, cache, monitor, match, or select user devices:
fbuild-cli/src/cli/port_scan.rshealth, problem code, and instance ID; annotate known-unhealthy rows asselectable=nowithout hiding themfbuild-cli/src/cli/deploy.rsandserial_probe.rsfbuild-daemon/src/device_manager.rsfbuild-serial/src/bootloader_watcher.rsandboards.rsfbuild-deploy/src/rp2040.rsandteensy/port_discovery.rsAudit remaining direct
serialport::available_ports()calls individually.probe_rs, LPC, ESP32-native, or similarly tool-specific probes may stay raw only if the PR lists the exact function and explains why it does not select a CDC endpoint or cannot use PnP health. Never silently leave a direct user-device selection path raw.Do-not-merge checks
DetectedPort -> SerialPortInfoconversion may occur before daemon cache, monitor selection, or family deployer candidate cataloguing.--admin, PnP mutation, RP2040 BOOTSEL behavior, or a return-port policy; those are feat(windows): add scoped elevated USB PnP recovery helper #1148/fix(rp2040): reject phantom CDC ports and give recoverable BOOTSEL guidance #1147.