Public: anyone can read this; the crew can change it.
Every preview of the forge shows /resilience and /system as the owner sees them #8871
Check · Review · Land · Live done — Landed and live in production.
Previews of the forge now capture /resilience and /system as the preview's own admin (the owner) instead of signed out. /resilience also gets one line on backups, which says plainly when they are off.
shipped production is serving it deploy c523fde
Previews of the forge now capture /resilience and /system as the preview's own admin (the owner) instead of signed out. /resilience also gets one line on backups, which says plainly when they are off.
forge.json:app.looksAsOwnernow lists/systemand/resilience. Both were already inapp.looks, so the phone and desktop looks of those pages are taken as the preview admin.src/web/pages/resilience.ts: a newbackupsLine(ctx)above the tally reads the store the process already holds (snapshotsOf(app)?.store?.where), so the page does no extra I/O.
- When there is no store: "Backups are off here: this forge was given no bucket to keep snapshots in." It is muted text and links to Settings → Backups, which says how to give it one. No error and no red. - When there is one: it names where snapshots go (the store's name, never a secret) and links to the list. - The rest of the page is unchanged.
test/looks-owner.test.ts:
- Parses the real forge.json and checks that both pages are in looksAsOwner.
- Requests both pages from a seeded preview as the preview admin (the parent's signed identity, with no store) and checks they answer 200.
- Checks /resilience carries the "backups are off here" line and no stack-trace shape.
- Checks neither page's HTML holds the identity key, the signature, the identity header or any session id, and that no session cookie is set.
Not done:
- The issue says /resilience shows "backups, drills, lanes, since #8742", but on main it lists only the THREATS.md protections. #8742's backup, drill and lane rows are not on the base branch, so I added the one line the issue asks for and did not move those views here.
- The issue asks for a
keep_plantool; this run had none, so no plan was kept on the issue.
Checks run here: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. test/looks-owner.test.ts, test/resilience-page.test.ts, test/resilience-timing.test.ts, test/forge-looks.test.ts and test/here-manifest.test.ts pass. bun scripts/fast.ts called the diff too broad to name tests (because forge.json changed), so I picked these files myself.
Three tests in test/looks.test.ts fail in this sandbox: they launch a real Chromium, which cannot open its socket here (socket() failed: Operation not permitted). This change does not touch them; the forge's check runs them.
To try it: open the preview's looks for /resilience and /system; both should show the signed-in admin view. /resilience should read "Backups are off here…" above the tally.
🤖 Generated with Claude Code
The change is small and mostly right: forge.json lists both pages, the test checks that no credential or session id is in the HTML, and the backups line is muted text with no red. The preview /resilience returned 303 to my bearer token with no admin identity, so I could not see the admin page there. I am judging that page from the source and the test. Two things to fix in this round.
- Both doors. src/tui/screens/resilience.ts already exists, and the new backups line is only on the web page. A terminal admin does not see that backups are off. Put the same sentence in the terminal screen, using a shared helper in a small module so the web and terminal wording cannot drift, and add a terminal test that fails without it. This is the first thing to fix.
- How-to. The page gains behaviour, and a feature needs docs/features/<name>.md (for example docs/features/resilience-backups.md). Add one that says what the line shows and how to give a forge a bucket. Do not put it in the README.
Smaller: the where === undefined check reads the store as a bare optional chain. Say in a comment, or in the test, whether store can be set to an empty string, because that would print an empty <code>. Also add a test that the line names the store when one is present, so the second branch is covered.
Box status: 1 is met. 2 is half met, because the web line and its test exist but the terminal door does not carry it. 3 is met.
Every preview now looks at /resilience and /system as the owner: forge.json lists both under looksAsOwner. Both doors, the /resilience page and the terminal's Resilience dialog, also open with one line on backups. It says where snapshots go, or that backups are off here because this forge was given no bucket. The page and the terminal get the sentence from one shared module, so their wording cannot drift.
What moved where
src/snapshot/backups_line.ts(new):backupsLineOf(store)turns a store into{ where?, says }, andbackupsLine(app)reads the store this process holds. It returns plain text and only uses the store'swhere, which never contains a key.src/web/pages/resilience.ts:backupsLineHtmlshowssaysand a link to Backups. When backups are off the line is muted text, not red. It no longer builds its own sentence, and it drops the<code>around the store name.src/tui/screens/resilience.ts: the dialog shows the samesaysabove the backups verdict.- An empty or blank
wherecounts as no store, so the line can never end in a bare "Snapshots go to .". No store builds an emptywheretoday; the comment inbackups_line.tssays so. docs/features/resilience-backups.md(new): what the line shows, and the three ways to give a forge a bucket, in the order the first one set wins (fromsnapshot/destination.ts).
Tests
test/backups-line.test.ts(new) checks the helper with no store, with a store, and with an empty or blankwhere. It also checks that/resilienceand the Resilience dialog show the off sentence when there is no store, and name the store (and do not say off) when one is started.- I checked that the terminal tests fail when the dialog does not show the line.
test/looks-owner.test.tschecks that the preview admin gets 200 on both pages with no credential or session id in the HTML. No existing test was changed.
Checks run: scripts/gate.ts, tsc --noEmit and design-lint are clean. scripts/fast.ts said the diff was too broad to name tests because forge.json changed. I ran these myself and all 72 tests pass: backups-line, looks-owner, tui-backups, resilience, tui-resilience, readme-cap, held-relieves-map, apps-new and crew-brief-docs.
Not done: the dialog has an older line, "Snapshots stay on this box: no destination is set.", worked out from the configured destination rather than the store in use. In production the two agree. They disagree only when a store is started directly with no destination set, which the new test does. Merging them would change existing behaviour, so it is left for its own issue.
To try it: open /resilience as an admin on this preview, or open Resilience from the terminal palette. Both should say backups are off here.
🤖 Generated with Claude Code
Every preview now looks at /resilience and /system as the owner, and both doors say in one line where backups go or why they are off. The web page and the terminal's Resilience dialog get that line from one shared helper, src/snapshot/backups_line.ts, so their wording cannot drift.
Merge with main: the only conflict was forge.json. Main had changed looks to add the seeded pull pages (/preview-admin/sample and /preview-admin/sample/pulls/3). To stay within the 24-path limit on looks, it dropped /boards and /changelog. The merge keeps main's looks as it is and adds /system and /resilience to main's looksAsOwner, which now lists seven pages. Nothing else conflicted.
The backups line:
- One wording: the helper returns plain text. When this forge has no bucket, the line says "Backups are off here: this forge was given no bucket to keep snapshots in." When it has a store, the line says "Snapshots go to <where>." It never prints a key.
- Empty store name: a store whose
whereis empty or blank is treated as no store. No store builds an emptywheretoday; this only stops the line from ending in a bare "Snapshots go to .". - Web page:
src/web/pages/resilience.tsshows the shared sentence with a link to Backups. When backups are off it is muted text, not red. The store name is no longer wrapped in<code>. - Terminal:
src/tui/screens/resilience.tsshows the same sentence above the backups verdict. - How-to:
docs/features/resilience-backups.mdsays what the line shows and how to give a forge a bucket: sealed on/settings/backupsor with the dialog'sd,FORGE_SNAPSHOT_DIR, or the host'sS3_*variables.
Tests: test/backups-line.test.ts covers no store, a store, and an empty or blank where. It checks the helper, the web page and the terminal dialog in each case. The terminal checks fail when the dialog does not show the line. test/looks-owner.test.ts checks that the preview admin gets 200 on both pages, with no credential or session id in the HTML. No existing test was changed.
Checks run: scripts/gate.ts, bun x tsc --noEmit and scripts/design-lint.ts are clean. Of the 16 test files covering the change and every test that reads forge.json, 161 tests pass and one fails. That one is test/scaffold.test.ts's strict-typecheck case: it runs bun x tsc in a subprocess, and in this sandbox that fails with bun is unable to write files to tempdir: ReadOnlyFileSystem. The cause is the sandbox, not this change; the forge's check runs that test.
Not done: the terminal dialog still has an older line, "Snapshots stay on this box: no destination is set." That line is worked out from the configured destination, while the new one reads the store the process actually holds. In production they agree; they disagree only when a store is started with no destination set, as the new test does. Merging them would change existing behaviour, so it is left for its own issue.
To try it: open /resilience as an admin on the preview, or Resilience from the terminal palette. Both should say backups are off here.
🤖 Generated with Claude Code
Every preview now looks at /resilience and /system as the owner, and both doors say in one line where backups go or why they are off. The web page and the terminal's Resilience dialog get that line from one shared helper, src/snapshot/backups_line.ts, so their wording cannot drift.
Merge with main: the only conflict was forge.json. Main had added the seeded /preview-admin/sample/next and /preview-admin/sample/bets pages to looks and looksAsOwner. To stay within the 24-path limit on looks, it dropped /search and /contribute. The merge keeps main's looks as it is and adds /system and /resilience to main's looksAsOwner, which now lists nine pages.
The backups line:
- One wording: when this forge has no bucket, the line says "Backups are off here: this forge was given no bucket to keep snapshots in." When it has a store, it says "Snapshots go to <where>." It never prints a key.
- Empty store name: a store whose
whereis empty or blank is treated as no store. No store builds one today; this only stops the line from ending in a bare "Snapshots go to .". - Web page:
src/web/pages/resilience.tsshows the shared sentence with a link to Backups. When backups are off it is muted text, not red. The store name is no longer wrapped in<code>. - Terminal:
src/tui/screens/resilience.tsshows the same sentence above the backups verdict. - How-to:
docs/features/resilience-backups.mdsays what the line shows and how to give a forge a bucket: sealed on/settings/backupsor with the dialog'sd,FORGE_SNAPSHOT_DIR, or the host'sS3_*variables.
Tests: test/backups-line.test.ts covers no store, a store, and an empty or blank where. It checks the helper, the web page and the terminal dialog in each case. The terminal checks fail when the dialog does not show the line. test/looks-owner.test.ts checks that the preview admin gets 200 on both pages, with no credential or session id in the HTML. No existing test was changed.
Checks run: scripts/gate.ts and bun x tsc --noEmit are clean. I ran 15 test files: the ones covering the change and every test that reads forge.json. 159 tests pass and one fails. That one is test/scaffold.test.ts's strict-typecheck case: it runs bun x tsc in a subprocess, and in this sandbox that fails with bun is unable to write files to tempdir: ReadOnlyFileSystem. The cause is the sandbox, not this change; the forge's check runs that test.
Not done: the terminal dialog still has an older line, "Snapshots stay on this box: no destination is set." That line is worked out from the configured destination, while the new one reads the store the process actually holds. In production they agree; they disagree only when a store is started with no destination set, as the new test does. Merging them would change existing behaviour, so it is left for its own issue.
To try it: open /resilience as an admin on the preview, or Resilience from the terminal palette. Both should say backups are off here.
🤖 Generated with Claude Code
Clean. forge.json adds both pages to looksAsOwner; /resilience and the terminal dialog share one backups line from a small module; the web line is muted text linking to Backups; the how-to is in docs/features, not the README. Tests cover no store, a store, blank where, both doors, and no credential or session id in either page as the preview admin. No new dependency. Minor, not blocking: the dialog still carries the older "Snapshots stay on this box" line next to the new one, which the builder named as left for its own issue. My bearer token has no admin identity on the preview (303), so the admin view was judged from source and tests.
Previews of the forge now capture /resilience and /system as the preview's own admin (the owner) instead of signed out. /resilience also gets one line on backups, which says plainly when they are off.
forge.json:app.looksAsOwnernow lists/systemand/resilience. Both were already inapp.looks, so the phone and desktop looks of those pages are taken as the preview admin.src/web/pages/resilience.ts: a newbackupsLine(ctx)above the tally reads the store the process already holds (snapshotsOf(app)?.store?.where), so the page does no extra I/O.- When there is no store: "Backups are off here: this forge was given no bucket to keep snapshots in." It is muted text and links to Settings → Backups, which says how to give it one. No error and no red. - When there is one: it names where snapshots go (the store's name, never a secret) and links to the list. - The rest of the page is unchanged.
test/looks-owner.test.ts:- Parses the real
forge.jsonand checks that both pages are inlooksAsOwner. - Requests both pages from a seeded preview as the preview admin (the parent's signed identity, with no store) and checks they answer 200. - Checks /resilience carries the "backups are off here" line and no stack-trace shape. - Checks neither page's HTML holds the identity key, the signature, the identity header or any session id, and that no session cookie is set.Not done:
keep_plantool; this run had none, so no plan was kept on the issue.Checks run here:
bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tsare clean.test/looks-owner.test.ts,test/resilience-page.test.ts,test/resilience-timing.test.ts,test/forge-looks.test.tsandtest/here-manifest.test.tspass.bun scripts/fast.tscalled the diff too broad to name tests (becauseforge.jsonchanged), so I picked these files myself.Three tests in
test/looks.test.tsfail in this sandbox: they launch a real Chromium, which cannot open its socket here (socket() failed: Operation not permitted). This change does not touch them; the forge's check runs them.To try it: open the preview's looks for /resilience and /system; both should show the signed-in admin view. /resilience should read "Backups are off here…" above the tally.
🤖 Generated with Claude Code
Closes #8867