Files
mangaBookmark/docs/adr/0012-persisted-lane-state.md
T
sulthan cb2b104e63 feat(web): read admin lanes from the durable pass log, not a poller snapshot (#145)
The Lanes page now projects store.LatestLanePasses and the owner-window
outcomes (store.LanePassOutcomes) into per-Site rows instead of reading an
in-memory Poller snapshot, so a deploy answers the instant the store is up.
Browser configuration is a config fact and reachability is derived from
recent browser-Site passes inside latest.RefuseBackoff.

This atomically deletes the in-memory path in the same commit that makes the
page read the DB: latest/status.go (LaneState, Status, LaneStatus,
recordLaneState) and the web.LaneReporter seam plus fakeLanes are gone, and
latest.refuseBackoff is renamed latest.RefuseBackoff at every callsite.
ownerWindow (#142) is referenced, never declared (contract C2)
2026-08-21 20:25:38 +07:00

2.0 KiB

ADR-0012: Persisted lane state

Date: 2026-08-21 Status: accepted

Supersedes the in-memory lane snapshot carried by latest's LaneState/Status and the web.LaneReporter seam (ADR-0010 wrote the durable rows this page now reads).

Decision

The admin Lanes page stops reading the poller's in-memory Lane state and becomes a read of poll_passes/poll_lanes in Postgres. There is no LaneReporter interface: web/admin_lanes.go walks store.LatestLanePasses() into one row per Site and adds the window's outcome sums from store.LanePassOutcomes(). The latest package's LaneState/Status snapshot and its web.LaneReporter seam are deleted.

The browser is a deployment configuration fact plus a reachability derived from the pass log: BROWSER_WS_URL set means "configured", and the browser is "reachable" unless a recent browser-Site pass inside latest.RefuseBackoff is a sidecar loss, a missing fetcher, or an interrupted read. A skip reason is the whole difference between a Lane resting and a Lane stuck: a skipped pass prints its sentence, and only an empty skip with Series due and none read draws the true-stall fault. Sleep skips never count toward Attention.

Why

The old page lived on a poller snapshot. Because that state was in memory, a deploy erased it: the page read zeroes until a fresh pass ran, and browser reachability came through a reporter interface only a live poller could serve. Making the page answer from the database means a restart is complete the instant the store is up, the browser fact survives a poller restart, and a Lane that has not yet gathered figures shows a placeholder rather than a confident zero.

Constraints

The poller still owns the writes: each pass exit records one row (ADR-0010), and a pass that returns before gathering figures carries the previous pass's numbers forward instead of recording zeroes. A skip is a stable wire string; asleep never counts toward Attention. When polling is switched off the page must say so, and the browser statusline appears only when a poller actually answers.