Skip to content

finding(components): an action:bar inline action authored with component: 'action:menu' or 'action:group' renders nothing — the action vanishes from the toolbar and a ?runAction= deep link to it runs nothing #10345

Description

@objectstack-fleet

Ruled: 5825579152 · letter A (menu/group = placement on the bar) · 2026-09-25T02:17Z

Filing-gate category: ① a product defect with named sites and a reproduction. Reader: triage first (route and grade), then the domain:ui execution seat that claims it. The sites: packages/components/src/renderers/action/action-bar.tsx (the inline member dispatch) and the action:menu / action:group renderers.

Filed by the domain:ui#4 execution seat (session_01BP8CMtACxTdLjqR6rhd33C) from the os-dev-report of objectui#10274 (PR objectui#10342). The dev measured it; ⛔ it is filed bare, not graded here.

The defect

action:bar renders each inline action with the renderer its component names (action.component || 'action:button', then ComponentRegistry.get), and spreads the single action onto that renderer's schema. The spec's ActionSchema.component enum offers 'action:menu' and 'action:group', and the metadata-admin ActionDefaultInspector offers them as "Menu item" and "Button group". Both renderers read schema.actions, which a single action does not carry, and return null (action-menu.tsx: if (actions.length === 0) return null;). So:

  • the action disappears from the toolbar with no error and no author diagnostic;
  • a ?runAction= deep link to it is armed and consumed by the host, then runs nothing.

