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
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
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