Skip to content

feat(hid): synchronous Transport, MockTransport, and HID backend - #1

Merged
RaapTechllc merged 1 commit into
mainfrom
claude/project-status-review-30zcqb
Aug 31, 2026
Merged

RaapTechllc merged 1 commit into
mainfrom
claude/project-status-review-30zcqb

Conversation

@RaapTechllc

Copy link
Copy Markdown
Owner

First M1 slice: the transport foundation, built test-first. Scope is deliberately narrow — descriptor loading and report decoding are the next slice.

What this adds

bosun-hid

  • Transport — synchronous and policy-free, per AGENTS.md. No Tokio.
  • ReadOutcome — an elapsed timeout is a distinct outcome rather than a zero-length report, so a poll loop cannot silently confuse "nothing happened" with "the device went away".
  • DeviceMatch / select_index / select — match on VID, PID and usage page, then open the enumerated path of the interface that matched (PLAN-REVIEW #8). Matching on VID/PID alone opens whichever collection the OS listed first, which for a composite device is usually the wrong one.
  • MockTransport — replays scripted reports, timeouts, and disconnects, and records every write. An exhausted script reports a disconnect, which models an unplug and keeps a reconnect loop from spinning against an empty mock.
  • HidTransporthidapi over the stock OS HID stack (ADR-0001). Backend errors are flattened into HidError::Backend so nothing above this crate names hidapi types.

bosunctl

  • device list renders enumerated interfaces along with the path used to open them, filtered by --vid / --pid / --usage-page given on the command line. No product identifiers are compiled in, so the G13 stays data.
  • Logs go to stderr; stdout stays a clean, pipeable listing.

Hardware tests (crates/bosun-hid/tests/hardware.rs)

#[ignore]d and additionally gated on BOSUN_HW=1, per CONTRIBUTING.md. One of them is risk R1 directly: open an interface and prove input reports reach a second reader while Logitech Gaming Software holds its own handle.

cargo run -p bosunctl -- device list --vid 0x046D --pid 0xC21C --usage-page 0xFF00
BOSUN_HW=1 BOSUN_HW_PATH='<path from above>' \
  cargo test -p bosun-hid --test hardware -- --ignored --nocapture

Testing

27 unit tests, covering the failure modes that matter here:

  • a matching VID/PID with the wrong usage page is rejected;
  • select picks the vendor collection out of a composite device and is deterministic when several interfaces match;
  • no Duration — including Duration::MAX — ever converts to a negative timeout, which hidapi would read as "block forever";
  • a scripted timeout consumes only its own entry; an exhausted script keeps reporting a disconnect;
  • a short buffer is refused rather than truncating a report;
  • the trait is object-safe, since layers above hold Box<dyn Transport>;
  • out-of-range and bare-hex identifiers (0x10000, 65536, 046D) are rejected rather than silently truncated into the wrong device.

The suite was mutation-checked: removing the usage-page criterion and swapping the saturating timeout conversion for a wrapping cast each fail the tests written to catch them.

Verification caveat — please read

The sandbox this was written in has an egress policy that blocks crate downloads from static.crates.io, so cargo build/clippy/test could not run at all.

What was verified locally, by compiling the real sources directly with rustc --test and clippy-driver:

  • 27 tests passing across device, transport, mock, and listing;
  • clippy clean at -D warnings;
  • rustfmt --check clean on every file.

What was not verified locally: anything touching hidapi, thiserror, or clap. hid.rs, error.rs, main.rs, and the crate-root doctest compile for the first time in CI. Treat the three-OS CI run as the real gate on this PR; I'll drive any failures to green.


Generated by Claude Code

First M1 slice: the transport foundation, built test-first.

- `Transport` is synchronous and policy-free. `ReadOutcome` makes an elapsed
  timeout a distinct outcome so a poll loop cannot confuse "nothing happened"
  with "the device went away".
- `DeviceMatch`/`select_index` match on VID, PID, and usage page, then the
  backend opens the enumerated path of the interface that matched
  (PLAN-REVIEW #8). VID/PID alone would open the wrong collection of a
  composite device.
- `MockTransport` replays scripted reports, timeouts, and disconnects and
  records every write, so layers above this crate need no hardware. An
  exhausted script reports a disconnect, which keeps a reconnect loop from
  spinning against an empty mock.
- `HidTransport` wraps `hidapi` over the stock OS HID stack (ADR-0001) and
  flattens backend errors so nothing above this crate names `hidapi` types.
- `bosunctl device list` renders enumerated interfaces with the path used to
  open them, filtered by criteria given on the command line. No product
  identifiers are compiled in.
- Hardware tests are `#[ignore]`d and gated on `BOSUN_HW=1`. One of them is
  risk R1: open an interface and prove input reports reach a second reader
  while LGS holds its own handle.

Scope held to M1: no Tokio, no engine, no adapters, no profiles, no descriptor
loading yet. Descriptor loading and report decoding are the next slice.

Verification: 27 unit tests pass and clippy is clean at `-D warnings`, but
they were run by compiling the sources directly with rustc, because this
sandbox's egress policy blocks crate downloads from static.crates.io. The
hidapi, thiserror, and clap layers therefore compile for the first time in
CI, not locally. `rustfmt --check` is clean on every file.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0157Fe2eqqST8yYonUyYbBqk
@RaapTechllc
RaapTechllc marked this pull request as ready for review August 31, 2026 11:44
@RaapTechllc
RaapTechllc merged commit 80bdf9a into main Aug 31, 2026
4 checks passed
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-31T11:46:48.421554Z 66be855 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants