|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +--- |
| 4 | + |
| 5 | +docs(spec): scope the email-template locale-floor claims to a call that NAMES a locale (#18056) |
| 6 | + |
| 7 | +Clause-②: yes — no accept set moves (no key is added, removed or revalidated), |
| 8 | +but what a PUBLISHED package states about its own resolution contract is |
| 9 | +corrected, which is a contract act in substance. |
| 10 | + |
| 11 | +`packages/spec` stated two different rung counts for one resolution. |
| 12 | +`EmailTemplateDefinitionSchema.locale`'s `describe` and the |
| 13 | +`EMAIL_TEMPLATE_FLOOR_LOCALE` docblock published **one** retry rung and an |
| 14 | +explicit "no fallback floor at all"; `SendTemplateInput.locale` in |
| 15 | +`contracts/email-service.ts`, same package, documents a **three-rung** ladder |
| 16 | +whose third rung is reachable exactly on the path the first says cannot exist. |
| 17 | + |
| 18 | +Measured against the runtime rather than reconciled by preference — |
| 19 | +`EmailService.resolveAndRenderTemplate` and `createSysEmailTemplateLoader` in |
| 20 | +`@objectstack/plugin-email`, and the CI pins in |
| 21 | +`template-locale-resolution.test.ts` — the three-rung text is the correct one: |
| 22 | + |
| 23 | +1. the named locale, matched exactly (no language-subtag folding); |
| 24 | +2. the literal `en-US`, which is also where a call naming no locale starts; |
| 25 | +3. **only for a call that named no locale**, and only when the bundle carries |
| 26 | + no `en-US` row: the bundle's lowest locale tag. |
| 27 | + |
| 28 | +So a bundle with no `en-US` row dead-letters (`TEMPLATE_NOT_FOUND`, permanent) |
| 29 | +for every recipient whose locale was NAMED, and silently renders whichever |
| 30 | +language sorts first for every call that named none. The shipped declaration |
| 31 | +promised the loud permanent refusal on the path where the runtime performs the |
| 32 | +silent fill; an author reading it was told a missing locale always |
| 33 | +dead-letters. Both call shapes are now named wherever the floor is claimed, and |
| 34 | +the ladder itself is stated in one place only. |
| 35 | + |
| 36 | +Also corrected: `SendTemplateInput.template` said the service "picks the |
| 37 | +best-matching locale row", which the resolver has never done — there is no |
| 38 | +best match and no folding, only the ladder above. |
| 39 | + |
| 40 | +`defineStack`'s `warnEmailTemplateLocaleFloor` gains a declaration of the two |
| 41 | +shapes it deliberately does NOT examine (a stack whose `i18n.supportedLocales` |
| 42 | +is absent or empty; a bundle whose tags all fall outside `supportedLocales`) — |
| 43 | +both can still ship a floorless bundle. Its logic is unchanged and its warning |
| 44 | +stays advisory; whether either shape should warn is the ADR-0049 |
| 45 | +enforce-or-remove question and is not answered here. Both shapes are now pinned |
| 46 | +against a warning discriminator so neither can change without a test saying so. |
0 commit comments