Two-axis review found the shipped colour and three weak tests.
- --patina was a warm gold at the same hue family as --brass; the spec
asked for a cool blue-green so an unhealthy Lane is unmistakably not
ember. Now verdigris in both branches, with docs/design-system.md
stating why the far side of the wheel is the point.
- A Lane pass that returns before computing its figures carries the
previous pass's due count and gap forward instead of recording zeroes,
and Checked rides beside Due so a stopped Lane is distinguishable from
a quiet one.
- TestAdminPageWithoutAPollerSaysSo now separates the two causes it
conflated, TestOwnerClearsReaderMarks seeds real counters so the
clearing assertion can fail, and TestLaneStatus asserts Checked and
the carry-forward.
- backend/AGENTS.md records the one deliberate owner comparison outside
requireOwner and the carry-forward rule.
- 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.
The only operational surface was /healthz and a fold-out roster inside the
owner's own reading page. This gives the owner a page: two sections of facts on
the same measured sheet, no cards.
- admin.go holds every route that reaches past the acting Reader, listed once
in adminRoutes() and wrapped in requireOwner at registration - a missing gate
is visible in the route list rather than hidden inside a handler. A non-owner
gets 404, the same answer revoke already gave.
- Lane figures arrive through the LaneReporter seam, so the page reads the
running poller rather than a table. newRouter converts a nil *Poller to a nil
interface: a typed nil would make the page claim a poller exists.
- The roster moves out of the reading page and gains the Sighting counters, the
blocked verdict and Clear marks. Clearing restores a privilege, so it is a
plain ghost button; --danger stays with revocation.
- --patina is the page's one accent, held at the weight of the other action
accents. Neither --ember (new chapter) nor --danger (destruction) is borrowed
for system health.
A Lane's pace, its refusal backoff and whether its Site needs the browser were
only ever visible in the log. The admin page (issue #102) has to state them, so
the Poller keeps one snapshot per Lane.
- LaneState/Status (status.go) is the page's view of a Lane: due, gap, clamped,
refusing, browser.
- runOnce records a snapshot on every return path, including the pass that
refuses, so a cooling Lane does not read as one that never ran.
- Refusing is derived at snapshot read time from the backoff, not stored: a
Lane that cooled down between passes must report false without a new pass.
- LaneStatus reports only Sites that have completed a pass, so a fresh restart
renders "no data yet" instead of confident zeroes.
The owner's page (issue #102) shows each Reader's Sighting record and offers
one action to wipe it. The counters ship before the Sighting feature that
moves them (issue #103) on purpose: the remedy for a false mark must exist
before marks can be made, or the first broken adapter is fixed with SQL
against production.
- 0010 adds sighting_agreements / sighting_disagreements, both zero-defaulted,
so every existing Reader reads as trusted.
- ReaderSummary carries both, plus Blocked() against SightingDisagreementLimit,
so the page states the verdict rather than making the owner derive it.
- ClearReaderMarks zeroes one Reader's pair.