Skip to content

fbuild-side hardening from #834 — daemon open_port + write_to_port still passes None / single-write (followup) #893

Description

@zackees

Follow-up to #834 (closed because the FastLED-side workaround unblocks the AutoResearch flow). The proposal's fbuild-side hardening items did not land; this issue tracks them as a focused unit so they're not lost.

What's still open on the fbuild side

1. Three daemon call sites still pass None for the board-family hint

The proposal's primary acceptance criterion is that Esp32NativeUsbCdc (VID 303A:*) opens with DTR=false, RTS=false instead of falling back to (true, true). The wiring exists (fbuild_serial::boards::family_for_vid_pid) but isn't called from the three daemon attach paths:

  • crates/fbuild-daemon/src/handlers/websockets.rs:152 — WebSocket Attach message hands None
  • crates/fbuild-daemon/src/handlers/operations/monitor.rs:263 — HTTP monitor request hands None
  • crates/fbuild-daemon/src/handlers/operations/deploy.rs:950 — post-deploy monitor attach hands None

Each call site should:

  1. Probe the OS port for its VID/PID (the daemon already has fbuild_serial::probe).
  2. Look up BoardFamily via family_for_vid_pid.
  3. Pass Some(family) to SharedSerialManager::open_port so the family's idle_dtr_rts applies.
  4. Log the inferred family + DTR/RTS on every attach (tracing::info!) — issue's debuggability ask.

For the deploy path specifically (third site above), prefer carrying the known deploy-context family forward — VID/PID probing post-flash is racy on Windows USB re-enumeration.

2. SerialMonitor.write() ack vs data frame race

crates/fbuild-python/src/serial_monitor.rs::write() sends a Write frame and waits for the next WebSocket frame. If a serial Data frame arrives before the WriteAck, it returns 0 (looks like a failed write) and leaves the ack unconsumed for the next unrelated read to swallow. Should keep reading until it sees the matching WriteAck (or timeout/error), interleaving any Data frames into the existing read-side buffer instead of treating them as the response.

3. write_to_port() partial-write handling

crates/fbuild-serial/src/manager.rs::write_to_port() calls serial.write(data) once and acks the returned byte count. On a partial write that should either loop to write the full buffer (write_all) or fail loudly with a partial-write error including requested vs accepted byte counts. Silent partial writes are how JSON-RPC pings vanish without a trace.

Acceptance criteria

  • All three open_port(...None) daemon call sites infer family from the OS port's VID/PID (or carry the deploy-context family forward), with a tracing::info! line on every attach showing port=<...> family=<...> dtr=<...> rts=<...>.
  • SerialMonitor.write() returns the requested byte count on success or a typed error on partial/timeout; Data frames arriving during the wait window do not corrupt the ack-matching state.
  • write_to_port() either writes the full buffer or returns Err; no silent partial-byte ack.

Why split out

#834 was closed because the user-visible symptom (AutoResearch RPC round-trip) is fixed via FastLED-side mitigation. The fbuild-side hardening listed above is the infrastructure the next downstream consumer would otherwise re-trip. Splitting it out keeps the closed-issue tracking accurate while making the remaining work concrete.

cc-ref: #834

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions