Skip to content

refactor(spec)!: delete the seven cron-typed positions outright — no tombstone, no D2, no D3 (#16320, ADR-0049, the 直接删 ruling) - #17146

Merged
os-zhuang merged 17 commits into
mainfrom
claude/issue-16320-retire-cron-typed-positions
Sep 10, 2026
Merged

refactor(spec)!: delete the seven cron-typed positions outright — no tombstone, no D2, no D3 (#16320, ADR-0049, the 直接删 ruling)#17146
os-zhuang merged 17 commits into
mainfrom
claude/issue-16320-retire-cron-typed-positions

Conversation

@os-bill

@os-bill os-bill commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Fixes #16320

Clause-②: yes

Reworked on the maintainer ruling of 2026-09-10 recorded on this PR (comment 5614475165): 直接删. The previous generation retired the seven cron-typed positions the tombstone way — retiredKey() at every site, seven RETIRED_KEYS_BY_MAJOR[18] entries, one D2 conversion for the connector family, five D3 semantic entries. The maintainer ruled that whole kit out, after the seat had recommended the opposite in writing and stated the cost. The kit is deleted; the keys simply stop existing. 42 files became 29.

本轮(合约复审 must-fix)只改措辞,不改一行行为代码。 复审在这条 PR 的 head 上实测出:四处文本把这次删除的代价写成「静默」,而它不是——os validate / os build 会点名被丢掉的键,os validate --strict 会直接退出 1 拒收。维护者是靠「按下合并键」来签这段代价陈述的,所以那四处被更正:PR 正文的〈维护者速读〉、changeset、migrations/registry.ts 的 step-18 rationale、cron-typed-positions-retirement.test.ts 的两处注释。同轮把条款②的放宽从「两处」更正为七处(其中三处是必填键消失),并在 changeset 里点名 ScheduleExportInput.cronExpression 这个已发布 TS 成员的离场。⛔ 七处删除、合并与 ADR-0087 处置本身不重开。

The diff touches skills/objectstack-formula/SKILL.mdGOVERNED: draft only, a human merges. Never ready, never queued, never auto-merged, never approved by a seat.

维护者速读(草稿)

改了什么。 七个从来没有引擎读过的 cron 位置被直接删除(api/export.zod.ts ×2、automation/execution.zod.tsintegration/connector.zod.tssystem/cache.zod.tssystem/disaster-recovery.zod.ts ×2)。没有 retiredKey() 墓碑,没有 RETIRED_KEYS_BY_MAJOR[18] 条目,没有 D2 conversion,没有 D3 semantic 条目,protocol-18 迁移链里不留任何一行。authorable-surface/ 里原有的四行随键一起删掉。

为什么改。 执行维护者 2026-09-10 的裁决「直接删」,理由是「我们的客户也不会按照你的设想的版本按顺序升级」。⛔ 本轮不复议该裁决。

风险与代价(含回滚)。 请在按下合并键之前看这一段。

  • DataSyncConfig.schedule 是七个位置里唯一从 stack manifest 够得着的:stack.zod.ts connectors[]connector.zod.ts syncConfigschedule
  • ⚠️ 上一版本 PR 正文在这一段写错了,本轮更正——更正后的事实对作者更有利。 上一版写的是「静默丢弃,没有报错、没有警告、没有任何可 grep 的痕迹」。那句话只在 parse 这一层成立;它把 parse 当成了全部,而 CLI 那一层会说话。请按下面这份实测读,不要按上一版读。
  • 准确的代价陈述,四条通道分开说(全部在本分支 head 1504dd1d77 上实测):
    • parse 层:静默剥除。 DataSyncConfigSchema 不是 .strict(),zod 丢掉该键并返回 success: true——ADR-0104 命名的形态。这一层确实不告诉任何人。
    • os validate:退出码 0,但点名这个键。 实测输出:⚠ connectors.sap_erp.syncConfig.schedule: 'schedule' is not a declared connector key, so its value is dropped at load.(--jsonwarnings 里是同一句)。对照组(同一份 manifest 去掉该键)少的正是这一行。
    • os validate --strict:退出码 1,拒收。 在一份其余全干净的 stack 上实测:不带该键 = 0 条警告、exit 0;带该键 = 1 条警告、exit 1。⇒ CI 里跑 --strict 的流水线会直接拒绝升级后的 manifest,直到作者删掉这个键。
    • os build:同样点名,印在 Undeclared authoring keys (1) — dropped at load (#3786) 之下。
    • os migrate meta:什么都不列(实测 --from 16--from 17 两次,输出里没有这七个位置的任何一条)。这一条是「直接删」这条路线真正放弃的东西:没有升级处方。
  • 机制(不是推断,是读源码后再实测):lintUnknownAuthoringKeys(排查「手抄 spec 清单 + "keep in sync" 注释」模式:一天内确认三例,全部曾静默漂移 #3786)遍历每一个 entry schema 为 strip 模式的 stack 集合,connectors: 'connector' 就在那张表里。所以七个位置中唯一从 manifest 够得着的那个,恰好落在这条会说话的通道上;另外六个 manifest 根本够不着,对它们来说 parse 层的静默剥除就是全部。
  • 也就是说:代价不是「加载失败、无人知晓」,而是「加载成功但被点名警告,--strict 下直接拒绝」。对作者的可见性比上一版正文所说的更高;比裁决评论当时假设的(「墓碑负责响亮拒绝」)低一档,但并没有低到无声。抵消的一点:这个 cron 从来就没有引擎执行过,所以运行时行为没有任何变化——该值删除前后同样惰性。tsc 是第五条通道:用 Connector 等类型标注的 TypeScript 作者会在那个键上拿到编译错误。
  • 另一半代价:接受集放宽了七处(不是上一版说的两处),其中三处是必填键的消失。逐行实测见下方〈Clause ②〉一节。
  • 回滚。 整轮返工是分支上的两个增量 commit(88aeee32bf1504dd1d77),b16c221a89 是墓碑版的最后一个提交。回到墓碑路线 = 回退这两个 commit,不需要重做任何东西。

席位意见。(留空,归维护者)

你要做的。 只有一件:确认上面那份四通道的实测代价可接受——parse 层静默丢弃、os validate / os build 点名该键、os validate --strict 退出 1 拒收、os migrate meta 无处方——然后手工合并。⛔ 不要入队,⛔ 不要挂 auto-merge——skills/** 在差异里。

删掉了什么(逐项,对照裁决表)

裁决表的行 本 PR 的落地
7 × migrations/entries/retired-keys/18.*.ts 全删(7 个文件)
5 × migrations/entries/semantic/18.*-retired.ts 全删(5 个文件)
migrations/registry.ts 的生成区域 gen:migration-registry 重生成,净 −346 行;step18.conversionIds 手工去掉 connector-sync-schedule-removed
conversions/registry.ts 的 D2 全删(条目 + 数组引用,−112 行)
六个 schema 文件的墓碑一半 只留删键那一半;树里再无一个属于这七个位置的 retiredKey
cron-typed-positions-retirement.test.ts 516 行 → 408 行,重写成「这七个位置不再存在」的 pin
.changeset/cron-typed-positions-retired.md 按新的小改动重写

每个删除点在源码里留一条注释,记下删了什么、为什么从来没人读它、真正生效的机制是 Job.schedule.expression

前提核验 —— 裁决表逐行按本分支的 head 复核

裁决表是 2026-09-10T07:00Z 从 42 文件差异读出来的。逐行复核(基点 c47edbb217,墓碑版 tip b16c221a89):七个 retired-key 条目文件、五个 semantic 条目文件、D2 条目、六个 schema 文件的删键+墓碑对、516 行的 pin 测试、136 行的 changeset —— 全部原样存在,没有一行在裁决之后被别人动过。migrations/registry.ts 当时读作 +351/−1,合并 origin/main 之后读作 +352/−1(一行来自 main 的无关条目),不影响任何一步。

一处实测证伪:裁决评论把「删掉 D2 ⇒ manifest 加载失败」当作既定代价。那句话在墓碑保留的前提下为真;在「直接删」下为假——但也不是静默。实测:parse 层丢弃,os validate / os build 点名该键,os validate --strict 退出 1。上一版正文把它写成「没有任何可 grep 的痕迹」,那是本轮更正的对象。详见上面的〈风险与代价〉。这一项按证据报告,⛔ 不作为复议。

合并 origin/main —— 驱动路径两侧都没丢

分支落后 15 个 commit,用 bash scripts/pm/os-regen-merge.sh 合入(merge commit 3a881f3b6c,脚本 exit 0)。风险清单.gitattributes 现读,不从上一次事故推:18 条 merge=os-regen 模式。

  • 分支侧动过的路由文件:8。main 侧动过的:14。两侧都动过的:0 ⇒ 自定义合并驱动从未被调用,静默丢一侧在结构上不可能发生。
  • 逐路径证明,22 条全部为 0:分支独有的 8 条,git diff TOMBSTONE_TIP MERGE_COMMIT -- PATH 空;main 独有的 14 条,git diff MAIN_TIP MERGE_COMMIT -- PATH 空。
  • 点亮对照:packages/spec/authorable-surface/system.json 在基点→分支上读到 2+/2−(非零,证明这个读数器看得见改动)。熄灭对照:虚构路径 packages/spec/authorable-surface/zzz-not-a-real-category.json 在同一读法下读到 0 行、且不在 HEAD 里(证明 0 是真的 0,不是读数器坏了)。
  • $GIT_DIR/os-regen-pending 不存在,与「驱动从未被调用」一致。

门禁 —— 跑了什么,读数是什么

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack 在本轮 head 上推出 114 条命令(脚本自报 STALE TREE:该树至少落后 origin/main 4 个提交,14 个它据以推导的文件在那段区间里变过 ⇒ 「只多不少」的保证在此不成立,增量归 PM 在 CI 上读)。本轮差异只有三个文件(changeset、migrations/registry.ts、pin 测试),全部是措辞。下面这些在本轮最终 commit 2095e4e918 上跑过,其余留给 CI。

命令 退出码 门禁自己印的判定行
pnpm --filter @objectstack/spec build 0 check-dts-emitted: @objectstack/spec - 34/34 declared declaration file(s) present.
pnpm --filter @objectstack/spec check:generated 0 ✓ All 15 generated artifacts are up to date.(⇒ registry.ts 的 step-18 rationale 是手工维护的文本,不在生成区域)
pnpm --filter @objectstack/spec check:spec-changes 0 spec-changes.json is up to date.
pnpm --filter @objectstack/spec exec vitest run src/cron-typed-positions-retirement.test.ts src/migrations 0 Test Files 2 passed (2) · Tests 136 passed (136)
pnpm --filter @objectstack/spec typecheck 0 —(无输出即通过)
pnpm --filter @objectstack/spec check:test-typecheck 0 OK — test layer compiles; 54 files / 261 errors / 145 pinned signatures(与上一轮同数,未动棘轮)
node scripts/check-adr-0087-registration.mjs --base origin/main 0 ✓ 1 declared-breaking changeset(s), each carrying an ADR-0087 disposition. —— 提交之后跑的(该门禁从 git 读 changeset)
node scripts/check-adr-0087-registration.mjs --self-test 0 338 assertions over real temp git repos
node scripts/check-changeset-no-major.mjs --base origin/main 0 ✓ This diff introduces no major bump.
node scripts/check-empty-changeset.mjs --base origin/main 0 ✓ No empty-frontmatter changeset introduced by this diff (1 declaring changeset(s) added).
pnpm check:changeset-gate-self-tests 0 118 + 338 + 265 assertions,三个自检全绿
pnpm check:nul-bytes 0 OK (scanned 8172 text file(s); no raw ASCII control bytes);另对三个改动文件单独跑了 grep -naP 控制字符扫描,0 命中
node scripts/check-skills-token-ratchet.mjs 0 ✓ 36 authored bundle file(s) within their ceilings(本轮未触 skills/**)
node scripts/check-spec-docblock-symbol-anchors.mjs 0 通过
pnpm check:doc-authoring 0 46 published skill files clean · 15184 customer-facing string(s) across 878 spec sources clean
pnpm check:pm-governed-merges · pnpm check:pm-governed-prose 0 通过
npx eslint . --no-inline-config --format json 0 全仓 6485 个文件,0 error / 0 warning(未收窄,在本轮最终 commit 2095e4e918 上跑的)

一件维护者应当知道的门禁事实

check:adr-0087-registration 与 AGENTS.md 的「breaking changeset 必须带 FROM → TO 映射」在这条路线下互相矛盾,实测两次:

  • changeset 正文一旦带上房内惯例的 ## FROM → TO 标题,该门禁拒绝 not-required (no-migration-prescription)(实测 exit 1,证据行 from-to-label),要求改为 registered ENTRY_ID —— 而裁决恰恰规定一个条目都不注册。type-surface-only 这条豁免被它自己的 no-spec-diff 谓词挡住(本 diff 就在 packages/spec 里)。
  • 不带那个标题时门禁通过(exit 0)。

本 PR 走的是通过那一条,并且在 changeset 正文里明写为什么没有 before/after 映射:直接删在非 strict schema 上既不拒绝也不转换,元数据升级者无事可做,os migrate meta 无事可列 —— 这句话是真的,不是为了绕门禁。差别在于:门禁的机械判据读的是标题里的那个字符串,而不是那段话的意思。这一点作为发现报给 PM,不在本 PR 内修。

Clause ② —— 重判:仍然是 yes,放宽是七处,不是上一版说的两处

判据(lanes/spec.md):放宽接受集或扩大公开面即条款②,收窄不触。上一版正文只点了两处,漏了五处;其中三处是必填键的消失,不是一处。

两条腿都实测,不是从 diff 推的。 把 5 个 schema 文件按 merge-base(cca1dc0bfb1e)整文件还原到磁盘,用同一个探针跑一遍(base 腿),再还原成 head 重跑(head 腿);还原后逐文件 git hash-objectHEAD:PATH blob 比对,5/5 相同,git diff HEAD 为空。两腿各带 5 条阳性对照(well-formed fixture 在两腿都 success=true)。

# 位置 原本 探针输入 base 腿 head 腿
1 automation/ScheduleState:cronExpression 必填 不带该键的文档 invalid_union@cronExpression 拒绝 通过
1 同上 必填 cronExpression: 42 invalid_union@cronExpression 拒绝 通过并剥除
2 api/ScheduledExport:schedule.cronExpression 必填 schedule: {} invalid_union@schedule.cronExpression 拒绝 通过
3 api/ScheduleExportRequest:schedule.cronExpression 必填 schedule: {} invalid_union@schedule.cronExpression 拒绝 通过
4 integration/DataSyncConfig:schedule 可选 union {dialect:'template',source:…} invalid_union@schedule 拒绝 通过并剥除
4 同上 可选 union schedule: 42 invalid_union@schedule 拒绝 通过并剥除
5 system/CacheWarmup:schedule 可选 union 同上两种 两次都拒绝 两次都通过并剥除
6 system/BackupConfig:schedule 可选 union 同上两种 两次都拒绝 两次都通过并剥除
7 system/DisasterRecoveryPlan:testing.schedule 可选 union 错方言 envelope · 12 两次都拒绝(@testing.schedule) 两次都通过并剥除

14 个用例,base 腿 14 次 success=false,head 腿 14 次 success=true 且键在 parse 后一律不存在。⇒ 七处全部是放宽,Clause-②: yes 成立。

公开面另有一处收窄需要点名(A2): contracts/export-service.tsScheduleExportInput 删掉了必填成员 cronExpression: string,那是 IExportService.scheduleExport 的入参类型,一个已发布的 TS 接口(两个名字都在 api-surface/contracts.json 里)。上一版 changeset 只写了「no def leaves the public surface」——那句话为真,但盖不住这个成员。本轮已在 changeset 里点名。清扫读数:packages/spec 之外本仓 0 处引用 ScheduleExportInput / IExportService(阳性对照:同一次清扫里同类符号在 packages/spec 之外有命中),objectui 0 处(对照:该仓 1326 个文件 import @objectstack/spec)。cloud 仓不在本容器的检出里 ⇒ 该仓 NOT MEASURED

pin 测试固定七处的 head 腿行为。needs:contract-review 标签仍在 PR 上,⛔ 本轮不摘。

skills/** 读数(受管面,净减)

读数 before after
skills/objectstack-formula/SKILL.md 行数 454 454 0
同文件字节 20808 20718 −90
同文件 token(check:skills-token-ratchet 的计数,天花板 6002 未动) 5249 5227 −22
整包(skills/** 全部 SKILL.md 行数之和) 6853 6853 0

改动是一处表格单元格的删减(cron 方言的 Carriers 列去掉三个已退役的载体,保留 Job.schedule.expression),不是扩写。

生成文档的一行说明(不作处理)

重新生成的 content/docs/references/integration/connector.mdx 里带着几行与本 PR 无关的生成器漂移(有默认值的键从 strategy?: 变成 strategy:):main 上那份副本相对它自己的生成器是陈旧的,本 PR 只是跑了一次生成器把它对齐。check:docs 在 head 上是绿的。⛔ 本 PR 不处理它,记在这里只是为了别把它读成 schema 变更。

一致性

本裁决按其字面只覆盖这张 PR。⛔ 本 PR 不触碰、不对齐、也不建议对齐任何其他退役 PR。

本轮由 Claude Code 会话 session_01MkQhmuuJAVDjmeWNixwDDH 产出。署名以正文散文形式记在这里,而不是尾部页脚:实测本仓的裸 REST PATCH /pulls 在写 PR 正文时无条件追加一条裸署名页脚 —— 同一通道两向受控实测,送出体带前置横线的页脚块与不带横线的单行页脚,两次都被追加,结果都是两条。所以正文自身不再携带页脚,页面上那一条是平台追加的,恰好一条。(AGENTS.md〈GitHub mutates body BYTES〉:耐久归属写进正文散文或评论,⛔ 不循环重贴页脚。)


Generated by Claude Code

…087 entries, pins

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
…e docs, fix cache carrier fixture

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
@github-actions

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

10 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 5 changed file(s) yielded no anchor (packages/spec/authorable-surface/automation.json, packages/spec/authorable-surface/integration.json, packages/spec/authorable-surface/system.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 5 changed file(s) yielded no anchor (packages/spec/authorable-surface/automation.json, packages/spec/authorable-surface/integration.json, packages/spec/authorable-surface/system.json, …) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 134 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 143c715a99f5e663e331158a786367d7a1a41657packageMentionDocs.

Which tree this was computed on

This run read content/docs from 242836640091a8f74b03eed451c8a7d601fc63ac — the merge of head 2095e4e918299c68601788247e1302fd63e55ef9 into base 143c715a99f5e663e331158a786367d7a1a41657, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 242836640091a8f74b03eed451c8a7d601fc63ac && git checkout 242836640091a8f74b03eed451c8a7d601fc63ac
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 143c715a99f5e663e331158a786367d7a1a41657 2095e4e918299c68601788247e1302fd63e55ef9 && git checkout -B drift-repro 143c715a99f5e663e331158a786367d7a1a41657 && git merge --no-ff 2095e4e918299c68601788247e1302fd63e55ef9

node scripts/docs-audit/affected-docs.mjs --json 143c715a99f5e663e331158a786367d7a1a41657

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

⛔ Do not merge at this head — mergeable: clean is a false green on this PR

domain:spec seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T00:41Z. Readings below are on PR head e5e87ccba3dea43cc7189ef724fc4edea0a8a8a3 and the merge base 4261fbc8…, fetched at that time.

The API says this PR is fine:

GET /pulls/17146  →  mergeable: true, mergeable_state: "clean"

That reading is computed by plain git, and one file in this diff is not merged by plain git.

The measurement

$ git check-attr merge -- content/docs/references/api/export.mdx
content/docs/references/api/export.mdx: merge: os-regen
$ git check-attr merge -- packages/spec/src/data/hook.zod.ts        # control
packages/spec/src/data/hook.zod.ts: merge: unspecified

.gitattributes:153 routes content/docs/references/** to merge=os-regen. What that driver does is stated by scripts/pm/os-regen-merge.sh's own header, verbatim:

Paths routed to merge=os-regen in .gitattributes merge with exit 0 and zero conflict markers while SILENTLY DROPPING one side's changes — only a full regeneration exposes the loss.

Both sides have edited that file since the merge base (4261fbc80e67b1715d62e02417f959430ad2666d):

side commit what
origin/main 5f392f04c feat(spec): ADR-0112 error envelope gains a producer-side refusal declaration … (PR #17090, card #16335)
this branch 0fe47980f wip(spec): move export constants below the module docblock, regenerate docs, …

So the conditions for the silent drop are met, and a merge at this head can land main having quietly lost PR #17090's half of that generated file — with zero conflict markers and every gate green, which is the whole reason that script exists.

The exposure is exactly one file — measured, not estimated

branch files changed: 42     main files changed: 73
both-sides intersection: 1
  content/docs/references/api/export.mdx     ← and it is the os-regen one

Nothing else in this diff is contended. packages/spec/authorable-surface/*.json and the other four content/docs/references/** pages this branch regenerated are merge=os-regen too, but main has not touched them since the merge base, so step 2 of the script (take main's side only for os-regen paths the branch has not edited) has nothing to take there.

What the head needs before a merge

bash scripts/pm/os-regen-merge.sh, run inside this branch's worktree — steps 1–3 mechanically (merge origin/main, per-file side selection in the worktree only, commit the merge first), then the regen chain and the generated-artifact gates it prints as step 4, including its assertion that PR #17090's implementation body still exists on the merged tree by quoted-exact git grep against origin/main. ⛔ Not a rebase, ⛔ not a force-push, and ⛔ not gen:schema while the tree is still in MERGE state — the script's header records that the latter silently rolls the authorable-surface anchor back to the branch's fork point while staying authentic, so every gate passes over an undone advance.

This seat dispatches that round as soon as a dev slot frees — three are in flight against the maintainer's cap of 3. This comment exists so the blocker is on the PR rather than in a seat's memory: ⛔ not mergeable as-is, notwithstanding what the mergeability field says.

Card #16320 stays pm:dispatched and assigned until this PR merges.

Correction (2026-09-10T01:45Z) — one label, and one number that has since moved

Mislabelled sha, corrected above. The opening line called 4261fbc8 origin/main. It is the merge base, not main's tip at that time. Every load-bearing use of it in this comment was already as the merge base and is unaffected; only the opening label was wrong. Caught by the dev running the round, not by me.

⚠️ main files changed: 73 is stale. origin/main advanced while this comment stood; re-measured during the round it reads 123. The reading that mattered did not move: the both-sides intersection is still exactly one path, and it is still the merge=os-regen one.

✅ Resolved — and the silent drop was REAL, measured rather than predicted

The regen-merge round landed: merge commit fb230b840, repair commit 4041edb9b, now the PR head.

⭐ The loss this comment predicted was confirmed empirically, not assumed away: the merge commit's export.mdx blob came out byte-identical to the pre-merge branch tip (d53a4ab90) and carried 0 of PR #17090's refusal rows — exit 0, no conflict markers, one side gone. Step 2 restored main's side and the regeneration re-derived this branch's cron tombstones on top; the repair commit lands the 6 recovered rows and nothing else, so the page now carries both intents. The survival assertion ran on 5 quoted-exact symbols with a control leg proving it can detect the drop (59 pre-repair vs 65 on main and post-repair).

⛔ Still not mergeable at this instant, for the ordinary reason: CI on 4041edb9b is mid-flight (read 01:43Z, newest run per check name: 12 success, 2 skipped, 17 in progress, 0 red). This comment's blocker is discharged; the head still owes a green run.


Generated by Claude Code

The os-regen merge driver resolved content/docs/references/api/export.mdx
with exit 0 and no conflict markers while silently dropping origin/main's
side: the merge commit's blob was byte-identical to this branch's tip and
carried 0 of PR #17090's `refusal` envelope rows.

scripts/pm/os-regen-merge.sh step 2 restored main's side in the worktree,
and `gen:schema && gen:docs` re-derived this branch's cron-retirement
tombstones on top. The merged page now carries both intents.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
…tombstone, no D2, no D3

Executes the maintainer ruling of 2026-09-10 on this PR: 直接删. The previous
round retired the seven positions the tombstone way — `retiredKey()` at every
site, seven `RETIRED_KEYS_BY_MAJOR[18]` entries, one D2 conversion for the
connector family and five D3 semantic entries. The maintainer ruled that whole
kit out on the reading that customers do not upgrade major by major in order,
after the seat had recommended the opposite in writing and stated the cost.

So the keys simply stop existing:

- 7 `migrations/entries/retired-keys/18.*.ts` deleted
- 5 `migrations/entries/semantic/18.*-retired.ts` deleted
- `conversions/registry.ts`: `connector-sync-schedule-removed` (D2) deleted,
  and its id dropped from step 18's `conversionIds`
- `migrations/registry.ts` regenerated — the generated regions collapse
- the six schema files keep only the deletion half; no `retiredKey()` survives.
  Each site keeps a source comment recording what left, why nothing read it,
  and that `Job.schedule.expression` is the one cron slot the platform evaluates
- `authorable-surface/`: the four rows that existed are deleted rather than
  marked `[RETIRED]`; the build's own #4650 proof 2 carries each of them (the
  def is not reachable from the 26 metadata-type roots)
- the pin test is rewritten around what a bare deletion actually produces: the
  authored value is accepted and STRIPPED, and no ADR-0087 surface names any of
  the seven — with a lit and a dark control on every absence reading
- the changeset is rewritten for the smaller change

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/l and removed size/xl labels Sep 10, 2026
`CACHE_WELL_FORMED` and `DR_PLAN_WELL_FORMED` are the ATTRIBUTION CONTROLS of
every pin in this file — each site asserts that the same document without the
deleted key still parses, so a fixture that does not parse turns the control
into a second failure mode and the pin proves nothing. Retyping them from
memory rather than carrying them across produced exactly that: `DistributedCacheConfig`
needs `tiers` and `invalidation`, and `DisasterRecoveryPlan` takes no `name`.
Both are restored to the bytes they carried before, and the suite is green.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>
@os-bill os-bill changed the title feat(spec)!: retire the seven cron-typed positions nothing evaluated — export schedules, ScheduleState.cronExpression, DataSyncConfig.schedule, CacheWarmup.schedule, backup / DR-test schedules (#16320, ADR-0049) refactor(spec)!: delete the seven cron-typed positions outright — no tombstone, no D2, no D3 (#16320, ADR-0049, the 直接删 ruling) Sep 10, 2026

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

一条只有合并人需要知道的门禁事实(与裁决无关,不复议)

这张 PR 之所以能让 check:authorable-surface删除门放行四行 authorable-surface/ 基线,靠的是 #4650证明 2:该 def 从 26 个 metadata-type 根不可达 ⇒ 没有作者可以为之立墓碑 ⇒ 基线行可以随键一起删。

gen:schema 对四行都打印了这条证明,其中包括:

- integration/DataSyncConfig:schedule — def not reachable from the 26 metadata-type roots
  (BUILTIN_METADATA_TYPE_SCHEMAS + EXTRA_METADATA_TYPE_SCHEMAS overlay; BFS over this
  build's in-memory Zod graph): an over-collected entry, never parsed against a
  metadata document.

这一条实测为假。 同一批模块上的探针(OS_EAGER_SCHEMAS=1,与 gen:schema 同条件)显示 getMetadataTypeSchema('connector') 就是 DeclarativeConnectorEntrySchema,它的 syncConfig 属性剥掉一层 optional 之后就是 DataSyncConfigSchema 本身(同一实例)。本 PR 的 pin 测试也从三个门(ConnectorSchemaDeclarativeConnectorEntrySchemaObjectStackSchemaconnectors[])观察到该 def 真的在解析 metadata 文档。

已按发现单独立卡:#17356(未指派,不带 pm: 标签)。

对这张 PR 的意义,一句话:删除本身是维护者裁决,证明 2 的真假不改变裁决;它改变的只是「这张 PR 的绿灯里有多少是门禁真的看过的」。 这四行删除若按证明 2 的字面失效来判,该走的路是「墓碑」,而墓碑正是裁决删掉的东西 —— 所以这里没有第三条路可选,只有一件事该做:合并人知情。

⛔ 本卡不在本 PR 内修,本 PR 不因它改变任何一行。


Generated by Claude Code

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Seat disposition on the 直接删 rework — head 1504dd1d7

domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T08:0xZ. ⛔ Governed: no ready-flip, no enqueue, no auto-merge, no approving review. ⛔ Not yet ready for the hand merge — see the review below.

The ruling 「直接删」 has been executed literally: 42 files → 29, seven positions deleted outright, no retiredKey() tombstone, no RETIRED_KEYS_BY_MAJOR[18] row, no D2, no D3, nothing left in the protocol-18 chain.

⛔ First: the seat's own error, corrected

The cost sentence the seat wrote into the ruling record — and required be placed in the ## 维护者速读 in front of the merge signer — is FALSE for the route that was actually ruled. The round that executed the ruling falsified it, and the seat re-verified it from source before writing this.

The seat's sentence was "a stack manifest that loads today will fail to load on that key after upgrade". Measured: DataSyncConfigSchema is lazySchema(() => z.object({...})) — a bare z.object, ⛔ not .strict(). Lit control: three strict declarations in the same file, so the probe can see strictness. Dark control: a fabricated symbol reads 0. ⇒ An unknown key on a non-strict object is stripped, not refused; and under 直接删 the tombstone — the thing that would have refused loudly — is gone too. The manifest loads, and syncConfig.schedule is silently dropped.

Where the error came from, precisely: the seat computed the cost of its own recommendation (keep the tombstone, drop the D2) and labelled it the cost of the ruling (drop both). The premise "the key becomes unknown ⇒ the load is refused" was asserted without measuring whether the schema is strict. The correction is appended in place to the ruling record here (5614475165) and on card #16320 (5614475299); the round's ## 维护者速读 already carries the measured version, and ⛔ that is the one to trust.

⚠️ The corrected cost is quieter, not louder: "loads fine, the thing you configured is gone, nobody tells you" is the ADR-0104 silent-strip shape. Offsetting: nothing ever evaluated these crons, so there is no runtime behaviour change; tsc still speaks to a TypeScript author annotating with the affected types.

needs:contract-review has been re-hung, and a fresh at-tier review is commissioned

The prior at-tier PASS (card #16320 comment 5609546402, 2026-09-09) reviewed the tombstone diff, which no longer exists. ⇒ It does not carry.

⭐ And the round measured why it cannot carry: the rework is not a pure subtraction from the reviewed surface — it converts two refusals into acceptances.

  • automation/ScheduleState:cronExpression was REQUIRED. With the key gone, a document lacking it goes from refused to accepted.
  • A wrong-dialect expression envelope at connectors[].syncConfig.schedule goes from an invalid_union refusal to silent acceptance.

Clause-②: yes stands on the reworked diff, and more strongly than on the tombstone diff, in the widening direction. That is the definition of the clause. The label is re-hung (read back: present, PR still draft), and a CONTRACT_REVIEW_TIER review of this head is running.

⭐ The round was right to write no label itself: reverting another actor's post-PASS act is not a dev's call. It flagged it and recommended exactly this. ⛔ Its option B — "the PASS carries because the rework only deletes" — is wrong on the facts, and the round said so itself.

The merge proof is the strongest artefact in the round

os-regen-merge.sh exit 0, merge commit 3a881f3b6c. The risk list was derived from .gitattributes at run time as the order required, ⛔ not from the previous incident: 18 merge=os-regen patterns; branch touched 8 routed paths, main touched 14, both touched 0 ⇒ the custom driver was never invoked and a silent drop is structurally impossible, not merely absent. All 22 per-path preservation diffs read 0 files changed, with a lit control (authorable-surface/system.json reads 2+/2− over base..branch, so the reader can see changes) and a dark control (a fabricated path reads 0 changed lines and 0 entries in the tree). $GIT_DIR/os-regen-pending absent, consistent with the driver never running.

⭐ That is the shape this branch needed: earlier in its life it lost a side silently, and this round proves the mechanism could not fire rather than asserting it did not.

Second falsification, filed not folded

#17356computeSurfaceReachability (packages/spec/scripts/build-schemas.ts) answers "not reachable from the 26 metadata-type roots" for integration/DataSyncConfig, which is two hops from the connector root (probe: getMetadataTypeSchema('connector') === DeclarativeConnectorEntrySchema, its shape has syncConfig, unwrapping one optional gives DataSyncConfigSchema). ⇒ #4650 proof 2 waived a tombstone for a key a stack manifest really authors — the exact false-unreachable that function's own docblock names as its silent-waiver hazard.

⚠️ Part of this PR's green rests on that waiver, and the round flagged it here (5615614427) so the merge signer sees it. ⛔ That flag stands and is not superseded by this comment.

Open question 2 — the seat's answer: A for this PR, and B is now filed as #17357

The round found check:adr-0087-registration and AGENTS.md's FROM→TO requirement jointly unsatisfiable on this route: with a denial heading (## No FROM → TO mapping, and why this section is not one) the gate refuses at exit 1 with evidence line from-to-label; without the token it passes at exit 0.

A — accept as shipped. The changeset takes the passing form and states its reasoning in the open rather than reading as gate-dodging, and the reasoning is substantively true: a bare deletion on a non-strict schema refuses nothing and converts nothing, so no metadata upgrader has an edit. ⚠️ The commissioned reviewer is asked to judge that substance independently.

B is filed as #17357 — the round declined to file it for lack of a nameable carrier, which was the right call from where it stood. The seat can name one: the script itself, and the next author of a declared-breaking changeset with genuinely nothing to map — an author who now exists as a precedent, here. Structural verification by the seat: FROM_TO_LABEL_RE at :1132, the from-to-label branch at :1401, and the script's own self-test at :5337 asserting that a bare heading matches. ⇒ Heading-matching is by design and the detector has no polarity; #6967 already narrowed this exact false-positive class for inline text and stopped there. ⛔ #17357 does not ask to weaken the gate.

What is left before this can be merged by hand

  1. The commissioned at-tier review of head 1504dd1d7 returns.
  2. CI green, read as newest run per check NAME and the combined commit status — they are different populations.
  3. The merge signer reads the round's ## 维护者速读 (the measured cost, ⛔ not this comment's original version) and the [finding] the authorable-key deletion gate's reachability BFS reports integration/DataSyncConfig unreachable from the metadata-type roots — it is two hops from the connector root, so #4650 proof 2 waives a tombstone for a key a stack manifest really authors #17356 flag.

⛔ Then it is the maintainer's hand merge, as it has been throughout.


Generated by Claude Code

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

Contract review of head 1504dd1d7PASS WITH FINDINGS, one must-fix, and a must-fix round is dispatched

domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T09:0xZ. ⛔ Governed: no ready-flip, no enqueue, no auto-merge, no approving review. ⛔ Do not merge this head — the must-fix below is in the very sentence the merge signs off on.

⚠️ Provenance, stated so it is not overread: this review was run by a CONTRACT_REVIEW_TIER subagent dispatched by this seat. A subagent inherits the dispatching session's id, so it is seat-side at-tier, ⛔ not cross-seat independent. It reviewed the 29-file diff at 1504dd1d77 against merge-base cca1dc0bfb, and every claim below carries the probe that produced it.

⛔ MUST-FIX (M1) — the cost statement is factually wrong, for the third time on this PR

Four sites say the strip is silent. Measured, it is not:

  • os validate on a stack carrying connectors[].syncConfig.schedule: exit 0 with
    ⚠ connectors.sap_erp.syncConfig.schedule: 'schedule' is not a declared connector key, so its value is dropped at load.
    Dark control (same stack minus the key): 0 such lines, 4 warnings vs 5. --json carries the same string.
  • os validate --strict exits 1 on any warning ⇒ CI running --strict on an otherwise-clean stack REFUSES the upgraded manifest.
  • os build prints the same list under "Undeclared authoring keys — dropped at load (排查「手抄 spec 清单 + "keep in sync" 注释」模式:一天内确认三例,全部曾静默漂移 #3786)".
  • Mechanism: lintUnknownAuthoringKeys walks every strip-mode collection; connectors: 'connector' is in the map, descent depth 12. In-process the walker returns {path:'connectors.c.syncConfig.schedule', surface:'connector', key:'schedule'}; dark control 0.

The truth is MORE favourable to authors than what the maintainer was told — a named warning and a --strict refusal, not silence. But the sentence the maintainer is asked to confirm is the wrong one, and on a governed PR the merge is the sign-off. Hence must-fix.

⚠️ This is the third reading of one fact, each closer than the last, and the first two were wrong in opposite directions:

claim verdict
the seat's ruling record "a manifest that loads today fails to load" false — computed for the tombstone route, not the ruled one
the round's ## 维护者速读 "silently dropped, no error, no warning, no greppable trace" true at the parse layer, false end-to-end — the CLI speaks
this review parse strips silently; os validate/os build name the key; --strict refuses; os migrate meta lists nothing the measured one

⭐ Worth recording as a shape: two independent, careful readings both under-measured the same fact, in opposite directions, because each stopped at the first layer that answered. The seat stopped at the schema; the round stopped at the parse.

Also to fix — the clause-② characterisation is incomplete: seven widenings, not two

The reviewer enumerated all seven, and three are required-key removals rather than one — the export pair's requiredness left with the key, and api/ScheduledExport:schedule.cronExpression is already pinned in this PR's own test at :243-244 while going unnamed in the body and changeset. Clause-②: yes stands and is if anything stronger.

Verified, and not to be re-opened

  • Claim 1 — non-strict, stripped: PARTIALLY true. All five schema files: 0 hits for .strict(|strictObject|.passthrough(|.catchall(. Probe at head: DataSyncConfigSchema.safeParse({schedule: X}) accepts and strips for a cron string, a {dialect:'cel'} envelope, and the number 42. Other doors checked and ruled out: DeclarativeConnectorEntrySchema's superRefine never touches syncConfig; ObjectStackDefinitionSchema is strictObject at the top level only; /meta/connector binds the same entry schema; saveMetaItem adds no schema door. The CLI lint is the door that speaks — M1.
  • Claim 3 — no reader: TRUE. Sweep excluding tests/fixtures/CHANGELOGs: syncConfig 0, CacheWarmup 0, BackupConfig 0, DisasterRecoveryPlan 0, the export pair 0, ScheduleState 0. cronExpression reads 4 — all the report-schedule API, a different position not among the seven. Lit control (the croner path in service-job/trigger-schedule) = 6 lines; dark control = 0. ⭐ And the reviewer discarded its own first sweep because that one's lit control read 0 — an invalid pathspec. Exactly right.
  • Claim 4 — the merge preserved both sides: TRUE, independently reproduced. 19 merge=os-regen lines = 18 real patterns + 1 comment (the round's 18 is correct). Branch touched 8 routed, main 14, both 0. Raw both-touched set is 2 files (migrations/registry.ts, shared/expression.zod.ts), neither routed. And the decisive one: git merge-tree --write-tree of the two parents → exit 0, no conflicts, and git diff <tree> 3a881f3b6c = 0 files. ⭐ The mechanical 3-way reproduces the commit exactly, so nothing was dropped on either side. On a branch that has already lost a side silently once, that is the reading that closes it.
  • Claim 5 — ADR-0087: gate satisfied (re-run: 338 self-test assertions, exit 0), substance correct. ADR-0087 :596 refuses no-migration-prescription only when the body carries a migration prescription; the "What to write instead" block is forward guidance, not a FROM→TO rewrite. ⚠️ But its premise sentence "refuses nothing" is false per M1 (--strict refuses) — reword with the rest. The conclusion holds only because the ruling forbids registering an entry, and the reviewer notes the house rule this ruling overrides is explicit in the retirement skill (「永不从非 strict schema 上裸删一个键」) — recorded, ⛔ not relitigated.
  • Claim 6 — [finding] the authorable-key deletion gate's reachability BFS reports integration/DataSyncConfig unreachable from the metadata-type roots — it is two hops from the connector root, so #4650 proof 2 waives a tombstone for a key a stack manifest really authors #17356: TRUE, and the reviewer named the root cause the card left open. Under OS_EAGER_SCHEMAS=1, BFS from getMetadataTypeSchema('connector') does reach DataSyncConfigSchema (438-node closure). From "all roots" it does not — because listMetadataTypeSchemaTypes() returns 26 types and deliberately excludes UNREGISTERED_KIND_SCHEMAS (connector / webhook / sharing_rule, per meta: bind Zod schemas for webhook / connector / sharing_rule WITHOUT registering the kinds — close the unvalidated PUT /meta write hole (#2657 audit, option A) #6245). The gate's docblock claims it covers "the schemas a metadata document is actually parsed against", but /meta/connector and stack.connectors[] parse against an unenumerated root. ⇒ root enumeration, not an edge or def-key-map bug. Being added to [finding] the authorable-key deletion gate's reachability BFS reports integration/DataSyncConfig unreachable from the metadata-type roots — it is two hops from the connector root, so #4650 proof 2 waives a tombstone for a key a stack manifest really authors #17356. Impact here: the proof-2 waiver is a false waiver, but the outcome (no tombstone) is what was ruled anyway, so no assertion in this PR is undermined.

Advisory

A2contracts/export-service.ts drops the required cronExpression: string member from ScheduleExportInput, a published runtime TS interface on IExportService. The changeset's "no def leaves the public surface" is true but this member's departure is unmentioned. No consumer outside packages/spec. Being named.
A3 — the regenerated content/docs/references/integration/connector.mdx carries generator drift unrelated to this PR (strategy?:strategy: on defaulted keys); main's copy was stale relative to its generator and check:docs is green at head. ⛔ Left alone; noted so nobody reads it as a schema change.

What both the seat and the round missed

The CLI unknown-key lint · three required-key removals rather than one · the #17356 root cause · the ScheduleExportInput member.

Next

A must-fix round is dispatched: reword the four M1 sites, name all seven widenings, name A2, note A3 in one line, and add the root cause to #17356. ⛔ No code change — the deletions, the merge and the disposition all stand. needs:contract-review stays on until the seat clears it against the corrected head.


Generated by Claude Code

…es, and re-count clause ② as seven

A CONTRACT_REVIEW_TIER review measured this branch's head and found the cost
statement the maintainer signs off on by merging to be factually wrong. Four
sites said the strip is silent. It is not: `lintUnknownAuthoringKeys` (#3786)
walks every stack collection whose entry schema is strip-mode, and `connectors`
is one, so for `connectors[].syncConfig.schedule` — the one deleted position a
stack manifest reaches — `os validate` and `os build` NAME the dropped key and
`os validate --strict` EXITS 1 on it. Measured at head on an otherwise-clean
stack: without the key 0 warnings / exit 0, with it 1 warning / exit 1.

The truth is more favourable to authors than what the maintainer was told, but
it is not the sentence they were asked to confirm. Corrected at all four sites:
the changeset, `migrations/registry.ts`'s step-18 rationale, two comments in
`cron-typed-positions-retirement.test.ts`, and the PR body's 维护者速读. The
accurate statement is per channel: the PARSE strips silently, `os validate` /
`os build` name the key, `--strict` refuses, `os migrate meta` still lists
nothing.

Same round, two more corrections and no behaviour change:

  * Clause ② is SEVEN widenings, not two, and THREE of them are required-key
    removals (`ScheduleState.cronExpression`,
    `ScheduledExport.schedule.cronExpression`,
    `ScheduleExportRequest.schedule.cronExpression`). Measured on both legs:
    the five schema files restored to the merge-base on disk, one probe run,
    restored to HEAD and re-run, with a byte-identity check after the restore.
    14 cases, 14 refusals on the base leg, 14 accept-and-strip on the head leg.
  * The changeset now names the departure of the required `cronExpression:
    string` member from `ScheduleExportInput` — a published TS interface on
    `IExportService`. "No def leaves the public surface" is true and does not
    cover a member. Sweep: zero consumers outside `packages/spec`.

⛔ No behaviour changed: the seven deletions, the merge and the ADR-0087
disposition are untouched, and the 直接删 ruling is not re-litigated.

Claude-Session: https://claude.ai/code/session_01MkQhmuuJAVDjmeWNixwDDH
Co-authored-by: Claude <noreply@anthropic.com>

os-bill commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT — must-fix round, head 2095e4e91. needs:contract-review CLEARED.

domain:spec execution seat, session_01MkQhmuuJAVDjmeWNixwDDH, 2026-09-10T09:34Z. ⛔ Governed: no ready-flip, no enqueue, no auto-merge, no approving review. The hand merge is available again, and it remains yours.

The must-fix is discharged, and the round did not take the review on trust

M1 was corrected at five sites, not the four the order named — the round found a second place in the pin test's module docblock. And ⭐ it re-measured the reviewer's claim before editing, in every leg:

  • os validate: exit 0, prints the named key; lit 4 warnings vs dark control 3, the delta exactly that line; --json carries the identical string.
  • os validate --strict isolated: dark control (no key) 0 warnings, exit 0; lit (with key) 1 warning, exit 1. ⭐ Isolating it is what makes "a --strict CI refuses the upgraded manifest" a reading rather than an inference.
  • os build: same line under Undeclared authoring keys (1) — dropped at load (#3786); dark control prints no such block.
  • os migrate meta --from 17 and --from 16: exit 0, 0 occurrences of cronExpression, no prescription for any of the seven.

⇒ The 维护者速读 now states the cost per channel and says explicitly that the correction is more favourable to authors than the previous text. That is the sentence the merge signs off on, and it is now the measured one.

Seven widenings — verified on a real base/head leg, ⛔ not transcribed

The order handed a table and said not to trust it. The round didn't:

Five schema files restored on disk from merge-base cca1dc0bfb1e, probe run (base leg), restored to HEAD, probe re-run (head leg). Mutation proved on disk first — CronExpressionInputSchema occurrence counts 0/0/0/0/0 → 3/2/2/2/3 and a non-empty git diff HEAD; restore proved after — empty git diff HEAD and all five git hash-object values equal to their HEAD:PATH blob. trap ... EXIT INT TERM with absolute paths.

Result: 14 cases. Base leg 14/14 refused, every one invalid_union at the expected path. Head leg 14/14 accepted with the key absent from parsed data. Five positive controls accepted in BOTH legs. All seven rows confirmed, three of them required-key removals; ⛔ zero deviations from the review.

⭐ That is the strongest form available for a widening claim: not "the schema no longer has the key" but "this exact document was refused before and is accepted now", with the positive controls proving the probe was not simply accepting everything.

The round corrected the reviewer, and the reviewer corrected the round's predecessor

⚠️ UNREGISTERED_KIND_SCHEMAS holds FOUR entriesanalytics_cube, connector, sharing_rule, webhook — not the three the review named (#10194 added theme and analytics_cube; #10485 retired theme). ⇒ four unenumerated roots, so #17356's blast radius is larger than either reading had it. Posted to #17356 (5616358457) with the all-roots closure (5420 nodes, does not contain DataSyncConfigSchema, while positive control ObjectSchema is in it) against the connector-root closure (438 nodes, does contain it).

⭐ And a harness trap worth keeping: a static ESM import of these CJS-loaded spec modules from an .mts probe under tsx yields a SECOND module instance and fakes the bug's own signature (root===entry false, reachability false). The round's first probe hit exactly that, discarded its numbers, and re-ran all-dynamic with an identity guard. ⛔ Anyone re-measuring #17356 must do the same or they will "confirm" the bug from an artifact.

Open question 1 — the footer: A, as shipped. ⛔ Not a deviation to correct.

The order required exactly one session-URL footer. Measured two ways, that is unsatisfiable on this channel: raw REST PATCH /pulls/17146 appends its own bare footer unconditionally — once with the canonical rule+footer block, once with the rule line removed per platform-readings.md's 「PR 正文页脚不带前置横线」. Both stored two. Only a body sent with no footer stores exactly one.

⇒ ⭐ The round hit a conflict between a hard requirement (exactly one) and a form requirement (the session-URL spelling), measured it rather than picking, and kept the hard one — putting the session id in body prose, which is where AGENTS.md says durable attribution lives. Read back: footer count 1, session id present and greppable. Correct call; the order was wrong to state both as if compatible.

The reading is being added to #15275, which already owns the footer channel × action grid.

Open question 2 — the split carrier: A, as done. ⛔ And the boundary, restated.

--pair 17146 exited 4 on a split dual carrier: needs:contract-review was on the PR but not on card #16320. The round hung the card half additively (POST .../labels, read back, nothing stripped), touched neither the PR's label nor any clearing. --pair now exits 0 — verified independently by the seat at 09:34Z.

⭐ Accepted, and the reasoning is right: hanging is fail-closed, and a card missing the gate is enqueueable straight past a gate demonstrably live on its PR. ⚠️ The direction matters and is the whole distinction: clearing a gate carrier is the seat's act and would have been refused; hanging one is fail-closed and repairs a split. ⛔ Do not read this as a general licence — a dev that finds a split should still prefer reporting it.

Clearing the gate

The review is discharged: the one must-fix is corrected and re-measured, the clause-② characterisation is complete and independently verified, A2 is named with its sweep (0 consumers outside packages/spec in objectstack and objectui, with positive controls resolving 5+ and 1326 files respectively; ⚠️ the cloud half is NOT MEASURED — that repo is not checked out — and the PR body says so), and A3 is noted in one line without touching the file.

needs:contract-review is cleared by the seat, against this head, on the at-tier verdict plus this round's corrections. ⚠️ It was seat-side at-tier, ⛔ not cross-seat independent — the subagent inherits this session's id. ⛔ The clearing is not an approval and is not a merge.

What remains before the hand merge

CI convergence on 2095e4e91 (at the round's read: 31 names, 10 success / 4 skipped / 17 in progress / 0 failure; combined commit status success). ⛔ That is the seat's to read, and it will be read again — the round did not wait, correctly.

⚠️ One NOT MEASURED worth the signer's eye: dispatch-gates.mjs --commands printed 114 commands but self-reported a stale tree (≥4 commits behind, 14 derivation files changed), so its "only more" guarantee does not hold. The increment is CI's on this PR.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:system size/l tests tooling

Projects

None yet

3 participants