Reproduction (the dev's probe at PR head 3b6341b, not committed)

The setup is one action:bar with an unflagged control action plus one member flagged autoTrigger, with the member's component varied:

member component executions buttons rendered
action:button 1 Control, Member
action:icon (after PR objectui#10342) 1 Control, Member
action:menu 0 Control
action:group 0 Control

⚠️ These readings are the dev's, relayed by the seat, ⛔ not re-run by the seat.

Grading notes (for triage, not a grade)

  • Authorable today through a spec enum member and a designer option, and the failure is silent.
  • The fix direction is open. The bar could render a single-action member of these kinds as a button; the spec could refuse component: 'action:menu' | 'action:group' on a single action; or the renderers could accept a single action. That may be a small decision.

Seam: spec:ActionSchema.component ('action:menu' | 'action:group') → renderer:action:bar inline member dispatch → action:menu / action:group (read schema.actions, absent on a single action ⇒ null)

Dedupe

REST page walk over the 1000 most recently updated objectui items, pattern (action:menu|action:group) … (inline|member|bar) … (null|nothing|vanish|renders no) / action:bar … component … (menu|group) ⇒ 0 hits. Control pattern action:icon … autoTrigger ⇒ objectui#10274, the must-hit control, which did hit.

Dedupe words: component action:menu bar member renders nothing · action:group inline member vanishes · action:bar component override · autoTrigger menu member


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    分诊首次定级:priority:p2 · bug · domain:ui · needs-user-decision —— 工具栏里把一个动作的显示方式设成「下拉菜单」或「按钮组」,这个动作就从工具栏上消失了,深链接也执行不了;设计器里明明提供这两个选项,而「单个动作设成菜单是什么意思」需要维护者定

    Path: packages/components/src/renderers/action/action-bar.tsx(按 action.component || 'action:button' 取渲染器,把单个动作铺进它的 schema)→ action-menu.tsx(if (actions.length === 0) return null;)/ action:group

    Triage: lands in @object-ui/components ⇒ domain:ui, bug, priority:p2, needs-user-decision (finding removed — graded); rationale: @objectstack/spec's ActionSchema.component enum offers 'action:menu' ("Dropdown menu") and 'action:group' ("Button Group") and the metadata-admin inspector offers both, but action:bar hands a SINGLE action to renderers that read schema.actions and return null, so the action silently vanishes and a ?runAction= deep link runs nothing; the repair turns on what those two values MEAN for a single action (overflow placement, a one-button menu/group, or refusal in the spec), which is a contract-meaning call for the maintainer.

    分诊席 #6015,2026-09-24T19:26Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectui origin/main 1f8ef0a8 和 objectstack origin/main b81da66df7 上核对。

    本席核对

    需要维护者决定:单个动作的 component: 'action:menu' | 'action:group' 是什么意思

    • A(本席建议):当成「放在哪里」来理解。
      • action:menu 表示把这个动作放进工具栏已有的「更多」下拉菜单;
      • action:group 表示把它和相邻动作渲染成一个按钮组。
      • 理由:spec 注释说的正是「按位置默认是按钮或菜单项,可以覆盖」,工具栏也已经有这个溢出菜单,改动集中在 action-bar.tsx 的分发逻辑,不改契约。
    • B:让菜单和按钮组的渲染器也能接受单个动作,渲染成「只有一项的下拉菜单」和「只有一个按钮的按钮组」。改动小,但用户看到的形状很奇怪。
    • C:在 spec 里拒绝单个动作使用这两个值。 这是收窄契约,要改 objectstack 的 spec 和设计器的选项,而且已经写成这样的元数据会在校验时失败。

    ⇒ 不管选哪个,今天的「静默消失」都必须消除。

    定级说明

    p2:设计器里可选、spec 里合法,结果动作静默消失,深链接也什么都不执行。不给 p1:不涉及数据或权限,只有专门选了这两个值才会触发。

    裁决后的执行要点

    1. action-bar.tsx 正被 finding(components): action:icon never consumes autoTrigger — a ?runAction= deep link to an icon-rendered toolbar action is armed, consumed, and does nothing (the #4162 signature on a third renderer) #10274 的 PR fix(components): action:icon consumes autoTrigger through the shared hook (objectui#10274) #10342 修改(action:icon 成员),⛔ 排在它后面,或者在它落地之后再动手。
    2. 把卡面的四行探针表做成测试:按钮、图标、菜单、按钮组四种成员,执行次数和渲染出的按钮都要钉住。
    3. 深链接 ?runAction= 指向这类成员时,必须能执行。

    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 24, 2026
  3. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Ruling: batch #224 item 1 · letter A (a single action's action:menu / action:group is a PLACEMENT) · maintainer 「批 #224 同意」 (chat, director seat summon #29, session_01EcrTi7s5oDYPHS4Pi7h31d) 2026-09-25T02:15Z

    Director seat, summon #29. Presented with recommendation A (fallback C); the maintainer agreed. Thread re-read to its last comment (5820756170) in this act.

    Ruled: A. On an action:bar, a single action's component is read as where it goes, which is the spec's own comment: "Defaults to 'button' or 'menu_item' based on location, but can be overridden".

    • action:menu puts the action into the bar's existing single overflow menu (the one "More" button).
    • action:group renders it in a button group with its adjacent action:group members.
    • The dispatch lives in action-bar.tsx. ⛔ No contract change, and ⛔ the menu and group renderers are not taught to accept a single action (B).
    • The silent vanish is gone: every member renders somewhere, and a ?runAction= deep link to it runs.
    • Pins: the card's four-row probe (button, icon, menu, group), with run count and rendered placement for each, plus the deep-link case.
    • What counts as "adjacent" for action:group is the dev's to measure and state in the PR.

    Execution: needs-user-decision → pm:queue in this stroke. Serial after objectui#10274 / PR #10342 (it also edits action-bar.tsx). priority:p2, bug and domain:ui stand.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 — domain:ui execution seat 4
    Session: session_01BP8CMtACxTdLjqR6rhd33C
    Branch: claude/issue-10345-action-bar-menu-group-placement
    Worktree: objectui-issue-10345
    Domain: domain:ui
    Seat: domain:ui#4
    File surface:

    • packages/components/src/renderers/action/action-bar.tsx: the inline member dispatch and its docblock. It reads a single action's component: 'action:menu' as a place in the bar's one overflow menu, and 'action:group' as a place in a button group with its adjacent action:group members.
    • Tests beside it under packages/components/src/renderers/action/__tests__/.
    • One .changeset/10345-…md.

    Stop on breach; explain in the report. Per the ruling, the action:menu / action:group renderers are ⛔ not taught to accept a single action, and no contract moves.
    Container & model: M, mode:subagent, model: opus — executes ruling 5825579152 (letter A)
    Clause-②: no
    Thread-read: 5825579152
    Serial constraints cleared:

    • The predecessor, objectui#10274's PR objectui#10342 (the last holder of action-bar.tsx), merged 2026-09-24T19:34Z.
    • The open-PR file lists read 2026-09-25T02:44Z show one hit, draft PR objectui#8941 (lucide bump). It edits one line of the docblock usage example in action-bar.tsx (the trash-2 icon), so it is hunk-disjoint from the member dispatch; this claim leaves that example line alone.
    • The live pm:dispatched claims of the other seats, read 2026-09-25T02:43Z, name no file under packages/components/src/renderers/action/.

    Clause-②: no. The spec's ActionSchema.component enum already accepts both values; the bar starts honouring them as placement, as the spec's own comment says. No declared key, schema, export or accept set moves.


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10345,
      "status": "done",
      "branch": "claude/issue-10345-action-bar-menu-group-placement",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10470",
      "session": "session_01BP8CMtACxTdLjqR6rhd33C — the dispatching seat's session; this subagent run carries the parent's id (the commit Claude-Session trailers and the fleet-write route derived from CLAUDE_CODE_REMOTE_SESSION_ID both name it)",
      "premise_still_valid": true,
      "summary": "Ruling A is carried entirely inside action-bar.tsx. A member authored component 'action:menu' is taken out before the maxVisible split and placed in the bar's one existing overflow menu. A member authored 'action:group' renders inline through the action:button renderer, inside the existing custom ButtonGroup primitive (role=group), shared with the adjacent action:group members of the drawn inline row; the docblock's opening paragraph now says this, and the trash-2 example line is untouched. A2 measured: action-group.tsx draws its own InlineActionButton, which consumes no autoTrigger and gates visible fail-soft, so routing grouped members through that renderer would leave the deep link dead. Having the bar draw the group, with action:button members, is the compliant route inside the file surface; no needs_decision fork was needed, action-menu.tsx and action-group.tsx are untouched, and no contract moved. A1 held (the menu's existing ActionAutoTrigger runs a menu-placed member's deep link). A3: menu-placed members do not count toward maxVisible. A4: the overflow order is the spilled actions, then the menu-placed ones, each in bar order, then the separator and the system actions (page:header's relative order). A5 is pinned for both placements. One pending changeset sentence was corrected (see tests). The coordinator's resume note said no branch existed; in fact the empty-branch probe push had landed at 0348bc9f1 before the 429 cut, and the work continued on it.",
      "tests": "All readings are at HEAD da122c4cf. (1) New suite action-bar-member-placement-10345.test.tsx: 21/21 passed. It holds the card's four-row probe (button, icon, menu and group rows each read 1 run, placed inline / inline / More menu / button group), the deep-link rows (armed the way ObjectView maps ?runAction=, run once, re-renders with fresh objects do not re-run, unflagged runs nothing), A3/A4 accounting and order, 'adjacent' semantics, and A5 gates (hidden menu member stays hidden and its deep link is refused with the objectui#4191 notice; hidden group member not drawn; disabled menu item; requiredPermissions at both placements, each with a held-capability control leg). (2) Reverse proof, run under trap-restore from the committed fix: action-bar.tsx was replaced with its BASE 0348bc9f1 blob, removing the placement branch. On disk before: inlineRow=2, inlineActions.map=0; after: inlineRow=0, inlineActions.map=1, and the hash e986327a equalled the base blob. The suite read 'Tests 19 failed | 2 passed (21)'. The 2 passing are the action:button/action:icon probe rows, which are correct in both worlds. The menu/group probe rows read 'expected vi.fn() to be called 1 times, but got 0 times'. The restore used git checkout HEAD; the hash dd60d8a9 equalled the HEAD blob and git diff HEAD was 0 lines. The first ablation run exposed the two requiredPermissions rows as vacuous (they stayed green because the defect also reads absent), so held control legs were added and the ablation re-run. (3) Targeted vitest over 38 files (32 @object-ui/components suites: every one naming action-bar, action:bar, action:menu, action:group or useAutoTriggerOnce, plus every file under renderers/action/__tests__; 4 app-shell deep-link consumers: EnvironmentListToolbar deepLinkArming/deepLinkOverflow/main and useOfferedNavRunAction; 2 script pins naming action:bar): 'Test Files 38 passed (38) / Tests 668 passed (668)', lock VERDICT command-exit 0. (4) Closure build pnpm --filter '@object-ui/components^...' build: exit 0, 8 of 47 projects. Then pnpm --filter @object-ui/components type-check (tsc --noEmit AND tsc -p tsconfig.test.json): exit 0, and --listFiles confirms both configs include action-bar.tsx and the new test. (5) eslint on the 2 touched source files with the repo config: 0 errors; 5 warnings, the same 5 as on the BASE blob, all on untouched lines. (6) check-changeset-presence 0, check-changeset-no-major 0, check:new-line-citations 0 (0 new citations), check:control-bytes 0, check:changeset-claims 0 ('No pending changeset names a file this change touches'), check-vi-mock-override-shape 0, check-test-path-roots 0, check-lint-coverage 0. (7) Changesets: new .changeset/10345-action-bar-menu-group-placement.md ('@object-ui/components': patch). The pending objectui#10274 changeset's sentence 'action:bar renders each inline action with the renderer its component names' became false (an inline action:group member now renders through action:button), so it was narrowed to action:button / action:icon, body only; the front-matter md5 is unchanged. The pending 6306, 7415, 4191 and 5493 changesets were read and judged still true (their spread and 'decided by maxVisible' sentences describe members whose component does not place them; the literal 'type: componentType, actionType: action.type' spread is kept). Local gates were hand-derived from objectui package.json and lint.yml, since dispatch-gates.mjs does not cover objectui. NOT MEASURED: the ButtonGroup's rendered look in a real browser (jsdom computes no Tailwind, so the attached styling and empty:hidden were not observed). The CI farm (pnpm lint, full pnpm test shards, Build & E2E) is left to CI; its state is in_progress/unread at report time.",
      "mcp_calls": "0 — no MCP GitHub tools called",
      "api_writes": "2 — both via the fleet-write relay as objectstack-fleet[bot] (each one repository_dispatch POST /repos/objectstack-ai/objectstack/dispatches): (1) pr_create, i.e. POST /repos/objectstack-ai/objectui/pulls, draft=true, opening #10470, relay run 36092791117 concluded success; (2) this os-dev-report comment, POST /repos/objectstack-ai/objectui/issues/10345/comments via post-stamped.mjs. Plus git push (not REST): the empty-branch probe, then three pushes of the branch. No label writes (objectui labels are path-synced by labeler.yml and the dispatch named none). No PR body PATCH.",
      "open_questions": [],
      "out_of_scope_findings": [
        "carrier: 承接者:无 · noted, not filed (dormant, zero producer) — action:group's own leaves (InlineActionButton, DropdownActionItem in action-group.tsx) consume no autoTrigger, while auto-trigger.ts states 'every renderer that can receive an action consumes it'. No host sends a flagged action into a standalone action:group today; the ?runAction= host (ObjectView, EnvironmentListToolbar) composes an action:bar. Recorded in PR #10470 Acceptance notes.",
        "carrier: 承接者:无 · noted, not filed (pre-existing observation) — an overflow whose every entry is hidden by its own visible still renders an empty More trigger; this was already true for spilled actions before this change. Recorded in PR #10470 Acceptance notes."
      ]
    }

    Generated by Claude Code

  6. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10470 at da122c4, landing now (ready → merge queue)

    domain:ui seat #4, session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report 5826498127 and checked it against the diff, the tree and CI. The contract-review record is on the PR. This executes ruling 5825579152 (letter A): on an action:bar, a single action's component is its PLACEMENT.

    Implemented-by:  claude/issue-10345-action-bar-menu-group-placement
    Reviewed-by:     session_01BP8CMtACxTdLjqR6rhd33C
    
    item reading
    action:menu The member leaves the row candidates before the maxVisible cut and joins the bar's one existing overflow menu, after the spilled actions and before the separator and system actions (page:header's relative order). It spends no inline slot, and an overflow holding only such members still draws exactly one More button. Its deep link runs through the menu's existing per-action ActionAutoTrigger (A1)
    action:group Consecutive action:group members of the drawn inline row (after ordering, the maxVisible cut and menu placement) render through the action:button renderer inside the repo's ButtonGroup primitive (role="group"). [G1, G2, Plain, G3] gives two groups. A group tail cut by maxVisible lands in the More menu as plain items. That is the dev's measured reading of "adjacent", as the ruling left it
    why not the action:group renderer Its leaves consume no autoTrigger and gate visible fail-soft, so routing members there would leave the ruling's deep-link requirement unmet. Fixing that means editing action-group.tsx, outside the claim. The bar-drawn group is the in-surface compliant route: the reviewer judged it within the ruling's letter, not a quiet fork. ⛔ action-menu.tsx and action-group.tsx are untouched, and no contract moved
    gates requiredPermissions, visible (fail-closed) and disabled gate a relocated member exactly as they gate an inline one (A5)
    unchanged bars A bar with no menu or group member renders the same element tree: the same slices, keys, spread and data
    pins 21 cases on the real registered renderers under a real ActionProvider, with no module mocks. Reverting the file to base gives 19 red and 2 green (the button and icon rows, correct in both worlds). The two requiredPermissions rows carry held-capability control legs, so they are not vacuous
    surface The claim's three items, plus .changeset/10274-action-icon-auto-trigger.md: body only, frontmatter sha256 identical at base and head. Ratified here. Its sentence "action:bar renders each inline action with the renderer its component names" was true at base and becomes false with this PR, because an inline action:group member now renders through action:button. Correcting it in the same PR is what .claude/agents/os-dev.md requires. The trash-2 example line is byte-identical, which keeps it hunk-disjoint from draft PR objectui#8941
    changesets Every sentence is true at head. patch on @object-ui/components. The pending 6306, 7415, 4191 and 5493 changesets stay true
    Clause-② no. Both enum values were already legal and now render. No export or accepted set moves
    boundary Fixes #10345 is the only closing keyword, and there are no model identifiers
    CI 43 check-runs on da122c4: 40 success, 3 skipped by design, 0 red

    Out of scope

    Acceptance notes (not filed)

    • The look differs. A bar-drawn group is the ButtonGroup primitive, an attached segmented control. A standalone action:group renders a gapped row. The ruling's "a button group" is met by the repo primitive of that name. The rendered look and the all-members-hidden empty:hidden collapse were not observed in a real browser (jsdom).
    • An overflow whose every entry is hidden by its own visible still shows an empty More trigger. This was already true for spilled actions, and a menu-placed member can now reach it too. Dropped: pre-existing, and a hidden entry's gate is doing its job.
    • action-icon.tsx and action-icon-autotrigger.test.tsx keep a comment saying the bar renders an inline member with the renderer its component names. It is still true for action:icon members, but it reads more general than it is. Dropped: comment wording only.

    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

No one assigned

    Labels

    bugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions