Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,26 @@ jobs:
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- name: Assert Windows MSVC host
if: runner.os == 'Windows'
shell: pwsh
run: |
$info = rustc -vV | Out-String
if ($info -notmatch 'host: x86_64-pc-windows-msvc') {
throw "expected host x86_64-pc-windows-msvc, got:`n$info"
}
- run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets --all-features -- -D warnings
- run: cargo test --workspace --all-features
- name: bosun-hid has no Tokio
if: runner.os == 'Linux'
run: |
if cargo tree -p bosun-hid | grep -qi tokio; then
echo "tokio must not appear in bosun-hid" >&2
exit 1
fi
- name: Hardware tests skip without BOSUN_HW
run: cargo test -p bosun-hid --test hardware -- --ignored --nocapture

supply-chain:
runs-on: ubuntu-latest
Expand Down
11 changes: 11 additions & 0 deletions .scratch/m1-software/issues/01-descriptor-load.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# 01: Load and reject G13 descriptors

**What to build:** The committed G13 TOML loads as a descriptor with 34 literal key names, a match block, axes, screen, rgb, leds, and ignored key bits. Missing fields, wrong types, and range syntax return `Err` and never panic.

**Blocked by:** None (can start immediately)

**Status:** ready-for-agent

- [ ] Valid `devices/logitech-g13.toml` loads with 34 keys and the required blocks (AC-5)
- [ ] Missing field, wrong type, and `G1..=G22`-style range syntax each return `Err`
- [ ] G13 VID/PID literals are not compiled into non-test `bosun-hid` policy
13 changes: 13 additions & 0 deletions .scratch/m1-software/issues/02-codec-and-goldens.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 02: Decode edges from golden reports

**What to build:** An 8-byte report ID `0x01` decodes to named key downs/ups and 0–255 axis values. Bits 22, 23, and 36–39 emit no key event. Committed goldens cover every named control plus stick center, cardinals, corners, and diagonals. Physical LEFT/DOWN/TOP mapping stays pending.

**Blocked by:** 01

**Status:** ready-for-agent

- [ ] Bits `0..=21` and `24..=35` map to the 34 descriptor names; pressed = 1 (AC-6)
- [ ] Events are edges against the previous report
- [ ] Flipping each ignored bit yields an empty event stream
- [ ] Fixture files exist and drive the decode tests (AC-10)
- [ ] `docs/hardware-notes.md` still says LEFT/DOWN/TOP are pending
13 changes: 13 additions & 0 deletions .scratch/m1-software/issues/03-cli-info-and-outputs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# 03: info, rgb, leds, and lcd test over mock

**What to build:** `bosunctl device info` prints the loaded descriptor. `rgb`, `leds`, and `lcd test` send the measured reports over a transport. Mock tests assert exact bytes. No command writes feature report 6.

**Blocked by:** 01, 02

**Status:** ready-for-agent

- [ ] `info` prints id, match, 34 keys, axes, screen, rgb, and leds (AC-7 software path)
- [ ] `rgb R G B` sends feature report 7 and reads it back (AC-8)
- [ ] `leds MASK` sends feature report 5 (AC-8)
- [ ] `lcd test` sends one 992-byte report 3: byte 0 = `0x03`, bytes 1–31 zero, rows 43–47 zero (AC-8)
- [ ] Across the CLI surface, no feature write has report ID 6
12 changes: 12 additions & 0 deletions .scratch/m1-software/issues/04-watch-record-reconnect.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# 04: watch, record, and mock reconnect

**What to build:** `watch` prints decoded named key and axis events over `MockTransport`. `record` writes raw 8-byte reports that decode back to the same events. After a scripted disconnect, watch prints one reconnect line and resumes within 2 s of mock time.

**Blocked by:** 02, 03

**Status:** ready-for-agent

