Remove legacy BLE test scripts and repoint agent/skill to pytest
Delete scripts/ble_host_pairing_test.py (859 lines) and
scripts/ble_host_pairing_test_sticks3.py — now superseded by the
pytest QA system under qa/tests/ble/.
Update all four agent/skill files (.github/agents/ble-qa-runner.agent.md,
.claude/agents/ble-qa-runner.md, .github/skills/kleidos-ble-hardware-qa/SKILL.md,
.claude/skills/kleidos-ble-hardware-qa.md) to use the canonical pytest
invocations (cd qa && pytest tests/ble --dut sticks3 --host mastodon
--run-hardware) and real test names (test_happy_path, test_reconnect_cycles,
test_sc_reject_e3, test_blerp_repair_confirm_e4, test_bond_store_dump_well_formed).
Add scenario-mapping table; remove /mnt/build/ absolute paths.
Reword stale references in: qa/tests/ble/test_ble_pairing.py,
qa/qa_lib/host/plus1.py, scripts/qa/linux-harness/run.py,
scripts/qa/linux-harness/lib/qa_common.sh, scripts/qa/linux-harness/README.md,
and src/tools/ble_host_harness.cpp — describe the machine-greppable
OK/FAIL/SKIP marker convention without citing the deleted script.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X
Add Linux QA-mode harness for BLE + WiFi (normal + attack)
Introduce a reusable Linux QA harness under scripts/qa/linux-harness for
exercising the Kleidos DUT over BLE-HID and the WiFi admin SoftAP, both
normal and attack paths, with machine-greppable *_OK/_FAIL/_SKIP output.
Runner and modules:
* run.py QA-mode entry point: SSHes to the configured QA server, syncs
the harness (tar-over-ssh), and dispatches a suite; or --local.
* setup.sh idempotent provisioning (venv + bleak, apt radio/capture tools,
pinned qa-env), --system-site-packages for dbus/gi/requests.
* BLE: nc_agent.py (BlueZ numeric-comparison auto-confirm agent), ble_normal.py
(bleak scan/connect/pair/HOGP/subscribe/reconnect), ble_attack.sh
(E1-E6: SC-only reject, IO-cap/NC-only, accept-list, RPA/IRK, BLERP).
* WiFi: wifi_net.sh (static-IP bring-up, no default route), wifi_normal.sh
(WPA2/SAE station + portal checks), portal_checks.py (auth + fuzz),
wifi_attack.sh (monitor capture, evil-twin, empirical injection probe
with auto-skip when the adapter cannot inject).
Safety model (production server): interface pinning, a preflight that asserts
the default route stays on the Ethernet iface and never via WiFi, static IP
with no DHCP/default route, WiFi left DOWN when idle, and controller knobs
restored. The BLERP public-address spoof is opt-in (default SKIP) because the
Intel vendor command tx-timeouts and wedges the controller.
Config hygiene: all coordinates (SSH host/port/user, interface names, SoftAP
subnet, DUT addresses) load from a gitignored .env; only .env.example with
placeholders is tracked. No host/IP/interface literals in tracked code.
Claude-Session: https://claude.ai/code/session_01Y7Cy1HetRp6TZAUAeekN8X