Repository navigation
fix(plugin-dashboard,plugin-designer): the 22 slot-entry and chart-producer reads compile without BaseSchema's index signature (objectui#11598) - #11599
Merged
objectstack-fleet[bot] merged 3 commits intoOct 4, 2026
Conversation
…ate hand-off type, `ChartSchema` plus `colors` and `isAnimationActive` (objectui#11598, N1) Both dashboard surfaces compose the palette and `isAnimationActive: false` onto the `chart` node they build for a series widget bound to inline rows. Neither face of `ChartSchema` declares the two keys, so the literal compiled only through `BaseSchema`'s index signature, which objectui#8347 removes. The literal is now checked against `DashboardChartRenderSchema` (`chartRenderHandoff.ts`), not exported from the package index and never added to `@object-ui/types`: the seat's ruling B on objectui#8347, reusing the objectui#6356 ruling carried by plugin-timeline's `renderHandoff.ts`. The node is handed to `SchemaRenderer` with no cast. Nothing drawn changes. Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ Co-authored-by: Claude <noreply@anthropic.com>
…dget arm alone; the either-arm `dataset` fork retires (objectui#11598, N2) Every dashboard surface reads a `widgets[]` entry by the slot's element type, the component arm (`metric-card`) or the widget arm. Twenty reads took a widget key off that union, which compiled only through `BaseSchema`'s index signature (objectui#8347 removes it), and one was a deliberate fork: "a `dataset` draws `DatasetWidget` on either arm". The seat's ruling N2 = A on objectui#8347: every read narrows with `isSlotComponentEntry` first. Both surfaces decide the slot-component passthrough before any widget key is read; `entryComponent` reads `component` on the widget arm only; the dataset fork, the filter-bar scoping and the config panel's seed read the widget arm; `DashboardEditor` narrows with a local twin of the guard (it cannot import plugin-dashboard) and offers the widget-only Color Variant select on a widget alone. Runtime moves only for a `metric-card` entry carrying a widget key, which the strict face refuses: it draws its card instead of the dataset tile, the spread `options`, or the envelope node. The two doc passages that described the dataset tile in the card's place are rewritten. Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ 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
|
…widget arm alone, as of objectui#11598
Three notes in `@object-ui/types` described the either-arm draw as live:
`DASHBOARD_COMPONENT_WIDGET_TYPES`' docblock ("a `dataset` draws
`DatasetWidget` in the card's place by a rule that ignores the `type`"),
the Zod widget vocabulary's docblock (the same, plus "The `options` bag is
spread onto the node as literal props"), and the objectui#11483 test's
header and two inline comments. The previous commit on this branch retired
both behaviours, so each note now dates the objectui#11483 measurement and
states what holds since objectui#11598: a `metric-card` carrying a widget key
is refused by the strict face and is not drawn as a widget.
Comment and prose only; no type, schema or test assertion changes. The
changeset declares `@object-ui/types` for the shipped JSDoc.
Claude-Session: https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ
Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ 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
|
objectstack-fleet
Bot
deleted the
claude/issue-11598-dashboard-reads-off-signature
branch
October 4, 2026 07:20
This was referenced Oct 4, 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 #11598
Clause-②: no
The plugin-dashboard and plugin-designer half that the removal of
BaseSchema's index signature (objectui#8347) needs first. The 22 source reads that compiled only through that signature now compile without it, and the signature is still present. Two commits, one per class, so they read apart:921e56cc(N1) andd6da79f6(N2). The parent objectui#8347 stays open; this PR only clears itsBlocked-by:.N1: the
chartproducers' render keys (seat ruling B)packages/plugin-dashboard/src/chartRenderHandoff.tsdeclaresDashboardChartRenderSchema:ChartSchemapluscolors?: string[]andisAnimationActive?: boolean. Its header follows plugin-timeline'srenderHandoff.ts(the objectui#6356 ruling, Q1 = A). It is not exported from the package index (the builtdist/index.d.tsnames it 0 times) and is not added to@object-ui/types.DashboardRendererandDashboardGridLayoutbuild the static-datachartnode as a const annotated with the hand-off. That annotation excess-checks the literal against it. The node is then returned toSchemaRendererwith no cast. Nothing drawn changes.ChartRenderer(packages/plugin-charts/src/ChartRenderer.tsx) declares both keys on its own props type (ChartRendererProps.schema:colors?: string[],isAnimationActive?: boolean) and readsschema.colorsandschema.isAnimationActivewith no cast. The registeredchartcomponent receives the node asschema, typed by that props type, not byChartSchema. The new test pins the hand-off's two key types as assignable toReact.ComponentPropsofChartRenderer'sschema.N2: widget keys read off the slot entry (seat ruling A, premised)
metric-cardentry.if (isSlotComponentEntry(widget)) return toDashboardNodeType({ ...widget }). Below that linewidgetis the widget arm, sooptions,chartConfig,filter,compareTo,searchableandpaginationare read off the arm that declares them.datasetfork is gone.DashboardRendererreadsdatasetoffwidgetArm(the entry narrowed byisSlotComponentEntry).DashboardGridLayout's twin fork was a cast,(widget as { dataset?: unknown }). The P1 census could not see a cast, so this is a 23rd read; it now narrows the same way.entryComponentreadscomponenton the widget arm only. Its cross-arm read is gone.DashboardWithConfigseeds the config panel'sdataset,dimensions,valuesandcolorVariantfrom the widget arm. Before, the first three were read throughas any.DashboardEditor(plugin-designer) narrows with a local twin of the guard, because it cannot import plugin-dashboard (the same reason itsmeasureRefusalis a twin). Both read the one closed list,DASHBOARD_COMPONENT_WIDGET_TYPES. The Color Variant select is offered for a widget only (H2, below).DashboardRenderer(buildWidgetScopedFilterreadsfilterBindingsthrough aPick), and thesubCaptionread at both dataset forks.metric-cardentry that carries a widget key changes. The strict face refuses such an entry and the tolerant face accepts it (pinned). That card now draws itself. Before, it drew one of three other things: the dataset tile (on both surfaces), its own keys withoptionsspread over them, or the node in itscomponentenvelope. Every document the strict face accepts draws as before.Measurements
H1: the 22 sites, before and after
The instrument: the four tsc programs (
tsconfig.jsonandtsconfig.test.jsonof each package), one-off, trap-restored. It deletes the docblock and signature lines ofBaseSchemainpackages/types/src/base.tsand rebuilds@object-ui/types; the dist keeps one signature,ComponentRendererProps'. Restore is proven by the blob hash equal to HEAD's and an emptygit diff HEAD.H1 holds:
origin/mainis still the census's94985a9, and BASE reproduces the census exactly. No site was added or removed.94985a9, signature removedd6da79f6, signature removeddatasetoptionschartConfigfilter(object-chart node)compareTocolorson thechartliteral (N1;isAnimationActiveis its second excess key, which tsc does not name)filter(object-data-table node)searchablepaginationfilter(dataset-widget merge)optionschartConfigcolorson thechartliteral (N1)filtersearchablepaginationcolorVariantentryComponent·componentvaluesdimensionsdimensionscolorVariantProgram totals with the signature removed, BASE then HEAD:
The 6 left are objectui#8347's adjudicated probe flips (
bogusKey×2,zzUndeclared7353×4), the same 6 at BASE. With the signature present, all four programs exit 0 at both commits.The N1 hand-off really checks the literal. In a one-off probe with the signature removed, a planted
bogusProbeKey11598on eachchartliteral fails with TS2353 "does not exist in type 'DashboardChartRenderSchema'", inDashboardGridLayoutand inDashboardRenderer. The probe was trap-restored and hash-verified.N2 premise census (at BASE)
git grep -l -F metric-card 94985a92 -- ., 45 files. Each file is read withgit showat the same commit, so the file list and the contents come from the same source.typeis'metric-card', found two ways. AST: the TypeScript compiler over TS, TSX and JSON. SCAN: the balanced brace region around each occurrence, which covers md and mdx. Each literal's keys are compared with the widget-arm-only key set (dataset,options,chartConfig,filter,component,colorVariant,values,dimensions,compareTo,searchable,pagination,filterBindingsand the rest of the spec row).packages/types/src/__tests__/metric-card-needs-value-11483.test.ts. They are refusal probes: a card withdatasetandvalues, and a card withoptions, each with novalueand refused on every face. They are not authored documents.content/docs/**: 2 files, 9 literals.examples/**: 3 schema-catalog dashboards, 11 literals.skills/**: no file in the population.plugin-dashboard:metric-cardnode key.dataset, and a TS file whose card carriesoptionsandcomponent. Both are flagged.type: 'bar'sees the known widget-armdatasetentry incontent/docs/plugins/plugin-dashboard.mdx({ "id": "w1", "type": "bar", "dataset": "invoices", … }) and the schema catalog'sfiltered-dashboard-dataset-widgets.jsonentry.The two-way
datasetpin, and reverse validationpackages/plugin-dashboard/src/__tests__/slotEntryWidgetArmReads-11598.test.tsxtests on both surfaces. It identifiesDatasetWidgetby itsdataset-loadingskeleton under a query that never settles.datasetdrawsDatasetWidget, and the query is called with{ dimensions: ['status'], measures: ['count'] }.metric-cardcarryingdatasetdoes not reach it: the card's$24kis drawn, no skeleton appears, and no query is sent.optionsandcomponentreads,entryComponent, and the face verdicts: each moved card is strict-refused atwidgets.0and tolerant-accepted.The designer test is
packages/plugin-designer/src/__tests__/DashboardEditor.slotComponentArm-11598.test.tsx.Reverse validation, run from the committed state. The five N2 source files were put back to their bytes at
921e56cc. On disk, theN2 Amarkers went from 10 to 0 and the either-arm comment came back. With the new pins unchanged, the run wasTests 8 failed | 10 passed (18):dataset-loadingrendered in the card's place,OPTIONS-FIGUREdrawn, and the envelope drawn;entryComponentunit failed;metric-cardcase failed;The direction was red, as expected. The restore is proven by 5 blob hashes equal to HEAD and
git diff HEADat 0 bytes. My first attempt stopped before running any test: its marker count was wrong, because N1 had already put the issue number into two of the files. It was re-run with theN2 Amarker.H2:
DashboardEditorvaluesanddimensionsare read bymeasureRefusal, the door probe behind the type picker. For a strict-validmetric-cardthey were already absent, so the probe is unchanged and nothing is visible.colorVariantis the Color Variant select. Here the seat's "invisible" expectation does not hold. The select was offered for everymetric-cardentry, and a pick wrotecolorVariantonto the card. The strict face refuses that key on the card, andMetricCardhas no such prop, so the value is not drawn (app-shell'swidget-dom-leak-sweeptest notes it lands as acolorvariantDOM attribute). After this PR, ametric-cardentry is not offered the select. Title, width and height stay, and a widget is offered the select as before. So one control is gone for strict-valid cards, and its only effect was storing a key that publish refuses.Gates (all at
d6da79f6, each exit captured before any pipe, heavy runs under the shared verify lock,TURBO_FORCE=trueon every turbo run)turbo run buildfor plugin-dashboard and plugin-designer: exit 0. "Tasks: 19 successful, 19 total", "Cached: 0 cached".--build-filter): exit 0, "Tasks: 35 successful, 35 total".pnpm --filter @object-ui/plugin-dashboard type-check: exit 0.pnpm --filter @object-ui/plugin-designer type-check: exit 0.pnpm --filter @object-ui/types type-check: exit 0.pnpm exec vitest run --maxWorkers=2 packages/plugin-dashboard/: exit 0, "Test Files 168 passed (168)", "Tests 1669 passed | 6 skipped (1675)".pnpm exec vitest run --maxWorkers=2 packages/plugin-designer/: exit 0, "Test Files 41 passed (41)", "Tests 305 passed (305)".check:doc-snippets: exit 0, "777 of 777 block(s) judged, 0 failed".check:doc-examples: exit 0, "35 compile, 87 fail, 87 of those declared in the ledger".check:doc-types: exit 0.check:readme-exports: exit 0.check:doc-snippets,check:doc-examplesandcheck:readme-exportsstopped on PRECONDITION NOT MET (packages unbuilt). They were not red, and were re-run after the closure build.check:changeset-claims: exit 0.check:pending-changeset-literals: exit 0.check:control-bytes: exit 0.changeset:check: exit 0.check-changeset-presence: exit 0, "9 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)".type-check:coverage: exit 0.check:new-line-citations: exit 0, "0 new citation(s)".--format jsonoutput. The config does not enable type-aware linting, so this diff cannot move a verdict on an untouched file. The repo-widepnpm lintis left to CI.type-check(app-shell, console): NOT MEASURED. No exported declaration changed: the hand-off is not reachable fromdist/index.d.ts, and the props ofDashboardRenderer,DashboardGridLayout,DashboardWithConfigandDashboardEditorare untouched.Acceptance notes
File surface, declared. This PR also rewrites one paragraph in
packages/plugin-dashboard/README.mdand the same paragraph incontent/docs/plugins/plugin-dashboard.mdx. Both said "adatasetmakes the dashboard draw its dataset tile in the card's place", which this change makes false (AGENTS.md Add automated testing infrastructure and CI/CD workflows #2). It also repairs four cross-file line citations inside theDashboardGridLayout.tsxcomments it touched, by content (AGENTS.md [WIP] Update documentation for project #11).Prose made false in
packages/types, not edited, because the claim excludespackages/types. Each is a present-tense account of the either-arm draw:complex.ts, in theDASHBOARD_COMPONENT_WIDGET_TYPESdocblock: "adatasetdrawsDatasetWidgetin the card's place by a rule that ignores thetype";zod/complex.zod.ts, the same sentence, plus "Theoptionsbag is spread onto the node as literal props";__tests__/metric-card-needs-value-11483.test.ts, in its header and two inline comments.The first two ship in
@object-ui/types' dist JSDoc. Proposed carrier: the PR that executes objectui#8347, which already edits complex.ts'swidgetsnotes. Or the seat may direct otherwise (see the report).Observation, not reproduced:
DashboardEditor's type picker still offers widget types for ametric-cardentry. A pick would write a widgettypeonto the card's keys.isLegacyRetiredWidgetstill readsdataset,data,optionsandobjectthrough its own named shape. After the reorder, it only ever receives widget-arm entries on both surfaces.Generated by Claude Code