- [ ] `watch` over mock prints the expected named events (AC-7)
- [ ] `record` output round-trips through the decoder (AC-7)
- [ ] Scripted disconnect then reports: one reconnect line, events resume, elapsed ≤ 2 s (AC-9)
- [ ] Existing `device list` filters still pass (AC-2)
15 changes: 15 additions & 0 deletions .scratch/m1-software/issues/05-gates-and-hygiene.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# 05: CI gates, license hygiene, Windows MSVC

**What to build:** Format, clippy with warnings denied, workspace tests, and cargo-deny are green without `BOSUN_HW=1`. Hardware tests stay ignored and skip unless that gate is set. Windows CI asserts the MSVC host triple. No GPL identifiers.

**Blocked by:** 01, 02, 03, 04

**Status:** ready-for-agent

- [ ] `cargo fmt --all -- --check` (AC-11)
- [ ] `cargo clippy --workspace --all-targets --all-features -- -D warnings` (AC-11)
- [ ] `cargo test --workspace --all-features` (AC-11)
- [ ] `cargo deny check` (AC-12)
- [ ] Ignored hardware tests skip with a clear message unless `BOSUN_HW=1`
- [ ] Windows MSVC expectation documented; `windows-latest` asserts `x86_64-pc-windows-msvc` (AC-13)
- [ ] `cargo tree -p bosun-hid` has no Tokio (AC-3)
49 changes: 49 additions & 0 deletions .scratch/m1-software/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Plan — M1 remaining SOFTWARE ACs (mockable)

Scope is locked to mockable paths. This plan does **not** unpark AC-R1, engine, adapters, Tauri, or live finger-on-key work.

## Files / modules

### Add

- `bosun-hid` descriptor loader (generic TOML types + validation)
- `bosun-hid` codec (input edge events, LCD/RGB/LED report builders)
- `crates/bosun-hid/tests/fixtures/g13/` golden 8-byte reports
- `bosunctl` library surface so commands run over `impl Transport`
- CLI subcommands: `info`, `watch`, `record`, `rgb`, `leds`, `lcd test`
- CONTRIBUTING + CI notes for Windows MSVC (AC-13)

### Do not rewrite

- `Transport`, `HidTransport`, `MockTransport`
- device match/select
- `g13_probe`
- `bosunctl device list` filters
- `crates/bosun-hid/tests/hardware.rs` (keep `#[ignore]` + `BOSUN_HW=1`)

## Interfaces

- `load_descriptor(path) -> Result<DeviceDescriptor>`
- `DeviceDescriptor` exposes match, literal keys, axes, screen, rgb, leds, ignored key bits
- `Decoder::from_descriptor` + `decode(&[u8]) -> Result<Vec<InputEvent>>`
- `rgb_feature_report` / `leds_feature_report` / `lcd_test_report`
- CLI commands accept an opener `FnMut() -> Result<impl Transport>` so tests inject `MockTransport`
- `watch` reprints after `Disconnected` by re-opening; one `reconnect` line; no 2 s sleep in mock

## Test list

- AC-5: valid G13 file; missing field; wrong type; range syntax rejected; no panic
- AC-6: golden replay of 34 keys + stick envelope; ignored bits 22, 23, 36–39 emit no key events
- AC-7: `watch` prints named events over mock; `record` file round-trips through decoder
- AC-8: mock exact bytes for RGB (report 7), LEDs (report 5), LCD (992-byte report 3); no report-ID-6 write
- AC-9: scripted disconnect then reports → one reconnect line, events resume, elapsed ≤ 2 s
- AC-10: fixture files present and drive AC-6; hardware-notes LEFT/DOWN/TOP stay pending
- AC-11/12/13: fmt, clippy `-D warnings`, workspace tests, cargo-deny; ignored HW tests skip without `BOSUN_HW=1`; Windows MSVC documented/CI-asserted

## Protocol source

`docs/BOSUN-PLAN.md` §2.4 and `AGENTS.md` Measured G13 protocol only.

## Stop condition

If a ticket would require live hardware, physical LEFT/DOWN/TOP mapping, or any PARKED crate — stop.
83 changes: 83 additions & 0 deletions .scratch/m1-software/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Spec: M1 remaining SOFTWARE acceptance criteria

