phase: 01_verification_gate
All green. Report: **Implemented / verified (phase 01 final pass)** - `.agents/validate.sh` re-verified as the gate: ①`tests/run_tests.sh` ②`SANITIZE=1` run ③`uv … platformio run -e bridge -e console` ④page byte count ⑤hygiene, then the informational `tools/hw_probe.sh`. Added an explicit `== repo hygiene ==` header + `repo hygiene: OK (57 tracked files…)` verdict line so every check prints one (previously hygiene passed in silence). - `.agents/HW_VERIFICATION.md`: baseline (bridge 26.7 %/87 592 B, 36.1 %/1 135 293 B; console 16.8 %/33.6 %; 3406 checks; page 48 598 B) re-confirmed; served-page row extended with this pass's one-attempt measurements. **Tests / gate (100 % green)** - `bash .agents/validate.sh` → **exit 0** (also from `/tmp`): `RESULT: all suites passed` twice (3406 = 3162 C++ + 160 relayctl + 84 page), `2 succeeded`, `page bytes: 48598`, `repo hygiene: OK`, `validation OK`. - Faults re-demonstrated then reverted: (a) inverted `test_keystate` assert → cold-copy gate `RESULT: FAILED`, **GATE_EXIT=1**; (b) markup id `pArmed`→`pArmedZ` → page suite rc 1; (c) type error in `src/main.cpp` → `bridge FAILED / console FAILED`, rc 1; (d) hygiene regex matches `certs/test.pem` (git index commands are forbidden in this pass — task 01's report holds the original live `git add` demo). - Lint: no linter is configured for this repo; compiler is the linter — `-Wall -Wextra` host suites + both firmware builds: **0 warnings**; `bash -n` clean; `py_compile` clean. `ruff check` (this machine's broad ruleset) reports 21 pre-existing *preview-rule* style nits in untouched `tools/*.py` + `tests/test_relayctl.py` (SIM/UP/ISC/EXE/RUF/FURB), zero correctness findings — not churned: `watch_target.py`/`relayctl.py` are hardware-verified artifacts outside this phase. `shellcheck` is absent here and not in the local container image (fetching it would violate the gate's no-network rule). Coverage: n/a (shell + docs; no new Python). **Completion criteria** — all met: exit 0 with per-check output ✓; SANITIZE in-gate ✓; no `-t upload`/device curl/network fetch (grep: only in comments; `run_tests.sh` checks `podman image exists` first, so never pulls) ✓; faults bite ✓; HW_VERIFICATION.md names command+expected observation for every 02–05 device claim + one-attempt rule ✓; full suite green, `git status` = phase artifacts + mandated doc edits, nothing stray ✓. **Documentation** — README inconsistencies found & fixed: `test_proto` 193→**2327** (table now sums to the headline 3406); duplicate stale `test_page 47` row deleted (element-id clause merged into the 84 row); "last six checks"/"fails 3 checks" claims replaced by measurement (5 fail-safe source pins; `linked()` gate → 2, accept-stamp → 1, both → 3); console line formats corrected to what the code prints (`mouse interface=present target=report protocol buttons=0x00 …`, `queue 0 keys pending…`, `target=BOOT protocol (refused)`); status-JSON sample gained `typed/typing/mouse/mbtn/mframes`; footprint 87 400/35.5 %→87 592/36.1 %; page delivery `Content-Length: 35164`→**48 598** (re-measured live, one capped curl: 200/48 598, 404 on `/nope`, 401 on `/status`); stale Backlog item "Mouse." removed and renumbered; "~35 KB"→"~48 KB"; page-button names matched to the real markup; `validate.sh`/`HW_VERIFICATION.md`/`hw_probe.sh` added to Architecture, Tests and End-to-end. AGENTS.md: only stale statements fixed — "one commit, `Initial import`… predates the mouse and `output.h`" and the pure-module list missing `mouse.h`/`output.h`; not expanded. **Deviations** — Did not run the keystroke/mouse device rows: the board's target is this very machine, so injecting input during an unattended pass is the side effect the README's "hazard nobody warns you about" forbids; those rows stay blank per the contract (read-only GETs only). No live infra changed. **Next pending phase:** `02_tls_control_channel` (tasks `01_tls_identity_module` … `06_tls_docs_rollback`).
This commit is contained in:
@@ -0,0 +1,49 @@
|
||||
# Phase 01 — Verification gate and hardware contract
|
||||
|
||||
**Source:** `TODO.md` — (foundation, no TODO line). Required before the four TODO items
|
||||
can be executed by subprocesses behind a pass/fail gate.
|
||||
**Story:** `n/a`
|
||||
**Context:** `PLAN.md` "Validation / Verification Workflow"; `tests/run_tests.sh`; `AGENTS.md` "Build, test, flash"
|
||||
|
||||
## Objective
|
||||
|
||||
Give this repository a `.agents/validate.sh` that actually fails when keyrelay is broken —
|
||||
host suites (including the sanitiser run and the page suite) plus **both** firmware builds —
|
||||
and write down the hardware contract so that no later unit ever blocks waiting for a board
|
||||
that nobody is holding.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- — (none)
|
||||
|
||||
## Tasks
|
||||
|
||||
1. `01_adapt_validate_sh.md` — author `.agents/validate.sh` with keyrelay's real checks and
|
||||
the template's repo-hygiene gates, and prove it fails on a deliberately broken tree.
|
||||
2. `02_hardware_contract.md` — `.agents/HW_VERIFICATION.md` + `tools/hw_probe.sh`: what can
|
||||
only be checked on hardware, how to check it, the baseline RAM/flash numbers, and the
|
||||
rule that a unit attempts a device step once, with a timeout, and records the outcome.
|
||||
|
||||
## Testing & Quality
|
||||
|
||||
- Unit/integration: the gate itself is the artifact. It must pass on a healthy tree **and**
|
||||
fail on each injected fault: a failing C++ assertion, a page-suite id mismatch, a firmware
|
||||
compile error in `src/main.cpp`, and a tracked `*.pem`.
|
||||
- Coverage: n/a for shell scripts; >90% applies only to any Python added (`tools/hw_probe.sh`
|
||||
is shell, not Python).
|
||||
- `.agents/validate.sh` must run the **full** suite — a partial gate defeats the
|
||||
"no phase commits unless 100% passing" guarantee.
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
- [ ] `bash .agents/validate.sh` exits 0 on the untouched tree and prints, per check: the
|
||||
suite results (`RESULT: all suites passed`), the two build results, and the hygiene verdict.
|
||||
- [ ] `SANITIZE=1` is exercised by the gate (or by a documented, gate-called second pass) and
|
||||
passes.
|
||||
- [ ] The gate contains no `pio … -t upload`, no `curl` to a device, no network fetch.
|
||||
- [ ] Each of the four injected faults makes the gate exit non-zero (demonstrated in the
|
||||
task's report, then reverted).
|
||||
- [ ] `.agents/HW_VERIFICATION.md` exists and names, for every device-only claim in this
|
||||
roadmap, the command that observes it (`tools/watch_target.py`, the serial console,
|
||||
`curl -k https://keyrelay.local/status`).
|
||||
- [ ] Full test suite green; `git status` shows only planning artifacts.
|
||||
@@ -0,0 +1,65 @@
|
||||
# Task 01 — Author the gate: `.agents/validate.sh`
|
||||
|
||||
**Phase:** `01_verification_gate` · **Source:** `TODO.md` — (foundation: no TODO item; the
|
||||
gate the other four items need)
|
||||
**Story:** `n/a`
|
||||
|
||||
## Objective
|
||||
|
||||
`.agents/validate.sh` becomes the authoritative pass/fail gate for this firmware project:
|
||||
the host suites (normal and sanitised), both PlatformIO environments, and the repo-hygiene
|
||||
checks. Nothing in it may need a device or the network.
|
||||
|
||||
## Work
|
||||
|
||||
1. `.agents/validate.sh` — if the pipeline already installed its generic template, adapt it
|
||||
in place; otherwise write it, keeping the template's three hygiene gates verbatim
|
||||
(tracked-but-ignored files, key-material shapes `.pem/.key/.crt/…`, `PII_PATTERNS`).
|
||||
Add, in this order, each one printing its own header and accumulating `rc`:
|
||||
- `bash tests/run_tests.sh` — the C++ suites, `test_relayctl.py` and `test_page.js`.
|
||||
- `SANITIZE=1 bash tests/run_tests.sh` — ASan/UBSan, the run that found 5 of the 10 bugs.
|
||||
- `uv run --python 3.14 --with platformio python -m platformio run -e bridge -e console`
|
||||
— the exact invocation from `AGENTS.md`. **Never** `-t upload`. If `uv` is missing, the
|
||||
gate **fails** with the message from `AGENTS.md` (`pio` on PATH is broken; the fallback
|
||||
probe is `find / -maxdepth 6 -name platformio -type d -path '*site-packages*'`) — a
|
||||
silent skip here would let a firmware that does not compile reach `complete/`.
|
||||
- A page-size sanity check that the served page's byte length equals
|
||||
`kIndexHtmlLen`-by-construction: `python3` (or `grep`) counts the bytes of the raw
|
||||
string between `R"HTML(` and `)HTML"` in `src/web_page.h` and prints it; the phase
|
||||
final passes compare it to the README claim. No device.
|
||||
2. `.gitignore` — add `.agents/phase-sessions/` and `.agents/pipeline.log` if the pipeline
|
||||
has not already; never ignore `.agents/` itself.
|
||||
3. Run `bash .agents/validate.sh` and record the output in the task report, including the
|
||||
bridge `RAM:`/`Flash:` lines, so later phases can quote a delta instead of a guess.
|
||||
4. Prove the gate bites, one fault at a time, reverting immediately after each and
|
||||
re-running to confirm it goes green again:
|
||||
(a) invert one assertion in `tests/test_keystate.cpp`;
|
||||
(b) rename an element id in `src/web_page.h` that the script reaches for;
|
||||
(c) introduce a syntax error in `src/main.cpp`;
|
||||
(d) `touch certs/test.pem && git add certs/test.pem` (then `git rm --cached`).
|
||||
`AGENTS.md` warns that a multi-line regex rewrite across a source file has destroyed
|
||||
`main.cpp` before — make these four edits with exact-string edits only.
|
||||
|
||||
- ASSUMPTION: the gate is allowed to take ~30 s per task (measured: suites ≈2 s with g++
|
||||
inside `localhost/ai-sandbox:ubuntu-9a08b1d500b0`, builds ≈2 s incremental). The full
|
||||
recompile of `bridge` from clean is the worst case and is acceptable.
|
||||
- ASSUMPTION: a missing `uv`/PlatformIO is a gate failure rather than a skip, because every
|
||||
phase in this roadmap touches firmware and a skipped build check is the exact failure
|
||||
`AGENTS.md` records ("`pio run -t upload` reports SUCCESS on a stale binary").
|
||||
|
||||
## Testing & Quality
|
||||
|
||||
- The four injected faults each make the gate exit non-zero; the untouched tree exits 0.
|
||||
- The gate is idempotent and safe to run from any cwd (`cd` to the repo root by
|
||||
`BASH_SOURCE` like `tests/run_tests.sh` does).
|
||||
- No `set -e` swallowing: use the `rc` accumulation pattern so every check reports, not
|
||||
just the first failure.
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
- [ ] `bash .agents/validate.sh` → exit 0, printing suite results, both build results, page
|
||||
byte count, and the hygiene verdict.
|
||||
- [ ] Each of the four injected faults produced a non-zero exit (paste the four lines into
|
||||
the report), and the tree is reverted (`git diff` clean apart from `.agents/`).
|
||||
- [ ] No `upload`, no `curl`, no `podman pull`, no network access in the gate.
|
||||
- [ ] Full test suite green.
|
||||
@@ -0,0 +1,68 @@
|
||||
# Task 02 — The hardware contract: what the gate cannot see
|
||||
|
||||
**Phase:** `01_verification_gate` · **Source:** `TODO.md` — (foundation; `AGENTS.md`
|
||||
"Verification standard": *"A feature that lives on the device is not done when the source
|
||||
builds — it is done when the device is running it and something observed it."*)
|
||||
**Story:** `n/a`
|
||||
|
||||
## Objective
|
||||
|
||||
Make the device-only half of this roadmap explicit, runnable and non-blocking: a probe that
|
||||
reports what hardware is reachable right now, and a file that pairs every device-only claim
|
||||
with the exact command that would confirm it — so a unit running unattended never hangs on
|
||||
a board and never claims a device result it did not observe.
|
||||
|
||||
## Work
|
||||
|
||||
1. `.agents/HW_VERIFICATION.md` — three sections:
|
||||
- **Baseline** (measured, from task 01's gate run): `bridge RAM 26.7% (87 592 B) /
|
||||
Flash 36.1% (1 135 293 B)`, `console RAM 16.8% (55 200 B) / Flash 33.6% (1 056 081 B)`,
|
||||
`3406 host checks`. Later phases quote a delta against these numbers.
|
||||
- **Claims that need hardware**, each with its command and its expected observation, at
|
||||
least: keystroke/chord delivery and *which* keys a chord really was
|
||||
(`python3 tools/watch_target.py` on the target); served-page byte agreement
|
||||
(`curl -sk https://keyrelay.local/ | wc -c` vs the gate's page count — `http` until
|
||||
phase 02 lands, `https -k` after); TLS handshake + the SHA-256 fingerprint printed once
|
||||
on the serial console; takeover actually releasing the incumbent's held keys
|
||||
(`watch_target.py` must show key-up *before* the new session's first key); the mobile
|
||||
soft keyboard and touch pad (a phone on the board's AP, `watch_target.py` for both the
|
||||
characters and the pointer deltas).
|
||||
- **The blocking rule**: a unit attempts a device step **at most once**, wrapped in
|
||||
`timeout`, and writes the outcome — `ok`, `skipped (no device)`, or `failed: …` — with
|
||||
the command it ran into the section's table. It never loops on a port, never re-flashes
|
||||
to "see if it works", and never leaves the board in ROM download mode without saying so
|
||||
in the report (`tools/to_bootloader.py` drops it there deliberately; a power cycle is
|
||||
the only way back and nobody may be there to do it).
|
||||
2. `tools/hw_probe.sh` (executable, `set -uo pipefail`, no side effects) — prints one line
|
||||
per fact and **always exits 0** (it informs, it does not gate):
|
||||
`/dev/ttyACM*`/`/dev/ttyUSB*` present?; `keyrelay.local` resolves?; `https` reachable
|
||||
(`curl -sk -m 2 -o /dev/null -w '%{http_code}'`)?; `http` reachable and does it redirect?;
|
||||
`uv` on PATH?; `node` on PATH?; is a `watch_target.py`-capable evdev device visible
|
||||
(informational only). Nothing is opened on a serial port except a `test -e`/`test -r`
|
||||
check — opening it toggles DTR/RTS and resets the board, so the probe must not.
|
||||
3. `.agents/validate.sh` — append `bash tools/hw_probe.sh` as the **last, non-affecting**
|
||||
line (its exit status is deliberately not folded into `rc`), with a comment saying why:
|
||||
it is context for whoever reads the gate output, not a check.
|
||||
4. `README.md` — no behaviour change yet; only if the "End-to-end verification" section
|
||||
contradicts what the probe just printed, fix the stale line. Do not restructure it here.
|
||||
|
||||
- ASSUMPTION: at execution time a board *may* be attached and reachable but nobody is
|
||||
watching it, so "board available" is treated as best-effort. Any phase that needs an
|
||||
on-target second machine (`watch_target.py`) records `skipped (no device)` rather than
|
||||
inventing a result. This is the user-facing cost of running the roadmap unattended; the
|
||||
file is where they pick those checks up.
|
||||
|
||||
## Testing & Quality
|
||||
|
||||
- `bash tools/hw_probe.sh` exits 0 with and without a board, with and without `uv`, from any
|
||||
cwd; it never blocks (every probe has a `-m`/`timeout`).
|
||||
- The gate still passes with the probe line added, and its output cannot flip `rc`.
|
||||
- `shellcheck`-clean if `shellcheck` is installed; otherwise `-n` syntax check in the report.
|
||||
|
||||
## Completion Criteria
|
||||
|
||||
- [ ] `bash tools/hw_probe.sh` prints the six facts and exits 0 (paste its output).
|
||||
- [ ] `.agents/HW_VERIFICATION.md` covers every phase 02–05 device claim listed above, each
|
||||
with command + expected observation + the rule about one attempt.
|
||||
- [ ] `bash .agents/validate.sh` still exits 0.
|
||||
- [ ] Full test suite green; no firmware source touched.
|
||||
Reference in New Issue
Block a user