docs(04-01): complete USB manager plan — goroutine-per-device, VID/PID enumeration
- 10/10 tests pass with -race flag - goroutine count stable across 5 replug cycles - T-04-01 read buffer cap mitigated - Known stubs: VID/PID placeholder pending hardware 2026-04-13
This commit is contained in:
parent
82eaf6bed7
commit
63b66f7a94
1 changed files with 110 additions and 0 deletions
110
.planning/phases/04-usb-manager-label-printing/04-01-SUMMARY.md
Normal file
110
.planning/phases/04-usb-manager-label-printing/04-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
phase: 04-usb-manager-label-printing
|
||||
plan: "01"
|
||||
subsystem: usb
|
||||
tags: [usb, serial, goroutine, enumeration, reconnect]
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides: [internal/usb package, USBManager, DeviceSpec, DeviceEvent, Command]
|
||||
affects: [internal/api/router.go (future SSE integration), main.go (future Manager.Start call)]
|
||||
tech_stack:
|
||||
added: [go.bug.st/serial v1.6.4, github.com/creack/goselect v0.1.2]
|
||||
patterns: [goroutine-per-device, context+done-channel teardown, injectable test seams]
|
||||
key_files:
|
||||
created:
|
||||
- internal/usb/device.go
|
||||
- internal/usb/enumerate.go
|
||||
- internal/usb/manager.go
|
||||
- internal/usb/device_test.go
|
||||
- internal/usb/manager_test.go
|
||||
- internal/usb/mock_port_test.go
|
||||
modified:
|
||||
- go.mod
|
||||
- go.sum
|
||||
decisions:
|
||||
- "VID/PID enumeration via system_profiler SPUSBDataType JSON + injectable sysProfilerCmd for tests; gracefully returns empty map on Linux/CI"
|
||||
- "serialPort interface abstraction allows test injection via noopSerialOpener without hardware"
|
||||
- "mockPort.Read() blocks on closeCh channel until Close() is called — mirrors real blocking serial Read behavior"
|
||||
- "Read buffer capped at 4096 bytes per T-04-01 threat mitigation before forwarding to command handler"
|
||||
- "pollInterval injected at newManagerForTest level (20ms in tests vs 2s default) for fast deterministic cycling"
|
||||
metrics:
|
||||
duration_minutes: 15
|
||||
completed_date: "2026-04-10"
|
||||
tasks_completed: 2
|
||||
tasks_total: 2
|
||||
files_created: 6
|
||||
files_modified: 2
|
||||
---
|
||||
|
||||
# Phase 4 Plan 01: USB Manager Summary
|
||||
|
||||
**One-liner:** Goroutine-per-device USB Manager with VID/PID enumeration via system_profiler, context+done-channel teardown, and injectable test seams — no hardware required for testing.
|
||||
|
||||
## What Was Built
|
||||
|
||||
### internal/usb/device.go
|
||||
Core types: `DeviceSpec`, `DeviceRole`, `DeviceState`, `DeviceEvent`, `Command`, `CommandType`. `KnownDevices` registry maps `"VID:PID"` to `DeviceSpec` (PRT Qutie placeholder `0525:a4a7`). `ParseVIDPID()` helper with error on malformed input.
|
||||
|
||||
### internal/usb/enumerate.go
|
||||
`enumerateConnected()` shells out to `system_profiler SPUSBDataType -json`, parses the USB device tree, normalises hex IDs (strips `0x` prefix, lowercases), and cross-references with `serial.GetPortsList()` to return `map[VIDPID]portPath` containing only `KnownDevices` entries. Two injectable function vars (`sysProfilerCmd`, `serialPortsListCmd`) enable hermetic unit tests. Returns empty map with no error on Linux/CI (graceful degradation when `system_profiler` is unavailable).
|
||||
|
||||
### internal/usb/manager.go
|
||||
`Manager` owns all device goroutines. Poll loop calls `enumerateFunc()` every `pollInterval`, reconciles prev/current snapshots, spawns/stops `deviceLoop` goroutines. `deviceLoop` opens the serial port, runs an inner read goroutine (exits on `ctx.Done()` or port error), and handles `CmdWrite`/`CmdClose` commands. Goroutine teardown: child context cancelled → `port.Close()` called → read goroutine unblocked → `done` channel closed → poll loop proceeds. `ErrDeviceNotConnected` returned by `Send()` when device absent.
|
||||
|
||||
### Threat mitigations applied
|
||||
- **T-04-01**: Read buffer capped at 4096 bytes in `deviceLoop` before forwarding to command handler.
|
||||
|
||||
## Commits
|
||||
|
||||
| Task | Commit | Description |
|
||||
|------|---------|-------------|
|
||||
| Task 1 | f5b1d31 | USB device types, KnownDevices registry, VID/PID enumeration |
|
||||
| Task 2 | 82eaf6b | USB Manager goroutine-per-device, poll loop, reconnect, leak-safe teardown |
|
||||
|
||||
## Test Results
|
||||
|
||||
```
|
||||
=== RUN TestKnownDevicesContainsPRTQutie PASS
|
||||
=== RUN TestEnumerateConnectedMockOutput PASS
|
||||
=== RUN TestDeviceSpecString PASS
|
||||
=== RUN TestParseVIDPIDValid PASS
|
||||
=== RUN TestParseVIDPIDInvalid PASS
|
||||
=== RUN TestManagerStartSpawnsOneGoroutine PASS
|
||||
=== RUN TestManagerDisconnectReconnectEvents PASS
|
||||
=== RUN TestManagerStopGoroutineLeak PASS
|
||||
=== RUN TestManagerSendToAbsentDevice PASS
|
||||
=== RUN TestGoroutineStability PASS
|
||||
PASS (race detector clean, 30s timeout)
|
||||
```
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
### Additional Files
|
||||
|
||||
`internal/usb/mock_port_test.go` was added (not listed in plan's `files_modified`). The plan referenced `newMockPort()` in the test action spec but did not create a dedicated file for it — the mock helper needed to be in a separate `_test.go` file to avoid shipping test infrastructure in the production binary.
|
||||
|
||||
## Known Stubs
|
||||
|
||||
| File | Item | Reason |
|
||||
|------|------|--------|
|
||||
| internal/usb/device.go | `KnownDevices["0525:a4a7"]` VID/PID placeholder | Real VID/PID unknown until hardware arrives 2026-04-13 — update after characterization |
|
||||
| internal/usb/manager.go | Read bytes in `deviceLoop` are logged but not forwarded | No upstream consumer yet; Phase 4 plan 03 (printer driver) will consume read events |
|
||||
|
||||
## Threat Flags
|
||||
|
||||
None — no new network endpoints, auth paths, or schema changes introduced. USB serial I/O is local-only per T-04-03 acceptance.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
- internal/usb/device.go: FOUND
|
||||
- internal/usb/enumerate.go: FOUND
|
||||
- internal/usb/manager.go: FOUND
|
||||
- internal/usb/device_test.go: FOUND
|
||||
- internal/usb/manager_test.go: FOUND
|
||||
- internal/usb/mock_port_test.go: FOUND
|
||||
- Commit f5b1d31: FOUND
|
||||
- Commit 82eaf6b: FOUND
|
||||
Loading…
Add table
Reference in a new issue