test, docs: admin page gate tests and the page's written rules (#102)

- web_test.go walks web.AdminPatterns() rather than naming routes by hand, so a
  new administrative route that forgets requireOwner fails the gate test
  instead of shipping open.
- The harnesses take a LaneReporter; a fake one keeps the page's tests free of
  a poller and a Site.
- backend/AGENTS.md records the adminRoutes/requireOwner rule and the nil-poller
  trap; design-system.md records --patina and the admin page's shape.
This commit is contained in:
2026-08-16 14:17:36 +07:00
parent e69da2a99b
commit 0b946ee2c4
6 changed files with 218 additions and 31 deletions
+13 -3
View File
@@ -211,6 +211,16 @@ Guidance for OpenCode (and Claude Code) working under `backend/`. See root `AGEN
`POST /rotate-token` (atomic epoch bump + hash
rewrite; invalidates every installed copy, so the panel warns to reinstall
on all devices).
Owner-only `POST /readers/{id}/revoke` (drops one Reader's session rows and
re-renders the `readers` panel; 404 for any non-owner) is the only route that
reaches across Readers.
- **Owner-only admin page (`internal/web/admin.go`, issue #102):** `GET /admin`
carries the Reader roster (sessions, Sighting counters, `POST
/readers/{id}/revoke` and `POST /readers/{id}/clear-marks`) and Poll Lane
status (`GET /ui/admin/lanes`, self-refreshing every 30s). Every route that
reaches past the acting Reader is listed in `adminRoutes()` and wrapped in
`requireOwner` at registration — add a route there, not a check inside a
handler; `web.AdminPatterns()` is what the gate test walks. A non-owner gets
404, never 403. Lane figures come from the running poller through the
`web.LaneReporter` seam (`latest.Poller.LaneStatus`), never from a table: a
nil reporter or a Lane that has not finished a pass renders "no data yet"
rather than zeroes. `main.newRouter` takes the reporter as an interface and
converts a nil `*Poller` to a nil interface — a typed nil would make the page
claim a poller exists.