Polish Allure, add no-hardware layer coverage + docs (Phase 5)
- Drop the unused pytest-embedded dependency. It was never imported — SerialDut
(pyserial) reimplements MAC->port, flashing, expect, and multi-DUT itself (the
plan's documented pyserial fallback). Removing it cuts Allure containers from
~47/test to ~2.6/test and eliminates the record_xml_attribute warning; keep
esptool explicit (bridge-board MAC confirm). Refresh docstrings + constraints.
- Allure: an autouse fixture tags each test with a feature (its layer) + story
(its name) so the dashboard groups by BLE / WiFi / Serial / Video / Smoke /
Native. scripts/allure_report.sh already carries history/trends forward.
- No-hardware layer coverage: tests/video/test_video_decode.py (synthetic KVID
frames: raw + RLE decode, motion score, GIF) and tests/wifi/test_wifi_parse.py,
so every layer has a mock-mode test (32 passed no-hardware).
- Docs: docs/testing/qa-system.md (operator guide — install, run, markers,
Allure, non-negotiables, topology gotchas); CLAUDE.md §2/§4/§11 point QA at qa/.
Gate: pytest -m "not needs_hardware" green with no devices; Allure results carry
per-layer feature labels + evidence; ruff + black clean.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X
Convert transport and DUT-layer docstrings to RST
Add Sphinx-ready RST docstrings (native field lists: :param:, :returns:,
:raises:, :cvar:, :ivar:) and complete PEP 484 type hints on all public
signatures across the transport + DUT-control layer:
- transport/base.py: DutTransport / HostTransport ABC contracts, all
concrete helpers, TransportError / TransportTimeout / HostResult.
- transport/serial_dut.py: SerialDut.__init__, send, read_available,
expect_token, close, raw property; _open, _open_with_recovery,
_kill_port_holders.
- transport/mock.py: MockDut / MockHostTransport all methods, ivar/cvar
for .sent/.ran/.mac; make_boot_banner.
- dut/debug_console.py: DebugConsole and all 60+ public methods; VaultStatus
dataclass; parse_fields.
- dut/flash.py: FlashError, _find_pio, flash_debug, _confirm_boot_marker.
- dut/identify.py: IdentityError, MacMismatchError, discover_ports,
read_mac_esptool, resolve_verified_port, confirm_mac; private helpers.
- dut/screenshot.py: ScreenshotError, _Header, _rgb565_to_image,
_parse_header, _read_header, _decode_row, capture, to_png_bytes.
- dut/video.py: VideoError, _FrameHeader, _StreamReader (open/close/
next_frame), _parse_header, _rle_decode, _frame_to_image, record_stream,
motion_score, frames_to_gif_bytes.
No logic, control flow, or public signatures were changed.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X
Convert transport and DUT-layer docstrings to RST
Add Sphinx-ready RST docstrings (native field lists: :param:, :returns:,
:raises:, :cvar:, :ivar:) and complete PEP 484 type hints on all public
signatures across the transport + DUT-control layer:
- transport/base.py: DutTransport / HostTransport ABC contracts, all
concrete helpers, TransportError / TransportTimeout / HostResult.
- transport/serial_dut.py: SerialDut.__init__, send, read_available,
expect_token, close, raw property; _open, _open_with_recovery,
_kill_port_holders.
- transport/mock.py: MockDut / MockHostTransport all methods, ivar/cvar
for .sent/.ran/.mac; make_boot_banner.
- dut/debug_console.py: DebugConsole and all 60+ public methods; VaultStatus
dataclass; parse_fields.
- dut/flash.py: FlashError, _find_pio, flash_debug, _confirm_boot_marker.
- dut/identify.py: IdentityError, MacMismatchError, discover_ports,
read_mac_esptool, resolve_verified_port, confirm_mac; private helpers.
- dut/screenshot.py: ScreenshotError, _Header, _rgb565_to_image,
_parse_header, _read_header, _decode_row, capture, to_png_bytes.
- dut/video.py: VideoError, _FrameHeader, _StreamReader (open/close/
next_frame), _parse_header, _rle_decode, _frame_to_image, record_stream,
motion_score, frames_to_gif_bytes.
No logic, control flow, or public signatures were changed.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X