Public: anyone can read this; the crew can change it.
The passkey ceremony's sentences live in one module, read by sign-in, sign-up and Settings #5601
Check · Review · Land · Live done — Landed and live in production.
I added a new module, `src/web/passkey-words.ts`, that holds the three sentences a passkey ceremony says when it fails. No existing module already owned them. The constants are `PASSKEY_EXPIRED` ("That request expired. Try again."), `PASSKEY_REJECTED` ("That passkey was not accepted.") and `PASSKEY_TAKEN` ("That passkey is already registered."). The module imports nothing, so any file can read fro…
shipped production is serving it deploy 3b6d43c
I added a new module, src/web/passkey-words.ts, that holds the three sentences a passkey ceremony says when it fails. No existing module already owned them. The constants are PASSKEY_EXPIRED ("That request expired. Try again."), PASSKEY_REJECTED ("That passkey was not accepted.") and PASSKEY_TAKEN ("That passkey is already registered."). The module imports nothing, so any file can read from it without creating an import cycle.
What moved:
src/web/pages/passkeys.ts(adding a key in Settings, passkey sign-in, recovery): seven typed copies now use the constants.src/web/pages/signup-passkey.ts: the tworefuse(400, …)sentences andWORDS.passkey_takennow use the constants.src/web/passkey-ceremony.ts: theexpired,rejectedandtakenentries ofPASSKEY_WORDSpoint at the constants. Themissingsentence stays where it was, since only this file says it.
Only the lines holding the sentences changed, plus one import per file. No wording changed. grep -rn for each sentence under src/ now finds only the new module. The existing test test/claim-link.test.ts still checks the rendered "already registered" text and passes unchanged.
The new test/passkey-words.test.ts checks two things: that the module exports exactly these three non-empty sentences, and that each of the three files imports the module and contains none of the sentences as text. To prove it catches drift, I typed one sentence back into passkey-ceremony.ts: the test failed (3 pass, 1 fail), and I removed the copy before committing.
Before pushing:
- The new test plus the 26 test files covering the files I touched (passkeys, sign-up, claim, everyday, first admin, recovery): 174 pass, 0 fail.
bun x tsc --noEmitis clean after abun install.bun scripts/design-lint.tsreports nothing.
🤖 Generated with Claude Code
A behaviour-neutral refactor: the three passkey failure sentences now live in src/web/passkey-words.ts, and sign-in, sign-up, recovery and Settings all read them from there. Wording is unchanged and no new dependency, route or input is added. On the base, every typed copy sits on the lines the diff replaces, so nothing is left over in those three files. The preview answers 200 on /. The web-facing wording is unchanged, so the design system is unaffected. I did not run bun test here; the pull is green.
Two things I would do next:
- The guard test checks source text with readFileSync and includes(). It would also fail on a harmless comment that quotes a sentence. Add one behavioural assertion, such as an expired challenge POST returning PASSKEY_EXPIRED, so the guard does not rely only on grep.
- src/web/pages/account.ts:869 ("That key is already registered on this account.") and src/tui/screens/common.ts:842 are near-duplicates with their own wording. Decide whether they should share the module or are deliberately different.
I added a new module,
src/web/passkey-words.ts, that holds the three sentences a passkey ceremony says when it fails. No existing module already owned them. The constants arePASSKEY_EXPIRED("That request expired. Try again."),PASSKEY_REJECTED("That passkey was not accepted.") andPASSKEY_TAKEN("That passkey is already registered."). The module imports nothing, so any file can read from it without creating an import cycle.What moved:
src/web/pages/passkeys.ts(adding a key in Settings, passkey sign-in, recovery): seven typed copies now use the constants.src/web/pages/signup-passkey.ts: the tworefuse(400, …)sentences andWORDS.passkey_takennow use the constants.src/web/passkey-ceremony.ts: theexpired,rejectedandtakenentries ofPASSKEY_WORDSpoint at the constants. Themissingsentence stays where it was, since only this file says it.Only the lines holding the sentences changed, plus one import per file. No wording changed.
grep -rnfor each sentence undersrc/now finds only the new module. The existing testtest/claim-link.test.tsstill checks the rendered "already registered" text and passes unchanged.The new
test/passkey-words.test.tschecks two things: that the module exports exactly these three non-empty sentences, and that each of the three files imports the module and contains none of the sentences as text. To prove it catches drift, I typed one sentence back intopasskey-ceremony.ts: the test failed (3 pass, 1 fail), and I removed the copy before committing.Before pushing:
bun x tsc --noEmitis clean after abun install.bun scripts/design-lint.tsreports nothing.🤖 Generated with Claude Code
Closes #5582