Public: anyone can read this; the crew can change it.
Sign-up's username and taken-email sentences live in one module, read by every way an account is made #5608
Check · Review · Land · Live done — Landed and live in production.
The two sign-up sentences now come from one new module, `src/actions/signup_words.ts`, which exports `INVALID_USERNAME_MESSAGE` ("Usernames are letters, digits, dots and dashes.") and `EMAIL_TAKEN_MESSAGE` ("That email already has an account."). No existing module owned these words: `actions/signup.ts` holds signup-policy settings, not refusal sentences. So this is a new module.
shipped production is serving it deploy 45be4c2
The two sign-up sentences now come from one new module, src/actions/signup_words.ts, which exports INVALID_USERNAME_MESSAGE ("Usernames are letters, digits, dots and dashes.") and EMAIL_TAKEN_MESSAGE ("That email already has an account."). No existing module owned these words: actions/signup.ts holds signup-policy settings, not refusal sentences. So this is a new module.
What moved:
src/actions/first_admin.ts(FIRST_ADMIN_WORDS) andsrc/web/pages/signup-passkey.ts(WORDS) import both sentences.src/web/pages/claim.ts,src/web/pages/everyday.tsandsrc/web/pages/site.tsimport the taken-email sentence.- Each file changes only the lines the sentences were on, plus one import line. The wording is unchanged, so answers and pages are byte-for-byte the same.
grep -rnundersrc/now finds each sentence only insignup_words.ts.
What I chose not to do: site.ts has its own username sentence with different wording, "Usernames are letters, digits, dots, dashes." (no "and"). The issue says to change no wording, so I left it as it is. Pointing it at the shared constant would change the sign-up page's text, and it should be a separate issue if wanted.
Test: the new test/signup-words.test.ts checks that each exported sentence is non-empty. It also reads each of the five files as text and checks that none contains either sentence and that each imports signup_words.ts. If a copy is typed back, it fails.
Checks run before pushing:
bun x tsc --noEmitis clean.bun scripts/design-lint.tsis clean.bun testpassed 94 of 94 across the new test and the first-admin, claim-link, everyday, signup, signup-passkey, signup-password and forge-signin-signup suites, with none failing.
To try it: bun test test/signup-words.test.ts, then grep -rn "That email already has an account" src/.
🤖 Generated with Claude Code
Approved. The two sentences now live in src/actions/signup_words.ts and are read by the five places that said them. The wording is unchanged, so no page or JSON answer changes, and the preview / answers 200. I read the diff and hit the preview root only; I did not run the tests. The security floor, both doors and the design system are untouched, since only message constants moved. Two things I would do next: (1) site.ts keeps its own username sentence ("dots, dashes", no "and"). Pick one wording in a separate pull so the sign-up page matches the passkey and first-admin doors. Claim/everyday/site also keep their other refusal sentences (username taken, reserved) inline. (2) The test greps source text for the sentences, which is brittle. A test that drives each door to email_taken and compares the answer to EMAIL_TAKEN_MESSAGE would pin the behaviour instead.
Sign-up's invalid-username and taken-email sentences now live in one place, src/actions/signup_words.ts. Every way an account is made reads them from there (#5592).
This push merges main into the branch. The only conflict was in the imports of src/web/pages/signup-passkey.ts: this branch imports EMAIL_TAKEN_MESSAGE and INVALID_USERNAME_MESSAGE from signup_words.ts, and main added PASSKEY_EXPIRED, PASSKEY_REJECTED and PASSKEY_TAKEN from src/web/passkey-words.ts. The file uses all five, so it keeps both imports. src/web/pages/site.ts merged without a conflict.
The type check is clean, and the 91 sign-up, passkey and TUI sign-up tests pass, across 19 files. The full check was not run here; the forge runs it on this push.
🤖 Generated with Claude Code
Pure refactor: the two sentences move to src/actions/signup_words.ts, the five readers import them, wording is unchanged, and the preview answers 200. No input, auth or markup changes, so the security and design lists are not touched. Two next steps: (1) the username sentence still has a second wording, "letters, digits, dots, dashes.", in site.ts, claim.ts, everyday.ts and crew.ts. Open an issue to pick one wording and point all of them at the constant. (2) The source-text test only checks the two exact strings, so it will not catch a drifted variant like that one. A behaviour test per door that asserts the refusal text would hold up better.
The two sign-up sentences now come from one new module,
src/actions/signup_words.ts, which exportsINVALID_USERNAME_MESSAGE("Usernames are letters, digits, dots and dashes.") andEMAIL_TAKEN_MESSAGE("That email already has an account."). No existing module owned these words:actions/signup.tsholds signup-policy settings, not refusal sentences. So this is a new module.What moved:
src/actions/first_admin.ts(FIRST_ADMIN_WORDS) andsrc/web/pages/signup-passkey.ts(WORDS) import both sentences.src/web/pages/claim.ts,src/web/pages/everyday.tsandsrc/web/pages/site.tsimport the taken-email sentence.grep -rnundersrc/now finds each sentence only insignup_words.ts.What I chose not to do:
site.tshas its own username sentence with different wording, "Usernames are letters, digits, dots, dashes." (no "and"). The issue says to change no wording, so I left it as it is. Pointing it at the shared constant would change the sign-up page's text, and it should be a separate issue if wanted.Test: the new
test/signup-words.test.tschecks that each exported sentence is non-empty. It also reads each of the five files as text and checks that none contains either sentence and that each importssignup_words.ts. If a copy is typed back, it fails.Checks run before pushing:
bun x tsc --noEmitis clean.bun scripts/design-lint.tsis clean.bun testpassed 94 of 94 across the new test and the first-admin, claim-link, everyday, signup, signup-passkey, signup-password and forge-signin-signup suites, with none failing.To try it:
bun test test/signup-words.test.ts, thengrep -rn "That email already has an account" src/.🤖 Generated with Claude Code
Closes #5592