**Status:** ready-for-agent
**Parent:** Heavy Lift brief — Bosun M1 remaining SOFTWARE ACs (mockable paths)
**Authoritative ACs:** `docs/PRD.md` §5, software paths only

## Problem Statement

M1 already has a synchronous HID transport, a scripted mock, device match, and `bosunctl device list`. What is still missing is everything that turns those reports into named device behavior on CI: loading the G13 descriptor, decoding kept key bits into edge events, driving `info|watch|record|rgb|leds|lcd test` over `MockTransport`, recovering from a scripted disconnect, and committing golden fixtures — without touching live hardware or PARKED milestones.

## Solution

A generic descriptor loader and codec live in `bosun-hid` as data-driven types. `bosunctl` becomes a thin library so each device command can be exercised with `MockTransport`. Golden 8-byte reports, derived from the measured bitmap in `docs/BOSUN-PLAN.md` §2.4, drive decode tests. Physical LEFT/DOWN/TOP mapping and AC-R1 stay pending.

## User Stories

1. As a CI runner, I want descriptor load tests to pass without a G13 attached, so that AC-5 is checkable on every OS.
2. As a CI runner, I want a malformed descriptor to return `Err` rather than panic, so that bad device data cannot crash the process.
3. As a CI runner, I want range-syntax key lists rejected, so that only literal key arrays ship.
4. As an operator, I want the committed G13 descriptor to name 34 keys plus match, axes, screen, rgb, and leds, so that the device is data rather than compiled policy.
5. As a decoder caller, I want bits 0..=21 and 24..=35 mapped to those 34 names, so that a pressed bit becomes a named key.
6. As a decoder caller, I want down/up events from a diff against the previous report, so that watchers see edges rather than raw bitmaps.
7. As a decoder caller, I want toggling bits 22, 23, 36, 37, 38, and 39 to emit no key event, so that device-state flags stay silent.
8. As a decoder caller, I want stick bytes 1 and 2 as 0–255 axes, so that joystick motion is named and numeric.
9. As a CI runner, I want golden fixtures to replay through the decoder, so that AC-6 does not depend on a finger on a key.
10. As an operator, I want `bosunctl device info` to print the loaded descriptor, so that I can confirm match and capabilities before opening a handle.
11. As an operator, I want `bosunctl device watch` to print named key and axis events, so that I can see decoded input.
12. As a test, I want watch to run over `MockTransport`, so that AC-7 is mockable.
13. As an operator, I want `bosunctl device record` to write raw 8-byte reports to a file, so that later hardware captures have a stable format.
14. As a test, I want a recorded file to decode back to the same events, so that record round-trips.
15. As an operator, I want `bosunctl device rgb R G B` to send feature report 7 and read it back, so that backlight writes are exercisable.
16. As an operator, I want `bosunctl device leds MASK` to send feature report 5, so that M-key LEDs are exercisable.
17. As an operator, I want `bosunctl device lcd test` to send one 992-byte report 3 with the measured header and hidden rows zero, so that the LCD path is proven without widgets.
18. As a reviewer, I want no CLI command to write feature report 6, so that the LGS identity channel stays untouched.
19. As a test, I want mock writes compared to exact expected bytes, so that AC-8 does not need a glowing keypad.
20. As an operator, I want watch to print one reconnect line after a disconnect and then resume events, so that hot-plug recovery is visible.
21. As a test, I want that recovery to finish within 2 s on a scripted mock without sleeping, so that AC-9 is mockable.
22. As a reviewer, I want one raw fixture per named control plus stick center, cardinals, corners, and diagonals, so that AC-10 has committed goldens.
23. As Kyle, I want `docs/hardware-notes.md` to keep LEFT/DOWN/TOP as pending until I measure them, so that agents do not invent physical mapping.
24. As a CI runner, I want fmt, clippy `-D warnings`, workspace tests, and cargo-deny green without `BOSUN_HW=1`, so that AC-11 and AC-12 hold.
25. As a Windows operator, I want the MSVC host triple documented and asserted on `windows-latest`, so that AC-13 is a software path.
26. As a contributor, I want hardware tests to stay ignored and to skip unless `BOSUN_HW=1`, so that `--ignored` on a bare machine stays honest.
27. As a license reviewer, I want no GPL identifiers or dependencies, so that AC-12 holds.
28. As a maintainer, I want existing device-list and transport tests unchanged in behavior, so that AC-2, AC-3, and AC-4 do not regress.

