越界发现,来自 #3828 的实现(PR #3887 )。按 PD #10 立案,未认领、未定级 。观察类:同样是一段文档 ,今天没有用户会撞到它,但它对姊妹系统的失败方向做了一个已经不成立 的断言 —— 而且这次是在本仓的 ADR 里,即这套设计的权威记录处。
事实
docs/adr/0036-field-conditional-rules.md 的 ## Server enforcement (framework) 段(:55 起)在逐条讲完 requiredWhen / readonlyWhen 的服务端行为后,:67-68 写:
A predicate that fails to evaluate is fail-open and logged (a broken rule must never block a legitimate write).
这一条无条件 覆盖上面两个 bullet(requiredWhen 与 readonlyWhen),而自 objectstack#4889 起它对 readonlyWhen 的一个 fault 类不成立 :谓词因为引用了本次写入没有绑定的 scope 根(parent.status == 'paid' 而手里没有 master-detail 头)而 fault 时,服务端是 fail-CLOSED。
objectstack origin/main @ fec784863,packages/objectql/src/validation/rule-validator.ts:
事实
位置
未绑定根 → 记日志 treating the field as LOCKED
:599-603
同一分支 return true(= LOCKED)
:604
其他 fault 才是 failed to evaluate — change allowed through + return false
:606-607
stripReadonlyWhenFields 随即 delete 掉该 key
:522 / :537
设计说明「readonlyWhen: the UNBOUND-ROOT case is fail-CLOSED (#4889)」,并明确「Every OTHER readonlyWhen fault … keeps the fail-open policy」
:99-109
requiredWhen 那一半仍准确(objectstack#4977 绑了 parent scope 但刻意保留 fail-open 语义,:117-141、:1416-1430),所以要收窄的只是 :67-68 这条概括 ,不是整段。
与 #3828 的关系,以及为什么另立一单
#3828 / PR #3887 修的是 packages/core/src/evaluator/fieldRules.ts 的模块头,文件面被 issue 明确限定为那一个 docblock,所以本条没有捎带修。两者是同一个陈旧断言的两个副本 :代码注释那一份已收窄,ADR 这一份仍在,而 ADR 是新读者查「服务端到底怎么办」时的第一站,留着它等于把刚拆掉的错误心智模型原样保存在更权威的位置。
顺带记录一处已核准确、不需要动 的地方,免得后来者重复排查:packages/types/src/form.ts:1076-1085 关于 previousValues 的注释把这条链路讲对了 —— 「faulting on an unbound root and failing OPEN — which let a user edit a locked field and have the change silently dropped on save」,正是本条要补上的那个后果。
建议修法(留给分诊,不自选)
把 :67-68 收窄为「多数 fault fail-open;readonlyWhen 的未绑定根一类自 objectstack#4889 起 fail-CLOSED(锁死并丢弃写入),按 ADR-0057 D10 以服务端为准」,并在 ## Client enforcement (objectui) 段点一句两端在这一格方向相反(PR docs(core): fieldRules 模块头收窄 readonly fail-open 的「与服务端一致」断言 (#3828) #3887 的 docblock 已是现成措辞,可直接对齐,注意 framework 的 ADR-0057 与本仓 docs/adr/0057-console-ai-chat-one-conversation-docked.md 撞号,引用需写明是 framework 编号);
只在 :67 加「except an unbound scope root for readonlyWhen(objectstack#4889)」一句,不展开;
不动,记录为已知边界。
倾向 1:这段的价值就是给出服务端行为的权威概括,而概括错了方向比缺失更贵 —— #3828 的成本正是如此。
查重
仓内三次检索:ADR-0036 fail-open(0 命中)、readonlyWhen 4889(仅 #3828 本身)、0036-field-conditional-rules(0 命中);另检 fail-CLOSED 全文(#3792 / #3368 / #3871 主题不同),无同题单。
Refs: #3828 、PR #3887 、objectstack#4889、objectstack#4977、ADR-0057 D10(framework 编号)。
越界发现,来自 #3828 的实现(PR #3887)。按 PD #10 立案,未认领、未定级。观察类:同样是一段文档,今天没有用户会撞到它,但它对姊妹系统的失败方向做了一个已经不成立的断言 —— 而且这次是在本仓的 ADR 里,即这套设计的权威记录处。
事实
docs/adr/0036-field-conditional-rules.md的## Server enforcement (framework)段(:55起)在逐条讲完requiredWhen/readonlyWhen的服务端行为后,:67-68写:这一条无条件覆盖上面两个 bullet(
requiredWhen与readonlyWhen),而自 objectstack#4889 起它对readonlyWhen的一个 fault 类不成立:谓词因为引用了本次写入没有绑定的 scope 根(parent.status == 'paid'而手里没有 master-detail 头)而 fault 时,服务端是 fail-CLOSED。objectstack
origin/main@fec784863,packages/objectql/src/validation/rule-validator.ts:treating the field as LOCKED:599-603return true(= LOCKED):604failed to evaluate — change allowed through+return false:606-607stripReadonlyWhenFields随即delete掉该 key:522/:537readonlyWhen: the UNBOUND-ROOT case is fail-CLOSED (#4889)」,并明确「Every OTHERreadonlyWhenfault … keeps the fail-open policy」:99-109requiredWhen那一半仍准确(objectstack#4977 绑了parentscope 但刻意保留 fail-open 语义,:117-141、:1416-1430),所以要收窄的只是:67-68这条概括,不是整段。与 #3828 的关系,以及为什么另立一单
#3828 / PR #3887 修的是
packages/core/src/evaluator/fieldRules.ts的模块头,文件面被 issue 明确限定为那一个 docblock,所以本条没有捎带修。两者是同一个陈旧断言的两个副本:代码注释那一份已收窄,ADR 这一份仍在,而 ADR 是新读者查「服务端到底怎么办」时的第一站,留着它等于把刚拆掉的错误心智模型原样保存在更权威的位置。顺带记录一处已核准确、不需要动的地方,免得后来者重复排查:
packages/types/src/form.ts:1076-1085关于previousValues的注释把这条链路讲对了 —— 「faulting on an unbound root and failing OPEN — which let a user edit a locked field and have the change silently dropped on save」,正是本条要补上的那个后果。建议修法(留给分诊,不自选)
:67-68收窄为「多数 fault fail-open;readonlyWhen的未绑定根一类自 objectstack#4889 起 fail-CLOSED(锁死并丢弃写入),按 ADR-0057 D10 以服务端为准」,并在## Client enforcement (objectui)段点一句两端在这一格方向相反(PR docs(core): fieldRules 模块头收窄 readonly fail-open 的「与服务端一致」断言 (#3828) #3887 的 docblock 已是现成措辞,可直接对齐,注意 framework 的 ADR-0057 与本仓docs/adr/0057-console-ai-chat-one-conversation-docked.md撞号,引用需写明是 framework 编号);:67加「except an unbound scope root forreadonlyWhen(objectstack#4889)」一句,不展开;倾向 1:这段的价值就是给出服务端行为的权威概括,而概括错了方向比缺失更贵 —— #3828 的成本正是如此。
查重
仓内三次检索:
ADR-0036 fail-open(0 命中)、readonlyWhen 4889(仅 #3828 本身)、0036-field-conditional-rules(0 命中);另检fail-CLOSED全文(#3792 / #3368 / #3871 主题不同),无同题单。Refs: #3828、PR #3887、objectstack#4889、objectstack#4977、ADR-0057 D10(framework 编号)。