Skip to content

BatchOptions.validateOnly 声明了 dry-run 但从不实现 —— "预演"会真实落库(PD #10) #4052

Description

@os-zhuang

问题

BatchOptionsSchema(packages/spec/src/api/batch.zod.ts:64)声明了:

validateOnly: z.boolean().optional().default(false).describe('If true, validate records without persisting changes (dry-run mode)'),

该字段挂在 updateManyData / deleteManyDataoptions 上(protocol.zod.ts:526,541)以及 /batch 请求体里。但运行时没有任何一处读它 —— 全仓 grep validateOnly 的消费者为零(只有 schema 声明和一个断言默认值能解析的测试)。protocol.tsupdateManyData / deleteManyData / batchData 都无条件落库。

为什么这比无害的 no-op 更糟

validateOnly 的语义是数据安全承诺 —— 调用者以为在跑一次不落库的预检(导入前校验一批记录、CI 里验证 payload),实际行为是照常写库。这个字段把"预演"骗成了"真删/真改"。这是 PD #10「声明 ≠ 强制」里危害最高的一类:不是能力缺失,而是契约主动误导

对比同组三个兄弟,全都真实生效,唯独它悬空:

选项运行时消费点
atomicprotocol.ts:3482 + rest-server.ts:6607(atomic===false → 400)
returnRecordsprotocol.ts:3501
continueOnErrorprotocol.ts:3486,3610
validateOnly

决定:墓碑退役(而非仓促实现)

一个真正的 dry-run 有独立的设计空间(no-commit 下的 cascade / 唯一约束语义、响应契约该如何表达每行"若执行是否会通过"),值得在有真实需求时单独立项做对,而不是为了填坑仓促实现。因此:

  • batch.zod.ts:validateOnlyretiredKey(...) 墓碑(写它 → 解析报错并给出处方,而非静默剥除,ADR-0104)。
  • step18.semantic[] 注册一条 semantic 迁移记录(与 analytics-query-request-format-retired 同型:声明但从未实现、无存储可改写,故为 semantic TODO 而非 stack conversion),使 spec-changes.json / upgrade-guide / spec_changes MCP 工具如实记录本次退役。
  • major changeset。

validateOnly 是 optional + 有 default,退役不影响任何现有正确调用者。日后要做 dry-run 随时可重新引入一个设计完整的版本。

顺带(同一 PR)

packages/spec/src/api/plugin-rest-api.zod.ts:918requestSchema: 'CreateManyRequestSchema' 是一处悬空引用 —— 从未有 schema 导出此名(真实契约是 protocol.zod.tsCreateManyDataRequestSchema)。该字符串运行时和 OpenAPI 生成都不消费,是文档字符串失真,危害低于 validateOnly。改指向真实存在的 CreateManyDataRequestSchema

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions