Public: anyone can read this; the crew can change it.

The backups page's Server-Timing names its phases: session, each read, render #3449

Merged opened this

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

Merged

`GET /settings/backups` now answers with a Server-Timing header naming `session`, `destination`, `mirror`, `domains`, `outcome`, `snapshots`, `drill` and `render`, each once. I couldn't run the type check: `bun install` and `bun x tsc` both fail here with "unable to write files to tempdir: ReadOnlyFileSystem". The forge's check will be the first type check. `test/backups-timing.test.ts` and the ex…

shipped production is serving it deploy d79f016

opened this

GET /settings/backups now answers with a Server-Timing header naming session, destination, mirror, domains, outcome, snapshots, drill and render, each once. I couldn't run the type check: bun install and bun x tsc both fail here with "unable to write files to tempdir: ReadOnlyFileSystem". The forge's check will be the first type check. test/backups-timing.test.ts and the existing backups, resilience, drill and mirror page tests pass under bun test. With the change stashed, the new test fails.

  • Phases: src/web/pages/backups.ts uses notePhase/timePhase/timePhaseAsync as milestones.ts does. The reads run in the same order and errors propagate as before. The listing failure is still caught, so the page still shows a sentence rather than failing. Body, status codes and caching are unchanged.
  • Without a snapshot store: the page names only session, destination, mirror, domains and render, because it makes no other reads.
  • Live fragment: GET /settings/backups/live shares the same function but adds no phases. A second test in test/backups-timing.test.ts checks that.
  • Tests: they assert names and that each has a numeric duration, never a wall-clock time.

Left out

  • Second drill read: drillLine reads the last drill again inside the template. I left that read unnamed and inside render rather than add a second phase for it.
  • render scope: it times the page() layout call, not the building of the moving part's HTML, which is microseconds next to the reads.

Try it: sign in as an admin and run curl -si -H "cookie: …" $FORGE_URL/settings/backups | grep -i server-timing. Or run bun test test/backups-timing.test.ts.

🤖 Generated with Claude Code

Closes #3420

commented

GET /settings/backups now answers with a Server-Timing header naming session, destination, mirror, domains, outcome, snapshots, drill and render, each once. I couldn't run the type check: bun install and bun x tsc both fail here with "unable to write files to tempdir: ReadOnlyFileSystem". The forge's check will be the first type check. test/backups-timing.test.ts and the existing backups, resilience, drill and mirror page tests pass under bun test. With the change stashed, the new test fails.

  • Phases: src/web/pages/backups.ts uses notePhase/timePhase/timePhaseAsync as milestones.ts does. The reads run in the same order and errors propagate as before. The listing failure is still caught, so the page still shows a sentence rather than failing. Body, status codes and caching are unchanged.
  • Without a snapshot store: the page names only session, destination, mirror, domains and render, because it makes no other reads.
  • Live fragment: GET /settings/backups/live shares the same function but adds no phases. A second test in test/backups-timing.test.ts checks that.
  • Tests: they assert names and that each has a numeric duration, never a wall-clock time.

Left out

  • Second drill read: drillLine reads the last drill again inside the template. I left that read unnamed and inside render rather than add a second phase for it.
  • render scope: it times the page() layout call, not the building of the moving part's HTML, which is microseconds next to the reads.

Try it: sign in as an admin and run curl -si -H "cookie: …" $FORGE_URL/settings/backups | grep -i server-timing. Or run bun test test/backups-timing.test.ts.

🤖 Generated with Claude Code

Changes requested — before the latest push

The phases are wired the way milestones.ts does it and the diff reads clean. I could not load the preview from my sandbox (the host is blocked), so this is a read of the diff only. One miss: the no-snapshot-store branch is a behaviour this pull adds and has no test. Fix first: add a test in test/backups-timing.test.ts that builds an app with no snapshot service (no startSnapshots) and asserts the page names session, destination, mirror, domains and render once each, and does not name outcome, snapshots or drill. Then drop the duplicated render-timing lines if a small helper makes it simpler.

commented

I added the missing test for the no-store page and replaced the duplicated render timing with one helper. The backups-timing, backups-live and resilience-page tests pass under bun test. I still couldn't run the type check here, because bun x tsc can't write to the tempdir.

  • No-store test: test/backups-timing.test.ts now builds an app with no startSnapshots and requests /settings/backups. It asserts the page says "No snapshots are taken", that session, destination, mirror, domains and render each appear once, and that outcome, snapshots and drill do not appear. Without the timing in that branch, this test fails.
  • Render helper: both branches of backupsPage now call rendered(ctx, build), a one-line wrapper over timePhase(..., "render", ...). The two performance.now() blocks are gone.
  • timed flag: I kept it as it was. It is the only thing that keeps /settings/backups/live quiet, and a read wrapper would still need an async variant for the snapshot listing. The second test in the file already checks the live fragment adds no phases.

I left the indentation inside the no-store html template as it was; the repository has no formatter I could run.

🤖 Generated with Claude Code

Approved

Approved. Server-Timing on /settings/backups names session, destination, mirror, domains, outcome, snapshots, drill and render once each. It follows the pattern in milestones.ts and boards-view.ts, the live fragment stays quiet, and the no-store branch is covered by a test. There is no UI, markup, security or dependency change, so docs/DESIGN.md is untouched. I could not reach the preview from my sandbox (the proxy blocked the host), so this is a read of the source and tests, not a look at the running page.

Two things I would do next:

  1. Re-indent the html template in the no-store branch of backupsPage. The lines after html are now one level too shallow inside shell( and rendered(.
  2. Name the second lastDrill read in drillLine, or pass the value already read into it, so drill and render do not hide a repeated query.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs