You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Found while implementing #14787. Filed rather than fixed in that PR, because docs/adr/** is a governed surface (Prime Directive #14): including the amendment there would fork an ordinary contract-review PR into governance territory, and the prescribed handling for a mixed diff is to split the governed files into their own PR.
The drift
Maintainer ruling 2026-09-03 on #14787 (option B, adopted 「同意」) widened the ADR-0092 D2 self-service whitelist:
The identity table's user-writable set grows from two fields to three […] SYS_USER_PROFILE_EDIT_FIELDS becomes {name, image, locale}
The code now says exactly that. ADR-0092 does not. Three places in docs/adr/0092-sys-user-profile-field-delegation.md still state the two-field set as the decision:
the TL;DR bullet for D1 — "only name and image are profile-editable through the generic path";
the D1 tier table itself — "Tier 1: profile-editable" lists name and image and nothing else;
the D2 bullet and its Behaviour section — "sys_user → {name, image} is its first entry" / "First and only entry shipped by this ADR".
docs/adr/0105-group-tenancy-posture-and-first-class-org-scope.md repeats the two-field spelling in passing as well.
Why this is not just a stale sentence
Prime Directive #13: an accepted ADR binds until a superseding ADR says otherwise, and reversing a recorded decision is itself a decision that needs a new ADR or an amended status line — not a changeset that quietly does the opposite. A reader who reaches ADR-0092 first will read the two-field set as the governing decision and the third field as drift to be corrected, which is backwards. The ruling is the newer decision; the ADR is the older one and has to say so.
There is one wrinkle worth deciding rather than assuming. ADR-0092's own scope note says:
the decision about which fields open up is sys_user-specific (D1); the mechanism (D2) is family-wide by construction. A future "org admins may edit sys_organization.name" is a one-line whitelist registration citing this ADR, not a new ADR.
That anticipates new object registrations as ordinary work. It does not obviously cover changing D1's own tier table for sys_user, which is the decision text rather than an example. So the amendment route is a judgement call for the maintainer: an amended status line plus a D1 revision, or a small superseding ADR.
What the amendment should record
Tier 1 becomes {name, image, locale}, dated, citing the ruling.
locale has no auth semantics — it is not a login key, not authorization state, and better-auth is oblivious to it — which is the same reasoning that put name and image in Tier 1.
The D6 session-snapshot mirror deliberately did not widen with it: better-auth carries no locale on its user model, so there is no cached copy to keep coherent and mirroring one would manufacture an incoherence. That is now a separate named constant in identity-write-guard.ts, and the ADR's D6 text should say the two sets are no longer the same set.
Nothing is broken at runtime; the enforcement and the pins are correct and shipped. The cost is that the governing document disagrees with the governed code, which is the condition under which a later PR "restores" the old behaviour in good faith.
Found while implementing #14787. Filed rather than fixed in that PR, because
docs/adr/**is a governed surface (Prime Directive #14): including the amendment there would fork an ordinary contract-review PR into governance territory, and the prescribed handling for a mixed diff is to split the governed files into their own PR.The drift
Maintainer ruling 2026-09-03 on #14787 (option B, adopted 「同意」) widened the ADR-0092 D2 self-service whitelist:
The code now says exactly that. ADR-0092 does not. Three places in
docs/adr/0092-sys-user-profile-field-delegation.mdstill state the two-field set as the decision:nameandimageare profile-editable through the generic path";nameandimageand nothing else;sys_user → {name, image}is its first entry" / "First and only entry shipped by this ADR".docs/adr/0105-group-tenancy-posture-and-first-class-org-scope.mdrepeats the two-field spelling in passing as well.Why this is not just a stale sentence
Prime Directive #13: an accepted ADR binds until a superseding ADR says otherwise, and reversing a recorded decision is itself a decision that needs a new ADR or an amended status line — not a changeset that quietly does the opposite. A reader who reaches ADR-0092 first will read the two-field set as the governing decision and the third field as drift to be corrected, which is backwards. The ruling is the newer decision; the ADR is the older one and has to say so.
There is one wrinkle worth deciding rather than assuming. ADR-0092's own scope note says:
That anticipates new object registrations as ordinary work. It does not obviously cover changing D1's own tier table for
sys_user, which is the decision text rather than an example. So the amendment route is a judgement call for the maintainer: an amended status line plus a D1 revision, or a small superseding ADR.What the amendment should record
{name, image, locale}, dated, citing the ruling.localehas no auth semantics — it is not a login key, not authorization state, and better-auth is oblivious to it — which is the same reasoning that putnameandimagein Tier 1.localeon its user model, so there is no cached copy to keep coherent and mirroring one would manufacture an incoherence. That is now a separate named constant inidentity-write-guard.ts, and the ADR's D6 text should say the two sets are no longer the same set.member_defaultstill deniesallowEditonsys_user. Whether an ordinary member gets a self-service route to their ownlocaleis a further decision that [Decision] May a user set their ownsys_user.locale? — the ADR-0092 D2 self-service whitelist stays{name, image}after #13881 (column landsreadonly, system-context writes only) #14787's ruling did not take.Not urgent, but not cosmetic
Nothing is broken at runtime; the enforcement and the pins are correct and shipped. The cost is that the governing document disagrees with the governed code, which is the condition under which a later PR "restores" the old behaviour in good faith.
Generated by Claude Code