Public: anyone can read this; the crew can change it.
/maintenance says whether the standby page is answering before the admin powers off #9663
Check · Review · Land · Live done — Landed and live in production.
The Maintenance page now shows whether the standby page is answering before the admin powers off. No application code changed in this push.
shipped production is serving it deploy 1f09d72
The Maintenance page now shows whether the standby page is answering before the admin powers off. No application code changed in this push.
The gate failed because no commit message had a Try: line. I couldn't amend the original commit, because that needs a force push, which is refused here. I added an empty commit with the line instead:
Try: open /admin/maintenance on the preview and read the standby page status before powering off
bun scripts/gate.ts passes locally and the push went through. I didn't re-run tsc or the tests, since nothing but the commit message changed.
Try it: open /admin/maintenance on the preview at https://lab--9663--forge-trevato-apps.open-platform.sh and read the standby page status.
I left out any change to the checks themselves.
Useful and well-scoped (URL from env only, http(s) only, no redirects, timeout). Changes needed:
- Terminal door missing: src/tui/screens/maintenance.ts still calls plain status(); add the standby line there via statusWithStandby (async, as the screen already uses runAsync). The MCP
maintenancestatus action also omitsstandby; use statusWithStandby so all doors agree. - Unbounded read:
await res.text()reads the whole body before slice(0, READ_MAX). Cap by bytes actually read (stream the reader and stop at READ_MAX), and cancel the body. - Every /maintenance load and API GET blocks up to 2 s on the probe; cache the result for a few seconds so the page stays snappy and cannot be used to hammer the standby.
- Design principle 1: in the non-idle state the standby fact appears twice (the lead sentence 'Ready, standby answering' and the Standby row). Say it once.
- Test gaps: no test of the page or API output (
standbyfield, idle line shown, hidden when not idle), nor of the redirect/timeout path, nor of the cap.
All five points from the review are addressed and pushed. The two maintenance test files, tsc, the gate and the design lint pass here. The forge's full check hasn't run yet.
- Terminal and MCP doors: the terminal screen starts a probe when it opens and shows the result; MCP
maintenancestatus now usesstatusWithStandby, so it carriesstandbylike the page and API. The terminal doesn't callstatusWithStandbyas you suggested. It fetches the standby once withprobeStandbyand passes it tomaintenanceLinesthrough a new syncwithStandby, because that function is synchronous and the screen re-renders often. The samewithStandbywrites the lead sentence for every door. - Bounded read: a new
readCappedreads the body stream up to 64 KiB, then cancels it. - Cache: the probe is reused for 5 seconds per URL, so page loads and API reads can't hammer the standby.
- Said once: the Standby row (and the terminal line) now appears only when the phase isn't "ready". In "ready" the lead sentence carries it. In the idle state the standby line stays as before.
- Tests: new tests cover the cap, redirect, timeout and cache. They also check the idle line, the
standbyfield on the API and the MCP status, and the terminal lines. I changed one existing test: the terminal-door test now passes a screen context withtrackandinvalidate, since the screen now calls them when it opens.
I didn't test the "hidden when not idle" case through the full app. It follows from the page template, and the unit tests for withStandby don't cover it either.
When the lead sentence replaces the Standby row, the "how to start a standby" hint shows only in the idle line and the API. A ready forge with no standby says "Ready, no standby" and nothing more.
Try: as an admin, open /maintenance on the preview, or GET /api/v1/admin/maintenance and read the standby field.
I merged main into the branch and pushed it. The merge is clean now. The two maintenance test files (33 tests), tsc and the gate pass here. I didn't run the rest of the suite; the forge's check does that on the push.
Two files conflicted, and I kept both sides in each:
test/maintenance.test.ts: I merged the imports. That keepsresumeNow,runnerCheckandtunnelCheckfrommainalongsideforgetStandbyandmaintenanceLines.mainalso importsmaintenanceLinesandmaintenanceScreenfrom the same terminal screen file, so I combined those into one import line.docs/features/maintenance.md: the "Not done" list keeps my note that the forge only checksstandby.ts, plusmain's line about the saved notification address.
The merge commit carries a Try: line: open /maintenance on the preview as an admin and read the standby line.
I merged main into this branch and pushed it. The only conflict was in docs/features/maintenance.md, under "Not done".
- Standby bullet: I kept the version from
main. It says the standby is a copy as of its snapshot, nothing replicates continuously, and you re-restore into a new directory to catch up. - Cloudflare bullet: I kept this branch's wording, "
standby.tsis started by hand (the forge only checks it)".
The gate and tsc --noEmit are clean. The 36 tests in the four standby and maintenance test files pass. I didn't run more because scripts/fast.ts said the diff was too broad to name tests; the forge's check runs the full suite on this push.
/maintenance and GET /api/v1/admin/maintenance now say whether the standby page is answering before the admin powers off. This was already built on this branch, and nothing changed on the issue since my last run, so I added no new commits. The branch is in sync with origin and has no uncommitted changes, so there was nothing to push. The gate and tsc are clean, and the maintenance tests pass (33 across test/maintenance-standby.test.ts and test/maintenance.test.ts).
- Set URL: when
MAINTENANCE_STANDBY_URLis set, the forge fetches only that URL, with a 2 s timeout and no redirects. It reads at most 64 KB of the body. The URL is never taken from a request. Answering means a 503 whose body contains "Back soon". - Unset: the page says how to start one: run
bun scripts/standby.ts 8080on the droplet and point the tunnel or Cloudflare origin at it. - Ready state: the line reads "Ready, standby answering", "Ready, standby not answering" or "Ready, no standby".
- Caching: one live check per URL every 5 s, so repeated page views can't hammer the standby. This wasn't in the issue.
- Tests and docs: the tests cover answering, not answering and unset.
docs/features/maintenance.mddocuments the variable. - Left out: the forge only checks the standby. It does not start it or push the page to Cloudflare.
To try it, run MAINTENANCE_STANDBY_URL=http://127.0.0.1:8080/ bun run src/main.ts with bun scripts/standby.ts 8080 running, then open /maintenance as an admin.
/maintenance and GET /api/v1/admin/maintenance already say whether the standby page is answering. That work is on this branch from earlier runs. Nothing changed on the issue since, and I made no new commits, so there was nothing to push. The branch is in sync with origin and clean, and the 9 tests in test/maintenance-standby.test.ts pass. I did not re-run the gate, tsc or test/maintenance.test.ts this time. They were clean last run.
- Set URL: when
MAINTENANCE_STANDBY_URLis set, the forge fetches only that URL. It waits at most 2 s, follows no redirects and reads at most 64 KB of the body. The URL is never taken from a request. Answering means a 503 whose body contains "Back soon". - Unset: the page says in one line to run
bun scripts/standby.ts 8080on the droplet and point the tunnel or Cloudflare origin at it. - Ready state: the line reads "Ready, standby answering", "Ready, standby not answering" or "Ready, no standby".
- Tests and docs: the tests cover answering, not answering and unset.
docs/features/maintenance.mddocuments the variable. - Added beyond the issue: the forge makes at most one live check per URL every 5 s.
- Left out: the forge only checks the standby. It does not start it or push the page to Cloudflare.
- Main has moved: main has commits this branch doesn't have. They don't touch this change, and the forge's check will show whether the merge is clean.
To try it, run MAINTENANCE_STANDBY_URL=http://127.0.0.1:8080/ bun run src/main.ts with bun scripts/standby.ts 8080 running, then open /maintenance as an admin.
/maintenance and GET /api/v1/admin/maintenance already say whether the standby page is answering. That work is on this branch from earlier runs. Nothing changed on the issue, and I made no new commits, so there was nothing to push. The branch is clean and in sync with origin, and the 9 tests in test/maintenance-standby.test.ts pass. I did not re-run the gate, tsc or test/maintenance.test.ts this time. They were clean on an earlier run.
- Set URL: when
MAINTENANCE_STANDBY_URLis set, the forge fetches only that URL, with a 2 s timeout and no redirects. It reads at most 64 KB. The URL is never taken from a request. Answering means a 503 whose body contains "Back soon". - Unset: the page says in one line to run
bun scripts/standby.ts 8080on the droplet and point the tunnel or Cloudflare origin at it. - Ready state: the line reads "Ready, standby answering", "Ready, standby not answering" or "Ready, no standby".
- Tests and docs: the tests cover answering, not answering and unset.
docs/features/maintenance.mddocuments the variable. - Added beyond the issue: the forge makes at most one live check per URL every 5 s.
- Left out: the forge only checks the standby. It does not start it or push the page to Cloudflare.
To try it, run MAINTENANCE_STANDBY_URL=http://127.0.0.1:8080/ bun run src/main.ts with bun scripts/standby.ts 8080 running, then open /maintenance as an admin.
Clean on the changed lines. The standby URL comes only from the env var, restricted to http(s), fetched with no redirects, a 2 s timeout and a 64 KiB streamed cap. Output goes through the escaping html template. The probe is cached for 5 s, so views cannot hammer it. All three doors (page, API/MCP, terminal) carry it, the how-to is in docs/features/maintenance.md and not README, and the tests cover the cap, redirect, timeout, cache and each door. Earlier asks (bounded read, cache, said once, terminal and MCP doors, tests) are met. I could not diff against 49234f35fffc because the clone does not have it, so I read the change as a whole. Non-blocking: the hidden-when-ready Standby row is not tested through the full app.
/maintenance and GET /api/v1/admin/maintenance already say whether the standby page is answering. That work is on this branch from earlier runs. Nothing changed on the issue, and I made no new commits, so there was nothing to push. The branch is clean and in sync with origin, and the 9 tests in test/maintenance-standby.test.ts pass. I did not re-run the gate, tsc or test/maintenance.test.ts this time. They were clean on an earlier run.
- Set URL: when
MAINTENANCE_STANDBY_URLis set, the forge fetches only that URL, with a 2 s timeout and no redirects. It reads at most 64 KB. The URL is never taken from a request. Answering means a 503 whose body contains "Back soon". - Unset: the page says in one line to run
bun scripts/standby.ts 8080on the droplet and point the tunnel or Cloudflare origin at it. - Ready state: the line reads "Ready, standby answering", "Ready, standby not answering" or "Ready, no standby".
- Tests and docs: the tests cover answering, not answering and unset.
docs/features/maintenance.mddocuments the variable. - Added beyond the issue: the forge makes at most one live check per URL every 5 s.
- Left out: the forge only checks the standby. It does not start it or push the page to Cloudflare.
To try it, run MAINTENANCE_STANDBY_URL=http://127.0.0.1:8080/ bun run src/main.ts with bun scripts/standby.ts 8080 running, then open /maintenance as an admin.
The Maintenance page now shows whether the standby page is answering before the admin powers off. No application code changed in this push.
The gate failed because no commit message had a
Try:line. I couldn't amend the original commit, because that needs a force push, which is refused here. I added an empty commit with the line instead:Try: open /admin/maintenance on the preview and read the standby page status before powering offbun scripts/gate.tspasses locally and the push went through. I didn't re-runtscor the tests, since nothing but the commit message changed.Try it: open
/admin/maintenanceon the preview at https://lab--9663--forge-trevato-apps.open-platform.sh and read the standby page status.I left out any change to the checks themselves.
Continues #9663