## Implementation Decisions

- Descriptor and codec live in `bosun-hid`. Types are generic. G13 VID/PID literals stay in descriptor data, fixtures, and tests — not crate policy.
- The G13 TOML gains an `ignored_key_bits` list so the ignore set is data. Keys remain a literal array in listed order; the codec maps kept bits in ascending index onto that array.
- `keys.len() + ignored_key_bits.len()` must equal 40 (bits 0..=39). Duplicate names, empty names, and any key token containing `..` are rejected.
- `InputEvent` is `KeyDown`, `KeyUp`, or `Axis { name, value }`. The first report diffs against "no previous keys / no previous axes": pressed keys emit down; current axis values emit.
- LCD, RGB, and LED builders live next to the codec. RGB is `[0x07, r, g, b, 0x00]`. LEDs are `[0x05, mask, 0, 0, 0]`. LCD test is 992 bytes, byte 0 = `0x03`, bytes 1..=31 zero, a fixed visible-row pattern, rows 43..=47 zero. Packing uses `offset = col + (row >> 3) * 160` and `bit = 1 << (row & 7)`.
- `bosunctl` exposes a library API. Commands take a writer and, where I/O is needed, an opener that returns a `Transport`. Production `main` opens `HidTransport` from the descriptor match (or an explicit path). Tests inject `MockTransport`.
- `watch` treats `Disconnected` as "drop the handle and open again". It prints exactly one `reconnect` line per successful reopen after a drop. Reopen is synchronous; tests assert elapsed time ≤ 2 s with no sleep.
- `record` writes one report per line as eight space-separated lowercase hex bytes. That file is the round-trip fixture format.
- `info` prints the descriptor (id, match, keys, axes, screen, rgb, leds) without requiring a live handle.
- `--descriptor` defaults to `devices/logitech-g13.toml`.
- Feature report 6 has no builder and no CLI path.
- Golden fixtures are synthetic protocol reports under `crates/bosun-hid/tests/fixtures/g13/`. They do not claim physical thumb-button identity or a measured stick envelope.
- Hardware tests stay `#[ignore]` and keep the `BOSUN_HW=1` gate. CI on Windows asserts `x86_64-pc-windows-msvc`. CONTRIBUTING documents the rustup-vs-Chocolatey PATH rule.
- No new ADR: D11 and ADR-0001 already lock descriptor-driven HID. No Tokio. No engine, adapters, Tauri, or empty crates.

## Testing Decisions

- Test behavior at public seams: `load_descriptor` / `parse_descriptor`, `Decoder::decode`, CLI command functions, and `MockTransport` write logs.
- Expected bytes and event names come from `docs/BOSUN-PLAN.md` §2.4 and committed fixture files — not from re-encoding the implementation.
- Prefer existing test style: table-driven unit tests next to the module, CLI parse tests like the current `device list` tests.
- One dedicated test flips each ignored bit after a rest report and expects an empty event stream.
- One CLI-surface test inspects every command's mock feature writes and asserts none begin with `0x06`.
- Do not add a live-hardware pass/fail to `docs/hardware-notes.md`.

## Out of Scope

- AC-R1 live shared-input while LGS runs
- Physical LEFT/DOWN/TOP mapping and measured joystick envelope
- Engine, adapters, JSON-RPC, HTTP/WS, `bosund`, Tauri, profiles, LCD widgets
- libusb, WinUSB, Zadig, kexts, `EVIOCGRAB`, exclusive opens
- Reading or translating GPL G13 sources
- Merging the PR

