feat(spec): api 补进 DEFAULT_METADATA_TYPE_REGISTRY 与 BUILTIN_METADATA_TYPE_SCHEMAS (#5271) - #5312
feat(spec): api 补进 DEFAULT_METADATA_TYPE_REGISTRY 与 BUILTIN_METADATA_TYPE_SCHEMAS (#5271)#5312os-zhuang wants to merge 5 commits into
api 补进 DEFAULT_METADATA_TYPE_REGISTRY 与 BUILTIN_METADATA_TYPE_SCHEMAS (#5271)#5312Conversation
…A_TYPE_SCHEMAS (#5271) Part of #5206 (step 1, spec 车道)。 `api` 条目一直被产出(artifact ingest 把 `defineStack({ apis })` 映射为 `api`)、 被索引(`buildEndpointIndex`)、被执行(#5040 E5/E8),而 spec 里哪儿都没声明这个 kind。于是 `getMetadataTypeSchema('api')` 返回 undefined,`saveMetaItem` 走它自己 文档写明的「未注册 schema 的类型不经校验直接落库」分支 —— `PUT /meta/api/:name` 接受任意 JSON。这是 `declared ≠ enforced` 反着读:enforced but undeclared。 - `MetadataTypeSchema` + `DEFAULT_METADATA_TYPE_REGISTRY` 补 `api` 条目; - `BUILTIN_METADATA_TYPE_SCHEMAS` 补 `api: ApiEndpointSchema`; - `ApiEndpointSchema` 补 ADR-0010 保护信封(每个注册类型的不变量); - `api` 的最小 create seed(教 carve-out 形状与 object_operation 的两个半边); - showcase `KIND_COVERAGE` 接手 `apis` 的覆盖(它不再是「非注册表 kind」)。 旗标按证据定,不是新授权:无静态条目时 `isRuntimeCreateAllowed` 与 `assertAllowed` 都走「无注册表条目 ⇒ 可运行时创建」的兜底(两处注释都点名 `api`),所以运行时直写 本来就被接受、只是不校验。`allowRuntimeCreate: true` 把这个既有判决写下来, `allowOrgOverride: false` 同样是今天的实际取值。code-only 方案被证据否掉:它会把 今天的 200 变成 403,且 #5086 在落库前对 draft 一视同仁地拒绝,#5206 第 2 步 (PR #5279)将无 draft 可门。 `ApiEndpointSchema` 的收紧被实测否掉:同一个 schema 也解析存量行,而存量行带 `packageId` / `state`,`strictObject` 让 packages/metadata 10 条测试转红。`api` 因此 与 `view` 同列 STILL_STRIP,实测写进该列表注释,真正的修法(信封/正文分离)另立 #5309。 Fixture 逐条裁定而非批量改写:protocol-meta 与 sys-metadata-repository 里的 `api` 标本被**替换**(留着会让断言经另一条分支变绿、却仍宣称在证明「无静态条目」那条); endpoint-matcher 那条「strips storage annotations」整条重写(它钉的正是信封被丢弃 这个缺陷本身)。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 107 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
CI 红诊断(PM,
|
CI 红诊断补充(第二签名,
|
CI 红诊断补充(第三签名,
|
`git merge origin/main`(至 5aae790,无冲突),生成物按 os-regen 四步互保: generated 文件整体 checkout 回合并基线,再 wholesale 重生成,最后断言兄弟 PR 的条目仍在。 两处过程中发现并纠正的坑,记下来免得下一个人重踩: 1. `gen:api-surface` 读的是**构建产物**,不是源码。合并后没重建就重生成,会把 #5021(PR #5289)刚退役的 `AnimationSchema` / `ZIndexSchema` 四行**重新加 回去** —— 正是 AGENTS.md §9 的陈旧产物陷阱。重建 spec 后重生成才对 (4422 → 4418 exports)。 2. 第 2 步的 `git checkout origin/main -- <generated>` 必须用**你实际合并的那个 tip**,不是 `origin/main` 的当前值。main 在我合并与 checkout 之间又前进了, 于是把 #4938 的 http-server 生成文档拉了进来 —— 而我的源码里没有那个改动, 等于提交了一份源码产不出的生成物。改用合并基线 5aae790 后归零。 最终生成物 delta 相对合并基线只有 7 行,纯增量(ApiEndpoint 的保护信封键); #5021 的退役完好(Animation/ZIndex 确认缺席)。 `protocol-publish-drafts-endpoint-gate.test.ts`(#5279,已合入 main)的 ENDPOINT_SCHEMA 用例改了**落地路径**而非断言:该 draft 现在过不了 saveMetaItem 的 422(这正是 #5206「一处修,两面得」要的结果),所以 fixture 改为先按真实写 路径存一条合法 draft、再只污染其 body —— 让那条 backstop 分支仍然被真实覆盖, 而不是删掉用例留一条无测试的活分支。未改该 PR 的任何生产代码。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
追加:已
|
PM 返工第 1 轮。把 `api` 注册成 metadata type 就落入「每个 REGISTERED 类型
必须被治理」不变量,而 `check:liveness` 不在我上一轮跑过的门清单里:
✗ 1 REGISTERED metadata type(s) governed by nothing: api
走的是第一条路线(纳入 GOVERNED + 播种台账),没有用 PENDING_GOVERNANCE ——
这是治理成本最低的时点:#5040 的 E 系列执行器全部已合 main,每个键**今天**
都有真实证据路径,而不是一句承诺。datasource 的教训(#4487:无治理期积了六个
惰性键,只能靠人手找出来)就是这条路线存在的理由。
27 条属性分类:live 25、planned 2、dead 0。
逐键证据按层给到 file:line —— 匹配器(endpoint-matcher)吃 name/path/method;
执行器(endpoint-executor)吃 type/target/objectParams;策略链
(endpoint-policy + security/inbound-rate-limit)吃 authRequired/rateLimit/
cacheTtl;映射层(api-mapping)吃 inputMapping/outputMapping;OpenAPI 增强
(rest/openapi-endpoints)吃 summary/description。7 个保护信封键由门自动判 live
(ADR-0010),故不写进台账。
两个 `transform` 判 **planned 而非 dead**,这个区分是有承重的:台账里的 dead
指「解析了、没有消费方」即静默 no-op;而 transform 是反过来 —— 它被解析后在
publish 与 runtime 两处**响亮拒绝**(api-mapping.ts:259),作者会被告知怎么改。
它留在词表里而不是被删,是因为接纳它需要函数注册表 + sandbox 裁决
(#5040 §3.4),那是一个待做的设计决定,不是一个可以顺手删掉的键。
无任何 `api` 键属于 proof-registry 的 bound high-risk class,因此没有一条带
`proof` —— 不为显得周全而编造。
另:`check:docs` 实测**仍红**(上一轮的同步提交并没有修好它,我从未跑过
`gen:docs`)。三份生成文档已重生成,delta 纯属本单:endpoint.mdx 的 7 个信封键,
metadata.mdx / metadata-plugin.mdx 的枚举里多了 `api`。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
返工第 1 轮:
|
| 层 | 文件 | 键 |
|---|---|---|
| 匹配器 | packages/metadata/src/endpoint-matcher.ts |
name(:210 重名裁决)、path(:199)、method(:115/:199) |
| 执行器 | packages/runtime/src/endpoint-executor.ts |
type(:216/:232)、target(:233)、objectParams.object(:217/:394)、objectParams.operation(:218/:368) |
| 策略链 | packages/runtime/src/endpoint-policy.ts、security/inbound-rate-limit.ts |
authRequired(:354)、rateLimit.enabled(:88)、.windowMs(:91)、.maxRequests(:90)、cacheTtl(:252) |
| 映射层 | packages/runtime/src/api-mapping.ts |
inputMapping.source/target(:318/:322)、outputMapping.source/target(同函数,经 :358) |
| OpenAPI | packages/rest/src/openapi-endpoints.ts |
summary(:214)、description(:215) |
两个 transform 判 planned 而不是 dead,这个区分有承重。 台账里 dead 的定义是「解析了、没有消费方」——静默 no-op。transform 恰恰相反:它被解析后在 publish 与 runtime 两处响亮拒绝(api-mapping.ts:259),作者当场被告知怎么改。它留在词表里而不是被删,是因为接纳它需要函数注册表 + sandbox 裁决(#5040 §3.4)——那是一个待做的设计决定,不是可以顺手删的键。判 dead 会把它送进 enforce-or-remove 的清单,而它已经被 enforce 了(以拒绝的形式)。
无任何 api 键属于 proof-registry.mts 的 bound HIGH_RISK class,因此没有一条带 proof —— 没有为显得周全而编造一个。
两门输出
$ pnpm --filter @objectstack/spec check:liveness
api 27 classified (live 25, planned 2)
✓ every governed-type property at the walk's one-level granularity is classified,
every registered type is governed or explicitly pending, no ledger row outlives its
property, every container inheritance is declared, and all bound high-risk proofs resolve.
$ pnpm --filter @objectstack/spec check:docs
✅ import examples resolve against api-surface.json (137 accepted gap(s) in the baseline)
✅ 244 generated files in sync with packages/spec
⚠️ 一处需要更正你的判断:check:docs 之前并没有被我的同步提交修好
任务里写的是「确认你同步提交已把三份参考文档修好」。实测它当时仍是红的:
✗ content/docs/references/ is out of date with packages/spec:
~ content/docs/references/api/endpoint.mdx
~ content/docs/references/api/metadata.mdx
~ content/docs/references/kernel/metadata-plugin.mdx
原因很简单:上一轮我跑的是 gen:schema / gen:api-surface / gen:spec-changes / gen:upgrade-guide / gen:strictness-ledger,从未跑过 gen:docs —— content/docs/references/** 是另一条生成链。已在本次提交里重生成,delta 纯属本单:endpoint.mdx 多 7 个信封键,另两份的枚举里多一个 api,无兄弟 PR 内容被动。
顺带记一条工具坑(与本单无关,但会咬下一个人)
重建 worktree 后 check:api-surface 报 Could not resolve module symbol for . (dist/index.d.ts). Is the package built? —— 它读的是构建产物,而新 worktree 的 dist 是 gitignore 的、不存在。这不是内容失败。重建 spec 后复跑:public API surface + factory signatures unchanged ✓,且 api-surface.json 工作区干净(本轮改的是 liveness 台账、一个脚本数组、生成文档,都不可能动导出面)—— 我选择重建后实测而不是据此推断。
复验
@objectstack/spec Test Files 309 passed (309) / Tests 7972 passed (7972)
check:liveness OK check:docs OK check:api-surface OK check:authorable-surface OK
check:spec-changes OK check:strictness-ledger OK check:upgrade-guide OK check:nul-bytes OK
其余已验证工作未改动。
Generated by Claude Code
Generated by Claude Code
PM 返工第 2 轮。`api` 进 metadata type registry 后,platform-objects 的
metadata-forms bundle 键集多出该类型的 label/description,四个 locale 漂移。
`node scripts/check-i18n-bundles.mjs --write`(merge 模式:不覆盖任何既有翻译,
新键以源文填充)。delta 纯属本单 —— 四个 bundle 各 +4 行,只有 `api` 一个键:
api: { label: "API Endpoint", description: "Declarative HTTP endpoint — …" }
非英文 bundle 里这两条暂为英文源文,这是 merge 模式的既定产物("they still
need translating");`check:i18n-coverage` 的冻结基线未被突破,故两门皆绿。
手改 `.generated.ts` 不是这条链的修法。
⚠️ 这是我第三次漏门(liveness → docs → i18n),根因是我一直按"改了哪个包就跑
哪个包的门"收工,而这三道都是**仓根**的、由被改内容触发而非由被改目录触发。
已改为按 `.github/workflows` 的 job 清单逐条过:本次 push 前跑完 44 道 CI 必跑
门(lint.yml 25 道 + spec-liveness-check.yml 4 道 + spec 生成物 13 道 +
validate-deps.yml 2 道),全绿。
注:i18n 两门必须在 **full build 之后**跑 —— 抽取器经 extract config 读
`@objectstack/spec` 的 dist、并跑构建后的 CLI。未构建时它报的是九个包
"extract failed — no output",很容易被误读成配置坏了而不是缺构建。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
返工第 2 轮:i18n bundle 重生成 + 按 workflow job 清单过全部 44 道门
修法: 非英文 bundle 里这两条暂为英文源文,是 merge 模式的既定产物(门自己的话:"they still need translating"), 门清单已改为按
|
Fixes #5271
Part of #5206(第 1 步,spec 车道)。engine 半边 PR #5279 不依赖本 PR,本 PR 也不动它的文件面。
前提先验证:是,两处都真的缺,且直写通道今天是开的
origin/main@88b9b2d:DEFAULT_METADATA_TYPE_REGISTRY无api条目(反证:view/flow在,行号可见);BUILTIN_METADATA_TYPE_SCHEMAS无api绑定;MetadataTypeSchema枚举里也没有api—— 子单正文没写这一条,但它是前置:注册表条目的type字段就是这个枚举。比子单正文更要紧的一条实情:直写通道今天不是关着的,是开着且不校验。
isRuntimeCreateAllowed(metadata-protocol/protocol.ts)与assertAllowed(sys-metadata-repository.ts)各有一条兜底 —— 「没有静态注册表条目的类型由getMetaTypes()合成为allowRuntimeCreate: true,写入门必须一致」—— 而两处注释都点名api。所以PUT /meta/api/:name一直返回 200,只是不过任何 schema。一处需要更正父单措辞:
getMetaTypes()其实枚举得到api(只要库里有行,metadataService.getRegisteredTypes()就报它),只不过拿到的是合成描述符 ——label: 'api'、filePatterns: []、domain: 'system'、schema: undefined。所以 Studio 不是「看不到这个类型」,而是「看到一个没有 JSON Schema 的类型,只能给 raw-JSON 文本框」。缺口是真的,形状比正文描述的窄一点。证据闸(存量扫描):干净
仓内可及的全部 4 条作者声明,逐条跑
ApiEndpointSchema.safeParse:sys_metadata无法从本环境扫描 —— 这是可行范围的诚实边界,不是「扫过了没事」。changeset 里给了运维侧的处方:升级前跑GET /api/v1/meta/diagnostics?type=api(该类型现在被这个 sweep 覆盖了)。旗标怎么定的(这是本单唯一的语义抉择)
allowRuntimeCreate: true+allowOrgOverride: false—— 两个都等于今天的实际取值,所以授权判决逐字节不变,本 PR 只加了一道形状门(422)。code-only(
allowRuntimeCreate: false+allowOrgOverride: false,即job/agent的形状)被证据否掉,三条:apidraft 将无法创建,api不在 metadata 类型注册表里 —— Studio 直写路径完全不校验端点,publishPackageDrafts 也没有 E7 门 #5206 第 2 步(PR fix(metadata-protocol): publishPackageDrafts 对apidraft 跑 ADR-0121 端点发布门 (#5206 step 2) #5279)的publishPackageDrafts端点门没有 draft 可门;allowOrgOverride: false:端点是发布方的对外 URL 契约,per-org fork 可以挪path、翻authRequired、摘rateLimit,而对方系统已经按这个 URL 集成了。ADR-0005 默认 false 正是要让 opt-in 是刻意的。姿态与datasource一致。executionPinned: false(端点是委派,ADR-0121 D5,被 pin 的是目标 flow)、loadOrder: 92(在 flow 的 80 之后)。ADR-0088 准入测试三条全过,且不构成对
router退役的翻案:router的交付形态是代码贡献,单条ApiEndpoint是声明式工件 —— 正是 ADR-0088 自己那一行预告的「第三种真实交付形态」。收紧被实测否掉(本 PR 最值得读的一段)
api进注册表后落入 #4001 收紧运动的两条不变量。保护信封已补(...MetadataProtectionFields);未知键收紧尝试了、被测量否掉了:这个 schema 不只是作者面,它也是存量行的解析器(
buildEndpointIndex、gateApiItemsForPublish),而存量行带着元数据层自己的记账键。strictObject让packages/metadata10 条测试转红 —— 装载期兜底把端点整条排除(路由答 404),publish 门报 schema 错误而不是它本该给的 D6 判决。所以
api与view同列STILL_STRIP,实测过程写进该列表自己的注释(不是一句「暂不收紧」)。真正的修法是元数据层的信封/正文分离,另立 #5309 —— 而不是教作者词表认两个存储层的键,那正是这场运动拒绝的交易。Fixture 逐条裁定,不是批量改写
三条
apifixture,三种处置:protocol-meta.test.ts的「plugin-registered types」isRuntimeCreateAllowed的另一条分支(已静态注册且allowRuntimeCreate: true)继续变绿,却仍宣称在证明「无静态条目」那条。theme/webhook接手;api另立两条专属断言(有效 → 200、无效 → 422)sys-metadata-repository.test.ts的 runtime-only 标本webhook,并另加一条「已静态注册的api走 runtime-only 仍可写」endpoint-matcher.test.ts的「strips storage annotations」not.toHaveProperty('_lock')—— 钉的正是「信封被丢弃」这个缺陷本身,而...MetadataProtectionFields修的就是它;且 fixture 里_lock: { managed: true }是 ADR-0010 从未定义过的形状。改为钉真正的分工:信封(已声明 ⇒ 存活)vs 记账键(⇒ 被 strip)消费半径按规则的调用方扫的,不是按编辑的包:改在
packages/spec,坏掉的 fixture 在packages/metadata与packages/objectql。反向验证(方向先预测再跑,其中一条是「变绿」)
把注册表条目与 schema 绑定 stash 掉重跑:
api那一行从表里消失第 4 条是特意预测的:那是「因为什么都没产出所以通过」的绿。记在这里,免得下一个读者把它当成覆盖。
验证
sweep 表新增的那一行(经真实
saveMetaItem路径):生成基线整体重生成(gen:schema / api-surface / spec-changes / upgrade-guide / strictness-ledger),delta 只有 7 行,纯增量,是保护信封的 7 个键:
json-schema.manifest.json/spec-changes.json/api-surface*.json/protocol-upgrade-guide.md/ 台账.counts.md零变化(全部 check 门通过)。check:nul-bytesOK,并对全部改动文件另跑了控制字符自扫(零命中)。顺带发现(已单独立单,均未指派,未在本 PR 修)
ApiEndpointSchema无法对未知键收紧 —— 同一个 schema 既解析作者声明又解析存量行(行上带packageId/state) #5309 ——ApiEndpointSchema无法收紧:信封/正文未分离(本 PR 的实测就是它的证据)ApiEndpointSchema.path的.describe()举的例子会被 ADR-0121 D1 当场拒绝 —— 而这段文案正是 Studio 端点表单显示给作者的提示 #5310 ——path的.describe()举的例子/api/v1/customers会被 ADR-0121 D1 当场拒绝,而这段文案正是本 PR 让 Studio 表单开始显示的那段saveMetaItem的 direct-active 写入是api的第三条门外通路 —— 命名空间门(ADR-0121 D1/D2)与去重门在这条路上无人跑 #5311 ——saveMetaItem的 direct-active 写入是api的第三条门外通路:命名空间门与去重门在这条路上无人跑(装载期兜底按设计跳过这两道)。api不在 metadata 类型注册表里 —— Studio 直写路径完全不校验端点,publishPackageDrafts 也没有 E7 门 #5206 两步都落地后仍然存在未触碰
content/docs/releases/、#5206 的 engine 半边、PR #5279。🤖 Generated with Claude Code
https://claude.ai/code/session_01ErbEDVAg1No9gdg1pgDAGB
Generated by Claude Code