Skip to content

fieldRules.ts 的模块头自称 readonly fail-open「与服务端一致」——#4889 之后对未绑定根这一类已不成立(服务端是 fail-CLOSED) #3828

Description

@os-project-manager

越界发现,来自 objectstack#6713 / #6716 的实现(PR objectstack#6798)。按 PD #10 立案,未认领、未定级。观察类:这是一段注释,今天没有用户会撞到它,但它对姊妹系统的行为做了一个已经不成立的断言。

事实

packages/core/src/evaluator/fieldRules.ts 的模块头写:

Evaluation is fail-open for visibility/required (a broken predicate must not hide a field or wrongly block submit) and fail-open for readonly (a broken predicate leaves the field editable) — matching the server, which logs and allows the change through.

后半句在 objectstack#4889 之后对未绑定根这一类已经不成立。packages/objectql/src/validation/rule-validator.tsisReadonlyWhenLockedunknownVariableOf(res.error) 命中时:

logger?.warn?.(
  `readonlyWhen for '${name}' reads '${unbound}', which is not bound for this operation — ` +
    `treating the field as LOCKED (the declared lock is not waived because it could not be evaluated). ` + …
);
return true;

return true = LOCKED,随后 stripReadonlyWhenFields 把该字段从 payload 中删除。也就是说服务端对这一类不是「logs and allows the change through」,而是记日志后锁死并丢弃写入

客户端这一侧本身没错 —— resolveFieldRuleStatereadonlyWhenfallback: false(字段仍可编辑)是 ADR-0057 D10「server enforces, client is courtesy」下的合理选择。错的只是「matching the server」这半句:两端在这一类上恰好方向相反,而注释把它描述成一致。

为什么记下来

这正是 objectstack#6716 那一条的同类:一句把姊妹系统失败方向讲反的说明,会让读者建立相反的心智模型。具体到这里,读注释的人会以为「客户端可编辑 + 服务端放行 = 值会落库」,而真实链路是「客户端可编辑 + 服务端锁死丢弃 = 表单能改、保存报成功、值静默不落库」—— 排障方向完全不同。

requiredWhen 那一半仍然准确(objectstack#4977 明确没有采用 #4889 的 carve-out,两端都 fail-open),可见性那一半也准确(服务端根本不评估字段级 visibleWhen)。只有 readonly 这一句需要收窄。

可能的修法(留给分诊,不自选)

  1. 把 "matching the server, which logs and allows the change through" 收窄为「对大多数 fault 与服务端一致;未绑定根那一类服务端自 #4889 起是 fail-CLOSED(锁死并丢弃写入),两端方向相反,按 ADR-0057 D10 以服务端为准」;
  2. 只删掉 "matching the server" 这半句,不展开解释(成本最低,但丢掉了「两端在这里会不一致」这条对调用方有用的信息);
  3. 不动,记录为已知边界。

倾向 1:这段注释的价值恰恰在于说清两端关系,而两端在这一格不一致本身就是调用方需要知道的事。

查重

fieldRules fail-open readonly serverreadonlyWhen4889 fail-closed 三次仓内检索,无同题单。

Refs: objectstack#4889(fail-closed carve-out)、objectstack#4977、objectstack#6716(同类:诊断把失败方向讲反)、objectstack#6798(发现现场)、ADR-0057 D10。

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions