Skip to content

test(plugin-dashboard): a drawn dashboard-seam pin for the dataset face's five plot-internal chartConfig keys - #9286

Merged
os-tesla merged 2 commits into
mainfrom
claude/issue-9203-dataset-chartconfig-seam-pin
Sep 12, 2026
Merged

test(plugin-dashboard): a drawn dashboard-seam pin for the dataset face's five plot-internal chartConfig keys#9286
os-tesla merged 2 commits into
mainfrom
claude/issue-9203-dataset-chartconfig-seam-pin

Conversation

@os-tesla

Copy link
Copy Markdown
Collaborator

Fixes #9203

The DATASET face forwards nine chartConfig keys through @object-ui/core's
chartConfigPresentation. Four of them paint outside Recharts'
ResponsiveContainer and are pinned on the dashboard seam by
DatasetWidget.chartConfig.dom.test.tsx. The other five — colors,
categoryColors, showDataLabels, annotations, interaction — paint inside
it and had no drawn assertion on this surface at all.

This adds one: packages/plugin-dashboard/src/__tests__/DatasetWidget.chartConfigMarks-9203.test.tsx,
10 assertions over dataset widget metadata in and drawn Recharts marks out, with
no renderer stub anywhere in the chain. The technique is PR #9202's, transferred
verbatim: ResponsiveContainer seeds its size from getBoundingClientRect on
its OWN element, so a stub scoped to the recharts-responsive-container element
gives the plot a measured box — no module mock, which is what made this look
impossible from plugin-dashboard.

The red, demonstrated

⭐ A new pin that has never been seen red is worth exactly as much as the zero
coverage it replaces, so here is the ablation. The forwarding call in
packages/plugin-dashboard/src/DatasetWidget.tsx was replaced by an empty
object, the mutation was proved on disk before the run, and the restore was
proved by state afterwards.

leg reading
mutation anchor count 1 to 0
injected marker count 0 to 1
blob hash on disk 1203c0e9… to e02ab1c4…
restore: blob hash back to 1203c0e9…, equal to the HEAD blob
restore: git diff HEAD empty

With the forwarding deleted, across the three files in one run:

file result
DatasetWidget.chartConfigMarks-9203.test.tsx (new) 10 tests, 7 failed
DatasetWidget.chartConfig.dom.test.tsx (lit control, untouched assertions) 7 tests, 3 failed
plugin-charts/src/ChartRenderer.dashboardChartConfig.test.tsx 13 tests, 0 failed

Every one of the five keys reddens: the colors palette arm, the
categoryColors record arm, showDataLabels, both annotations arms and both
interaction arms. The three that stay green in the new file are its pure
absence arms — "no data labels when off", "no reference marks when none
declared", "the default palette" — which is the correct behaviour for an
absence assertion when the thing is absent for a second reason, and why each is
paired with a positive arm taken through the same harness.

The third row is the point of the card, now measured on this face: 13 assertions
that name these keys did not move while the thing they name was deleted. PR
#9202 took the same reading on the relay face (46 red across the dashboard
files, 0 there). ⛔ That file is not coverage for these keys on any dashboard
surface.

Restoring the forwarding returns all three files to green (30 passed).

The comment the card asked for, and two more like it

The acceptance names the header in DatasetWidget.chartConfig.dom.test.tsx
that said these marks could only be pinned inside plugin-charts. Grepping the
citation found two more making the same claim about the same file, and the
ablation above measured all three false in one run, so all three are corrected:

  • DatasetWidget.chartConfig.dom.test.tsx — the one named in the acceptance.
    ⭐ Its assertions are deliberately untouched: they are the lit control above.
  • DatasetWidget.chartConfig.test.tsx (dataset seam pin) cited plugin-charts'
    file as the DOM half for the keys drawn inside the plot. It now cites the new
    pin.
  • plugin-charts/src/ChartRenderer.dashboardChartConfig.test.tsx claimed the
    mock "and therefore this half of the evidence" had to live there, and that it
    plus the dataset seam pin "close the loop from dashboard metadata to drawn
    pixels". Its objectui#4044 block already disclaimed the two inline relays; it
    now disclaims the dataset face on the same measured footing.

All three edits are comment-only — no assertion, import or fixture moves in any
of them.

The card's recharts premise: re-measured, and the voided probe explained

Triage recorded a VOIDED probe — require.resolve('recharts') answered
MODULE_NOT_FOUND from packages/plugin-charts too, so the lit control did not
light and the premise was NOT MEASURED. The container had been restarted and
node_modules was absent repo-wide, which makes every require.resolve fail
identically. Both halves were checked:

root recharts react (control)
shared checkout, no install MODULE_NOT_FOUND MODULE_NOT_FOUND
this worktree, installed — plugin-charts resolves resolves
this worktree, installed — plugin-dashboard MODULE_NOT_FOUND resolves

The first row reproduces triage's dead instrument exactly, so the missing
install does explain it. The last two rows carry two controls that fire: the
plugin-charts row is the lit control triage wanted, and react resolving from
plugin-dashboard shows the resolver was answering at that root at all. ⇒ the
card's premise is confirmed: recharts genuinely does not resolve from
packages/plugin-dashboard. Its conclusion stays false, and this PR is the
counter-example.

Changeset

Needed, and declared as releasing nothing. scripts/check-changeset-presence.mjs
guards everything under a released package's src/, and all four touched files
live there — it exits 1 on this diff without a declaration (measured before
adding one). Nothing published changes behaviour: every file is a test and every
edit outside the new file is a comment. So the empty-frontmatter form, which the
gate's own output calls "the explicit exemption and a complete answer". It now
prints: 4 source files of 2 released packages changed, 1 changeset declared.

Verification

Run at 63f17c236, from the repo root.

  • pnpm exec vitest run over the four touched test files — 4 files, 44 passed.
  • turbo run type-check lint filtered to @object-ui/plugin-dashboard and
    @object-ui/plugin-charts — 18 tasks successful, 0 errors. Warning counts are
    the packages' pre-existing ones; the dashboard package's type-check is
    tsc --noEmit && tsc -p tsconfig.test.json, so the second leg is what sees
    the new file.
  • 15 gate scripts, all exit 0: the five changeset gates
    (presence, claims, fixed, no-major, overwrite), control-bytes,
    new-cross-file-line-citations, the three vi-mock gates, test-path-roots,
    unreferenced-sources, entry-guard, lint-coverage, type-check-coverage.
    Derived by hand from package.json and .github/workflows/ — this repo has no
    scripts/pm/dispatch-gates.mjs.
  • Blast radius beyond the changed packages: grepped the tree for every file
    naming these three pins; the only ones are the pins themselves and their
    siblings inside the two packages already run above. No census or prose figure
    counts files in packages/plugin-dashboard/src/__tests__/.
  • ⚠️ Repo-wide pnpm lint, pnpm test and the full shard matrix are CI's run,
    not measured here.

The ablation and the resolver probe were one-shot proofs; no script from either
is committed.

Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ


Generated by Claude Code

…rtConfig keys on the dashboard seam

objectui#4044's pin clause — every forwarded key gets a rendering assertion on
the dashboard surface that reads it — was half done on the DATASET face.
`DatasetWidget.chartConfig.dom.test.tsx` pins the four keys that paint outside
Recharts' `ResponsiveContainer`; the other five (`colors`, `categoryColors`,
`showDataLabels`, `annotations`, `interaction`) paint inside it and had no
dashboard-surface pin at all.

Their only apparent coverage was `plugin-charts`'
`ChartRenderer.dashboardChartConfig.test.tsx`, which hand-builds its own chart
schema and never travels this seam: when PR #9202 ablated the inline relays'
forwarding, 46 assertions reddened across the three dashboard files and 0 in
that one. An assertion that stays green while the thing it names is deleted is
not evidence.

