Uh oh!
There was an error while loading. Please reload this page.
feat(sdui-parser): warn on dashboard widget options keys no renderer consumes (#5709) - #5743
Merged
os-zhuang merged 3 commits intoAug 23, 2026
Merged
Conversation
…consumes (#5709) Maintainer ruling 2026-08-23 (option B): a key riding DashboardWidgetOptionsSchema.passthrough() that no renderer consumes gets an authoring-time WARNING naming the consumed set. `invert` is the first pinned case, pinned as a case of the general mechanism. Census (measured on 8689166, spec 17.1.0): the spec requires `dataset` on every widget, both dashboard surfaces route dataset-bound widgets through DatasetWidget, and on that path the consumed options keys are exactly the five the spec declares — dateGranularity, sortBy, sortOrder, limit, stageOrder — plus `description` (the subCaption translation convention, DashboardRenderer.tsx read site). Notably NOT consumed on that path: `format` (the metric value is formatted with the dataset measure's own metadata), `thresholds` (zero read sites repo-wide), and `invert`. The census is re-measured every test run: spec-declared keys re-derived from the installed schema, DatasetWidget reads re-extracted from source (with loud failure on any consumption shape the extractor cannot see), the sub-caption read site re-checked, and a repo tripwire pinning the set of files that read widget.options. Scope: dataset-bound shorthand widgets on dashboard/dashboard-grid hosts only; legacy inline forms (spec-illegal, spread-shaped consumption) are skipped, as is the legacy component format. The spec's per-widget suppressWarnings: ['unconsumed-widget-option'] escape hatch is honoured. Warning severity only — no new red gates. Docs: plugin-dashboard page now states what a dataset-bound widget's options actually drive, and that gauge/solid-gauge/kpi/bullet render as a metric card whose format comes from the measure metadata (per #4295 there is no dial renderer). Fixes#5709 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m
Ablation B (emitter silenced) left this test green: its for-loop asserted nothing over an empty diagnostic list. Assert non-emptiness first. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EuPCi56cnGyykygi3z9w4m
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
|
This was referenced Aug 23, 2026
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
|
os-zhuang
marked this pull request as ready for review
August 23, 2026 10:21
Uh oh!
There was an error while loading. Please reload this page.
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 freeto 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#5709
Implements the 2026-08-23 maintainer ruling (option B, 「其他同意你的意见」): a dashboard widget
optionskey ridingDashboardWidgetOptionsSchema.passthrough()that no renderer consumes now draws an authoring-time warning naming the consumed set.invertis the first pinned case, pinned as a case of the general mechanism (a different dead key on a different widget type draws the identical diagnostic). Open extras stay open — a document with such keys still parses, saves and renders; warning severity only, no new red gates.Published API decisions (clause-2 surface — object to these by name)
This widens the
diagnose()/validateTreeoutput surface. The decisions, stated explicitly:unconsumed-widget-option(exported constantUNCONSUMED_WIDGET_OPTION). One diagnostic per dead key, matchingunknown-propgranularity, so consumers switching ond.codeand filtering by message keep working unchanged.warning— pinned by a dedicated test; the ruling forbids escalation.[host] widget "id-or-index" (widgetType): options.key reaches no renderer — dashboard widget renderers read only: dateGranularity, description, limit, sortBy, sortOrder, stageOrder(host tag in angle brackets in the real message; the consumed list is printed from the same exported array the check reads, so message and behavior cannot diverge).widgetsarray of adashboard/dashboard-gridnode the manifest resolves, in the spec shorthand form, dataset-bound only,optionsa plain object, and not suppressed. Deferred$exprvalues are opaque and never guessed at.suppressWarnings: ["unconsumed-widget-option"]is honoured (spec-legal, verified against the installed 17.1.0 schema).@object-ui/sdui-parser:checkDashboardWidgetOptions,CONSUMED_WIDGET_OPTION_KEYS,DASHBOARD_WIDGET_HOST_TYPES,UNCONSUMED_WIDGET_OPTION— so other surfaces (designer, save gates) can consume the same census instead of restating it.The census (measurement-first, as ruled)
Measured on
origin/mainat8689166f6, spec 17.1.0:dataseton every widget, and both dashboard surfaces (DashboardRenderer,DashboardGridLayout) route every dataset-bound widget throughDatasetWidget— so the dataset path is the only spec-legal render path.optionskeys are exactly the five the spec declares:dateGranularity,sortBy,sortOrder,limit(query-affecting,DatasetWidget.tsx:436-446),stageOrder(funnel stage order,DatasetWidget.tsx:1160) — all directoptions.keyreads.description— the metric-card sub-caption channel (DashboardRenderer.tsx:412; the server'stranslateDashboardwrites this key per thesubCaptionconvention, objectstack#8056 / objectstack#5428). Warning on a key the platform's own translation pipeline writes would be a false positive, so it is accepted with documented evidence....optionsspreads exist only on the legacy non-dataset branches, which the warning deliberately skips — that is what keeps the census small enough to be maintainable.Deviation from the ruling's premise — reported, not silently resolved
The ruling's docs instruction assumed gauge reads
format/thresholds. The census falsified both for the spec-legal path:thresholdshas zero read sites repo-wide, and the dataset-bound metric value is formatted with the dataset measure's own metadata (measureField(...).format/currency/percentScale,DatasetWidget.tsx:768), notoptions.format. (options.formatis read only on the retired non-dataset inline path, via a spread intoMetricWidget.) The ruling's own "plus whatever the census finds" makes the census authoritative, so: both keys draw the warning on dataset-bound widgets, and the gauge docs now state the measured truth — the number's format comes from the measure metadata;format/thresholds/inverthave no read site there, with the derived-measure recipe (hotcrm's own fix) as the documented alternative. If the maintainer intendedformat/thresholdsto be treated as consumed regardless, that is a one-line edit toCONSUMED_WIDGET_OPTION_KEYSplus its census-test pin — flagging for review rather than guessing.Maintenance cost (the ruled deliverable)
The accepted set is one pinned array whose every input is re-measured on each test run by
dashboard-widget-options-census.test.ts:@objectstack/specschema (with a passthrough-premise probe and adataset-required probe — if the spec changes either, the test names what to re-visit);DatasetWidget.tsxsource text, with loud refusal of any consumption shape the extractor cannot see (spread / destructuring / computed access) — under-counting is the false-positive direction and is designed to be impossible to introduce silently;descriptionread site re-checked;widget.options— a new consumer file turns the test red until the census is re-run.Steady-state cost: a renderer change that adds/removes a consumed key turns one test red; the fix is editing one array plus its census notes. Instrument limits are stated in the test header (text census counts comments as reads — safe direction; the tripwire cannot see a renamed receiver such as
w.options— known example documented). On this measurement the census is maintainable; the ruled fallback (D + honest docs) was not needed.Docs (path outside
packages/sdui-parser/**, reported per the fence)content/docs/plugins/plugin-dashboard.mdx— new section "Widgetoptions— what is actually read" with the consumed-key table, the warning and its escape hatch, and the gauge subsection (no dial renderer per #4295; format comes from the measure; derived-measure recipe for inversion).Evidence
All commands at final head
83150f062, exit codes captured before any pipe, verdict lines quoted:pnpm exec vitest run packages/sdui-parser/—Test Files 9 passed (9),Tests 77 passed (77); lock-runnerVERDICT command-exit 0.pnpm run type-check(sdui-parser:tsc --noEmit && tsc -p tsconfig.test.json) — exit 0.diagnose()consumers (plugin-grid gridNonAuthorKeys, console ga-honoured + union-specimens, schema-catalog pageheader, components record-picker parity):Test Files 5 passed (5),Tests 74 passed (74).pnpm lint(eslint . --no-inline-config):LINT_EXIT=0, 4m12s under the shared verify lock.check:spec-symbols"spec alignment claims: 2 declared deliberate copies, 18 unbacked claims in 5 packages" exit 0 ·check:doc-types"Every documented component type is registered" exit 0 ·check:control-bytes"OK (scanned 4791 tracked text file(s))" exit 0 ·check:esm-specifiersexit 0 ·check:self-importexit 0 ·check:phantom-deps"Every in-scope import is declared" exit 0 · changeset presence + no-major exit 0.check:doc-snippetsexits 1 with the known fresh-worktree shape ([unbuilt-package] ... dist/index.d.ts not on disk — run the build first; "The snippet program was NOT run") — the dispatch-listed worktree-broken gauge, not a diff failure; my docs edit adds no code fences. CI owns the real verdict.Ablations (commit-first; mutations confirmed on disk by marker counts in both directions; restored under
trap ... EXIT INT TERM; no build legs needed — these tests import package src via relative paths and the census reads renderer source text directly, so no dist resolution is involved)CONSUMED_WIDGET_OPTION_KEYS = []): 2 failed — exactly the two designed guards ("CONSUMED_WIDGET_OPTION_KEYS is non-empty..." and "every declared key is accepted..."). Measured justification for the vacuity guards: with an empty census the invert pin itself stays green (everything warns), so only explicit guards catch it.return []): 7 behavior pins red, census green — the expected split. This leg caught one vacuously-passing loop (the message-naming test) which was hardened in83150f062with a non-emptiness assertion.{ ...options }appended toDatasetWidget.tsx): "contains no consumption shape the extractor cannot see" red.options.ablationProbeKey): "the extracted read set equals the declared set — both directions" red.Marker counts: mutate leg 1-in / anchors-out, restore leg 0-in / anchors-back, printed by each script.
Out of scope, filed
dashboardpublishes only columns/gap/className as inputs —widgets,title,header,globalFilters,dateRange,refreshIntervalall drawunknown-propwhile the renderer honours them #5742 — thedashboardblock publishes onlycolumns/gap/classNamewhile the renderer honourswidgets,title,header,globalFilters,dateRange,refreshInterval; an inline dashboard author drawsunknown-propon the verywidgetskey this warning descends into (objectui#4668 class, needs per-key triage).Generated by Claude Code
Generated by Claude Code