Skip to content

docs(adr): amend ADR-0092 D5 — self-service edits route through the generic data path - #15109

Merged
hotlong merged 2 commits into
mainfrom
claude/issue-14959-adr-0092-d5-amendment
Sep 4, 2026
Merged

docs(adr): amend ADR-0092 D5 — self-service edits route through the generic data path#15109
hotlong merged 2 commits into
mainfrom
claude/issue-14959-adr-0092-d5-amendment

Conversation

@claude

@claude claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Part of #14959

Governed surface — the maintainer merges this by hand. This PR carries the ADR amendment alone; the code that implements it is a separate PR, cross-linked below, and that one must not be armed for merge before this one lands.

What this amends

ADR-0092 D5 ("Who can edit whom: unchanged permission topology") said, in its third bullet:

Self-service stays on better-auth /update-user (update_my_profile action) … We do not build an RLS "self-row edit" carve-out for CRUD — one self-service door is enough, and better-auth's is strictly better.

Maintainer ruling 2026-09-03, decision batch #22, verbatim and untranslated as adopted:

「同意」

A rank-and-file member may edit their own sys_user row on the generic data path. The original text is kept verbatim and the amendment is added as a blockquote beneath it, in the house style ADR-0105 established — two of D5's three bullets still hold, and the third is only readable against what it used to say.

Why the original text stopped being true

D5 rests on a premise a later ruling retired: that better-auth /update-user can carry everything Tier 1 holds. It could, while Tier 1 was {name, image}. The 2026-09-03 ruling on #14787 admitted locale, and locale is deliberately not a better-auth additionalFields entry — #13881 measured that declaring it there makes getSession SELECT a column an environment that has not run schema-sync does not have.

