review: verdigris patina, honest Lane figures, tests that can fail (#102)

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.
This commit is contained in:
2026-08-16 14:31:00 +07:00
parent d878580df8
commit 58014eb8dd
10 changed files with 203 additions and 61 deletions
+29 -7
View File
@@ -31,7 +31,11 @@ type adminView struct {
// browser fact, which is shared by the three browser Sites rather than held
// once per Site.
type lanesView struct {
Rows []laneRow
Rows []laneRow
// PollerOff means no poller is running at all (disabled by config, or its
// client could not be built). The browser line must not answer "not
// configured" then: the sidecar is not the reason nothing is polled.
PollerOff bool
BrowserConfigured bool
BrowserReachable bool
}
@@ -40,9 +44,15 @@ type lanesView struct {
// template renders strings and flags, and every judgement about what they mean
// is made here.
type laneRow struct {
Site string
Due int
Ran string
Site string
Due int
Ran string
// Checked is how many Series the last pass read. Due without Checked is a
// Lane that has stopped working; the two figures side by side are what
// separate that from a Lane with nothing to do.
Checked int
// Gap is empty when no pass has reached the pace yet, so the row omits the
// figure instead of stating a zero.
Gap string
Clamped bool
Refusing bool
@@ -50,6 +60,9 @@ type laneRow struct {
// sidecar while the sidecar is unreachable — including the case where none
// is configured, which stops those Series just as completely.
BrowserLost bool
// Stalled marks a Lane with Series waiting that its last pass did not read
// — the difference between a stopped Lane and a quiet one (story 13).
Stalled bool
// Attention is the one flag the template colours on, so an unhealthy Lane
// is found at a glance rather than read for.
Attention bool
@@ -127,7 +140,7 @@ func (h *Handler) uiLanes(w http.ResponseWriter, r *http.Request) {
// — an empty page a few seconds after a restart must not read as a stopped one.
func (h *Handler) lanesView() lanesView {
if h.lanes == nil {
return lanesView{}
return lanesView{PollerOff: true}
}
snap := h.lanes.LaneStatus()
v := lanesView{
@@ -138,15 +151,24 @@ func (h *Handler) lanesView() lanesView {
now := time.Now()
for _, l := range snap.Lanes {
lost := l.Browser && !snap.BrowserReachable
// Series waiting and none read is the shape of a Lane that has stopped
// working, as distinct from one that is quiet for want of work.
stalled := l.Due > 0 && l.Checked == 0
gap := ""
if l.Gap > 0 {
gap = l.Gap.Truncate(time.Second).String()
}
v.Rows = append(v.Rows, laneRow{
Site: l.Site,
Due: l.Due,
Ran: since(now, l.LastRun),
Gap: l.Gap.Truncate(time.Second).String(),
Checked: l.Checked,
Gap: gap,
Clamped: l.Clamped,
Refusing: l.Refusing,
BrowserLost: lost,
Attention: l.Clamped || l.Refusing || lost,
Stalled: stalled,
Attention: l.Clamped || l.Refusing || lost || stalled,
})
}
return v