Uh oh!
There was an error while loading. Please reload this page.
feat(types)!: DashboardWidgetSchema refuses undeclared keys (.strict()) - #7033
Merged
Conversation
Route 1 step 2 of the maintainer ruling on objectui#6002: after #6150 declared the genuinely-consumed keys, the widget schema flips from strip-in-silence to loud refusal — zod `unrecognized_keys` names every offending key. The widget-slot component route (objectstack#8593 ruling: metric-card props belong to passthrough BaseSchema) moves into the shared DashboardComponentSchema widget slot as a union, so ruled-legal component nodes keep parsing whole with props intact. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
…r, breaking narrative) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PBjwYLS6BciTQW3c9xQiD2
os-warren
marked this pull request as ready for review
August 31, 2026 14:20
os-warren
enabled auto-merge
August 31, 2026 14:20
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 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#6002
Route 1 step 2 of the maintainer ruling (issue comment 5406212152, 2026-08-25): after #6150's declaration catch-up,
DashboardWidgetSchemaflips from strip-in-silence to.strict()— an undeclared widget key now refuses the parse with zod'sunrecognized_keysissue naming every offending key. The card's measured five-keys-in/three-keys-out ACCEPT is reproduced in this PR's pin suite as a loud refusal namingzzcanary/categoryField/aggregate.Step 0 — premise re-verified on the merged ref
#6150 closed via PR #6945 (squash commit
2c45966, contained in this branch's basef7850ee). All 13 recorded renderer-consumed keys verified declared, key by key, against the interface blocks on that ref:TextSchema.content·CarouselSchema.opts/orientation/itemClassName·FilterBuilderSchema.wrapperClass·TreeViewSchema.nodes/onNodeClick/title·CheckboxSchema.required·FileUploadSchema.buttonText/wrapperClass·HoverCardSchema.align·ContextMenuSchema.trigger. 13/13 declared — premise holds. (Note: none of the 13 are dashboard-widget keys; the family-1 corpus measurement below is what answers whether any LIVE dashboard depends on a key nothing declares.)Step 1 — blast radius, measured before the change (ruling: not assumed)
The strict-refusal delta over the live corpus is EMPTY — no per-key fork back to triage is needed.
.strict()candidate, routing component-enum types toBaseSchemaper the 2026-08-14 ruling: 575 JSON files under examples/apps/packages/e2e/public plus every dashboard-bearing fenced block in md/mdx docs — 10 dashboard documents, 30 widgets (11 of them component nodes). Newly-refused widgets: zero. Doc fences that the script could not auto-parse were hand-triaged (all shape references or component-node examples; details in the report comment on finding(types):DashboardWidgetSchemaSILENTLY DROPS every undeclared widget key — the file's own docstring names this failure mode, and it still applies to the keys nothing declares #6002). Two pre-existing, non-delta facts recorded:packages/plugin-dashboard/README.mdfence 8 authors a widget withtype: 'card', which the closed type enum already refuses TODAY (doc drift predating this PR — reported to PM as an out-of-scope finding);apps/console/src/preview-samples.ts's dashboard sample is already in that suite's KNOWN_STALE ledger against the spec schema (value/format/chart— also not a delta of this change).plugin-dashboardexamples are refused by the spec'sDashboardSchema— the AI few-shot corpus teaches the pre-ADR-0021 widget shape #4600's BaseSchema routing). Measured on the current build: with a bare.strict()and no routing, the 11 ruled-legalmetric-cardwidgets in the catalog corpus would refuse at the whole-document parse onvalue/icon/trend/trendValue— live documents the 2026-08-14 ruling (objectstack#8593) declares legal.BaseSchemaaccepts all 11. The routing the ruling names (component node owned by passthrough BaseSchema) therefore moves into the sharedDashboardComponentSchemawidget slot in this PR, as a union whose component arm is gated on the closed component-type enum — it is structurally unreachable for spec-family widgets, so it cannot become a hatch around the refusal. The routing schema is deliberately NOT exported (internal slot property, no new authoring surface).Step 2 — the change
DashboardWidgetSchemagets.strict()(packages/types/src/zod/complex.zod.ts). The spec's tombstones (actionUrl/actionType/actionIcon/aria/responsive) stay declared z.never members and keep their specific removal messages (re-verified; pinned in report-chart-query-spec-parity).DashboardComponentSchema.widgetsroutes component-enum types to a passthrough BaseSchema arm before the strict widget schema (see above).BaseSchemaitself is untouched; the A gauge widget'soptions.inverthas no observable effect, and nothing can say so — it is an undeclared key ridingDashboardWidgetOptionsSchema.passthrough()#5709 options-layer passthrough is untouched; schema-catalog: all 9plugin-dashboardexamples are refused by the spec'sDashboardSchema— the AI few-shot corpus teaches the pre-ADR-0021 widget shape #4600's closed type enum is untouched.invalid_union("Invalid input") with the namedunrecognized_keys/ enum issues nested per arm. Direct widget parses (the CLI/gate routing path) are unchanged in shape and fully named. The schema-catalog: all 9plugin-dashboardexamples are refused by the spec'sDashboardSchema— the AI few-shot corpus teaches the pre-ADR-0021 widget shape #4600 gate's counter-probe regexes were updated for this (order-insensitive lookaheads; both message halves still asserted).Tests and gates (final head
7718ccc; every run below was re-run at this sha)packages/types/src/__tests__/dashboard-widget-strict-6002.test.ts: the card's probe refuses naming all three keys; the retired inline-analytics quartet refuses as a set; a declared-surface widget parses green with every key surviving; the legacy id/component/layout envelope parses; a component-node document parses whole with props kept; two not-a-hatch pins refuse stray keys at document level.pnpm exec vitest run packages/types/ examples/schema-catalog/ packages/cli/— 112 files, 3113 tests, all passed (includes the schema-catalog: all 9plugin-dashboardexamples are refused by the spec'sDashboardSchema— the AI few-shot corpus teaches the pre-ADR-0021 widget shape #4600 gate with updated counter-probes, the safe-validate corpus sweep, and the CLI validate tests).pnpm exec vitest run packages/plugin-dashboard/ packages/plugin-designer/— 99 files, 901 tests, all passed.@object-ui/typestype-check (tsc noEmit + examples + test tsconfigs) green; tsc --listFiles confirms the new test file is in the compiled closure (1 hit — not a NOT-MEASURED green)..strict()turns exactly the 4 refusal pins red (5 routing/positive pins stay green, as predicted); restore verified byte-identical to the HEAD blob. The suite resolves the subject through the vitest root alias to src, so no dist build sits in the ablation loop.pnpm lint(repo-wide eslint) and the full cross-packagepnpm testfarm were not run locally; the targeted set above covers every suite that runtime-parses these schemas (enumerated by grep over safeParse/parse call sites).Review parking
Clause-② yes (accept/reject behaviour change on a published contract): this PR is parked as draft with
needs:contract-reviewon both carriers, to be released through the in-seat contract-review flow (objectstack#13795) — not self-merged.Generated by Claude Code
Generated by Claude Code