You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2e818d0
Browse filesBrowse the repository at this point in the historyBrowse files
fix(plugin-dashboard,plugin-designer): the 22 slot-entry and chart-producer reads compile without BaseSchema's index signature (objectui#11598) (#11599)
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) and `d6da79f6` (N2). The
parent objectui#8347 stays open; this PR only clears its `Blocked-by:`.
## N1: the `chart` producers' render keys (seat ruling B)
- New `packages/plugin-dashboard/src/chartRenderHandoff.ts` declares
`DashboardChartRenderSchema`: `ChartSchema` plus `colors?: string[]` and
`isAnimationActive?: boolean`. Its header follows plugin-timeline's
`renderHandoff.ts` (the objectui#6356 ruling, Q1 = A). It is not
exported from the package index (the built `dist/index.d.ts` names it 0
times) and is not added to `@object-ui/types`.
- `DashboardRenderer` and `DashboardGridLayout` build the static-data
`chart` node as a const annotated with the hand-off. That annotation
excess-checks the literal against it. The node is then returned to
`SchemaRenderer` with no cast. Nothing drawn changes.
- **H3 holds; the consumer side needs nothing.** `ChartRenderer`
(`packages/plugin-charts/src/ChartRenderer.tsx`) declares both keys on
its own props type (`ChartRendererProps.schema`: `colors?: string[]`,
`isAnimationActive?: boolean`) and reads `schema.colors` and
`schema.isAnimationActive` with no cast. The registered `chart`
component receives the node as `schema`, typed by that props type, not
by `ChartSchema`. The new test pins the hand-off's two key types as
assignable to `React.ComponentProps` of `ChartRenderer`'s `schema`.
## N2: widget keys read off the slot entry (seat ruling A, premised)
- **Premise checked first, and it holds** (evidence below): no authored
document carries a widget-arm key on a `metric-card` entry.
- Both surfaces now decide the slot-component passthrough before reading
any widget key: `if (isSlotComponentEntry(widget)) return
toDashboardNodeType({ ...widget })`. Below that line `widget` is the
widget arm, so `options`, `chartConfig`, `filter`, `compareTo`,
`searchable` and `pagination` are read off the arm that declares them.
- The either-arm `dataset` fork is gone. `DashboardRenderer` reads
`dataset` off `widgetArm` (the entry narrowed by
`isSlotComponentEntry`). `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.
- `entryComponent` reads `component` on the widget arm only. Its
cross-arm read is gone.
- `DashboardWithConfig` seeds the config panel's `dataset`,
`dimensions`, `values` and `colorVariant` from the widget arm. Before,
the first three were read through `as any`.
- `DashboardEditor` (plugin-designer) narrows with a local twin of the
guard, because it cannot import plugin-dashboard (the same reason its
`measureRefusal` is a twin). Both read the one closed list,
`DASHBOARD_COMPONENT_WIDGET_TYPES`. The Color Variant select is offered
for a widget only (H2, below).
- Also narrowed: the filter-bar scoping in `DashboardRenderer`
(`buildWidgetScopedFilter` reads `filterBindings` through a `Pick`), and
the `subCaption` read at both dataset forks.
- **What changes at runtime.** Only a `metric-card` entry 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 with `options` spread over them, or the node in its
`component` envelope. Every document the strict face accepts draws as
before.
## Measurements
### H1: the 22 sites, before and after
The instrument: the four tsc programs (`tsconfig.json` and
`tsconfig.test.json` of each package), one-off, trap-restored. It
deletes the docblock and signature lines of `BaseSchema` in
`packages/types/src/base.ts` and rebuilds `@object-ui/types`; the dist
keeps one signature, `ComponentRendererProps`'. Restore is proven by the
blob hash equal to HEAD's and an empty `git diff HEAD`.
H1 holds: `origin/main` is still the census's `94985a9`, and BASE
reproduces the census exactly. No site was added or removed.
| Site (file · key) | BASE `94985a9`, signature removed | HEAD
`d6da79f6`, signature removed |
|---|---|---|
| DashboardRenderer · `dataset` | TS2339 | none |
| DashboardRenderer · `options` | TS2339 | none |
| DashboardRenderer · `chartConfig` | TS2339 | none |
| DashboardRenderer · `filter` (object-chart node) | TS2339 | none |
| DashboardRenderer · `compareTo` | TS2339 | none |
| DashboardRenderer · `colors` on the `chart` literal (N1;
`isAnimationActive` is its second excess key, which tsc does not name) |
TS2353 | none |
| DashboardRenderer · `filter` (object-data-table node) | TS2339 | none
|
| DashboardRenderer · `searchable` | TS2339 | none |
| DashboardRenderer · `pagination` | TS2339 | none |
| DashboardRenderer · `filter` (dataset-widget merge) | TS2339 | none |
| DashboardGridLayout · `options` | TS2339 | none |
| DashboardGridLayout · `chartConfig` | TS2339 | none |
| DashboardGridLayout · `colors` on the `chart` literal (N1) | TS2353 |
none |
| DashboardGridLayout · `filter` | TS2339 | none |
| DashboardGridLayout · `searchable` | TS2339 | none |
| DashboardGridLayout · `pagination` | TS2339 | none |
| DashboardWithConfig · `colorVariant` | TS2339 | none |
| widgetDispatch `entryComponent` · `component` | TS2339 | none |
| DashboardEditor · `values` | TS2339 | none |
| DashboardEditor · `dimensions` | TS2339 | none |
| DashboardEditor · `dimensions` | TS2339 | none |
| DashboardEditor · `colorVariant` | TS2339 | none |
Program totals with the signature removed, BASE then HEAD:
- plugin-dashboard src: 18, then 0.
- plugin-dashboard test: 24, then 6.
- plugin-designer src: 4, then 0.
- plugin-designer test: 4, then 0.
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 `bogusProbeKey11598` on each `chart`
literal fails with TS2353 "does not exist in type
'DashboardChartRenderSchema'", in `DashboardGridLayout` and in
`DashboardRenderer`. The probe was trap-restored and hash-verified.
### N2 premise census (at BASE)
- **Population**: `git grep -l -F metric-card 94985a9 -- .`, 45 files.
Each file is read with `git show` at the same commit, so the file list
and the contents come from the same source.
- **Detector**: every object literal whose `type` is `'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`, `filterBindings` and the rest of the spec row).
- **Result**: 51 AST and 88 SCAN literals. **2 carry a widget-arm key**,
both in
`packages/types/src/__tests__/metric-card-needs-value-11483.test.ts`.
They are refusal probes: a card with `dataset` and `values`, and a card
with `options`, each with no `value` and refused on every face. They are
not authored documents.
- **Per corpus, 0 hits**:
- `content/docs/**`: 2 files, 9 literals.
- `examples/**`: 3 schema-catalog dashboards, 11 literals.
- Package READMEs: 2 files, 16 literals.
- `skills/**`: no file in the population.
- app-shell seeds and presets: none. app-shell's only file in the
population is a test that uses the `plugin-dashboard:metric-card` node
key.
- **Lit controls**:
- Planted: an md file with a fenced dashboard whose card carries
`dataset`, and a TS file whose card carries `options` and `component`.
Both are flagged.
- Real corpus: the same detector pointed at `type: 'bar'` sees the known
widget-arm `dataset` entry in
`content/docs/plugins/plugin-dashboard.mdx` (`{ "id": "w1", "type":
"bar", "dataset": "invoices", … }`) and the schema catalog's
`filtered-dashboard-dataset-widgets.json` entry.
### The two-way `dataset` pin, and reverse validation
`packages/plugin-dashboard/src/__tests__/slotEntryWidgetArmReads-11598.test.tsx`
tests on both surfaces. It identifies `DatasetWidget` by its
`dataset-loading` skeleton under a query that never settles.
- A widget-arm entry with `dataset` draws `DatasetWidget`, and the query
is called with `{ dimensions: ['status'], measures: ['count'] }`.
- A `metric-card` carrying `dataset` does not reach it: the card's
`$24k` is drawn, no skeleton appears, and no query is sent.
- The CONTROL is a strict-valid card, which draws as before.
- The file also pins the `options` and `component` reads,
`entryComponent`, and the face verdicts: each moved card is
strict-refused at `widgets.0` and 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, the `N2 A`
markers went from 10 to 0 and the either-arm comment came back. With the
new pins unchanged, the run was `Tests 8 failed | 10 passed (18)`:
- every card case failed on both surfaces: `dataset-loading` rendered in
the card's place, `OPTIONS-FIGURE` drawn, and the envelope drawn;
- the `entryComponent` unit failed;
- the designer `metric-card` case failed;
- the positives, controls and face checks stayed green.
The direction was red, as expected. The restore is proven by 5 blob
hashes equal to HEAD and `git diff HEAD` at 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 the `N2 A` marker.
### H2: `DashboardEditor`
- **`values` and `dimensions`** are read by `measureRefusal`, the door
probe behind the type picker. For a strict-valid `metric-card` they were
already absent, so the probe is unchanged and nothing is visible.
- **`colorVariant`** is the Color Variant select. Here the seat's
"invisible" expectation does not hold. The select was offered for every
`metric-card` entry, and a pick wrote `colorVariant` onto the card. The
strict face refuses that key on the card, and `MetricCard` has no such
prop, so the value is not drawn (app-shell's `widget-dom-leak-sweep`
test notes it lands as a `colorvariant` DOM attribute). After this PR, a
`metric-card` entry 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=true` on every turbo
run)
- `turbo run build` for plugin-dashboard and plugin-designer: exit 0.
"Tasks: 19 successful, 19 total", "Cached: 0 cached".
- Build of the doc-gate closure (`--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.
- The first runs of `check:doc-snippets`, `check:doc-examples` and
`check:readme-exports` stopped 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)".
- ESLint, narrowed to the 9 touched TS/TSX files: 0 errors and 23
warnings, all pre-existing rules on lines this PR did not add. The count
of 9 is read from the `--format json` output. The config does not enable
type-aware linting, so this diff cannot move a verdict on an untouched
file. The repo-wide `pnpm lint` is left to CI.
- Consumer `type-check` (app-shell, console): NOT MEASURED. No exported
declaration changed: the hand-off is not reachable from
`dist/index.d.ts`, and the props of `DashboardRenderer`,
`DashboardGridLayout`, `DashboardWithConfig` and `DashboardEditor` are
untouched.
## Acceptance notes
- **File surface, declared.** This PR also rewrites one paragraph in
`packages/plugin-dashboard/README.md` and the same paragraph in
`content/docs/plugins/plugin-dashboard.mdx`. Both said "a `dataset`
makes the dashboard draw its dataset tile in the card's place", which
this change makes false (AGENTS.md #2). It also repairs four cross-file
line citations inside the `DashboardGridLayout.tsx` comments it touched,
by content (AGENTS.md #11).
- **Prose made false in `packages/types`, not edited, because the claim
excludes `packages/types`.** Each is a present-tense account of the
either-arm draw:
- `complex.ts`, in the `DASHBOARD_COMPONENT_WIDGET_TYPES` docblock: "a
`dataset` draws `DatasetWidget` in the card's place by a rule that
ignores the `type`";
- `zod/complex.zod.ts`, the same sentence, plus "The `options` bag 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's
`widgets` notes. Or the seat may direct otherwise (see the report).
- Observation, not reproduced: `DashboardEditor`'s type picker still
offers widget types for a `metric-card` entry. A pick would write a
widget `type` onto the card's keys.
- `isLegacyRetiredWidget` still reads `dataset`, `data`, `options` and
`object` through its own named shape. After the reorder, it only ever
receives widget-arm entries on both surfaces.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01CPvhwGcirXqBGEdPSb72TZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
The dashboard surfaces read every `widgets[]` entry without leaning on `BaseSchema`'s index signature: a widget key is read on the widget arm alone, and the `chart` node is built as a private hand-off type (objectui#11598).
8
+
9
+
**N1, the `chart` producers.**`DashboardRenderer` and `DashboardGridLayout` build a `chart` node for a series widget bound to inline rows, and compose two render keys onto it: the dashboard palette (`colors`) and `isAnimationActive: false`, the deterministic first paint inside the grid (#2756). `ChartSchema` declares neither key, on either face, and the chart renderer reads both. Each producer now checks its literal against a hand-off type private to this package, `ChartSchema` plus those two keys, and hands the node on with no cast; the type is not exported, and the keys stay off `@object-ui/types`, because the strict authoring face refuses both on an authored `chart` node. Nothing drawn changes.
10
+
11
+
**N2, widget keys on the slot entry.** An entry of `widgets[]` is a widget or a component node placed in the slot (a `metric-card`), and only the widget declares the widget keys (`dataset`, `options`, `chartConfig`, `filter`, `component`, `colorVariant`, `values`, `dimensions`, …). Every read of one now narrows the entry to the widget first, on `DashboardRenderer`, `DashboardGridLayout`, `DashboardWithConfig` and `DashboardEditor`. What changes is confined to a `metric-card` entry that carries a widget key, which `@object-ui/types/zod`'s strict face refuses and only the tolerant face accepts:
12
+
13
+
- a `metric-card` carrying `dataset` draws its card; it used to draw the dataset tile in the card's place, on both dashboard surfaces;
14
+
- a `metric-card` carrying `options` draws its own keys; `options` used to be spread over them, so `options.value` replaced `value`;
15
+
- a `metric-card` carrying a `component` draws its card; the envelope's node used to be drawn instead.
16
+
17
+
A document the strict face accepts draws exactly as before. `@object-ui/types`' docblocks on `DASHBOARD_COMPONENT_WIDGET_TYPES` and the Zod widget vocabulary, which described the dataset tile in the card's place and the `options` spread as live, were corrected to match; no type in it changes. In `DashboardEditor`, a `metric-card` entry is no longer offered the Color Variant select: the card declares no `colorVariant`, `MetricCard` draws nothing from it, and a pick stored a key publish refuses. A widget is offered it as before.
0 commit comments