Skip to content

Commit c4f538e

Browse files
committed
docs(#13881): contract-review patch round 1 — two auth-manager docblocks, second behaviour change + TEMPLATE_* asymmetry in the changeset, wording
1 parent aa2994e commit c4f538e

4 files changed

Lines changed: 37 additions & 15 deletions

File tree

.changeset/notification-locale-per-recipient.md

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ deferral.
1919

2020
Now the locale is resolved PER RECIPIENT, at delivery time, through ONE read
2121
point (`recipient-locale.ts`, `resolveRecipientLocale`): the recipient's own
22-
`sys_user.locale` — read off the same row the channel already fetches for the
23-
address, so it costs no second query — else the deployment default, probed
22+
`sys_user.locale` — email and SMS read it off the same row they already fetch
23+
for the address, so it costs no second query there; the inbox channel, which
24+
never read the row before, makes one read for it on the template path — else
25+
the deployment default, probed
2426
lazily so live `localization` changes are honoured. The same chain serves the
2527
email channel's two arms (`sendTemplate` and `sys_notification_template`), the
2628
inbox channel's template path, and the SMS channel, so one notification cannot
@@ -42,13 +44,28 @@ it now sends each recipient their own language, else the deployment default —
4244
which is the ruled behaviour, not a regression. Nothing to migrate: remove the
4345
key, or leave it, it is inert either way.
4446

47+
Second behaviour change: on the `sys_notification_template` arm (email topic
48+
path, SMS) the deployment default (`II18nService.getDefaultLocale()`) is now
49+
the second rung; before, that arm fell straight from `payload.locale` to the
50+
static `en` and never consulted it. A deployment whose `localization.locale`
51+
is e.g. `zh-CN` with a topic bundle holding `en` and `zh` rows renders `zh`
52+
there now for recipients without a column. SMS is newly handed the
53+
deployment-default probe.
54+
4555
`@objectstack/spec` ships the contract text: the `notify` node's `template`
4656
description and its refusal messages now state the per-recipient chain and
4757
name `payload.locale` as not consulted (`automation/io-node-config.zod.ts`),
4858
mirrored on the runtime descriptor in `@objectstack/service-automation`.
4959

5060
Interaction with the `TEMPLATE_*` permanent-failure class is unchanged in
51-
kind: `sendTemplate`'s ladder still ends at `en-US`, so a recipient locale can
52-
dead-letter a delivery only where the deployment default would have too — a
53-
bundle with neither the requested row nor an `en-US` row, which is off the
54-
documented contract.
61+
kind: `sendTemplate`'s ladder for a NAMED locale still ends at `en-US`, so a
62+
recipient locale can dead-letter a delivery only against a bundle that has
63+
neither the requested row nor an `en-US` row — off the documented contract.
64+
Two asymmetries against the old single value, both on such bundles: (a) the
65+
bundle carries the deployment default's row but no `en-US` row — old delivered,
66+
new fails for a recipient whose own tag is a third language; (b) there is NO
67+
deployment default (i18n absent or `getDefaultLocale` unimplemented) — old
68+
called `sendTemplate` with no locale and the ladder's any-row rung delivered,
69+
new names the recipient's tag, the any-row rung is skipped, and a tag absent
70+
from the bundle is `TEMPLATE_NOT_FOUND` (permanent). The fix in both is the
71+
bundle (`en-US` is the ladder's floor), not a third rung.

.changeset/sys-user-locale-column.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ D4, so the standard edit form does not advertise a write the runtime refuses)
3030
and is NOT added to `MANAGED_EXTENSION_EDITABLE_FIELDS` or to the ADR-0092 D2
3131
self-service whitelist (`{name, image}`). Widening that whitelist so a user can
3232
set their own language is a security-boundary decision recorded on #13881, not
33-
made here; until then the column is written by admin / system surfaces.
33+
made here; until then the column is written only by system-context callers (no
34+
admin surface writes it today).
3435

3536
Who reads it: the messaging channels, per recipient, after fan-out — see the
3637
`@objectstack/service-messaging` changeset in the same release.

packages/plugins/plugin-auth/src/auth-manager.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4456,9 +4456,10 @@ export class AuthManager {
44564456
* entirely and `EmailService`'s ladder resolves its documented `en-US`
44574457
* default exactly as before.
44584458
*
4459-
* Still NOT a per-recipient stored preference: `sys_user` carries no locale
4460-
* column and the 2026-09-02 ruling continues to defer one. What is read is
4461-
* the language this request expressed, not a profile.
4459+
* Still NOT a per-recipient stored preference: `sys_user.locale` exists
4460+
* since #13881 (ruling 2026-09-01) but auth mail does not read it yet
4461+
* (#14762 for this send; #14641 for invitations). What is read is the
4462+
* language this request expressed, not a profile.
44624463
*/
44634464
private async sendChangeEmailNotice(
44644465
from: { email: string; name?: string; id?: string },
@@ -4665,8 +4666,9 @@ export class AuthManager {
46654666
* `kernel:ready` and on every settings change (same pattern as
46664667
* {@link setAppName}). Unset ⇒ the built-in English text.
46674668
*
4668-
* Per-user locale is not resolved yet — `sys_user` carries no locale
4669-
* column; when it grows one, resolution should prefer it (#2815).
4669+
* Per-user locale is not resolved here yet — `sys_user.locale` exists since
4670+
* #13881 (ruling 2026-09-01) and the messaging channels read it per
4671+
* recipient; auth SMS adopting it is #14762 (supersedes the #2815 note).
46704672
*/
46714673
setDefaultSmsLocale(locale: string | undefined): void {
46724674
this.smsLocale = locale?.trim() || undefined;

packages/services/service-messaging/src/recipient-locale.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
*
1818
* ## The chain
1919
*
20-
* 1. the recipient's own `sys_user.locale` ({@link RECIPIENT_LOCALE_FIELD}),
21-
* read from the SAME `sys_user` row the channel already fetches for the
22-
* address (email / phone) — one query per recipient, no second lookup;
20+
* 1. the recipient's own `sys_user.locale` ({@link RECIPIENT_LOCALE_FIELD}).
21+
* Email and SMS read it off the SAME `sys_user` row they already fetch
22+
* for the address — one query per recipient, no second lookup there; the
23+
* inbox channel, which never read the row before, makes one read for it
24+
* on the template path only;
2325
* 2. the deployment default — `II18nService.getDefaultLocale()`, probed
2426
* lazily at delivery time by the plugin (`getDefaultTemplateLocale`).
2527
*

0 commit comments

Comments
 (0)