Public: anyone can read this; the crew can change it.
The terminal's deploys screen redeploys or stops production with a key and a confirm, as MCP `app_deploy` does #7250
Check · Review · Land · Live done — Landed and live in production.
A writer can now redeploy production from the terminal's **Deploys** screen with `D` and stop it with `S`. These are the same actions the app page and MCP `app_deploy` use (`redeployEnv`, `stopEnv`), so the access rules stay in `src/actions/apps.ts`.
shipped production is serving it deploy 4db86a7
A writer can now redeploy production from the terminal's Deploys screen with D and stop it with S. These are the same actions the app page and MCP app_deploy use (redeployEnv, stopEnv), so the access rules stay in src/actions/apps.ts.
Screen (src/tui/screens/deploys.ts)
- Each key opens a one-line confirm:
Redeploy production of alice/lamp?orStop production of alice/lamp?. Cancel and Esc call nothing. - The answer appears as a toast in the page's own words:
Redeploying production.,production already serves that commit., orStopped production. Its data is kept; Redeploy brings it back.A refusal toasts in red. - The title line now ends with production's state, read through
appView, for example· running. - The keys are offered only to a writer (
canWrite, the rulerequireWriterapplies) of an app the forge is serving. When production is stopped, onlyDis offered. A reader gets neither key, and pressing them does nothing.
Shared words (src/actions/env_words.ts, new): envLabel and the redeploy, stop and refusal sentences moved here out of src/web/pages/apps.ts, so the page and the terminal say them from one place. This follows the pull_ask_words.ts pattern. The page's text is unchanged.
Test (test/tui-deploy-act.test.ts): uses a runtime that is created but not started, with its deploy, stop and status calls faked, the same way tui-deploys.test.ts fakes envStatus. It covers:
- redeploy after the confirm calls
redeployEnv - cancelling calls nothing
- stop, after which the screen reads stopped and offers only redeploy
- a reader sees neither key
Checks run: bun x tsc --noEmit, bun scripts/design-lint.ts, and 8 test files including mcp-size, mcp-app-deploy, web-apps and redeploy-refusals: 81 pass, 0 fail. scripts/fast.ts named 909 test files for this diff, so I picked these myself rather than run them all.
Not done: redeploying or stopping a pull's preview from the terminal (the issue asks for production only), and no MCP change; the size ceilings are untouched. docs/TOUR.md gets one paragraph on the new keys.
To try it: ssh into the forge as a writer, open an app repository's Deploys screen, press D, then Enter. Then press S, then Enter, and the title line reads · stopped.
🤖 Generated with Claude Code
The design is right: same actions as the page and MCP, access rules left in src/actions/apps.ts, shared words in env_words.ts, keys hidden from readers. One miss, fix it in one round.
- (test/tui-deploy-act.test.ts) The refusal path has no test. The description and TOUR say a refusal toasts in red in the page words, but no test makes redeployEnv or stopEnv refuse. Delete the refused branch in
confirmin src/tui/screens/deploys.ts and every test still passes. Add a test where the runtime is unavailable or nothing_to_deploy, press D then enter, and assert the refusal sentence ("Nothing is serving apps on this forge." or the nothing_to_deploy text) shows and nothing is deployed. Also add a test for a writer whose email is unverified; see 2.
- (src/actions/env_words.ts, REFUSALS) redeployEnv also refuses with the requireVerified error, and a terminal writer with an unverified email gets only "That could not be done.". Name that error with a sentence that says what to do, and make not_found and no_such_env say something plain in the terminal too, since envFailure only maps them to a 404 on the page. This helps the page as well.
- (src/tui/screens/deploys.ts, readProduction) The
writercheck duplicates the rule in requireWriter. Prefer asking the action for it, or keep canWrite and note it in the test: a writer whose repo is not served has no keys, and that case is untested. Add it.
Next after this: preview redeploy and stop from the terminal, and a TOUR line on the refusal wording.
A writer can redeploy production from the terminal's Deploys screen with D and stop it with S, each after a one-line confirm. The screen uses the same actions as the app page and MCP app_deploy (redeployEnv, stopEnv), so the access rules stay in src/actions/apps.ts. The answer is a toast in the page's own words, now kept in one module, src/actions/env_words.ts. Production's state shows on the title line. A stopped production offers only D, and a reader gets neither key. This round makes the changes from the review.
Refusal wording (src/actions/env_words.ts):
unverifiednow gets a sentence built fromVERIFY_FIRST: "Verify your email first: open the link the forge mailed you, or ask for another from your account settings."not_foundsays "This repository has no app."no_such_envsays what an environment is called: production, or a pull's preview aspull-N.- The page still answers
not_foundandno_such_envwith a 404 before it asks for a sentence, so only its unverified message changes; that one used to be the generic "That could not be done."
Write check (src/tui/screens/deploys.ts): I kept canWrite to decide which keys to show. A comment now says it applies the same rule as requireWriter, and that the actions check again and refuse in words, so a writer with an unverified email still sees D. There is no action today that answers "may this person redeploy", and adding one only to show a key seemed like more than this change needs.
New tests (test/tui-deploy-act.test.ts):
- A redeploy refused as
nothing_to_deploy, on an app whose repository has no commits: the page's sentence appears, the toast is red, and nothing deploys. - A writer whose email is unverified gets the verify sentence in a red toast, and nothing deploys.
- A writer whose repository has no app is offered neither key, and pressing them does nothing.
The tests wrap app.toast to record each toast's colour, so dropping the red tone on a refusal now fails a test.
Checks run: type check plus 5 test files (tui-deploy-act, tui-deploys, web-apps, redeploy-refusals, mcp-size): 43 pass, 0 fail.
The next steps the review named, preview redeploy and stop from the terminal and a docs/TOUR.md line on the refusal wording, are left for a follow-up.
🤖 Generated with Claude Code
A writer can redeploy production from the terminal's Deploys screen with D and stop it with S, each after a one-line confirm that names the app and production. The screen calls the same actions as the app page and MCP app_deploy (redeployEnv, stopEnv), so the rules stay in src/actions/apps.ts. The answer comes back as a toast in the page's own words, and refusals are red.
The title line shows production's state. A stopped production offers only D, and a reader gets neither key. So does a writer whose repository has no app.
What moved where
src/actions/env_words.ts(new):envLabeland the redeploy, stop and refusal sentences now live here, moved out ofsrc/web/pages/apps.ts. The page and the terminal read them from one place, as withpull_ask_words.ts.- New refusal sentences:
- an unverified email is told to verify it (built from VERIFY_FIRST);
- not_found says the repository has no app;
- no_such_env says what an environment is called.
On the page only the unverified one shows up, replacing the generic "That could not be done."; the page still answers the other two with a 404 before it asks for words.
src/tui/screens/deploys.ts: decides which keys to show withcanWrite, the same rulerequireWriterapplies. The actions still check on their own and refuse in words, so a writer with an unverified email seesDand is told why it was refused.
Tests
test/tui-deploy-act.test.ts uses an apps runtime that is created but not started, with its deploy, stop and status calls faked. It covers:
- redeploy after confirm, and cancel calling nothing;
- stop, after which the screen reads stopped and only redeploy is left;
- a reader, who gets no keys;
- a
nothing_to_deployrefusal: the page's sentence, a red toast, nothing deployed; - an unverified writer: told to verify, nothing deployed;
- a writer whose repository has no app, who gets no keys.
About the last red check: it failed on the timing test "GET /apps/open names its phases and answers within its budget", in a run where the forge also killed three shards. This pull does not change /apps/open: the diff in src/web/pages/apps.ts only replaces the redeploy and stop words. That test passed four times here, both alone and alongside the deploy and page tests. I merged current main and changed no code for it; if it fails again, it's worth looking at the test's budget.
Checks run here: type check, plus cloud-org-apps-timing, tui-deploy-act, tui-deploys, web-apps, redeploy-refusals and mcp-size: 48 pass, 0 fail.
Left out on purpose: redeploying or stopping a pull's preview from the terminal, and a docs/TOUR.md line on the refusal wording. The review named both as the next steps.
To try it: ssh into the forge as a writer of an app's repository, open its Deploys screen, then press D and Enter. Then press S and Enter, and the title line ends · stopped.
🤖 Generated with Claude Code
Earlier asks: the refusal wording (unverified, not_found, no_such_env) now lives in env_words.ts and is tested (nothing_to_deploy and unverified assert the red toast and that nothing deploys). A writer with no app is offered neither key, and that is tested. The canWrite choice is explained in a comment, and the actions re-gate. The TOUR.md paragraph for D/S is added. The terminal does what the page does, through the same redeployEnv/stopEnv. No web markup was touched beyond the shared words, so there is no design change. I could not diff against 50d0341081d9 (the commit is not in the clone), so I judged the current change as a whole.
Non-blocking: the header comment in test/tui-deploy-act.test.ts runs long on one line; rewrap it. Non-blocking next steps: preview redeploy/stop from the terminal, and a keyboard hint on the Deploys screen when D/S are hidden for a reader.
A writer can now redeploy production from the terminal's Deploys screen with
Dand stop it withS. These are the same actions the app page and MCPapp_deployuse (redeployEnv,stopEnv), so the access rules stay insrc/actions/apps.ts.Screen (
src/tui/screens/deploys.ts)Redeploy production of alice/lamp?orStop production of alice/lamp?. Cancel and Esc call nothing.Redeploying production.,production already serves that commit., orStopped production. Its data is kept; Redeploy brings it back.A refusal toasts in red.appView, for example· running.canWrite, the rulerequireWriterapplies) of an app the forge is serving. When production is stopped, onlyDis offered. A reader gets neither key, and pressing them does nothing.Shared words (
src/actions/env_words.ts, new):envLabeland the redeploy, stop and refusal sentences moved here out ofsrc/web/pages/apps.ts, so the page and the terminal say them from one place. This follows thepull_ask_words.tspattern. The page's text is unchanged.Test (
test/tui-deploy-act.test.ts): uses a runtime that is created but not started, with its deploy, stop and status calls faked, the same waytui-deploys.test.tsfakesenvStatus. It covers:redeployEnvChecks run:
bun x tsc --noEmit,bun scripts/design-lint.ts, and 8 test files includingmcp-size,mcp-app-deploy,web-appsandredeploy-refusals: 81 pass, 0 fail.scripts/fast.tsnamed 909 test files for this diff, so I picked these myself rather than run them all.Not done: redeploying or stopping a pull's preview from the terminal (the issue asks for production only), and no MCP change; the size ceilings are untouched.
docs/TOUR.mdgets one paragraph on the new keys.To try it: ssh into the forge as a writer, open an app repository's Deploys screen, press
D, then Enter. Then pressS, then Enter, and the title line reads· stopped.🤖 Generated with Claude Code
Closes #7231