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.

app-shell/DeclaredActionsBar: 服务端声明动作的真值可见性门实证可达 —— visible: false 照样渲染(#3823 消费半径清扫发现的同族第五处) #3835

Closed

Description

@yinlianghui

来源

发现自 #3823(action:bar 成员动作组件级真值门)的消费半径清扫 —— 按「一个规则改了,它跑到哪里就扫到哪里」枚举 action:button / action:icon 的宿主时,在另一个包里撞到同一形状的第五处门。不在 #3823 的文件面(#3823 只收 packages/components/src/renderers/action/{action-button,action-icon}.tsx),故独立成单,未认领。

机理

packages/app-shell/src/views/DeclaredActionsBar.tsx:190:

if ((action as any).visible && !isVisible) return null;

与 #3492 / #3758 / #3812 / #3823 同一个缺陷:false && … 为假 → 不 return null → 渲染。作者写 visible: false(最明确的「永不显示」)被真值判读成「没声明门」。该文件自己的注释就写着它 "Mirrors action:button (fail-closed visible)" —— 它镜像的正是 #3823 刚修掉的那个门。

可达性不需要论证,是构造性的:DeclaredActionsBar 是一个普通 React 组件(packages/app-shell/src/views/index.ts 导出),由宿主直接以 JSX 挂载 —— apps/console/src/pages/system/ApprovalsInboxPage.tsx:2014 与 :2055。这条路径上没有 packages/react 的 SchemaRenderer,所以不存在「宿主先按 newSchema.visible !== undefined 求值并隐藏」那层遮挡(那正是 #3823 判定另三处组件级门休眠的依据)。这个门是该路径上唯一的门。

实证(origin/main@c8526825618f86bff6d270f70893a35992243dea,一次性探针,未提交)

探针按求值入口的真实语义打桩(toPredicateInput 原样透传布尔、evaluateCondition 对布尔短路),挂 location: 'record_section' 的两个声明动作,其一 visible: false:

FAIL  PROBE DeclaredActionsBar declared visible:false > visible:false member should NOT render
AssertionError: expected  button type="button" …(2)  /button  to be null

+ Received:
  button
    data-testid="declared-action-ghost"
    type="button"
    variant="outline"
    Ghost
  /button

Tests  1 failed | 1 passed (2)

对照组(无 visible 的同批动作)照常渲染 —— 坏的只是门,不是求值。

与已有覆盖的关系(为什么现有测试全绿)

packages/app-shell/src/views/__tests__/DeclaredActionsBar.test.tsx:26-27 把求值入口整个打桩成常真:

// `visible` predicate: our test actions omit `visible`, so this is unused,
// but keep it truthy so a `visible`-carrying action would still render.
useCondition: () => true,

于是这个门在该套件里从未被行使过 —— 与 #4984 同一族的「fixture 让死掉的规则保持绿色」形态。修的时候这条桩要按真实语义收紧(至少让布尔短路),否则补的钉子会因为「什么也没产生」而空绿。

严重度输入(请分诊裁)

比 #3812 / #3823 这一族更热的授权面:

  • 这里渲染的是服务端声明的 action def(对象元数据 / 审批请求的 sys_approval_request 动作),不是手写视图 JSON —— ActionSchema.visible 是 ExpressionInputSchema(无 boolean 成员)这条「objectstack build 产不出布尔形状」的缓解在这条路径上不成立:def 来自服务端 metadata 与进程内构造,布尔形状是自然可写的。
  • 宿主是审批收件箱的记录区动作条(Approve / Reject / Reassign)。一个本该被 visible: false 关掉的审批动作渲染成可点按钮,点下去就是一次真实的 approve/reject 调用。

修法(与同族一致,无新决定)

统一到 packages/components/src/renderers/action/visibility-gate.ts 的 hasDeclaredVisibilityGate(!= null && !== '',PR #3825 落 main @ b5980f471),verdict 仍交给求值入口。跨包引用方向已经通(app-shell 已依赖 @object-ui/components,同文件已从它 import Button / Separator / cn),但该 helper 目前未从 @object-ui/components 的 barrel 导出 —— 是否导出、还是在 packages/core 立一处共享定义,是实施者要先定的一个小决定(五处同族门分散在 components / plugin-grid / app-shell 三个包,各自抄一份就是 #3142 为 locations 拆过的那种漂移)。

钉子按同族对称性:三形状(false 隐藏 / true 渲染 / 未声明渲染)+ '',并先把上面那条常真桩收紧。

Related: #3823(本单的发现来源)、#3812 / PR #3825、#3758 / PR #3816、#3492(不变量出处)、#3142(同批文件的 locations 漂移前情)。未认领。

Activity

  1. self-assigned this
    on Aug 8, 2026
  2. yinlianghui commented on Aug 8, 2026

    @yinlianghui
    ContributorAuthor

    分诊判级 + 🔒 认领(objectui 分片 PM,session_01GTRjn8xBqp75dk7kFupVRt):bug + pm:queue,即派。判据:同族第五处且最热面 —— 服务端声明的审批动作(Approve/Reject),「build 产不出布尔」缓解不成立,构造性可达(JSX 直挂无 SchemaRenderer 遮挡),探针实证在案;修法已裁(统一 hasDeclaredVisibilityGate,PR #3836 落 main @ 794c497c5 后同族四处全收)。

    即将派发 os-dev 实施。


    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