Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

[finding] ActionEngine 的 visible 过滤仍用自己的「空/非谓词」范围:visible: 0 / NaN / ' ' 在引擎面隐藏、在渲染面显示 —— #3850 收敛后剩下的最后一处分叉 #3957

Closed

Description

@yinlianghui

来源

发现自 #3850 / #3862 结对实施(PR 分支 claude/issue-3850-3862-empty-predicate-gate)。#3850 把「已声明门」的唯一定义下沉到 @object-ui/core(hasDeclaredPredicate),渲染面(hasDeclaredVisibilityGate 保名 re-export)、SchemaRenderer 的 disabled 链、ActionRunner 的两处执行门都改读它。收敛后只剩一处仍用自己的范围回答同一个问题:ActionEngine.getActionsForLocation 的 visible 过滤。

packages/core/src/actions/__tests__/ActionRunner.conditionGate.test.ts 里那条 DOCUMENTED DIVERGENCE 用例写的是「objectui#3850 的 follow-up 拥有统一它」—— #3850 落地时并没有统一(它的裁决面是「已声明门」的定义与放置,ActionEngine 的自有范围不在其中),所以这张卡把那个指针落成实体,而不是留一句指向不存在的 follow-up 的注释。观察类:需要作者写出非谓词值或纯空白谓词才命中,今天没有已知用户路径。严重度请分诊裁。

机理

packages/core/src/actions/ActionEngine.ts:229-243(origin/main @ 65bb513dc073deb4e72950c287eef32cb56bf8ce):

const raw = ra.action.visible;
if (raw == null || raw === '' || raw === true) return true;
if (raw === false) return false;
const expr = toPredicateInput(raw);
if (expr === undefined) {
  // Not an evaluable predicate: an envelope with an empty `source`
  // (→ nothing declared → visible), or a stray non-predicate value,
  // which keeps the historical `Boolean(raw)` coercion.
  return typeof raw === 'object' ? true : Boolean(raw);
}

三条腿各自处理了一部分「空」:raw === '' 显式放过、空 source 信封走 typeof raw === 'object'(与 #3850 裁决一致 → 显示)。剩下两类落到与共享定义不同的答案上:

  1. 非对象的非谓词值:Boolean(raw),于是 visible: 0 / NaN = 隐藏。共享定义答「不是谓词 → 没有门 → 显示」。
  2. 纯空白串:toPredicateInput(' ') 不折叠而是包成 '${ }'(≠ undefined),所以根本走不到上面那支,而是进入求值 → 求出 falsy → 隐藏。共享定义把纯空白明确列为「没有门」(它是 toPredicateInput 唯一不折叠、因而需要定义处点名的那一形状)。

一个值,两个入口,两个答案 —— 正是 #3314 立的那条不变量所禁止的形状。

实测(worktree @ 65bb513dc + 本 PR 的改动,一次性探针,未提交)

引擎面用 ActionEngine.registerAction + getActionsForLocation('record_section') 数结果条数;渲染面是 action:button 叶子渲染后 DOM 里有没有按钮。

value                     | ActionEngine  | action:button 渲染面(#3850 后)| 一致?
0                         | 隐藏          | 显示                          | 分叉
NaN                       | 隐藏          | 显示                          | 分叉
'   '(纯空白)            | 隐藏          | 显示                          | 分叉
{}                        | 显示          | 显示                          | 一致
''                        | 显示          | 显示                          | 一致
{dialect:'cel',source:''} | 显示          | 显示                          | 一致

其中 ' ' 一行是本 PR 造成的新分叉,如实记录:改动前渲染面对纯空白也是隐藏(与引擎面一致),#3850 的裁决把「纯空白 = 没有门」定为语义后,渲染面变为显示,而引擎面没有跟着改(不在裁决的改读清单里)。方向上渲染面是对的 —— 一个什么都没说的谓词不该把动作从所有人面前藏起来 —— 分叉在引擎面。

两条方向,以及为什么它需要裁而不是顺手改

更彻底的方向是 producer 侧:@objectstack/spec 在发布期就拒绝非谓词值与空谓词(跨仓,domain:spec),那样运行期两个入口都不必猜。#3850 的分诊已把同类的 producer 侧半边(空 source 信封的拒收)判为「无需求证据前不排」,本卡同理只作记录。

Related: #3850(「已声明门」范围裁决与定义下沉,本卡是它收敛后剩下的最后一处)、#3848 / #3872(ActionRunner 两处执行门的 fail-open 姿态出处)、#3314(一值一答的不变量)、#3862 / #3955(SchemaRenderer 侧的同族)。未认领。

Activity

  1. changed the title [-][finding] ActionEngine 的 visible 过滤对「非谓词」值保留 Boolean(raw) 强转:visible: 0 在引擎面隐藏、在渲染面显示 —— #3850 收敛后剩下的最后一处范围分叉[/-] [+][finding] ActionEngine 的 visible 过滤仍用自己的「空/非谓词」范围:visible: 0 / NaN / ' ' 在引擎面隐藏、在渲染面显示 —— #3850 收敛后剩下的最后一处分叉[/+] on Aug 9, 2026
  2. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    PM 分诊(session_01GTRjn8xBqp75dk7kFupVRt):finding → 晋级 pm:queue,方向代裁 A 案 —— ActionEngine.getActionsForLocation 的 visible 过滤改读 hasDeclaredPredicate,非谓词值与纯空白 = 没有门 = 显示。代裁理由:①这是 #3850 裁决语义在最后一处自带范围消费者上的机械收敛,消灭 #3314 禁止的「一值两答」;②' ' 行的分叉是 PR3966 落地当天新产生的,不修则收敛工程留尾巴;③与谓词抛错的 fail-closed 姿态不冲突(正文已论证:求值失败 vs 压根没有条件是两个问题);④B 案会推翻 #3850 刚裁掉的方向,正文自己已排除。visible: 0 隐藏→显示的行为变更面为观察类(无已知用户路径),changeset 写明即可。producer 侧拒收半边维持「无需求证据前不排」,不另立卡。

    排程:与 #3955、#3960 三单结对一 PR,Blocked-by PR #3966 —— 合入后即派。


    Generated by Claude Code

  3. self-assigned this
    on Aug 9, 2026
  4. yinlianghui commented on Aug 9, 2026

    @yinlianghui
    ContributorAuthor

    🔒 认领(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt,批次 4)—— 与 #3955、#3960 三单结对一 PR,分支与文件面声明见 #3955 认领评论(claude/issue-3955-3957-3960-predicate-scope-converge)。本单按分诊评论 A 案执行。


    Generated by Claude Code

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