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 逐个判定 登记了姊妹的那个,另一个照红
镜像:登记了仍在发布的 def exit 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

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 6, 2026 1:28pm

Request Review

@github-actions github-actions Bot added the size/l label Aug 6, 2026
@github-actions

github-actions Bot commented Aug 6, 2026

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 free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants