Repository navigation
finding(components): autoTrigger executes an action whose own declared visible gate hides it — in both renderers #4191
Description
Activity
Findings triage (first grade; premise check @
origin/maine1ade8f): HOLD —findingstands.Anchors verified live in
packages/components/src/renderers/action/action-button.tsx: the auto-trigger effect at:157(useAutoTriggerOnce(hasAutoTrigger(schema), handleClick)) sits above the visibility early return at:164(if (hasDeclaredVisibilityGate(schema.visible) && !isVisible) return null;) — so the ordering the card's probe measured is exactly what the source says. The parity pin it points at exists:packages/components/src/renderers/action/__tests__/action-overflow-autotrigger.test.tsx.Held rather than queued: no host in this repo composes
autoTriggeronto an action whose metadata also declares a falsevisible, so there is no live subject — and the card is explicit that the fix is a semantics choice, not a patch. That choice is genuinely three-way (gate it / keep today's precedence / refuse loudly when both are declared), and two of the three changeaction:buttonbehaviour for every existing consumer of #844's flag.Worth stating plainly for whoever grades this next: this is not an authorization finding. The runner's confirm / param / entitlement gates still apply; what is bypassed is the author's offer-ability verdict. That distinction is what keeps it observation-class rather than a security card, and it should survive re-grading intact.
Exit path: a decision card once the repo wants the semantics settled — the three options are already written out on this card, so escalation is cheap when someone wants it. Nothing blocks it today.
本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
Findings cadence re-check @
origin/main6314e87: HOLD —findingstands.Premise live:
packages/components/src/renderers/action/action-button.tsxregistersuseAutoTriggerOnce(hasAutoTrigger(schema), handleClick)at:157before the declared-visibility gate returns null at:165— hooks having already run, the auto-trigger effect fires even when the render is suppressed;action-menu.tsx's consumption point (:142/:264-273) shares the contract. Held: real but requires an authored action carrying bothautoTrigger: trueand a falsyvisiblegate — no in-repo example does; the fix (gate the trigger on the same visibility verdict) is small and should ride the auto-trigger family (#4162's shared./auto-triggermodule) rather than dispatch alone.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
Findings cadence re-check @
origin/main6d01319: HOLD —findingstands, premise live.Verified on main: the effect still precedes the visibility early-return (
action-button.tsx:157useAutoTriggerOnce(...)), and the parity pin naming this card is live ataction-overflow-autotrigger.test.tsx:296("the ACTION's own declared visible gate does not suppress it — and inline agrees with overflow"). Recent lane activity on the renderer family (#4207's forward-whitelist gate, #4169's retirement) did not touch this seam. Still unreachable in-repo — no host composesautoTriggeronto avisible:falseaction — and the runner's confirm/param/entitlement gates keep it short of an authorization hole.Held rather than escalated: the eventual disposition is a design ruling (host intent vs author verdict vs refuse-loudly), but a decision card with zero reachable producers would sit in the maintainer's inbox ahead of decisions users are actually waiting on. Restart condition: escalate to
needs-user-decisionthe moment any host (or the deep-link flow of #844's lineage) can compose the flag onto an action carrying a declared-falsevisible— at that point the third option (refuse loudly when both are declared) should be in the fork presented.本评论来自分诊座位 Routine(#5474 试点),不构成认领。
Generated by Claude Code
Findings state conversion (batch 3):
finding→pm:on-hold, type Task.Held three times without leaving the label — converting per the 2026-08-13 state-semantics ruling.
Hold rationale (unchanged): the semantics question (host intent vs author verdict vs refuse-loudly) is real but has ZERO reachable producers — no host in this repo composes
autoTriggeronto an action whose metadata declares a falsevisible— and a decision card with no reachable subject would queue in the maintainer's inbox ahead of decisions users are actually waiting on. Not an authorization hole (the runner's confirm/param/entitlement gates still apply); that framing should survive future re-grades.Restart-when: any producer appears that can compose
autoTriggeronto an action carrying a declaredvisiblegate (check: grepautoTriggercomposers in hosts/deep-link flows against today's set — currently only runner-initiated flows that just decided to run the action). On that trigger, escalate toneeds-user-decisionwith the three options already written on this card.Trigger files:
packages/components/src/renderers/action/auto-trigger.ts,packages/components/src/renderers/action/action-button.tsx,packages/components/src/renderers/action/action-menu.tsx.Hold date: 2026-08-17. 本评论来自分诊座位 Routine,不构成认领。
Generated by Claude Code
- addeddomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seat
on Aug 23, 2026 Triage:
domain:ui(lane only —pm:on-holduntouched). Landing:packages/components—autoTriggerexecuting an action whose own gate has not been consulted.Routed via the maintainer direct-dispatch channel, this session, verbatim: 「然后 批 4–5」. PM session
session_0124Qg8rLvpXnQDwCmpKUmaJ. objectui three-stream split (maintainer 2026-08-21); not a Routine triage fire — the triage seat may re-grade.
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actions裁定:A —— 作者声明的
visible优先,被隐藏的操作不因autoTrigger执行,并明确提示;回到队列,定级priority:p2分诊席(
session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T11:12Z。objectui 清理第七批(#8587 的挂起卡一次性清点):本席读完了卡面和评论(5 条,全部读到),并在本地 main(62597c5)上重新测量。本卡的重启条件已经满足,本席按它的约定把 A / B 交给维护者。维护者答:「objectui 第⑦批 同意」,即同意本席的建议 A。重启条件已经满足
5312337132约定:一旦出现能给"声明了visible"的操作打上autoTrigger的来源,就交维护者裁定。本地实测,这个来源已经存在:- Consume the declared nav
runActionslot — retire the private?runAction=string convention #5216 的通用导航深链:useNavRunAction从网址读取?runAction=<操作名>(参数名定义在packages/layout/src/NavigationRenderer.tsx:525)。 packages/app-shell/src/views/ObjectView.tsx:1412-1438只检查这个操作的locations是否包含list_toolbar(actionRendersAt,packages/types/src/ui-action.ts:85),不检查visible,然后给它加上autoTrigger: true。packages/components/src/renderers/action/action-button.tsx:287的自动执行 effect 仍在:294的visible提前返回之前。
⇒ 任何人在列表页网址后面加上
?runAction=<被隐藏的操作>,就能执行作者在当前上下文里隐藏掉的操作。这不是越权漏洞,但也不能放任
确认框、参数框、授权检查和服务端权限仍然生效,所以这不是越权(
5248001404的判断成立)。被绕过的是作者"这个操作在这里不该被提供"的判断,比如"只在某种记录状态下才显示"这类业务规则,而绕过它的只是一个传输层参数。裁定内容(A)
- 作者声明的
visible优先于autoTrigger:visible判为不可见时,不执行。 - ⛔ 不能悄悄吞掉:要明确提示"此操作在当前页面不可用",并在开发模式下给出诊断。这正是 finding(app-shell): useAutoRunCreate strips ?runAction=create_environment even when the toolbar has no create action, destroying the deep link unrecoverably #4123 / finding(components): an
autoTriggeraction that spills past action:bar's maxVisible lands in action:menu, which never runs it — deep link consumed, nothing triggered #4162 反复处理过的"静默无效"问题。 action:button和action:menu两个渲染器保持一致(finding(components): anautoTriggeraction that spills past action:bar's maxVisible lands in action:menu, which never runs it — deep link consumed, nothing triggered #4162 的对齐原则)。- 导航深链的准备阶段(
ObjectView调用useNavRunAction时判断能否执行的那一步)也要把visible算进去,不可见的操作不应消耗这次一次性的意图。 action-overflow-autotrigger.test.tsx里那条"the ACTION's own declared visible gate does not suppress it"的测试,改写为断言"会被拒绝并提示"。⛔ 不删测试,也不在树里留一条和本裁定相反的钉。
定级
priority:p2:可以通过网址绕过作者设置的可见性规则,但不涉及越权。摘pm:on-hold,加pm:queue,可以派发。
Generated by Claude Code
- Consume the declared nav
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsDeferred, not skipped — serialised behind objectui#10046 on
ObjectView.tsxdomain:uiseat #2,session_01LkCKMa5bvrw3L4ezcNXEXW. ⛔ No label, assignee or state change.Ruling A (
5812953871) makes this dispatchable, and it is the next pick for this seat once the region below frees up. Its fix has two halves, and the deep-link preparation half lands inpackages/app-shell/src/views/ObjectView.tsx(theuseNavRunAction/autoTriggercomposition around:1412-1438on the ruling's reading). That file is the expected landing file of objectui#10046, which is in flight on this seat. I can't be sure which region #10046 will edit, so the two run one after the other, not in parallel.Known for whoever takes it: the renderer half is
packages/components/src/renderers/action/{action-button,action-menu,auto-trigger}. Open PR objectui#8941 (dependency bump) touchesaction-bar.tsxandresolve-icon.tsin that directory, which is disjoint but adjacent. The pin inaction-overflow-autotrigger.test.tsxis to be inverted to assert the refusal, not deleted, per the ruling.readings taken 2026-09-24T13:35Z
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsClaim: PM loop round 1 —
domain:uiexecution seat 2
Session:session_01LkCKMa5bvrw3L4ezcNXEXW
Branch:claude/issue-4191-autotrigger-honours-visible
Worktree:objectui-issue-4191
Domain:domain:ui
Seat:domain:ui#2
File surface:packages/components/src/renderers/action/{action-button.tsx,action-menu.tsx,auto-trigger.ts}and their tests (incl.__tests__/action-overflow-autotrigger.test.tsx, inverted not deleted),packages/app-shell/src/views/ObjectView.tsx(theuseNavRunAction/autoTriggercomposition only) andpackages/app-shell/src/hooks/useNavRunAction.tsif the prep-stage gate lives there, the refusal message keys inpackages/i18nlocale packs if a new user-facing string is needed, one.changeset/4191-…md(stop on breach; explain in the report)
Container & model:M,mode:subagent,model: opus(default judgement tier;dispatch-gates.mjs --tierREFUSES for objectui — no path-derived mandate exists here)
Clause-②: no
Ruling-ref: 5812953871
Thread-read: 5815174898
Serial constraints cleared: objectui#10046 landed (5b6d177), releasingObjectView.tsx· open PR objectui#8941 touchesaction-bar.tsxandresolve-icon.tsin the same directory, disjoint files · no other open PR touches the surface (open-PR file lists read 2026-09-24T14:00Z) · seat 1's objectui#10024 holds thesearch.resultsCount*i18n keys, a different key blockClause-②
no: the ruling narrows a runtime behaviour (an action whose declaredvisiblehides it no longer executes onautoTrigger) and adds no accepted shape or exported surface.⚠️ It is a runtime gate change on an action surface, i.e. the human-floor class, and it is ruled (5812953871, maintainer 「objectui 第⑦批 同意」). Readings taken 2026-09-24T14:00Z.os-support-ai commented
on Sep 24, 2026 CollaboratorMore actionsos-dev-report
{
"issue": 4191,
"status": "done",
"branch": "claude/issue-4191-autotrigger-honours-visible",
"pr": "#10272",
"session": "session_01LkCKMa5bvrw3L4ezcNXEXW (dispatch-stamped; container CLAUDE_CODE_REMOTE_SESSION_ID=cse_01LkCKMa5bvrw3L4ezcNXEXW)",
"premise_still_valid": true,
"summary": "Ruling A is implemented. The shared useAutoTriggerOnce (components renderers/action/auto-trigger.ts) now takes the action plus the renderer's own visible verdict. A hidden action is not executed: it gets a toast.warning naming it (new key actions.notAvailableHere, all ten packs) and a dev-only console.warn diagnostic, each once per mounted action. action:button and action:menu both go through this one hook; the menu's headless ActionAutoTrigger computes its verdict through the same local hook as ActionMenuItem. A refusal does not use up the once-guard, so an action that becomes visible later still runs once. On the deep-link side, ObjectView now arms through the new internal useOfferedNavRunAction (app-shell hooks/useNavRunAction.ts). It evaluates the candidate with the renderer's own predicate, built from the same exported functions (hasDeclaredVisibilityGate plus fail-closed useCondition over usePredicateRecordContext(undefined) and the ambient scope). A hidden candidate is not composed with autoTrigger, the URL param is left in place, and the same notice is shown. A nine-row parity table checks the prep verdict against the real action:bar. The not-an-authorization-hole framing is in the PR body and the changeset. Mechanism assumptions, measured: (1) confirmed, base 0427036 ran the old pin green, i.e. execute despite hidden, in both renderers. (2) Confirmed that isVisible was already computed before the trigger in action:button. In action:menu it was NOT: the headless ActionAutoTrigger had no verdict, so it now computes one through useMenuActionVisible, a local hook shared with ActionMenuItem. No hook ordering problem arose. (3) Before this change, useNavRunAction consumed the param (stripped the URL) whenever the armed callback answered true; the callback checked placement only. The prep step has every renderer input it needs (ambient scope from the same tree position; no row, because the list-toolbar action:bar is mounted without data). No input is missing. (4) No existing refused-action notice channel was found in the #4123/#4162 lineage. The closest precedent is useNavActionDispatch (sonner toast plus console.warn). Components already toast through ui/sonner (form.tsx), so I reused that with a new i18n key. There are 10 locale packs; parity is enforced by all-locales-key-parity plus check:i18n-keys, both green. (5) Pin inversion done; the grep is in tests. Noticed while working: EnvironmentListToolbar's own prep step (outside the ruling's ObjectView clause and outside the file surface) still consumes on a hidden create_environment; the renderer now refuses and notifies there.",
"tests": "At final HEAD 23cbae7, after merging origin/main (the merge commit is e64fad5): the root run 'pnpm exec vitest run packages/components/src/renderers/action/ packages/i18n/' plus 41 app-shell files (every ObjectView importer and every useNavRunAction/autoTrigger test) gave 'Test Files 130 passed (130) / Tests 1903 passed (1903)', lock VERDICT command-exit 0. At e64fad5, full packages/components/ plus packages/i18n/ gave 356/357 files, with the one failure being de-quote-pairing-3876 (62 to 63 matched pairs, caused by the new de value). That was fixed in 23cbae7 and i18n re-ran 70/70. Premise before the change: base 0427036 ran action-overflow-autotrigger 12/12 including the old execute-despite-hidden pin. Ablation 1 (committed state, objectstack scripts/ablation-replace.mjs, anchor hit 1 to 0, blob dcd4daf8cc72 to 5cbfef12012f): auto-trigger.ts 'const hidden = ...' replaced with 'const hidden = false;' gave 3 failed / 12 passed (the three refusal tests); restored, blob == HEAD and git diff HEAD empty. Ablation 2 (blob df480aa988ce to e0c4fec930c5): the useNavRunAction.ts arm condition had '&& !hidden' removed, giving 6 failed / 8 passed (the hidden-candidate test, the once-notice test, and 4 parity rows); restored, git diff HEAD empty. The components vitest alias resolves to src, so the ablations needed no dist leg. Pin-inversion grep over tracked files, .changeset excluded: 'gate does not suppress it' has 1 hit on base (control lit) and 0 on HEAD; 'execute=1' has 1 hit (the historical note in the inverted test); 'gate does not suppress the trigger' has 1 hit (packages/components/CHANGELOG.md, published history, not edited); 'does not suppress' has 10 hits, none of them pins; autoTrigger together with visible in test files is only in the inverted file. Extra probe (not committed): an action:icon inline member ignores autoTrigger, with icon_execute=0 against the control button_execute_control=1.",
"gates": [
"vitest (root, targeted, HEAD 23cbae7): exit 0, 130 files / 1903 tests",
"type-check @object-ui/components @object-ui/app-shell @object-ui/i18n: exit 0 (after turbo build of the app-shell^... closure); --listFiles: both edited/new test files are included (1/1)",
"lint (eslint .) same three packages: exit 0, 0 errors",
"check:vi-mock-specifiers 0 · check:vi-mock-inherit 0 · check:vi-mock-override-shape 0",
"check:changeset-claims 0 · check-changeset-presence.mjs 0 · check:pending-changeset-literals 0",
"check:i18n-keys 0 · check:i18n-drift 0 · check:i18n-dead-keys 0 · check:i18n-designer-parity 0",
"check:new-line-citations 0 (0 new) · check:control-bytes 0 · check:action-forward-parity 0 · check:test-path-roots 0 · check:unreferenced-sources 0",
"check:eager-locale-catalogues 0 and check:eager-closure 0: the first run gave exit 2 with PREREQUISITE NOT MET (no console dist), then I built console and re-ran both green",
"NOT MEASURED: the full test farm and every other check:* gate; CI runs them"
],
"line_budget": "n/a: no skills/** or line-ratcheted ledger touched. Diff vs origin/main: 19 files, +633/-68",
"files_changed": [
".changeset/4191-autotrigger-honours-visible.md",
"packages/app-shell/src/hooks/tests/useOfferedNavRunAction.test.tsx",
"packages/app-shell/src/hooks/useNavRunAction.ts",
"packages/app-shell/src/views/ObjectView.tsx",
"packages/components/src/renderers/action/tests/action-overflow-autotrigger.test.tsx",
"packages/components/src/renderers/action/action-button.tsx",
"packages/components/src/renderers/action/action-menu.tsx",
"packages/components/src/renderers/action/auto-trigger.ts",
"packages/i18n/src/tests/de-quote-pairing-3876.test.ts",
"packages/i18n/src/locales/ar.ts",
"packages/i18n/src/locales/de.ts",
"packages/i18n/src/locales/en.ts",
"packages/i18n/src/locales/es.ts",
"packages/i18n/src/locales/fr.ts",
"packages/i18n/src/locales/ja.ts",
"packages/i18n/src/locales/ko.ts",
"packages/i18n/src/locales/pt.ts",
"packages/i18n/src/locales/ru.ts",
"packages/i18n/src/locales/zh.ts"
],
"deviations": [
"File surface grew by one: packages/i18n/src/tests/de-quote-pairing-3876.test.ts, where a count pin went from 62 to 63 because the new de value adds one matched quote pair (precedent: the same pin grew by comment each time a quoted key was added). The claim's surface named locale packs only.",
"One changeset FILE declares a patch for all three touched published packages (components, app-shell, i18n). The claim said 'one .changeset/4191-...md' and the dispatch said 'one patch changeset per touched package'; this form satisfies both.",
"The first POST /pulls was refused with 415 because the Content-Type header was missing. Nothing was stored. It was resent with the header and returned 201. It is counted in api_writes.",
"Zero label writes: objectui has no gate that reads dev-applied labels, and the dispatch named none.",
"PR footer: I used the os-dev.md session-URL footer rather than the harness attribution lines, because os-dev.md takes precedence."
],
"mcp_calls": "0",
"api_writes": "3: POST /repos/objectstack-ai/objectui/pulls (415, not stored), POST /repos/objectstack-ai/objectui/pulls (201, draft PR 10272), POST /repos//issues/4191/comments (this os-dev-report). Plus git push of the branch (not REST). Reads were REST GETs only.",
"open_questions": [],
"out_of_scope_findings": [
"class: a · action:icon (packages/components/src/renderers/action/action-icon.tsx) does not consume autoTrigger. action:bar hands an inline member with component 'action:icon' the composed flag and it never runs. Probe on this branch: PROBE-ICON icon_execute=0, control button_execute_control=1. Reachable through the ObjectView deep link, which consumes the param, for any list_toolbar action authored with component action:icon; this is the #4162 signature on a third renderer. dedupe words: action:icon autoTrigger · icon deep link runAction · autoTrigger inline icon member",
"carrier: none (承接者:无) · EnvironmentListToolbar's useAutoRunCreate prep step consumes the deep link without evaluating create_environment's own visible. The ruling's prep clause named ObjectView only; the renderers now refuse and notify there. Recorded in the PR's Acceptance notes, not filed.",
"carrier: none (承接者:无) · ObjectView's prep step does not apply action:bar's capability gate (requiredPermissions), which EnvironmentListToolbar applies through useCapabilityGate. Pre-existing, observation only; recorded in the PR's Acceptance notes."
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actionsReview — ACCEPT · PR objectui#10272 (head
23cbae7)domain:uiseat #2,session_01LkCKMa5bvrw3L4ezcNXEXW. Checked against GitHub (PR metadata, diff) and the branch tree, not against the report's own account. Implements ruling A (5812953871).check reading form draft · base main· body first lineFixes #4191· 19 files, +633/−68 (most are the ten locale packs' one new key)renderers the shared useAutoTriggerOnce(auto-trigger.ts) now takes the action plus the renderer's ownvisibleverdict: hidden ⇒ not run, onetoast.warning(actions.notAvailableHere, all ten packs) + a dev-onlyconsole.warnnaming the predicate.action:buttonandaction:menuboth go through it; the menu's headlessActionAutoTriggernow computes a verdict through the same local hook asActionMenuItem⇒ #4162 parity kept. A refusal does not spend the once-guarddeep-link prep the new useOfferedNavRunActionevaluates the candidate'svisiblewith the same exported predicate the renderer uses (hasDeclaredVisibilityGate+useCondition(…, { throwOnError: true })overusePredicateRecordContext(undefined)). A hidden candidate is not composed withautoTrigger, and the one-shot URL intent is not consumed (ruling clause 4). Nine parity rows pin prep against the realaction:barfault posture, checked by the seat useCondition'sthrowOnErrorleg catches and answersfalse(packages/react/src/hooks/useExpression.ts) ⇒ a faulting predicate hides and reports, and never crashes the page; the prep step and both renderers pass the identical option ⇒ no driftpin the "does not suppress it" pin is inverted to refusal + notice, not deleted (grep: 1 hit on base, 0 on head; lit control) evidence ablations: the renderer gate removed ⇒ 3 refusal tests red; the prep !hiddenremoved ⇒ 6 red (incl. 4 parity rows)framing "not an authorization hole" (confirm / params / entitlement / server permissions still apply) is in the PR body and the changeset Clause-② noholds: a narrowing of runtime behaviour, no accepted shape or export widenedchangeset-claim bot (report-only): the three pending changesets it names were re-read against this diff.
6306scopestype: schemaicon resolution (this PR touchesaction-menuforautoTrigger, not icon typing),6661describes threeconsole.navkeys (this PR addsactions.notAvailableHere, a different block), and7070describes the gantt view-options branch ofObjectView.tsx(this PR touches the runAction composition). ⇒ each sentence stays true; no body correction owed.Out of scope:
action:icondoes not consumeautoTriggerat all (the #4162 signature on a third renderer, probeicon_execute=0vs control1) ⇒ filed as its own card. TheEnvironmentListToolbarprep step andObjectView's missing capability gate are recorded as Acceptance notes in the PR (outside the ruling's named surface).readings taken 2026-09-24T15:01Z
Generated by Claude Code
objectstack-fleet commented
on Sep 24, 2026 ContributorMore actions✅ Landed — PR objectui#10272, squash
978507bonmain, card cleaneddomain:uiseat #2,session_01LkCKMa5bvrw3L4ezcNXEXW. Verified by content onorigin/main, not by sha alone:useOfferedNavRunActionis present inuseNavRunAction.ts(2 hits) andObjectView.tsx(3 hits);- the gate
hasDeclaredVisibilityGate(action.visible) && !isVisibleis present inauto-trigger.ts(1 hit); lit control in the same file,useAutoTriggerOnce: 1 hit; - the old pin text "gate does not suppress it" has 0 hits under
renderers/action/__tests__/— inverted, not deleted.
Closed by
Fixes; this stroke stripspm:dispatchedand the assignee. Theaction:iconsibling is carried by objectui#10274.readings taken 2026-09-24T15:27Z
Generated by Claude Code
- added a commit that references this issue
on Sep 28, 2026
Measured while implementing #4162 (making
action:menuconsumeautoTrigger). 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 inaction:button.Fact
action:buttondeclares its auto-trigger effect before itsvisibleearly return:React commits effects for a component that returns
null, so an action carrying bothautoTrigger: trueand a declared-falsevisiblerenders nothing and executes anyway. Measured onorigin/mainate16fd9597, realaction:buttonplus the real runner:Empty DOM, one execution. #4162 extends the same semantics to
action:menudeliberately — 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 inpackages/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:baroraction:menuauto-triggers nothing. That one is pinned too.Why it may matter
autoTriggeris 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 falsevisible, 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:
visibleis the metadata author's verdict on whether this action may be OFFERED here (fails closed on a throwing predicate, perActionEngine.getActionsForLocation);autoTriggeris 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
packages/components/src/renderers/action/action-button.tsx,action-menu.tsx, and the shared guardauto-trigger.tsautoTriggeraction that spills past action:bar's maxVisible lands in action:menu, which never runs it — deep link consumed, nothing triggered #4162autoTrigger/ visible gate /action:menu) — no duplicate. Unassigned.Generated by Claude Code