Skip to content

validate-null-guards.ts 的接缝台账在 PR #6454 合并后会说假话 —— 字段 readonlyWhen 那行仍写着 "sparse / never materializes" #6458

Description

@baozhoutao

Filed unassigned from PR #6454(#4953 裁决第 1 条的 engine-core 份额)。只记录发现,不含修法承诺 —— 派发口径明确本轮不触 packages/lint(裁决第 3 条:闸门扩面要等两处服务端接缝都补齐,flow 半边在 services 席)。

发现

packages/lint/src/validate-null-guards.ts 的模块头维护着一张接缝台账(#4811 留下的,「一个被排除但没写理由的面,和一个根本没人看过的面无法区分」正是这一族 issue 的主题)。其中一行:

| field `readonlyWhen` | sparse | `stripReadonlyWhenFields` merges `{...previous, ...data}` and never materializes | excluded |

PR #6454 合并后,这一行的 binding 列(sparse)与 evidence 列(never materializes)都不再成立 —— 该接缝的 record / previous 两个根已过 materializeDeclaredFields。verdict 列(excluded)在 flow 半边落地前仍然正确,但理由已经换了:不再是「绑定是稀疏的,规则会开错药方」,而是「裁决第 3 条要求两处服务端接缝都齐才扩面」。

一张说假话的台账比没有台账更糟:它正是下一个作者用来决定「这个面要不要接闸门」的依据,而 evidence 列指向的那行代码已经不在了。

两半,别混在一起

  1. 台账更正(小,随时可做):把 binding / evidence 列改成实际形状,verdict 保持 excluded 但把理由换成「等 flow 触发记录播种补齐」。
  2. 闸门扩面(裁决第 3 条,有前置):两处服务端接缝都补齐后,readonlyWhen 面可纳入 null-guard 闸门。注意这一面的 fail 策略与已覆盖的两面不同 —— 校验规则与 hook condition 是 fail-closed,readonlyWhen 是 fail-open(除 Parent-scoped readonlyWhen is unenforced server-side — the field lock fails open, so a paid invoice's frozen lines can be rewritten over the API #4889 的未绑定根),即「谓词写错 ⇒ 声明的锁被放行」,与 requiredWhen 那行台账已经记下的理由同类(「fail-OPEN,所以一条没守卫的谓词静默地什么都不强制」)。扩面时这条理由要一并写进 evidence 列。

顺带:packages/objectql/src/cel-fault.ts 模块头那句「the same argument that put materializeDeclaredFields in front of both evaluators」在 #6454 后是三处,属同类文档漂移,可一并捎上。

Blocked-by: PR #6454(engine-core 半边)
Blocked-by: #4953 裁决第 1 条的 services 半边(flow 触发记录播种,尚未立单)

#4811 已 closed,故本单不作为它的子单;若分诊按裁决把 #4953 拆成子单,本单可并入「结论落档 + 闸门扩面」那一支。

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions