Repository navigation
Commit 399e3aa
fix(examples,spec): dashboard widget options census — examples drop the options keys no dataset-bound renderer reads, and the schema doc states the read set (#21130)
Fixes #20960
Clause-②: no — the census found no key that has both a writer and a
reader, so nothing is declared and no value is typed. The accept set of
`DashboardWidgetOptionsSchema` is unchanged.
The claim declared `yes` with the arm left to the census. The line above
is the measured arm.
## What the census found
No key in the census has both a writer and a reader. Every key an
example writes is read by no renderer, and the one key a renderer reads
(`description`) has no authored writer. So the card's rule gives these
verdicts:
- written but unread: each key is **removed from its producer**;
- read but unwritten: **nothing is declared**.
This PR therefore contains no declarations. It has three parts:
- the producer removals in two example apps;
- a correction to the `DashboardWidgetOptionsSchema` doc comment, which
said the renderer "understands" presentation extras that no renderer
reads;
- a `patch` changeset, because that doc comment ships in
`dist/ui/index.d.ts`.
**Landing site.** This is the expected site from the dispatch, minus the
declarations: the producers under `examples/**` and the
`DashboardWidgetOptionsSchema` block in
`packages/spec/src/ui/dashboard.zod.ts`.
## Census — every key written through the widget's open shapes
How it was measured:
- **Writers** were read at objectstack `2821e9f15`. The population is
every dashboard producer: `examples/**` (app-crm, app-todo, and
app-showcase's three dashboards), the packaged templates
(`create-objectstack` blank, `os generate`'s dashboard scaffold,
Studio's create seed in `metadata-create-seeds.ts`), and the platform
package `platform-objects` (`system_overview`).
- **Readers** were read in objectui
`packages/plugin-dashboard/src/DatasetWidget.tsx` at objectui `main`
`5262f7dd`. `DashboardRenderer` and `DashboardGridLayout` both route any
widget that has a `dataset` to `DatasetWidget`, and the spec requires
`dataset`. The objectui pin `e420df310` reads the same set
(`:492`-`:501`, `:1065`, `:1694` there).
- **Open shapes:** walked at runtime from the built
`DashboardWidgetSchema`, the widget has exactly one open catchall:
`options`. The widget itself, `layout`, `compareTo` and `chartConfig`
are closed. The four maps it reaches are the `title` locale map,
`chartConfig.colors`, `filter` (filter grammar keyed by field name) and
`filterBindings` (filter name to field). None of the four is a
renderer-extras bag.
| Key | Writer | Reader on the dataset-bound path | Verdict |
| :-- | :-- | :-- | :-- |
| `format` | app-crm `pipeline.dashboard.ts` L46, L64, L82
(`'currency'`) | None. The metric tile formats with the dataset
measure's own metadata: `formatMeasure(value, f?.format, f?.currency,
…)` at `:1126`, where `f = measureField(values[0])` at `:1043`.
`opportunity.dataset.ts` already declares `format: '0,0', currency:
'USD'` on `total_amount` and `avg_amount`. | Written, unread. Removed
from the producer. |
| `currency` | app-crm, same three widgets (`'USD'`) | None (same read
as `format`) | Written, unread. Removed. |
| `color` | app-todo, four metric tiles (hex colours) | None. A tile's
accent is `widget.colorVariant`
(`metricAccentTextClass(widget?.colorVariant)` at `:1071`). | Written,
unread. Removed. |
| `suffix` | app-todo `completion_rate` (`'%'`) | None | Written,
unread. Removed. |
| `showLegend` | app-todo pie and donut (`true`) | None. The legend
comes from `widget.chartConfig`
(`chartConfigPresentation(widget?.chartConfig, …)` at `:1800`), where
`showLegend` defaults to `true`. | Written, unread. Removed. |
| `horizontal` | app-todo bar (`true`) | None. The declared spelling is
`type: 'horizontal-bar'`. | Written, unread. Removed. |
| `showDataLabels` | app-todo line (`true`) | None. The declared home is
`chartConfig.showDataLabels`. | Written, unread. Removed. |
| `description` | No authored writer. The single-line grep and every
dashboard file read by hand both found none, and the grep's control hit
is `content/docs/ui/dashboards.mdx`. `translateDashboard`
(`system/i18n-resolver.ts`) only overlays the key from a `subCaption`
bundle entry, and this repo ships no such entry. | Metric sub-caption:
`pickLocalized(options.description, …)` at `:1118` | Read, unwritten.
Not declared. The seat note (5925333725) records objectui#11228 ruling
C, which keeps this key refused by name as an inline-dialect key, so
dropping the read is objectui's follow-up. |
The declared keys (`dateGranularity`, `sortBy`, `sortOrder`, `limit`,
`stageOrder`) are unchanged. `DatasetWidget` reads them at `:541`-`:551`
and `:1785`.
The one place outside the example apps that still writes an extra is the
published skill `skills/objectstack-ui/rules/dashboards.md:185`, which
writes `options: { icon: 'DollarSign' }`. That file is teaching text on
a Tier H governed surface, so it is reported for the seat and not edited
here (Acceptance notes).
A reader for most of these keys exists only on objectui's legacy inline
arm. There, `DashboardRenderer.getComponentSchema` spreads `...options`
into component props, and for example `MetricWidget` reads `format`,
`currency` and `suffix`. A widget that has a `dataset` never reaches
that arm. That arm is the inline dialect objectui#11228 ruled C, which
is out of scope here.
## Mechanism hypotheses, measured
- **H1 (census first)** — held. The census table is above.
- **H2 (typing follows the reader)** — moot, because `currency` has no
reader to type from. A finding from the search: `packages/spec` has no
shared ISO 4217 code schema. `z.string().length(3)` is spelled three
times (`data/field.zod.ts:485`, `:509` and `ui/dataset.zod.ts:288`), and
the `iso_4217_currency` value domain
(`data/currency-fraction-digits.ts`) is a separate membership list.
- **H3 (what typing breaks)** — zero. No value is typed, so nothing the
catchall admitted is now refused. `node
scripts/check-adr-0087-registration.mjs --base origin/main` exits 0 on a
`patch` changeset that declares `no`.
- **H4 (strict face)** — measured with a scratch replica of objectui
`strict-authoring-face.ts`'s walker (`5262f7dd`). The replica gives
every reachable object `catchall: z.never()` and walks lazy, union,
array, tuple, record, set, map, intersection, pipe and the optional
family. It was applied to the built spec `DashboardSchema`. This is a
faithful reading of the walker, not objectui's own face:
```
control-lit tolerant=FAIL strict=FAIL(3) unrecognized_keys @
widgets.0.options keys=["zzProbe"] (+2 id-length issues of the fixture)
crm-before tolerant=PASS strict=FAIL(3) unrecognized_keys @
widgets.{0,1,2}.options keys=["format","currency"]
crm-after tolerant=PASS strict=PASS
todo-before tolerant=PASS strict=FAIL(8) color x4, suffix, showLegend
x2, horizontal, showDataLabels
todo-after tolerant=PASS strict=PASS
```
app-crm's `pipeline.dashboard.ts` now carries no `options` at all, so it
passes the strict face without any objectui follow-up. The run link the
card's acceptance asks for is the closer's to add, from objectui's own
strict-face run over this example.
## Changes
- `examples/app-crm/src/dashboards/pipeline.dashboard.ts` — drop
`options: { format: 'currency', currency: 'USD' }` from the three metric
tiles, with a comment pointing at the dataset measures that carry the
currency.
- `examples/app-todo/src/dashboards/task.dashboard.ts` — drop `options`
from all eight widgets, with a comment naming the declared homes
(`colorVariant`, `chartConfig`, the measure's `format`).
- `packages/spec/src/ui/dashboard.zod.ts` — the
`DashboardWidgetOptionsSchema` doc comment, and only that comment. It no
longer says the renderer "understands `icon`, `trend`, `columns`,
`striped`, `density`". It states:
- the measured read set: the five declared keys, plus `description` as
read but unwritten;
- the rule that a key is declared only with both a writer and a
dataset-bound read site;
- the declared homes for presentation.
`.describe()` is unchanged. The generated reference page shows only the
describe text, and `check:docs` is green.
- `.changeset/20960-widget-options-census.md` — a `patch` for
`@objectstack/spec`. The doc comment ships in `dist/ui/index.d.ts`; this
was checked by grepping the built `.d.ts`. The examples are `private:
true` and ship nothing.
## Verification (head `9efd8ffa5`)
- **Spec build:** `pnpm --filter @objectstack/spec build` exits 0. `pnpm
--filter @objectstack/spec check:generated` prints `✓ All 15 generated
artifacts are up to date`.
- **Gate derivation:** `node scripts/pm/dispatch-gates.mjs --commands
--repo objectstack-ai/objectstack`, with no paths, derived 80 commands
from the four changed files. All 80 were run. Reconciliation with
`--ran` prints `80 derived famil(ies) accounted for — 79 run, 1
NOT-MEASURED`.
- NOT MEASURED: `check:dual-build-cjs-loads` exits 3 with `PREREQUISITE
NOT MET` because it needs the whole workspace built (86 package dists).
CI's Build Core runs it.
- **Gate verdict lines:**
- `check:widget-option-census`: `OK check-widget-option-census: 5 key(s)
declared by DashboardWidgetOptionsSchema are all in
CONSUMED_WIDGET_OPTION_KEYS (6 member(s)); 1 non-declared member(s) each
still witnessed.`
- `check-adr-0087-registration --base origin/main`,
`check-changeset-no-major --base origin/main`, `check-empty-changeset
--base origin/main`, `check:api-surface`, `check:docs`,
`check:spec-docblock-symbol-anchors`, `check:objectui-pin-citations`,
`check:issue-citations`, `check:doc-authoring` and `check:nul-bytes` all
exit 0.
- **eslint, narrowed:** run on the three touched TypeScript files with
`--no-inline-config --format json`. It reported 3 files, 0 errors and 0
warnings. `--print-config` resolves a config for each file, so none is
ignored. The narrowing excludes nothing: `eslint.config.mjs` enables no
type-aware linting (no `parserOptions.project`, no typed rules, as
stated at `:327`), so this diff cannot move the verdict on any untouched
file.
- **Tests:** shown in the table below.
| Package | Command | Result | Tree |
| :-- | :-- | :-- | :-- |
| `@objectstack/spec` | `test` | 588 files, 17239 tests passed |
`ef4a1f5a7` |
| `@objectstack/spec` | `typecheck` (tsc, scripts, test-typecheck) | OK
| `ef4a1f5a7` |
| `example-crm` | `test` + `typecheck` | 45 tests passed; typecheck Done
| `9efd8ffa5` |
| `example-todo` | `test` + `typecheck` | 238 tests passed; typecheck
Done | `9efd8ffa5` |
| `@objectstack/sdui-parser` | `test` | 217 passed | `ef4a1f5a7` |
| `@objectstack/service-analytics` | `test` | 3498 passed, 10 skipped |
`ef4a1f5a7` |
| `@objectstack/platform-objects` | `test` | 948 passed | `ef4a1f5a7` |
| `@objectstack/lint` | `test` | 5452 passed | `ef4a1f5a7` |
| `@objectstack/metadata-protocol` | `test` | 2921 passed, 19 skipped |
`ef4a1f5a7` |
| `@objectstack/objectql` | `test` | 6874 passed | `ef4a1f5a7` |
| `@objectstack/metadata` | `test` | 836 passed | `ef4a1f5a7` |
| `@objectstack/cli` | `vitest run --project unit`, all 4 shards | 1005
+ 843 + 778 + 793 passed (60 files each) | shard 1 at `ef4a1f5a7`,
shards 2–4 at `9efd8ffa5` |
Notes on the test table:
- `ef4a1f5a7` is this branch's change on `2821e9f15`, before `main` was
merged.
- The consumer packages were run in the upstream-dependency direction,
with each package's dependency closure built through `turbo run build
--filter=PKG...`.
- After the merge, only the spec doc comment changed on this branch, so
their CI rerun is the post-merge reading.
- The CLI integration tier is declared to CI.
## Acceptance notes (observations, not filed)
- **Teaching surfaces** still describe unread `options` extras as
working:
- `content/docs/ui/dashboards.mdx:138` says "keys the renderer
understands (`icon`, `columns`, `striped`, `density`, …) pass through
untouched".
- `skills/objectstack-ui/rules/dashboards.md:185` and `:341` write
`options: { icon: 'DollarSign' }` and say "presentation extras … pass
through untouched". The skill is Tier H, so neither file is edited here.
Both go to the seat with the report.
- **Silent at validate:** in this repo, nothing runs
`checkDashboardWidgetOptions` over dashboard metadata. Only SDUI page
dashboard nodes reach it (`sdui-parser/src/validate.ts:318`). So a
`*.dashboard.ts` carrying an unread `options` key is silent at `os
validate`, which is how these 11 widgets carried them. This goes to the
seat with the report.
- **Mislabelled tile:** the app-todo `completion_rate` tile is titled
"Completion Rate" but renders `task_count`. Its removed `suffix: '%'`
never rendered.
- **Not migrated:** the removed app-todo keys have declared homes, but
those were not used because they would change what the example renders:
`chartConfig.showDataLabels` for the line chart, and `type:
'horizontal-bar'` for the bar chart. A hex `color` has no faithful
`colorVariant` mapping.
- **Stale comment:** app-crm `pipeline.dashboard.ts`'s header comment
still describes `categoryGranularity: 'month'` bucketing on the trend
widget. That key is retired, and the dataset dimension carries
`dateGranularity: 'month'`.
- **`.describe()` kept:** the schema's `.describe('Widget configuration
— declared query keys + open renderer extras')` is quoted in
`@objectstack/sdui-parser`'s lockstep header and in objectui's copy, so
rewording it is a cross-repo lockstep edit. It was left unchanged.
---
_Generated by [Claude
Code](https://claude.ai/code/session_017VaLJnYwhPsanVCe9dMCJU)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 39ab294 commit 399e3aa
4 files changed
Lines changed: 53 additions & 20 deletions
File tree
- .changeset
- examples
- app-crm/src/dashboards
- app-todo/src/dashboards
- packages/spec/src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
38 | 43 | | |
39 | 44 | | |
40 | 45 | | |
| |||
43 | 48 | | |
44 | 49 | | |
45 | 50 | | |
46 | | - | |
47 | 51 | | |
48 | 52 | | |
49 | 53 | | |
| |||
61 | 65 | | |
62 | 66 | | |
63 | 67 | | |
64 | | - | |
65 | 68 | | |
66 | 69 | | |
67 | 70 | | |
| |||
79 | 82 | | |
80 | 83 | | |
81 | 84 | | |
82 | | - | |
83 | 85 | | |
84 | 86 | | |
85 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
20 | 25 | | |
21 | 26 | | |
22 | 27 | | |
| |||
26 | 31 | | |
27 | 32 | | |
28 | 33 | | |
29 | | - | |
30 | 34 | | |
31 | 35 | | |
32 | 36 | | |
| |||
36 | 40 | | |
37 | 41 | | |
38 | 42 | | |
39 | | - | |
40 | 43 | | |
41 | 44 | | |
42 | 45 | | |
| |||
46 | 49 | | |
47 | 50 | | |
48 | 51 | | |
49 | | - | |
50 | 52 | | |
51 | 53 | | |
52 | 54 | | |
| |||
56 | 58 | | |
57 | 59 | | |
58 | 60 | | |
59 | | - | |
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
| |||
75 | 76 | | |
76 | 77 | | |
77 | 78 | | |
78 | | - | |
79 | 79 | | |
80 | 80 | | |
81 | 81 | | |
| |||
86 | 86 | | |
87 | 87 | | |
88 | 88 | | |
89 | | - | |
90 | 89 | | |
91 | 90 | | |
92 | 91 | | |
| |||
99 | 98 | | |
100 | 99 | | |
101 | 100 | | |
102 | | - | |
103 | 101 | | |
104 | 102 | | |
105 | 103 | | |
| |||
110 | 108 | | |
111 | 109 | | |
112 | 110 | | |
113 | | - | |
114 | 111 | | |
115 | 112 | | |
116 | 113 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
214 | 214 | | |
215 | 215 | | |
216 | 216 | | |
217 | | - | |
218 | | - | |
219 | | - | |
220 | | - | |
221 | | - | |
222 | | - | |
223 | | - | |
224 | | - | |
225 | | - | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
| 220 | + | |
| 221 | + | |
| 222 | + | |
| 223 | + | |
| 224 | + | |
| 225 | + | |
| 226 | + | |
| 227 | + | |
| 228 | + | |
| 229 | + | |
| 230 | + | |
| 231 | + | |
| 232 | + | |
| 233 | + | |
| 234 | + | |
| 235 | + | |
| 236 | + | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
226 | 246 | | |
227 | 247 | | |
228 | 248 | | |
| |||
0 commit comments