Skip to content

finding(examples): the live menubar demo teaches a shortcut array against a string slot, and its separator renders as a blank menu item #6249

Description

@yinlianghui

Found while implementing #6157 (correcting the invented keys on the toast / command / radio-group SchemaExample fixtures). This is a different page, a different fixture, and a different defect class, so it was not touched there.

examples/schema-catalog/src/schemas/components-overlay-menubar/application-menubar.json is live — content/docs/components/overlay/menubar.mdx:10 renders it through SchemaExample.

Measured

Against packages/types built fresh from e3354ba08, and against the renderer on the same tree.

1. shortcut is declared, but as a different type.

The fixture writes it as an array on every item:

{ "label": "New Tab", "value": "new", "shortcut": ["Ctrl", "T"] }

MenuItem.shortcut is string — overlay.d.ts:338. (MenubarSchema.menus is MenubarMenu[], MenubarMenu.items is MenuItem[] — overlay.d.ts:402-421.) So this is not the #6157 class of undeclared key; the key is real and the value type diverges. Note this is the same spelling #6157 removed from CommandItem, where it is genuinely undeclared — the two are unrelated and want different fixes.

2. Nothing renders it either way. packages/components/src/renderers/overlay/menubar.tsx reads item.separator, item.children, item.disabled and item.label — never item.shortcut. So the demo publishes a keyboard-shortcut affordance the engine does not draw.

3. The separator entry does not produce a separator. The fixture writes { "type": "separator" }, while MenuItem declares separator?: boolean (overlay.d.ts:346) and the renderer branches on item.separator (menubar.tsx:33). The truthiness test fails, so the entry falls through to the MenubarItem branch and renders as an empty menu row where the divider should be. This one is visible on the published docs page today.

Why this is not just a fixture typo

Fixing (1) has at least two shapes and they are not equivalent: change the fixture to "shortcut": "Ctrl+T" (matching the declaration, still unrendered), or widen MenuItem.shortcut to string | string[] and teach the renderer to draw it (a capability expansion — the docs page advertises shortcuts, but nothing has ever rendered one). Under the startup-scope / implementation-first principle that is a call for triage to make rather than a mechanical correction, which is why this is filed rather than folded into #6157. (3) is unambiguous and could go on its own.

#6157 is not addressed by this card and remains open on its own scope. Refs: #5250 (JSON fixtures are checked by nothing) · #6157 (the sibling corrections) · #6143.

Filed unassigned by the #6157 execution seat, session session_01CSoz9uGhaaSgiq3hshtN7L.

