Skip to content

fix(spec): OpenAPI components.schemas 不再是空的 —— lazySchema Proxy 撞上 typeof === 'object',并补上产物自洽门禁 - #5459

Merged
os-zhuang merged 2 commits into
mainfrom
claude/issue-5168-openapi-components
Aug 5, 2026
Merged

fix(spec): OpenAPI components.schemas 不再是空的 —— lazySchema Proxy 撞上 typeof === 'object',并补上产物自洽门禁#5459
os-zhuang merged 2 commits into
mainfrom
claude/issue-5168-openapi-components

Conversation

@os-zhuang

Copy link
Copy Markdown
Contributor

Fixes#5168

前提复核(基线已前移)

单子写的基线是 81e2744,现已前移到 ed0d2aac0;#5293 昨日动过 build-openapi.ts(退役 HttpServerConfigSchema)。逐条对 origin/main 复核,前提完全成立:

  • 判据行仍在 packages/spec/scripts/build-openapi.ts:255,一字未动;
  • 对照实验在当前 origin/main 上复现,唯一变量是 Proxy:
$ npx tsx scripts/build-openapi.ts -> Components: 0
$ OS_EAGER_SCHEMAS=1 npx tsx scripts/build-openapi.ts -> Components: 9
  • 6 个悬空 $ref 与单子列的完全一致:ApiError / CreateRequest / DeleteResponse / ListRecordResponse / SingleRecordResponse / UpdateRequest,defined schemas: [];
  • 运行时探针确认判据是第一段短路,不是第二段:typeof'function',而 '_zod' in schematrue

改动一:放宽判据(建议 1)

判据同时接受 'object''function'lazySchema 不需要改动 —— 它专门维护了 _zod facade 供 toJSONSchema 遍历,判据的第二段对 Proxy 本来就有效。

改动二:产物自洽门禁(建议 2)

生成器在写盘之前自检两条,任一不满足即非零退出,自恰不了的文档根本不会被写出来(红臂实测:失败时 json-schema/openapi.json 未被创建):

  1. 每个本地 $ref 必须解析得到 —— 按 JSON Pointer 解析而非按 #/components/schemas/ 前缀匹配,将来新增的 #/$defs/... 引用自动覆盖;报错逐条点名悬空 $ref 及其文档位置,并把「已定义 schema 列表」一并打出(哪一侧是空的是读者最先需要的信息)。
  2. 没有 schema 被静默降级 —— 九个契约 schema 是一张字面清单,某个名字没产出永远是缺陷。原循环写成 if (像 zod) { 收 }没有 else,正是这个静默跳过的形状让九次跳过发布成了空文档;现在声明即强制。z.toJSONSchema() 抛错时原先塞一个 {type:'object'} 占位冒充契约,同样改为响亮失败(当前九个全部干净转换,零占位)。

接线点选择(实测后定):接在生成器内部,不做独立 check: 脚本。因为 packages/spec/json-schema/.gitignore:61、每次 pnpm build 重新生成 —— 独立检查脚本无论如何都要先跑一次生成器才有东西可查。「产物自洽」这类断言不需要任何基线快照,比「产物最新」更便宜。

关于「提交 json-schema/openapi.json

做不到,也不该做:该目录是 gitignore 的,产物不入库,由 pnpm build(gen:schema && gen:openapi && tsup)每次重新生成,并通过 files + exports 随包发布。所以本 PR 不含产物文件;真实 pnpm --filter @objectstack/spec build 产出的结果已在报告与下方 tests 中附上(components: 9,DANGLING: NONE)。

typeof === 'object' 普查结果(只报告,未改)

没有第二处'_zod' in 这个成员测试全仓仅此一处。其它生成器用的是 instanceof z.ZodType(仅 build-schemas.ts),而实测 instanceof 对 Proxy 返回 true —— 即这个惯用法是 Proxy 安全的,不存在同类隐患。另外 build-schemas.ts / build-react-blocks-contract.ts / check-liveness.mts / check-variant-docs.mts / check-react-blocks-declaration-parity.ts 都在文件顶部设了 OS_EAGER_SCHEMAS=1,build-openapi.ts 是唯一没设的 —— 本 PR 让它对 Proxy 直接免疫,比依赖环境变量更稳。

packages/rest 侧:仅注释,无行为改动

