chore(core): 弃用 ObjectValidationEngine —— 规则 enforcement 单实现在服务端 (#3110) - #3111
Merged
Merged
Conversation
…ingle-implementation on the server (#3110) Validation rules are enforcement, and enforcement lives on the server (`objectql`'s rule-validator). objectui already draws that line for the predicates it DOES evaluate client-side: `evaluator/fieldRules.ts` handles the presentation predicates by delegating to the canonical `ExpressionEngine`, "rather than re-implementing a parallel evaluator" (ADR-0036). This engine was that parallel evaluator, on the enforcement side of the line. #3103 converged its semantics onto the server rule-for-rule with eight mutation-tested gates, and STILL left a known divergence: the server carries ADR-0113's legacy-violation exemption (reject only when the merged state violates AND this write worsens it); this engine does not. Editing an unrelated field on a legacy row would be blocked here and accepted there. One careful pass still left a gap — mirroring cross-repo behaviour is structurally unreliable, not unreliable-this-time. Nothing removed, no behaviour change. Callers keep working; the barrels keep re-exporting, because publishing a deprecated API is not wiring. The decision ships as a MECHANISM, not a comment. What #3103 deleted was a doc comment claiming spec canonicity that had been false for fifteen majors; shipping its successor as another comment would repeat the mistake one level up (#3017). `validation-engine-stays-unwired.test.ts` scans `packages/*/src` and fails if a production module references the engine, naming the file and the issue to reverse first. It strips comments and string bodies before matching, so prose that merely names the engine is not a wiring, and it self-checks that the scan reached a plausible tree — a guard that silently scans zero files is the objectstack#4115 failure mode. Three mutations verified it: a direct import, an indirect namespace access, and a broken scan path all turn it red. Pre-submit feedback moves to a server validate-only (dry-run) write — objectstack#4372 — which also covers `unique` and `json_schema`, the two rule kinds a client can never check. The five spec-derived rule TYPES in @object-ui/types are untouched: they anchor objectstack#4115's ledger and are independent of whether objectui ships an engine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
执行 #3110 的拍板结论(选 B,理由见 该 issue 的决策评论)。
零行为变更,零删除。 现有调用方原样工作,barrel 照常再导出——发布一个 deprecated API 不是接线。
拍板一句话
规则是 enforcement,enforcement 单实现在服务端。 要「提交前反馈」就加 validate-only(dry-run),而不是养第二个求值器。
仓里这条线本来就画好了:
evaluator/fieldRules.ts的头注释明写,客户端 CEL 委托给 canonical 的ExpressionEngine,"rather than re-implementing a parallel evaluator"(ADR-0036)。presentation 谓词(visibleWhen/readonlyWhen/requiredWhen)客户端评估——判错了最坏是多显示一个字段;enforcement 谓词单实现在服务端——判错了是数据或可用性事故。ObjectValidationEngine正是那句注释说的 parallel evaluator,而且在边界的 enforcement 一侧。决定性证据
#3103 是一轮认真的收敛:逐条对着服务端
rule-validator.ts源码改,配 8 个 mutation-test 闸门。它仍然留了一处分歧——服务端带 ADR-0113 的 legacy-violation 豁免(仅当合并后状态违规且本次写入使其恶化时才拒,存量违规行原样放行),引擎没有。用户在一条老数据上改个无关字段,预检报红、服务端本来会接受。一轮认真收敛尚且留尾巴 ⇒ 跨仓镜像 parity 是结构性不可靠,不是这一轮执行力的问题。
替代路径
渲染服务端拒绝即可——它已经是结构化的(
field/code/message,objectstack#3957 起带 label)。要提交前反馈走 objectstack#4372(写路径 validate-only):同样的 UX、零 parity 风险,并且覆盖客户端永远做不到的两类——unique(要查库;客户端 SELECT-then-check 本身就是 TOCTOU 竞态,正是 spec 移除它的理由)和json_schema(ajv 在服务端)。决策做成机制,不是注释
这是本 PR 的重点。#3103 删掉的是一条冒领 spec canonicity、错了十五个大版本的文档注释;把它的继任决策再写成一条注释,就是在上一层重犯同一个错(#3017:comments are not mechanisms)。
新增
validation-engine-stays-unwired.test.ts,扫packages/*/src,生产模块一旦引用引擎就红,失败文案点名文件 + 指向要先翻的 issue。三个设计取舍:@object-ui/types的文档注释里正当地提到了引擎名,那不是接线。剥字符串同时意味着报错文案里引用该名字也不算。import * as core+core.ObjectValidationEngine的间接路径也会被抓到(纯 import 正则会漏)。闸门 mutation-test(3/3 红):
fieldRules.tscore.ObjectValidationEngine)未动的东西
@object-ui/types):它们是 objectstack#4115 台账的锚,与引擎存废无关。schema-validator.ts/validation-engine.ts):那是客户端表单校验,不是服务端规则的镜像,不在本决策范围。已在validation/index.ts头注释里点明,免得下一个 agent 误伤。重启条件(tripwire 写在代码里)
offline 写入进路线图之日(
types里已有OfflineConfig)。离线时没有服务端可问,客户端求值器从「冗余」变成「必需」。届时前置条件必须是 spec 携带 conformance fixtures——golden 的「规则 + 记录 + 判定」三元组,两仓 CI 各跑同一套;那是唯一能把跨仓行为 parity 从约定变成机制的办法。要翻案先翻 #3110。验证
关联:#3110(拍板)、#3103 / #3107(语义收敛与留下的 ADR-0113 分歧)、objectstack#4372(dry-run)、objectstack#4115(台账)。