Lanes page reads the database, and the in-memory Lane state is deleted #145

Closed
opened 2026-08-21 16:39:24 +07:00 by sulthan · 1 comment
Owner

Parent

Spec #134.

What to build

The Lanes page starts answering the question thirty seconds after a deploy, and the in-memory Lane state that could not is removed in the same change. One row per Site: what its last pass found waiting, how many it read, its pace, and, when it did no work, the reason it declined. That reason is the whole difference between a Lane resting and a Lane stuck, and it is recorded rather than guessed.

Columns: Site, Due, Checked, Gap, Last pass, the outcomes-and-state phrase, controls. The five outcome counts render named -- the page never prints the word "failures" bare -- and a zero renders as none observed, because the wording is the honesty: the failure kind that would hurt most, an adapter parsing a wrong number successfully, is not in this taxonomy and never can be. The five counts render unlinked: the pass row holds counts and never identities, so there is no list of the four refused Series to point at. A trouble state renders danger; the one true stall (due series, none checked, no skip reason) is distinguishable from every Lane that declined and said why.

Browser reachability is a status line pinned to the section heading, not a paragraph. Browser configuration becomes "is the browser websocket URL set", read in the web layer's config -- strictly more accurate than today, since it describes the deployment rather than whether one goroutine happened to construct a fetcher. Reachability is derived: any browser Site whose latest pass carries an unreachable count inside the refusal backoff of now, which needs that backoff constant exported from the poller package.

Numerics on this table are centred rather than right-flushed, because at column width a right-flushed figure loses its header. Rows are single-line. The block keeps its own 30s timer and stays the only timer on the surface -- now a six-row primary-key read rather than a map copy.

Deleted in this ticket, atomically: the poller's Lane state map, its recorder, the Lane state and status types, the status query method, the whole status file, the in-memory refusal map left behind by the recording ticket, and the web-side Lane reporter interface with its test fake. A half-deleted twin means two sources of truth in tree, so the deletion is not staged. This reduces the test seam count: a Lanes-page test inserts a pass row instead of constructing a fake reporter.

Write the ADR for persisted Lane state and the deletion of its in-memory twin with this implementation, not before.

Acceptance criteria

  • The Lanes page renders one row per Site from the latest pass per Site, with no poller running at all in the test
  • A Lane with a skip value renders its reason rather than a stall; the one true stall renders as a fault
  • The outcome counts render named, with none observed at zero, and none of them is a link
  • The word "failures" appears nowhere on the page
  • A fresh backend renders real Lane rows immediately after restart, given pass rows in the database
  • Browser configured reads from the deployment config; reachability is derived from the latest browser-Site passes inside the exported backoff
  • The Lane state map, its recorder, the Lane state and status types, the status query, the status file, the in-memory refusal map and the web Lane reporter interface with its fake are all gone from the tree
  • The 30s timer is scoped to the Lanes fragment and is the only timer on the admin surface
  • An ADR for persisted Lane state lands with the change
  • go test ./... green

Blocked by

  • #138 — Split the admin surface into four bookmarkable pages with a nav row
  • #139 — Durable Lane state: poll_passes and poll_lanes, plus their store surface
  • #141 — The poller records one pass row per exit, with a skip reason and outcome counts
