emails: copy has no automated language gate, and tests are the wrong fix #128

Stängd
öppnade 2026-07-31 20:58:41 +00:00 av supernaut · 0 kommentarer
Ägare

Transactional email copy in src/i18n/emails.ts has no automated protection of any kind — no tests, and no language check that runs unless somebody remembers to run it.

This was a deliberate choice while building the sign-up journey, not an oversight, and it is recorded here so nobody rediscovers it as one. But the risk it accepted turned out to be real.

What actually went wrong

During that work the email copy was revised three times, each time fixing a genuine fault, each caught by a human reading it late:

  1. A call to action that contradicted its link. The continue-your-sign-up link was labelled "Börja om" / "Start again" — telling users to restart the sign-up they were being invited to finish. In both languages.
  2. Grammar in the shared footer tagline. "Ägt, drivet och driftat" uses participles that must agree with a subject the sentence never names.
  3. An instruction that excluded half its audience. "När du har sparat din passkey …" left out users who set a password instead.

None of these are typos. Each was fluent, plausible copy that said the wrong thing.

Why tests are probably the wrong tool

The obvious reflex is snapshot or equality assertions on the strings. That mostly asserts a string equals itself: it catches accidental deletion or a bad merge, and it catches none of the three faults above. Worse, it makes copy edits noisy — every intentional wording change becomes a test update, which trains people to update the expectation without reading it.

The failure mode here is semantic: does this sentence match what the link does, does it cover every user it addresses, is it grammatical. Assertions on literals cannot see any of that.

What would help instead

Run the existing content-style check automatically on changed content and i18n files, rather than on request. It already encodes the known faults as rules — including, since this work, the tagline form and keeping "passkey" in English — so it would at minimum stop the same mistakes recurring silently.

Candidate hook: the lefthook pre-commit path that already formats and lints staged files, scoped to src/content/** and src/i18n/*.ts.

Be honest about the ceiling. The checker is a regex heuristic. It cannot tell that a link labelled "start again" points at a continue flow, and it cannot judge tone. So this reduces recurrence of known faults; it does not catch a new instance of the class. Reviewing email copy by reading it, in both languages, stays necessary — the real value here is that the known faults stop needing a human to remember them.

Done when

A change to src/content/** or src/i18n/*.ts runs the content-style check without anyone invoking it, and the check's failure output names the style-guide rule it is enforcing.

Transactional email copy in `src/i18n/emails.ts` has no automated protection of any kind — no tests, and no language check that runs unless somebody remembers to run it. This was a deliberate choice while building the sign-up journey, not an oversight, and it is recorded here so nobody rediscovers it as one. But the risk it accepted turned out to be real. ## What actually went wrong During that work the email copy was revised three times, each time fixing a genuine fault, each caught by a human reading it late: 1. **A call to action that contradicted its link.** The continue-your-sign-up link was labelled "Börja om" / "Start again" — telling users to restart the sign-up they were being invited to finish. In both languages. 2. **Grammar in the shared footer tagline.** "Ägt, drivet och driftat" uses participles that must agree with a subject the sentence never names. 3. **An instruction that excluded half its audience.** "När du har sparat din passkey …" left out users who set a password instead. None of these are typos. Each was fluent, plausible copy that said the wrong thing. ## Why tests are probably the wrong tool The obvious reflex is snapshot or equality assertions on the strings. That mostly asserts a string equals itself: it catches accidental deletion or a bad merge, and it catches none of the three faults above. Worse, it makes copy edits noisy — every intentional wording change becomes a test update, which trains people to update the expectation without reading it. The failure mode here is semantic: does this sentence match what the link does, does it cover every user it addresses, is it grammatical. Assertions on literals cannot see any of that. ## What would help instead Run the existing content-style check automatically on changed content and i18n files, rather than on request. It already encodes the known faults as rules — including, since this work, the tagline form and keeping "passkey" in English — so it would at minimum stop the *same* mistakes recurring silently. Candidate hook: the lefthook pre-commit path that already formats and lints staged files, scoped to `src/content/**` and `src/i18n/*.ts`. **Be honest about the ceiling.** The checker is a regex heuristic. It cannot tell that a link labelled "start again" points at a continue flow, and it cannot judge tone. So this reduces recurrence of known faults; it does not catch a new instance of the class. Reviewing email copy by reading it, in both languages, stays necessary — the real value here is that the known faults stop needing a human to remember them. ## Done when A change to `src/content/**` or `src/i18n/*.ts` runs the content-style check without anyone invoking it, and the check's failure output names the style-guide rule it is enforcing.
supernaut lade till detta till projektet Bitborg Web 2026-07-31 22:36:55 +00:00
Logga in för att delta i denna konversation.
Ingen milstolpe
Inget projekt
Inga tilldelade
1 deltagare
Notiser
Förfallodatum
Förfallodatumet är ogiltigt eller utanför gränserna. Använd formatet "åååå-mm-dd".

Inget förfallodatum satt.

Beroenden

Inga beroenden satta

Referens
bitborg/bitborg-web#128
Ingen beskrivning angiven.