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

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

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

opened this

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_LANES may carry backsUp and afterSpinDown. 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 word retired (any case) in afterSpinDown makes the lane retired. 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.lanes now has backsUp, afterSpinDown and a line. The line is backs up X; after spin-down: Y, or No 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 backups tool gets it with no new tool. The Resilience dialog gets it as a second line under each lane. /settings/backups gets a new "Lanes" card above the destination card.
  • Docs: docs/features/backup-truth.md shows 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.ts cover 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 includes line.

Choices to check

  • primary and mirror get their lines built into src/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_LANES with backsUp and afterSpinDown on a lane. Open Resilience in the terminal or /settings/backups as an admin, or call the MCP backups tool and read verdict.lanes.

🤖 Generated with Claude Code

Closes #9067

commented

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_LANES may carry backsUp and afterSpinDown. 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 word retired (any case) in afterSpinDown makes the lane retired. 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.lanes now has backsUp, afterSpinDown and a line. The line is backs up X; after spin-down: Y, or No 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 backups tool gets it with no new tool. The Resilience dialog gets it as a second line under each lane. /settings/backups gets a new "Lanes" card above the destination card.
  • Docs: docs/features/backup-truth.md shows 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.ts cover 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 includes line.

Choices to check

  • primary and mirror get their lines built into src/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_LANES with backsUp and afterSpinDown on a lane. Open Resilience in the terminal or /settings/backups as an admin, or call the MCP backups tool and read verdict.lanes.

🤖 Generated with Claude Code

commented

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 in FORGE_BACKUP_LANES may carry backsUp and afterSpinDown. 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 in verdict.lanes gains backsUp, afterSpinDown and line. The line is backs up X; after spin-down: Y, or No line yet: say what it backs up and who reads it after spin-down. The missing-line case is information only. primary and mirror carry built-in lines taken from the old doc table, so they don't show "No line yet".
  • Where it shows: the MCP backups tool (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.ts cover 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 includes line.

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 and mcp-size files.
  • Not run here: the full suite, which is left to the forge's check. scripts/fast.ts named 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_LANES with backsUp and afterSpinDown on a lane.
  • As an admin, open /settings/backups and read the Lanes card, or open Resilience in the terminal.
  • Or call the MCP backups tool and read verdict.lanes.

🤖 Generated with Claude Code

Changes requested — before the latest push

Core logic is sound (validation, retired never read, green on missing line, docs, no README edit), but three things block approval.

  1. FIRST: the web door has no test. The new lanesCard on /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 includes l.line. Each must fail without the change.
  2. Design: lanesCard hand-rolls a <ul class="stack"><li data-lane data-state> with <strong>, <br> and a bare muted span. DESIGN.md §5 has the list row row() / .setting-list for 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 in says, fine, but a data-state="retired" with no styling is dead). Put any new class in docs/design/areas/backups.md, not DESIGN.md.
  3. 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.

commented

I addressed all three blockers and the smaller notes, and pushed to the same branch.

What changed

  • Web card: lanesCard in src/web/pages/backups.ts now draws each lane with row() 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() in src/snapshot/bucket.ts now shows the value a lane gave and names the missing one, for example backs up the postgres dump; after spin-down: (not said). The No line yet… sentence appears only when neither value is set.
  • Tests: test/backup-lanes.test.ts has 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: backupVerdict reads 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 on lanesCard now 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

Approved

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:

  1. 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.
  2. Non-blocking: add a test for the lanesCard guest path, so a guest sees no lane names.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs