Uh oh!
There was an error while loading. Please reload this page.
fix(plugin-dashboard): stop authoring the retired widget-level actionUrl key (objectstack#7129) - #4058
Merged
Conversation
…Url key (objectstack#7129) `actionUrl`/`actionType`/`actionIcon` are retiredKey tombstones at the widget level since @objectstack/spec 17.0.0-rc.3 (objectstack#5010, ADR-0049 D2) — the spec types them `never` and refuses any value. Two producers in plugin-dashboard still emitted the widget-level key: - WidgetConfigPanel's Behavior group offered a "Click-through URL" field bound to `actionUrl`, a control with no consumer on the render side at all. - DashboardWithConfig seeded `actionUrl: widget.actionUrl ?? ''` into every widget config, so EVERY panel save persisted `actionUrl: ''` — a parse error — even when the author never opened the Behavior group. Removes both, scrubs all three keys in sanitizeDraftForType as defence in depth, and corrects the @object-ui/types docblock that listed them as ordinary inherited keys. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016R9de1FqP7NvwKvqXi92Gh
The latest updates on your projects. Learn more about Vercel for GitHub. |
Contributor
✅ Console Performance Budget
📦 Bundle Size Report
Size Limits
|
os-help
marked this pull request as ready for review
August 10, 2026 02:42
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.
Fixesobjectstack-ai/objectstack#7129
What was wrong
actionUrl/actionType/actionIconwere retired at the widget level in@objectstack/spec17.0.0-rc.3 (objectstack#5010, ADR-0049 D2). They areretiredKeytombstones:DashboardWidgetSchematypes themneverand refuses any value, so authoring one is a tsc error and a parse error.plugin-dashboardwas still producing the widget-level key. All verified on objectuiorigin/main@11c1e71e, not a working tree:WidgetConfigPanel.tsx:391— a Behavior-group authoring field labelled "Click-through URL", bound toactionUrl.DashboardWithConfig.tsx:114—actionUrl: widget.actionUrl ?? ''seeded into every widget config handed to the panel.WidgetConfigPanel.tsxsanitizeDraftForType— the only scrub between panel draft and persistence, and it deletesLEGACY_ANALYTICS_KEYS(dataset-shape keys) only.The defect is wider than the issue body measured
The issue and its triage thread frame this as "the panel offers a field that authors a tombstoned key". Measured here, the seed at site 2 makes it unconditional: because
selectedWidgetConfigalways setsactionUrl(to''when absent),useConfigDraftstarts from it and the scrub does not remove it, so every save from the widget panel emittedactionUrl: ''— including a save where the author merely renamed a widget and never opened the Behavior group. The reverse-verification run below shows exactly that:actionUrlin the persisted payload of a test that touches only the title field.The field was inert in the other direction too
Zero-hit result, falsified as the card requires: no dashboard widget renderer reads
widget.actionUrlanywhere in objectui. Falsifier —colorVariant, the sibling key that travels the identical seed to panel-field to draft to scrub path — resolves to a real consumer chain (DashboardWithConfig.tsx:113,WidgetConfigPanel.tsx:407,colorVariants.ts,DatasetWidget.tsx:722), so the sweep does find consumers when they exist. Also checked for the blind spots named on the card: no bracket or dynamic access anywhere inpackages/apps/examples/e2e, and no cross-line concatenation candidates.DashboardRenderer's 14actionUrloccurrences remainheader.actions[]-scoped, so the ledger's claim about the renderer half still holds.So the control was a lying control twice over: the URL an author typed was never navigated to, and the value was refused by the spec on the way in.
What changed
WidgetConfigPanel.tsx— the Behavior section is removed (it held exactly this one field), replaced by a comment recording why it must not come back.sanitizeDraftForTypenow also scrubs all three retired keys, as defence in depth for stored widgets that already carry them and for hosts drivingWidgetConfigPaneldirectly.DashboardWithConfig.tsx— theactionUrlseed is removed.packages/types/src/complex.ts— theDashboardWidgetSchemadocblock listed the three keys among those that "flow in from the spec" alongside live keys likecolorVariant. They do flow in, as?: never. The prose now says so, and records the asymmetry that let these producers survive the 2026-08-04 renderer-side sweep: authoring one is a tsc error, but reading one still type-checks asnever | undefined.I took the removal route rather than only widening the scrub, per the dispatch's preference: a UI field whose value is silently scrubbed on save is exactly the lenient-consumer shape this campaign removes. I found no live non-tombstoned consumer that would argue for keeping the field.
actionType/actionIconwere checked separately, as the card asked: neither is authored by the panel nor read byDashboardWithConfig, so no field removal was invented for them. They are covered by the scrub and by the new pin only.Verification
All commands run from the repo root (the repo's vitest guard rejects the per-package form).
Executed the spec parse rather than reading it —
DashboardWidgetSchema.safeParseagainst the real@objectstack/spec17.0.0-rc.5:The two falsifiers matter:
colorVariantpassing shows the schema is not rejecting everything, and the bogus key failing with a different code (unrecognized_keys, notinvalid_type) shows theactionUrlfailures are the tombstone firing rather than generic strictness.Reverse verification. Predicted direction: plain red — the new pins assert the absence of a producer this PR removes, so restoring it must break them. Taking the fix out with
git checkout origin/mainon the two source files (nevergit stash):5 of 6 red as predicted. The single case that stayed green is the intended control — the "spec really does refuse these keys" premise block, which does not depend on this change. The
DashboardWithConfigfailure is the direct evidence for the unconditional-seed claim above:expected { id: 'w1', title: 'Revenue v2', ... } to not have property "actionUrl", in a test that only edits the title.One honest correction to my own first draft: the round-trip assertion initially failed because the panel emits the documented flattened shape (
layout.wbecomeslayoutW), which the spec refuses asunrecognized_keys. That is the panel's documented host-facing intermediate shape, not a defect this PR introduces; the test now un-flattens before parsing. It did surface a separate question about that contract, filed rather than fixed here (see below).Targeted suites, all green:
Dependency closure built first (
pnpm --filter '@object-ui/plugin-dashboard^...' build) so tsc read rebuilt.d.tsrather than stale artefacts.Out of scope, filed separately
objectstack#7193 —
DashboardWithConfigforwards the panel's flattenedlayoutW/layoutHverbatim toonWidgetSave, and the spec refuses both asunrecognized_keys. Surfaced by the test correction described above. Whether it bites depends on the out-of-repo Studio host (objectui has no in-repo consumer ofDashboardWithConfig), so it is filed observation-class rather than fixed here.Generated by Claude Code