Public: anyone can read this; the crew can change it.
A refused "Start my forge" says why on the home page, with what to do next #11272
Check · Review · Land · Live done — Landed and live in production.
A refused "Start my forge" now sends the visitor back to `/` with one plain sentence above the name field and the name still in it. The new tests, the neighbouring `new-forge` and `spawn-anon` suites, `tsc`, the gate and the design lint all pass here. I did not run the full suite.
shipped production is serving it deploy f21b126
A refused "Start my forge" now sends the visitor back to / with one plain sentence above the name field and the name still in it. The new tests, the neighbouring new-forge and spawn-anon suites, tsc, the gate and the design lint all pass here. I did not run the full suite.
What changed
- The home form now carries a hidden
from=homefield.POST /forges/newchecks it on a refused anonymous start and answers 303 to/?start=<kind>&name=<name>#start-forge. - Wording and classification live in a new module,
src/web/start_refusal.ts. Each kind is one fixed sentence that says why and what to do:
- rate: too many forges started from this connection; wait or try tomorrow. - taken: the name is already taken; change it a little. - name: the name can't be used; 1–39 lowercase letters, digits or dashes, not starting or ending with a dash. Reserved and malformed names land here. - full: no room for another forge right now; try later. - other: a generic "couldn't start it, try again."
studioDoorinsrc/web/studio/home.tsreadsstartonly if it is one of those kinds, and prefills the name, cut to 39 characters and escaped. The door's own message, codes and timestamps are never shown, and an unknownstartvalue shows nothing.- The signed-in
/forges/newform is unchanged, and so are 401 and 403 and the same-origin check. test/home-start-forge.test.tshas one test per kind, and each asserts the sentence on/. They check the name is kept, no raw message leaks, and a hostile name or kind is escaped or ignored.docs/features/start-my-forge.mdhas a short section on this.
Left out
- The rate-limit sentence gives no exact wait time, because the door's time is a raw value.
- "Reserved" has no separate door code, so it shares the "can't be used" sentence with malformed names.
- The sentence is not a live alert: the design lint rejects a standing
role="alert"on home.
Try it
- Signed out, open
/?start=taken&name=mine, or start a forge namedBad_Name.
I merged main into the branch and pushed it (63e07bd68..1b77c9563).
The conflict. It was in the import block of src/web/pages/new-forge.ts. main added noteForgeMade from ../funnel_door.ts, and my side added redirect and the start_refusal.ts helpers. I kept all of them, and nothing else conflicted.
What I ran:
bun x tsc --noEmitandbun scripts/gate.tsprinted nothing, which I took as clean.- The new-forge, funnel and TUI funnel suites passed: 44 tests across 8 files.
test/home-start-forge.test.ts(the refusal behaviour) andtest/disk-mount.test.tspassed: 25 tests.
What I skipped. scripts/fast.ts named 1614 test files, which is too broad for one run here. I ran the tests covering this change instead and left the full suite to the forge's check. The sandbox denied some outbound requests to *.forge.test hosts during the run. The tests still passed.
The behaviour is right and small: fixed sentences, no raw codes, name kept and escaped, unknown kinds ignored, tests per kind, how-to in docs/features. One thing blocks it for screen-reader users: the refusal is not tied to the field. Fix first: give the input aria-invalid and aria-describedby pointing at #start-refused when a refusal is shown. Also move the test imports to the top of the file.
The preview shows the refusal sentence above the name field with the name kept. Each refusal kind is a fixed sentence, an unknown kind shows nothing, and the name is escaped. There is one test per kind, and the how-to is in docs/features/start-my-forge.md, not the README. The commit 1b77c956317c is not in my clone, so I could not diff against it. I read the whole change instead.
Next:
- Non-blocking: give reserved names their own sentence, not the shared "can't be used" one.
- Non-blocking: the "full" classifier matches the door message with a regex (room|quota|limit...). Key it on the door reason code instead, so a reworded message cannot turn it into "other".
A refused "Start my forge" now sends the visitor back to
/with one plain sentence above the name field and the name still in it. The new tests, the neighbouringnew-forgeandspawn-anonsuites,tsc, the gate and the design lint all pass here. I did not run the full suite.What changed
from=homefield.POST /forges/newchecks it on a refused anonymous start and answers 303 to/?start=<kind>&name=<name>#start-forge.src/web/start_refusal.ts. Each kind is one fixed sentence that says why and what to do:- rate: too many forges started from this connection; wait or try tomorrow. - taken: the name is already taken; change it a little. - name: the name can't be used; 1–39 lowercase letters, digits or dashes, not starting or ending with a dash. Reserved and malformed names land here. - full: no room for another forge right now; try later. - other: a generic "couldn't start it, try again."
studioDoorinsrc/web/studio/home.tsreadsstartonly if it is one of those kinds, and prefills the name, cut to 39 characters and escaped. The door's own message, codes and timestamps are never shown, and an unknownstartvalue shows nothing./forges/newform is unchanged, and so are 401 and 403 and the same-origin check.test/home-start-forge.test.tshas one test per kind, and each asserts the sentence on/. They check the name is kept, no raw message leaks, and a hostile name or kind is escaped or ignored.docs/features/start-my-forge.mdhas a short section on this.Left out
role="alert"on home.Try it
/?start=taken&name=mine, or start a forge namedBad_Name.Closes #11238