Repository navigation
Commit d92b2a1
fix(plugin-dashboard): retire the metric sub-caption reads at both ends (objectui#11389) (#11957)
Fixes #11389
Clause-②: no
objectui#11070 remains open. This PR is the objectui reader half of
ruling C on objectui#11389 (record `5942430353`), after objectstack's
half landed as `99e1912afc` and shipped in `@objectstack/spec` 17.7.0,
which objectui `main` installs.
> **C: retire the metric sub-caption at both ends, objectstack first.**
> A widget keeps one authored description, `widget.description`, still
rendered as the card-header subtitle.
## What changes
- **`@object-ui/plugin-dashboard`.** No metric tile draws a sub-caption
any more, on either surface (`DashboardRenderer`,
`DashboardGridLayout`), dataset-bound or stored inline.
- `DatasetWidget` loses its `subCaption` prop, its read of the options
bag's `description`, and the caption row.
- The resolver module `widgetSubCaption.ts` (`useWidgetSubCaption`, both
limbs) is deleted, and both dispatch sites stop passing the prop.
- Both surfaces' inline metric arms spread `...options` onto the metric
node, and `MetricWidget` draws the node's `description` as its caption.
So the spread alone kept drawing `options.description`. Both arms now
spread `withoutRetiredSubCaption(options)`, one helper in
`widgetDispatch.ts`, so the two surfaces cannot disagree.
`DashboardGridLayout`'s arm was not on the dispatch's list; it is the
same read through the same spread.
- `widget.description` still draws as the card-header subtitle on
`DashboardRenderer`, translated through the widget's `description`
bundle key.
- **`@object-ui/i18n`.** `useObjectLabel()` no longer returns
`widgetSubCaption` (limb 2's resolver).
- **`@object-ui/sdui-parser`.** `CONSUMED_WIDGET_OPTION_KEYS` drops
`description`; it is now exactly the five keys the spec declares.
- **Docs.** `plugin-dashboard.mdx` (the options table row and the
translation-key list), the plugin-dashboard README's `localized`
section, and `dashboard-filters.md`, whose sentence "the renderer still
draws a stored widget that carries them" this change made false for
`options.description`.
## Exports removed or narrowed
- `@object-ui/i18n`: the `widgetSubCaption` member of
`useObjectLabel()`'s return value. Nothing in this repo read it.
- `@object-ui/sdui-parser`: `CONSUMED_WIDGET_OPTION_KEYS` loses
`'description'`. The export stays.
- `@object-ui/plugin-dashboard`: nothing on the package entry.
`useWidgetSubCaption`, `SubCaptionWidget` and `DatasetWidget`'s
`subCaption` prop were internal. The new `withoutRetiredSubCaption` is
internal too.
The changeset `.changeset/11389-retire-metric-subcaption.md` declares
the three packages `minor` with the BREAKING note and lists the same
removals.
## The restart probe, run first on the install surface
The hold comment (`5947079886`) ordered it before any edit:
- **Installed spec.** `@objectstack/spec` resolves to 17.7.0 from
`packages/types`, `packages/i18n`, `packages/plugin-dashboard` and
`packages/sdui-parser`.
- **Refused by name.** `TranslationDataSchema` and
`TranslationBundleSchema` refuse a bundle carrying
`dashboards.D.widgets.W.subCaption`: `invalid_type` at that path, with a
message naming the key as removed in 17.7.0.
- **Lit control.** The same bundle with `widgets.W.title` parses on both
schemas.
- **Consumed sets.** The installed `@objectstack/sdui-parser@17.7.0`
(pulled in by `@objectstack/lint`) has five keys and no `description`.
objectui's own `CONSUMED_WIDGET_OPTION_KEYS` still had `description`
before this PR.
## The dispatch's mechanism assumptions, measured
**H1 (read sites): confirmed, plus one.** Re-measured on `19a7348`
(`main` had moved past the seat's `f1781be`). The read sites:
- `DatasetWidget`'s metric branch;
- `useWidgetSubCaption`'s limb 1;
- `DashboardRenderer`'s inline metric arm (`description: subCaption`
after the spread);
- the two dataset dispatch sites
(`subCaption={tWidgetSubCaption(datasetWidget) ?? null}`);
- **the extra one:** `DashboardGridLayout`'s inline metric arm, whose
bare `...options` spread carried the key into `MetricWidget`.
**H2 (limb 2 has no live feed on 17.7.0): holds.**
- **Producers measured: zero.** No non-test `subCaption` bundle entry
exists anywhere in objectui. On objectstack `origin/main` (`f4bed583`)
only the tombstone, the conversion, the migration entries and prose
remain.
- **The server cannot serve one.** A 17.7.0 server refuses a stored
`subCaption` at load: the conversion
`translation-widget-sub-caption-removed` is `retiredFromLoadPath`, and
the schema tombstone does the refusing.
- **Doors that still take the key without the schema.** There are three:
- a host's `I18nProvider` `config.resources`;
- a host's `loadLanguage`: `transformSpecTranslations` copies
`dashboards` through verbatim;
- a pre-17.7 server serving a stored entry.
- **Why the open doors are not a feed.** Each would carry only a key the
installed spec refuses by name, and nothing writes one. Keeping a client
reader for them would be consumer-side tolerance for off-spec metadata
(AGENTS.md #0.1). So this PR takes the full route: limb 1, limb 2, the
hook, the prop chain and the `useObjectLabel` member.
**H3 (the parser half): narrows.** The accepted set goes from 6 keys to
5. For an authored `options.description` on a dataset-bound widget:
- **Before:** no diagnostic. Ablation A below reads the old set as
`expected [] to have a length of 1 but got +0`.
- **After:** exactly one diagnostic, severity `warning`, code
`unconsumed-widget-option`. It is a warning, never an error, and
`suppressWarnings` still applies.
## Pins: reversed, not deleted
- **`DashboardRenderer.metricSubCaption.test.tsx` (inline metric arm,
both surfaces).**
- An authored string, or a per-locale map, draws nothing.
- A bundle `subCaption` draws nothing, while the same entry's `title`
translates (the lit control).
- On `kpi`, `widget.description` draws as the header subtitle and
`options.description` beside it does not.
- A dashboard with no `name` draws nothing either.
- **`DashboardSurfaces.datasetSubCaption.test.tsx` (dataset tile, both
surfaces).**
- R1 and R2: an authored string or map grows no caption node.
- R3: a bundle `subCaption` grows none, with or without an authored
value.
- L1 (lit, on every case): the value is drawn.
- L2: the translated `widget.description` header subtitle is drawn.
- **`DatasetWidget.subCaption.test.tsx` (direct mount).** Every authored
form renders the no-caption markup byte for byte.
- **`DashboardRenderer.servedLocalized-11295.test.tsx` and app-shell
`DashboardView.servedLocalized-11295.test.tsx`.** The four live texts
keep their pins. The retired sub-caption stays in the fixtures (on the
served widget and in the bundle) and is pinned absent.
- **`dashboard-widget-options.test.ts`.** A new H3 block, with a
control: a widget-level `description` and a declared option draw
nothing.
- **`dashboard-widget-options-census.test.ts`.**
- Leg 1: no undeclared key is accepted.
- Leg 2: the read set is the declared set, and `description` is refused
by name.
- Leg 3 (reversed): no `plugin-dashboard` source reads
`options.description` or resolves a `subCaption`, with a lit
`options.limit` control on the same comment-stripped scan. The resolver
module is gone and no surface calls its hook.
- Leg 4: the tripwire list loses `widgetSubCaption.ts` and
`useObjectLabel.ts`.
- **`useObjectLabel-identity-5564.test.tsx`.** The member-count pin goes
from 28 to 27 and now names `widgetSubCaption` as absent.
## Reverse verification
All three ablations ran from the committed `344f8cea`. Each mutation
went through `ablation-replace.mjs`, with the anchor count and the blob
change verified on disk, and each restore was proven: blob equal to
`HEAD` and `git diff HEAD` empty. The tests resolve `@object-ui/*` to
`src/` through the root vitest aliases and import their subjects
relatively, so no `dist/` leg applies.
- **A: put `description` back into `CONSUMED_WIDGET_OPTION_KEYS`.** Red
as predicted: 4 failed and 27 passed. The failures are the two H3 cases,
census leg 1 and census leg 2's "every accepted key has a read site".
The H3 control stayed green.
- **B: make `withoutRetiredSubCaption` return `options` unchanged.** Red
as predicted: 18 failed and 19 passed. Every failure is an inline-arm
pin: `metricSubCaption` on both surfaces, and both `servedLocalized`
files. The two dataset-tile files stayed green, so the pins discriminate
by arm.
- **C: re-inject a string-only `options.description` caption into
`DatasetWidget`'s metric branch.** Red as predicted: 11 failed and 29
passed.
- **Red:** census legs 2 and 3; R1 and R3 (authored beside a bundle
entry) on both surfaces; L2; and the direct-mount string, overlay and
repro cases.
- **Green:** the per-locale map cases, because the injected read was
string-only, and the inline-arm file. That is the direction expected.
- **First attempt:** it was refused by the tool, because the replacement
contained the anchor. It held the lock 0s and ran no test, and the
restore was proven. It was re-run with a non-repeating anchor.
## Tests and gates
The head is `0916a26d`. The two commits after `344f8cea` touch only
`packages/i18n`: the count pin, and one restored space in
`useObjectLabel.ts`.
**Tests (`pnpm exec vitest run` from the repo root):**
- **`packages/plugin-dashboard/`:** 174 files, 1725 passed and 6
skipped, at `344f8cea`. The baseline at `19a7348` was 195 files and 2030
passed across plugin-dashboard and sdui-parser together.
- **Part two, at `b8b7d515`:** 117 files, 2304 passed and 13 skipped. It
covered:
- `packages/sdui-parser/` and `packages/i18n/`;
- the app-shell `servedLocalized` test;
- the outside tests that read the changed files or symbols:
`unconsumed-widget-option-claim-6186`,
`one-authority-per-exported-name-6273`, `markdown-test-inputs`,
`dollar-dialect-alias-census`, `check-doc-component-types`,
`check-control-bytes`, `check-i18n-call-site-keys`,
`check-i18n-dead-keys`, the schema-catalog dashboard gallery render, the
console and cli `unfulfilled-chart-stubs-8760` tests, and the types
`dashboard-widget-slot-component-arm-7952` and
`strict-face-read-keys-11070` tests.
- The first part-two run, at `344f8cea`, was red in exactly one test:
the i18n member-count pin, 28 against 27. `b8b7d515` updates it.
- **`packages/i18n/` and `packages/sdui-parser/` again, at `0916a26d`:**
103 files, 1617 passed and 13 skipped.
**Type-check:**
- `pnpm --filter … run type-check` for `@object-ui/plugin-dashboard`,
`@object-ui/sdui-parser` and `@object-ui/i18n` is green at `344f8cea`,
with the script name echoed for each.
- `@object-ui/i18n` is green again at `0916a26d`, and
`@object-ui/app-shell` is green at `344f8cea`.
- `tsconfig.test.json --listFilesOnly` lists the 4 changed
plugin-dashboard test files and the 2 changed sdui-parser test files.
**Gates:**
- **Green at `0916a26d`:** `check-changeset-presence` (1 changeset, 4
released packages touched), `check:changeset-claims`,
`check:new-line-citations` (0 new), `check:control-bytes`,
`check:i18n-keys`.
- **Green at `b8b7d515`:** `check:pending-changeset-literals`,
`check:i18n-drift`, `check:i18n-dead-keys`, `check:doc-fences`,
`check:doc-types`, `check:doc-snippets` (784 of 784 blocks judged, 0
failed), `check:doc-examples`, `check:readme-exports`,
`check:skill-examples`.
- **How the build-dependent gates got a build.** The last four first
answered PRECONDITION NOT MET (unbuilt `dist/`). They ran after the
scoped build `check-doc-snippet-types.mjs --build-filter` names (35
tasks).
- **Lint.** `pnpm lint` is CI's. ESLint over the 14 changed TS/TSX files
gives 0 errors and 34 warnings. One warning sits on a changed line: the
`DatasetWidget` signature, rewritten to drop `subCaption`; its `widget:
any` predates this branch. This is a targeted run, not a proven
narrowing.
- **Governed surface.** `check-governed-queue-guard.mjs --test` answers
NOT GOVERNED for all 18 paths.
## Acceptance notes
- **A producer outside both repos is not measured.** objectui#7293, the
card that taught `DatasetWidget` to draw `options.description`, was
filed from `objectstack-ai/duly` (duly#109), which asked for a caption
under a KPI value. The ruling's producer count covered objectui and
objectstack. Whether duly authors `options.description` today is NOT
MEASURED: this session could not read that repository (REST 403, and
repository access was not granted). If it does, its tiles lose that
caption silently on render, and the authoring validator now warns
`unconsumed-widget-option`. The ruling already names the way back: a
declared widget-level key outside `options`.
- **The grid surface draws no header subtitle.** `DashboardGridLayout`'s
card header draws the title only, never `widget.description`;
`DashboardRenderer` draws both. This predates this PR and is dormant: no
in-repo surface mounts `DashboardGridLayout`. Not filed.
- **Stale objectstack comments.** Two example comments in objectstack
("plus the description sub-caption", in the app-crm pipeline and
app-todo task dashboards) go stale with this PR. The holder's pointer
(`5946940099`) says objectstack seat post #6017 carries them for the
`.objectui-sha` bump.
The implementing session is
`https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw`.
---
_Generated by [Claude
Code](https://claude.ai/code/session_01DBZ9bntPZ7VKyQNtJeNsgw)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent f0496bd commit d92b2a1
19 files changed
Lines changed: 473 additions & 893 deletions
File tree
- .changeset
- content/docs
- guide
- plugins
- packages
- app-shell/src/views
- i18n/src
- __tests__
- plugin-dashboard
- src
- __tests__
- sdui-parser/src
- __tests__
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
78 | 78 | | |
79 | 79 | | |
80 | 80 | | |
81 | | - | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
82 | 85 | | |
83 | 86 | | |
84 | 87 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
367 | 367 | | |
368 | 368 | | |
369 | 369 | | |
370 | | - | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
371 | 378 | | |
372 | 379 | | |
373 | 380 | | |
| |||
518 | 525 | | |
519 | 526 | | |
520 | 527 | | |
521 | | - | |
522 | | - | |
| 528 | + | |
| 529 | + | |
| 530 | + | |
| 531 | + | |
523 | 532 | | |
524 | 533 | | |
525 | 534 | | |
| |||
Lines changed: 13 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
29 | 34 | | |
30 | 35 | | |
31 | 36 | | |
| |||
63 | 68 | | |
64 | 69 | | |
65 | 70 | | |
| 71 | + | |
66 | 72 | | |
67 | 73 | | |
68 | 74 | | |
| |||
86 | 92 | | |
87 | 93 | | |
88 | 94 | | |
89 | | - | |
90 | 95 | | |
91 | 96 | | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
92 | 101 | | |
93 | 102 | | |
94 | 103 | | |
95 | 104 | | |
96 | 105 | | |
97 | | - | |
98 | 106 | | |
99 | 107 | | |
100 | 108 | | |
| |||
103 | 111 | | |
104 | 112 | | |
105 | 113 | | |
106 | | - | |
107 | 114 | | |
108 | 115 | | |
109 | 116 | | |
110 | 117 | | |
111 | 118 | | |
112 | 119 | | |
113 | | - | |
114 | 120 | | |
115 | 121 | | |
116 | 122 | | |
| |||
126 | 132 | | |
127 | 133 | | |
128 | 134 | | |
129 | | - | |
| 135 | + | |
130 | 136 | | |
131 | 137 | | |
132 | 138 | | |
| |||
167 | 173 | | |
168 | 174 | | |
169 | 175 | | |
| 176 | + | |
170 | 177 | | |
171 | 178 | | |
172 | 179 | | |
| |||
175 | 182 | | |
176 | 183 | | |
177 | 184 | | |
| 185 | + | |
178 | 186 | | |
179 | 187 | | |
Lines changed: 5 additions & 2 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
170 | 170 | | |
171 | 171 | | |
172 | 172 | | |
173 | | - | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
174 | 176 | | |
175 | 177 | | |
176 | 178 | | |
177 | | - | |
| 179 | + | |
| 180 | + | |
178 | 181 | | |
179 | 182 | | |
180 | 183 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
463 | 463 | | |
464 | 464 | | |
465 | 465 | | |
| 466 | + | |
| 467 | + | |
| 468 | + | |
| 469 | + | |
| 470 | + | |
466 | 471 | | |
467 | 472 | | |
468 | 473 | | |
469 | 474 | | |
470 | 475 | | |
471 | 476 | | |
472 | 477 | | |
473 | | - | |
474 | | - | |
475 | | - | |
476 | | - | |
477 | | - | |
478 | | - | |
479 | | - | |
480 | | - | |
481 | | - | |
482 | | - | |
483 | | - | |
484 | | - | |
485 | | - | |
486 | | - | |
487 | | - | |
488 | | - | |
489 | | - | |
490 | | - | |
491 | | - | |
492 | | - | |
493 | | - | |
494 | | - | |
495 | | - | |
496 | | - | |
497 | | - | |
498 | | - | |
499 | | - | |
500 | | - | |
501 | 478 | | |
502 | 479 | | |
503 | 480 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
834 | 834 | | |
835 | 835 | | |
836 | 836 | | |
837 | | - | |
| 837 | + | |
838 | 838 | | |
839 | 839 | | |
840 | 840 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| 20 | + | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
25 | | - | |
26 | 26 | | |
27 | 27 | | |
28 | 28 | | |
| |||
175 | 175 | | |
176 | 176 | | |
177 | 177 | | |
178 | | - | |
179 | | - | |
180 | | - | |
181 | | - | |
182 | | - | |
183 | | - | |
184 | | - | |
185 | | - | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | 178 | | |
195 | 179 | | |
196 | 180 | | |
| |||
415 | 399 | | |
416 | 400 | | |
417 | 401 | | |
418 | | - | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
419 | 406 | | |
420 | 407 | | |
421 | 408 | | |
| |||
711 | 698 | | |
712 | 699 | | |
713 | 700 | | |
714 | | - | |
715 | | - | |
716 | | - | |
717 | | - | |
718 | | - | |
719 | | - | |
720 | | - | |
721 | 701 | | |
722 | 702 | | |
723 | 703 | | |
| |||
0 commit comments