fix(spec): ADR-0005 白名单强制 —— 九类未获批 allowOrgOverride:true 回滚为 false (#6483) - #6608
Conversation
…6483) 依据 2026-08-08 维护者三段式裁决(issue #6483,os-project-manager 评论)执行, 与 #6283(flow)同一裁决家族。ADR 锚:docs/adr/0005-metadata-customization-overlay.md:53-68 (租户可定制类型白名单 + 收尾段「新类型默认 false + admission pair」)。 ━━━ 逐类型交付表(registry 行号为本分支最终行号 / origin/main 行号)━━━ 裁决分支①(立即回滚,免测量;row counts 仍测量入档): - permission registry 877 (main 820) true→false 测量: 0 条仓内活 org-scoped overlay 行; 但发现 1 条生产写路径:plugin-security ADR-0094 write-through (permission-set-projection.ts:713/752/794/928,方向确认注释 2026-07-14)。 详见下方「ADR-0094 冲突证据」。ADR 锚: ADR-0005 security 行(:58) - tool registry 970 (main 904) true→false 测量: 0 行、0 生产写点。ADR 锚: ai 行(:60) - skill registry 971 (main 905) true→false 测量: 0 行、0 生产写点。ADR 锚: ai 行(:60) 裁决分支②(先测量后定;六类全部测得 0 行 ⇒ 全部回滚): - page registry 675 (main 653) true→false 0 行。ADR 锚: ui ❌ 行(:62) - app registry 677 (main 655) true→false 0 行;生产写点 runtime/domains/packages.ts:232 (ADR-0045 publish unhide)经核实走 allowRuntimeCreate 层(DB-materialized 行, sys_metadata provenance ≠ artifact),不受影响。ADR 锚: ui ❌ 行(:62) - action registry 681 (main 656) true→false 0 行;其 supportsOverlay:false 与 true 并存 即 #6283 flow 的 #6190 幽灵写形态,本次一并关闭。ADR 锚: ui ❌ 行(:62) - dataset registry 685 (main 660) true→false 0 行;表中缺席 ⇒ 收尾段默认 false。 - book registry 852 (main 817) true→false 0 行;表中缺席 ⇒ 默认 false。 - position registry 878 (main 821) true→false 0 行;表中缺席 ⇒ 默认 false。 admission pair 测量:九类均有 resolveOverlaySchema() 接受的 overlay schema (schema registry 统一后 (a) 恒真),九类均无 ADR-0005 要求的书面 render-only 理据 ⇒ (b) 全部不成立。无「有行且理据齐全」形态,无需 promote 保留。 对照组不动(核实):view 674 / dashboard 676 / report 682 = true; translation/email_template = true;flow 717 = false(#6283/PR #6478 已合并,未触碰)。 ━━━ 测量方法(入档)━━━ 1. 种子/夹具静态扫描: grep 全仓 *.seed.{ts,yml,json}·examples/·dogfood 数据· sys_metadata insert 点 —— 仅 1 个生产 insert(repository 本身),0 条九类 overlay 行。 2. 逐类型字面量普查: grep "type: '<t>'" 全仓非测试代码,逐文件人工分类 (artifact 定义 vs overlay 写)。 3. 决定性实验:九类临时全部翻 false,跑 spec/metadata-protocol/objectql/rest/runtime/metadata/plugin-security/dogfood 全套 —— 失败 16 例 = 精确爆炸半径(全部为测试对旧 true 值的依赖,逐一如实对齐, 无一静默放宽;清单见下)。 ━━━ 写侧消费者核实 ━━━ - OVERLAY_ALLOWED_TYPES (metadata-protocol/src/protocol.ts:6954) 派生读取 ✓ 自然收敛 - assertAllowed 白名单 (metadata-protocol/src/sys-metadata-repository.ts:184,995) 派生读取 ✓ 自然收敛 - 无任何硬编码副本需要对齐;promoteDraft 生产调用方(protocol.ts:8799)已正确 按 isArtifactBacked 传 intent,无需改动(仅测试补传 intent)。 ━━━ 测试随动(16 处失败 → 全绿)━━━ 新增: metadata-protocol/src/protocol.adr0005-org-override-rollback.test.ts (40 例:9×声明 pin + 9×2 403 拒绝(org-scoped/无 environmentId)+ 9×runtime-create 仍开 + 派生集 pin + action 幽灵形态 pin + view/dashboard/report 对照) 对齐(specimen 改走 OS_METADATA_WRITABLE 文档化逃生门,机制覆盖不降): - protocol.save-receipt-wording.test.ts / protocol.delete-receipt-wording.test.ts (overlay-less-yet-overridable 种群 pin 为空集 + ACTION 收据案例挂逃生门) - objectql/protocol-registry-shadow.test.ts(4 例,app 信封机制) - objectql/protocol-meta.test.ts([#5265] app 收据案例) - runtime/meta-overlay-read-your-writes.test.ts(action shadow/restore 机制) 对齐(断言新行为): - objectql/overlay-precedence.test.ts(permission/position 期望 false + 重引入护栏注释) - objectql/sys-metadata-repository.test.ts(promoteDraft 测试补 intent:'runtime-only', 与生产调用方一致) 反转(ADR-0094 行为 pin → 403 拒绝 pin,见冲突证据): - dogfood/showcase-permission-projection.dogfood.test.ts(2 例反转 + 文件头改述) - dogfood/two-doors-permission.dogfood.test.ts(块2 两例反转 + env-authored 案例 换真正 runtime-authored specimen + 新增 artifact-backed 拒绝案例) ━━━ ③ 治理锚 ━━━ - registry 四个域段落均加 ADR-0005 + #6483 注释(ui 段 675-685 前、book 852 前、 security 877 前、ai 970 前),含「promote 须修订 ADR-0005 而非改本文件」指路。 - scripts/adr-anchors.json 新增 packages/spec/src/kernel/metadata-plugin.zod.ts → ADR-0005 锚(#6191 同机制);check:adr-anchors 39/39 OK。 ━━━ 验证读数 ━━━ - spec test: 342 files / 8786 全绿;spec typecheck 绿 - metadata-protocol test: 58 files / 651+40 全绿 - objectql: 147 files / 2470 全绿;runtime: 111 files / 1611 全绿 - metadata / plugin-security / rest / dogfood: 全绿(dogfood 86 files, permission 两文件 13/13) - check:adr-anchors OK(39);check:generated 10/10(registry 值不入生成物, authorable-defaults 仅含 schema 默认值 false,无需重生成) - pnpm lint(改动文件)0 error;三示例 objectstack validate exit 0/0/0 (仓内自身元数据不依赖被回滚的 overlay 能力,warnings 为存量与本单无关) - 逆向验证(方向先判后跑):skill false→true 重建后,预测 4 红 (声明 pin + 派生集 pin + 2 拒绝断言以「promise resolved {success:true} instead of rejecting」形态红)/其余 36 绿;实测 4 红/36 绿,恢复后 40/40 绿。 - push 前已 merge 最新 origin/main(d0a5ceb,快进,无重叠文件);合并基座上 复跑:metadata-protocol 58 files/651 全绿、spec test+typecheck 与 objectql 全绿、check:adr-anchors 39 OK、check:generated 10/10、ADR-0087 门禁绿。 ━━━ ADR-0094 冲突证据(留独立 ADR-0005 修订单裁处)━━━ permission 回滚与 ADR-0094「2026-07-14 方向确认:package 集经 ADR-0005 overlay 做环境定制」直接冲突。证据链: 1. plugin-security/src/permission-set-projection.ts 文件头注释明写该方向及日期; 写路径 4 处 saveMetaItem({type:'permission'})。 2. dogfood 两文件 4 例曾以功能 pin 固化该行为(本 commit 已按裁决反转为 403 pin)。 3. 其单测 permission-set-projection.test.ts 为 mock saveMetaItem 的隔离 harness, 感知不到真实门 —— 回滚后依旧全绿(未改动;真实门行为由 dogfood + 新增 403 套件覆盖)。 4. 行为边界实测:门读 artifact-backing 而非记录 managed_by —— code-declared 集(member_default、bootStack 下的 showcase_contributor)拒 403; runtime-created / metadata-door materialized 集(sys_metadata provenance) 经 allowRuntimeCreate 层不受影响。 若维护者意图保留 ADR-0094 定制通道,应在独立修订单中为 permission 补 admission pair 后 promote —— 本 PR 未做任何 promote。 changeset: .changeset/adr0005-nine-type-org-override-rollback.md(minor —— 实测 全类型 0 活行,按裁决口径 minor 如实写明 403 行为收紧;ADR-0087 门禁绿, 无 breaking 声明故无需处置标记)。 范围红线遵守:未动 flow 行;未做 promote;未动 objectui(#5768 症状在 changeset/注释中交叉引用);未动 content/docs/releases/。 Closes #6483 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DE52u4GiUSiVh1F7F75arw
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 113 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
…teral union check:type-check-debt --re-measure caught +2 in metadata-protocol's DEBT layer (63 -> 65): ROLLED_BACK and CONTROLS were bare string[], and the derived-set pin calls Set<RegistryType>.has() on them. Typed via the registry's own literal union — as const satisfies for the controls, a cast for Object.keys(BODIES) whose keys are the nine rolled-back types by construction. tsc raw count back to 63; the 40-case suite stays green.
…0005-override-rollback # Conflicts: # scripts/adr-anchors.json
…0005-override-rollback
…LE 逃生门 合并 origin/main 后的 jointly-wrong 破坏(AGENTS.md §10):两侧各自正确、 合起来红。main 在 meta-overlay-read-your-writes.test.ts 新增了整个 `#5079 — list / get / dispatch agree immediately after deleteMeta` describe 块, 其中 `an ARTIFACT-backed delete resets to the shipped value` 用例对一个 PACKAGED action 做 overlay 写;本 PR 把 `action` 的 allowOrgOverride 回滚为 false(#6483 裁决),该写点遂抛 NOT_OVERRIDABLE。文本合并干净,故 git 无冲突。 对齐方式沿用本 PR 已提交的同类处置(#4521 块中的同形用例):打开 `OS_METADATA_WRITABLE` 运营逃生门并重置两处 memoised 缓存,配 afterEach 清理, 避免 action 可写性泄漏到相邻用例。该 describe 块其余用例写的都是全新名字, 走 allowRuntimeCreate,不受回滚影响,故只此一处需要逃生门。 pnpm --filter @objectstack/runtime test: 115 files / 1733 tests 全绿。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_011sGk4SKHqGRgmmqUok1P8M
Merge conflict resolved; branch re-greened
The one conflict — One jointly-wrong breakage fixed (AGENTS.md §10 — a clean merge is not a working merge). Sibling-survival assertions after the merge: the nine rolled-back rows ( Gates re-run locally, one by one, enumerated from
No generated spec artifact needed regeneration — the merged tree's generated paths are byte-identical to The branch merges cleanly against current Generated by Claude Code Generated by Claude Code |
⛔ merge queue 构建失败 — 先分诊,再决定要不要重排队列构建 31294377737 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集), 失败的 job(日志抽取,best effort):
历史信号:
分诊清单:
Generated by Claude Code · merge-queue-triage workflow (#4859) |
Queue steward — NEW signature recorded, not re-queued (round 70)This PR was kicked out of the merge queue once at 04:21Z. The steward's four-branch signature triage lands on new signature, whose prescription is ⛔ no re-queue — recording only. Moot in outcome (see "What actually happened"), but the signature is now on the record. Full signature (read from the complete log archive, not the tail — SKILL note 7)
Ledger check (#5810, human-only to amend)Matches none of the four objectstack rows nor the cross-repo row: it is not a 5000ms timeout (so neither the #4796/#4856 "fixed signature" row nor the #6044 coverage-hole row), not the mongodb-memory-server download race, not merge_group cold-cache slowness, not infrastructure flakiness of the runner/registry kind. ⇒ New signature ⇒ ⛔ not re-queued by this seat. What actually happened (no action was needed)The PR re-entered the queue on its own at 04:49:19Z ( Preliminary reading (no verdict, no code touched)The failing test is not in this PR's diff (13 files; none under Routed to the spec lane on #6483 and proposed for ledger evaluation on #5810. ⛔ The steward does not amend the ledger itself. Seat authority (unchanged)No re-queue, no merge, no ready/draft flip, no assignment, no code touched by this seat. Generated by Claude Code |
…nting at D5-R (#8292) (#8385) ADR-0086 carried three live-voiced, present-tense pointers at ADR-0094 D5's 2026-07-14 direction (an env overlay of a package-owned permission set is a first-class ADR-0005 customization), which was RETIRED on 2026-08-09 by D5-R (#6858 / PR #6962) after #6483 / PR #6608 rolled `permission` back to `allowOrgOverride: false`: - D7 status row: "an env edit of a package set is now a first-class overlay, not a flat 403" — directly contradicted by the current 403 not_overridable. - P2 status row: "Subtract layer landed as ADR-0005 first-class overlay". - The superseded-mechanisms block, item 2 (the site named in #8292). Each now names D5-R and marks the direction retired, following the idiom PR #8291 used inside ADR-0094 D2. The upshot is recorded where it matters: this ADR's own two-doors answer stands unsuperseded — edit the package and re-publish. The narrow carry-overs D5-R preserves (data-door delete still degrades to a reset; supportsOverlay unchanged, so a pre-rollback overlay row still merges at read time) are stated so they are not mistaken for a re-route. The historical record is deliberately kept as history, not rewritten. A fourth D5 citation (D4 status row, composition anchored at the position) is left untouched: D5-R does not withdraw that bullet. Part of #8292 Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz Co-authored-by: Claude <noreply@anthropic.com>
…DR-0094 D5-R (objectstack-ai#7082) (objectstack-ai#7346) PR objectstack-ai#6962 retired the 2026-07-14 env-overlay direction and fixed this file's header; the function-level JSDoc of `upsertEnvPermissionSet` kept the same two claims D5-R retired -- "an env overlay is the platform's standard customization of a packaged definition" and "deleting the overlay resets the row to the shipped declaration". objectstack-ai#6483 / PR objectstack-ai#6608 rolled `permission` back to `allowOrgOverride: false` (the first is 403 NOT_OVERRIDABLE) and objectstack-ai#6960 measures the delete path refusing to lift even a legacy overlay (the second). Prose only, no behaviour change. Three neighbouring copies in the same package are corrected with it so the package no longer states the direction in two voices. Claude-Session: https://claude.ai/code/session_01BM1tNf5U3nEbHKR4fo5qVQ Co-authored-by: Claude <noreply@anthropic.com>
…ed-back overlayable type (objectstack-ai#6960) (objectstack-ai#7429) objectstack-ai#6483 / PR objectstack-ai#6608 closed the per-org WRITE door on `permission` / `position` / `page` / `app` / `dataset` / `book` and left `supportsOverlay: true` alone, so a row authored before the rollback still merges overlay-wins at read time while the ordinary delete answered 403. Per the maintainer's ruling of 2026-08-10 the delete side moves: removing the overlay restores the code-declared state, is strictly narrowing, and cannot widen anything. Both refusal points are treated, because the second is topology-independent: * `deleteMetaItem`'s `environmentId !== undefined` two-tier block, which refused before it even probed for the row; * `SysMetadataRepository`, which now routes the DELETE verb through `assertDeleteAllowed` while `put` keeps calling `assertAllowed` unchanged — so a control-plane kernel, which skips the first gate entirely, is released by the same rule rather than left half-fixed. The carve-out is keyed on the registry's `supportsOverlay` flag, not on `allowOrgOverride`, so it stops at the tier boundary: `object` (`supportsOverlay: false`, its overlay a contributor layer per ADR-0029 D9) keeps refusing both verbs, which is D9.6's declared cost. Create and update are untouched, and both gates' doc comments now record the delete-only asymmetry so it is not later "fixed" into symmetry. Claude-Session: https://claude.ai/code/session_01W6bLax4KMrSfnE1ydFU8Dw Co-authored-by: Claude <noreply@anthropic.com>
…ired 2026-07-14 overlay direction (objectstack-ai#7351) (objectstack-ai#8290) Both suites already pin the post-objectstack-ai#6483 refusal correctly in their test bodies, which carry the objectstack-ai#6483 annotations. Only the file headers still described the 2026-07-14 direction — "a data-plane edit is TRANSLATED into an env-scope ADR-0005 overlay" and "delete RESETS to the declared body" — as current. That direction was retired on 2026-08-09 by ADR-0094 D5-R (objectstack-ai#6858 / PR objectstack-ai#6962), after objectstack-ai#6483 / PR objectstack-ai#6608 rolled `permission` back to `allowOrgOverride: false`. Prose only: the diff touches comment lines exclusively, no assertion and no behaviour changes. Claude-Session: https://claude.ai/code/session_016pY4Xb2iDecfDtT3CWoiTW Co-authored-by: Claude <noreply@anthropic.com>
…or edits of packaged permission sets (objectstack-ai#8292) (objectstack-ai#8384) ADR-0094 D5's 2026-07-14 direction (translate an admin-door edit of a package-owned permission set into a first-class ADR-0005 env-scope overlay) was retired on 2026-08-09 by D5-R (objectstack-ai#6858 / PR objectstack-ai#6962), after objectstack-ai#6483 / PR objectstack-ai#6608 rolled `permission` back to `allowOrgOverride: false`. The published page still asserted the retired direction in the present tense — and inverted: it told a reader the edit succeeds, when the platform answers 403 not_overridable. Rewrites the data-layer gate bullet to state current behavior and name the supported channel (ADR-0086 two-doors: edit the package and re-publish), in retired-voice consistent with metadata-plugin.zod.ts:921. The sibling bullet ("deleting an artifact-backed set resets it to its declared body") is left unchanged: it is still correct, pinned end-to-end by packages/qa/dogfood/test/two-doors-permission.dogfood.test.ts. Part of objectstack-ai#8292 Claude-Session: https://claude.ai/code/session_01Jqe56GnYFddggeAyfkZFVz Co-authored-by: Claude <noreply@anthropic.com>
Fixes #6483
依据 2026-08-08 维护者三段式裁决(issue 评论)执行,与 #6283(flow 行)同一裁决家族。完整交付表、测量方法、16 处测试随动清单与验证读数见分支 head commit message(
95ef570fb)—— 本正文摘要三个复核要点与一处需维护者知情的冲突。交付摘要
permission/tool/skill→false(row counts 仍测量入档:全部 0 条仓内活 org-scoped overlay 行)。page/app/action/dataset/book/position六类全部实测 0 行 ⇒ 全部回滚。admission pair 测量:九类均无 ADR-0005 要求的书面 render-only 理据,无「有行且理据齐全」形态,无 promote。scripts/adr-anchors.json登记(ADR-0005 的租户可定制白名单表说flow/workflow/approval一律 ❌ per-org override,注册表却写着allowOrgOverride: true——反转没有走 ADR(Prime Directive #13) #6191 同机制),check:adr-anchors39/39 OK。OVERLAY_ALLOWED_TYPES/assertAllowed)均为派生读取,自然收敛,零硬编码副本。allowOrgOverride:true回滚为 false——#6155 Q1=B 裁决的契约半边,同步了结 #6191 #6283 已落)核实未触碰。permission回滚与 ADR-0094「2026-07-14 方向确认:package 集经 ADR-0005 overlay 做环境定制」直接冲突 —— plugin-security 的permission-set-projection.ts有 4 处生产写点(:713/:752/:794/:928),回滚后 code-declared 集的 write-through 拒 403(runtime-created / metadata-door materialized 集经 allowRuntimeCreate 层不受影响)。裁决①明令 permission 免测量回滚且「爆炸半径入档」,故本 PR 按裁决执行并把 dogfood 两文件 4 条功能 pin 反转为 403 pin;若维护者意图保留 ADR-0094 定制通道,应在独立修订单中为 permission 补 admission pair 后 promote —— 本 PR 未做任何 promote。该裁处已由 PM 立决策卡跟踪。交付通道注记
云端工头 F 会话(
session_01DE52u4GiUSiVh1F7F75arw)无 GitHub 工具,走交付降级通道:dev 实现并 push 分支,PM(session_011M7UwH25Unfi73UHim7ajY)代开本 PR 并跟进 CI 至合并。Generated by Claude Code