🤖 Opened by Claude (Opus 5.5), agent account.
Why
Fable 5.1's low findings on #19 (feat/door-copy, merged as 0b4d92c). That review approved the branch with nits; this branch fixes findings 2–6. Nothing in English's words changes, and no app has to change: the package helpers SignInEmail, EmailSubject and EmailBody keep their signatures and output.
What (chrome/door.go, chrome/door_test.go; +111/−28)
- Finding 2, cite the real spec. The "spec §9.2/§10" citations now name the document: the shared-chrome spec,
docs/superpowers/specs/2026-09-23-shared-chrome.md on estilo's spec/shared-chrome branch (§9.2 "Copy deck (en, used everywhere)", §10 "Page titles and names"). DoorCopy's comment says that once. The other comments and tests say "shared-chrome spec §9.2/§10". The spec isn't on main, so the comment names the branch it lives on. - Finding 3, the email belongs to the copy set. New methods
DoorCopy.SignInEmail(app, link), MailSubject(app) and MailBody(app) read the set's own EmailSubject/EmailBody, so a locale's email can now be reached. They can't be named EmailSubject and the like, because Go doesn't allow a method with the same name as a field. The package-level SignInEmail/EmailSubject/EmailBody are now English's (English.SignInEmail(…) and so on). - Finding 4, tokens rather than Printf.
SignedOutBody and the email formats use {app} and {link} slots, filled in one pass by strings.NewReplacer. A translation without a slot, or with a literal "%", now renders as written instead of producing "%!(EXTRA string=…)". A link that happens to contain {app} isn't rewritten. No app sets Copy (I grepped all eight door-copy branches), so changing the format breaks nothing. - Finding 5, translations.
DoorCopy's comment states the plan: translations are estilo's follow-up, never an app's. English is the only set today. When the suite needs another locale, estilo adds that locale's whole DoorCopy next to English, with a way to choose one by locale, and every app gets it on its next re-pin. An app passes Copy only a set that estilo publishes. It never writes or edits one. - Finding 6.
var minutes → linkMinutes.
Not in scope here: finding 7 (LinkLifetime == auth.LinkTTL) has to wait until estilo moves off github.com/carlosframework/rastrillo v0.23.0, as Fable said.
Tests: red, then green
TestDoorCopyFillsTokensNotFormats and TestSignInEmailReadsTheCopy are new. I ran them first against stub methods that delegated to the English package helpers, on the old Sprintf code:
--- FAIL: TestDoorCopyFillsTokensNotFormats (0.00s)
door_test.go:155: signed-out body "100%!d(string=Docs)one: {app} is signed out here."
door_test.go:159: a body without {app} rendered "Signed out.%!(EXTRA string=Docs)"
--- FAIL: TestSignInEmailReadsTheCopy (0.00s)
door_test.go:181: locale email = "Sign in to Docs", "Here’s your link to sign in to Docs:\n\nL\n\nIt works once and expires in 15 minutes. …"
door_test.go:184: MailSubject "Sign in to Docs"
door_test.go:187: MailBody "Here’s your link to sign in to Docs:\n\nL\n\nIt works once and expires in 15 minutes. …"
Both pass now. Every existing pin (TestDoorCopyIsPinned, TestDoorTitles, TestTheDoorDrawsItsOwnCopy, TestSignInEmail, and the twelve-locale test, which now uses {app}) is unchanged and green. make test passes locally with GOTOOLCHAIN=go1.26.5, and hub CI passed at 1a41328.
Consumers
None needed. The app branches can re-pin whenever they like, and nothing in them changes.
Stop before merge: this needs the whole-branch review per AGENTS.md.