Formalize trackball QA: TRKRW parser + hardware suite
- debug_console.py: TrkSelfTest dataclass, parse_trk_selftest, and the
dut.trk_selftest() helper (device token, pin mapping, click level, ISR-armed
state, PASS/FAIL) mirroring kbd_selftest/imu_selftest.
- test_trk_selftest_parse.py: no-hardware parser unit tests over a MockDut
(quadrature PASS, click-held informational, absent, not-armed FAIL, boot
noise, missing line).
- test_trackball.py: trackball-capability-gated self-test (PASS, device match,
ISRs armed — the load-bearing input-path evidence for a device whose motion
cannot be synthesized) plus an absent-graceful check for the rest of the
fleet. Motion is intentionally not driven — it needs a physical finger.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Formalize trackball QA: TRKRW parser + hardware suite
- debug_console.py: TrkSelfTest dataclass, parse_trk_selftest, and the
dut.trk_selftest() helper (device token, pin mapping, click level, ISR-armed
state, PASS/FAIL) mirroring kbd_selftest/imu_selftest.
- test_trk_selftest_parse.py: no-hardware parser unit tests over a MockDut
(quadrature PASS, click-held informational, absent, not-armed FAIL, boot
noise, missing line).
- test_trackball.py: trackball-capability-gated self-test (PASS, device match,
ISRs armed — the load-bearing input-path evidence for a device whose motion
cannot be synthesized) plus an absent-graceful check for the rest of the
fleet. Motion is intentionally not driven — it needs a physical finger.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Formalize trackball QA: TRKRW parser + hardware suite
- debug_console.py: TrkSelfTest dataclass, parse_trk_selftest, and the
dut.trk_selftest() helper (device token, pin mapping, click level, ISR-armed
state, PASS/FAIL) mirroring kbd_selftest/imu_selftest.
- test_trk_selftest_parse.py: no-hardware parser unit tests over a MockDut
(quadrature PASS, click-held informational, absent, not-armed FAIL, boot
noise, missing line).
- test_trackball.py: trackball-capability-gated self-test (PASS, device match,
ISRs armed — the load-bearing input-path evidence for a device whose motion
cannot be synthesized) plus an absent-graceful check for the rest of the
fleet. Motion is intentionally not driven — it needs a physical finger.
Claude-Session: https://claude.ai/code/session_01SV58JXhfxhhc9DC6vdjBo4
Add pytest QA system bootstrap (Phase 0, no hardware)
Introduce the qa/ pytest system that will become the single entry-point for
all Kleidos QA (HIL + a thin wrapper over the native Unity suite), replacing the
~60 loose scripts. Phase 0 lands the no-hardware skeleton:
- pyproject.toml: strict markers/config, live logging, marker taxonomy (layer /
requirement / capability / cost), Python-only ruff+black config.
- requirements.txt + constraints.txt: pinned stack on Python 3.13. Drop
pytest-repeat — its --count collides with pytest-embedded's --count (the
mandated transport foundation); soak uses in-test loops instead.
- qa_lib: config resolver (CLI > env > .env > yaml > default) + MAC-keyed device
and host inventories; central redaction filter (logs + Allure attachments);
per-test redacting logging; transport interfaces + mock DUT/host for the
no-hardware mode; redacting Allure evidence helpers.
- inventory: devices.yaml (all 9 DUTs, derived from docs/device-identifiers.md,
MAC = identity), hosts.yaml (mastodon Linux + Plus1 embedded, coords by
env-var name), .env.example placeholders.
- tests: redaction guard (seeded secrets never leak on log/attachment/plain
paths), config + inventory-drift guard (devices.yaml vs the doc), mock
transport, and the native Unity wrapper (skips cleanly without PlatformIO).
Gate: `pytest -m "not needs_hardware"` green (20 passed, native suite included);
`pytest --collect-only`/`--markers` show the taxonomy; redaction self-test
proves no seeded secret leaks; ruff + black clean.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X
Make QA multi-DUT: auto-flash, device-agnostic geometry/motion, telemetry evidence
Validated on the T-Deck (--dut tdeck) as a second DUT family (keyboard/touch,
320x240) alongside the Stick S3:
- `--flash`: build + flash <dut>_debug once per session, MAC-verified, refusing
_secure — so `pytest --dut tdeck --run-hardware --flash` compiles, flashes, and
runs everything in one command. flash.py finds pio on PATH or the penv.
- Screenshot assertion no longer hard-codes 135x240 — accepts any plausible
framebuffer (T-Deck/Core are 320x240).
- Video motion is now device-agnostic: capture frames SPACED across full-screen
toggles (PIN entry <-> HOME) instead of a fast burst under a Stick-only ARC
sweep. Verified green on both Stick S3 and T-Deck (the stream does reflect UI
changes; the old burst just sampled a single screen state).
- Telemetry as evidence: the HAL telemetry test now captures and attaches RES
(heap/loop-stack), TASKS (per-task core/prio/stack-free/CPU%), and STACKHWM
(per-task stack high-water) to Allure; add DebugConsole.stackhwm().
T-Deck result: serial (button tests capability-skip — 1-button), HAL/telemetry,
screenshots, smoke, WiFi admin-portal + monitor, and video all green; BLE E3/E4
security green; BLE happy-path pairs (AUTH_OK) but HOGP_FAILs — the Linux HoG
plugin does not create a uhid keystroke node for the T-Deck (a real per-device
finding, not masked).
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X