fix(components): page container registrations publish children, not the retired body (#4027) - #4070
Merged
Merged
Conversation
… the retired `body` (#4027) `inputs` is the published authoring surface — the Studio designer panel, `sdui.manifest.json`, `sdui-intrinsics.d.ts` and the JSX-page prop whitelist all come from it — and two `page:*` container declarations had drifted from the contract in opposite directions. `page:card` published `{ name: 'body', type: 'slot' }`. objectstack#5775 (PR objectstack#6281, merged 2026-08-07, ADR-0087 D2) retired `PageCardProps.body` and declared `children` in its place; the designer was teaching a key the contract now rejects by name. `page:section` / `page:footer` / `page:sidebar` declared no `inputs` at all, so nothing could authorize the child list those three exist to render. The same PR replaced their `EmptyProps` entries with the shared `PageContainerProps`, whose single key is `children` — mirrored here as one shared literal. Rendering is untouched in both directions: the renderers keep READING `body` for documents stored under the old contract, which the ADR-0087 D2 conversion rewrites at load time. A back-compat read is not a second authorable spelling. The pinned `@objectstack/spec@17.0.0-rc.5` predates #6281, so the repo-wide parity gate reads all four correct declarations as off-spec; they are registered as stale-pin exemptions in both directions, alongside the `element:record_picker` trio from the same upstream issue, and the gate's own `no stale exemption` test deletes them when the pin moves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Qqyix2QcnpUC9XeYVDzx3
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
yinlianghui
marked this pull request as ready for review
August 10, 2026 04:11
This was referenced Aug 10, 2026
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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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 #4027
inputsis the published authoring surface, not documentation: the Studio block designer builds its panel from it,sdui-parser'sgen-manifest.tsserializes it intosdui.manifest.jsonandsdui-intrinsics.d.ts, andpage.tsxbuilds the JSX-page prop whitelist from it. Twopage:*container declarations had drifted from the contract, in opposite directions.Retired key still offered.
page:cardpublished{ name: 'body', type: 'slot' }. objectstack-ai/objectstack#5775 (PR objectstack-ai/objectstack#6281, merged 2026-08-07, ADR-0087 D2) retiredPageCardProps.bodyand declaredchildrenin its place — one composition slot with one spelling, the onegrid,flex,page:sectionandpage:tabsitems already use. The designer was teaching a key the contract now rejects by name.Declared slot not offered at all.
page:section/page:footer/page:sidebarregistered with noinputswhatsoever, so nothing could authorize the child list those three components exist to render. The same upstream PR replaced theirEmptyPropsentries with the sharedPageContainerProps, whose single key ischildren; all three now publish that slot from one shared literal, mirroring the spec's single definition.Rendering is untouched — deliberately
The renderers keep READING
body:PageCardRendererstill resolvesschema?.body ?? schema?.children, and the three thin containers still readschema?.children || schema?.body. Documents stored under the old contract keep rendering until the ADR-0087 D2 conversion rewrites the key at load time; deleting the read now would blank an existing card's content silently. A back-compat read is not a second authorable spelling — the same split thepage-header-subtitle-aliassequencing already established inpackages/layout.No validation verdict moves either, which is what keeps this to the authoring surface:
sdui-parser/src/validate.tslistschildreninBASE_PROPS, so it was never anunknown-propand does not become one;codegen.ts:emitInterfacefiltersslotinputs out of the generated.d.ts, whereSduiBaseProps.childrenalready types it;isContainer: truewas already set on all four blocks — that is what authorizes children to the parser'snot-a-containercheck.The pin lag, and why it did not block this
The dispatch asked this to be measured rather than assumed: objectui pins
@objectstack/spec@^17.0.0-rc.5, and rc.5 predates objectstack-ai/objectstack#6281. Verified in the installed package, not inferred —node_modules/@objectstack/spec/dist/ui/index.d.tsdeclaresPageCardPropswithbodyand nochildren, exports noPageContainerProps, and still maps the three thin containers toEmptyProps.It does not fork the fix: registration
inputsare objectui-side designer descriptors (plain literals), so nothing here type-checks against a spec export. What the lag does reach isapps/console/src/__tests__/registry-inputs-spec-parity.test.ts, which derives its expectations fromComponentPropsMapat runtime and therefore reads all four correct declarations as off-spec. That file already carries six stale-pin entries from this same upstream issue (theelement:record_pickertrio, both directions), so the fourchildrenentries and thepage:card.bodyentry follow that established discipline verbatim — each naming #4027 and the upstream PR, and each deleted by the gate's ownno stale exemptiontest when the pin moves. Consistent with that file's own note: "EXPECTATIONS ARE DERIVED, NOT RESTATED … which is also how the stale-pin exemptions below are meant to resolve themselves."Two list moves follow mechanically, both pinned rather than silent: the three containers move from
EXPECTED_WITHOUT_INPUTStoEXPECTED_COVERED, and out ofpublic-contract.test.ts'sPROP_LESS_CURATED(whose "reads nothing beyondchildren" was doing double duty as "therefore declares nothing"). One new carve-out was needed and is self-clearing: the gate's non-empty-shape probe guard would otherwise accuse itself of being broken for three blocks whose pinned shape is legitimately{}—SPEC_SHAPE_EMPTY_ON_THE_PIN, with an assertion that fails the moment those shapes stop being empty.Tests
New pin file
packages/components/src/__tests__/page-container-authorable-keys.test.tsx, both directions, asserting the merged contract literally rather than deriving from the stale pin (with the reason written down):page:carddoes not offerbody; offerschildrenas aslot; its full input list is pinned exactly (title,bordered,children,footer) so the respelling cannot widen into a second surface;['children'], is stillisContainer, and all three share one literal;bodyand underchildren, so the sequencing guard fails if someone "finishes the job" by deleting the read.Reverse verification (fix removed with
git checkout origin/main -- containers.tsx, restored after — nevergit stash): 7 of the 15 new assertions go red, and the 8 that stay green are exactly the ones that must not move (isContainer, both back-compat render paths). The console gates go red too — 7 failures across the two files.tsc --noEmitclean inpackages/componentsandapps/console(after building each package's dependency closure — the stale-artifact trap otherwise reports missing.d.tsas if this change broke the imports).eslinton the four changed files: 0 errors (83 pre-existingno-explicit-anywarnings incontainers.tsx).pnpm check:control-bytesOK;node scripts/check-changeset-presence.mjsreports the changeset.Generated by Claude Code