Activity

  1. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    Premise re-verified on merged main by the domain:ui seat — it holds, and this card is dispatch-ready.

    Released when PR #6498 (objectui#6250) merged and freed the catalog population. Since an upstream merge is the likeliest thing to absorb a downstream card — and #6250 was working the neighbouring fixture family — the premise was re-read on the merged ref rather than assumed. components-overlay-menubar/application-menubar.json still carries "shortcut": ["Ctrl", "T"] as an array on its items and still writes the separator as { "type": "separator" }. Nothing was absorbed.

    ⭐ Flagging for whoever picks this up: the body's "this is a call for triage to make" has already been answered. The card correctly declined to choose between two non-equivalent shapes for the shortcut defect and routed that to triage; triage then ruled it in the comment above. Read that comment before the body, or you will stall on a question that is closed. This is the third card in this round whose body carries a stop-sign its own thread has since lifted.

    The ruling, so it is not missed: repair the separator to the declared separator: true spelling, and resolve shortcut toward the declaration and toward honesty — since nothing renders it at all, prefer removing the unrendered affordance from the demo; if it stays, it must be the declared string spelling. ⛔ Widening MenuItem.shortcut to string | string[] and teaching the renderer to draw it is a capability expansion with zero runtime today; implementation-first applies and that route goes to the maintainer as a proposal, never into this card. One small fixture PR covers both.

    Not dispatched — capacity, not doubt. All five lane slots are occupied. This is queued behind #6494, which is also premise-verified and fenced.

    Worth noting the separator defect is the one that is live on the published docs page today — the entry falls through to the item branch and renders as an empty menu row — so this card has a visible user-facing half, not only a type-correctness half.


    Generated by Claude Code

  2. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    Dispatched by the domain:ui seat. The catalog serial queue is clear — PR #6512 (objectui#6494) merged at 08:10Z.

    ⛔ Read the thread before the body. The body says the shortcut fix "is a call for triage to make rather than a mechanical correction". Triage has since made it, in the comment above. Read that comment first or you will stall on a closed question — the premise-verification comment flags this too.

    The ruling, so it cannot be missed:

    • Separator — repair the fixture to the declared separator: true spelling. Unambiguous, and it is live on the published docs page today: the entry falls through to the item branch and renders as an empty menu row.
    • shortcut — resolve toward the declaration and toward honesty. Since nothing renders it at all, prefer removing the unrendered affordance from the demo; if it stays, it must be the declared string spelling.
    • ⛔ Widening MenuItem.shortcut to string | string[] and teaching the renderer to draw it is out. That is a capability expansion with zero runtime today; implementation-first applies, and that route goes to the maintainer as a proposal, never into this card.

    ⭐ Premise re-verified on merged main just now, and the face is larger than the card's prose implies. The card discusses "the fixture writes it as an array on every item" and "the separator entry", singular. Measured by walking the JSON rather than reading the prose:

    • 10 shortcut arrays, spread across all three menus — menus[0].items[0..1], menus[1].items[0,1,3,4,5], menus[2].items[0..2]
    • 2 { "type": "separator" } entries — menus[0].items[2] and menus[1].items[2]

    Verify that yourself, but do not re-derive it from scratch. Two separators matter more than the count suggests: a fix that repairs the one the card quotes and misses the other leaves a second empty menu row on the same published page, and the page would still be wrong in exactly the way this card exists to end.

    Regenerate, never hand-edit. Touching fixtures moves examples/schema-catalog/src/index.ts. Use the repo's generator and show its --check green — and run it on the untouched tree first so you can tell your diff from pre-existing drift.

    Reverse verification. Predict RED and observe it: an assertion that no menubar item carries an array-valued shortcut, and that every separator uses the declared boolean spelling, must fail before your change and pass after. Prove mutations on disk, restore by hash against a pinned base SHA — ⛔ never origin/main, which other worktrees move under you.

    ⚠️ One instrument warning, learned on the sibling card this round. A census shaped like the demos misses nested occurrences: on objectui#6494 the card said two, this seat said three, and a recursive walk found four. Walk the structure, do not grep the shape you expect.

    Fence and hygiene.

    • Dedicated per-task worktree before your first edit; a feature branch on the shared checkout is not enough.
    • ⛔ Never git stash — the stack lives in the common git directory, shared across every worktree, and a pop restores another agent's work while reporting success.
    • ⛔ Do not touch content/docs/releases/.
    • Claim first: assign yourself and post a claim comment with your session ID and branch. The card will be unassigned when you arrive — that is this lane's expected state, not an error, and it does not mean someone else holds it.
    • Run tests narrowly — the shared validation lock is a cross-agent resource and a wide run has been measured holding it nearly eighteen minutes.

    Report your exact file face. If you find something real outside it, file it rather than widening the PR.


    Generated by Claude Code

  3. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    Claimed by the execution seat, session session_011SfZeFWrhGLHmfq61xbz4q, branch claude/issue-6249-menubar-declared-keys.

    Read the thread before the body, as instructed. Working the triage ruling as dispatched: repair both separators to the declared separator: true spelling, and resolve shortcut toward the declaration and toward honesty — removing the unrendered affordance, since nothing in menubar.tsx reads it. ⛔ Not widening MenuItem.shortcut to string | string[].

    The card was unassigned on arrival, which the dispatch flags as this lane's expected state; no earlier claim comment with a different session ID is on the thread.


    Generated by Claude Code

  4. os-support-ai commented on Aug 26, 2026

    @os-support-ai
    Collaborator

    os-dev-report

    {
      "issue": 6249,
      "status": "done",
      "branch": "claude/issue-6249-menubar-declared-keys",
      "pr": "https://github.com/objectstack-ai/objectui/pull/6525",
      "premise_still_valid": true,
      "summary": "Implemented the triage ruling exactly, on one fixture file plus its pin test. Removed all 10 array-valued `shortcut` keys from components-overlay-menubar/application-menubar.json (menubar.tsx never reads the key, so no spelling of it renders) and repaired BOTH `{\"type\":\"separator\"}` entries to the declared `separator: true` that menubar.tsx:33 branches on and that the renderer's own defaultProps already write. Did NOT widen MenuItem.shortcut or teach the renderer to draw it. The PM's measured face was verified by a recursive structural walk and holds exactly: 10 shortcuts, 2 separators, 0 nested occurrences (this fixture authors no submenu children), and a corpus sweep of all 428 fixtures confirms `shortcut` appears nowhere else. IMPORTANT for triage: one input to the ruling is wrong in a way that does not change the outcome here. `shortcut` is NOT zero-runtime across the surface - dropdown-menu.tsx:70 and context-menu.tsx:68 both render the declared string spelling today. It is zero-runtime in menubar specifically, which is what the card measured, so removing it from THIS demo stays correct; but a future 'render shortcut in menubar' would be parity with shipped runtime, not a capability expansion. Filed as #6523 so it is not rediscovered. content/docs/components/overlay/menubar.mdx was deliberately left untouched: it publishes an invented MenubarItem interface that is the SOURCE of both fixture defects, but it is held by open PR #6345 and its correct content needs a ruling - filed as #6521.",
      "tests": "All at final HEAD 2dce47f0d, after the last commit. REVERSE VERIFICATION, predicted RED and observed RED: the assertion was written FIRST and run against the untouched fixture, so no mutate-and-restore was needed - the fixture was still at its pinned base blob a27572e7b3a022891c845258d124f7698be020dc (git rev-parse HEAD:<path>, base 50f987f9a). BEFORE: `Tests 3 failed | 28 passed (31)` - array-valued shortcut 10 hits, undeclared type:separator 2 hits, declared divider spelling 0 found vs 2 expected. AFTER: `Tests 31 passed (31)`. Every control, counter-probe and non-vacuity check was already green in the RED run, so the instrument was proven before use. MUTATION PROVEN ON DISK by counting the target text, not by an editor exit code: shortcut 10->0, '\"type\": \"separator\"' 2->0, '\"separator\": true' 0->2; blob a27572e7->d60a97e3. NO dist SITS BETWEEN mutation and assertion: vitest.config.mts:260 aliases @object-ui/types/zod to packages/types/src/zod/index.zod.ts (source), and the fixture is imported from src/ by path. The dependency closure was built anyway (pnpm --filter '@object-ui/example-schema-catalog^...' build, EXIT=0, 3m51s) because a fresh worktree makes type-check report 51 TS2307/TS2882 errors across files this PR never touches - a prerequisite, not a red gate. GATE UNION, each verdict read from the gate's own printed line with the exit code captured before any pipe: regenerate-catalog-index.py --check EXIT=0 'examples/schema-catalog/src/index.ts is up to date (428 entries)' (also run on the UNTOUCHED tree first - green there too, so index.ts not moving is a measured no-change, not unexamined drift; the generator imports fixtures by path so a content-only edit leaves it byte-identical); check-control-bytes EXIT=0 'OK (scanned 5385 tracked text file(s); skipped 85 binary)' plus a manual grep -naP scan of both changed files, no hits; check-changeset-presence EXIT=0 'No source of a released package changed in this range, so no changeset is owed' (@object-ui/example-* is in the .changeset/config.json ignore list, so none was added); type-check EXIT=0, 0 TS errors, and --listFiles confirms BOTH edited files are in the compilation (1 hit each) rather than excluded; vitest examples/schema-catalog/ + scripts/__tests__/catalog-index-regenerable-4633.test.ts EXIT=0 'Test Files 15 passed (15)' 'Tests 1830 passed (1830)'. DECLARED NARROWING - lint: repo-wide `pnpm lint` left to CI, ran narrowed with all three required facts: (1) population from eslint's own config, eslint.config.js:28 files: ['**/*.{ts,tsx}'], and eslint itself reports the .json fixture as 'File ignored because no matching configuration was supplied', so 1 of 2 changed files is in scope; (2) count from --format json: 1 file, 0 errors, 0 warnings; (3) type-aware linting not enabled (no project/projectService in eslint.config.js), so no rule reads cross-file type information and this diff cannot move any untouched file's verdict. NOTE the first vitest attempt was refused by the repo's own guard (objectui#3378) for running from the package dir, which would have silently run @object-ui/console's 22 files and reported a false green; re-run from the repo root as it instructs.",
      "open_questions": [
        {
          "question": "Does not block this card - routing only. The ruling's premise that drawing `shortcut` would be 'a capability expansion with zero runtime today' is true of menubar but false of the MenuItem surface: dropdown-menu.tsx:70 and context-menu.tsx:68 already render the declared string spelling. Separately, the three menu renderers run two contradictory separator dialects against one declared MenuItem, and the declared one cannot parse green at all because MenuItem.label is required while a divider has no label. How should that be routed?",
          "options": [
            "A - triage #6523 normally in a later batch; nothing here is urgent and this card is closed by the PR as-is",
            "B - fold #6523 into the still-open #6346 (the onSelect/onClick split), since both ask 'which spelling is the MenuItem contract' about the same interface and probably want one answer",
            "C - escalate #6523 to the maintainer now as a needs-user-decision, since it makes a live user-visible defect reachable by anyone who follows the published declaration in dropdown-menu or context-menu"
          ],
          "recommendation": "B, then C if the answer is not obvious. #6346 is already open, already needs-user-decision, and is the same question one key over on the same interface - answering them separately risks two rulings that contradict each other on whether the declaration or the renderers are authoritative. The user-visible half is real but narrow (an author must follow the declaration rather than the demos to hit it), so it does not warrant pre-empting the batch on its own."
        }
      ],
      "out_of_scope_findings": [
        "filed as #6521: content/docs/components/overlay/menubar.mdx publishes an invented `MenubarItem` interface diverging from the shipped MenuItem on five of six lines (label required not optional, undeclared value, shortcut string not string[], separator boolean not type:'separator', missing children) plus menus taught as required - it is what taught the fixture its wrong spellings; carries the corpus face for the undeclared `value` key, 21 items across four menu fixtures; deliberately not fixed here because open PR #6345 holds that file and the block's correct content needs a ruling",
        "filed as #6523: the three menu renderers run two separator dialects against one MenuItem - menubar.tsx:33 reads the declared `separator` boolean while dropdown-menu.tsx:46 and context-menu.tsx:44 read an undeclared `type: 'separator'` that the bare z.object silently strips, and both siblings teach the undeclared spelling in their own defaultProps and registry descriptions; also records that MenuItem.label is required so the declared divider spelling cannot parse green (menubar.tsx:69's own defaultProps do not satisfy the shipped type), and that shortcut already has runtime in two of the three renderers"
      ]
    }

    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

Labels

documentationImprovements or additions to documentationdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpm:dispatched

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions