Skip to content

docs(adr): ADR-0092 Tier 1 is {name, image, locale} — amended in place per the #14787 ruling (#14951) - #15752

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-14951-adr-0092-tier1-locale
Sep 5, 2026
Merged

docs(adr): ADR-0092 Tier 1 is {name, image, locale} — amended in place per the #14787 ruling (#14951)#15752
os-zhuang merged 2 commits into
mainfrom
claude/issue-14951-adr-0092-tier1-locale

Conversation

@claude

@claude claude Bot commented Sep 5, 2026

Copy link
Copy Markdown
Contributor

Fixes #14951

What this is

ADR-0092's D1 tier table still recorded Tier 1 as {name, image} while the enforced
constant has held {name, image, locale} since the 2026-09-03 maintainer ruling on
#14787. This amends ADR-0092 in place per the vehicle ruling on this card, and
corrects ADR-0105's passing two-field spelling.

Ruling followed (binding): director-seat record at
#14951 (comment)
maintainer reply 「同意」 to option A: dated revision/status line + D1 tier table
rewritten, no superseding ADR; ADR-0105's same sentence corrected in the same PR.

Code truth verified before writing a word

packages/plugins/plugin-auth/src/sys-user-writable-fields.ts:57
export const SYS_USER_PROFILE_EDIT_FIELDS: ReadonlySet<string> = new Set([
  'name',
  'image',
  // Maintainer ruling 2026-09-03 (option B). ...
  'locale',
]);
packages/plugins/plugin-auth/src/identity-write-guard.ts:153
const SESSION_SNAPSHOT_MIRRORED_FIELDS: ReadonlySet<string> = new Set(['name', 'image']);

The premise holds: the code says three, the ADR said two.

Before / after, every changed sentence

