feat(types): the widget slot's component arm declares the spec's widget layout (objectui#11070 round 11) - #11387
Conversation
…et `layout` (objectui#11070 round 11) `DashboardGridLayout`'s Save Layout (`mergeLayoutIntoSchema`) writes `layout` onto every `widgets[]` entry, a `metric-card` component node included, and the grid positions each entry by it. The strict authoring face refused it on the component arm as `unrecognized_keys: ['layout']`. - `DashboardWidgetSlotComponentSchema` (zod and TypeScript) declares `layout` by reference to `@objectstack/spec`'s `DashboardWidgetSchema.shape.layout`, the member the strict widget arm already carries. - The 11022 pin parses Save Layout's own output on the strict face, with a malformed-`layout` control refused on both faces. - Read-site comments in plugin-dashboard now say what is true: `layout` is declared on both arms; `options.description` is read and served-path written but not declared by the spec (an open decision). Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh Co-authored-by: Claude <noreply@anthropic.com>
…t arms; changeset and dated notes (objectui#11070 round 11) - The 11022 types pin reads the slot arm's key set as the base's plus `layout`. - plugin-dashboard README, `plugin-dashboard.mdx`, `schema-reference.md` and the types README say that both arms declare `layout`; the "read a widget key through `DashboardWidgetSchema`" example reads `colorVariant` instead. - `.changeset/11070-dashboard-keys-round11.md` prices the widening as minor and states the tolerant-face narrowing for a malformed `layout`. - Dated notes on the 11348 and 11022 changesets, whose arm descriptions this round makes false. Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh Co-authored-by: Claude <noreply@anthropic.com>
…onent arm's own verdict (objectui#11070 round 11) The widget arm has always judged `layout`, so a path check over the whole union could be answered by it. The control now reads the union's first arm, the component node's, alone. Claude-Session: https://claude.ai/code/session_01TdiauJaVCHuj45EzZGUxHh 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
|
Refs #11070
Clause-②: yes (widening). The strict face is expected to accept
layouton a metric-card slot, andoptions.descriptionif the spec carries it. It is priced in the changeset.Round 11 of objectui#11070, the two dashboard keys the
domain:specpointers routed here (5928509607,5930223710), claim5933040700. Base2e060ba6(carries PR #11377); merged withorigin/mainat3ae91930; headcbc637c2.layout: done. The widget slot's component arm declareslayoutby reference to the spec's widgetlayout, on both faces.options.description: stopped atneeds_decision, nothing declared.@objectstack/spec17.5.0 does not declare it, so under the order's rule it is the spec lane's call. This PR only makes the read-site comments true. The question, with the four-axis analysis, is in the report on objectui#11070.(i)
layout— producer / reader / declarationlayoutDashboardGridLayout's Save Layout,mergeLayoutIntoSchemalayout: { x, y, w, h }onto everywidgets[]entry it can match by id, ametric-cardcomponent node included. The README's documented persistence route hands that document toclient.meta.saveItem.DashboardGridLayout(buildDefaultLayouts,widgetsSignature),DashboardRenderer(inferredColumns,renderWidget),DashboardWithConfigx/y/w/h, defaulting a missing one.@objectstack/spec17.5.0DashboardWidgetSchema.shape.layoutzod/complex.zod.tsDashboardWidgetSchemaspecFieldsExcept). It acceptedlayouton both faces at base.DashboardWidgetSlotComponentSchemalayout. The tolerant face kept any value throughBaseSchema's passthrough. The strict face refused it:unrecognized_keys: ['layout']on that arm, and the union refused the widget.layout: stripImportedDefaults(SpecDashboardWidgetSchema).shape.layout(zod) andlayout?: SpecDashboardWidget['layout'](TypeScript).Measured on the built
@object-ui/typesdist with a scratch probe, at base and at head, a dashboard holding{ type: 'metric-card', title: 'Revenue', value: '1', layout: { x: 0, y: 0, w: 3, h: 2 } }:layoutas Save Layout writes itlayoutunrecognized)x: 'a'layout.x)hmissinglayout.h)zlayout)layout(control)metricwidget withlayout(control)The bold cells are the one narrowing: a malformed
layouton a component node is now refused on the tolerant face, as it already was on the widget arm. The changeset states it, and prices the change asminor(§9: nevermajor).The pin.
plugin-dashboard/src/__tests__/metricCardRegisteredInputsStrictFace-11022.test.ts, extended:mergeLayoutIntoSchema) and parses its output on the strict face, so it measures what Save Layout writes rather than a copy of it.layoutcontrols read the component arm's own verdict, not the widget arm's.layoutexactly as the spec's member does.@ts-expect-erroron a malformed literal.The types-side pin
strict-widget-slot-registered-inputs-11022.test.tsnow reads the arm's key set as the base's pluslayout.(ii)
options.description— producer / reader / declaration (no change)@objectstack/spec17.5.0DashboardWidgetOptionsSchemadescriptionmember. Its open catchall (unknown) admits the key unjudged. The spec's own docblock at objectstackmainsays it "stays undeclared", and objectstack'scheck:widget-option-censuskeeps it in a ledger as an undeclared resolver output.translateDashboard(packages/spec/src/system/i18n-resolver.ts, objectstackmainb9087d77e)if (subCaption) next.options = { ...w.options, description: subCaption };, live.subCaptionentry.DatasetWidget(metric caption row),widgetSubCaption.ts(limb 1),DashboardRendererinline arms (viatWidgetSubCaption)StrictAnyComponentSchemaunrecognized_keys, exactly like the undeclared control keynotAKey11070. Ruling C on objectui#11228 keeps that refusal. The tolerant face and the spec accept it.What changed here is comments only:
DatasetWidget.tsxsaid the slot was "declared end to end", andwidgetSubCaption.tssaid the spec "admits" it as a typed value. Both now say it is wired but not declared, and that its status is an open decision on objectui#11070.M3 (
scripts/measure-strict-authoring-face.mjs --json), base vs headtotalsare identical at base (2e060ba6) and at head (9efb7a34): 2230 nodes, 77 strict-refused, 46 strict-only, 31 red today, 602 documents, 72 whole-tree refusals and 24 whole-document red.dashboardrow is identical: 15 documents, 6 strict-refused.rendererMentionsOfUndeclaredKeys.key, 9110 to 9113. It is a text count of the word in renderer source, moved by this PR's comment prose.layouton a component node, so hypothesis H3 holds.Ablations (predictions were written before each run)
layoutmember throughablation-replace.mjs(anchor 1 to 0, blob239db046to55d40c71). The subject is source: the root vitest config aliases@object-ui/types/zodtosrc.HEADandgit diff HEADis empty.layout?: SpecDashboardWidget['layout'];fromcomplex.ts, then rebuilds@object-ui/types. Thedist/complex.d.tsmarker count was 0, and the tree was otherwise clean.@object-ui/plugin-dashboardtype-checkfails with exactly one TS2578 (unused@ts-expect-error) in the pin.type-checkexit 0 with 0 errors.Gates, at head
cbc637c2unless noted@object-ui/types,@object-ui/plugin-dashboard,@object-ui/app-shell,@object-ui/plugin-designer,@object-ui/plugin-list,@object-ui/sdui-parser,@object-ui/core,examples/schema-catalog.DashboardComponentSchema,DashboardWidgetSlotComponentSchemaorDashboardWidgetSchema(the types whose shape moved), found bygit grep, plus the order's named set.^...closures, with turbo--concurrency=2.@object-ui/i18nis not touched.--maxWorkers=2:packages/types/,packages/plugin-dashboard/,examples/schema-catalog/and the sdui-parser widget-options census. 508 files, 11690 passed, 6 skipped, 0 failed.check:doc-snippets(its own--build-filterclosure built),check:doc-fences,docs:check-links,check:control-bytes,check-doc-component-types,check-doc-example-ids,check:readme-exportsandcheck:new-line-citationsall exit 0.check:changeset-claims(report-only; the 31 bodies it names were read and none is falsified by this change),check:pending-changeset-literals,check-changeset-presence(9 source files of 2 released packages, 1 changeset) andchangeset:checkall exit 0..changeset/11348-dashboard-widget-reads.mdand.changeset/11022-strict-widget-slot-registered-inputs.md, whose component-arm statements this round makes false..ts/.tsxfiles with the rooteslint.config.js:files: ['**/*.{ts,tsx}']block. Count: 9 files in--format json.parserOptions.projectorprojectServicein the config), and no custom rule ineslint-rules/reads another file, so this diff cannot move a verdict in an untouched file.check:docs-route-closure(needs the site build),check:published-dist, the Spec Main Shape Gate, and the repo-widepnpm lint.Acceptance notes
layoutwriters (out of scope; filed through the report for the seat).DashboardWidgetInspector(app-shell metadata admin),DashboardWithConfigand the designer'sDashboardEditorwrite{ ...(widget.layout ?? {}), w }, cast toDashboardWidgetSchema['layout']. On a widget with nolayoutthat is{ w }, which the spec'slayoutrefuses (x,yandhmissing). Before this round the tolerant face kept such a value on a component node; it now refuses it there too, as it always did on the widget arm.{ id, component, layout }envelope'scomponentis still judged as a bareBaseSchemanode on the strict face (the objectui#11022 changeset says so). Itslayoutwas never refused..changeset/7293-dataset-metric-subcaption.mdspeaks of a sub-caption "its author declared inoptions.description". This round does not make that false, but whichever way (ii) is decided, that round should re-read it.Generated by Claude Code