fix(metadata-protocol)!: batch 逐行结果迁移到 BatchOperationResultSchema 形状 —— 方案 B 硬切 (#4793) - #4841
Conversation
…4793) - protocol.ts 三个 bulk-write 端点(batchData/updateManyData/deleteManyData)的 逐行结果迁到 spec 声明形状:error:string → errors:ApiError[],record → data, 补发 index(请求数组下标);行类型直接别名 BatchOperationResult,tsc 双向钉死 - ROLLED_BACK / NOT_ATTEMPTED 从 message 前缀提升为 ApiError.code 一级结构化信息 (ERROR_CODE_LEDGER 注册),message 保留人类可读致因;删除 ADR-0119 D4 留下的 「刻意分歧」注释(存在理由已消灭) - 新增 conformance 钉子 protocol.batch-row-conformance.test.ts:三端点真实响应行 逐行过 BatchOperationResultSchema.parse + 旧键缺席 + errors/index 在场断言 (sabotage 实证:改回 error/record 时 8/10 红) - spec:ledger 注册两码;migrations/registry step17 增 semantic 条目 batch-row-result-schema-shape;atomic/errors describe 更新;衍生物再生成 - 仓内消费方清扫:#4798 三个测试套件 + delete-many/dropped-fields/record-not-found 测试迁移断言;client.batch-transaction.test.ts:135 双形状对冲清除; docs(data-api/wire-format/client-sdk)示例与文字更新,data-api 中 #4798 遗留的 「deleteMany 无事务」陈述一并修正;ADR-0119 追加 addendum 记录编码演进 - changesets:@objectstack/metadata-protocol major(升级指引 + 与 #4798 changeset 的叠加关系),@objectstack/spec minor Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9
…ch-row-result-schema
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 2 package(s): 106 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
CI:
|
| PR | 内容性质 | 首跑 |
|---|---|---|
| #4831 | 纯删除 | OOM(重跑转绿) |
| #4841(本 PR) | wire 形状迁移 | OOM |
| #4846 | patch,零运行时代码改动 | OOM |
第三例决定性:#4846 只改散文与注释,spec 类型图与 v17 前逐字节等价,照样在同一步被杀 —— 说明命中取决于「是否真跑 DTS 这一趟」,与改了什么无关。根因分析见 #4845(--max-old-space-size=12288 对 16 GB runner 而言是「允许涨到 12 GB」,V8 因此不积极 GC,越过物理内存被内核杀掉)。
本 PR 现在不重跑,因为它必须先做一次强制的 main 同步再推:本 PR 与 #4831(#4657,合并队列中)、#4846(#4709)共用 packages/spec/spec-changes.json,而该文件在 .gitattributes 里是 merge=os-regen —— merge 零冲突标记却会静默吞掉一侧改动,只有重新生成才暴露(#4616 今日实证)。等 #4831 落地后本 PR 会 git merge origin/main + 取 main 版重新生成再 push,那次推送会触发全新 CI。现在重跑等于烧一轮必然作废的 CI。
实施侧本地读数(合并基上)供参考:metadata-protocol 268/268(含新钉子 10 条)、spec 7380、client 222、rest 573、全仓 typecheck 122/122、全仓 build 71/71。
Generated by Claude Code
…ch-row-result-schema-work
os-regen 合并驱动对 spec-changes.json / protocol-upgrade-guide.md 不做文本合并, 本次合并零冲突标记地只保留了本分支一侧,静默吞掉 #4657 的条目(实测: origin/main 的 spec-changes.json 含 4 条 activationEvents 条目,合并后为 0)。 按协议 git checkout origin/main -- <生成物> 后从合并后的源整体重新生成: - spec-changes.json:现同时含 #4657 的 activationEvents 4 条与本单 batch-row-result-schema-shape 2 条 - protocol-upgrade-guide.md:同时含 plugin-activation-events-retired 与 batch-row-result-schema-shape - 四张 ratchet(api-surface / api-surface-signatures / authorable-surface / json-schema.manifest)与 origin/main 逐字节相同 —— 本单不增删任何导出 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9
已同步 #4831(#4657 activationEvents),重生成 + 全量复验完成推送 1. os-regen 静默吞并 —— 实测复现
按协议修复: 2. 核对断言(全部通过)
关于 #4616 的一个澄清:manifest 中
3. 验证读数(同步后全量重跑)
4. PR body 更正早前 body 里「merge origin/main(含 #4616 / #4657 线上先行合入)」一句当时不成立(#4831 尚在 CI),已在 body 中更正并保留更正说明,避免留下将来误导人的断言。 5. CI
保持 draft,un-draft 与入队归 PM。若 #4846(#4709)先入队,我可再同步一次 —— 届时 Generated by Claude Code |
…ch-row-result-schema
os-regen 第三轮实测:#4846 对 app-dead-authoring-keys-removed summary 的散文修正 ('WAS read by objectui')在合并中被静默吞掉(origin/main 2 处命中,合并后 0)。 按协议 checkout origin/main 后从合并源整体重生成;spec-changes.json 现三方并存: #4657 activationEvents 4 条、#4846 散文 2 处、#4793 batch-row 2 条。 四张 ratchet 与 origin/main 逐字节相同(本单导出增删 0)。 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9
末次同步完成:已含 #4846(main
|
| 断言 | 读数 |
|---|---|
spec-changes.json 三方并存 |
#4657 activationEvents 4 / #4846 WAS read by objectui 2 / #4793 batch-row 2 |
registry.ts step17 两条 id |
plugin-activation-events-retired 1 / batch-row-result-schema-shape 1 |
protocol-upgrade-guide.md 三方 |
各 1(#4846 散文 1 处) |
| #4657/#4616 删除未复活(带引号精确名) | manifest 6 个名 + api-surface 6 个名 → 全 0 |
| 四张 ratchet vs origin/main | 逐字节相同(本单导出增删 0) |
验证读数
check:generated:✓ All 8 up to datecheck:dual-source-exports:0 accepted dual-source(4276 names / 16 entry points)- metadata-protocol test:33 files / 281 passed(main 新增 1 个测试文件入包;含 conformance 钉子 10 条)
- spec test:295 files / 7359 passed(已带 fix(spec): 给 packages/spec 的 vitest 设 testTimeout 60s —— 止血,不再把无关 PR 踢出合并队列 (#4850) #4856 的
testTimeout: 60_000) - 全仓 build:71 tasks 全过;全仓 typecheck:122 tasks 全过
按 AGENTS.md §10 判断:本轮增量提交(#4856/#4842/#4847/#4822/#4838/#4836/#4857/#4858/#4846)与本 PR 改动面唯一交叠是 spec(#4846 registry 散文 + #4856 vitest 配置),故重跑 spec 全量 + metadata-protocol 全量 + 全仓 build/typecheck;client/rest 与增量零交叠,未重跑(上一轮读数 222/573 仍有效),合并队列会验证合并提交本身。
过程记录(自纠):本轮
pnpm install误跑在 merge 之前,导致@objectstack/verify对 #4857 新增的@objectstack/types/node子路径解析失败(TS2307)——又是 §9 陈旧态,补pnpm install --frozen-lockfile后全绿。已核实packages/types/packages/verify树与 origin/main 逐字节相同,非本 PR 引入。
CI 首跑预期能绿(#4856 已带上)。可以 un-draft + 入队。
Generated by Claude Code
Fixes #4793
按维护者拍板的方案 B 实施:实现迁到 schema 形状,搭 v17 major 窗口硬切,不做双发过渡,changelog 诚实写明升级路径。
分歧对照(迁移前 → 迁移后)
三个端点(
POST /data/:object/batch//updateMany//deleteMany)的逐行results条目:BatchOperationResultSchema声明)error: stringerrors: ApiError[]row.errors?.[0]?.message,按row.errors?.[0]?.code分支recorddataindex: number(请求数组下标)droppedFields实现侧行类型直接别名 spec 类型(
type BatchDataRowResult = BatchOperationResult),tsc 双向钉死;protocol.ts原 814-820 的「刻意分歧」注释已删(存在理由已消灭)。batch.zod.ts只动 describe 文案,schema 形状一字未变 —— 本 PR 是「实现追上已发布契约」,不是改契约迁就实现。ROLLED_BACK/NOT_ATTEMPTED结构化#4798 引入的两个 message 字符串前缀提升为
ApiError.code一级结构化信息,注册进ERROR_CODE_LEDGER(@objectstack/metadata-protocol名下,含语义注释;#4805 的 ledger 现状不动,只做本单最小扩充):errors[0].code === 'ROLLED_BACK'—— 已写入、被回滚撤销;message 保留致因行下标与原始错误errors[0].code === 'NOT_ATTEMPTED'—— 从未执行;更早的行失败中止了批次RECORD_NOT_FOUND/VALIDATION_FAILED等;无码的引擎抛错按status映射,兜底INTERNAL_ERROR,httpStatus有则镜像)未复用
BATCH_ABORTED(metadata publish 面)与标准目录的TRANSACTION_FAILED(描述整个事务,非逐行判定)——「已尝试被撤销 vs 从未执行」的区分正是本单要求成为 code 一级的信息,合并进任一既有码都会把它抹回 message。Conformance 钉子(结构性收益)
新增
packages/metadata-protocol/src/protocol.batch-row-conformance.test.ts:三端点真实响应行(成功行、失败行、ROLLED_BACK 行、NOT_ATTEMPTED 行、returnRecords:false 行)逐行过BatchOperationResultSchema.parse。.parse()的钉子不足以发现回退:zodz.object对未知键是静默剥离而非报错,且errors是 optional —— 实现改回发error/record时,.parse()照样绿。故钉子额外断言旧键缺席(error/recordnot present)与新键在场(失败行必有非空errors,每行index等于请求下标)。Sabotage 实跑(临时把三处 emitter 改回发
error/record,跑钉子):钉子必红,确认后已还原实现。
仓内消费方清扫读数(裸名扫描)
packages/client/src/client.batch-transaction.test.ts:135的双形状对冲?? (x as any).record?.project—— 清除,收敛到GetDataResponseSchema声明的单一形状并升级为等值断言protocol.batch-atomic/protocol.many-data-atomic/ objectqlprotocol-batch-atomic)—— 断言从前缀正则迁到 code 判定protocol.delete-many/protocol.dropped-fields.bulk/protocol.record-not-found测试)—— 一并迁移packages/restingress:三条路由均res.json(result)直传,无行级重映射,零改动;packages/clientSDK:纯 spec 类型直传,零改动data-api.mdx/wire-format.mdx(响应示例整体换新形状)/client-sdk.mdx更新;references/api/*为 describe/ledger 变更的再生成物。顺带修正data-api.mdx中 fix(metadata-protocol): deleteMany/updateMany 的 atomic 要么为真、要么拒绝 (#4620) #4798 遗留的失实陈述(「deleteMany 无包裹事务」)—— 与本次同段落文档直接矛盾,留着会自相矛盾,故在同 PR 修正并在此声明.error/.record命中均属其它契约(import 行{ row, ok, action, error }、notification/job service outcome、security explain layer、corebulk-write内部 util、单记录GetDataResult.record)—— 不在本单范围objectui 影子排查(PM 于
785b8a5d432cf009389a1a9180fdac2a8297543f实测 + 本实施 agent 独立浅克隆971302e22e8e8c49d73d16ef3c3f56b8d2510ebe复测,两次读数一致):objectui 仅使用跨对象
/api/v1/batch(batchTransaction,不同契约,本 PR 未动)——零旧键消费方,无需跨仓 linkage issue。ADR-0087 conversion 评估
对照 D3
semantic[]先例(enhanced-api-error-field-errors-renamed,同为 RESPONSE 面):已登记。step17 新增 semantic 条目batch-row-result-schema-shape(响应面、无存量元数据可重写,故为 semantic TODO 而非 D2 conversion),gen:spec-changes+gen:upgrade-guide再生成,docs/protocol-upgrade-guide.md迁移条目即升级指引通道(#4535 §5 先例)。ADR-0119 追加 addendum 记录 D4 行标记编码从前缀演进为结构化 code(D4 不变量本身不变)。Changesets
@objectstack/metadata-protocol: major —— wire 变更对照表 + 升级指引 + 与 fix(metadata-protocol): deleteMany/updateMany 的 atomic 要么为真、要么拒绝 (#4620) #4798 minor changeset 的叠加关系(其描述的前缀在本 major 中变为结构化 code)。绝不双发旧键。@objectstack/spec: minor —— ledger 两码 + registry semantic 条目 + describe 更新与 main 的同步(2026-08-03 10:2xZ,已含 #4831/#4657)
registry.ts与spec-changes.json/protocol-upgrade-guide.md与 #4831 正面重叠,且后两者在.gitattributes中是merge=os-regen。实测复现了该驱动的静默吞并:按协议修复:
git checkout origin/main -- <生成物>后从合并后的源整体重新生成,不做文本合并。核对断言(全部通过):plugin-activation-events-retired1 /batch-row-result-schema-shape1spec-changes.json同时含两单条目protocol-upgrade-guide.md同时含两条"kernel/ActivationEvent"/"studio/ActivationEvent"/ActivationEventSchema/activationEvents在 manifest 与 api-surface 中均 0"system/EmailTemplate"/"system/SMSTemplate"/"system/PushNotification"/"system/InAppNotification"均 0(manifest 中 3 处EmailTemplateDefinition*是另一条live 家族,与 origin/main 一致)check:dual-source-exports名数 4286→4276 的下降全部来自 #4657/#4616 的删除,已正确保留)commit
2a58532f1单独记录该重生成,pre-commit 的 os-regen 守卫亦独立确认「2 artifacts merged WITHOUT a text merge … marker cleared」。验证读数(同步 #4831 后全量重跑)
@objectstack/metadata-protocoltest:32 files / 268 passed(含新钉子 10 条)@objectstack/spectest:295 files / 7358 passedcheck:generated:✓ All 8 generated artifacts are up to datecheck:dual-source-exports:0 accepted dual-source(4276 names / 16 entry points,171 re-exported)check:exported-any:0(1864 types + 1607 schemas);check:liveness/check:empty-state/check:strictness-ledger/check:adr-anchors:全绿@objectstack/clienttest:17 files / 222 passed;@objectstack/resttest:38 files / 573 passed;objectqlprotocol-batch-atomic:6 passedpnpm typecheck:122/122;全仓pnpm build:71/71已知 CI 状况(非本 PR 引入)
Test Core (1/2)对每个 spec PR 均红(签名:DTS Build start后零 tsc 输出直接ELIFECYCLE),已由 PM 立案 #4845 并在排查根因;本 PR 未为此改任何代码,亦未尝试绕过。影响面(诚实声明)
被 break 的只有绕开类型(
as any/ 裸 JS)读error/record的 off-contract 用法;照 SDK 类型/schema/文档写的消费方是从本 PR 起第一次在运行时拿到已发布契约。content/docs/releases/未触碰。🤖 Generated with Claude Code
https://claude.ai/code/session_0176qgxgCXTJCUv4YFLtusP9