feat(types)!: refuse both content channels on the family-D re-measure's residual — markdown, chart, bar-chart, code-editor, detail, report, list-view and six designers (objectui#9256) - #11003
Conversation
…al (objectui#9256) The family-D re-measure keyed the population on each registered key's type LITERAL rather than on the renderer's declared props type. Thirteen declarations whose renderer reads neither `body` nor `children` still accepted `children`: - markdown, chart, bar-chart, code-editor, detail, report: `?: never` pair on the TypeScript face and two `retirementTombstone` members fed one `neitherContentChannelGuidance` string on the zod mirror; - list-view: the zod mirror only, since `ListViewSchema` is `z.input` of that mirror intersected with its runtime props; - the six designer faces (page, data-model, process, report, object manager, field): TypeScript only, since none has a zod mirror. New pin file content-channel-remeasure-9256.test.ts. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
`@object-ui/types` minor with an explicit BREAKING note and a migration line, the spelling the earlier family-D slices used under this repo's no-major rule. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
One conflict, in packages/types/src/zod/objectql.zod.ts: main added `SpecRuleConditionSchema` (objectui#10946) directly above `ListViewSchema`, where this branch added `LIST_VIEW_NEITHER_CHANNEL`. Both are module-level constants read by `ListViewSchema`; both are kept, main's first. No other file conflicted. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DuWo5bdP9SdVebamn99GGk
|
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: Isolated adversarial at-tier review of PR objectui#11003 (card objectui#9256, family D re-measure; ① Derived judgments1. The thirteen narrowed declarations — renderer readership, re-derived. For each row I read the registration that owns the bare key, every alias or namespaced twin of the same literal (the registry snapshot in
No registration of the thirteen declares a 2. Producers, re-run twice. (a) The repo's own instrument, 3. Shape. Six declarations carry 4. Refusal messages, all read. Every zod string is the corrected objectui#10928 clause (the builder at head says "no render-time error or warning and no element; only the parser tier's 5. Pins and ablations. The 97-test pin covers all thirteen: seven zod arms × two channels × five assertions plus controls, and 26 6. Downstream. 7. CI and commits. Head ② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
Part of #9256
Clause-②: yes
Clause-②
yes, as the claim declared: thirteen published declarations stop accepting an authoredchildren. Each one's renderer reads neither content channel, so the key rendered nothing; it is now refused by name. A published accept set narrows, so a contract review is owed before landing.This is the re-measure that release
5867503945on objectui#9256 asked for, plus the slice it found. The card stays open: the re-measure also found twenty neither-channel registrations it does not narrow here (see "Still open" below), so this PR saysPart of.What changed
markdown,chart,bar-chart,code-editor,detail,report:body?: neverandchildren?: neveron the TypeScript face (MarkdownSchema,ChartSchema,BarChartSchema,CodeEditorSchema,DetailSchema,ReportComponentSchema), and tworetirementTombstonemembers fed oneneitherContentChannelGuidancestring on the zod mirror. Both stay MEMBERS, sozod-mirror-parity's key sets stay equal.list-view: the two members on the zod mirror only.ListViewSchema's TypeScript face isz.inputof that mirror intersected withListViewRuntimeProps, so the members are what refuse both keys on both faces.page-designer,data-model-designer,process-designer,report-designer,object-manager,field-designer):body?: neverandchildren?: neveron the TypeScript face only. None of them has a zod mirror, so that face is the only gate, as it was fornl-query.bodywas already refused on all of these faces, byBaseSchema(objectui#6771). It is restated because that refusal nameschildrenas the remedy, andchildrenis dead here too.packages/types/src/__tests__/content-channel-remeasure-9256.test.ts..changeset/9256-remeasure-content-channels.md:@object-ui/typesminorwith an explicit BREAKING note and a migration line, the spelling the earlier family-D slices used under this repo's no-major rule.Why these were missed
The first family-D sweep keyed its population on the renderer's DECLARED props type. A registration typed with a published declaration was family D; one typed with an inline or local type was filed as "no published face to narrow". That second bucket was wrong for every registration whose
typeLITERAL has a published declaration anyway: the declaration an author types against is chosen by the literal, not by the renderer's props type. The re-measure keys on the literal.The measurement
Taken on
origin/main328abeb55, and re-taken on this PR's merge head1ac8cb627(aftergit merge origin/main); both runs give the same verdicts. The full table is in theos-dev-reportcomment on objectui#9256.check:registry-bare-names --json(the shipped enumerator: literal, loop and indirect claims) joined to a TypeScript compiler-API walk of everyComponentRegistry.register/registerLazycall site in non-test source. Every enumerated claim maps to a call site, and the only call sites without a claim are the data-driven registrars the enumerator reports as unresolved plus two generic forwarders inpackages/core(PluginScopeImpl,WidgetRegistry). 23 packages callComponentRegistry.register;apps/consoleregisters lazily only;packages/coreholds the two forwarders. That is the card's 25.tsconfig.json(the workspace packages,apps/console, the examples), on a BUILT tree, 0 unresolved-module diagnostics. Every.body/.childrenread (property access, string element access, object destructuring) is filed under the declared type of its receiver, with the enclosing function recorded so anany-typed read can be attributed. Lit controls fire in the same run:ButtonSchema,DivSchema,CardSchemaandContainerSchemaeach file achildrenread.packages/types/dist/index.d.tsread with the compiler API (every exported type and every member of every exported union, fortypeliterals and the declared type ofbody/children), and the built zod mirror read BEHAVIOURALLY:AnyComponentSchemaparses{ type },{ type, children }and{ type, body }for every arm literal, and an issue counts against a channel only if it sits at that channel's path and is absent from the bare document. Controls in the same run:divacceptschildren,divrefusesbody,dialogrefuseschildren, an unknowntypeis refused.validateChildreninpackages/corenow readsschema.childrenonly (the card'schildren || bodypremise is gone). It,sdui-parser's parse andvalidateTree,cli validateandpage:tabs' related-list / attachment probes walk descendants to validate or count; they render nothing.SchemaRendererdestructures both keys out of the props bag it spreads. The only livechildren || bodyfallbacks left arepage:card,page:section,page:footerandpage:sidebar(family E, out of this card).Per-key readership for this slice
markdownplugin-markdown:markdown, inline props typechildrenreads are hast nodes and React propschildrenlive → bothneverchildrenaccepted → both refused by namechartplugin-charts:chart(+chart:baralias),ChartRenderer's inline props typechildrenof its own componentsbar-chartplugin-charts:bar-chart,ChartBarRenderer's inline props typedata,dataKey,xAxisKey,height,className,colorcode-editorplugin-editor:code-editor,CodeEditorRenderer's inline props typedetailview:detail(owner of the bare key) →DetailView, typedDetailViewSchemaDetailViewSchema; the package's other reads areFeedItem.body,err.body, a Reactchildrenandrecord:alert's text propreportplugin-report:report→ReportRenderer's three pathsdocument.bodylist-viewplugin-list:list-view, typedListViewSchemaobjectName,viewType,columns,filter,sort,optionsand moreplugin-designer:*, noschemaprop: the node's keys are spread into the component as propsNavigationEntryItem.childrenand a provider's Reactchildrenchildrenlive → bothneverNone of these registrations declares a
childrenslot input (objectui#9910).Producers.
pnpm census:body-dialect --keysover every key in this slice and the twenty still open, withdiv/card/page/page:cardas lit controls in the same pass, at the merge head: the one node that authorschildrenon any of them is this PR's own nested-refusal case in the new pin file.bodyappears only in tests (record:alert's text prop, and onedetailnode in the sdui-parser dialect pin). The controls readchildrenondiv177,card183,page54. Nothing was migrated.Red on base, then green
Predictions were written to a file before any mutation. Each leg mutated one committed file through
ablation-replace.mjs(anchor count and blob hash proven on disk), measured, and restored under its trap; the restore was proven by the blob hash equal to HEAD and an emptygit diff HEAD, and the tree was clean at the end. Run on7d451884d, then again on the merge head1ac8cb627because the merge touched two of the three mutated files; both runs read the same.tsc -p packages/types/tsconfig.test.jsonMarkdownSchema's zodchildrenmembermarkdown.childrenrefusal rows and the nested control; the member row stays green, as predicted, becauseBaseSchemaalready declares the keyzod-mirror-parity(the pair and the keychildren)FieldDesignerSchema'schildren?: never(no mirror)field-designerpinChartSchema'schildren?: neverchartpin plus 1 × TS2322 inzod-mirror-parityConsumer-side reverse validation. A probe file inside
packages/components, compiled with that package's options, resolves@object-ui/typestopackages/types/dist/index.d.ts. Authoringchildrenon eight of the narrowed types gives 8 × TS2322; thedivcontrol withchildrencompiles. The probe is deleted in afinallyand the tree is clean.Gates (merge head
1ac8cb627; heavy runs through the shared verify lock; exit codes by redirect-then-capture)turbo run build(all but site and console)@object-ui/typestype-check+vitest run packages/types/type-check: every package downstream of@object-ui/typesthat has the script, except@object-ui/siteand the repo root (38 packages, three batches)type-check: Done, exit 0vitest runcli, sdui-parser, plugin-markdown, plugin-editor, plugin-report, plugin-designervitest runplugin-list, plugin-charts, plugin-detailvitest run scripts/vitest run examples/schema-catalog/vite build, thencheck:eager-closure·check:sdui-registration-pinscheck:control-bytes·check:new-line-citations(0 new) ·changeset:check·check-changeset-presence·check:changeset-claims·check:pending-changeset-literalscheck:doc-types·check:doc-snippets·check:doc-examples·check:skill-examples·check:doc-fences·check:doc-example-idscheck:handler-key-reads·check:spec-symbols·check:component-surface-parity·check:readme-exports·check:registry-bare-names·check:prompt-keys·check:test-path-roots·pnpm check--testover the 12 pathsAGENTS.md: exit 3)Bundle Analysis: measured, not argued. The TypeScript members emit no JavaScript. The zod members land in the console build's
types-zodchunk only (the one chunk carrying the new refusal strings), and that chunk is not in the eager-closure report the build writes: the root@object-ui/typesentry imports the zod mirror for types only, and the one runtime importer of@object-ui/types/zodin the console's graph,plugin-map'sObjectMap.tsx, is lazy.check:eager-closurepasses at1ac8cb627.NOT MEASURED, left to CI: the
pnpm testshards beyond the packages above (app-shell, components, core and the rest),@object-ui/site's type-check,test:dist, E2E, and the repo-widepnpm lint(the local lint read the touched files only; this repo's eslint config enables no type-aware rule, so no untouched file's verdict can move).Serial constraints
At branch time no open PR touched these files' sites.
origin/mainthen gained 13 commits;git merge origin/main(no rebase, no force-push) conflicted only inzod/objectql.zod.ts, where objectui#10946 addedSpecRuleConditionSchemadirectly aboveListViewSchemaand this branch addedLIST_VIEW_NEITHER_CHANNEL. Both constants are kept, main's first. Open draft PR objectui#10930 edits azod/complex.zod.tsdocblock; this PR does not touch that file. Open PR objectui#10997 editszod/objectql.zod.tsatObjectMapConfigSchema, a different site from this PR'sListViewSchemablock;git merge-treeof this head againstorigin/main665025908is clean. Whichever of the two lands second re-mergesmainonly if a conflict appears.Still open — measured, not narrowed here
Twenty registrations read neither channel and have a published face that still accepts
children. They are the next slice, and two questions route it:record:activity,record:details,record:discussion,record:highlights,record:history,record:path,record:quick_actions,record:reference_rail,record:related_list,record:alert(itschildrenonly),page:header,page:tabs,page:accordion,element:text,element:number,element:button,element:divider,object-metric,object-master-detail-form. Each arm is zod-only and inheritschildrenfromBaseSchema. The spec's ownPageComponentSchema(installed 17.4.0) refuses a flatchildrenon a page-component node (unrecognized_keys, measured onrecord:details,page:header,page:tabs,page:cardandelement:text). Their module belongs to open card objectui#10872, whose next batch edits the same arms, and the element/page/object blocks areany-typed, which this card's brief lists as family E while ruling5861449497(Q2 A) treated anany-typed registration with a published arm as family-D-shaped. That conflict is in the report, not decided here.metric-card: its TypeScript faceDashboardWidgetSlotComponentSchemaacceptschildren, and its zod twin is a private routing arm inside the dashboard widget slot's union, so a member refusal there needs its own design.Acceptance notes — out of scope, not fixed here
record:alertreads a key namedbodyas its message TEXT (throughreadProps, which merges the flat node key withproperties.body), while its zod arm refuses a flatbodywithBaseSchema's message, which nameschildrenas the remedy. Carrier: objectui#10872's flat-props batch.content-channel-family-d-9256.test.tsabove the chatbot rows still saysbodyis ACCEPTED onchatbot-enhancedandchatbot-floating; the same file's LIVE CONTROL now asserts both refuse it. Carrier: the next family-D slice.Generated by Claude Code