## Parent Spec #134. ## What to build The Lanes page starts answering the question thirty seconds after a deploy, and the in-memory Lane state that could not is removed in the same change. One row per Site: what its last pass found waiting, how many it read, its pace, and, when it did no work, the reason it declined. **That reason is the whole difference between a Lane resting and a Lane stuck, and it is recorded rather than guessed.** Columns: Site, Due, Checked, Gap, Last pass, the outcomes-and-state phrase, controls. The five outcome counts render **named** -- the page never prints the word "failures" bare -- and a zero renders as *none observed*, because the wording is the honesty: the failure kind that would hurt most, an adapter parsing a *wrong* number successfully, is not in this taxonomy and never can be. The five counts render **unlinked**: the pass row holds counts and never identities, so there is no list of the four refused Series to point at. A trouble state renders danger; the one true stall (due series, none checked, no skip reason) is distinguishable from every Lane that declined and said why. Browser reachability is a status line pinned to the section heading, not a paragraph. Browser *configuration* becomes "is the browser websocket URL set", read in the web layer's config -- strictly more accurate than today, since it describes the deployment rather than whether one goroutine happened to construct a fetcher. Reachability is **derived**: any browser Site whose latest pass carries an unreachable count inside the refusal backoff of now, which needs that backoff constant exported from the poller package. Numerics on this table are centred rather than right-flushed, because at column width a right-flushed figure loses its header. Rows are single-line. The block keeps its own 30s timer and stays the only timer on the surface -- now a six-row primary-key read rather than a map copy. **Deleted in this ticket, atomically:** the poller's Lane state map, its recorder, the Lane state and status types, the status query method, the whole status file, the in-memory refusal map left behind by the recording ticket, and the web-side Lane reporter interface with its test fake. A half-deleted twin means two sources of truth in tree, so the deletion is not staged. **This reduces the test seam count**: a Lanes-page test inserts a pass row instead of constructing a fake reporter. Write the ADR for persisted Lane state and the deletion of its in-memory twin with this implementation, not before. ## Acceptance criteria - [ ] The Lanes page renders one row per Site from the latest pass per Site, with no poller running at all in the test - [ ] A Lane with a skip value renders its reason rather than a stall; the one true stall renders as a fault - [ ] The outcome counts render named, with *none observed* at zero, and none of them is a link - [ ] The word "failures" appears nowhere on the page - [ ] A fresh backend renders real Lane rows immediately after restart, given pass rows in the database - [ ] Browser configured reads from the deployment config; reachability is derived from the latest browser-Site passes inside the exported backoff - [ ] The Lane state map, its recorder, the Lane state and status types, the status query, the status file, the in-memory refusal map and the web Lane reporter interface with its fake are all gone from the tree - [ ] The 30s timer is scoped to the Lanes fragment and is the only timer on the admin surface - [ ] An ADR for persisted Lane state lands with the change - [ ] `go test ./...` green ## Blocked by - #138 — Split the admin surface into four bookmarkable pages with a nav row - #139 — Durable Lane state: poll_passes and poll_lanes, plus their store surface - #141 — The poller records one pass row per exit, with a skip reason and outcome counts
sulthan added the ready-for-agent label 2026-08-21 16:39:24 +07:00
sulthan self-assigned this 2026-08-21 19:23:29 +07:00
Author
Owner

Merged into spec-134 as 6f9109c (branch ticket/145-lanes-from-db, commits cb2b104, fc8a40c, d72c482).

The Lanes page now reads poll_passes and poll_lanes through store.LatestLanePasses and store.LanePassOutcomes, so Lane history survives a restart and the page renders from seeded rows with no poller present. Skip reason is distinguished from a true stall, asleep never counts toward attention, named outcome counts render zero as a digit, and browser reachability is derived from the pass log rather than asked of the poller.

The deletion is atomic in the same change: internal/latest/status.go, LaneState, Status, LaneStatus, recordLaneState, web.LaneReporter, fakeLanes and the lane-reporter parameter on newRouter are all gone. refuseBackoff is exported as latest.RefuseBackoff. ADR-0012 lands with it.

Review: one Critical, four Important and two Minor findings fixed across two rounds; three judgement-call Minors declined with reasons in the report.

go test ./... green on the merged base.

Merged into `spec-134` as 6f9109c (branch `ticket/145-lanes-from-db`, commits cb2b104, fc8a40c, d72c482). The Lanes page now reads `poll_passes` and `poll_lanes` through `store.LatestLanePasses` and `store.LanePassOutcomes`, so Lane history survives a restart and the page renders from seeded rows with no poller present. Skip reason is distinguished from a true stall, `asleep` never counts toward attention, named outcome counts render zero as a digit, and browser reachability is derived from the pass log rather than asked of the poller. The deletion is atomic in the same change: `internal/latest/status.go`, `LaneState`, `Status`, `LaneStatus`, `recordLaneState`, `web.LaneReporter`, `fakeLanes` and the lane-reporter parameter on `newRouter` are all gone. `refuseBackoff` is exported as `latest.RefuseBackoff`. ADR-0012 lands with it. Review: one Critical, four Important and two Minor findings fixed across two rounds; three judgement-call Minors declined with reasons in the report. `go test ./...` green on the merged base.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: sulthan/mangaBookmark#145