1. Date line (ADR-0092, the file's own revision convention — appended, nothing replaced)

  • after: … · **2026-09-03 (amended: D5 …)** · **2026-09-05 (amended: D1 — Tier 1 becomes {name, image, locale}, carrying the 2026-09-03 ruling on #14787; see the D1 Amendment)**

2. TL;DR D1 bullet

  • before: "classify sys_user columns into three tiers; only name and image are profile-editable through the generic path."
  • after: "… only name, image and locale are profile-editable through the generic path." plus a dated "Amended 2026-09-05" line quoting the superseded wording.

3. TL;DR D2 bullet

  • before: "sys_user → {name, image} is its first entry."
  • after: "sys_user → {name, image, locale} is its first entry ({name, image} as originally shipped — see the D1 Amendment)."

4. D1 Tier 1 list — gains a third member:

D1 is the one section rewritten rather than kept-verbatim-plus-note, and the section
says why: it is the set the D2 guard is read against, so a reader taking the old list as
current would delete an enforced member in good faith. The superseded wording is quoted
inside the Amendment, so nothing is lost.

5. D2 Behaviour

  • before: "First and only entry shipped by this ADR: sys_user → SYS_USER_PROFILE_EDIT_FIELDS (D1 Tier 1)."
  • after: "… (D1 Tier 1 — {name, image, locale} since the 2026-09-05 amendment, {name, image} as originally shipped). Still the only entry: what the amendment widened is that entry's field set, not the number of registered objects."

6. D5 Amendment's closing paragraph — it asserted the very state this PR ends:

7. D6 gains an amendment note stating the two sets are no longer the same set.

8. D3's design sketch is left verbatim (it is dated 2026-07-10 design text) and
annotated beneath, so its new Set(['name', 'image']) literal is not read as the live
value. Flagged for reviewers: the alternative treatment is to edit the snippet itself —
say the word and I will.

9. ADR-0105 F4

Deliberately NOT changed (each is correct as written, and the ADR house form keeps
original text verbatim): the Context table row "Self profile (name, image) |
better-auth /update-user" — that describes what better-auth's own endpoint carries, and
it carries two; the Context note "for the D1 whitelist (name, image) staleness is
cosmetic … cosmetic until someone widens the whitelist" — answered in the new D6 note
rather than rewritten, since it turned out prophetic; and the two past-tense {name, image}
references inside the D5 Amendment's own narration.

⚠️ One point of the ruling was stale before it was written — please confirm my reading

The ruling's binding point 4 says to restate D5 including "member_default still
denies allowEdit on sys_user", and to record member self-service on locale as
OPEN. Against origin/main that is no longer true, and ADR-0092 itself already
says so:

So writing point 4 literally would have created a fresh contradiction between the governing
document and the governed code — the defect this card exists to remove — and would have
regressed the ADR's own D5 Amendment. I therefore recorded point 4's mechanism verbatim
(whitelist decides which columns, permission sets decide who; widening one does not widen
the other) and recorded as OPEN the question that genuinely still is open and that both
the original D5 text and its Amendment leave open: an org admin editing another member's
row
(sys_user_org_members stays select-only precisely so it cannot compose into that
write). Nothing is resolved that was not already ruled.

If reviewers prefer point 4's literal wording, that is a maintainer call and I will change it.

Verification — every exit code captured before any pipe

Gate set derived, not guessed: node scripts/pm/dispatch-gates.mjs --changed --commands --repo objectstack-ai/objectstack
(EXIT=0) → 17 commands. All 17 run at head f875a58a3, all EXIT=0:

Gate EXIT
node scripts/check-adr-links.mjs (+ --self-test) 0 / 0
node scripts/check-adr-symbol-anchors.mjs (+ --self-test) 0 / 0
node scripts/check-ci-filter-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs (+ --self-test) 0 / 0
node scripts/check-comment-mask-corpus.mjs 0
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0
pnpm check:adr-anchors 0
pnpm check:cross-package-test-inputs 0
pnpm check:doc-authoring 0
pnpm check:driver-memory-census 0
pnpm check:nul-bytes 0
pnpm check:pm-governed-merges 0
pnpm check:refd-timer-probe 0
pnpm check:watch-hint-literal 0

Verdict lines quoted from the gates themselves:

  • ✅ check-adr-symbol-anchors: 1925 anchors across 135 records resolve — 189 symbol (170 declaration, 19 literal), 1721 file-level, 15 cross-repo, 6 exempt, 2 continuation. 0 line anchors survive.
  • check-nul-bytes: OK (scanned 7610 text file(s) -- 7610 tracked, 0 untracked-not-ignored; skipped 7 binary, 1 non-regular; no raw ASCII control bytes).
  • ✓ check-governed-merges --self-test: 274 assertions …
  • ✓ doc authoring guard: 394 files clean — no bare metadata literals.

Governed-surface predicate, quoted:

node scripts/pm/check-governed-merges.mjs --test docs/adr/0092-... docs/adr/0105-...
governed-surface predicate: 2 of 2 path(s) hit the register (5 surfaces, repo-agnostic).
  ⛔  GOVERNED — a human merge is the review record for this PR (#9495 regime).

Reverse verification (the citations are gate-enforced, not just prose)

The three code citations were first written as bare backticked paths. docs/adr/**
deliberately sets the symbol-anchor corpus's checkBarePaths off (a measured corpus
decision — judging every bare path there yields 1,056 findings, almost none of them
anchors), so a bare path is counted but never resolved. The second commit rewrites them
in symbol-anchor form, which the gate does resolve. Proof, both legs, on a committed tree:

  • Mutated legSESSION_SNAPSHOT_MIRRORED_FIELDS…FIELDZ; mutation confirmed on
    disk (2 injected occurrences, blob 82011c7a8d39d1); gate EXIT=1 with
    [unresolved-symbol] … has no declaration site or string-literal token in packages/plugins/plugin-auth/src/identity-write-guard.ts.
  • Restore leggit checkout HEAD -- ABSPATH; blob back to 82011c7, 0 marker
    occurrences, gate EXIT=0.

Counts moved the expected direction: symbol anchors 186 → 189, declaration 167 → 170.

NOT MEASURED, by name

  • scripts/check-cross-package-test-inputs.mjs --union-into "$RUNNER_TEMP/…" --changed "$RUNNER_TEMP/…"
  • scripts/check-shard-attestation.mjs --emit --job test --shard MATRIX.SHARD --total 6 --out "$RUNNER_TEMP/…"
  • scripts/check-test-completeness.mjs "$RUNNER_TEMP/test-core.log" --scheduled … --package-list …

All three take a value that only exists inside a CI run; the derivation names them as
NOT MEASURED rather than green. CI measures them.

No repo-wide pnpm lint run: this diff is two Markdown files under docs/adr/**, outside
the ESLint corpus entirely.

No changeset — the exemption, quoted

AGENTS.md: "⛔ never skip-changeset: that label is for a diff that publishes nothing from
any released package.
" This diff is two docs/adr/** Markdown files and publishes nothing
from any package, so the label applies and no changeset is owed. No anchor JSON under
scripts/adr-anchors/ was needed — pnpm check:adr-anchors passes as-is, and no file was
added beyond the two ADRs.

Governed surface — draft, human merge, reviewers os-zhuang + hotlong

docs/adr/** is governed (Prime Directive #14). This PR stays a draft: never flipped
ready, never enqueued, never auto-merged. Review requested from os-zhuang and hotlong;
a human performs the merge.

Files changed: docs/adr/0092-sys-user-profile-field-delegation.md,
docs/adr/0105-group-tenancy-posture-and-first-class-org-scope.md — nothing else.


🤖 Generated with Claude Code

https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk


Generated by Claude Code

The 2026-09-03 maintainer ruling on #14787 widened the ADR-0092 D2
self-service whitelist to three fields, and the code has said so since:
SYS_USER_PROFILE_EDIT_FIELDS in
packages/plugins/plugin-auth/src/sys-user-writable-fields.ts holds
{name, image, locale}. ADR-0092 still recorded the two-field set as the
decision, which is the condition under which a later PR "restores" the
old behaviour in good faith.

Amend ADR-0092 in place per the vehicle ruling on this card (A: dated
revision line + D1 rewritten, no superseding ADR):

- Date line gains a 2026-09-05 amendment entry, in the file's own
  revision convention.
- D1's Tier 1 list gains `locale`, with a D1 Amendment blockquote
  recording the four binding points.
- TL;DR D1 and D2 bullets, and the D2 Behaviour sentence, restate the
  three-field set.
- D6 gains a note stating the mirror set and Tier 1 are no longer the
  same set, naming SESSION_SNAPSHOT_MIRRORED_FIELDS in
  packages/plugins/plugin-auth/src/identity-write-guard.ts.
- The D5 Amendment's closing "Not amended by this" paragraph is trued
  up: the table it said was unreconciled is reconciled here.
- The D3 design sketch is left verbatim and annotated, so a code-shaped
  two-field literal is not read as the live value.
- ADR-0105's passing two-field spelling is corrected.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
The three constants the amendment cites were written as bare backticked
paths. `docs/adr/**` sets the symbol-anchor corpus's `checkBarePaths`
off (a measured corpus decision -- judging every bare path there yields
1,056 findings, almost none of them anchors), so a bare path is counted
but never resolved: the citation could rot silently.

Rewritten in the symbol-anchor form the gate does resolve, which also
satisfies the ruling's "cite it by name" more literally than prose did.
check-adr-symbol-anchors goes 186 -> 189 symbol anchors and
167 -> 170 declaration anchors, 0 hard findings.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012zGPuVVX3deAx9LdjK8jCk
@claude
claude Bot requested review from hotlong and os-zhuang September 5, 2026 06:08
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 5, 2026
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Sep 5, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review September 5, 2026 06:11
@os-zhuang
os-zhuang enabled auto-merge September 5, 2026 06:11
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 5, 2026
Merged via the queue into main with commit 3d4e92f Sep 5, 2026
36 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-14951-adr-0092-tier1-locale branch September 5, 2026 06:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ADR-0092 D1's tier table still records Tier 1 as {name, image} — the 2026-09-03 ruling made the shipped set {name, image, locale}

2 participants