## Further Notes

Wayfinder is not required: the hop is one session of mockable software. Existing transport and list command stay asserted, not rewritten.
29 changes: 29 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Bosun

Userspace HID ownership of a descriptor-driven command pad, starting with the Logitech G13. M1 is foundational transport, descriptor, codec, and CLI — not agent UX.

## Language

**Descriptor**:
A TOML document that names a device's match criteria, keys, axes, screen, RGB, and LEDs. The G13 is data in `devices/logitech-g13.toml`, not compiled crate policy.
_Avoid_: profile, driver, device definition

**Match**:
The VID, PID, and usage page that identify one HID interface. Selection then opens the enumerated path.
_Avoid_: VID/PID-only identity, product name lookup

**Codec**:
The mapping from an 8-byte input report onto named key and axis events, and the packing of LCD, RGB, and M-LED output reports.
_Avoid_: parser, translator, driver logic

**Edge event**:
A key down, key up, or axis change produced by diffing the current report against the previous one.
_Avoid_: poll state, held-key stream, raw bitmap

**Ignored bit**:
A bit in the 40-bit key field that is device state, not a user control. Toggling it emits no key event.
_Avoid_: mask (the incomplete Appendix B mask), filter byte

**Transport**:
A synchronous, policy-free HID channel that moves reports. `MockTransport` scripts those reports for tests with no hardware.
_Avoid_: async HID, exclusive open, libusb backend
12 changes: 11 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test --workspace --all-features
```

New behavior follows RED-GREEN-REFACTOR. Hardware-only tests must be ignored by default and gated by `BOSUN_HW=1`.
New behavior follows RED-GREEN-REFACTOR. Hardware-only tests must be ignored by default and gated by `BOSUN_HW=1`. Running `cargo test -- --ignored` without that variable must skip with a clear message and pass.

## Windows MSVC toolchain (AC-13)

Hardware runs and `windows-latest` CI use the MSVC rustup toolchain. Before any live G13 session, check:

```text
rustc -vV
```

The host line must be `x86_64-pc-windows-msvc`. On Kyle's workstation, `C:\Users\Kyle\.cargo\bin` must precede Chocolatey's Rust shims. If a GNU `cargo` wins PATH, use `rustup run stable cargo ...` instead. CI's `windows-latest` job asserts this host triple.

## Licensing and clean-room protocol

Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Bosun turns the Logitech G13 into a cross-platform command console for AI coding

## Status

Phase 0 is complete on live Windows hardware. The repository is prepared for **M1 only**: a synchronous HID transport, descriptor-driven G13 codec, mock transport, and `bosunctl device` commands.
Phase 0 is complete on live Windows hardware. M1 software paths are mockable: a synchronous HID transport, a G13 TOML descriptor and codec, `MockTransport`, and `bosunctl device list|info|watch|record|rgb|leds|lcd test`. Live shared-input (AC-R1) is still pending a finger-on-key run.

## Non-negotiable boundaries

Expand All @@ -14,3 +14,17 @@ Phase 0 is complete on live Windows hardware. The repository is prepared for **M
- Logitech Gaming Software may overwrite LCD/RGB output. The first M1 hardware test must determine whether shared input reads work while LGS is running.

See `AGENTS.md`, `docs/PLAN-REVIEW.md`, and `docs/BOSUN-PLAN.md` before implementing. The product requirements, M1 acceptance criteria, and open owner decisions are in `docs/PRD.md`.

## CLI

Descriptor path defaults to `devices/logitech-g13.toml`.

```text
bosunctl device list --vid 0x046D --pid 0xC21C --usage-page 0xFF00
bosunctl device info
bosunctl device watch
bosunctl device record --output capture.hex
bosunctl device rgb 255 0 0
bosunctl device leds 0x0F
bosunctl device lcd test
```
Loading
Loading