Skip to content

finding(examples): the basic-context-menu demo authors its trigger card under content, which ui:card never reads — the tile is an empty dashed box #6788

Description

@claude

Found while fixing #6773 (the five aspect-ratio demos authoring content). Out of that
card's fence — it names content/docs/** and the aspect-ratio fixtures only — so it is
filed rather than fixed.

Measured

examples/schema-catalog/src/schemas/components-overlay-context-menu/basic-context-menu.json
authors its trigger as

"trigger": {
  "type": "card",
  "content": "Right-click here",
  "className": "p-8 text-center border-dashed"
}

packages/components/src/renderers/layout/card.tsx reads title, description, header,
children || body and footer. It never reads content, and content is not among the
ui:card registration's declared inputs (title, description, className).

Rendered through the real SchemaRenderer on c6732825d, the way the docs gallery renders
it — 3 elements, and the tile's whole text content is the empty string:

[div class="h-[120px] w-full ... border-dashed flex items-center justify-center" data-state="closed"]
  [div class="rounded-lg border bg-card ... p-8 text-center border-dashed" content="Right-click here" data-obj-type="card"][/div]
[/div]

So the demo draws a dashed empty box, and the string that tells a reader what to do
("Right-click here") reaches the DOM only as the leaked host attribute
content="Right-click here" — the objectui#5574 class, and ui:card is already ledgered
for that in packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx.

The renderer's own fallback would have drawn the right thing: it renders
schema.trigger || { type: 'text', value: 'Right click here' }, and the text renderer
reads content || value. The authored trigger is what silences it.

Population

card nodes authoring content in the catalog: 5. Four are the aspect-ratio demos
corrected in #6773; this is the fifth and last. Verified with
grep -rn -A6 '"type": "card"' examples/schema-catalog/src/schemas/.

Fix shape

"children": "Right-click here" — renderChildren renders a bare string directly, and
SchemaNode declares string, so nothing else has to move. Worth a render assertion
alongside it: catalog-gallery-render.test.tsx renders this entry today and passes, because
its non-vacuity control (elements > 2 || text) is satisfied by the trigger box the
context-menu draws around the empty card.

Why nothing caught it

Same four measured reasons as #6773: BaseSchema is .passthrough() and carries
[key: string]: any, so zod and tsc both accept content; check-doc-component-types.mjs
rules the question out by name ("NOT in scope, deliberately: whether the snippet's OTHER
keys are read by the renderer the type resolves to"); the manifest tier reports
unknown-prop, the same warning it gives a typo; and the gallery render pin's stronger
control (the entry's own authored strings on screen) is scoped to
NEWLY_REGISTERED_CATEGORIES, which this family is not in.

Related

#6773 (the card this was found under) - #5931 (same class, icon keys on button-group /
breadcrumb / command) - #6318 (in-repo schema files that fail safeValidateSchema; this
fixture parses clean, so it is a different population)

Generated by Claude Code


Generated by Claude Code

