Skip to content

parent 表头绑定也是稀疏的 —— #4953 修完 record 之后,同一个作者陷阱在 parent 根上原样留着 #6457

Description

@baozhoutao

Filed unassigned from PR #6454(#4953 裁决第 1 条的 engine-core 份额)。只记录发现,不含修法承诺,不在该 PR 内扩面 —— 维护者 2026-08-06 裁决只覆盖 record / previous 两个根,parent 未被覆盖,而 #4889 给表头读语义自有一套(未绑定 ⇒ LOCKED),不能顺手推广。

发现

PR #6454 把字段 readonlyWhenrecord(merged)与 previous 两个根过了 materializeDeclaredFieldsparent 根刻意没有物化,理由写在代码注释里:它是另一个对象的行,该函数手上没有它的声明字段表;而且「未绑定」正是 #4889 fail-closed 判定所依赖的信号,不能被物化成一个存在的空对象。

但表头本身是驱动读出来的一行,没有任何补齐:

packages/objectql/src/engine.ts:3074
  const row = await this.findOne(rel.master, { where: { id: parentId }, context: { isSystem: true } } as any);
  return (row as Record<string, unknown>) ?? null;

于是 parent 上完整重现了 #4953 正文描述的那个陷阱,只是换了一个根:

情形 readonlyWhen: parent.status == null 结果
表头行带 status 正常求值 按谓词真假锁 / 不锁
驱动没回读 status fault No such key: status unknownVariableOf 不匹配(parent 根是绑定的),走普通 fail-open 出口 ⇒ 声明的锁被放行
表头解析不到(null) fault Unknown variable: parent LOCKED(#4889 的 fail-closed 出口)

中间那一行由 PR #6454 的测试实测钉住(does not disturb the #4889 parent binding 用例的后半段:表头 { id: 'inv1' }status{ quantity: 9999 } 原样放行 + failed to evaluate — change allowed through)。该用例当时是为了证明没有顺手物化 parent,顺带把这个洞量了出来。

同一形状也存在于 bulk 路径的 resolveMasterDetailParents(engine.ts:3097),以及 #4977requiredWhen 绑的同一个 parent 绑定 —— 后者 fail-open 的后果是要求不被强制,方向与 readonlyWhen 相反但同为 declared ≠ enforced

为什么单独立单而不是随 PR 扩面

  1. 裁决没覆盖:2026-08-06 的裁决逐条列的是「字段 readonlyWhen」与「flow 触发记录播种」两处服务端接缝,parent 不在其中。
  2. 修法不是同一套:record 的物化用的是本对象fields;parent 要用的是 master 对象fields,意味着物化点要么下移到 resolveMasterDetailParent(s)(引擎手上有 master schema),要么给 strip 函数多传一份 master 声明字段表 —— 这是一个接口形状选择,不该由一个 bugfix PR 顺手定。
  3. 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 的 fail-closed 出口有交互:物化后「表头缺键」会从 fault 变成可求值,而「表头读不到」仍应保持 LOCKED —— 两条路径必须继续可区分,需要一并想清楚再动。

建议(供分诊判断,非承诺)

若采纳,落点更可能在 resolveMasterDetailParent(s) 内(引擎在那里同时握有 master schema 与刚读回的行),这样 readonlyWhen / requiredWhen 两个消费者一次到位,strip 函数签名不变。

Blocked-by: PR #6454(先把 record / previous 两根的形状定下来)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions