Skip to content

写路径 validate-only(dry-run)模式:把「提交前反馈」的 UX 用单实现的方式补回来(objectui#3110) #4372

Description

@os-zhuang

背景

objectui 刚拍板(objectui#3110):客户端不再镜像对象级校验规则 —— 规则是 enforcement,enforcement 单实现在服务端 objectql/src/validation/rule-validator.ts。objectui 的 ObjectValidationEngine 已标 @deprecated,并加了 tripwire 测试防止再被接进生产路径。

拍板过程里唯一被牺牲掉的东西是 UX:用户现在只能提交之后才吃到 rule_violation。这个 issue 就是把那份 UX 用不产生第二个求值器的方式补回来。

促成拍板的实证:objectui#3103 逐条对着本仓 rule-validator 源码做了一轮认真收敛(8 个 mutation-test 闸门),仍然留了一处分歧 —— ADR-0113 的 legacy-violation 豁免(仅当合并后状态违规且本次写入使其恶化时才拒)客户端没有实现。一轮认真收敛尚且留尾巴,说明跨仓镜像 parity 是结构性不可靠。

诉求:写路径的 validate-only(dry-run)模式

真正的 rule-validator、不落库、返回 violations 列表。

  • 零 parity 风险:同一份代码路径,不存在第二个实现可漂移。
  • 覆盖客户端永远做不到的两类:unique(要查库,而且客户端做 SELECT-then-check 本身就是 TOCTOU 竞态 —— 正是 spec 有意移除 unique 规则类型的理由)、json_schema(ajv 在服务端)。
  • ADR-0113 语义自动正确:豁免逻辑就在那条路径上,不需要任何一侧去"记得"实现它。
  • 也顺带能服务非 UI 消费者(CLI 校验、导入预检、AI 生成记录的自检)。

需要设计的点(非结论,列出来供讨论)

  1. 接口形态:insert/updatedryRun: true 选项,还是独立端点?倾向前者——同一入口保证走同一段代码,独立端点容易长出自己的分支。
  2. previous 的取法:update 的 dry-run 要不要真去读 prior record(state_machine、跨字段谓词、ADR-0113 都需要它)?读了才准,但就有一次读的成本。
  3. 权限:dry-run 必须走与真实写入相同的权限判定,否则会变成一个探测器(用报错差异反推他人数据是否存在/字段值)。
  4. 副作用边界:明确 lifecycle hooks(beforeInsert 等)在 dry-run 下执行——只跑声明式规则。否则 dry-run 自己就成了副作用来源。
  5. 返回形状:直接复用现有的 FieldValidationError[](field/code/message/label),console 已经会渲染,不需要新契约。

排期建议

不建议现在就做。 objectui 侧的弃用已经落地,现状(服务端拒绝 + 结构化报错渲染)是可用的。这个 issue 先作为决策记录存在,等产品确认「提交前反馈」是真需求时再排期。先记下来是为了:objectui#3110 的结论明确指向这里,没有这个 issue,下一个 agent 会重新发明客户端预检。

关联

  • objectui#3110 —— 拍板与理由(含为什么选 B 而不是接线)
  • objectui#3103 / objectui#3107 —— 语义收敛,以及留下的 ADR-0113 分歧
  • ADR-0020(规则求值器)、ADR-0113(legacy-violation 豁免)

另:未来若 objectui 需要离线写入

OfflineConfig 一旦进路线图,客户端求值器会从"冗余"变成"必需"(离线时没有服务端可问)。届时的前置条件应该是 @objectstack/spec 携带 conformance fixtures —— golden 的「规则 + 记录 + 判定」三元组,两仓 CI 各跑同一套。那是唯一能把跨仓行为 parity 从约定变成机制的办法。这不是本 issue 的范围,但值得在这里留个指针。

Metadata

Metadata

Assignees

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