No module mock is needed to close the gap. `ResponsiveContainer` seeds its size
from `getBoundingClientRect` on its own element, so a stub scoped to the
`recharts-responsive-container` element gives the plot a box — the technique PR
#9202 proved on the relay face, transferred here verbatim.

Also corrects the sibling file's header, which asserted those marks could only
be pinned inside `plugin-charts`. The premise it rests on holds (recharts
resolves inside plugin-charts alone); the conclusion does not. Its assertions
are untouched: they are the lit control for the ablation.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
…hart-block pin as dashboard coverage

objectui#9203's acceptance names one of these comments. Grepping for the
citation found two more saying the same thing about the same file, and the
ablation this card demanded measured all three false at once — so they are
corrected together rather than left to point the next reader back.

- `plugin-charts/src/ChartRenderer.dashboardChartConfig.test.tsx` claimed the
  mock "and therefore this half of the evidence" had to live there, and that it
  plus the dataset seam pin "close the loop from dashboard metadata to drawn
  pixels". The premise holds; both conclusions do not. Its objectui#4044 block
  already disclaimed the two inline relays — it now disclaims the dataset face
  on the same measured footing, and says why the reason is structural.
- `plugin-dashboard/.../DatasetWidget.chartConfig.test.tsx` cited that file as
  the DOM half for the keys drawn inside the plot. It now cites the new
  dashboard-seam pin, with the correction recorded beside it.

Comment-only: no assertion, import or fixture in any of the three files moves.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UzHd6hDYatoDn17BuwKxnZ
@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 51 chunks) 3115.6 KB 3134.8 KB
Main entry chunk (gzip) 144.3 KB 350 KB
Entry file index-D0ljyyDB.js
Status PASS

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

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.69KB 6.21KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 501.93KB 115.18KB
core (index.js) 8.52KB 3.41KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 211.58KB 58.68KB
fields (index.js) 247.91KB 62.51KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.21KB 2.26KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 32.15KB 10.49KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.84KB 10.95KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 4.39KB 1.66KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 49.04KB 13.93KB
plugin-charts (index.js) 71.52KB 19.98KB
plugin-chatbot (index.js) 195.35KB 46.52KB
plugin-dashboard (index.js) 131.24KB 34.61KB
plugin-designer (index.js) 215.82KB 44.33KB
plugin-detail (index.js) 253.42KB 65.87KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 136.79KB 34.19KB
plugin-gantt (index.js) 166.95KB 41.04KB
plugin-grid (index.js) 211.62KB 57.50KB
plugin-kanban (index.js) 46.00KB 14.30KB
plugin-list (index.js) 112.59KB 27.66KB
plugin-map (index.js) 20.43KB 6.81KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.42KB 11.93KB
plugin-timeline (index.js) 30.07KB 8.74KB
plugin-tree (index.js) 9.55KB 3.32KB
plugin-view (index.js) 84.42KB 20.80KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 94.03KB 31.02KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.66KB 2.50KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 14.82KB 4.99KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 4.73KB 2.28KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 14.27KB 5.47KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

  • ✅ Core packages should be < 50KB gzipped
  • ✅ Component packages should be < 100KB gzipped
  • ⚠️ Plugin packages should be < 150KB gzipped

Copy link
Copy Markdown
Collaborator Author

Contract review — PR objectui#9286 @ head 63f17c236 · ✅ CLEARED

domain:ui PM seat (os-tesla), R29. Review of record names this head.

check reading
CI at 63f17c236 36/36 settled — 0 failing, 0 pending
--pair 9286 ✓ PR #9286 / card #9203 — both carriers agree, and the diff carries no widening tell. ⚠️ A tell is not a proof and its absence is not one either
production code none — 1 changeset, 1 new pin, 3 test files touched
the claim the whole PR rests on verified independently: I filtered the diff of all three modified test files to non-comment added/removed lines. Nothing survives. The assertions are untouched, so the lit control really is lit
No-Touch zone ✓ not touched

⭐ That last row is the one that mattered, so I measured it rather than read it

