Filed by the domain:skills @ objectui execution seat while implementing objectui#8065 (branch claude/issue-8065-skills-baseschema), unassigned, finding only — grading and domain:* are triage's. ⛔ Not repaired in that PR: the dispatch's file surface is exactly two files (skills/objectui/guides/architecture.md, skills/objectui/rules/protocol.md) and skills/** is a governed surface, so this cannot ride along.
⚠️ Generic parameters are spelled in WORDS below, never in angle-bracket shape — this repo's AGENTS.md records that GitHub's body sanitizer eats tag-shaped fragments even inside backticks and fenced blocks, and this card is about type declarations.
The observation
skills/objectui/guides/plugin-development.md:219-228, under "Type definitions", teaches a plugin author to declare their widget's node type like this (verbatim except for the angle-bracket avoidance, which does not occur in these ten lines):
export interface MyWidgetSchema {
type: 'my-widget';
id?: string;
className?: string;
bind?: string;
props?: MyWidgetProps;
hidden?: string;
disabled?: string;
}
Two readings, measured at origin/main d3499b31:
hidden and disabled are narrower than what the renderer accepts. The real declarations are packages/types/src/base.ts:376 and :412 — each is boolean OR ExpressionWire, and ExpressionWire (packages/types/src/expression.ts:67) is itself a string OR the CEL envelope object. SchemaRenderer evaluates them through evaluateCondition, whose parameter is string | boolean | undefined with the envelope routed to @objectstack/formula (documented in that same base.ts docblock, ruled 2026-09-04 on objectui#7530). So a plugin author who copies this fence declares a type that refuses hidden: true and refuses the envelope — both of which the shipped renderer accepts and acts on. This is the same shape objectui#8092 was graded a card for on root AGENTS.md.
- The interface re-states members rather than extending the published one.
MyWidgetSchema does not extend BaseSchema, so a plugin schema typed this way silently loses visible / visibleWhen / children / body / testId / ariaLabel and the rest. The repo's own published node types do the opposite — ActionSchema extends BaseSchema (packages/types/src/crud.ts:40), EventableSchema extends BaseSchema (packages/types/src/api-types.ts:278).
Re-derive:
sed -n '217,231p' skills/objectui/guides/plugin-development.md
sed -n '376p;412p' packages/types/src/base.ts
Why it is worth a card rather than a footnote
Under the maintainer's admission threshold this is the metadata-authoring-trap class, and it is the published surface: a plugin author (or an agent) copying this block gets a declaration that is wrong in the quiet direction — the JSON still renders, and the mismatch only surfaces later at the member, exactly the failure direction objectui#8092 recorded.
⚠️ Nothing catches it. pnpm check:skill-examples is opt-in and this fence carries no marker (the next marker in the file is on the fence below it), so no run has an opinion about it; and even if it were marked it would pass, because the gate compiles a fence as a self-contained program — that is objectui#7646's finding, measured with a two-arm control there.
Deliberately NOT proposed here
⛔ This seat is not choosing the repair. The obvious shapes each have a cost worth pricing by whoever takes it: make the fence extends BaseSchema and drop the four re-stated members (shortest, and correct, but changes what the tutorial is showing); keep the standalone interface and correct the two declarations only; or leave it and add the pointer sentence. ⛔ Also not proposed: a new gate over unmarked fences — that is a new scanning population, the class objectui#7646 and objectui#5250 already hold.
Dedup — run with a live control
Channel: repo-scoped REST (/repos/.../issues?state=open, 4 pages, 383 open issues read) plus local grep of every title and body; the REST /search/* endpoint answers 403 from this container, and no MCP search call was spent. Live control: the token BaseSchema matches 43 of the 383, so a zero for the query terms is a real zero and not a broken read. Query terms plugin-development, MyWidgetSchema, MyWidgetProps matched three cards, none of them this:
- objectui#7646 — 26 doc fences re-declare a published type name instead of importing it, so the gate judges a private copy. Adjacent and the reason nothing catches this one; its census keys on fences declaring a name a workspace package already exports, and
MyWidgetSchema is not such a name.
- objectui#7493 —
ComponentInput.defaultValue?: any and the bare-any baseline row at plugin-development.md:92. Same file, different line, different contract.
- objectui#5174 — the doc-snippet scanner collecting only
.mdx under content/docs. Coverage, not content.
Same family, checked, neither is this: objectui#8065 (the two fences carrying the retired UIComponent name — being repaired now, and this file is outside its ruled surface), objectui#8092 (root AGENTS.md's member list), objectui#8093 (extending the vocabulary pin's corpus once both governed halves land — that pin bans two name spellings, so it would not see this at all).
Generated by Claude Code for the domain:skills @ objectui execution seat, session session_019RfFHiRCSs3JXLK4cwcfox.
Generated by Claude Code
Filed by the
domain:skills@ objectui execution seat while implementing objectui#8065 (branchclaude/issue-8065-skills-baseschema), unassigned,findingonly — grading anddomain:*are triage's. ⛔ Not repaired in that PR: the dispatch's file surface is exactly two files (skills/objectui/guides/architecture.md,skills/objectui/rules/protocol.md) andskills/**is a governed surface, so this cannot ride along.AGENTS.mdrecords that GitHub's body sanitizer eats tag-shaped fragments even inside backticks and fenced blocks, and this card is about type declarations.The observation
skills/objectui/guides/plugin-development.md:219-228, under "Type definitions", teaches a plugin author to declare their widget's node type like this (verbatim except for the angle-bracket avoidance, which does not occur in these ten lines):Two readings, measured at
origin/maind3499b31:hiddenanddisabledare narrower than what the renderer accepts. The real declarations arepackages/types/src/base.ts:376and:412— each isbooleanORExpressionWire, andExpressionWire(packages/types/src/expression.ts:67) is itself a string OR the CEL envelope object.SchemaRendererevaluates them throughevaluateCondition, whose parameter isstring | boolean | undefinedwith the envelope routed to@objectstack/formula(documented in that same base.ts docblock, ruled 2026-09-04 on objectui#7530). So a plugin author who copies this fence declares a type that refuseshidden: trueand refuses the envelope — both of which the shipped renderer accepts and acts on. This is the same shape objectui#8092 was graded a card for on rootAGENTS.md.MyWidgetSchemadoes not extendBaseSchema, so a plugin schema typed this way silently losesvisible/visibleWhen/children/body/testId/ariaLabeland the rest. The repo's own published node types do the opposite —ActionSchema extends BaseSchema(packages/types/src/crud.ts:40),EventableSchema extends BaseSchema(packages/types/src/api-types.ts:278).Re-derive:
Why it is worth a card rather than a footnote
Under the maintainer's admission threshold this is the metadata-authoring-trap class, and it is the published surface: a plugin author (or an agent) copying this block gets a declaration that is wrong in the quiet direction — the JSON still renders, and the mismatch only surfaces later at the member, exactly the failure direction objectui#8092 recorded.
pnpm check:skill-examplesis opt-in and this fence carries no marker (the next marker in the file is on the fence below it), so no run has an opinion about it; and even if it were marked it would pass, because the gate compiles a fence as a self-contained program — that is objectui#7646's finding, measured with a two-arm control there.Deliberately NOT proposed here
⛔ This seat is not choosing the repair. The obvious shapes each have a cost worth pricing by whoever takes it: make the fence
extends BaseSchemaand drop the four re-stated members (shortest, and correct, but changes what the tutorial is showing); keep the standalone interface and correct the two declarations only; or leave it and add the pointer sentence. ⛔ Also not proposed: a new gate over unmarked fences — that is a new scanning population, the class objectui#7646 and objectui#5250 already hold.Dedup — run with a live control
Channel: repo-scoped REST (
/repos/.../issues?state=open, 4 pages, 383 open issues read) plus local grep of every title and body; the REST/search/*endpoint answers 403 from this container, and no MCP search call was spent. Live control: the tokenBaseSchemamatches 43 of the 383, so a zero for the query terms is a real zero and not a broken read. Query termsplugin-development,MyWidgetSchema,MyWidgetPropsmatched three cards, none of them this:MyWidgetSchemais not such a name.ComponentInput.defaultValue?: anyand the bare-anybaseline row atplugin-development.md:92. Same file, different line, different contract..mdxundercontent/docs. Coverage, not content.Same family, checked, neither is this: objectui#8065 (the two fences carrying the retired
UIComponentname — being repaired now, and this file is outside its ruled surface), objectui#8092 (rootAGENTS.md's member list), objectui#8093 (extending the vocabulary pin's corpus once both governed halves land — that pin bans two name spellings, so it would not see this at all).Generated by Claude Code for the
domain:skills@ objectui execution seat, sessionsession_019RfFHiRCSs3JXLK4cwcfox.Generated by Claude Code