三处以现在时陈述「components.schemas 是空的」的注释被本 PR 证伪,已按事实更新(openapi-endpoints.ts 一处、openapi-endpoints.test.ts 两处)。行为与断言一字未改:声明式端点的 enrichment 仍然只写 type: object 而不编造 $ref —— 理由从「components 是空的」修正为「九个契约 schema 是通用 CRUD 信封,不是某个具体对象的 body 形状」,结论不变。

越范围发现

已立 #5456(finding,未认领、未进 pm:queue):base spec 用 7 条手写 path 描述路由面,与 rest 真实路由无任何对账,漂移了不会红 —— 即 check:generated ledger 里 gen:openapi 那条 why 真正说的那件事(本 PR 补的是自洽,不是对账)。实测当前未漂移,故为休眠缺口。该单顺带澄清:「最新性门」(单子建议 3)在这里没有对象,因为产物不入库,没有入库快照可以变陈旧 —— 建议按「对账」而非「最新性」定级。


Generated by Claude Code

@vercel

vercelBot commented Aug 5, 2026

Copy link
Copy Markdown

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

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
objectstackIgnoredIgnoredAug 5, 2026 12:48pm

Request Review

@github-actionsgithub-actionsBot added size/l documentation Improvements or additions to documentation tests tooling labels Aug 5, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

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

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

  • content/docs/ai/connect-mcp.mdx(via @objectstack/rest)
  • content/docs/api/error-handling-server.mdx(via @objectstack/rest)
  • content/docs/api/index.mdx(via @objectstack/rest)
  • content/docs/permissions/authentication.mdx(via @objectstack/rest)
  • content/docs/plugins/index.mdx(via @objectstack/rest)
  • content/docs/plugins/packages.mdx(via @objectstack/rest)
  • content/docs/protocol/kernel/http-protocol.mdx(via @objectstack/rest)
  • content/docs/protocol/kernel/i18n-standard.mdx(via packages/rest)
  • content/docs/releases/implementation-status.mdx(via @objectstack/rest)
  • content/docs/releases/v12.mdx(via @objectstack/rest)
  • content/docs/releases/v17.mdx(via @objectstack/rest)

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

Copy link
Copy Markdown
Contributor

⛔ merge queue 构建失败 — 先分诊,再决定要不要重排

队列构建 31010196973 红了。队列跑的是全量套件(PR 侧 CI 只跑 affected 子集),
所以失败的测试可能在本 PR 没碰过的包里 —— 那不是重排能修的。每次盲目重排都会让排在后面的所有 PR 重建一轮。

失败的 job(日志抽取,best effort):

  • Test Core (3/3) — 失败步骤: Run this shard's tests

    �[41m�[1m FAIL �[22m�[49m src/commands/serve-email-config-parity.contract.test.ts�[2m > �[22mEmailServiceConfigSchema ↔ resolveEmailCapabilityArg�[2m > �[22mreads every key it declares, but for the fi
    

历史信号:

  • 本 PR 过去 24h 无队列失败记录(首次)。
  • 过去 24h 队列共有 5 个失败构建(不含本次)。

分诊清单:

  1. 失败测试在本 PR 改动的包里 → 真回归,修 PR。
  2. 失败测试与本 PR 无关 → 在其他 PR 的同类评论里搜同名测试;出现过 ⇒ flaky 实锤,开 issue 修/隔离那条测试。修好前重排只会再烧一轮全队列。
  3. 两者都不是 → 可能与同组 PR 语义冲突;等前面的 PR 落地或失败出队后再重排一次即可,不要连续重排。

Generated by Claude Code · merge-queue-triage workflow (#4859)

@os-zhuangClaude

Copy link
Copy Markdown
ContributorAuthor

分诊(spec 车道 PM):第 3 型 —— 同批连坐,非本 PR 回归,非 flaky,不需人工重排。

失败测试 serve-email-config-parity.contract.test.ts不存在于本 PR 的改动里 —— 它来自同批排在前面的 #5465(本次构建链在其推测合并之上)。根因与处置见 #5465 的分诊评论(与已合入的 #5470 语义冲突,#5465 已出队修复中)。

已核实:队列已自动将本 PR 重建到纯 main(cd2efe62a)之上(队列分支 pr-5459-cd2efe62a… 在),等待其自然落地即可。给 flaky 检索者:这次失败不要计入该测试的 flaky 记录。


Generated by Claude Code

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.

发布出去的 OpenAPI 文档 components.schemas 是空的,而 6 个 $ref 全部悬空 —— lazySchema Proxy 撞上 typeof === 'object' 判据

2 participants

@os-zhuang@claude