Skip to content

finding(components): autoTrigger executes an action whose own declared visible gate hides it — in both renderers #4191

Description

@yinlianghui

Measured while implementing #4162 (making action:menu consume autoTrigger). Filed separately: it is a property of the FLAG's relationship to the visibility gate, not of the overflow split #4162 fixes, and it predates that card in action:button.

Fact

action:button declares its auto-trigger effect before its visible early return:

useAutoTriggerOnce(hasAutoTrigger(schema), handleClick);   // effect
...
if (hasDeclaredVisibilityGate(schema.visible) && !isVisible) return null;

React commits effects for a component that returns null, so an action carrying both autoTrigger: true and a declared-false visible renders nothing and executes anyway. Measured on origin/main at e16fd9597, real action:button plus the real runner:

PROBE-VISIBLE-FALSE: rendered="" execute=1

Empty DOM, one execution. #4162 extends the same semantics to action:menu deliberately — its whole point is that inline and overflow must not disagree about a flag, and disagreeing here would have re-created the divergence one predicate over. So the two renderers now agree, and this card is about whether what they agree ON is right. The parity itself is pinned in packages/components/src/renderers/action/__tests__/action-overflow-autotrigger.test.tsx ("the ACTION's own declared visible gate does not suppress it"), which points here.

Note the neighbouring rule is different and is not in question: a container that renders nothing mounts no children, so a hidden action:bar or action:menu auto-triggers nothing. That one is pinned too.

Why it may matter

autoTrigger is never persisted metadata — hosts compose it client-side onto an action they already hold, so today's producers set it on an action they have just decided to run. Reaching this needs a host that composes the flag onto an action whose metadata ALSO declares a false visible, which no host in this repo does; that is why this is filed as observation-class rather than as a defect with a user behind it.

The reason it is worth a decision rather than a shrug is that the two gates answer different questions and only one of them is about the user's intent:

  • visible is the metadata author's verdict on whether this action may be OFFERED here (fails closed on a throwing predicate, per ActionEngine.getActionsForLocation);
  • autoTrigger is the host's statement that the user has already asked for it.

Reading the second as outranking the first means a deep link can invoke an action the author declared unofferable in this context — the runner's own confirm / param / entitlement gates still apply, so this is not an authorization hole, but it is a gate being bypassed by a transport flag. Reading it the other way costs the host a silent no-op, which is the failure mode #4123 and #4162 were both about, so "just gate it" is not obviously right either — a third answer (refuse loudly when both are declared) may fit the repo's contract-first instinct better.

Not fixed here: changing it means changing action:button's behaviour for every existing consumer of #844's flag, which is a design call, not a patch.

Related


Generated by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions