docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Add copy buttons to all
 blocks
(function() {
function addCopyButtons() {
document.querySelectorAll('pre code').forEach(function(codeBlock) {
if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;
codeBlock.parentElement.setAttribute('data-copy-added', 'true');
var btn = document.createElement('button');
btn.textContent = 'Copy';
btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';
btn.onmouseover = function() { this.style.opacity = '1'; };
btn.onmouseout = function() { this.style.opacity = '0.7'; };
btn.onclick = function() {
navigator.clipboard.writeText(codeBlock.textContent).then(function() {
btn.textContent = 'Copied!';
setTimeout(function() { btn.textContent = 'Copy'; }, 1500);
});
};
codeBlock.parentElement.style.position = 'relative';
codeBlock.parentElement.appendChild(btn);
});
}
addCopyButtons();
// Re-run on dynamic content
var observer = new MutationObserver(addCopyButtons);
observer.observe(document.body, { childList: true, subtree: true });
})();
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Force GitHub README to respect dark mode (function() { var style = document.createElement('style'); style.textContent = ' .markdown-body { color-scheme: dark light; } .markdown-body pre { background: #161b22 !important; } .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; } .markdown-body table th, .markdown-body table td { border-color: #30363d !important; } .markdown-body img { background: #0d1117; } .markdown-body blockquote { border-left-color: #8b949e; } .markdown-body hr { border-color: #30363d; } '; document.head.appendChild(style); })(); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Highlight search terms from Google/DuckDuckGo/Bing referrer (function() { var ref = document.referrer; var terms = []; if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) { var url = new URL(ref); var q = url.searchParams.get('q') || url.searchParams.get('p'); if (q) { terms = q.split(/\s+/).filter(function(t) { return t.length > 2; }); } } if (terms.length === 0) return; var style = document.createElement('style'); style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }'; document.head.appendChild(style); function highlight(node) { if (node.nodeType === 3) { // text node var text = node.textContent; var found = false; terms.forEach(function(term) { var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\]\\]/g, '\\') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { // Universal Dark Mode - works on any site (function() { var enabled = true; function applyDarkMode() { if (!enabled) return; // Create style element if it doesn't exist var style = document.getElementById('universal-dark-mode-style'); if (!style) { style = document.createElement('style'); style.id = 'universal-dark-mode-style'; document.head.appendChild(style); } // Dark mode CSS - inverts colors but preserves images/video style.textContent = ' /* Invert everything except media */ html { filter: invert(1) hue-rotate(180deg) !important; background: #1a1a2e !important; } /* Restore images, videos, iframes, canvas */ img, video, iframe, canvas, svg, picture, [style*="background-image"] { filter: invert(1) hue-rotate(180deg) !important; } /* Preserve specific elements that should not be inverted */ .no-dark-mode, .no-dark-mode *, [data-theme="light"], [data-theme="light"], .ace_editor, .ace_editor *, .CodeMirror, .CodeMirror *, .monaco-editor, .monaco-editor *, .markdown-body pre, .markdown-body pre *, .highlight, .highlight *, pre code, pre code * { filter: none !important; } /* Fix common UI elements */ .modal, .popup, .dropdown-menu, .tooltip, .popover { filter: invert(1) hue-rotate(180deg) !important; background: #2d2d44 !important; border-color: #444 !important; } /* Scrollbars */ ::-webkit-scrollbar { background: #1a1a2e !important; } ::-webkit-scrollbar-thumb { background: #444 !important; } ::-webkit-scrollbar-thumb:hover { background: #555 !important; } /* Selection */ ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; } ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; } '; } function removeDarkMode() { var style = document.getElementById('universal-dark-mode-style'); if (style) style.remove(); } // Toggle with Alt+Shift+D document.addEventListener('keydown', function(e) { if (e.altKey && e.shiftKey && e.key === 'D') { e.preventDefault(); enabled = !enabled; if (enabled) { applyDarkMode(); console.log('[Universal Dark Mode] Enabled'); } else { removeDarkMode(); console.log('[Universal Dark Mode] Disabled'); } } }); // Apply on load applyDarkMode(); // Re-apply on dynamic content var observer = new MutationObserver(function(mutations) { if (enabled && !document.getElementById('universal-dark-mode-style')) { applyDarkMode(); } }); observer.observe(document.head, { childList: true }); console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle'); })(); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem - #6855

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import
Aug 30, 2026
Merged

