Skip to content

docs(adr): ADR-0123 D2 records the two inserts its refusal now answers — the union-scope insert and the crossing operator's - #24918

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-24823-adr-0123-d2-amendment
Oct 21, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-24823-adr-0123-d2-amendment

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24823
Clause-②: no

ADR-0123 D2's text was narrower than the runtime after two merged changes. PR #24819 (8a058d85a) made the D2 refusal answer a union-scope insert from a session with no active organization. PR #24868 (98c3d00447) made it answer a crossing platform operator's insert from such a session. This PR records both in a dated amendment on the ADR. It is docs only: one file, no package touched, no code, no test.

What changed: docs/adr/0123-no-active-organization-session-semantics.md only (+9 / −2)

  1. Status block. A dated Amendment (2026-10-20) blockquote under the **Status**: line, in the shape PR docs(adr): ADR-0047 records the 18.0 retirement of filterableFields — no replacement key, no rewrite #24756 gave ADR-0047 (795ff708d): a bold lead with its provenance in parentheses, then the body. It records:
    • both arms are D2's own refusal (403 PERMISSION_DENIED, naming the missing active organization, D4), thrown from one place, anchored as packages/plugins/plugin-security/src/security-plugin.ts#refuseNoActiveOrganization;
    • neither arm changes an outcome. Each insert was already refused by the engine's organization check (ADR-0131 D9) with 500 ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED. Only the answer moved;
    • arm 1, the union-scope insert (PR fix(plugin-security): under group, a no-active-organization insert gets the ADR-0123 D2 403, not a 500 #24819), and its reason: a new row lands in the active organization (ADR-0105 D5);
    • arm 2, the crossing platform operator (PR fix(plugin-security): a platform operator's insert with no active organization gets the ADR-0123 D2 403, not a 500 #24868), and its reason: crossing (ADR-0095 D3) lets the operator read every organization's rows and update them where they are, but it gives a new row no organization. ADR-0131 D9 refused that write already;
    • the reach of both arms: they judge the stored image after the beforeInsert chain. Reads, updates of existing rows, deletes, inserts from a session with an active organization, and system contexts are unchanged.
  2. D2's scope paragraph keeps its text as written. A dated italic pointer at its end says that the operator clause and the "empty membership set" sentence bound the scope pre-test, not every write, and it names the two inserts refused past them.
  3. Consequences, What does not change. "Platform operators." is struck in place, not deleted, as PR docs(adr): ADR-0047 records the 18.0 retirement of filterableFields — no replacement key, no rewrite #24756 did on ADR-0047. It is followed by the corrected entry ("Platform operators' reads, their updates of existing rows, and their inserts from a session with an active organization") and a dated italic note naming the changed insert.

No other ADR text moves. The **Status**: line, the TL;DR, D2's bullets, D3, D4 and the "What changes" paragraph are untouched.

Before / after

Scope paragraph, last sentence. Before (origin/main a8b44d407, line 60):

Under group, "no organization scope" means an empty membership set, matching ADR-0105 D2's own fail-closed rule.

After (line 67): the same sentence, then:

(Amended 2026-10-20 — the operator clause and the "empty membership set" sentence bound the scope pre-test, not every write. Two inserts from a session with no active organization, of a row that names no organization, are refused past them: a group member's with a non-empty membership set, and a platform operator's crossing the wall. See the amendment in the status block above.)

What does not change. Before (line 78): Reads. System contexts. Platform operators. Single-posture deployments …

After (line 85): Reads. System contexts. ~~Platform operators.~~ Platform operators' reads, their updates of existing rows, and their inserts from a session with an active organization. *(Corrected by the 2026-10-20 amendment — …; see the status block above.)* Single-posture deployments …

Rendered locally with marked 16.4.2 (GFM):

  • the amendment renders as one blockquote: a paragraph, a two-item ordered list, a paragraph;
  • Platform operators. renders inside a del element;
  • the scope paragraph ends in the italic pointer.

A blank line follows the blockquote, so the **Deciders** / **Builds on** / **Consumers** / **Surfaced by** lines stay one paragraph of their own and are not folded into the quote by lazy continuation. One visible side effect: the **Status**: line now renders as its own paragraph, where before it ran into the Deciders paragraph.

Premise readings (the dispatch's mechanism hypotheses H1–H3)

Base: the worktree was created at origin/main a8b44d407. The dispatch named fcb40ad67. git log fcb40ad67..origin/main -- docs/adr/0123-no-active-organization-session-semantics.md is empty, so the ADR did not move between the two.

  • H1 holds, read from the merged code (security-plugin.ts step 3.7 at a8b44d407), not from either PR's body:
    • git merge-base --is-ancestor 98c3d00447 HEAD exits 0. 8a058d85a is in git log of security-plugin.ts on the same tree.
    • Union-scope arm, insertLacksActiveOrganization. It needs all four: the operation is insert; postureUsesUnionScope holds; the caller's own Layer 0 is non-null; carriesActiveOrganization(context) is false.
    • Operator arm, operatorInsertLacksActiveOrganization. It needs all three: the operation is insert; callerCrossesTenantWall holds (from computeWriteTenantWall's crossedByPlatformAdmin); there is no active organization.
    • Both arms throw refuseNoActiveOrganization('caller', …) inside the postHookWriteImageCheck seam, and only when some stored row names no organization.
    • The enclosing guard requires opCtx.context.userId, and isSystem short-circuits the middleware earlier, so system contexts never reach either arm.
    • Neither arm fires on update or delete.
  • H2 holds, with one reading added. The ADR-0047 amendment (795ff708d) puts a status-block blockquote under the **Status**: line and strikes the affected text in place, followed by an italic pointer. That is the form matched here. The repo has other forms too: a ## Amendment (date, #n): … section is the most common, and ADR-0099 carries an inline > **Amendment note (date):** beside the corrected row. ADR-0123 had no amendment chain of its own to join.
    • check-adr-symbol-anchors reads backticked path#symbol tokens. The one anchor added (security-plugin.ts#refuseNoActiveOrganization) is extracted from the new file and not from the base file. It resolves as declaration (a const binding). A probe with an unknown symbol on the same source resolves null. The gate's total went to 2370 anchors, all resolving.
    • Every other symbol named in the note is a bare identifier, which the gate does not read. Each exists on a8b44d407.
  • H3 holds. The scope paragraph was line 60 and the What does not change line was line 78, on fcb40ad67 and on a8b44d407.

Gates: all at e9913cb22, the final commit

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack was re-derived on the actual diff, with no paths. It gives the same 20 commands as the dispatch. Each ran with its output redirected first and its exit captured before any read. --ran reads 20 derived, 20 run, 0 NOT-MEASURED, 0 UNRUN, a derived zero.

command exit
node scripts/check-adr-links.mjs 0 — 770 relative link destinations under docs/adr/ resolve
node scripts/check-adr-links.mjs --self-test 0
node scripts/check-adr-symbol-anchors.mjs 0 — 2370 anchors across 142 records resolve, 0 line anchors
node scripts/check-adr-symbol-anchors.mjs --self-test 0
node scripts/check-ci-filter-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs 0
node scripts/check-closing-keyword-parity.mjs --self-test 0
node scripts/check-comment-mask-corpus.mjs 0 — 9691 files, 0 disagree
pnpm --filter @objectstack/lint run check:doc-formula-expressions 0
pnpm check:adr-anchors 0
pnpm check:closed-ceremony-text 0
pnpm check:cross-package-test-inputs 0
pnpm check:doc-authoring 0
pnpm check:driver-memory-census 0
pnpm check:gitlink-declared 0
pnpm check:nul-bytes 0 — no raw ASCII control bytes
pnpm check:pm-governed-merges 0
pnpm check:pm-prior-rulings 0
pnpm check:refd-timer-probe 0
pnpm check:watch-hint-literal 0
  • check:doc-formula-expressions reads the built @objectstack/formula / @objectstack/lint. Those were built first, under the verify lock (turbo run build --concurrency=2, 4/4 tasks, VERDICT command-exit 0).
  • No package is touched, so there is no dependency-closure build, no package test or typecheck, and no ablation: the change is documentation, and no tested behaviour moves.
  • There is no changeset: docs/adr/** publishes nothing, so this PR carries skip-changeset.
  • Not run locally, and owned by CI: the repo-wide scans and the families outside the derived total.

Acceptance notes

  • Two developer messages in step 3.7 still end "System contexts and platform operators are unaffected." One is on the scope pre-test's refusal, the other on the union-scope arm's. Each is true of its own arm, because a crossing operator never meets either of them; it meets the operator arm, whose text says only "System contexts are unaffected." Read as a claim about D2 as a whole, it no longer holds after this amendment. This is an observation, not filed: a read-only inference, with no public door measured. Carrier: none. This PR changes no code.
  • canWriteObject's row-less create preview still answers true for both arms' callers. PR fix(plugin-security): under group, a no-active-organization insert gets the ADR-0123 D2 403, not a 500 #24819 and PR fix(plugin-security): a platform operator's insert with no active organization gets the ADR-0123 D2 403, not a 500 #24868 each recorded this as a known boundary in their own acceptance notes. The amendment does not restate it. Carrier: none.
  • The **Status**: line is left alone. It still reads "Accepted (2026-08-13)". The dated amendment block carries the 2026-10-20 change, as ADR-0047's amendment did.

维护者速读(草稿)

改了什么:只动了一个文件 ADR-0123(“已登录但没有当前组织”的会话语义),三处改动,都标了日期 2026-10-20:

  • 文件顶部状态区下面加了一段修订说明,写明两种新被拒的“新建记录”情形以及各自的理由。
  • D2 的适用范围那一段原文不动,只在段尾加一句指向修订说明的注。
  • “不变的部分”里原来写着“平台运维人员”,现在用删除线划掉,改成更准确的说法,并加了注。

为什么改:两次已合并的修复让运行时的行为比 ADR 原文写的更宽:

这两种写入以前本来就被拒绝,只是返回的是看不出原因的 500。现在换成能看懂原因的 403。原文还写着“平台运维人员不受影响”,已经不准确。按第一指令 13,ADR 要如实反映已落地的行为;否则后来读 ADR 的人(和 AI)会误以为运维人员没有当前组织也能新建记录。

风险与代价(含回滚):纯文档改动,没有运行时影响,不发包,没有 changeset。回滚就是 revert 这一个提交。

席位意见:

你要做的:审阅这处 ADR 修订;同意的话,在 PR 上点 Approve(席位随后落地)。


Generated by Claude Code

…s — the union-scope insert and the crossing operator's

ADR-0123 D2's scope paragraph and its "What does not change" line were
narrower than the runtime after PR #24819 (8a058d8) and PR #24868
(98c3d00). A dated amendment in the status block names both arms and
their reasons (ADR-0105 D5; ADR-0095 D3 with ADR-0131 D9); the scope
paragraph keeps its text with a dated pointer, and the "Platform
operators" entry is struck and corrected in place.

Claude-Session: https://claude.ai/code/session_01M82feuetLzrCiBz41BJb7L
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 20, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 20, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: e9913cb227170bac642220142a15af2dc998ae13
Local-runs: none
Scope: docs/adr/0123-no-active-organization-session-semantics.md + packages/plugins/plugin-security/src/security-plugin.ts (step 3.7 on origin/main: the scope pre-test, refuseNoActiveOrganization, insertLacksActiveOrganization, operatorInsertLacksActiveOrganization, the postHookWriteImageCheck seam, crossedByPlatformAdmin), packages/objectql/src/engine.ts (the insert path's seam evaluation and resolveSystemInsertOrganization), packages/objectql/src/tenancy/system-write-organization.ts (SystemWriteOrganizationRequiredError), docs/adr/0131-total-organization-ownership-no-null-organization-id.md (D9), docs/adr/0105-group-tenancy-posture-and-first-class-org-scope.md (D5), docs/adr/0095-authz-kernel-tenant-layer-and-posture-ladder.md (D3), docs/adr/0047-object-ui-run-modes.md at 795ff708d (the amendment form cited), and the merge commits 8a058d85a (PR #24819) and 98c3d00447 (PR #24868). The whole diff is one governed file; there is no TIER_DEFAULT remainder.

Face: rules (docs/adr/**), read off the PR's file list. Net diff against main: one file, +9 / −2, byte-identical between GitHub's diff and git diff origin/main...e9913cb22 at origin/main 62bc12930 (the ADR has not moved on main since the PR's base a8b44d407). The cited code was read on origin/main twice, at a7199ef97 and again at 62bc12930: between the two, security-plugin.ts and engine.ts moved (7532ea3c0, f90ea677c, 9532e2788), but no hunk names a step 3.7 symbol or the engine's seam-to-organization-check order — the four definitions below and the seam-first call order are byte-identical in substance on both, so every judgment here holds on 62bc12930. Tier H: this record is the contract-tier review of the head; it lands nothing — an authorised approval is still owed, and the PR is correctly draft with no auto-merge armed.

① Derived judgments

The accept set here is the ADR's claims about the runtime. Each sentence of the diff, judged against the cited code on origin/main and the two merge commits:

Status-block amendment, lead.

  1. "D2's refusal also answers two inserts its scope paragraph did not name" — RIGHT. Both arms are additions of 8a058d85a and 98c3d00447 to step 3.7; neither existed when the scope paragraph was written.
  2. "Both arms are D2's own refusal — 403 PERMISSION_DENIED, naming the missing active organization (D4) — thrown from the one place it is spelled (security-plugin.ts#refuseNoActiveOrganization, step 3.7)" — RIGHT. Both arms call the one refuseNoActiveOrganization closure, which throws PermissionDeniedError with a message naming "no active organization". The symbol anchor resolves (Lint · docs & code guards green on this head). Note for the next editor: the anchor names a closure-scoped const inside the middleware, not an export — renaming it reds check-adr-symbol-anchors, which is the point.
  3. "Neither changes an outcome: each insert was already refused by the engine's organization check (ADR-0131 D9) as an organization-less system write, answered 500 ERR_SYSTEM_WRITE_ORGANIZATION_REQUIRED with its prose withheld from the wire" — RIGHT. SystemWriteOrganizationRequiredError declares status = 500 and its docblock says the declared 5xx withholds the prose from the wire; it is thrown from resolveSystemInsertOrganization, the ADR-0131 D9 site.
  4. "Only the answer moved" — RIGHT, given 3.

Arm 1, the union-scope insert.
5. "Under group, the scope pre-test reads the membership set, so a member whose session has no active organization passes it" — RIGHT. callerHasOrganizationScope reads accessible_org_ids under a union-scope posture and tenantId otherwise.
6. "rightly, for reads, and for updates and deletes of rows the membership wall selected, which keep the organization they already have" — RIGHT. The arm is gated on operation === 'insert'; an update's stored image carries the row's current organization.
7. "a new row lands in the active organization: the engine stamps it from tenantId (ADR-0105 D5)" — RIGHT. ADR-0105 D5: "On insert in group/isolated postures the engine stamps organization_id from ctx.tenantId (active org) when absent."
8. "With none, the insert is refused, as isolated already refused the same session" — RIGHT. insertLacksActiveOrganization = insert AND postureUsesUnionScope AND caller Layer 0 non-null AND NOT carriesActiveOrganization; under isolated the pre-test refuses on tenantId itself.

Arm 2, the crossing platform operator.
9. "has no Layer 0 there, so neither the pre-test nor arm 1 reaches it, under isolated or group" — RIGHT. crossedByPlatformAdmin is true only when the Layer 0 verdict is none, the caller is a platform admin, and the same ladder with isPlatformAdmin withheld is not none; the pre-test exempts a null Layer 0 and arm 1 requires a non-null one.
10. "Crossing lets it read every organization's rows and update them where they are; it gives a new row no organization" — RIGHT; the arm is insert-only.
11. "From a session with no active organization, its insert is refused" — RIGHT. operatorInsertLacksActiveOrganization = insert AND callerCrossesTenantWall AND NOT carriesActiveOrganization.
12. The ADR-0131 D9 quotation "Nothing defaults to any owner other than the derivable one." — RIGHT, verbatim from D9.

Reach paragraph.
13. "Both arms judge the insert's stored image — the rows after the beforeInsert chain, the ones the engine's check reads next" — RIGHT. In engine.ts the insert path evaluates postHookWriteImageCheck on each row's post-hook input.data and then calls resolveSystemInsertOrganization; the seam runs first.
14. "A stored row that names an organization (a hook placed it) is not refused by either arm and is judged as before; one row that names none refuses the whole write" — RIGHT. someRowHasNowhereToLand is rows.some(not namesOrganization); rows that name one fall through to the unchanged per-row clearsWall loop.
15. "Reads, updates of existing rows, deletes, inserts from a session with an active organization, and system contexts are unchanged" — RIGHT. Both arms are insert-only and gated on a missing tenantId; the enclosing guard needs context.userId; isSystem short-circuits the middleware above step 3.7.
16–17. "D2's scope paragraph is kept as written, with a pointer" / "the What does not change line is corrected in place" — RIGHT; matches the diff.

Scope-paragraph pointer.
18. "the operator clause and the 'empty membership set' sentence bound the scope pre-test, not every write" — RIGHT.
19. "Two inserts from a session with no active organization, of a row that names no organization, are refused past them: a group member's with a non-empty membership set, and a platform operator's crossing the wall" — RIGHT, and exact: a member with an empty set is refused by the pre-test before the arm; arm 1 needs a non-null caller Layer 0, which under group is the union filter of a non-empty set.

What does not change.
20. "Platform operators. Platform operators' reads, their updates of existing rows, and their inserts from a session with an active organization" — RIGHT in every item it lists; it says LESS than the code in two: an operator's deletes, and its inserts whose stored row names an organization (a hook placed it), are unchanged too. Both are stated in the governing status-block paragraph (sentences 14 and 15) and deletes are already governed by D2's own delete bullet, so the summary line under-lists without contradicting. Not verdict-changing; a later editor may widen the entry.
21. The italic correction note — RIGHT; it restates sentence 11 with the 500-to-403 move from sentence 3.

No sentence says more than the code does. The one that says less is 20, covered by its governing paragraph.

Instrument, under Prime Directive 13. An amendment note, not a superseding ADR, is the right instrument. The decision's direction — D2 "tenant-scoped writes are refused loudly", the TL;DR's "no silent NULL stamping, anywhere ... no layer gets an exemption", D3 — is unchanged, and the two merged PRs implement it more completely rather than reversing it; what was wrong was the scope paragraph's stated reach and the Consequences summary. Prime Directive 13 names "an amended status line on the old one" as the lighter form, and this PR's dated amendment sits directly under the Status line in the form ADR-0047 took at 795ff708d (dated bold-lead blockquote, strike-in-place, Status line left as Accepted). A superseding ADR would be owed only on a reversal. One reading recorded, not a finding: the scope paragraph's clause "who is not a platform operator crossing the wall" is now true of the pre-test only; striking it in place, as the Consequences entry was struck, would have been equally valid, and the dev's choice — keep the clause, point to the amendment — leaves no misreading once the pointer is read.

② Semver level

RIGHT. The diff is one file under docs/adr/**, which publishes nothing from any released package; the PR carries skip-changeset, no .changeset/*.md is in the net diff, and Check Changeset is green on this head. The PR body's Clause-②: no has no arm, which is the well-formed shape for a non-publishing diff. The runtime behaviour the ADR now records carried its own patch changesets in 8a058d85a (.changeset/24784-group-no-active-org-write-403.md) and 98c3d00447 (.changeset/24821-operator-no-org-insert-403.md), so nothing publishes here that was not already declared there.

③ Boundary flags

open_questions: [] in the os-dev report — none to answer. The six declared deviations and two out-of-scope findings, each answered:

  • Deviation 1, base a8b44d407 not fcb40ad67 — accepted. The head's parent is a8b44d407; the ADR's last touch on main is c3b63f7ad (September), unchanged through origin/main 62bc12930, so the three-dot diff is the PR's diff.
  • Deviation 2, commit trailer — verified on the head commit: Claude-Session: plus Co-authored-by: Claude, the AGENTS.md model-free pair; no model identifier in the PR title, body or commit. Accepted.
  • Deviation 3, blank line after the blockquote — cosmetic. ADR-0047 on main puts its blank line before the blockquote; this PR puts one after. No gate reads the layout; the dev's render check is declared. Accepted.
  • Deviation 4, local gate logs overwritten and re-run — a local bookkeeping note; the head's check-runs are the gate verdicts here, and all are green. Accepted.
  • Deviation 5, label leg — skip-changeset is on the PR per the REST read. Accepted.
  • Deviation 6, worktree cleanup — not a review matter.
  • Out-of-scope finding 1, the two step 3.7 developer messages still ending "System contexts and platform operators are unaffected." — confirmed in the code on main (the pre-test's refusal and arm 1's). Each is true of its own arm, since a crossing operator meets neither; it is a string-tidiness item, not a contract violation, so a note rather than a card is the Prime Directive 10 answer. Not escalated.
  • Out-of-scope finding 2, canWriteObject's row-less create preview answering true for both arms' callers — already a recorded boundary in PR fix(plugin-security): under group, a no-active-organization insert gets the ADR-0123 D2 403, not a 500 #24819's and PR fix(plugin-security): a platform operator's insert with no active organization gets the ADR-0123 D2 403, not a 500 #24868's acceptance notes; outside a docs-only PR. Not escalated.

Check-runs on the head, read at 2026-10-21T00:44Z: 45 runs, every one completed — 32 success, 13 skipped, 0 failing. Required contexts — Lint & Repo Gates success, TypeScript Type Check success, Test Core success (all six shards), Governed Surface Queue Guard success; Build Core skipped by the path filter (docs-only). Check Changeset, Check Documentation Links, every Lint and Type Check lane, and the claim/closing-keyword guards all success. Opt-in and full-battery jobs skipped as designed on a PR run.

Implemented-by: claude/issue-24823-adr-0123-d2-amendment
Reviewed-by: session_01M82feuetLzrCiBz41BJb7L

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:只改了一个文件 ADR-0123(「已登录但没有当前组织」的会话语义),共三处,都标注日期 2026-10-20:

  • 状态行下加了一段带日期的修订说明,写明两种新被拒绝的「新建记录」情形和各自的理由。
  • D2 的适用范围段原文保留,段尾加一句注,指向这段修订说明。
  • 「不变的部分」里原来写的「平台运维人员」用删除线划掉,换成更准确的说法:运维人员的读、对已有记录的更新、在有当前组织时的新建都不变。

为什么改:两个已经合并的修复让运行时比 ADR 原文写的拒得更多:

这两种写入以前本来就会失败,只是返回的是看不出原因的 500。ADR 原文还写着「平台运维人员不受影响」,已经不准确。第一指令 13 要求 ADR 如实反映已落地的行为,否则后来的人和 AI 会照着错误的描述写代码。

风险与代价(含回滚):纯文档,一个文件 +9/−2,不发包、不改运行时、没有 changeset。回滚就是 revert 这一个提交。

席位意见:建议批准。达档契约复核(评论 6305136018)把修订说明的每一句和 origin/main 上的代码逐条对过,结论 PASS;席位也自读了 diff,两处放宽与合并代码一致。修订用的是「带日期的修订说明」而不是新写一份 ADR,因为决定的方向(响亮拒绝、绝不静默写空组织)没有变,只是重新说明了覆盖范围。复核指出一处措辞「说得比代码少」:「不变的部分」没有单列运维人员的删除和由 hook 放置组织的新建,但上方修订说明已覆盖,不影响准确性。

你要做的:在 PR #24918 上点 Approve,席位随后落地。

席位核对

ACCEPT · domain:skills seat 1 (seat post #7623) · session_01M82feuetLzrCiBz41BJb7L · head e9913cb227170bac642220142a15af2dc998ae13 · 2026-10-21T00:47Z

  1. Form. The PR is a draft against main, and the body's first line is Fixes #24823. Assignee huangyiirene equals the card's. Files: docs/adr/0123-no-active-organization-session-semantics.md only (+9/−2). check-governed-merges.mjs --pr 24918: Tier H, 11 changed lines. skip-changeset is right: docs/adr/** publishes nothing.
  2. Premises measured. The seat read the whole diff. Both cited merge commits are on origin/main with matching subjects: 8a058d85a (PR fix(plugin-security): under group, a no-active-organization insert gets the ADR-0123 D2 403, not a 500 #24819) and 98c3d0044 (PR fix(plugin-security): a platform operator's insert with no active organization gets the ADR-0123 D2 403, not a 500 #24868). The review re-verified every sentence against security-plugin.ts step 3.7 and the engine's insert path on origin/main 62bc12930 (review ①).
  3. Gate check-runs, pinned to this head (2026-10-20T14:26Z): 45 complete, 32 success, 13 skipped, 0 failures. The contract review of record is comment 6305136018 (Served-tier: CONTRACT_REVIEW_TIER, this head, Local-runs: none, PASS), rendered by an isolated subagent at that tier and adopted by this seat.
  4. Accepted deviations.
    • The base is a8b44d407; the ADR is untouched between the two bases.
    • The status-block blockquote is followed by a blank line, so the metadata lines do not fold into it.
    • The model-free commit trailers follow AGENTS.md.
  5. Out-of-scope findings. Both stay in Acceptance notes; neither is in class a, b or c.
  6. Landing. Tier H, so this PR stays draft. Review is requested from both authorised approvers, and the card moves to needs-user-decision. On an authorised APPROVED review the seat clears the label, flips the PR ready, arms auto-merge, follows it to MERGED and closes out ADR-0123 D2 says "under group, no organization scope means an empty membership set"; since PR #24819 a union-scope insert with no active organization is refused too, so the ADR under-describes the runtime #24823.

Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review October 21, 2026 00:59
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 21, 2026
Merged via the queue into main with commit 05b28c1 Oct 21, 2026
46 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-24823-adr-0123-d2-amendment branch October 21, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

3 participants