feat(types)!: the widget slot's metric-card arm declares MetricCard's registered inputs, and the widget component slot takes the arm (objectui#11467) - #11482
Conversation
… registered inputs, and the widget `component` slot takes the arm (objectui#11467) `DashboardWidgetSlotComponentSchema` declares `title`, `value`, `icon`, `trend` and `trendValue` (the sixth input, `description`, is the base's member) with the types of the props `MetricCard` reads, on the TypeScript face and on the zod twin. `value` is required, as the registration marks it. The widget's `component` slot takes the arm first, then any other node, on both faces. The objectui#11022 side table of input names is retired with its walker arm: every name it held is now a member. Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…valued card; `value` is refused at its key when absent (objectui#11467) Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
…n both READMEs and the mdx twin; changeset (objectui#11467) 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 objectui#11467 (body, the claim and the os-dev report), PR objectui#11482 (body, 11-file list, net diff +572/-382), the head's check-runs, and REST reads of files at the head where a diff claim needed its source ( ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named:
② Semver level
What fails ②. A
The Changeset Claim Re-read gate is report-only by design and its went-false reading keys on a pending body naming a FILE this change touches, which neither entry does, so green there is not a verdict on this. Remedy, one commit on the branch: append a dated note to the two entries, retiring the quoted readings and naming ③ Boundary flags
Implemented-by: VERDICT: FAIL |
…ctui#11467 falsifies Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
✅ 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 objectui#11467 (body, the claim 5954392494 and the os-dev report 5956160782; the card carries no ruling comment), PR objectui#11482 (body, 13-file list, net diff +576/-382 against Delta from the earlier head: ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named:
② Semver level
What fails ② on this head. The same class, on a third pending entry the earlier record did not name and this head leaves untouched: ③ Boundary flags
Implemented-by: VERDICT: FAIL |
… accept-set readings objectui#11467 falsifies; scope the README's validate clause to a card in widgets[] Claude-Session: https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC Co-authored-by: Claude <noreply@anthropic.com>
✅ 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 objectui#11467 (body, the claim 5954392494 and the os-dev report 5956160782; the card carries no ruling comment and no sweep list), PR objectui#11482 (body, 14-file list, net diff +581/-382 against Delta from the earlier heads: ① Derived judgmentsEvery accept-set and public-surface change the diff implies, each named:
② Semver level
So ② holds on this head: the changeset says what the diff publishes, and every pending reading this head falsifies now carries its dated note. ③ Boundary flags
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 #11467
Clause-②: yes
Parent: objectui#8347, which keeps the index-signature removal. This PR is its F2 half and leaves the signature in place: every commit compiles with
BaseSchema's signature present.What this does
Seat answer A, executed.
DashboardWidgetSlotComponentSchemais the widget slot's component arm. It now declaresmetric-card's registered inputs as members, typed as the propsMetricCardreads. The zod twin declares the same members. The widget's legacycomponentslot takes the arm first, then any other node, on both faces.titlestring | I18nLabel, optionalI18nLabelSchema(defaults stripped), optionalMetricCardProps.titlevaluestring | number, requiredz.unionof string and number, requiredMetricCardProps.value; the registration marks itrequirediconstring, optionalz.string(), optionalMetricCardProps.icontrend'up' | 'down' | 'neutral', optionalz.enumof the three, optionalMetricCardProps.trendtrendValuestring, optionalz.string(), optionalMetricCardProps.trendValuedescriptionBaseSchemamember, not restatedstring | I18nLabelon both facescomponentchanges too. On the TypeScript face it isDashboardWidgetSlotComponentSchema | SchemaNode. On the zod face it isz.unionof the arm andBaseSchema."By reference" across a package boundary.
@object-ui/typescannot import the registration orMetricCard, so the members are transcribed. The registering package holds them to both sources inmetricCardRegisteredInputsStrictFace-11022.test.ts:BaseSchemaandlayoutis a registered input;valueis required, as the registration marks it;trendis exactly the registered enum;MetricCardPropsfor the six inputs.No second spelling. objectui#11022's side table of input names (
DASHBOARD_WIDGET_SLOT_REGISTERED_INPUTS, throughdeclareRegisteredInputs) recorded these same names so the strict walker could admit them. Every name it held is now a member, so the table,declareRegisteredInputs/registeredInputsOf, and the walker's branch for them are removed. They were internal and not exported, and the arm was their only caller.A guard instead of the table's
satisfies. The arm's zodtypeisz.enumoverDASHBOARD_COMPONENT_WIDGET_TYPESwith asatisfiescheck against the one-member listmetric-card. A second member of the closed set is therefore a compile error until the arm is split per component. Without the check, the new member would inherit the card's props.Measured first
H1. Which inputs
MetricCardreads. Two instruments, both at based93e53f5.MetricCardPropsmember it looked up the references to the member symbol, then the uses of the destructured binding in the render body.title,value,icon,trend,trendValueanddescriptionare each destructured and read. A standalonetscprogram over the same config: 0 errors.DashboardRendererdrew the card through the realSchemaRendererandComponentRegistry, one key removed at a time from a full card. The probe was a one-shot test file, deleted after the run.title,valueordescriptionchanged the text.iconchanged the drawn svg count.trendortrendValuechanged the text, the svg count and the trend colour class. Each is drawn only together with the other.trendfrom up to down or neutral moved the colour class from green to red or yellow.MetricCard's rest spread (objectui#4426, pre-existing).All five keys the card named are read.
descriptionis the sixth input and is already declared onBaseSchema.H2. What each zod face judged at base.
description(throughBaseSchema). The other five passed through the catchall unjudged.unknown), sotrend: 'sideways',title: 7andvalue: {}all parsed on both faces.componentslot wasBaseSchemaalone. The strict face therefore refused a card'stitle,value,icon,trendandtrendValuethere asunrecognized_keys. That includes the plugin-dashboard README's own legacy-envelope example.H3. PR objectui#11460 had landed (
9d1c0bff) before this branch was cut fromd93e53f5.H4.
zod-mirror-parity.test.tsdid not move. The file is untouched, and the types suite is green with it.componentstays inKnownDrift(the primitive arms ofSchemaNode), and so doeswidgets.Accept-set change (Clause-② yes)
widgets[]with all inputs well-typedwidgets[],trend: 'sideways'/title: 7/icon: 5/trendValue: 3/value: {}widgets[]withicon,trendortrendValueand novaluewidgets[]with onlytitle/description/layoutand novaluecomponentslot with its inputs (README example)componentwithtrend: 'sideways'BaseSchemafallback)trend)component({ type: 'chart' })someProp)The TypeScript face gains no accepted key while the signature stands. The read type of
titleoff awidgets[]entry moves fromanytostring | I18nLabel. Six consumer packages and two examples type-check unchanged; see Gates.The card's own reading, two legs
The probe used the parent's mutation: only
BaseSchema's documented index signature deleted, with@object-ui/typesrebuilt so thatdist/base.d.tskeeps 1 signature. Thentsc -p packages/types/tsconfig.test.jsonandpnpm check:doc-snippetsran. The mutation went throughscripts/ablation-replace.mjsfrom the objectstack checkout: anchor 1 → 0, blobce2bc9e31dc6→93c3221b8301, restored to blob == HEAD,git diff HEADempty. Both legs ran in this container, in two worktrees.d93e53f5(control)35a89136valuex3 (README usage copy;componentslot x2),titlex1 (9256 accepting literal), plus probesomePropsomeProp, probebogusonlycheck:doc-snippetspackages/plugin-dashboard/README.mdThe
bogusline at head is the arm-7952 file's "MEASURED LIMIT" constant. That corner shuts once thecomponentcard'svalueis declared, and the constant says to delete it then. This is noted for objectui#8347's last PR. The restore leg rebuilt types:dist/base.d.tssignatures 2, src 2,git diff HEAD0.Reverse verification of the new pins
Each leg deleted the members with
ablation-replace.mjson the committed tree (anchor 1 → 0, blob moved), ran, and restored (blob == HEAD,git diff HEADempty). The direction was predicted before each run: red.value-required row, the arm-shape pin, the README-literal strict pin, plugin-dashboard UNDER-admission x6, DECLARED, typed-members, and the saved-layout strict parse.Equalpins went false: the five members and the zod twin. Three@ts-expect-errordirectives went unused (TS2578). Thetitleread pin went false.Corpus re-judged
scripts/measure-strict-authoring-face.mjsran at base and at head over the schema catalog, the docs fences, the apps and the types sources: 610 documents, 2244 nodes.documentsRedTodayWholeDocumentis 23 → 23 andnodesRedToday30 → 30.metric-cardin the corpus carries a stringvalueand atrendfrom the three, so no document's verdict moved on the shipped faces.type: 'card'widget withchildren, in the plugin-dashboard mdx) also lists the component arm's missingvalueamong its union errors now. Its verdict is unchanged; only the error shape moved.Pins moved
strict-widget-slot-registered-inputs-11022.test.ts. Several blocks are rewritten:layoutand the five inputs";bogusandvalue-required fixtures now carry avalue.metricCardRegisteredInputsStrictFace-11022.test.ts. The OVER-admission test, which read "strict keys minus tolerant keys", is replaced by DECLARED-both-directions on both faces. Typed-member andMetricCardPropspins are added. The UNDER-admission andlayoutfixtures carry the requiredvalue.dashboard-widget-slot-component-arm-7952.test.ts.valueis added to two existing literals so each pin keeps refusing for its stated reason. A new objectui#11467 block holds the member types, the twin parity, the TypeScript refusals, thecomponentslot type, the README's two literals on both faces, and thecomponent-slot strict judgment.Files outside the claimed surface (declared)
The claim's surface did not list five files. Each one is the same accept-set change, not a side quest:
packages/types/src/zod/node-derivation.tsandpackages/types/src/strict-authoring-face.ts: the side table and its walker branch are removed, because every name in it is now a member.strict-widget-slot-registered-inputs-11022.test.ts(types) andmetricCardRegisteredInputsStrictFace-11022.test.ts(plugin-dashboard): they pinned the passthrough-plus-side-table state that the ruling replaces.packages/types/README.md: its strict-face paragraph described the side table.The renderer, the index signature and
SchemaNodeare untouched.Gates (head
35a89136)pnpm --filter PKG run type-check(each echoedtype-check): exit 0 for types, plugin-dashboard, core, sdui-parser, plugin-list, plugin-designer, app-shell, example-schema-catalog and example-console-starter. All 40 packages were built first, with turboTasks: 40 successful.packages/types/: 330 files, 8770 tests passed;packages/plugin-dashboard/: 160 files, 1532 passed, 6 skipped (pre-existing);examples/schema-catalog/: 39 files, 2235 passed;packages/sdui-parser/: 20 files, 289 passed;check:doc-snippets: 776 of 776 judged, 0 failed.check:doc-examples: 87 fail, all 87 declared.check:doc-types: green.check:spec-symbols,check:component-surface-parity,check:new-line-citations,check:control-bytes,check:changeset-claims,check:pending-changeset-literals,check:doc-fences,check:readme-exports,check:test-path-rootsandcheck:handler-key-reads: exit 0.node scripts/check-changeset-no-major.mjsandnode scripts/check-changeset-presence.mjs: exit 0.check:sdui-registration-pins: NOT MEASURED. It exits 2 with "No console build to weigh", a prerequisite the run did not meet. No registration orsideEffectsarray moved.node scripts/check-governed-queue-guard.mjs --teston the 11 paths: NOT GOVERNED.Overlap
mainadvanced 6 commits afterd93e53f5, objectui#11477 (F3) among them.git merge-treeagainst it is clean. The one shared file ispackages/types/README.md, in a different section. Nothing onmaintouchescomplex.ts,complex.zod.tsorzod-mirror-parity.test.ts.SchemaNode.componentisDashboardWidgetSlotComponentSchema | SchemaNode, so it composes with whateverSchemaNodebecomes, and the card stays typed by the arm.Acceptance notes
valuerequired is enforced on the arm, not on everymetric-card. A{ type: 'metric-card', title }entry with no card-only input still parses on both faces through the widget arm. The widget arm parses it becauseDashboardWidgetTypeSchemaalso carries the closed component types (objectui#4600). Reported to the seat as a finding.componentslot. A card that fails the arm still parses on the tolerant face through theBaseSchemafallback, and the TypeScript union has the same corner. Only the strict face refuses it. This is recorded as a MEASURED LIMIT in the arm-7952 file, not ruled.type: 'card'widget withchildrenis refused by the validator today. The README twin was repaired by objectui#7035; the mdx was not. Reported to the seat as a finding.The session that made this change is
https://claude.ai/code/session_01XvhGmGAP79ZB8swnkapxPC.Generated by Claude Code