You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Re-derived on my own base (c673282) — 5 of 5, confirmed
packages/components/src/renderers/layout/aspect-ratio.tsx reads exactly ratio, className, image (with alt), and children || body. It never reads content, and content is not among its declared inputs. All five entries in examples/schema-catalog/src/schemas/components-layout-aspect-ratio/ authored ratio + content and nothing else; a repo-wide sweep for type: "aspect-ratio" found no sixth
authoring site.
Measured before the fix, rendering each entry through the real SchemaRenderer the way the
docs gallery does (harness wrapper + SchemaRenderer, no data source):
entry
elements
text
img
16-9-aspect-ratio
3
""
0
square
3
""
0
4-3
3
""
0
ultrawide
3
""
0
video-aspect-ratio
3
""
0
Three elements is the harness wrapper plus the two Radix AspectRatio emits — the ratio box
and nothing inside it. The authored content reached the DOM only as the leaked host
attribute content="[object Object]" (the objectui#5574 class; ui:aspect-ratio is
already ledgered for it in packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx,
so this PR files nothing new for it).
What each demo now authors, and why
The renderer was read first and defines the shape; the docs page was not consulted for it,
because the docs page is the thing that was wrong.
entry
now authors
why that key
16-9-aspect-ratio
image + alt
The photo demo. image is the renderer's own declared input and its arm draws an img element classed rounded-md object-cover w-full h-full, i.e. the photo fills the box. The old nested { type: 'image', src, alt } node would render at natural size with no sizing class even once it was reachable.
square, 4-3, ultrawide
children on the box, children on the card
The renderer's no-image arm is renderChildren(schema.children ǀǀ schema.body), and renderChildren renders a bare string directly. Both levels had to move: the ui:card renderer reads title, description, header, children ǀǀ body, footer — never content — so moving only the outer key would have traded an empty box for an empty card.
video-aspect-ratio
same, keeping its authored className
Same two-level rename; bg-muted flex items-center justify-center was already authored.
One presentational addition, named because it is not a pure key rename: the four card demos
now carry flex h-full items-center justify-center. h-full is what makes the card fill
the ratio box rather than sit as a strip at its top, and the centering is the intent video-aspect-ratio already authored verbatim; the other three now match it.
After the fix, same measurement: the photo entry draws 4 elements with 1 img
(src and alt as authored), and each card entry draws 5 elements with its own label as
the tile's text ("Square (1:1)", "4:3 Ratio", "21:9 Ratio", "Video Player (16:9)").
The Schema block
It published content: ComponentSchema as contract and omitted image / alt, which the
renderer does read and does declare. It now documents the read surface: ratio, image, alt, children, className, with the two content modes stated in one line of prose.
Two deliberate choices:
body is not documented. The renderer reads children ǀǀ body, but a back-compat
read is not a second authorable spelling — the page:card discipline, verbatim
(objectstack#5775 / PR fix(app-shell): the Studio grid selects only fields the server has (cloud#1652) #6281). children is also what all nine sibling components-layout-semantic fixtures author.
SchemaNode, not ComponentSchema.AspectRatioSchema declares children?: SchemaNode ǀ SchemaNode[]; ComponentSchema is a real export but it is the
blocks-component interface, not the child-node union.
The fence stays plaintext: this file is one of the 80 in check-doc-fence-languages.mjs's
shrink-only ledger, at 1 block, and it still holds 1 block.
Gate verdict: would anything have caught this?
Nothing did, and the reasons are measured, not inferred:
examples/schema-catalog/test/catalog-gallery-render.test.tsxdoes render all five
and passes. Its non-vacuity control is drewSomething = elements > WRAPPER_ELEMENTS ǀǀ text.trim().length > 0 with WRAPPER_ELEMENTS = 2, and an empty ratio box clears it on the wrapper Radix draws for
the ratio itself (3 > 2). Its stronger control — the entry's own authored strings on
screen — is scoped to NEWLY_REGISTERED_CATEGORIES, which this family is not in.
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 content as unknown-prop, the same warning it gives a typo,
and nothing fails on it.
A corpus-wide "container fixture must render its authored content" sweep is NOT
source-decidable today, and this PR does not propose one. The predicate needs "which
authored key is this renderer's content slot", and the registry cannot answer it here: ui:aspect-ratio declares isContainer: true yet declares no type: 'slot' input at all
(its inputs are ratio, image, alt, className), while page:card does declare children as a slot. A sweep would have to guess the slot for every renderer in the first
shape, which is the false-red generator that gets gates deleted.
What IS decidable is the per-family pin, so that is what ships: examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx — the same shape toast-demo-dispatch-6250.test.tsx took for the same class of fixture correction. It
asserts at category scope, not over a list of five ids, so a sixth demo authoring the
phantom key fails without anyone remembering to add it:
every entry in the category authors only keys the renderer reads;
every entry puts its authored content in the box (its img src/alt for the image
arm, its authored slot strings otherwise);
a counter-probe renders the exact pre-fix content shape and pins that it draws no
text and no img — so the two assertions above cannot pass vacuously;
a second counter-probe pins that the judge does see content authored under a read key.
Verification
All at 8c093f6b5 (the final commit), heavy commands serialized through the shared verify
lock:
pnpm check:doc-fences OK every TypeScript block in 223 document(s) ... No unknown fence spelling hides one.
pnpm check:doc-types OK Every documented component type is registered.
pnpm check:doc-snippets OK Every covered documentation snippet compiles against the built types.
pnpm docs:check-links OK Links are valid across 17 scan roots.
pnpm check:control-bytes OK scanned 5648 tracked text file(s)
pnpm changeset:check OK + check-changeset-presence: no changeset is owed (1 added anyway)
pnpm type-check:coverage OK 41/41 packages compile their tests
pnpm lint:coverage OK 46/46 packages linted
pnpm exec vitest run examples/schema-catalog/ Test Files 16 passed (16) / Tests 1866 passed (1866)
turbo run lint type-check --filter=@object-ui/example-schema-catalog 32 successful, 32 total
check:doc-snippets first returned exit 2 (PRECONDITION NOT MET — packages not built); the
verdict above is from the re-run after turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter).
The repo-wide pnpm lint / full pnpm test are CI's runs, not narrowed away here: the
package-scoped lint and type-check above cover every file this PR touches that either
gate reads, and the docs page is read by the four doc gates, all four run in full.
Scope
Only content/docs/components/layout/aspect-ratio.mdx, the five aspect-ratio fixtures,
the pin test beside them, and a changeset. The renderer is untouched — it is the contract,
and widening it to accept content would add a third spelling beside children and body
(AGENTS.md #0.1 direction: fix the metadata, not the renderer). No release-notes file is
touched. The changeset has an empty frontmatter: a docs page plus @object-ui/example-schema-catalog fixtures publish nothing.
Every shipped `components-layout-aspect-ratio` demo authored `content`, which
`packages/components/src/renderers/layout/aspect-ratio.tsx` never reads: it
reads `ratio`, `className`, `image` (with `alt`) and `children || body`.
Measured on c673282 by rendering each entry through the real SchemaRenderer
the way the docs gallery does — 3 elements, empty text, zero images for all
five: the Radix wrapper for the ratio itself and nothing inside it.
The photo demo now authors `image` + `alt`, the renderer's own declared inputs,
whose img element is sized to fill the box. The four card demos author
`children` at BOTH levels — the nested `card` renderer reads
`children || body`, `title`, `description`, `header` and `footer`, never
`content`, so moving only the outer key would have traded an empty box for an
empty card.
The page's Schema block published `content` as contract while omitting
`image`/`alt`; it now documents the keys the renderer reads.
Adds `examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx`:
category scope rather than a list of five ids, with a counter-probe that
renders the pre-fix shape and proves the assertion can still fail.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CRJge11jso9TpXRWFt1Z49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#6773
Re-derived on my own base (c673282) — 5 of 5, confirmed
packages/components/src/renderers/layout/aspect-ratio.tsxreads exactlyratio,className,image(withalt), andchildren || body. It never readscontent, andcontentis not among its declaredinputs. All five entries inexamples/schema-catalog/src/schemas/components-layout-aspect-ratio/authoredratio+contentand nothing else; a repo-wide sweep fortype: "aspect-ratio"found no sixthauthoring site.
Measured before the fix, rendering each entry through the real
SchemaRendererthe way thedocs gallery does (harness wrapper +
SchemaRenderer, no data source):16-9-aspect-ratio""square""4-3""ultrawide""video-aspect-ratio""Three elements is the harness wrapper plus the two Radix AspectRatio emits — the ratio box
and nothing inside it. The authored
contentreached the DOM only as the leaked hostattribute
content="[object Object]"(the objectui#5574 class;ui:aspect-ratioisalready ledgered for it in
packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx,so this PR files nothing new for it).
What each demo now authors, and why
The renderer was read first and defines the shape; the docs page was not consulted for it,
because the docs page is the thing that was wrong.
16-9-aspect-ratioimage+altimageis the renderer's own declared input and its arm draws an img element classedrounded-md object-cover w-full h-full, i.e. the photo fills the box. The old nested{ type: 'image', src, alt }node would render at natural size with no sizing class even once it was reachable.square,4-3,ultrawidechildrenon the box,childrenon the cardrenderChildren(schema.children ǀǀ schema.body), andrenderChildrenrenders a bare string directly. Both levels had to move: theui:cardrenderer readstitle,description,header,children ǀǀ body,footer— nevercontent— so moving only the outer key would have traded an empty box for an empty card.video-aspect-ratioclassNamebg-muted flex items-center justify-centerwas already authored.One presentational addition, named because it is not a pure key rename: the four card demos
now carry
flex h-full items-center justify-center.h-fullis what makes the card fillthe ratio box rather than sit as a strip at its top, and the centering is the intent
video-aspect-ratioalready authored verbatim; the other three now match it.After the fix, same measurement: the photo entry draws 4 elements with 1 img
(
srcandaltas authored), and each card entry draws 5 elements with its own label asthe tile's text (
"Square (1:1)","4:3 Ratio","21:9 Ratio","Video Player (16:9)").The Schema block
It published
content: ComponentSchemaas contract and omittedimage/alt, which therenderer does read and does declare. It now documents the read surface:
ratio,image,alt,children,className, with the two content modes stated in one line of prose.Two deliberate choices:
bodyis not documented. The renderer readschildren ǀǀ body, but a back-compatread is not a second authorable spelling — the
page:carddiscipline, verbatim(objectstack#5775 / PR fix(app-shell): the Studio grid selects only fields the server has (cloud#1652) #6281).
childrenis also what all nine siblingcomponents-layout-semanticfixtures author.SchemaNode, notComponentSchema.AspectRatioSchemadeclareschildren?: SchemaNode ǀ SchemaNode[];ComponentSchemais a real export but it is theblocks-component interface, not the child-node union.
The fence stays
plaintext: this file is one of the 80 incheck-doc-fence-languages.mjs'sshrink-only ledger, at 1 block, and it still holds 1 block.
Gate verdict: would anything have caught this?
Nothing did, and the reasons are measured, not inferred:
examples/schema-catalog/test/catalog-gallery-render.test.tsxdoes render all fiveand passes. Its non-vacuity control is
drewSomething = elements > WRAPPER_ELEMENTS ǀǀ text.trim().length > 0withWRAPPER_ELEMENTS = 2, and an empty ratio box clears it on the wrapper Radix draws forthe ratio itself (3 > 2). Its stronger control — the entry's own authored strings on
screen — is scoped to
NEWLY_REGISTERED_CATEGORIES, which this family is not in.BaseSchemais.passthrough()and carries[key: string]: any, so zod and tsc both acceptcontent— the objectui#6157class-3 shape exactly. These five are therefore not part of 53 in-repo schema files carry a registered ObjectUI type but fail
safeValidateSchema#6318'ssafeValidateSchemapopulation.check-doc-component-types.mjsrules the question out by name: "NOT in scope,deliberately: whether the snippet's OTHER keys are read by the renderer the type
resolves to."
contentasunknown-prop, the same warning it gives a typo,and nothing fails on it.
A corpus-wide "container fixture must render its authored content" sweep is NOT
source-decidable today, and this PR does not propose one. The predicate needs "which
authored key is this renderer's content slot", and the registry cannot answer it here:
ui:aspect-ratiodeclaresisContainer: trueyet declares notype: 'slot'input at all(its
inputsareratio,image,alt,className), whilepage:carddoes declarechildrenas a slot. A sweep would have to guess the slot for every renderer in the firstshape, which is the false-red generator that gets gates deleted.
What IS decidable is the per-family pin, so that is what ships:
examples/schema-catalog/test/aspect-ratio-demo-content-6773.test.tsx— the same shapetoast-demo-dispatch-6250.test.tsxtook for the same class of fixture correction. Itasserts at category scope, not over a list of five ids, so a sixth demo authoring the
phantom key fails without anyone remembering to add it:
src/altfor theimagearm, its authored slot strings otherwise);
contentshape and pins that it draws notext and no img — so the two assertions above cannot pass vacuously;
Verification
All at
8c093f6b5(the final commit), heavy commands serialized through the shared verifylock:
check:doc-snippetsfirst returned exit 2 (PRECONDITION NOT MET — packages not built); theverdict above is from the re-run after
turbo run build $(node scripts/check-doc-snippet-types.mjs --build-filter).The repo-wide
pnpm lint/ fullpnpm testare CI's runs, not narrowed away here: thepackage-scoped
lintandtype-checkabove cover every file this PR touches that eithergate reads, and the docs page is read by the four doc gates, all four run in full.
Scope
Only
content/docs/components/layout/aspect-ratio.mdx, the fiveaspect-ratiofixtures,the pin test beside them, and a changeset. The renderer is untouched — it is the contract,
and widening it to accept
contentwould add a third spelling besidechildrenandbody(AGENTS.md #0.1 direction: fix the metadata, not the renderer). No release-notes file is
touched. The changeset has an empty frontmatter: a docs page plus
@object-ui/example-schema-catalogfixtures publish nothing.Generated by Claude Code
Generated by Claude Code