Repository navigation
feat(types)!: SidebarSchema declares what the sidebar node draws; nine unread keys retire, variant takes the registration's enum (objectui#11465) - #11485
Conversation
…e unread keys retire, variant takes the registration's enum (objectui#11465) Retire title, nav, content, footer, position, defaultCollapsed, collapsed, width and collapsedWidth on both faces: a ?: never tombstone on the TypeScript face, a retirementTombstone refused by name with the reason and what to write instead on the zod face. variant becomes sidebar / floating / inset, the sidebar registration's own enum; default and bordered are refused with no alias window. The types README's composition example and examples/dashboard.ts author the node through children. zod-mirror-parity: the arm-count non-vacuity control moves from SidebarSchema.content to SidebarSchema.children, and the NavLinkSchema recursion row leaves the ledger (nothing registered reaches it once nav is a tombstone). Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…of the types README markdown-test-inputs' audit found the new test reads packages/types/README.md; record it so a README-only pull request runs the shard that reads it. Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
|
changeset-claim-re-read
|
✅ Console Performance Budget
The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it. 📦 Bundle Size Report
Size Limits
|
Contract reviewServed-tier: Inputs: card #11465 (body and its three comments — triage ruling ① Derived judgmentsPublic surface the diff touches:
Nothing in the diff is judged wrong. ② Semver level
Clause-②: yes — present and identical on the PR body, the claim ③ Boundary flagsDev deviations (os-dev-report
Triage's own lines: ⛔ no narrowing at the consumer (renderer untouched) — held; ⛔ no grace window ( Out-of-scope findings ( Check-runs on the head, read at 2026-10-02T16:18:40Z and again at 16:22:25Z (42 runs): at the second read 31 Implemented-by: VERDICT: PASS |
Brings in objectui#11485, #11486 and #11482; the parity ledger merged without a conflict (#11485 moved rows in other blocks). Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
Fixes #11465
Clause-②: yes
Dev run for objectui#11465 under the
domain:specseat's claim (comment5955764801), sessionhttps://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC. It executes triage's key-by-key direction (comment5954161531): retire the nine keys thesidebarnode never draws, givevariantthe renderer's enum, and move the types README example to children composition.collapsiblewas settled by PR objectui#11463 and is untouched.Note on spelling: GitHub strips tag-shaped text from bodies, so generic parameters and placeholders below are written as words.
The accept-set change, key by key
titlestringtextnode at the start ofchildrennavnavigation, objectui#11441), or compose items aschildrencontentchildrenfooterchildrenpositionleftorrightdefaultCollapsedcollapsedwidthclassNamewidth utility such asw-72collapsedWidthvariantdefault,bordered,floatingsidebar,floating,inset(the registration's enum);defaultandborderedrefused withinvalid_valueand a message that says to writesidebarpackages/types/src/navigation.ts): each retired key is a?: nevertombstone with a RETIRED docblock, sotscrefuses it by name.variantis the three-value union with@default 'sidebar'.packages/types/src/zod/navigation.zod.ts): each retired key is aretirementTombstonebuilt by a localretiredSidebarKey(key, why, instead), whose message readsRETIRED (objectui#11465, ADR-0049) — KEY on sidebar had no reader: WHY Instead: INSTEAD.variantisz.enum(['sidebar', 'floating', 'inset'], { error }).variant: 'sidebar'andvariant: 'inset'were refused (invalid_valueatvariant) on both faces before this change, measured; both validate now. Narrowing: the nine keys, andvariant: 'default'/'bordered', which validated on both faces before, measured.sidebar/floating/inset, and shadcn'sSidebarreadsvariantoff the spread.Zone 2 readings, measured before building
H1 — which keys the renderer reads after PR objectui#11463.
SidebarNodereadsschema.childrenand destructurescollapsible; everything else goes through its rest spread onto shadcn'sSidebar, which destructuresside,variant,collapsible,className,childrenand spreads the remainder onto the panel element. A one-off runtime probe (realSchemaRenderer, real registry, bare host at desktop width, bothcollapsibleforms, one key at a time against its absence; not committed) read:title: 'ZZTITLE'landed as an HTMLtitleattribute on the panel element, a native hover tooltip, never a heading;nav,content,footerlanded as attributes whose value is the string[object Object];position,widthlanded as inert attributes;collapsedWidthas a lowercasecollapsedwidthattribute, with React's unknown-prop warning;defaultCollapsed,collapsed: markup byte-identical to absence, React warned and dropped them;side: 'right'moved the panel to the right edge, andclassName: 'w-72'replaced the width class, so the probe could see a change when one happened.No key on the retire list is read, so all nine retire.⚠️
titleis the one whose spread had a user-visible effect (the tooltip). I read that as the rest-spread DOM leak, the same channel that writesnav="[object Object]", not as the node honouring "Sidebar title"; it is flagged in the report for the contract review.H2 — the retirement convention. Followed the in-repo precedent:
retirementTombstone()on the zod face plus?: neveron the TypeScript face (objectui#11434 PR 1, objectui#10387). For the two retired enum values the precedent is an enum with a namederror(object-data-table'sdrillDown.target, objectui#10685). ⛔ No bare deletion:BaseSchema's index signature would type itany, and.passthrough()would let it parse green.H3 — does
variantreach shadcn as authored. Yes, through the realSchemaRenderer. On the collapsible form,sidebardraws exactly what an absentvariantdraws;floatingandinseteach draw differently (padded panel;floating's inner panel takes the rounded, bordered, shadowed classes throughdata-variant).defaultandborderedchanged onlydata-variant; their classes weresidebar's. On the in-flow form (collapsible: false) every value, the three included, draws byte-identical markup: shadcn's not-collapsible branch ignoresvariant. The renderer is not changed. The behaviour is stated in the describe text, the TypeScript doc and the changeset, and pinned (below).H4 — corpus authors of a retired key or value. Swept every tracked file for an authored
sidebarnode (type: 'sidebar', the JSON spelling, andui:sidebar) acrossexamples/**,content/docs/**, every README, every package's tests and fixtures, the designer palettes, and the objectstack checkout. Each hit re-judged:packages/types/README.md, "4. Composable"navcollapsible: false, atextand abuttonchild): the rulingpackages/types/examples/dashboard.tsnav(three links)buttonchildren;collapsible: truekept as authoredcontent/docs/components/basic/sidebar.mdxand the fourcomponents-basic-sidebarcatalog documentscollapsible,className,childrencontent/docs/components/navigation/header-bar.mdxsidebarplaceholdersidebar-node-provider-10859, the app-shell one-provider pin,widget-dom-leak-sweep,container-declaration-ratchet, the cli ratchet)type: 'sidebar'hits are appnavigationshapes, not this nodeNo document anywhere authors
varianton asidebarnode.Pins
packages/types/src/__tests__/sidebar-declared-surface-11465.test.tstsc -p tsconfig.test.json): each retired key reads asundefinedon both faces;variantis exactly the three values on both faces;collapsiblekeeps its boolean (non-vacuity); a@ts-expect-errorper retired key and per retired value.Instead:and its prescription; lit controls (the same node without the key parses everywhere; an undeclared key stays green on the tolerant face).variant: the three validate on both faces;defaultandborderedare refused atvariantwithinvalid_value.sidebarliteral of the README's "4. Composable" fence is extracted and evaluated, never retyped, and validates on both faces; control: the example the README taught before is refused atnav.packages/components/src/renderers/__tests__/sidebar-variant-registration-11465.test.tsx: reads the live registration'svariantenum; every value it offers validates on both faces, and the two retired values are refused; through the realSchemaRenderer, the offered values draw pairwise-different markup on the collapsible form, and identical markup on the in-flow form.pnpm check:doc-snippetscompiles the fence against the builtdist.Ledger moves:
zod-mirror-parity.test.ts: the arm-count non-vacuity control moves fromSidebarSchema.content(now a one-arm tombstone) toSidebarSchema.children, the channel the node reads. TheNavLinkSchemarow leavesRECURSION_BREAKING_MIRRORS: its no-dead-entries case went red naming it, becauseSidebarSchema.navwas the only registered slot that reached it.NavLinkandNavLinkSchemastay exported.scripts/markdown-test-inputs.mjs: the new test is adjudicated as a reader ofpackages/types/README.md(its--auditnamed it as unadjudicated).Gates — all at
c95b819epnpm --filter @object-ui/types run type-check(all three legs; the new test andexamples/dashboard.tsconfirmed in the programs with--listFilesOnly)pnpm --filter @object-ui/components run type-check(the new test confirmed in the program)pnpm exec vitest run packages/types/sidebar-node-provider-10859,container-declaration-ratchet, schema-catalogsidebar-node-docs-10859/safe-validate-corpus-6318/component-fixture-declared-keys/catalog-gallery-render, the cli ratchet, the layout one-provider pin,markdown-test-inputs.test.tspnpm check:doc-snippets(closure built with its own--build-filter)pnpm check:doc-examples,check:doc-types,check:doc-fences,check:readme-exportscheck-changeset-no-major,-presence,-fixed,-overwrite;check:changeset-claims(report-only: 18 pending changesets name a touched file; each read, none is falsified by this diff)check:new-line-citations(0 new),check:control-bytescheck:component-surface-parity(report-only)ui:sidebarinterface-extra-key rows leave the census;read-of-tombstoned-keystays 0check:handler-key-reads,check-prompt-component-keys,check-test-path-roots,check-type-check-coverage,check:unreferenced-sources,check:published-dist,check-doc-expression-carriage,check-lint-coverage,markdown-test-inputs --auditcheck:sdui-registration-pinsLint, narrowed and declared.
eslint --no-inline-config --format jsonover the seven changed lintable files: 7 files, 0 errors (2 warnings, both on unchangedzod-mirror-parity.test.tslines whose inline disable--no-inline-configignores). The**/*.{ts,tsx}block ofeslint.config.jscovers six of them and the.mjsis covered too; the config sets noparserOptions.projectorprojectService, so linting is not type-aware and this diff cannot move a verdict in an untouched file. The repo-widepnpm lintand the full test farm are CI's.Ablation and reverse verification
Each leg ran through objectstack's
scripts/ablation-replace.mjsin wrap mode on the committed tree: the mutation proven on disk (anchor count 1 to 0, blob changed), the restore proven (blob equals HEAD,git diff HEADempty). vitest resolves@object-ui/typesto source through the root alias, so the vitest legs need no rebuild.variantenum back todefault/bordered/floating: 7 failed (four in the types pin, three in the components pin).titleaccepting a string again: 2 failed (the refusal-path and message cases fortitle).title?: stringagain:tsc -p tsconfig.test.jsonred with TS2344 (theEqualrow), TS2578 (the unused@ts-expect-error) and TS2322 inzod-mirror-parity.test.ts's drift ledger.navline put back into the fence:pnpm check:doc-snippetsjudged 1 block failed, TS2322 at the README'snavline (a NavLink-shaped array is not assignable toundefined), which proves the gate read the rebuilt.d.ts. The first attempt was a no-op (my anchor was a prefix of its replacement, and the tool refused to count it as landed); it was re-run with a non-prefix anchor.Acceptance notes (observations, not filed)
side: the registration offersside(left/right) and shadcn honours it, butSidebarSchemadoes not declare it, so the strict face refusesside: 'right'(unrecognized_keys) while the render honours it.check:component-surface-parityalready reports it (ui:sidebar.side, input-outside-keyset). Thepositiontombstone deliberately does not point atside.varianton the in-flow form draws nothing (H3); not changed, by the ruling.NavLink/NavLinkSchemastay exported but no longer type any declared member; whether they retire too is an open question in the report, outside the claimed file surface.SidebarNodeforwards every unread key onto the panel element, which is how[object Object]attributes arose. After this change the validator refuses the nine; the renderer is untouched (⛔ no consumer-side narrowing).content/docs/components/basic/sidebar.mdxdoes not documentvariant; its sentence on what the renderer reads is not made false by this change, and it is outside the claimed surface.mainwas merged at2c0ddf226(PR objectui#11477 edits other blocks of the types README and the imports ofexamples/dashboard.ts); this diff's hunks there are thesidebarliterals only.Generated by Claude Code