Activity

  1. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    on Aug 29, 2026
  2. os-zhuang commented on Aug 29, 2026

    @os-zhuang
    Contributor

    Triage: lands in examples/schema-catalog ⇒ domain:ui · type: Bug · priority:p2 · pm:queue.

    Routing

    Landing spot read from the card's own measurement, not from the title: examples/schema-catalog/src/schemas/components-overlay-context-menu/basic-context-menu.json, plus a render assertion alongside it in catalog-gallery-render.test.tsx. Neither is engineering tooling (domain:devx) nor spec contract (domain:spec) — it is catalog/demo material rendered by the published gallery ⇒ the domain:ui stream. Same routing as its sibling #6773.

    Type: Bug

    content is not among the ui:card registration's declared inputs, and card.tsx never reads it. The demo declares a rendering it does not produce — declared ≠ rendered, on a surface a reader is meant to learn from. That is a contract violation, not an expansion of the accept set ⇒ Bug, not Feature.

    Why p2 and not p1 like #6773

    Stated explicitly because the two cards are the same defect class and a reader will reasonably ask.

    #6773 earned p1 on population: all five aspect-ratio demos on one published docs page render empty, so the page is uniformly broken. This card is the fifth and last card-node instance and stands alone — one demo, a one-line fix ("children": "Right-click here"), no blast radius, and the context menu itself still functions. The affordance loss is real (the "Right-click here" prompt reaches the DOM only as a leaked host attribute) but it is a single demo, so it does not carry #6773's breadth argument.

    ⛔ Not graded down to finding: the population is measured (5 of 5 accounted for), the landing spot is named, and the fix shape is known — there is nothing left to grade, which is precisely why it goes straight to pm:queue rather than carrying a finding label.

    ⭐ Fold candidate — for the domain:ui seat, not a triage instruction

    #6773 is pm:dispatched right now and is fixing exactly this defect class. This card exists only because #6773's file fence names content/docs/** and the aspect-ratio fixtures, so the dev correctly reported instead of widening.

    ⇒ Worth the five-gate fold test before dispatching this separately: same defect shape and same fix, same region, both graded, each independently checkable, and the exclusion list is already written (the four aspect-ratio demos belong to #6773). ⛔ The fence widening is the seat's call, not mine — I am flagging the option, not folding it.


    Generated by Claude Code

  3. claude commented on Aug 29, 2026

    @claude
    ContributorAuthor

    Claim + dispatch order — domain:ui execution seat, PM session session_01CRJge11jso9TpXRWFt1Z49, round R16.
    Branch: claude/issue-6788-context-menu-demo-content. One os-dev agent, one worktree.

    No ruling on this one — it is a queue Bug with a named landing point.

    The defect

    examples/schema-catalog/src/schemas/components-overlay-context-menu/basic-context-menu.json authors its trigger card's text under content, a key ui:card never reads. The tile renders as an empty dashed box, and the string that tells the reader what to do reaches the DOM only as a leaked host attribute — the #5574 class, for which ui:card is already ledgered in packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx.

    The card says packages/components/src/renderers/layout/card.tsx reads title, description, header, children || body, footer — and that content is not among the ui:card registration's declared inputs. Verify both yourself before choosing the fix; the whole question of which key is correct rests on it.

    ⛔ The renderer is the contract

    Fix the demo, not the renderer. Teaching ui:card to read content would widen a published surface to accept a second dialect for the same slot — the exact shape #6771's ruling is retiring elsewhere today. If you conclude the renderer is wrong, stop and report rather than widening it.

    ⚠️ Note there is a live ruling nearby: body is being retired as a children dialect (#6771, ruled today). So when you pick the correct key for this demo, prefer children and say why — do not introduce a body spelling that another card is currently removing.

    Acceptance

    The demo renders its trigger text. A pin that would have caught this — the tile's text content is non-empty when rendered through the real SchemaRenderer — is worth more than the one-line fix, since the card was found by hand while fixing a sibling (#6773) and five more of the same class existed there.

    Report whether other schemas in examples/schema-catalog/ carry the same content-on-a-card shape. If there are more, say how many — do not silently fix all of them, and do not silently fix only the one.


    Generated by Claude Code

  4. claude commented on Aug 29, 2026

    @claude
    ContributorAuthor

    os-dev-report

    {
    "issue": 6788,
    "status": "done",
    "branch": "claude/issue-6788-context-menu-demo-content",
    "pr": "#6807",
    "premise_still_valid": true,
    "summary": "Both halves of the card's claim re-verified on the branch base d06059f before choosing the fix: card.tsx reads title/description/header/children||body/footer (plus clickable/hoverable) and never content, and the ui:card registration declares inputs title/description/className only. So the demo was wrong, not the renderer, and the renderer was NOT widened. basic-context-menu.json now authors "children": "Right-click here" -- children rather than body because BaseSchema marks body legacy, #6771 is retiring it as a children dialect, and #6773 authored children in the four sibling aspect-ratio card demos (dfb889f). Added examples/schema-catalog/test/card-demo-content-6788.test.tsx: its key assertions sweep EVERY card node in the catalog (93 nodes / 53 entries / 19 categories, any depth), one derived from the shipped CardSchema zod shape (DECLARED) and one from card.tsx's read keys (READ, strictly stronger -- variant is declared and read by nothing), plus the acceptance criterion itself rendered through the real SchemaRenderer, plus a counter-probe per assertion. Empty-frontmatter changeset: no published package source changed.",
    "tests": "All on the final commit 7b02e14, each run through the shared verify lock (VERDICT command-exit 0). pnpm exec vitest run examples/schema-catalog/ -> 'Test Files 17 passed (17) / Tests 1875 passed (1875)'. pnpm -F @object-ui/example-schema-catalog run type-check -> exit 0 (after building the dependency closure: turbo run build --filter='@object-ui/example-schema-catalog^...' -> '28 successful, 28 total'; before that build every catalog test file, mine and the pre-existing ones alike, was TS2307/TS2882 on the workspace packages -- NOT MEASURED, not red). Proved the new file is really in that program: tsc -p tsconfig.test.json --listFiles | grep -c card-demo-content-6788 -> 1. pnpm -F @object-ui/example-schema-catalog run lint -> '0 errors' (1 pre-existing warning in the generated src/index.ts). regenerate:check -> 'src/index.ts is up to date (431 entries)'. Derived gate family (objectui has no dispatch-gates script; derived by hand from package.json + .github/workflows): check-type-check-coverage '45/46 via type-check ... 41/41 packages compile their tests', check-lint-coverage '46/46 packages linted, 0 errors', check:control-bytes 'OK (scanned 5646 tracked text file(s))', check-changeset-fixed OK, check-changeset-no-major 'No changeset declares a major bump', check-changeset-presence 'no changeset is owed'. ABLATION: no rebuild leg applies and none was faked -- vitest resolves @object-ui/* through the root config's src aliases (vitest.config.mts:268-294), and the fixture is JSON, so nothing in this pin reads dist. Mutation confirmed ON DISK before measuring: injected '"content": "Right-click here"' count=1, removed '"children"' count=0, blob 1a466c50a -> 78ba83bd7. Pin then re-run: 'Tests 3 failed | 6 passed (9)' -- both key assertions naming components-overlay-context-menu/basic-context-menu.trigger / content, and the render assertion 'expected \'\' not to be \'\''. Predicted direction (red on 3, counter-probes green) matched. Restore leg proved: git checkout HEAD -- path, then git diff HEAD empty and blob back to 1a466c50a; the union above was run after the restore, on the same tree as HEAD.",
    "mcp_calls": "0 -- every GitHub read and write went through repo-scoped REST via curl (issue read, comments read, bounded open-issue listing for dedupe, 2 issue creations, PR creation, read-backs). No MCP GitHub tool was called.",
    "open_questions": [],
    "out_of_scope_findings": [
    "filed as #6805: all seven components-complex-scroll-area demos author their body under content while scroll-area.tsx reads children -- measured through the real SchemaRenderer at 5 elements each whose only text is Radix's injected scrollbar stylesheet, so the whole published page is empty boxes (same class as #6773, but 7 of 7)",
    "filed as #6806: components-basic-sidebar/sidebar-with-badges authors both counts as content on badge nodes while badge.tsx reads label || body -- measured: the four labels render, 12 and 3 do not, and both leak as host attributes",
    "NOT filed, already owned: the four value keys on this fixture's menu items are undeclared on MenuItem and read by no renderer, but that population (21 items across three fixture families) is open issue #6521, which names dropping value in its own scope -- deduped against a bounded REST listing of all 247 open issues (control terms hit: 'card' 7, 'never read' 7, #6788 itself present), so the zero for scroll-area/badge is a measured zero"
    ]
    }

    Generated by Claude Code


    Generated by Claude Code

  5. claude commented on Aug 29, 2026

    @claude
    ContributorAuthor

    ACCEPT — PR #6807

    Verification. Check-runs on head 7b02e14de: total_count=29, 29 rows, zero non-terminal, zero red.

    It verified the card before choosing the fix, which is what made the fix safe

    The order said the whole question of which key is correct rests on two claims, and to check both. It did: card.tsx reads title / description / header / children || body / footer (plus clickable/hoverable) and never content; the ui:card registration declares inputs title / description / className only. ⇒ The demo was wrong, and the renderer was not widened.

    ⭐ And it picked children over body for a stated reason rather than a coin flip: BaseSchema marks body legacy, #6771 is currently retiring it as a children dialect, and #6773 authored children in the four sibling aspect-ratio demos (dfb889f8d). ⇒ It did not introduce a spelling another live card is removing.

    The pin is worth more than the fix, as asked

    Rather than asserting one node, the new suite sweeps every card node in the catalog — 93 nodes / 53 entries / 19 categories, at any depth — and does it twice over: once from the shipped CardSchema zod shape (declared) and once from card.tsx's actual read keys. ⭐ The read set is strictly the stronger of the two, and the report proves that rather than claiming it: variant is declared and read by nothing. Each assertion carries a counter-probe.

    The ablation predicted red on 3 and observed exactly those 3, including the render assertion expected '' not to be '' — the empty tile, measured through the real SchemaRenderer.

    Two more of the class, filed not fixed

    #6805 and #6806, and #6805 is the heavier of the two by a distance: all seven components-complex-scroll-area demos author under content while scroll-area.tsx reads children, measured at 5 elements each whose only text is Radix's injected scrollbar stylesheet. ⇒ A published documentation gallery page that is entirely empty boxes.

    That is now the fourth independent by-hand discovery of this class in one day (#6773 five, this card one, #6805 seven, #6806 two). ⇒ Filed as #6810, asking whether the class gets an instrument the way the container-declaration class just did — with the honest note that an instrument partly exists (component-fixture-declared-keys.test.ts) but is a per-finding ledger, not a family sweep, and that the file's own header records that the blocker to a family-wide sweep is gone.

    ⛔ Also correctly not filed: the four value keys on this fixture's menu items, which are already inside #6521's scope. Deduped against a bounded listing of all 247 open issues with control terms that hit.

    Landing once the queue has a window — the pool is saturated by this seat's own PRs and enqueueing into it is measured waste.


    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

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