Skip to content

meta: handoff — port diagnostics (#1279) and RP2350W HIL context #1282

Closed

Description

@zackees

Handoff for the next agent picking up the RP2350W / port-diagnostics thread. Written at the end of a long session so the context does not have to be re-derived.


1. The thing most likely to waste your time

health=phantom in fbuild port scan means the hardware is NOT ATTACHED. It is a leftover Windows registry record, not a broken device.

This cost this session hours. The RP2350W was reported (in #3864) as a wedged devnode needing BOOTSEL and scoped PnP recovery. Windows actually said Present = False, last seen six days earlier. It simply was not plugged in. Compounding it, a genuinely descriptor-failed board (code 43) sat on a different root port, and conflating the two produced the "wedged" reading.

Before attempting any recovery:

Get-PnpDevice -Class Ports | Select-Object Status, Present, FriendlyName
Get-PnpDeviceProperty -InstanceId '<instance>' -KeyName DEVPKEY_Device_LastArrivalDate

fbuild port doctor (now on main) answers this in one command. Use it first.

Absent hardware and broken hardware look identical in a port scan and need opposite responses. --admin PnP recovery on an absent device is a no-op.

2. Current state

Merged to fbuild main:

Open:

FastLED side: #3866 (teensy set_audioClock), #3869 (esp32p4 h264), #3871 (fbuild 2.5.15 pin), #3872 (autoresearch selective-suspend preflight) all merged. #3838 reopened (2 of 3 failure classes remain, upstream). #3870 filed (esp32dev 72KB over budget, not started).

3. Remaining scope on #1279

Not done, roughly in value order:

  1. scan footer + suspend= per port. Nobody reads --help for a subcommand they do not know exists; surfacing doctor where people already look is most of the value. Cheapest item here.
  2. Per-device suspend state. Only the plan-level setting is handled. The per-device layer lives at HKLM\SYSTEM\CurrentControlSet\Enum\<id>\Properties\... (DeviceIdleEnabled, DefaultIdleState, DefaultIdleTimeout). Until this exists, --fix cannot round-trip prior per-device state as the acceptance criteria require.
  3. --json, and --all / --hub scope selectors.
  4. last-seen=<age> — see the dead ends below.

4. Dead ends — do not repeat these

last-seen / DEVPKEY_Device_LastArrivalDate. I implemented it, could not make it work, and removed it rather than ship a field that silently returns None:

  • SetupDiGetDevicePropertyW on the devinfo set returns nothing for a phantom devnode.
  • The iterator's DevInst is invalid for phantoms by construction — CM_Get_DevNode_Status returning CR_NO_SUCH_DEVINST is exactly how pnp_observation classifies one.
  • CM_Locate_DevNodeW(..., CM_LOCATE_DEVNODE_PHANTOM) succeeds (cr=0x0) but the follow-up CM_Get_DevNode_PropertyW returns cr=0x25 (CR_NO_SUCH_VALUE), and the returned devinst=1 looks like the root node rather than the target.
  • The registry copy under ...\Properties\{540b947e-8b40-45bc-a8a2-6a0b894cbda2}\0066 exists but needs elevation, which conflicts with doctor being read-only.
  • PowerShell's Get-PnpDeviceProperty returns it unelevated, so a working path exists — I did not find it. Start by tracing what PowerShell actually calls.

uhubctl on Windows. Does not work — libusb's winusb.sys cannot issue the required control requests. A software VBUS cycle is impossible on Windows without hardware.

Bus reset ≠ VBUS cycle. Device disable/enable and root-hub restart do not recover a descriptor-failed device. This was tried repeatedly on the RP2040 IM411 board (code 43 since mid-July) and never worked. Do not add it to --fix.

5. Local/CI parity (fbuild)

Near-miss reproductions cost two CI cycles this session. Use the exact commands:

soldr cargo clippy --workspace --all-targets -- -D warnings   # the trailing -- -D warnings is load-bearing
soldr cargo fmt --check
uv run python ci/find_direct_subprocess.py --fail
  • Do not use RUSTFLAGS="-D warnings" — it applies to dependencies and breaks their builds; CI never does that (--cap-lints allow).
  • MSRV is 1.75, not 1.94. The root .clippy.toml pins msrv = "1.75" and overrides Cargo.toml's rust-version = "1.94.1". Anything stabilised after 1.75 trips clippy::incompatible_msrv. Option::is_none_or (1.82) bit me.
  • Every subprocess must go through fbuild-core::subprocess wrappers, or carry // allow-direct-spawn: <reason>. run_command_blocking is the sync one.
  • 1000-LOC cap per .rs file — put new commands in their own module.
  • A bare cargo anywhere in a Bash command, including inside a commit message, trips the rust_tool_guard hook. Use git commit -F <file>.

6. RP2350W HIL (FastLED#3864) — still blocked, but only on hardware

Blocker 1 (unreleased fbuild) is resolved: 2.5.15 shipped and is pinned; bash compile rp2350w --examples AutoResearch builds from a clean checkout (flash 1177936, ram 132048).

Remaining, both physical:

  • The RP2350W (2E8A:F00F, serial 2DCB876B587EA334) does not enumerate. A replug was attempted and no new device node appeared at all — not even a code-43 one — so try a different cable and a direct root port rather than the Genesys hub it was on.
  • No ESP32-C6 has ever been attached on this host. The only Espressif device present is an S3 (303A:1001 / 8C:BF:EA:CF:87:B4) on COM9. --net-peer and --ota cannot run without a real C6.

Also: #3864's five handoff commands all pass --upload-port COM17. Drop it. For RP-series that pins deploy to one record and defeats fbuild's own exclusion of unhealthy CDC records plus its BOOTSEL-volume fallback (#1147). Use --upload-port UF2=<volume> if the board is in BOOTSEL with no working CDC.

7. Two process notes

Verify against real hardware, not just tests. Two bugs this session passed their unit tests while being completely broken in practice: a capture_output=False default that left a probe silently returning nothing, and the last-seen property that always returned None. Both were caught only by running the thing. Mocked tests cannot catch a wrong API call.

A merged PR's branch is not a safe place to push. #1281 exists because I pushed a commit to #1280's branch minutes after it merged. git push succeeded, the branch ref updated, but the PR head stayed frozen and no CI ran — which looks exactly like GitHub lag. Check gh pr view --json state before assuming a push will be picked up.

Related

Activity

  1. zackees commented on Aug 7, 2026

    @zackees
    MemberAuthor

    Update: the last-seen dead end has a way through

    Section 4 says a working unelevated path exists but I had not found it. I have now — recording it so the next attempt starts from the answer rather than the Win32 API rabbit hole.

    Get-PnpDeviceProperty reads it unelevated, for phantom devices, today:

    > Get-PnpDeviceProperty -InstanceId 'USB\VID_2E8A&PID_F00F\2DCB876B587EA334' `
          -KeyName 'DEVPKEY_Device_LastArrivalDate'
    KeyName : DEVPKEY_Device_LastArrivalDate
    Type    : FileTime
    Data    : 8/1/2026 10:27:59 PM

    So the CIM path succeeds where all four Win32 approaches failed. That means no new Rust FFI is needed — this can reuse the same PowerShell-through-fbuild_core::subprocess pattern already used for powercfg and MSPower_DeviceEnable.

    The catch, measured

    query cost
    all 17 ports, one Get-PnpDeviceProperty per device 12,548 ms
    single device, one key 933 ms
    single device, both keys in one call 725 ms

    Get-PnpDeviceProperty is ~0.74 s per device and does not batch across devices, so the full listing is not viable. Win32_PnPEntity does not expose the property, and the registry copy still needs elevation.

    Therefore

    Implement last-seen only when --port is given — one call, both keys, ~725 ms. That is precisely the case where someone is asking "is this board broken or just unplugged?", so the information lands where it is wanted. Omit it from the all-ports listing rather than paying 12.5 s, and say so in the output rather than silently dropping it.

    Same shape as the per-device suspend decision: a diagnostic can afford ~1 s for a targeted question; a listing cannot.

    Convert Data with ([DateTimeOffset]$d.Data).ToUnixTimeSeconds() and parse the integer Rust-side — that sidesteps locale-dependent date formatting entirely.

  2. zackees commented on Aug 7, 2026

    @zackees
    MemberAuthor

    The port-diagnostics half of this handoff is complete — #1279 closed via #1280, #1281, #1283, #1284 (all merged).

    What changed since this was written

    §4's last-seen dead end has a way through, and it is implemented. The four Win32 approaches all fail for phantom devnodes as documented, but the CIM provider behind Get-PnpDeviceProperty answers unelevated, for phantoms. No new FFI — it reuses the PowerShell-through-fbuild_core::subprocess pattern. Measured ~0.74 s/device with no batching, so it runs only for --port.

    §1's lesson is now enforced by the tool. fbuild port doctor states in one command that a board is not attached, when it was last seen, and that a stale record is not a fault — and keeps unassociated code-43 devices in their own section so they are never blamed on the wrong port.

    §5's local/CI parity notes held up and earned their keep: the -- -D warnings requirement and the 1.75 clippy MSRV each cost a CI cycle before I applied them. Two additions worth carrying forward:

    • The 1000-LOC file cap applies to files a PR touches (≈10 pre-existing files exceed it). port_doctor.rs crossed it at 1041 and had to be split.
    • The merge guard resolves a CodeRabbit thread only on an in-thread reply (in_reply_to_id), not a PR-level comment.

    Still open from this handoff

    The RP2350W HIL work (FastLED/FastLED#3864) is untouched and still blocked on physical hardware:

    • The RP2350W (2E8A:F00F / 2DCB876B587EA334) does not enumerate. A replug produced no new device node at all — not even a code-43 one — so try a different cable and a direct root port rather than the Genesys hub.
    • No ESP32-C6 has ever been attached on this host. --net-peer / --ota cannot run without one.
    • Drop --upload-port COM17 from #3864's five commands; for RP-series it defeats fbuild's own BOOTSEL fallback (fix(rp2040): reject phantom CDC ports and give recoverable BOOTSEL guidance #1147).

    fbuild port doctor --port COM17 now reports exactly that state, which is the check to run first next time.

    Closing this as a handoff; the hardware work continues on FastLED/FastLED#3864.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions