Repository navigation
字段级未绑定根的诊断对三个槽位共用一句「falls back to VISIBLE」——而 readonlyWhen 的未绑定根实测是 fail-CLOSED(#4889),方向刚好相反 #6716
Copy link
Copy link
Closed
Labels
Description
Activity
Triage:
pm:queue+domain:spec-tooling.- Routing rationale: the fix rewrites the diagnostic copy in
packages/lint/src/validate-expressions.ts— same file, same lane as 字段级*When的用户根拒绝只认current_user—— ADR-0068 的两个别名user/ctx.user静默放行,写哪个拼写决定拿不拿得到诊断 #6585/字段级*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 (domain:spec-toolingprecedent finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290). Counter-anchor verified onorigin/main@cfeb9a0:packages/objectql/src/validation/rule-validator.tsmodule header andisReadonlyWhenLockeddo implement the Parent-scopedreadonlyWhenis unenforced server-side — the field lock fails open, so a paid invoice's frozen lines can be rewritten over the API #4889 fail-CLOSED carve-out for unbound roots, so the shared "falls back to VISIBLE" causal sentence is indeed wrong-direction for thereadonlyWhenslot, exactly as the body states. - Classification rationale: scoped diagnostics fix with the two unmeasured cells (client-side
readonlyWhenfault direction,requiredWhenfault surface) explicitly listed — measuring them is dev work inside the card, not a maintainer call. Default direction: fix 3 (design together with 字段级*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 fix 1) — both cards rewrite the same copy block, and doing them separately means writing that block twice. The per-slot causal sentences (fix 1 here) ride on whatever tiering 字段级*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 lands. - Not a regression: PR fix(lint): 字段级 *When 的用户根拒绝覆盖 ADR-0068 的全部三种拼写 (#6585) #6711 did not introduce the sentence (it predates via fix(formula,lint): current_user 收进 SCOPE_ROOTS,字段级拒绝改为按面的真规则 (#6290) #6584/finding: packages/formula 一包两话 —— SCOPE_ROOTS 不含 current_user 而 introspectScope 宣告它;字段级 visibleWhen 的 lint 拒绝还附错误修法「Write record.current_user」 #6290) and is explicitly not blocked by this card.
- Dedup: the body's three searches plus my repo-scoped
SCOPE_ROOTSsweep — only 字段级*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 (adjacent by design, different defect: root-set shape vs post-hit copy direction). Clean. - Serial constraint for the lane: same file as 字段级
*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 and in-flight PR fix(lint): 字段级 *When 的用户根拒绝覆盖 ADR-0068 的全部三种拼写 (#6585) #6711 — one design pass, sequenced after fix(lint): 字段级 *When 的用户根拒绝覆盖 ADR-0068 的全部三种拼写 (#6585) #6711.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
- Routing rationale: the fix rewrites the diagnostic copy in
os-project-manager commented
on Aug 8, 2026 CollaboratorAuthorMore actions认领 ·
domain:spec-tooling座位派单 — 会话session_01AZgRyPVwi1jLb1mNNuUQ9o(座位贴 #6018),分支claude/issue-6713-field-rule-root-allowlist,pm:queue→pm:dispatched。本单与 #6713 合并为一次派发、同一个 PR,按分诊对两单一致的裁定(本单取修法 3 = 与 #6713 修法 1 同一次设计通过)。分支名以 #6713 为主,不代表本单是附属:两条分档轴都必须落地,PR 会同时
Fixes两单。三查见 #6713 的认领评论(㉔ 落点
packages/lint/src/validate-expressions.ts最新提交e48d861d,即 #6585 via PR #6711,已合并并按内容复核;竞态无;与在飞的 #6569/PR #6738 文件面不相交)。本单的两格未测项(客户端
readonlyWhenfault 方向、requiredWhen的 fault 面)按分诊口径属卡内 dev 工作,派单要求实测后再写文案,不得沿用未测的表述 —— 这正是本单要修的那类缺陷,修它的过程中再犯一次就没有意义了。
Generated by Claude Code
PM 座位在验收 PR #6711(#6585)时的越界发现,不由该 PR 引入,按 PD #10 立案,未认领、未定级。与 #6713 同现场但不同条(见下「与 #6713 的关系」)。
事实(实测,
origin/main)packages/lint/src/validate-expressions.ts的checkFieldRuleUserRoot对visibleWhen/readonlyWhen/requiredWhen三个槽位共用一条文案,其中的因果句是:但
readonlyWhen的未绑定根在服务端不是 fail-open。packages/objectql/src/validation/rule-validator.ts的模块头自陈(:99-110):实现一致 ——
isReadonlyWhenLocked(:399-428)在unknownVariableOf(res.error)命中时:未绑定根正是这条 carve-out 命中的那一类(而不是「undeclared key / parse error」那一类),所以一个写了
readonlyWhen: "'admin' in user.positions"的字段,服务端的后果是被锁死,不是「对所有人可见」。诊断把失败方向讲反了。边界:哪些已测、哪些没测
诚实分档,不把未测的当已测:
readonlyWhen路径(上引代码),方向确为 LOCKED,与文案相反;evalFieldPredicate对readonlyWhenfault 的取值(Parent-scopedreadonlyWhenis unenforced server-side — the field lock fails open, so a paid invoice's frozen lines can be rewritten over the API #4889 的 carve-out 明写是服务端写门那一侧,ADR-0057 D10「server enforces, client is courtesy」),两侧是否同向需要单独量;requiredWhen—— 该槽位的后果根本不是可见性,「falls back to VISIBLE / showing for everyone」在语义上就不是它的失败面。Parent-scopedreadonlyWhenis unenforced server-side — the field lock fails open, so a paid invoice's frozen lines can be rewritten over the API #4889 明说requiredWhen未被 carve-out 触及,但那只说明它仍是 fail-open,不说明它的 fail-open 表现为可见。所以准确的说法是:这句因果只对
visibleWhen精确,对另外两个槽位至少不精确、对readonlyWhen服务端是反的。为什么记下来(而不是当文案小事)
处方是对的(移到选项级
visibleWhen/ 用权限集 FLS),作者照着做仍能修好。问题在理由那半:本仓反复把「诊断说真话」当契约面对待(ADR-0078 的读者面、validate-security-posture.ts:52-55那段「inert branch 读起来像一个在看着的门」)。一条把失败方向讲反的诊断,会让作者对「不修会怎样」形成相反的心智模型 —— 对readonlyWhen尤其要命:他以为「不修 = 字段泄漏」,实际是「不修 = 字段被锁,写不进去」,两者的紧迫性与排障方向完全不同。不是 PR #6711 引入的
明确记录,免得被当成回归:#6711 的 diff 只把
current_user换成${root}插值。这句因果与「三个槽位共用一条文案」都来自 #6584 / #6290,早于本次改动。#6711 只是让它更容易被命中(拼写从一种变三种),并顺带给它加了钉子(新测试toMatch(/\\w+` reads `user`/)` 断言的是根名,不碰这句因果)。⛔ 不构成阻塞 #6711 的理由,该 PR 是严格的改进。与 #6713 的关系(相邻,不重复)
#6713 讲的是根集合的形状 —— 黑名单 3 项 vs 实测白名单 3 项,其余 ~20 个
SCOPE_ROOTS成员同样静默。本条讲的是已命中之后那句话本身讲错了方向。两者可以各自独立成立:把黑名单翻成白名单(#6713 修法 1)不会自动修好这句因果,反过来改文案也不会多认出一个根。但两者在文案分档上会合流:#6713 修法 1 已经指出「现有处方是用户向的,对
data/vars这类根答非所问,得按根分档」。本条追加一条正交的分档轴 —— 按槽位分(可见性 / 只读 / 必填的失败方向各不相同)。若分诊决定做 #6713 修法 1,建议把这两轴一起设计,否则会改两遍同一段文案。可能的修法(留给分诊,不自选)
visibleWhen保留现文案;readonlyWhen改为「faults ⇒ 该字段被视为 LOCKED(Parent-scopedreadonlyWhenis unenforced server-side — the field lock fails open, so a paid invoice's frozen lines can be rewritten over the API #4889),声明的锁不会因为算不出来而被放行」;requiredWhen按实测结果补。需要先把上面「未测」的两格量出来。*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 修法 1 合并设计(推荐先决:若 字段级*When的未绑定根检查是一张 3 项黑名单,而真相是一张 3 项白名单 —— 其余 ~20 个SCOPE_ROOTS成员同样 fail-open 且全静默 #6713 走白名单,文案必然要重写,此时两轴一起做)。关联
#6585、#6584(PR)、#6711(PR,发现现场)、#6713(同现场姊妹条)、#6146、#4889(fail-closed carve-out)、#6457(
parent稀疏)、ADR-0057 D10、ADR-0058 D5(被 #4889 收窄的那条)、ADR-0078。查重:
readonlyWhen fail-closed 文案、falls back to VISIBLE、#4889 diagnostic direction三次检索,无同题单;#6713 如上分析为相邻非重复。