Skip to content

fix(spec): manifest 删行相对 merge-base 自证 —— 整 schema 消失不再只是纪律 (#4725) - #5971

Merged
baozhoutao merged 1 commit into
mainfrom
claude/issue-4725-manifest-removal-gate
Aug 6, 2026
Merged

fix(spec): manifest 删行相对 merge-base 自证 —— 整 schema 消失不再只是纪律 (#4725)#5971
baozhoutao merged 1 commit into
mainfrom
claude/issue-4725-manifest-removal-gate

Conversation

@baozhoutao

Copy link
Copy Markdown
Contributor

Fixes#4725

前提核验:成立,并且实测过

build-schemas.ts 自本单立单起被重写了四次(#5807 / #5851 / #5902 + 中间轮),所以先对着当前 origin/main 逐条核验:

  • manifest 在 L424 从 MANIFEST_PATH 读盘 —— 本 commit 可随手改的那份文件;
  • missing = manifest.schemas − generatedKeys(L447),没有任何一处从 git 取过 manifest;
  • 检查 (c) 的 goneDefs 出口原话:whole-schema removals are adjudicated by json-schema.manifest.json (#2978) and check:api-surface;
  • build-api-surface.ts--check 是对已提交快照的比对,失败提示是 If intentional, run gen:api-surface and commit the updated snapshots —— 纯新鲜度门。

没有任何 #4650 时代的删除证明覆盖到这一层。

实测复现(修复前,沙箱里跑真源码,非模型化): 删掉 src/data/index.ts一行export * from './validation.zod';ObjectSchema 是直接 from './validation.zod' 导入 ValidationRuleSchema 的,所以这 7 个 def 依旧从 object 元数据根可达、依旧解析真实元数据、它们的 116 个 key 依旧被作者书写 —— 消失的只是 JSON Schema、manifest 行和 ratchet 对它们的记录。再按 #2978 注释所允许的 "deliberate retirement" 手删 7 行 manifest、手删 116 行 baseline(不删的话检查 (a) 先红):

STEP 1 写模式 gen:schema exit 0
STEP 2 CI 形状 --check exit 0
authorable keys silently dropped: 116

检查 (c) 打印的正是那句 def no longer emitted by this build; whole-schema removals are adjudicated by json-schema.manifest.json (#2978) —— 交给了一个什么也不说的裁判。

改了什么

1. manifest deletion gate(scripts/build-schemas.ts)

#4650 的同一结构重新锚定:相对 merge-base(HEAD, origin/main) 的 manifest —— 本 commit 改不动的那一份 —— 计算「离开已发布集合」的 def(baseSchemas 里、本次构建不产出、且不在 RENAMED_DEFS 里),每一个都必须有登记,否则红。

比较的右侧刻意是 generatedKeys(本次真正产出的集合)而不是树里的 manifest 文件:文件正是 PR 能改的那一半,改它就是本单的旁路。

它跑在检查 (c) 之前 —— 这一步是让 (c) 那句「交给 manifest 裁判」从循环变成真裁决的关键;(c) 的措辞也同步更新,不再声称 check:api-surface 参与裁判(它不参与)。

2. RETIRED_DEFS_BY_MAJOR(新导出,src/migrations/registry.ts)

RETIRED_KEYS_BY_MAJOR 的上一层同胞,值是确切的 ${category}/${SchemaName}('identity/Session'),写法就是 manifest 里的写法。沿用 #4659精确集合判定:不取叶名、不按前缀、不从相邻条目辐射。失败时直接打印要粘贴的那几行和该进哪个 major。

配套镜像检查(#5902 的 (b2) 同形):表里登记了一个本次构建仍在发布的 def —— 一次没有任何东西消费的登记 —— 直接失败。没有它,真正的删除可以在几个月后别人的 PR 里落地,而门禁早已被满足、当时没有任何人写下任何东西。镜像不需要 git,任何环境都跑。

3. manifest 自身的 description

它此前写的 remove a key ONLY for a deliberate retirement 正是 issue 引用的那句「唯一的要求、且没有任何机器校验」。现在它指向这道门和这张表,并纳入陈旧判定(descriptionStale),所以不会再和代码漂移 —— 打开这个文件准备删行的人读到的,就是当前的程序。

关键判断:为什么是「声明」而不是「可达性」

派单给了两条路,并要求给证据。issue 建议的 (a) 复用可达性在本层用不了,而且是静默地用不了:

reachableVia(defKey) 的第一行是 const schema = zodByDefKey.get(defKey); if (!schema) return null;,而 zodByDefKey 只在产出循环里 .set() —— 只装本次构建产出的 def。一个刚刚不再被产出的 def 必然答 null,而 null#4650 的语义里就是「不可达 ⇒ 放行」。也就是说:直接复用可达性,会对这道门要拦的每一类删除开绿灯,而且是安静地开。

加宽 BFS 救不了这一点:def 已经从源码里没了,没有图可以走。要在本层拿到「删除前是否可达」,只能引入一个新的已提交投影(把可达性写进 manifest,或另立一个 root-set 快照)—— 那是更大的形状变更,且每次 root 图变动都会产生噪声 diff。所以本层唯一诚实的证据是声明,沿用 #5902 为 key 层定下的先例。

派单要求「measure current main's history for real whole-def removals」:近四天 main 上整-def 删除约 15 次(#4988/#5321 一次删 32 行、#4936/#5065 删 13 行、#4739/#4752 删 6 行、#4834/#4878 删 5 行……),其中既有可达形状也有仅声明形状。也就是说纯 reachability 这条路不仅静默失效,业务上也不成立:这类删除是常态,必须有一条能走的路,而这条路必须留下记录。每次一行、门禁把那行原样打给你,是这个记录最小的形态。

为什么这不是「把手改从一个文件搬到另一个文件」: 真正的牙齿是 merge-base 锚定 —— 今天这件事根本没有被检测。检测之后,它从「一个 1613 行生成文件里少了一行」(review 里不可见)变成「migrations 登记表里一条具名退休 + 一个 major」(review 里就是它本身),再加上镜像检查堵掉预登记。这与 #5902 为 key 层做的交易完全同形。

离线语义:沿用 #5235,不新造第三种

authorable-surface.base.json未出现在本 PR —— 逐文件核过 git status

测试

新增第三个沙箱(build-schemas-check-mode.test.ts),原因是结构性的:门读的是merge-base 的 manifest,所以 json-schema.manifest.json 必须是一个被 git 跟踪、且提交内容与工作区不同的文件 —— 第一个沙箱只提交 surface,在那里跟踪 manifest 会让每次 seedManifest() 弄脏 #5358 断言为干净的树;而登记表要逐用例不同,需要复制而非 symlink src/。生产代码路径逐字节不变,只有 fixture 数据。

9 条新用例:

用例断言
负控:没有东西离开已发布集合静默 + exit 0
旁路复现exit 1,点名两个 def,打印可粘贴的表行与 major,并回报 merge base
写模式同样拒绝exit 1,manifest 字节不变
已登记的删除放行打印「哪个 major 登记的」,exit 0
登记按 DEF 逐个判定登记了姊妹的那个,另一个照红
镜像:登记了仍在发布的 defexit 1
改名不是删除RENAMED_DEFS 源 def 从 base manifest 消失 → 绿
离线比较打印说明并跳过、绿;镜像照红
端到端(真源码)删掉 barrel 行 + 手删 manifest/baseline → exit 1,7 个 def 全部点名;并断言输出里没有旧判决 carry their own proof / baseline deletion(s) since

反向验证(方向先判后跑,方向如预测:修复前绿 → 修复后红)。 同一套 harness、同一个 fixture,只换 scripts/ 的版本:

修复前: STEP 1 exit 0 / STEP 2 exit 0,116 个 key 静默丢失
修复后: STEP 1 exit 1 / STEP 2 exit 1
❌ 7 schema(s) left the published set with no registered removal (#4725)
- json-schema/data/ConditionalValidation.json
… (7 个全部点名)
1. Declare each removal by its EXACT def key in RETIRED_DEFS_BY_MAJOR
(packages/spec/src/migrations/registry.ts) — copy these lines in:
'data/ConditionalValidation',
… under `17: [ … ]`

本地全量:

packages/spec test 323 files / 8276 tests passed (沙箱 45 = 原 36 + 新 9)
packages/spec typecheck tsc --noEmit + check:test-typecheck OK
check:generated ✓ All 10 generated artifacts are up to date
node scripts/check-nul-bytes.mjs OK (5726 files)

api-surface.json 因新增导出整体重生成,diff 是单行 + "RETIRED_DEFS_BY_MAJOR (const)"

#4723 的定价(必答项)

变简单一点点,方向不变,不会变难,也没有被本单取消。

#4723 想把 gen:schemacheck:docs 里摘出来,理由是「一个 check 在树脏的时候写生成物」。本单把 gen:schema 的写面往回收了一格:json-schema.manifest.json 的 description 纳入陈旧判定后,写模式的三个写点(manifest / surface / json-schema 目录)各自的触发条件都变得可判定,而新增的这道门在两种模式下行为完全一致、且永不写盘 —— 它是纯验证。所以「把 gen:schema 从 check: 里摘掉,调用方显式跑」这条路径不会因为本单多出新的写副作用要处理。

一处需要 #4723 的实现者知道:本门读 merge-base 需要 origin/main 可解析,和 check:authorable-surface 现有的解析路径共用同一次 git 解析(gitResolvedAnchor),没有第二次。若 #4723gen:schema 拆成独立步骤,这道门必须跟着 --check 一起走,而不是留在 gen: 一侧 —— 否则 CI 的验证面会少掉它。


🤖 Generated with Claude Code

https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW


Generated by Claude Code

`json-schema.manifest.json` 的 #2978 ratchet 对删行的全部要求是一句注释
("remove a key ONLY for a deliberate retirement"),没有任何机器校验。它的
`missing` 集合是「manifest − 本次产出」,而 manifest 是同一个 commit 里可以随手
改的文件:把导出、manifest 行、基线行一起删掉,`missing` 恒空;检查 (c) 又以
「def 已不再产出」为由放行它名下的每一行(那一条出口原话就写着交给 manifest 裁
判);`check:api-surface` 只是新鲜度门。三个门,一句话都不说。
实测(修复前):删掉 src/data/index.ts 一行 `export * from './validation.zod';`
—— ObjectSchema 直接从该模块导入 ValidationRuleSchema,所以 7 个 def 依旧从
`object` 元数据根可达 —— gen:schema 与 check:authorable-surface 双双 exit 0,
116 个 authorable key 静默离开契约。
- 新增 manifest deletion gate:相对 merge-base(HEAD, origin/main) 的 manifest
计算「离开已发布集合」的 def,跑在检查 (c) 之前。
- 新增导出 RETIRED_DEFS_BY_MAJOR(per-DEF,精确集合判定,#4659 先例)+ 镜像检查
(登记了一个仍在发布的 def 直接失败)。RENAMED_DEFS 前置豁免。
- 不复用 #4650 的可达性:reachableVia() 从 zodByDefKey 取实例,该表只装本次构建
产出的 def,刚被删的 def 必然答 null(「不可达 ⇒ 放行」)。
- 离线沿用 #5235 姿态:镜像照跑,比较打印说明并跳过。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01559M8FVm6W6vDLABL3jvdW
@vercel

vercelBot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 6, 2026 1:28pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec.

111 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/ai/agents.mdx(via @objectstack/spec)
  • content/docs/ai/skills-reference.mdx(via @objectstack/spec)
  • content/docs/ai/skills.mdx(via @objectstack/spec)
  • content/docs/api/client-sdk.mdx(via @objectstack/spec)
  • content/docs/api/environment-routing.mdx(via @objectstack/spec)
  • content/docs/api/error-catalog.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-client.mdx(via @objectstack/spec)
  • content/docs/api/error-handling-server.mdx(via @objectstack/spec)
  • content/docs/api/index.mdx(via @objectstack/spec)
  • content/docs/automation/approvals.mdx(via @objectstack/spec)
  • content/docs/automation/connectors.mdx(via @objectstack/spec)
  • content/docs/automation/flows.mdx(via @objectstack/spec)
  • content/docs/automation/hook-bodies.mdx(via packages/spec)
  • content/docs/automation/hooks.mdx(via @objectstack/spec)
  • content/docs/automation/index.mdx(via @objectstack/spec)
  • content/docs/automation/webhooks.mdx(via @objectstack/spec)
  • content/docs/automation/workflows.mdx(via @objectstack/spec)
  • content/docs/concepts/architecture.mdx(via @objectstack/spec)
  • content/docs/concepts/design-principles.mdx(via packages/spec)
  • content/docs/concepts/index.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-driven.mdx(via @objectstack/spec)
  • content/docs/concepts/metadata-lifecycle.mdx(via packages/spec)
  • content/docs/concepts/north-star.mdx(via @objectstack/spec)
  • content/docs/data-modeling/analytics.mdx(via @objectstack/spec)
  • content/docs/data-modeling/drivers.mdx(via @objectstack/spec)
  • content/docs/data-modeling/external-datasources.mdx(via @objectstack/spec)
  • content/docs/data-modeling/field-types.mdx(via @objectstack/spec)
  • content/docs/data-modeling/fields.mdx(via @objectstack/spec)
  • content/docs/data-modeling/formulas.mdx(via @objectstack/spec)
  • content/docs/data-modeling/index.mdx(via @objectstack/spec)
  • content/docs/data-modeling/objects.mdx(via @objectstack/spec)
  • content/docs/data-modeling/queries.mdx(via @objectstack/spec)
  • content/docs/data-modeling/schema-design.mdx(via @objectstack/spec)
  • content/docs/data-modeling/seed-data.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation-rules.mdx(via @objectstack/spec)
  • content/docs/data-modeling/validation.mdx(via @objectstack/spec)
  • content/docs/deployment/cli.mdx(via @objectstack/spec)
  • content/docs/deployment/tenancy-modes.mdx(via @objectstack/spec)
  • content/docs/deployment/troubleshooting.mdx(via @objectstack/spec)
  • content/docs/deployment/validating-metadata.mdx(via @objectstack/spec)
  • content/docs/getting-started/build-with-claude-code.mdx(via @objectstack/spec)
  • content/docs/getting-started/common-patterns.mdx(via @objectstack/spec)
  • content/docs/getting-started/examples.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-reference.mdx(via @objectstack/spec)
  • content/docs/getting-started/quick-start.mdx(via @objectstack/spec)
  • content/docs/getting-started/your-first-project.mdx(via @objectstack/spec)
  • content/docs/kernel/cluster.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/auth-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/cache-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/data-engine.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/index.mdx(via @objectstack/spec)
  • content/docs/kernel/contracts/metadata-service.mdx(via packages/spec)
  • content/docs/kernel/contracts/storage-service.mdx(via packages/spec)
  • content/docs/kernel/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/email-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/examples.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/index.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/queue-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/sharing-service.mdx(via @objectstack/spec)
  • content/docs/kernel/runtime-services/sms-service.mdx(via packages/spec)
  • content/docs/kernel/runtime-services/storage-service.mdx(via packages/spec)
  • content/docs/kernel/services-checklist.mdx(via @objectstack/spec)
  • content/docs/kernel/services.mdx(via @objectstack/spec)
  • content/docs/permissions/authorization.mdx(via @objectstack/spec)
  • content/docs/permissions/permission-sets.mdx(via @objectstack/spec)
  • content/docs/permissions/permissions-matrix.mdx(via @objectstack/spec)
  • content/docs/permissions/positions.mdx(via @objectstack/spec)
  • content/docs/permissions/rls.mdx(via @objectstack/spec)
  • content/docs/permissions/sharing-rules.mdx(via @objectstack/spec)
  • content/docs/plugins/adding-a-metadata-type.mdx(via @objectstack/spec)
  • content/docs/plugins/development.mdx(via @objectstack/spec)
  • content/docs/plugins/index.mdx(via @objectstack/spec)
  • content/docs/plugins/packages.mdx(via @objectstack/spec)
  • content/docs/protocol/backward-compatibility.mdx(via @objectstack/spec)
  • content/docs/protocol/diagram.mdx(via packages/spec)
  • content/docs/protocol/kernel/config-resolution.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/i18n-standard.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/index.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/lifecycle.mdx(via @objectstack/spec)
  • content/docs/protocol/kernel/plugin-spec.mdx(via @objectstack/spec)
  • content/docs/protocol/knowledge.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/query-syntax.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/schema.mdx(via @objectstack/spec)
  • content/docs/protocol/objectql/security.mdx(via packages/spec)
  • content/docs/protocol/objectql/state-machine.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/actions.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/concept.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/index.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/layout-dsl.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/record-alert.mdx(via @objectstack/spec)
  • content/docs/protocol/objectui/widget-contract.mdx(via @objectstack/spec)
  • content/docs/releases/implementation-status.mdx(via @objectstack/spec)
  • content/docs/releases/index.mdx(via @objectstack/spec)
  • content/docs/releases/v12.mdx(via @objectstack/spec)
  • content/docs/releases/v13.mdx(via @objectstack/spec)
  • content/docs/releases/v16.mdx(via @objectstack/spec)
  • content/docs/releases/v17.mdx(via @objectstack/spec)
  • content/docs/releases/v9.mdx(via @objectstack/spec)
  • content/docs/ui/actions.mdx(via @objectstack/spec)
  • content/docs/ui/apps.mdx(via @objectstack/spec)
  • content/docs/ui/create-vs-edit-form.mdx(via @objectstack/spec)
  • content/docs/ui/dashboards.mdx(via @objectstack/spec)
  • content/docs/ui/field-grouping-and-order.mdx(via @objectstack/spec)
  • content/docs/ui/forms.mdx(via @objectstack/spec)
  • content/docs/ui/index.mdx(via @objectstack/spec)
  • content/docs/ui/public-data-collection.mdx(via @objectstack/spec)
  • content/docs/ui/setup-app.mdx(via @objectstack/spec)
  • content/docs/ui/translations.mdx(via @objectstack/spec)
  • content/docs/ui/views.mdx(via @objectstack/spec)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

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

Labels

documentationImprovements or additions to documentationsize/lteststooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

json-schema.manifest.json 的「deliberate removal」删行仍是纪律而非门禁 —— #4650 的同类洞,上移一层(整 schema 级)

2 participants

@baozhoutao@claude