So /update-user cannot post the column, and with member_default denying allowEdit on sys_user, the generic path could not either. The column shipped reachable by platform admins alone — a user-stated preference (#14788 ruled the stored value outranks Accept-Language precisely because it is the user's own statement) that the user could not state.

What the amendment decides

Self-service edits of the D1 Tier-1 columns route through the generic data path, bounded on the two axes that already exist and in the shape sys_api_key has shipped since #8053:

  • which rowsmember_default gains an explicit sys_user entry (allowRead/allowEdit true, create/delete false), and its sys_user_self policy widens from select to all so it reaches the by-id write pre-image check. sys_user_org_members stays select-only: RLS policies OR-combine, so widening the org-peer visibility scope would compose "my id OR every user id in my org" and hand every member their colleagues' profile rows. The org-admin follow-up the original D5 text names at its end is still open, and still a separate decision.
  • which columns — unchanged. D2's identity write guard keeps bounding a user-context update to the registered whitelist. Widening who does not widen what.

name / image therefore become editable on the generic path too. That is the real cost, accepted deliberately: D6 already mirrors better-auth's refreshUserSessions for exactly those columns, so the session-cache coherence the original bullet bought by routing through /update-user is bought here by the companion hook instead. locale is correctly excluded from that mirror — better-auth carries no such field on its user model, so there is no stale cached copy to repair.

Rejected in the same ruling and recorded in the amendment so they are not re-proposed: a dedicated endpoint under system context (the "second stamping route" #14787's own ruling rejected one level up), leaving the column admin-only, and locale as an additionalFields entry.

Not amended: D1's tier table still lists two Tier-1 members while the enforced whitelist constant holds three. That reconciliation is tracked separately in #14951 and is deliberately left alone here.

Gates

docs/adr/** is excluded from dispatch-gates.mjs's derived family for check-adr-anchors, so it was checked explicitly. Every gate in the derived family for this diff was run at the pushed head 4807b058b1:

  • green: check:adr-anchors, check-adr-links (+ self-test), check-adr-symbol-anchors (+ self-test), check:doc-authoring, check:doc-formula-expressions, check:nul-bytes, check:pm-governed-merges, check:cross-package-test-inputs (+ the direct-node spelling), check-ci-filter-parity, check-closing-keyword-parity (+ self-test), check-comment-mask-corpus, check-shard-attestation, check:refd-timer-probe, check:watch-hint-literal
  • exit 3 = NOT MEASURED, not a pass and not a failure: check-test-completeness — it grades a saved turbo run test log that only CI produces, and a markdown-only diff cannot move it.

Cross-links

🤖 Generated with Claude Code

https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8


Generated by Claude Code

…eneric data path

Maintainer ruling 2026-09-03, decision batch #22 (verbatim 「同意」).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AUF1NoViznQK32gqpK8wS8
@github-actions github-actions Bot added the size/s label Sep 3, 2026
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Check Changeset red — resolved with the skip-changeset label, which is the route the gate itself names

domain:services execution seat, session session_01AUF1NoViznQK32gqpK8wS8.

The gate's own message gives three routes and closes two of them:

This PR adds no changeset. If it releases nothing […] apply the skip-changeset label; otherwise run pnpm changeset and name the packages. An empty-frontmatter changeset is NOT a third option any more […] because it is a real input to changesets/action and an all-empty set stalls the release silently and greenly (#4898).
If you are unsure, take route 2. A wrong skip-changeset label is caught by review; a wrong empty changeset is caught by nobody.

This PR releases nothing, measured rather than assumed. Its entire diff is one file:

docs/adr/0092-sys-user-profile-field-delegation.md   +70/-1

Zero files under packages/, zero non-ADR files. An ADR amendment publishes no package, so route 2 is the correct route and not merely the convenient one.

No empty-frontmatter changeset was added, which is the failure mode the gate spends most of its message warning about — it would have gone green here and then stalled a release silently.

Label applied with a read-modify-write and a comparative read-back against union(current, target): documentation, size/s, skip-changeset — matched, and nothing else on the PR moved.

Landing order for this pair is unchanged: this ADR PR is governed (docs/adr/**) and the maintainer merges it by hand; the code PR #15108 stays draft and unarmed until it does.


Generated by Claude Code

@claude

claude Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor Author

Ready for the maintainer — undrafted, green, and deliberately not armed

domain:services execution seat, session session_01AUF1NoViznQK32gqpK8wS8.

docs/adr/** is governed, so this PR merges by hand. It was draft, which meant nobody could merge it — that was the only thing blocking the manual step, so it is now marked ready for review.

State, read at job level rather than from an aggregator: 30 checks green, 0 failures, nothing still running on head 4807b058b1.

⚠️ One reading needs care if you look at the checks yourself. Check Changeset shows two runs on this head:

run conclusion completed
100847848724 failure 23:05:33Z — before the skip-changeset label
100848551533 skipped 23:06:20Z — the label-triggered re-run, and the current one

The failure is stale and superseded. ⛔ It does not need the label re-applied.

Not armed, on purpose. auto_merge is none and this seat did not enable it: arming a governed PR would merge it without the hand-merge this surface requires. This seat also does not approve or merge it.

What it gates. The code PR #15108 (member_default gains the sys_user entry; sys_user_self RLS widens to all) is green — 29 checks, 0 failures — and is being held draft and unarmed until this one lands, exactly as the ruling's execution note requires. Once this merges, #15108 is undrafted, armed, and followed to merge by this seat.

Review record for both halves is on the card: 14959#issuecomment-5533293493.


Generated by Claude Code

@hotlong
hotlong requested a review from os-zhuang September 4, 2026 01:34
@os-sales
os-sales added this pull request to the merge queue Sep 4, 2026
@github-merge-queue
github-merge-queue Bot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claude Bot commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Dequeued CI_FAILURE — not this PR's, and the re-arm is owed on quota

domain:services execution seat, session session_01AUF1NoViznQK32gqpK8wS8.

This PR was removed from the merge queue at 02:59:46Z with reason CI_FAILURE. Recording the diagnosis rather than silently re-queueing.

The failure is not this PR's, on three readings:

reading value
not-green checks on its own head 4807b058b1 0 — every check green, nothing running
what this PR changes 1 file, docs/adr/0092-sys-user-profile-field-delegation.md, +70/-1
files under packages/ 0

A documentation-only ADR amendment cannot fail Test Core, a typecheck lane or a build. The queue here is grouped, and GitHub's own dequeue notice says so explicitly: "In a grouped queue the failure can come from another PR in the same batch." The batch this PR sat in also carried #15008, which merged — main advanced to fcc42e6c1d in the same window.

⛔ The queue refs and their runs are already cleaned up, so the batch-mate cannot be named from here. That is a limit of the reading, stated rather than papered over: what is established is that this PR's own head is clean, not which sibling failed.

What happens next, and why it has not happened yet

The remedy is to re-queue, which on this repository means re-enabling auto-merge — there is no hand-merge path, as PUT /pulls/15109/merge answers 405 Repository rule violations found — Changes must be made through the merge queue.

⚠️ That re-arm is currently blocked on API quota, not on judgment: enabling auto-merge needs the MCP/GraphQL channel, and it answered API rate limit already exceeded at 03:0xZ. The repo-scoped REST channel this comment is posted through cannot set auto_merge. Per this seat's standing rule the response to an exhausted pool is to back off, not to retry-loop, so the re-arm is owed and scheduled rather than attempted repeatedly.

Anyone with the UI can clear this immediately by pressing Merge when ready — the PR is green, undrafted, mergeable_state: clean, and its skip-changeset label is applied. Otherwise this seat re-arms it as soon as the quota window recovers.

⛔ One re-arm is this seat's limit and it is being spent here, on the "failure that is not this PR's" case. If it dequeues a second time the failure will be treated as real and root-caused rather than re-queued again.

#15108 remains held behind this PR — green (29 checks, 0 failures), contract review PASSED at tier (14959#issuecomment-5534395446), carriers still applied, draft. It lands immediately once this one merges.


Generated by Claude Code

hotlong commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Maintainer ruling, 2026-09-04, given in chat to the director seat (session_01WXyGTWPbbreqXow7Z2pZCk) on the governed batch presented at 01:50Z, item 1 (this PR, option A). Verbatim: 「15109 同意 其他不处理」.

Executing on that instruction from the maintainer's own account: merging directly (squash) at head 4807b058b. No approving review is submitted by any seat; the human decision is the review record for the governed path docs/adr/0092-*.md. Pre-check: 35 check runs on the head, all completed with conclusion success or skipped; mergeable_state: clean. The companion code PR #15108 (contract review PASS on card #14959, comment 5534395446) lands next on the same ruling.


Generated by Claude Code

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/s 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.

2 participants