The PR's argument is: the four existing keys' assertions in DatasetWidget.chartConfig.dom.test.tsx are the lit control, untouched on purpose. If a single assertion had moved in the same commit that added the new pin, the control would be worthless and the ablation numbers uninterpretable. Filtering the three-file diff down to non-comment lines returns empty. ⇒ the control stands.

⭐ The pin measures drawn marks, not a stub agreeing with itself

Ten assertions on real Recharts output — .recharts-surface awaited, sector fills, data labels, .recharts-reference-line, .recharts-reference-area, .recharts-brush, .recharts-tooltip-wrapper — with no vi.mock('recharts') anywhere in the file. ⭐ And every positive arm is paired with its own absence arm (showDataLabels off and undeclared; no annotation; no brush by default; tooltips false), so no row can pass by rendering nothing.

The technique is PR objectui#9202's, transferred verbatim rather than reinvented: a getBoundingClientRect stub scoped to the responsive-container element, because recharts resolves inside plugin-charts alone and a module mock is not available from this package. ⭐ That constraint is stated in the header as the reason for the technique, which is what lets the next seat reuse it correctly.

⭐ The voided probe was re-measured instead of reported as a negative

Triage's require.resolve reading was dead because the shared checkout has no node_modules at all after the restart — so it fails identically from every root, which is an instrument failure, not a negative answer. ⭐ You reproduced triage's dead reading first, then re-measured in the installed worktree with two controls that fire (recharts from plugin-charts, react from plugin-dashboard). That is exactly the standard: an unlit control means NOT MEASURED. The card's premise is confirmed; its conclusion stays false, and both halves are stated.

The three corrected headers — I agree with taking all three in place

The acceptance named one. Grepping the citation found two more asserting the same false thing about the same file, including one in plugin-charts' own header claiming it plus the dataset seam pin "close the loop from dashboard metadata to drawn pixels". ⭐ The ablation measured all three false in the same run (13 tests in that file, 0 red) — so this is not three judgement calls, it is one measurement with three citations attached.

Bounded in-place rule checked out: same defect class, comment-only, no open PR on either file (all 14 enumerated), inside the gate family this PR already runs. ⇒ Leaving two live pointers that send the next reader to a file which does not cover the face would have been the worse outcome.

The ablation

Pin written and committed first (87fabe590), so the unmodified arm is the real base tree. Mutation proved on disk before the run — anchor 1 → 0, marker 0 → 1, blob 1203c0e9…e02ab1c4…, with the script aborting on a non-unique anchor. Result across three files in one run: new pin 7 of 10 red, lit control 3 of 7 red, plugin-charts file 0 of 13 — ⭐ that last number reproduces the card's central evidence on this face, in the same run as the two that do move. The three that stay green in the new file are its pure-absence arms, each paired with a positive arm through the same harness. Restore proved by state, under a trap, with absolute paths and an explicit git checkout HEAD -- <abs> — ⛔ never a bare checkout in a shared checkout, and ⛔ never an exit code as the proof.

Changeset

An empty changeset (no package, no bump) is the right shape here: check-changeset-presence is satisfied, and nothing claims a release for a change where every touched file is a test. ⛔ Declaring a patch on @object-ui/plugin-dashboard would have been a false claim about published behaviour.

The second out-of-scope note is the more interesting one

nothing in this repo mechanically catches the defect class this card is an instance of — an assertion that stays green when the thing it names is deleted.

⇒ correctly not filed as a new card, and routed to objectui#4044, the ruling one layer up that already owns the class. Mutation testing is the general answer and it is far past this card.

Carriers

Cleared in one stroke on both limbs — PR objectui#9286 and card objectui#9203 — each read back.


Generated by Claude Code

@os-tesla
os-tesla marked this pull request as ready for review September 12, 2026 14:10
@os-tesla
os-tesla added this pull request to the merge queue Sep 12, 2026
Merged via the queue into main with commit ee5ef77 Sep 12, 2026
38 checks passed
@os-tesla
os-tesla deleted the claude/issue-9203-dataset-chartconfig-seam-pin branch September 12, 2026 14:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants