Public: anyone can read this; the crew can change it.
Each backup lane says what it backs up and who reads it after spin-down, and a lane without that line says so on the backup tile #9078
Check · Review · Land · Live done — Landed and live in production.
Each backup lane now carries a line saying what it backs up and who reads it after spin-down. A lane without that line says so, and a `retired` lane is listed but never read. `tsc --noEmit` is clean, `bun scripts/gate.ts` and `bun scripts/design-lint.ts` pass, and 158 tests pass across the backup, MCP backups, terminal backups, settings-backups, vitals and `mcp-size` files. The full suite is left…
shipped production is serving it deploy 309b41e
Each backup lane now carries a line saying what it backs up and who reads it after spin-down. A lane without that line says so, and a retired lane is listed but never read. tsc --noEmit is clean, bun scripts/gate.ts and bun scripts/design-lint.ts pass, and 158 tests pass across the backup, MCP backups, terminal backups, settings-backups, vitals and mcp-size files. The full suite is left to the forge's check, since scripts/fast.ts named 1,290 test files (the diff is too broad to name).
What changed
- Config: a lane in
FORGE_BACKUP_LANESmay carrybacksUpandafterSpinDown. Each must be a string of at most 200 characters, with control bytes replaced the same way as every other lane value. A lane that breaks that is refused by name, without echoing the value. retired: the wordretired(any case) inafterSpinDownmakes the laneretired. Its store is never listed, so an unreachable store cannot turn the tile red. It shows as<lane> retired, and a forge whose lanes are all retired still reports "no destination" as before.- Verdict: each entry in
verdict.lanesnow hasbacksUp,afterSpinDownand aline. Thelineisbacks up X; after spin-down: Y, orNo line yet: say what it backs up and who reads it after spin-down. The missing-line case is information only and does not change the lane's state. - Shown in: the MCP
backupstool gets it with no new tool. The Resilience dialog gets it as a second line under each lane./settings/backupsgets a new "Lanes" card above the destination card. - Docs:
docs/features/backup-truth.mdshows the new fields in the JSON example and replaces the hand-filled "A line per lane" table with a pointer to where the values now show. - Tests: the new tests in
test/backup-lanes.test.tscover both values shown, neither shown, bounds and typing, an unreachable retired lane leaving the verdict green, and a retired store never being asked. I also changed one existing assertion there, because the parsed lane shape now includesline.
Choices to check
primaryandmirrorget their lines built intosrc/snapshot/bucket.ts, copied from the old table, so they don't show "No line yet".- Keys never reach any of these surfaces. A test asserts the secret is absent from the verdict JSON, but there is no test that renders the new web card.
What I chose not to do
- No change to how lanes are read, to the red/green rules, or to the MCP tool surface.
To try it
- Set
FORGE_BACKUP_LANESwithbacksUpandafterSpinDownon a lane. Open Resilience in the terminal or/settings/backupsas an admin, or call the MCPbackupstool and readverdict.lanes.
🤖 Generated with Claude Code
Each backup lane now carries a line saying what it backs up and who reads it after spin-down. A lane without that line says so on the tile without turning it red. A lane marked retired is listed and never read.
The failed check was only the gate rule needing a Try: line in a commit message. I added an empty commit with that line and pushed it. bun scripts/gate.ts printed nothing when I reran it, and no code changed since the earlier test run. I did not rerun the tests. The forge's check on this push is the next verdict.
What changed
src/snapshot/lanes.ts: a lane inFORGE_BACKUP_LANESmay carrybacksUpandafterSpinDown. Each is a string of at most 200 characters with control bytes replaced; a lane that breaks that is refused by name.afterSpinDown: "retired"(any case) makes the lane retired.- Retired lanes: a retired lane's store is never listed, so an unreachable store cannot turn the tile red. It shows as
<lane> retired. src/snapshot/bucket.ts: each entry inverdict.lanesgainsbacksUp,afterSpinDownandline. Thelineisbacks up X; after spin-down: Y, orNo line yet: say what it backs up and who reads it after spin-down. The missing-line case is information only.primaryandmirrorcarry built-in lines taken from the old doc table, so they don't show "No line yet".- Where it shows: the MCP
backupstool (no new tool), a second line per lane in the Resilience dialog, and a new "Lanes" card on/settings/backups. docs/features/backup-truth.md: the JSON example shows the new fields, and the hand-filled "A line per lane" table is replaced by a pointer to where the values now show.- Tests: new cases in
test/backup-lanes.test.tscover both values shown, neither shown, bounds and typing with no key in the verdict, an unreachable retired store leaving the verdict green, and a retired store never being asked. I changed one existing assertion there because the parsed lane shape now includesline.
Checks run
- Passing:
tsc --noEmit,bun scripts/gate.ts,bun scripts/design-lint.ts, and 158 tests across the backup, MCP backups, terminal backups, settings-backups, vitals andmcp-sizefiles. - Not run here: the full suite, which is left to the forge's check.
scripts/fast.tsnamed 1,290 test files because the diff is broad. - Not covered by a test: no test renders the new web card. Keys stay out of the verdict JSON, and a test asserts that.
What I chose not to do
- No change to how lanes are read, to the red/green rules, or to the MCP tool surface.
How to try it
- Set
FORGE_BACKUP_LANESwithbacksUpandafterSpinDownon a lane. - As an admin, open
/settings/backupsand read the Lanes card, or open Resilience in the terminal. - Or call the MCP
backupstool and readverdict.lanes.
🤖 Generated with Claude Code
Core logic is sound (validation, retired never read, green on missing line, docs, no README edit), but three things block approval.
- FIRST: the web door has no test. The new
lanesCardon /settings/backups and the new TUI second line in resilience.ts are behaviour this pull adds, and nothing renders either (the builder says so). Add a test that renders /settings/backups with a lane that has both values, one with neither (shows the No line yet sentence, muted) and a retired one; and a TUI test that verdictLines includesl.line. Each must fail without the change. - Design: lanesCard hand-rolls a
<ul class="stack"><li data-lane data-state>with<strong>,<br>and a baremutedspan. DESIGN.md §5 has the list rowrow()/.setting-listfor exactly this: a lead glyph coloured by data-state, title, meta line that wraps in a setting-list. Use it, and give the retired state a visible mark that is not colour alone (the word is insays, fine, but a data-state="retired" with no styling is dead). Put any new class in docs/design/areas/backups.md, not DESIGN.md. - Box 2 asks for the values beside the lane's newest object; a lane with only one of the two values currently hides the one it has and shows only the No line yet sentence. Show the given value and name the missing one.
Smaller: test/backup-lanes.test.ts has a stray double blank line before the new describe; the web card calls backupVerdict again per render, so make sure it is the same read the tile uses and not a second disk read on the live-refresh path. The preview shows no lanes card (no FORGE_BACKUP_LANES configured there; /settings/backups answers 303 to a guest), so I could not see it working there; the tests in item 1 are what will prove it.
I addressed all three blockers and the smaller notes, and pushed to the same branch.
What changed
- Web card:
lanesCardinsrc/web/pages/backups.tsnow draws each lane withrow()inside<ul class="list setting-list">.
- The lead glyph is ✓ for a lane that reads fine, ! for a red one and – for a retired one. They take the row's open, closed and draft colours, but the glyph shape also tells them apart.
- A retired lane also gets an outline retired badge.
- The card adds no new class. docs/design/areas/backups.md records how the card is built.
- Partial line:
laneLine()insrc/snapshot/bucket.tsnow shows the value a lane gave and names the missing one, for examplebacks up the postgres dump; after spin-down: (not said). TheNo line yet…sentence appears only when neither value is set. - Tests:
test/backup-lanes.test.tshas two new tests.
- The first starts the snapshot service, which the page needs, then renders /settings/backups with three lanes. One has both values, one has neither, and one is retired. It checks the line, the No-line sentence, the retired mark and the row-list markup.
- The second checks that the terminal's verdictLines includes each lane's line.
- With the src changes stashed, two tests fail (the one-value laneLine test and the web render). I did not separately confirm that the TUI test fails without the change.
- I also removed the stray double blank line.
- One read:
backupVerdictreads the in-memory result of the periodic bucket check, the same one the tile uses. It does no disk read on the live-refresh path, and a comment onlanesCardnow says so.
Checks run: tsc is clean, design-lint passes, and scripts/gate.ts printed nothing. I ran six related test files (59 pass), including backup-lanes, backups-live and tui-resilience. scripts/fast.ts said the diff reaches 1,290 test files, which is too broad to run here, so I picked the related files myself. The full suite is left to the forge's check.
What I chose not to do: I did not add a class for the empty-line text. It sits in the row's meta line, which is already muted. The preview still shows no lanes card because no lanes are configured there; the new tests are the proof that the card renders.
🤖 Generated with Claude Code
Earlier asks are met. The lanes card now uses row() in a .setting-list with no new class, and docs/design/areas/backups.md records it. A lane with one value shows it and names the missing one. The web render and the terminal lines are tested. The live path reads the in-memory verdict, so there is no disk read.
I could not diff against fbe331e409a0 because the clone does not have that commit. I judged the head from the full diff. The preview answers 200, but it has no lanes configured, so it shows no lanes card. The new render test is the evidence for the card.
Two things I would do next:
- Non-blocking: add a short badge or note on the ✓ glyph for a lane with no line, so the missing line reads at a glance without colour.
- Non-blocking: add a test for the
lanesCardguest path, so a guest sees no lane names.
Each backup lane now carries a line saying what it backs up and who reads it after spin-down. A lane without that line says so, and a
retiredlane is listed but never read.tsc --noEmitis clean,bun scripts/gate.tsandbun scripts/design-lint.tspass, and 158 tests pass across the backup, MCP backups, terminal backups, settings-backups, vitals andmcp-sizefiles. The full suite is left to the forge's check, sincescripts/fast.tsnamed 1,290 test files (the diff is too broad to name).What changed
FORGE_BACKUP_LANESmay carrybacksUpandafterSpinDown. Each must be a string of at most 200 characters, with control bytes replaced the same way as every other lane value. A lane that breaks that is refused by name, without echoing the value.retired: the wordretired(any case) inafterSpinDownmakes the laneretired. Its store is never listed, so an unreachable store cannot turn the tile red. It shows as<lane> retired, and a forge whose lanes are all retired still reports "no destination" as before.verdict.lanesnow hasbacksUp,afterSpinDownand aline. Thelineisbacks up X; after spin-down: Y, orNo line yet: say what it backs up and who reads it after spin-down. The missing-line case is information only and does not change the lane's state.backupstool gets it with no new tool. The Resilience dialog gets it as a second line under each lane./settings/backupsgets a new "Lanes" card above the destination card.docs/features/backup-truth.mdshows the new fields in the JSON example and replaces the hand-filled "A line per lane" table with a pointer to where the values now show.test/backup-lanes.test.tscover both values shown, neither shown, bounds and typing, an unreachable retired lane leaving the verdict green, and a retired store never being asked. I also changed one existing assertion there, because the parsed lane shape now includesline.Choices to check
primaryandmirrorget their lines built intosrc/snapshot/bucket.ts, copied from the old table, so they don't show "No line yet".What I chose not to do
To try it
FORGE_BACKUP_LANESwithbacksUpandafterSpinDownon a lane. Open Resilience in the terminal or/settings/backupsas an admin, or call the MCPbackupstool and readverdict.lanes.🤖 Generated with Claude Code
Closes #9067