chore(agent): phase roadmap from TODO.md, 5 phases

The four open items in TODO.md become an executable pipeline: 01 the
verification gate (host suites + sanitiser + both firmware envs, and the
hardware contract so no unit blocks on a board nobody is holding), 02 HTTPS
and wss:// with an identity the board generates itself into NVS, 03 session
takeover that always lands disarmed, 04 a responsive page whose safety
statements are pinned before they are trimmed, 05 the phone as a controller
(press-and-hold pad, soft keyboard, modifier latches over a new Q= frame).

TODO.md is cleared to its empty title; the items live in .agents/phases/ now.
AGENTS.md gains the five protocol rules and nothing else.

LLM-Generated: true
This commit is contained in:
2026-11-08 23:28:25 -05:00
parent ece0313d02
commit 56aa97d60d
28 changed files with 2033 additions and 0 deletions
@@ -0,0 +1,70 @@
# Phase 04 — The controller page: less to read, works on a phone
**Source:** `TODO.md` L4 — "Clean up keyrelay interface, don't need as much text, make it
responsive"
**Story:** `n/a`
**Context:** `src/web_page.h` (markup + `<style>` + script), `tests/test_page.js`, README
"Controller page"; `AGENTS.md` "The page is served from flash"
## Objective
Reorganise the page so the controls are the thing you see: single column and thumb-sized on a
phone, unchanged on a laptop, and the explanatory prose folded away to where it is asked for
instead of sitting between the operator and the ARM button. **No safety statement gets
weaker or shorter** — they move, they do not shrink, and each one gets a test that fails if it
disappears.
## Why the prose is load-bearing anyway
Every long hint in this page exists because someone got hurt without it: the arming banner
(Enter goes to another machine), the `Esc`-frees-the-pointer note (Pointer Lock is invisible),
the layout caveat (characters are positions), the KDE paragraph (those three buttons are the
laptop's until the board steals them). Trimming them is the right call for usability and the
wrong call for safety *unless* the trimming is pinned. So this phase writes the assertions
before it deletes the sentences.
## Dependencies
- `03_session_takeover` (todo) — its TAKE OVER affordance and the `tls` pill/fingerprint line
are new elements in the same markup; folding prose around them is easier once they exist,
and the id-coverage check has to be updated once.
## Tasks
1. `01_responsive_layout.md` — CSS and viewport only: breakpoints, thumb-sized targets, sticky
ARM bar, safe-area insets, collapsed log; no markup restructure, no id changes.
2. `02_prose_trim_pinned.md` — the safety assertions first, then the fold: long hints into
`<details>`, the footer shortened, the connection/mouse/paste paragraphs cut to one line
each with the reasoning one tap away.
3. `03_suite_size_and_docs.md` — `test_page.js` extended for the new structure, the README's
page-size claim and its duplicated `test_page` row corrected, totals made to agree, and the
`curl` byte check re-derived.
## Testing & Quality
- `tests/test_page.js` is the only thing that can see this page before a browser does
(`AGENTS.md`: a raw string is invisible to every compiler). It must cover, in addition to
what it covers today:
- **safety strings still present**: the arming warning names Enter and "every key"; the
mouse hint names `Esc` and release; the paste hint names "stop typing"/disarm cancelling a
run; the layout note says positions/US layout; the default-passphrase shout still exists.
- every element id the script reaches for exists in the markup (existing rule, re-verified
after the restructure);
- the pinned page constants (`TEXT_FRAME_MAX`, `TEXT_JOB_MAX`) still equal `config.h`;
- the script still behaves identically: arming, state frames, paste chunking, mouse
capture/movement/button ordering, `E12`.
- Coverage **>90%** on the page script's changed branches; CSS is checked by assertions on the
served bytes (breakpoints present, sticky ARM bar) plus a phone observation.
- Device (best effort, one attempt): `curl -sk https://keyrelay.local/ | wc -c` equals the
byte count the gate prints, and a phone shows one column with the ARM bar reachable and the
log not pushing it off screen.
## Completion Criteria
- [ ] The page works at 360 px wide and at 1440 px wide without horizontal scroll, ARM is
reachable with a thumb, and the log is collapsible.
- [ ] Every safety statement above is still on the page **and** asserted by `test_page.js`.
- [ ] The page is smaller in bytes than it is today, and README's size claim matches.
- [ ] README Tests table has one `test_page` row with the real count (the duplicate stale row
at 47 checks is removed), and the intro's total check count matches the suite output.
- [ ] `bash .agents/validate.sh` green; no behaviour change in phases 01–03.