Skip to content

docs(adr-0131): amend D13's reach — the legacy-anchor retirement leaves it for the walled posture, on the 17.x line - #18646

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-18413-adr-0131-d13-drop-reporter-symbols
Sep 18, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-18413-adr-0131-d13-drop-reporter-symbols

Conversation

@os-justin

Copy link
Copy Markdown
Collaborator

Fixes #18413

Governed surface (docs/adr/**), one file, one amendment: docs/adr/0131-total-organization-ownership-no-null-organization-id.md (+129 / -4). Draft, skip-changeset, maintainer-merged — no seat flips this ready, queues it, or arms auto-merge (Prime Directive #14). node scripts/pm/check-governed-merges.mjs --test docs/adr/0131-…md answers GOVERNED, 1 of 1 path.

1. The ruling and its authority

Maintainer ruling, option B on #18336, taken in session session_01LmoCFLwGkcAGefVf7jryEV on 2026-09-16T09:54Z and recorded on that card (#issuecomment-5695555178). The option text the maintainer selected, verbatim and untranslated:

B — #11663 设计说了算
修订 ADR-0131 D13,把这两个符号从其退役清单里摘掉,本卡留在 17.x 线执行。

Three things it decided: #11663's design governs this leg for these two symbols; the work stays on the 17.x line (not re-targeted to v18, not gated by #15193); the two symbols leave D13's retirements. Recorded under Prime Directive #13 — reversing a recorded decision needs an amended record, and this PR is that act. ADR-0131 stays Accepted; only the staging of one leg moves.

2. Reading correction — where the symbols actually were

The card states that D13's list names both symbols. Measured on origin/main 32be735e5, that is not where they are:

symbol docs/adr/ docs/ packages/ (positive control)
reportLegacyPlatformAdminGrant 1 — ADR-0131 :362, inside D5 3 15
resetLegacyPlatformAdminGrantReport 0 0 17

D13's own retirement list (:641–659 on main) names neither. What the record actually carries is D5's sentence 「reportLegacyPlatformAdminGrant and the legacy unscoped anchor retire with D13」 — an assignment to D13, which is what the ruling reaches. So this amendment edits D5, D14 and §7 and leaves D13's list as it stands. The substance of the card is unaffected: before, the retirement was D13's and therefore v18's; after, it is #11663's and 17.x's. premise_still_valid: true, with this correction.

3. The D13 text, before and after

D5, before:

PLATFORM_ADMIN derives from OS_PLATFORM_OWNER_EMAIL (#13514 L4) — configuration, not a row. The single-posture first-user promotion (Choice 4A), which today writes an admin_full_access grant row with a NULL organization, writes it owned by the Default Organization instead; under a walled posture no grant row is written (unchanged). reportLegacyPlatformAdminGrant and the legacy unscoped anchor retire with D13. No NULL grant row is ever produced.

D5, after (the bolded half is what moved):

… under a walled posture no grant row is written (unchanged). That ownership change is C3's, on the v18 line, and it is unchanged by the 2026-09-16 ruling — so no NULL grant row is ever produced once C3 has landed; until then the single row carries the NULL organization it carries today, and D10's migration is what clears it. The legacy unscoped anchor retires with D13 for the single posture only: its walled half, together with the two symbols that carry the migration window — reportLegacyPlatformAdminGrant and resetLegacyPlatformAdminGrantReport — left D13 on 2026-09-16 and retire on the 17.x line under #11663's design instead (#18336; see the Amendment at the end). The single anchor's own disposition is #11979's (Choice 4B), which this record does not pre-empt.

Plus: a **Amended** (2026-09-17, …) clause on the status line; one named exception written into D14; the §7 post-17.2 audit line given the same exception; and a full ## Amendment (2026-09-17, #18413) record at the end of the file carrying the authority, the two standing constraints, the closed question and the D14 reconciliation.

4. How each standing constraint is honoured

Both are quoted verbatim and untranslated in the amendment. Neither was touched by the ruling, and both still bind.

Choice 4A — maintainer, 2026-09-08, #16682 comment 5587754690:

The rest of Choice 4A (#11974, 2026-08-25) stands: retiring the walled write must not retire the single one

Honoured: the amendment narrows the retirement to the walled posture in every place it names it — D5, D14's exception and the §7 line each say "walled". The single write, its grant row and its zero-config first-user promotion are stated as surviving until #11979 (Choice 4B) disposes of them. Nothing in the diff removes or narrows the single half.

The zero-config constraint — maintainer, 2026-09-16, the same session as the ruling:

比如临时启动的开发环境,我不可能去配置啊

Honoured: the amendment states that single keeps its zero-config first-user promotion, and it does not touch the derivation that delivers it — the unscoped-grant read at §6/§6b of packages/core/src/security/resolve-authz-context.ts. The amendment also records the mechanical reason the all-postures reading was never available for this leg: the deprecation pointer is already posture-keyed (postureEnforcesWall(resolveTenancyPosture()) at the §6b-config arm), so under single there is no pointer behaviour to retire at all.

5. The open question — CLOSED in the text, not deferred

The STOP condition did not trigger. The two standing constraints do not conflict with the D13 change: the change reduces what retires, and a reduction cannot retire the single write; the zero-config channel is the unscoped read, which is not the reporter and is untouched. Closing the question needs no new maintainer decision — only two ruled texts that the option-B ruling explicitly left alone. The decision sentence, as it is written into the ADR:

Decision (2026-09-17, #18413). The single-posture first-user grant row still becomes owned by the Default Organization. That is D5's own answer, accepted by the maintainer on 2026-09-04 (§6, one of the four open questions answered 「接受你的建议」), and the option-B ruling does not touch it. It still lands on the v18 line, under C3 (§8), exactly where it was before this amendment. Until C3 lands, the row keeps the NULL organization it carries today — the state D10's migration exists to clear — and D1's NOT NULL constraint still reaches that table only when D10 reports zero for it.

Why the drop moves no row — measured, not argued. reportLegacyPlatformAdminGrant (packages/core/src/security/platform-admin.ts:351) is a once-per-process warn naming one holder and pointing at OS_PLATFORM_OWNER_EMAIL; resetLegacyPlatformAdminGrantReport (:371) drops that latch for tests. Neither writes the grant row, neither reads its organization, and neither participates in the derivation that confers PLATFORM_ADMIN — that is the unscopedUserPsIds read at resolve-authz-context.ts:839 and the check at :928, untouched by this amendment. So the 17.x retirement this amendment authorizes adds no NULL row, removes none, and re-owns none. D1 and D10 are therefore unaffected: the NULL-ownership question is not created by the drop and is not answered by it — it is answered by D5, on D5's own schedule.

6. Reconciliation with D14 and with #15193

D14 「⛔ No 17.x card narrows or removes an arm, adds a name column beside an id column, or ships a half of this record」 gains exactly one named exception, written into D14 itself. Two independent reasons, either sufficient:

  1. It is not a half of this record. The ruling assigned the leg to [Design] Re-anchor platform-admin: admin_full_access becomes a kernel metadata declaration; WHO holds it comes from env-configured verified emails — retiring the org-less row anchor #11663's design. Work this record no longer governs cannot be a half of it.
  2. It narrows no arm. The arms D14 fences are the drivers' orWhereNull arms inside applyTenantScope — the record's own vocabulary (D13 :641, D14 :674, §7 :836 "the arms are inside applyTenantScope … there are two"; the reads in resolve-authz-context.ts are called "NULL readings", never arms). The walled legacy-anchor read and its pointer are neither of those, add no column, and move no row.

The exception is written as exhausted by that leg and those two symbols: every other retirement in D13 keeps D14's fence and stays gated on D10's zero-NULL report.

#15193 (the v18 gate, open p1) — confirmed it no longer gates #18336 under this ruling, and the amendment says so in D14's exception and in its "What the ruling decided" list. #15193's own rule binds "every ADR-0131 execution card"; the option-B ruling took this leg out of that set, and the ruling comment states it directly (「本卡留在 17.x 线,⛔ 不改挂 target:v18、⛔ 不阻塞于 #15193」). ⛔ No label or body of #15193 is touched by this PR — that is #18336's own re-pointing, out of scope here.

7. Gates — derived, run, reconciled

Derived from this worktree with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no hand-written path list; the tool took the change set three-dot against merge base 32be735e5). 18 commands, every one run, exit code captured before any pipe:

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

node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran … → 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN — "a DERIVED zero — all 18 recorded an exit code and none of them is 3".

ⓘ check:doc-formula-expressions exited 3 on the first attempt — PREREQUISITE NOT MET, its own text: "Nothing was measured: this gate exited before running a single check". Its prescribed remedy was run (turbo run build --filter=@objectstack/formula --filter=@objectstack/lint, under scripts/pm/os-verify-lock.sh, VERDICT command-exit 0) and the gate re-run to a real exit 0. The 3 is recorded here as a non-measurement, ⛔ not as a failed gate.

Repo-wide eslint — a proven narrowing, not a skip. ① The population is read from eslint's own config: every files: entry in eslint.config.mjs names only {ts,tsx,mts,cts,js,jsx,mjs,cjs} (including COMMENT_SWALLOW_FILES), and grep -c '\.md' eslint.config.mjs returns 0. ② pnpm exec eslint --no-inline-config --format json over the one changed path returns 1 result, errorCount: 0, whose single message is "File ignored because no matching configuration was supplied." — the file is outside the linted population entirely. ③ Invariance: the diff is one .md file and touches no eslint config and no TS/JS source, so no untouched file's verdict can move; type-aware linting does not enter into it, because the changed file is not in the population at all. Measured against final head ef300b686.

Changeset — skip-changeset. Nothing in docs/adr/** is shipped by any package's files[]; it is the documented fast-track surface, and the diff contains no packages/** path.

Not run here, by design: CI's own repo-wide roster. dispatch-gates names 53 artifact-roster families, 11 wide-population families, 14 pending-changeset families and 1 path-scheduled CI job as outside the 18 derived commands; each is CI's run, not this PR's local debt.

维护者速读(草稿)

改了什么:只改了一个文件 —— ADR-0131。把「那两个平台管理员报告符号在 v18 退役」这条安排摘掉,改成:它们按 #11663 的设计、在 17.x 线上退役,而且只限 walled(隔离墙)姿态。同时在文本里把一个悬着的问题答死:single 姿态那条授权行的组织归属不变 —— 仍然按 ADR-0131 原本的答案改成「默认组织所有」,仍然在 v18 的 C3 那张卡上做;在那之前它保持今天的 NULL,由 D10 的迁移来清。另外给 D14 加了一条具名例外,并更正了 §7 的一句话。⛔ 没动任何代码、没动任何其它 ADR。

为什么改:2026-09-16 您在 #18336 上选了 B —— #11663 设计说了算,并要求修订 ADR-0131 D13。ADR-0131 今天是 Accepted 且在 main 上,如果不先改它,#18336 的移除一落地,代码就和一份已接受的 ADR 直接抵触。次序是固定的:先本卡 → 再 #18336。另外您在同一会话说的「比如临时启动的开发环境,我不可能去配置啊」,加上 2026-09-08 关于 Choice 4A 的裁决(「retiring the walled write must not retire the single one」),这两条本次裁决一个字都没碰,所以范围被机械地收成「只限 walled」——零配置开发环境不受任何影响。

风险与代价(含回滚):代价很小。改的是治理记录的措辞与分线,不改变任何运行时行为:被摘掉的两个符号一个是「每进程一次的提醒日志」、一个是「测试用的开关复位」,它们既不写那条授权行、也不读它的组织字段,所以这次修订不新增、不删除、不改写任何一行数据的归属,D1(非空约束)和 D10(零-NULL 迁移)完全不受影响。风险面只有一个:D14 原本禁止「17.x 上只发本记录的一半」,现在它多了一条例外 —— 例外写成一次性用尽(只覆盖这条腿和这两个符号),D13 其余退役仍然照旧受 D10 把关。回滚成本为零:单文件 revert 即可,没有代码、没有迁移、没有发布物。

席位意见:(留空,待维护者裁定)

你要做的:审一遍这个 PR 并手动合并(受管面按章程只能由您合)。

Acceptance notes


Generated by Claude Code

…es it for the walled posture

Maintainer ruling, option B on #18336 (session session_01LmoCFLwGkcAGefVf7jryEV,
2026-09-16T09:54Z): #11663's design governs this leg, the work stays on the 17.x
line, and ADR-0131 D13 drops `reportLegacyPlatformAdminGrant` and
`resetLegacyPlatformAdminGrantReport`.

D5's assignment of those symbols (and of the legacy unscoped anchor) to D13 is
withdrawn for the walled posture. Two standing constraints the ruling did not
touch — Choice 4A (#16682 comment 5587754690) and the maintainer's zero-config
statement in the same session — narrow the retirement to the walled posture:
`single` keeps its zero-config first-user promotion and its grant row until
#11979 disposes of it.

Closes the NULL-ownership question the drop leaves open, in the text: the
`single` row still becomes Default-Organization-owned (D5's own answer, accepted
2026-09-04, untouched by the ruling), on the v18 line under C3; until C3 it keeps
today's NULL organization, which D10's migration clears. The drop moves no row —
the two symbols are a once-per-process deprecation pointer and its test latch;
neither writes the row nor reads its organization.

D14 gains one named exception for this leg, exhausted by it. D13's own
retirement list is unchanged: it never named either symbol.

Claude-Session: https://claude.ai/code/session_01Gqi43smmqjJ5sUrhfoPeKu
Co-authored-by: Claude <noreply@anthropic.com>
@os-justin os-justin added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 17, 2026 — with Claude
@github-actions github-actions Bot added the documentation Improvements or additions to documentation label Sep 17, 2026

Copy link
Copy Markdown
Collaborator Author

维护者速读(终稿)

skills 席 session_01Gqi43smmqjJ5sUrhfoPeKu,2026-09-17T12:20Z。对照本席自己读的 diff 校正 dev 草稿;席位意见已填。

改了什么 — 只改 ADR-0131 一个文件(+129 / −4),把你 2026-09-16 在 #18336 上选的 B(「#11663 设计说了算,修订 D13 摘掉两个符号,本卡留在 17.x」)写进记录。五处:状态行加 Amended 注;D5 那句「reportLegacyPlatformAdminGrant 和 legacy 锚点随 D13 退役」改写为「只限 single 姿态随 D13;walled 半边和两个符号 2026-09-16 离开 D13,按 #11663 设计在 17.x 退役」;D14 加一条具名且封口的例外;§7 审计行加同一例外;文末新增 ## Amendment (2026-09-17, #18413) 完整记录。一个读数校正:D13 自己的退役清单(:641–659)其实不含这两个符号,它们只在 D5 :362 那句指派里出现(docs/adr/ 计数 1 和 0,packages/ 对照 15 和 17);裁决触及的是这个指派,所以改的是 D5,不动 D13 清单。

为什么改 — ADR-0131 仍在 main 上,D5 逐字把这两个符号的退役指派给 D13(v18 线)。#18336 在它被修订前落地,就是代码与一份已接受的 ADR 直接矛盾。裁决同时明确没有触碰的两条约束,都逐字原文进了记录:Choice 4A(#16682 评论 5587754690,「retiring the walled write must not retire the single one」)和零配置约束(「比如临时启动的开发环境,我不可能去配置啊」)⇒ 范围只限 walled 姿态。

风险与代价(含回滚) — 治理面,只改文本;不动代码、不动别的 ADR、不动 #15193 的标签。卡上要求必须在文本里关掉的开口(single 那行的 NULL 归属),写法是:仍按 D5 改成 Default Organization 归属,在 v18 线随 C3 落地;C3 落地前维持今天的 NULL,由 D10 迁移清掉。这是 §8 C3 已排的日程的复述,不是新决定;本席复核了「摘掉两个符号不动任何一行」:platform-admin.ts:351/:371 是一次性 warn 和测试用的闩锁,不写授权行、不读它的 organization,且弃用指针本来就按姿态键控(resolve-authz-context.ts:1024)。D14 例外是裁决的机械后果:该腿已归 #11663 设计,不再是「本记录的一半」,也不缩窄任何 applyTenantScope 的 orWhereNull 臂。回滚 = revert 一个提交,零副作用。

席位意见 — 荐合。它做的是裁决 B 要求的事,且把 B 明说的两条边界原文钉在了 ADR 里;开口按 ADR 自己的答案关掉,不需要新裁决。若你认为 single 行应现在就改归属而不是等 C3,那是一个新决定,Request changes 即可,本席另起。

你要做的(一个动作) — 认可这个写法即 Approve 本 PR(或直接人工合并);席位随后按裁定 C 落地,#18336(services 席)随之解锁。


Generated by Claude Code

@os-zhuang
os-zhuang marked this pull request as ready for review September 18, 2026 01:11
@os-zhuang
os-zhuang added this pull request to the merge queue Sep 18, 2026
Merged via the queue into main with commit 0b271e2 Sep 18, 2026
44 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-18413-adr-0131-d13-drop-reporter-symbols branch September 18, 2026 01:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

3 participants