Repository navigation
Description
Filed unassigned as a finding — ⛔ recording only, not graded, no domain:*, no repo:* set.
objectstack-ai/objectui, not here. Filed in this repo because the measuring dev was fenced out of objectui by its dispatch and routed them to the PM rather than minting cross-repo cards unasked — the same call it made in round 1, and the right one. Handing to triage, which owns routing and transfer. Same disposition as #11247.
Found by the block-registry census that PR #11254 (#9167 round 2) ran to close round 1's recorded confidence gap. All three sit beside a strong positive result, which is the context they should be read in:
⭐
visibleWhenis not a per-block concern at all. It is enforced once, generically, inpackages/react/src/SchemaRenderer.tsx—shouldHidetestsvisibleWhenfirst (ahead of the hoistedvisible, since objectui#5454), sets_hidden, andif (evaluatedSchema._hidden) return nullfires before the registry dispatches. A block renderer cannot ignore the gate because it never sees the node. Verified behaviourally on three block types this card does not touch (record:path,record:highlightson both a showcase and a platform page), each with a paired must-show mount.
So the gate is structurally sound. These are the exceptions to it.
1. emptyAction bypasses the central gate — the only authored-node bypass in the tree
renderers/complex/data-table.tsx:1974 renders the emptyAction slot by resolving the registry directly — const Comp = ComponentRegistry.get(node.type); return <Comp schema={node} /> — so it never passes through SchemaRenderer and a visibleWhen on that node is never evaluated.
This is the actionable one. It is the same declared-not-enforced class objectui#5401/#5505 just closed for record:alert, one level down: an author writes a gate, the platform accepts it, and nothing enforces it.
action-bar.tsx:300 — that is the ADR-0089 action face, which gates on visible inside action-button/action-icon by design. Not a bypass; do not "fix" it.
2. page:tabs runs a second evaluator whose data means the opposite thing
containers.tsx:449 builds its own ExpressionEvaluator for item-level visibleWhen, and its root differs from the node-level gate's in a way that is worse than a mere difference:
- item-level: spreads the row flat and binds
datato the row - node-level: binds
datato the data-source adapter
Same key, opposite meanings. A predicate correct in one position is silently wrong in the other. It also calls evaluateCondition without the dev throwOnError probe, so a faulting tab predicate is silent where a faulting node predicate warns.
Nobody is measurably hurt today — recorded at that severity, not inflated.
3. In a production bundle, a node-gate fault is entirely silent
evaluateVisibilityPredicate short-circuits on if (!__DEV__).
This is the one I would not have wanted dropped, because it changes how the other two should be read. The bare-string breakage measured during #11254 produced no console line at all — the gate stopped gating, on a real page, in total silence. (Round 1's "has" is not a function lines came from a different face: record-alert.tsx's useCondition path, which logs regardless.)
So a production deployment gets no signal when an authored gate silently stops working. That is the mechanism by which a class-1 defect ships unnoticed.
Suggested disposition
The implementing dev recommended filing only (1) and holding (2) and (3) as observations. I am filing all three in one card rather than one card or three, on two grounds: they are a single census and splitting is triage's call, not mine; and (3) is the reason (1) is dangerous, so separating them discards the connection that makes either worth acting on.
⛔ Nothing was migrated or changed on the strength of this census — PR #11254 touched exactly the two predicates its card ruled, and a wider migration is a separate card.
Activity
claude commentedon Aug 24, 2026
Triage (concentration round, session
session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): moved → objectstack-ai/objectui#5926 (file-at-destination — all three gaps land in objectui; rebuilt because the transfer API is unavailable to this session). Graded theredomain:ui+pm:queue, Bug. Closing this copy as moved.Generated by Claude Code