docs(core): app-schema's "Global Actions" snippet imports AppMenuItem, not the overlay MenuItem#6855
os-sam merged 1 commit into
mainfrom
claude/issue-6692-app-schema-menuitem-import

Conversation

@os-sam

Copy link
Copy Markdown
Collaborator

Fixes#6692

Executes the 2026-08-29 ruling on that card: option A — the docs snippet references the type AppAction.items actually resolves to. The ruling's census rider is recorded below, and it fired: option B is filed as #6854.

Diff is two files: content/docs/core/app-schema.mdx and a changeset. packages/types/** is read-only in this card (route B's surface, and held by PR #6826).

The defect

content/docs/core/app-schema.mdx's "Global Actions" section imported the bare name:

importtype{MenuItem}from'@object-ui/types';interfaceAppAction{items?: MenuItem[];}

That resolves to the overlay union — packages/types/src/overlay.ts:347, re-exported bare at packages/types/src/index.ts:255, the type behind ui:dropdown-menu / ui:context-menu / ui:menubar. But the real AppAction.items (packages/types/src/app.ts:728) is declared insideapp.ts, so it resolves to that file's own legacy navigation-item MenuItem (app.ts:461). The barrel re-exports that one renamed, as AppMenuItem (index.ts:59), precisely to avoid this collision — every line number in the finding re-derived at 26896c6 and confirmed.

The two are mutually incompatible, not merely differently named. The overlay union declares type?: never on both arms (dividers became { separator: true } in #6523), so the { "type": "separator" } item documented a few paragraphs earlier on this same page is refused by the type the snippet named.

⚠️ The suggested fix in the finding does not work

Both the original card and the ruling offer "MenuItem as AppMenuItem" as an alternative spelling. It does not fix this, and it fails in the worst way — it looks repaired and compiles:

importtype{MenuItemasAppMenuItem}from'@object-ui/types';// still the OVERLAY type

AppMenuItem is already the barrel's export name for the app.ts type. Importing MenuItem and locally aliasing it to AppMenuItem imports the overlay export and renames it, leaving the defect exactly in place under a name that reads correct. The only correct import is import type { AppMenuItem }, which is what this PR uses. Verified by type probe, below.

Census rider (required by the ruling) — result: MIXED, so #6854 is filed

The consumer named in the card is the wrong file.packages/components/src/renderers/navigation/header-bar.tsx never sees AppAction; it consumes HeaderBarSchema (packages/types/src/navigation.ts:57), whose actions?: SchemaNode[] is unrelated, and it never reads .items.

The real and only consumer is packages/runner/src/LayoutRenderer.tsx:301-315:

Field readLineIn app.tsMenuItem (what items IS)In overlay MenuItem
item.type === 'separator'302yesno — type?: never
item.label307yesyes
(item as any).onClick304noyes
(item as any).shortcut309noyes

Never read: path, href, badge, hidden, children.

So the renderer branches on the legacy type spelling and then reaches two overlay-shaped fields through as any, past its own declared type. That is the rider's trigger condition, so option B is filed as #6854 with this census as its evidence — not decided here, and no part of it is attempted in this PR.

This does not undermine option A: both declarations agree on what items is today (TS app.ts:728; zod mirror app.zod.ts:192, using the "Legacy MenuItem Schema" at app.zod.ts:167), so documenting AppMenuItem is documenting the shipped contract either way. If #6854 later re-types the field, this snippet changes with it.

⛔ What does NOT demonstrate this change is correct

A green CI run is not evidence here, and neither is check:doc-snippets passing. The snippet is a bare type reference (items?: MenuItem[]), never an object literal, so it compiles against eitherMenuItem. No gate goes red-to-green on this fix.

Measured rather than assumed — ablation at 4b65e8d, reverting the file to its pre-fix bytes and re-running the gate:

MUT hash : 3a8d64a (differs from HEAD blob: YES)
MUT fixed-import count : 0 (expect 0)
MUT buggy-import count : 1 (expect 1)
MUTATION CONFIRMED ON DISK
MUTATED-LEG GATE EXIT=0
Semantic phase: 267 of 267 block(s) judged, 0 failed.
Every covered documentation snippet compiles against the built types.
RESTORE CONFIRMED: hash matches HEAD blob AND git diff HEAD is empty

Identical verdict on the buggy tree. The gate is structurally blind to this defect.

What DOES demonstrate it

A type probe compiled against the builtpackages/types/dist/index.d.ts, using @ts-expect-error so each assertion fails if the expected error does not occur. Exit 0 — every assertion held:

  1. AppMenuItem accepts { type: 'separator' } and the full legacy shape (type/label/path/href/badge/hidden) — it is app.ts's type.
  2. The bare MenuItem the docs imported rejects{ type: 'separator' } and rejects path.
  3. MenuItem as AppMenuItem still rejects { type: 'separator' } — proving that spelling stays the overlay type.
  4. AppMenuItem has no onClick — the field LayoutRenderer casts past.

The probe was a scratch file, deleted before commit; it is reproducible from the four assertions above.

Gates

Run at 4b65e8d (the final commit), exit codes captured before any pipe; verdict lines quoted as each gate printed them:

GateResult
check:doc-snippetsexit 0 — "Every covered documentation snippet compiles against the built types." 267/267 blocks judged, 0 failed; controls (resolution / sentinel / positive / undeclared) all held. Built its declared 21-package closure first.
check:doc-fencesexit 0 — "every TypeScript block in 223 document(s) is fenced ts/tsx/typescript"
check:doc-typesexit 0
check:control-bytesexit 0 — "scanned 5647 tracked text file(s); skipped 85 binary"
check:docs-route-closureexit 0
check-changeset-presenceexit 0 — "No source of a released package changed in this range, so no changeset is owed."
check-changeset-no-majorexit 0 — "No changeset declares a major bump."
check-changeset-overwriteexit 0 — "1 changeset(s) added, 0 modified, 0 deleted."

app-schema.mdx is confirmed covered by check:doc-snippets: coverage is every doc under content/docs minus the script's UNGATED_DOCS ledger, and this page has no ledger entry.

The changeset carries empty frontmatter — the repo's declaration form for a change that publishes nothing, since the presence gate itself reports nothing owed for a docs-only diff.


Generated by Claude Code

…t the overlay MenuItem
`import type { MenuItem } from '@object-ui/types'` resolves to the overlay
union (overlay.ts, re-exported bare from the barrel), but `AppAction.items`
is declared inside app.ts and so resolves to that file's own legacy
navigation-item `MenuItem`, which the barrel re-exports renamed as
`AppMenuItem` precisely to avoid this collision.
The two are mutually incompatible, not just differently named: the overlay
union declares `type?: never` on both arms, so the `{ "type": "separator" }`
item documented elsewhere on the same page is refused by the type the
snippet named.
Note: `MenuItem as AppMenuItem` does NOT fix this — that imports the bare
(overlay) export and renames it locally. `AppMenuItem` is already the
barrel's export name.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013hfmP9hoMd3dJwTh85J4yB
@os-sam
os-sam marked this pull request as ready for review August 30, 2026 02:56
@os-sam
os-sam added this pull request to the merge queueAug 30, 2026
Merged via the queue into main with commit ce26b81Aug 30, 2026
30 checks passed
@os-sam
os-sam deleted the claude/issue-6692-app-schema-menuitem-import branch August 30, 2026 03:10
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

finding(docs): core/app-schema.mdx's "Global Actions" snippet imports the wrong same-named MenuItem

2 participants

@os-sam@claude