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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
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

@claudeclaudeBot 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-actionsgithub-actionsBot added the documentation Improvements or additions to documentation label Sep 3, 2026
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 3, 2026
@claude

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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

claudeBot commented Sep 3, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

runconclusioncompleted
100847848724failure23:05:33Z — before the skip-changeset label
100848551533skipped23: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

@os-sales
os-sales added this pull request to the merge queueSep 4, 2026
@github-merge-queue
github-merge-queueBot removed this pull request from the merge queue due to failed status checks Sep 4, 2026
@claude

claudeBot commented Sep 4, 2026

Copy link
Copy Markdown
ContributorAuthor

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:

readingvalue
not-green checks on its own head 4807b058b10 — every check green, nothing running
what this PR changes1 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

@hotlongClaude

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 freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/sskip-changesetPR 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

@hotlong@claude