Skip to content

fix(spec): shared/http.zod.ts 的两个不同 enum 不再发布成同一个 def key —— 7 值 HttpMethod 拿回裸名,5 值子集改名 HttpMethodSubset,build-schemas 补硬报错守卫 - #5976

Merged
qq9340100 merged 3 commits into
mainfrom
claude/issue-5832-httpmethod-defkey-collision
Aug 6, 2026
Merged

fix(spec): shared/http.zod.ts 的两个不同 enum 不再发布成同一个 def key —— 7 值 HttpMethod 拿回裸名,5 值子集改名 HttpMethodSubset,build-schemas 补硬报错守卫#5976
qq9340100 merged 3 commits into
mainfrom
claude/issue-5832-httpmethod-defkey-collision

Conversation

@qq9340100

Copy link
Copy Markdown
Collaborator

Fixes #5832

前提复现(动手前先对 origin/main 实测)

issue 三条复现命令逐条跑过,前提成立,行号与代码形状也与 issue 一致:

$ pnpm --filter @objectstack/spec gen:schema
$ node -e "console.log(require('./packages/spec/json-schema/shared/HttpMethod.json').enum)"
[ 'GET', 'POST', 'PUT', 'PATCH', 'DELETE' ]          # 5 值 —— 7 值那份被覆盖掉了
$ grep -n "export const HttpMethod" packages/spec/src/shared/http.zod.ts
20:export const HttpMethod = z.enum([
37:export const HttpMethodSchema = lazySchema(() => z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']));

顺带把碰撞面量全了(按 build-schemas.ts 同样的枚举顺序遍历 15 个命名空间):
全仓 15 个 def key 被写第二次,其中 14 个是 export const X = XSchema 自别名(两次写的是同一个对象,发布物逐字节相同),只有 shared/HttpMethod 是两个真正不同的 schema。
这条量测直接决定了守卫的判据(见下)。

改了什么

1. 5 值受限子集改名(裁 1,照 #4684 RateLimitConfig 先例)

改前 改后
schema const HttpMethodSchema HttpMethodSubsetSchema
类型别名 HttpMethodType HttpMethodSubset
发布名(shared) shared/HttpMethod (覆盖了 7 值那份) shared/HttpMethodSubset
发布名(ui) ui/HttpMethod ui/HttpMethodSubset

7 值的 HttpMethod 一字未动,拿回它本来的裸名。实测发布物:

shared/HttpMethod        ["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"]   # 5 值 -> 7 值
shared/HttpMethodSubset  ["GET","POST","PUT","PATCH","DELETE"]                    # 新增
ui/HttpMethodSubset      ["GET","POST","PUT","PATCH","DELETE"]                    # 由 ui/HttpMethod 改名
api/HttpMethod           ["GET","POST","PUT","DELETE","PATCH","HEAD","OPTIONS"]   # 不变

content/docs/references/shared/http 现在同时有 HttpMethod(7 值,含 HEAD/OPTIONS)与 HttpMethodSubset(5 值)两节,各带一句 .describe() 说明谁用在哪 —— 这是本单要修的用户可见面。

为什么类型别名也跟着改。 裁定里提到「其类型别名已是 HttpMethodType」,我本来只想改 const。实测拦住了:scripts/lib/docs-import-surface.ts 要求每个发布名 N 在自己的入口点有一个同名类型导出,否则记一条 no type export gap,而 docs-import-surface.baseline.json只减不增的 ratchet —— 只改 const 而发布名叫 HttpMethodSubset 会新增两条 gap,check:docs 直接红。所以按裁定里「沿仓内命名惯例定名」执行:发布名 / schema const / 类型别名三者对齐成一个名字(ADR-0112 D9「一个名字只指一件事」)。HttpMethodType 当年(#4691)只是因为 HttpMethod 被占用才起的名字,不是一个自带含义的名字;#4691 那条 pin 的论证一字未改,只换了拼写。

2. 选项 3 守卫:同一个 def key 被第二次写 = 硬报错

新增 packages/spec/scripts/lib/def-key-collisions.ts,在 build-schemas.ts 的生成循环之后、两个 ratchet 之前跑(它们都以 def key 计量,碰撞只产生一个 key,谁也看不见)。

判据是实例同一性,不是当天的字节相等:

一个 def key 允许被写两次,当且仅当两个导出名指向同一个 schema 实例。

这正好放行上面量到的 14 处 export const X = XSchema 自别名(第二次写的是同一个对象,不可能改变发布出去的内容),而拦住两份独立声明 —— 哪怕它们今天序列化结果相同,下一次改动其中任何一份都会让发布物重新取决于导出枚举顺序,而那正是本单的失效模式。

为什么没有收口到 #4696 的同一个工具函数(裁定要求说明):#4696scripts/lib/schema-index.ts 判的是源码文本 —— 正则扫 .zod.ts,按「文件/页面 slug」决定归属;它的冲突判据是「同一 category 内两个文件声明同名」。HttpMethodHttpMethodSchema同一个文件里,对它就是同一条目,天然看不见。而 build-schemas.ts 拿到的是运行期的值(命名空间对象里的 z.ZodType 实例),判据只能是实例同一性。两者输入类型不同、判据不同,没有可收口的公共函数。能收口的那一半已经收口了:def key 的拼法(schemaNameFromExportKey)两边共用同一个 scripts/lib/schema-name.ts(#4592 定下的单一来源),所以两个生成器对「什么是一个 def key」永远不会漂开。错误文案里也互相点名。

3. RENAMED_DEFS 登记

只登记 ui/HttpMethod -> ui/HttpMethodSubset:./ui 只 re-export 了子集,所以 ui/HttpMethod 确实离场了(0 key 结转,枚举 def 没有 authorable 属性)。shared/HttpMethod 故意不登记 —— 它仍在发出,只是内容从 5 值变回源码一直声明的 7 值;checkRenameTable 会把它判成「这是复制不是改名」而拒绝,判得对。

反向验证(方向在跑之前就写死了:)

把删掉的肢体接回去 —— 在 HttpMethodSubsetSchema 旁边重新声明 HttpMethodSchema(而不是把新名字改回去:那样 ui/view.zod.ts 的 import 会断,脚本在模块加载期就死,红得毫无意义),然后跑真 gen:schema:

$ pnpm --filter @objectstack/spec gen:schema
─── Summary ───
  Generated: 1623 (136 as input shape)

❌ 1 JSON Schema def key(s) are claimed by two or more different schemas:

    json-schema/shared/HttpMethod.json  <-  HttpMethod, HttpMethodSchema
...
Exit status 1

要点两条:恰好 1 条(14 处自别名全绿,守卫可用),而且它停在 ratchet 之前(输出里没有任何 json-schema.manifest.json 字样)。恢复后 gen:schema / gen:openapi / check:generated 全绿。这段反向验证还被固化成了常驻测试(scripts/def-key-collisions.test.ts 最后一例,在沙箱里 copy 一份 src/ 改完真跑子进程)—— 理由同 #5168:一个从未被观测到红过的门禁,不能算门禁。

跨包消费者(裁定要求如实列出)

本仓 packages/spec 之外没有任何 HttpMethodSchema / HttpMethodType 的消费点(只有 CHANGELOG、content/docs/references/** 生成物与 release notes 提到)。

兄弟仓 objectui 有两处,本 PR 不跨仓改,已另开 issue 跟踪(不改跨包语义,只跟名):

  • packages/types/src/zod/objectql.zod.ts:26,41import { HttpMethodSchema as SpecHttpMethodSchema }
  • packages/types/src/objectql.ts:44export type { HttpMethodType as HttpMethod }

两处都只在 objectui 升到下一个 @objectstack/spec 发布版时才需要跟名。

验证

  • pnpm --filter @objectstack/spec test324 files / 8275 tests passed
  • pnpm --filter @objectstack/spec typechecktsc --noEmit + check:test-typecheck 全绿
  • pnpm --filter @objectstack/spec check:generated10/10 绿(merge origin/main 之后重跑仍 10/10)
  • check:dual-source-exports(4218 names / 0 new)、check:exported-anycheck:generated --reconcile-onlycheck:adr-anchorscheck:nul-bytes 全绿
  • docs-import-surface.baseline.json 少了一行(ui/HttpMethod — no type export)—— ratchet 只减不增的方向,由 --update-import-baseline 生成;该文件仓内是 ASCII 转义编码而生成器写原始字符,为了让 diff 只剩这一行真实变化,内容取生成器输出后按仓内既有编码惯例重新序列化(内容 100% 来自生成器,门禁读的是 JSON.parse 后的条目,与编码无关)。

对兄弟单的影响(必答)

两单都不因本 PR 变简单、变难或变得不必要。


Generated by Claude Code

@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:40pm

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.

@github-actions github-actions Bot added documentation Improvements or additions to documentation tests protocol:ui tooling labels Aug 6, 2026
@qq9340100
qq9340100 marked this pull request as ready for review August 6, 2026 13:58
@qq9340100
qq9340100 added this pull request to the merge queue Aug 6, 2026
Merged via the queue into main with commit 795b6e1 Aug 6, 2026
25 checks passed
@qq9340100
qq9340100 deleted the claude/issue-5832-httpmethod-defkey-collision branch August 6, 2026 14:18
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:ui size/l tests tooling

Projects

None yet

2 participants