Skip to content

feat(types,plugin-dashboard)!: a dashboard widget's chartConfig type / xAxis / yAxis / series follow the spec retirement on the TS face and the dataset read path (objectui#11315) - #11371

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11315-widget-chartconfig-retired-keys
Oct 1, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/issue-11315-widget-chartconfig-retired-keys

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #11315
Clause-②: yes

@objectstack/spec 17.5.0 gave the dashboard widget its own chart config carrier, DashboardWidgetChartConfigSchema, which retires type, xAxis, yAxis and series (ADR-0021 · ADR-0049 D2; decision batch #121 item 1). objectui's Zod faces already refused the four by reference. The TypeScript twin still compiled them, and the dashboard's dataset widget still read three of them. This PR brings both into line. The react ObjectChart tier, where the spec keeps the four authorable, is unchanged, and it now has a control pin.

What changed

  • TypeScript twin (packages/types/src/complex.ts). DashboardWidgetSchema.chartConfig keeps its index signature. type, xAxis, yAxis and series are now typed by reference from the spec's own members, the same way PR objectui#11310 typed aria: each is NonNullable of SpecDashboardWidget['chartConfig'], indexed at the key. Each resolves to the spec's retired-key type, so an authored value is a compile error (TS2322). Only the key names are written here. Whether a key admits a value is the spec's answer, so a key the spec re-opened would compile again with no edit to this member.
  • Dashboard read path (packages/plugin-dashboard/src/DatasetWidget.tsx). The widget no longer calls mergeAuthoredPresentation. It emits the dataset-derived series and category axis as they are, and spreads no authored axes. A comparison overlay no longer copies its primary's merged mark and axis, because there is none. The combo entry in CHART_TYPE_MAP stays, so a combo widget still draws as a combo, and the renderer picks each series' mark. The chrome lowering (chartConfigPresentation) is unchanged. mergeAuthoredPresentation and its two series types are no longer re-exported from this module. The package index never exported them.
  • @object-ui/core (chart-presentation.ts): comments only. The file header now names which keys each carrier may author. It also says that mergeAuthoredPresentation has had no production caller since this change. The chartConfigPresentation docblock no longer calls aria "declared" or "pending objectstack#5175": since 17.5.0 it is a tombstone. Criterion 2 now notes that the spec refuses chartConfig.type on this carrier. The function's signature and behaviour are unchanged.
  • Pins.
    • packages/types/src/__tests__/chartconfig-structure-retired-11315.test.ts follows the shape of the aria pin. On the TypeScript side, each key is declared, is not any, and is the retired-key type (IsRetiredKeyType), with title as the live control. A @ts-expect-error sits on an authored literal for each key, chartConfig.series among them, and a legal { title, showLegend, colors } literal is the control. On the Zod side, the twin, DashboardComponentSchema, safeValidateSchema and StrictAnyComponentSchema each refuse every key at chartConfig.KEY with invalid_type and the spec's own message, read from the spec at run time. A spec-standing case checks both halves of the carrier split: the dashboard carrier refuses the four, and the base ChartConfigSchema still admits them.
    • DatasetWidget.comboPresentation.test.tsx is re-pinned as the dashboard refusal. A combo widget still resolves combo. With the retired combo shape authored, the series are byte-identical to the derived ones, no axis is spread, the normalized yAxes equal the no-chartConfig control, a bar widget stays a bar, the chrome still lowers, and the overlays carry no mark.
    • DatasetWidget.chartConfig.test.tsx: the REFUSED block now pins all three structure keys as unread. Its header no longer cites ChartConfigSchema or "pending objectstack#5175". The incidental type: 'bar' (a retired key) is dropped from the fixtures of the LOWERED block.
    • packages/plugin-charts/src/ObjectChart.inlineComboSeries-11315.test.tsx is new. It renders a real ObjectChart with inline rows and counts the recharts marks and axes. An authored series draws 1 bar, 1 line and 2 y-axes. An explicit specType: 'combo' authoring an area plus a line draws exactly those. The control with no per-series mark draws 2 bars.
  • Changesets.
    • @object-ui/types minor (TypeScript break, migration example included).
    • @object-ui/plugin-dashboard minor (rendering break: stored widgets that still carry the keys render the derivation).
    • An empty-frontmatter declaration for @object-ui/core (comments) and @object-ui/plugin-charts (test only).
    • Two pending changesets this change made false are corrected here. In 4044-chartconfig-aria-tombstone.md, "every other chartConfig key keeps its previous typing" now names the four keys. 10132-dashboard-i18n-declared-not-resolved.md gains a note that its axis-title point no longer reaches a dashboard, and its series-label aside no longer names DatasetWidget as the overriding caller. Both edits are prose corrections to changesets this PR does not own. check-changeset-overwrite lists them, report-only.

Zone 2 measurements (dispatch order)

  1. Reproduced on origin/main 5638529e67.
    • TypeScript: a probe of four DashboardWidgetSchema literals (chartConfig.series / xAxis / yAxis / type) plus a live control compiled with tsc exit 0. The positive control (chartConfig.aria) in the same probe failed with TS2322, so the probe could fail. After this change, the same probe exits 2 with four TS2322s, and the control line still compiles.
    • Reader: mergeAuthoredPresentation(derived, { series, xAxis, yAxis }) returned chartType: 'line', yAxis: 'right', axes.xAxis.title and two axes.yAxis entries.
    • Zod: the twin, DashboardComponentSchema, safeValidateSchema and StrictAnyComponentSchema refuse all four at chartConfig.KEY with invalid_type, and a { title } control parses on all four faces.
  2. Another declared home? None. The installed 17.5.0 dashboard widget declares id, title, description, type, chartConfig (appearance only), colorVariant, requiresObject, requiresService, filter, compareTo, dataset, dimensions, values, layout, options (dateGranularity, sortBy, sortOrder, limit and stageOrder declared, $loose), filterBindings and suppressWarnings. Retired as tombstones: actionUrl, actionType, actionIcon, responsive and aria on the widget, and aria, type, xAxis, yAxis and series inside chartConfig. Nothing declares a per-series mark or a second value axis. The series tombstone text says per-series appearance "is not authorable on a dataset-bound widget", and the yAxis one says "A second axis is a second measure, not a second axis declaration". The spec's liveness ledger (objectstack packages/spec/liveness/dashboard.json, the series row) records it as a ruled cost: "The per-series mark type that made a dataset-bound COMBO chart authorable goes with the key". The widget type enum still carries combo, which this PR keeps rendering. So the dashboard combo presentation retires on this carrier, and nothing was escalated.
  3. Callers of mergeAuthoredPresentation. It had one production caller, DatasetWidget, plus that module's own re-export, which no importer used. Two tests also call it directly: chart-presentation.axisTitleI18n-10132.test.ts in core and ChartRenderer.axisTitleLocale-10132.test.tsx in plugin-charts. The react ObjectChart tier never called it. ObjectChart hands schema.series / xAxis / yAxis to ChartRenderer, and normalizeChartSchema reads them there. So the carriers needed no switch, because the caller is the carrier. The dashboard call was removed, the function keeps its signature, and no second copy of the merge logic exists. plugin-report calls mergeAuthoredSeries (not this function), and ReportChartSchema.series is still live, so that path is unchanged.
  4. The TypeScript face. ⚠️ The dispatch premise that WIDGET_CHART_STRUCTURE_RETIRED is importable does not hold. In the installed spec it is a module-private message builder, (key, carried, instead) => string. It is absent from the @objectstack/spec/ui export list and from every .d.ts, so nothing can be read from it. The four keys are therefore typed exactly as PR objectui#11310 typed aria: by reference from SpecDashboardWidget['chartConfig'], the exported spec type whose members those tombstones build. No local list of retired keys, and no local never. Proof: the @ts-expect-error pins (they go stale if a key compiles again, see ablation 2) and the legal-literal control.
  5. Combo pin: both routes. The dashboard file is re-pinned as the refusal, and a new ObjectChart control in plugin-charts pins that an authored series still draws a combo.

Verification (union at head 25e43c3259, after the last commit)

  • Build: pnpm --workspace-concurrency=2 --filter '@object-ui/plugin-dashboard^...' --filter '@object-ui/plugin-charts^...' build → 12 packages built (the ^... direction is upstream dependencies). @object-ui/types dist/complex.d.ts carries the new members.
  • Type-check: @object-ui/types, @object-ui/core, @object-ui/plugin-charts and @object-ui/plugin-dashboard all print type-check: Done. Each runs tsc --noEmit plus tsc -p tsconfig.test.json, and --listFilesOnly confirms the new and edited tests are in those programs. Downstream consumers of the narrowed face (DashboardWidgetSchema / DashboardComponentSchema importers): @object-ui/sdui-parser, @object-ui/plugin-list and @object-ui/plugin-designer print type-check: Done against the rebuilt dist. @object-ui/app-shell and examples/schema-catalog are left to CI. A grep finds no dashboard chartConfig key use in either. The control: the same grep does hit app-shell's list-view chartConfig, which is a different carrier.
  • Tests: pnpm exec vitest run packages/types/ packages/core/src/utils/ packages/plugin-dashboard/ packages/plugin-charts/ → Test Files 653 passed (653), Tests 12268 passed | 15 skipped (12283) (lock verdict command-exit 0).
  • Reverse verification through a consumer: a temporary packages/plugin-dashboard/src file typing a DashboardWidgetSchema from @object-ui/types (the rebuilt dist) with chartConfig: { title, series } → tsc exit 2, TS2322 … is not assignable to type 'undefined' at series only. The file was removed under a trap, and git status is clean afterwards.
  • Gates: check:new-line-citations (0 new citation(s)), check:control-bytes OK, node scripts/check-changeset-presence.mjs (8 source file(s) of 4 released package(s) changed, and this change declares 3 changeset(s)), changeset:check OK, check:changeset-claims (the 10 pending bodies naming touched files were each read; none concerns chartConfig), check:pending-changeset-literals OK, check:test-path-roots OK, check:vi-mock-specifiers / -inherit / -override-shape OK, type-check:coverage (43/43), check:spec-symbols OK, check:installed-pin-claims OK, check:unreferenced-sources OK, check:component-surface-parity (report-only, no finding on chartConfig). apps/console/src/__tests__/registry-inputs-spec-parity.test.ts was not run, because no registration input text changed.
  • Lint, narrowed: the 8 changed TypeScript files were linted with the root config (eslint --no-inline-config --format json): 8 files, 0 errors. Warnings per modified file did not rise against the base blobs (complex.ts 9 → 9, DatasetWidget.tsx 12 → 11, the combo test 13 → 5, the chartConfig test 2 → 2). The new plugin-charts test has 2 no-explicit-any warnings, from the recharts mock its siblings use. The config enables no type-aware linting, so this diff cannot move another file's verdict. The repo-wide pnpm lint belongs to CI.

Ablations

Each ablation ran from committed state through ablation-replace.mjs. The anchor hit was proven, and each restore was proven by the blob equalling HEAD plus an empty git diff HEAD. Every subject is imported by relative source path, so no dist was involved.

  1. The merge put back into DatasetWidget (import plus series: mergeAuthoredPresentation(…).series and ...mergeAuthoredPresentation(…).axes). Predicted 7 red, and 7 went red: 5 in the combo file and 2 in the REFUSED block. The other 14 stayed green: the combo family, the no-chartConfig control, the forwards, and type / aria.
  2. series?: deleted from the TypeScript member → tsc -p tsconfig.test.json exit 2. The output had TS2322 on seriesTombstoned and TS2578 (unused @ts-expect-error) on the authored series literal.
  3. normalizeChartSchema stops reading series[].type (str(raw.chartType) ?? str(raw.type) → str(raw.chartType)).
    • On the first run, only the bar-plus-line case went red. The explicit-combo case stayed green, because a combo's positional default (a bar, then lines) draws the same counts.
    • That case now authors an area and a line. The re-run turned both authored cases red, and the no-mark control stayed green.

Acceptance notes

  • mergeAuthoredPresentation and axisPresentation have no production caller now. They are published exports of @object-ui/core. They were kept on purpose: the claim's Clause-② treats the function as staying exported, and deleting it would also rewrite ChartRenderer.axisTitleLocale-10132.test.tsx in plugin-charts, which is outside this claim's file surface. The comments in this file say so. Deleting both, and re-homing that render pin onto a chart node that authors its own axes, is the follow-up. Carrier: none.
  • Comments elsewhere that this change makes stale, left alone because they are outside the surface. Carrier: none.
    • packages/types/src/zod/data-display.zod.ts: "DatasetWidget → mergeAuthoredPresentation".
    • packages/plugin-report/src/DatasetReportRenderer.tsx: two places say the dashboard makes the same series merge.
    • chart-presentation.axisTitleI18n-10132.test.ts: "mergeAuthoredPresentation is what DatasetWidget calls".
    • ChartRenderer.axisTitleLocale-10132.test.tsx: "the path a dataset-bound surface actually takes".
    • content/docs/plugins/plugin-charts.mdx: "the ChartAxis a dashboard widget's chartConfig uses".
  • Comments stale since 17.5.0 that this change does not touch. Carrier: none.
    • DashboardRenderer.tsx (inline relay): "declared as the spec's full ChartConfigSchema", and objectstack#17385 described as still open.
    • DashboardChart.chartConfig-4044.test.tsx: "objectstack#17385 owns the precedence … Until it answers".
    • DatasetWidget.chartConfig.dom.test.tsx: "pending objectstack#5175".
    • The pending changeset dashboard-inline-chartconfig-4044.md, which says the same about ChartConfigSchema and objectstack#17385.
    • Per the seat's ruling, these negative pins are corrected only where this PR already edits the file.
  • Observation on a combo with compareTo. Every series now takes the combo's positional mark, overlays included, so an overlay can draw in a different mark from its own measure. Before this change, the same happened to any combo that authored no series. This is the ruled cost of the retirement, not a new defect class. No public-entry reading was taken. Carrier: none.
  • Same-day churn: origin/main moved 3 commits since the branch point (objectui#11361, objectui#11359, objectui#11346). None touches this diff's files, and git merge-tree is clean, so no merge was needed.

Dev session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ (dispatched by the domain:ui seat 1 PM loop).


Generated by Claude Code

claude added 4 commits October 1, 2026 08:53
…ype / xAxis / yAxis / series follow the spec retirement (objectui#11315)

@objectstack/spec 17.5.0 retired the four structure keys on the dashboard
widget's own chart config carrier (DashboardWidgetChartConfigSchema). The zod
faces already refused them by reference; this brings the TypeScript twin and
the dashboard read path into line.

- types: DashboardWidgetSchema.chartConfig types type / xAxis / yAxis / series
  by reference from the spec's members, as aria already was, so an authored
  value fails tsc. Pinned by chartconfig-structure-retired-11315.test.ts.
- plugin-dashboard: DatasetWidget no longer merges authored series / axis
  presentation onto the dataset derivation; comparison overlays carry no
  inherited mark. The combo pin is re-pinned as the refusal.
- core: chart-presentation comments corrected; mergeAuthoredPresentation keeps
  its signature and has no production caller.
- plugin-charts: a control pin that the react ObjectChart tier still draws an
  authored series as a combo.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…rd minor, core and plugin-charts no release)

The pending objectui#4044 changeset said every other chartConfig key keeps its
previous typing; this change types four more, so that sentence is corrected
in the same change.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…i#11315 changes about its axis-title point

The dashboard no longer reads chartConfig.xAxis / yAxis, so the axis-title
fix that changeset describes no longer reaches a dashboard; its series-label
aside named DatasetWidget as the overriding caller, which it no longer is.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
…itional default would not draw (objectui#11315)

An ablation that stopped the renderer reading series[].type left the
explicit-combo case green: a combo's positional default (a bar, then lines)
drew the same counts as the authored bar + line. The case now authors an area
and a line, so only a renderer that reads series[].type draws them.

Claude-Session: https://claude.ai/code/session_0122Knsowci76D2rBWReCzzZ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 10 pending changeset(s) describe a file this change touches

Their bodies publish verbatim into the CHANGELOG at the next release, so this is a request to re-read them against your diff — addressed here because you are the one seat that can answer it without re-deriving anything.

⛔ Nothing here blocks, and nothing here is a verdict on your change. This gate exits 0, is not a required context, and judges name resolution, never meaning: it asked whether a pending body names a file you touched. "Is this sentence still true?" is the one question it will not answer, and the one you are being asked to answer.

.changeset/6687-chatbot-surface-authorable.md

  • names packages/types/src/complex.ts → packages/types/src/complex.ts — edited by this change

    Measured on both declaration faces before the fix, each with a control that had to hit: schema.surface appeared 0 times in renderer.tsx against schema.placeholder at 3 (one per registration) and schema.processVisibility at 1; and ChatbotSchema (packages/types/src/complex.ts) declared 34 keys, not this one. Two faces agreeing is what made the zero a reading rather than a bad query.

.changeset/6939-kanban-column-cards.md

  • names complex.ts → packages/types/src/complex.ts — edited by this change

    Breaking, deliberately. KanbanColumn declared its card list as items in complex.ts and in the zod mirror complex.zod.ts. Every board reads cards. Measured on origin/main 78a3cc238: KanbanImpl.tsx reads .cards on 12 lines, KanbanEnhanced.tsx on 8, and bucketCardsIntoColumns twice more as col.cards || []; .items had zero read sites in either board (a same-shaped .title control on the same two files returns 8 and 3, so those zeros are readings and not a mis-shaped probe). Both catalog entries, the plugin docs and content/docs/api/schema-reference.md all author cards.

.changeset/7125-dashboard-empty-state-keys-retired.md

  • names DatasetWidget.tsx → packages/plugin-dashboard/src/DatasetWidget.tsx — edited by this change

    Not touched: table.noRows ('No rows to display') and engine.form.noRows (packages/app-shell/src/views/metadata-admin/i18n.ts, read at widgets.tsx) — two different, same-named keys in different namespaces. Nor the comments in WidgetEmptyState.tsx, DatasetWidget.tsx, ObjectDataTable.tsx and PivotTable.tsx that record WHY three widgets with three strings became one shared empty state; the packs' own comment keeps that rationale and now names the retirement instead of a row that is gone.

.changeset/7295-chat-message-avatar-keys.md

  • names packages/types/src/complex.ts → packages/types/src/complex.ts — edited by this change

    packages/plugin-chatbot/src/index.tsx:173–178 reads message.avatar || userAvatarUrl and message.avatarFallback || userAvatarFallback (and the assistant twins), the authoring-to-runtime seam spreads every unlisted key through (chatMessageAdapter.ts, ...passthrough), and the SDUI renderer feeds the authored messages[] straight in — a per-message avatar override renders, is documented, and no authoring-facing type declared it. ChatMessage in packages/types/src/complex.ts has no index signature (objectui#5155, deliberately — none is added here), so an author annotating ChatbotSchema.messages was told a value that renders is an error (TS2353); the zod mirror ChatMessageSchema is a plain strip-mode z.object, so the value parsed green and was silently DROPPED from the parsed output.

.changeset/7655-chatbot-registration-authoring-faces.md

  • names complex.ts → packages/types/src/complex.ts — edited by this change

    New published symbol: ChatbotSharedKey, the string-literal union of the twenty keys all three registrations read. It is exported from complex.ts because an exported interface may not extend a Pick over a private name (TS4022), so it is emitted into dist/complex.d.ts and is reachable through the published @object-ui/types/complex subpath (it is not re-exported from the package entry). It is a census, not an authoring face.

.changeset/7703-chatbot-dark-keys-retired.md

  • names packages/types/src/complex.ts → packages/types/src/complex.ts — edited by this change

    Each member goes to ?: never on packages/types/src/complex.ts and to retirementTombstone(...) on packages/types/src/zod/complex.zod.ts — both halves, in lockstep, the convention MarkdownSchema.sanitize (objectui#6972), TimelineSchema.timeScale (objectui#6355) and ObjectViewSchema.viewTabBar (objectui#7779) already carry. Each refusal names the key, says why it is retired, and points at what to write instead; one string feeds both the parse-time message and the .describe() metadata, so the two cannot drift.

.changeset/8415-filter-builder-condition-id.md

  • names complex.ts → packages/types/src/complex.ts — edited by this change

    Breaking for authored metadata: a filter-builder CONDITION must now declare id (objectui#8415). It is declared on both published faces — the TypeScript interface FilterBuilderCondition in complex.ts and the Zod mirror FilterBuilderConditionSchema in zod/complex.zod.ts — so a key the renderer has always required is finally validated instead of silently discarded.

.changeset/8802-8257-8008-kanban-gantt-family-retirement.md

  • names complex.ts → packages/types/src/complex.ts — edited by this change

    What each retirement was, measured. Three of the four were registration-only: no schema face in @object-ui/types ever declared kanban-ui, kanban-enhanced or gantt as a component node type, so unregistering is the whole retirement. The bare kanban key was the exception — it had a declared arm on both faces (KanbanSchema in complex.ts and its Zod mirror), and a plain deletion there would have been the objectui#7664 failure: BaseSchema is .passthrough(), so a document naming a dropped key validates green and renders nothing. It therefore retires as a named refusal: the Zod union keeps an arm claiming the literal and answers a { "type": "kanban" } document with a message naming object-kanban as the remedy, while the TypeScript half is the absence of the arm from ComplexSchema and of the key from SchemaRegistry, so tsc refuses it at the authoring site.

.changeset/9628-kanban-column-collapsed-honoured.md

  • names complex.ts → packages/types/src/complex.ts — edited by this change

    The key was declared on both published faces of the object-kanban arm — the lane element of ObjectKanbanSchema (objectql.ts and its Zod mirror) and the runtime lane KanbanColumn (complex.ts and its mirror) — and read by KanbanEnhanced alone, a module no production source imports. An authored { "id": "todo", "title": "To Do", "collapsed": true } therefore parsed green on both faces and reached a board that did nothing with it: KanbanImpl's only collapse is the SWIMLANE row's, held in viewer state under objectui:kanban-collapsed:ANGLE-BRACKETS(swimlaneField) and never keyed to a lane's declared value. That is the ADR-0049 declared-but-unhonoured shape.

.changeset/calendar-readme-schema-keys-5045.md

  • names packages/types/src/complex.ts → packages/types/src/complex.ts — edited by this change

    README.md's "Schema API / CalendarView" block described a CalendarViewSchema that does not exist. Measured against the interface itself (packages/types/src/complex.ts) and its zod mirror: events — the schema's only required key besides type — was published as events?, so a reader following the README omits it and TypeScript rejects the node; defaultDate was string where the schema says string | Date; and onDateClick was listed as a schema key when it is a CalendarViewProps component prop, sending readers to a different package's surface for a key calendar-view does not have (the schema's key is onDateChange). The block also listed 6 of the schema's 13 keys with nothing saying it was a summary (objectui#5045).

Read the paragraph, not the line: both false halves of the objectui#8617 claim sat in one paragraph, and correcting either alone would have left it asserting the same wrong thing.

If a claim did go false, correct the body. That is precedented and prose-only, frontmatter untouched; check-changeset-overwrite.mjs will report the correction as its own case 2 ("correcting a declaration on purpose … legitimate"), which is the intended shape — one gate asks for the read, the other records the write.

Not covered, stated so nobody reads this as more: a born-false claim that spells no line address at all (objectui#9495 coordinated one by ORDINAL — "a grep finds that member first" — and deciding that means reading what the sentence means), a claim spelled as a symbol or a package rather than a backticked file name, and a file named ambiguously.

Angle-bracketed names in the quoted prose above are rewritten as ANGLE-BRACKETS(name): GitHub deletes tag-shaped fragments from a stored body, and a quote that silently loses the identifier it is about is worse than a visible repair.

Compared the checked-out tree with d0fba91aa (merge-base with origin/main): 8 file(s) changed outside .changeset/, read against 1914 pending declaration(s) that publish a body (2530 pending in total). · run

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 330 chunks) 3552.5 KB 3574.6 KB
Main entry chunk (gzip) 150.4 KB 350 KB
Entry file index-B4v4UIBB.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.88KB 6.25KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.11KB 3.87KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.22KB 10.61KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.17KB 5.40KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.72KB 2.24KB
auth (SocialSignInButtons.js) 9.70KB 3.93KB
auth (UserMenu.js) 3.39KB 1.21KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.70KB 10.94KB
auth (createAuthenticatedFetch.js) 8.54KB 3.46KB
auth (index.js) 3.63KB 1.64KB
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) 27.13KB 7.95KB
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) 571.28KB 136.83KB
core (index.js) 10.00KB 3.96KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 229.96KB 63.80KB
fields (index.js) 261.05KB 66.23KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.35KB 12.88KB
i18n (translateFn.js) 0.20KB 0.18KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.49KB 9.23KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.93KB 11.29KB
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) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.86KB 5.00KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.52KB 2.26KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
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) 16.01KB 3.93KB
plugin-calendar (index.js) 53.15KB 15.45KB
plugin-charts (index.js) 84.07KB 22.93KB
plugin-chatbot (index.js) 198.22KB 46.97KB
plugin-dashboard (index.js) 139.07KB 37.18KB
plugin-designer (index.js) 216.54KB 44.64KB
plugin-detail (index.js) 245.45KB 64.47KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 173.25KB 44.58KB
plugin-gantt (index.js) 173.11KB 43.10KB
plugin-grid (index.js) 231.53KB 63.59KB
plugin-kanban (index.js) 49.49KB 15.54KB
plugin-list (index.js) 116.63KB 28.97KB
plugin-map (index.js) 23.54KB 7.83KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 44.04KB 12.21KB
plugin-timeline (index.js) 33.91KB 10.00KB
plugin-tree (index.js) 11.20KB 3.89KB
plugin-view (index.js) 90.32KB 22.76KB
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.81KB 3.58KB
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) 119.55KB 39.23KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.50KB 2.06KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.50KB 1.99KB
sdui-parser (codegen.js) 9.45KB 3.76KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 6.06KB 2.68KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.84KB 1.90KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 21.42KB 7.05KB
types (ai.js) 4.39KB 2.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 4.12KB 1.61KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 3.19KB 1.62KB
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.26KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 2.52KB 1.31KB
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) 4.99KB 1.96KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 21.59KB 7.71KB
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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 25e43c325928909f4fec0865c7413ea4d1f8b1ca
Local-runs: none

Inputs: card objectui#11315 (body; triage 5923810850, claim 5927792948, dev report 5928600236), PR objectui#11371 (body, 13-file list, 2 bot comments), the net diff against merge-base 5638529e67 (4 commits, 13 files, +747/−347), and the 42 check-runs on the head. Precedent: PR objectui#11310's body and the aria member as it stands on main. The spec premise was read off the spec SOURCE at the 17.5.0 release commit 0f6dcac5e9 (named by the card), because this checkout has no node_modules; that is a read of git objects, nothing was installed, built or run.

① Derived judgments

(a) TS face, and the falsified premise — RIGHT. WIDGET_CHART_STRUCTURE_RETIRED is a plain const, a function of (key, carried, instead) returning the tombstone string, at packages/spec/src/ui/dashboard.zod.ts:691 of 0f6dcac5e9, with no export anywhere in the spec source; ui/index.ts is export * from './dashboard.zod', which carries only exported names. A non-exported const cannot appear in any generated .d.ts, so the dev's "absent from every .d.ts" follows and the dispatch premise ("read from WIDGET_CHART_STRUCTURE_RETIRED") was unfulfillable as written. The route taken is the aria route PR objectui#11310 landed, member for member: type? / xAxis? / yAxis? / series? are each NonNullable of SpecDashboardWidget['chartConfig'] indexed at the key. The spec's DashboardWidgetChartConfigSchema (line 755) builds those four with retiredKey(...), which is z.never().optional() (shared/retired-key.ts:113), so each member resolves to the spec's own retired-key type. Triage's "no local list" is kept in the sense that matters: no local never, no copy of the tombstone type or message, and a key the spec re-opened compiles again with no edit here. What IS local is the four key names, exactly as aria's name is local; with the index signature retained (PR objectui#11310's fence), a whole-carrier reference was not available, because an index-signature any intersected with the spec object would widen the tombstones back to any. Residual, not a defect: a fifth key the spec retires later stays any here until named, the same residual aria left.

(b) Read path — RIGHT. At the base, mergeAuthoredPresentation had one production caller, DatasetWidget.tsx:1736, plus that module's own re-export (git grep at 5638529e67: nothing else outside tests and CHANGELOGs). plugin-report calls mergeAuthoredSeries and says in-line why not this function. The react ObjectChart tier never called it: in plugin-charts the only reference at base and head is the test ChartRenderer.axisTitleLocale-10132.test.tsx; normalizeChartSchema.ts:403 reads series[].type off the chart schema itself. So "stops reading them on the dashboard path" is realised by removing the dashboard call, the only form a pure function with no carrier parameter admits; the function and its signature stay exported from @object-ui/core (index: export * from './utils/chart-presentation.js'), and the only +/− lines in chart-presentation.ts are comment lines (checked: zero non-comment changes). "The combo family still resolves" is true: CHART_TYPE_MAP.combo stays and the re-pinned test asserts chartType === 'combo' both on the emitted schema and through normalizeChartSchema. What changes for a dashboard combo widget with compareTo, precisely: comboBaseFamily('combo') returns undefined (normalizeChartSchema.ts:726), so the combo arm's positional default rules every series (AdvancedChartImpl.tsx:2661: index 0 draws a bar, every later index a line; :2670: an authored combo with no series[].yAxis binds a bar left and a line right). With measures [m1, m2] and compareTo, the series are [m1, m2, m1__compare, m2__compare]: m1 bar/left, the other three line/right, so m1's overlay draws as a line on the right axis against a bar/left primary, and the second axis no longer carries an authored title, min or max. Before this PR a stored widget authoring series[] got overlays that copied their primary's merged mark and axis (#4229); a widget authoring none already drew exactly this. The dev's account of this is accurate and is declared in the plugin-dashboard changeset.

(c) Pins — RIGHT, and the ablations are sound. Types: Tombstoned is gated through IsAny before IsRetiredKeyType (the shared helper from objectui#11330, present at base, which admits both the pinned 17.5.0 undefined spelling and the branded mark on spec main), title is the live control through the same helper, and four @ts-expect-error literals sit beside a legal { title, showLegend, colors } control; the Zod leg reads the message off the spec at run time and pins both carrier halves. Ablation 2 (delete series?) predicts and gets TS2322 on seriesTombstoned plus TS2578 on the now-unused directive, which is the right pair. Dashboard: the combo file is seven cases, two of which are #4229's surviving half and the no-chartConfig control; restoring the merge (ablation 1) can redden exactly the five that compare against the control plus the two REFUSED-block cases in DatasetWidget.chartConfig.test.tsx whose assertions are 'xAxis' in schema === false and keys === ['dataKey','label']: 7, as reported, and the 14 that stay green are the ones a merge of nothing cannot move. ObjectChart control: renders a real ObjectChart with the recharts graph, counts marks and axes; the explicit-combo case was rewritten after ablation 3 to author area plus line, which the positional default (bar, then lines) cannot reproduce, so the 2-red/1-green re-run discriminates. It asks what triage asked: an ObjectChart with series still renders combo.

(d) Fixture triage — harmless. renderWidget(chartConfig, widgetType = 'bar') passes the family as its own parameter; chartConfigPresentation reads no type (grepped at head), the widget never read chartConfig.type (the REFUSED case "ignores chartConfig.type" is unchanged and still pins it), so dropping the incidental key from the LOWERED fixtures changes no path those cases exercise.

(e) Public surface, named. @object-ui/types: DashboardWidgetSchema.chartConfig narrows on four members, published through dist/complex.d.ts and the index: a TypeScript-only break, declared. @object-ui/plugin-dashboard: the module-local re-export of mergeAuthoredPresentation, AuthoredSeriesPresentation and MergedChartSeries from DatasetWidget.tsx is removed; package.json exports is . only, src/index.ts names no DatasetWidget, and no file outside that module imported the two types at base, so nothing published moves at the type level; the rendering of stored widgets carrying the keys changes, declared. @object-ui/core: export list and behaviour unchanged. @object-ui/plugin-charts: no source change.

(f) Check-runs on the head (read twice during this review). 31 completed success, 3 skipped (the matrix and dependabot placeholders), and 8 still in_progress, named here as NOT concluded and adopted as neither pass nor fail: Type Check, Test (shard 1/8), (2/8), (4/8), (5/8), (7/8), (8/8), Spec Main Shape Gate. Concluded and relevant: Lint, Test (shard 3/8), (6/8), Test (dist pins), Build & E2E, Changeset Declaration, Changeset Bump Policy, Changeset Fixed Group Check, Changeset Overwrite Report, Changeset Claim Re-read, Line Citation Gate, Governed Surface Queue Guard, Inert vi.mock Specifier Check, Build Docs, all success. The dev's declared-to-CI items (app-shell and examples/schema-catalog type-check) sit inside the unconcluded Type Check.

② Semver level

Clause-②: yes

  • @object-ui/types minor with "BREAKING (authoring, TypeScript only)" in the first line, the migration example included: matches the repo's version policy (AGENTS.md 版本号策略: objectui's own breaking changes are marked minor with the break spelled in the body; major is refused by check-changeset-no-major.mjs), and matches the grading PR objectui#11310 gave the same face. Every sentence checked: the Zod twin's by-reference refusal, the any-to-tombstone change, "every other key keeps its previous typing", and "no home on a dashboard widget" (the spec declares no per-series mark or second value axis; the tombstone texts quoted in the PR say so).
  • @object-ui/plugin-dashboard minor with "BREAKING (rendering)": true clause by clause: type was never read (the base REFUSED pin), a combo still draws as a combo under the renderer's own marks, a bar/line/area widget with one re-marked series now draws as its family (pinned), overlays copy nothing (pinned), chrome lowers as before (chartConfigPresentation unchanged). os migrate meta --from N is a real CLI command (packages/cli/src/utils/config.ts:446, the upgrade skill). Nit, not a falsity: the conversion is the spec's, the command is the CLI's.
  • @object-ui/core and @object-ui/plugin-charts on one empty-frontmatter changeset: the repo's documented shape (check-changeset-presence.mjs header: "An EMPTY frontmatter counts", section "The empty-frontmatter exemption"; fifteen-plus pending precedents). Its text is true: core changed comments only (verified), plugin-charts added one test and no source. Changeset Declaration reports 8 source files of 4 released packages against 3 declarations and passed.
  • The claim's Clause-② sentence placed the second break on core's exported mergeAuthoredPresentation. The dev is right that the export is byte-for-byte unchanged and the break lives in the dashboard caller; Clause-②: yes stands on the two published surfaces that do move, @object-ui/types (face) and @object-ui/plugin-dashboard (rendering), and the changesets declare both. No skip-changeset.
  • Two pending changesets corrected because this diff made them false, both prose-only with frontmatter untouched, both listed by Changeset Overwrite Report as the intended case: 4044-chartconfig-aria-tombstone.md ("every other key keeps its previous typing" now names the four, true since both ship in one release; minimal, one sentence) and 10132-dashboard-i18n-declared-not-resolved.md (the series-label override example moves from DatasetWidget, which no longer calls seriesPresentation, to the report renderer, where measureLabel outranks mergeAuthoredSeries' pick at DatasetReportRenderer.tsx:1176; and a paragraph saying point 2 no longer reaches a dashboard while axisPresentation still forwards a locale-map title through forwardedI18nLabel, both verified). The header sentence in chartconfig-aria-retired-4044.test.ts is the same correction, minimal. No other pending changeset goes false: the 21 pending bodies naming DatasetWidget, combo, series[], #4229 or yAxis[] were grepped at the head; 11261, 7694, 10654 speak of chart nodes or of one mark per measure, which this diff keeps.

③ Boundary flags

Dev deviations, each answered:

  1. Premise falsified (WIDGET_CHART_STRUCTURE_RETIRED private): confirmed at the spec source, ①(a). Accepted; the route is the precedent's.
  2. One caller, function kept, no second copy: confirmed by git grep at base and head, ①(b). Accepted; keeping the export is what the claim's Clause-② assumed.
  3. Edits beyond the file surface (one test header, two pending changesets): each true and minimal, ②. Accepted.
  4. Module-local re-export removed: not a published surface, ①(e). Accepted.
  5. Fixture triage: harmless, ①(d). Accepted.
  6. Control strengthened after ablation 3: sound, ①(c). Accepted.
  7. Downstream type-check narrowed, app-shell and examples/schema-catalog declared to CI: that CI run (Type Check) is not concluded at review time, ①(f). Escalated to the seat as a wait, not a defect.
  8. Core on an empty-frontmatter changeset; the break attributed to plugin-dashboard: right, ②.

open_questions: none declared; none found.

Out-of-scope findings, each routed:

  • mergeAuthoredPresentation and axisPresentation as dead exports: fileable, reach measured here: zero production callers at the head, three test files reach them (chart-presentation.axisTitleI18n-10132.test.ts, chart-presentation.i18nLabel-9038.test.ts, ChartRenderer.axisTitleLocale-10132.test.tsx), and the corrected 10132 changeset now names the two functions as point 2's surviving carrier, so a deletion card must re-home that pin first. No open card is named in the inputs; the seat decides whether to file.
  • Stale comments outside the surface (five sites) and stale-since-17.5.0 comments untouched (three sites plus one pending changeset): one sweep card for the eight comment sites. The pending changeset dashboard-inline-chartconfig-4044.md deserves an Acceptance note on its own: its line 13 still says the xAxis / yAxis / series refusal waits on "an open protocol question" at objectstack#17385, which 17.5.0 closed, and that body publishes verbatim into the CHANGELOG at the next release. Not this diff's falsification (false before it), so not blocking here; a one-sentence prose correction before the release is the remedy.
  • The compareTo overlay mark: fileable, renderer-side, with the reach measured in ①(b): every dashboard combo widget with compareTo now has its first measure's overlay drawn as a line on the right axis against a bar/left primary, where before only widgets authoring no series did. The spec's liveness ledger rules the authored mark gone; it does not rule that an overlay with variant: 'comparison' takes a positional slot of its own, so the candidate fix is plugin-charts' combo arm inheriting the overlaid series' resolved family and axis. Not blocking: the guard that held this at base was the authored path the spec retired.

Nothing in the inputs names an open card any of the three should join.

Implemented-by: claude/issue-11315-widget-chartconfig-retired-keys
Reviewed-by: session_0122Knsowci76D2rBWReCzzZ

VERDICT: PASS

The PASS is on the contract: every derived judgment holds, the semver level and the Clause-② line match what the diff publishes, and every flag is answered. Eight check-runs on the head are not concluded (Type Check, six Test shards, Spec Main Shape Gate); this record does not adopt them, and the merge waits on their conclusions.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 1, 2026 09:42
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 1, 2026 09:42
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 1, 2026
Merged via the queue into main with commit 1a88ce2 Oct 1, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-11315-widget-chartconfig-retired-keys branch October 1, 2026 10:03
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Oct 7, 2026
…scribing the dashboard chartConfig axis / series merge spec 17.5.0 retired (objectui#11373) (objectstack-ai#11447)

Fixes objectstack-ai#11373

Clause-②: no

## What changed

Comments, one docs sentence and one pending changeset. No code changed:
every changed line in the `.ts` / `.tsx` files is a comment line (116
comment lines, 0 other lines, counted from `git diff -U0` against the
merge base `31987bd50`).

Each sentence that said something false at `main` now says what is true:

- `@objectstack/spec` 17.5.0 refuses `type` / `xAxis` / `yAxis` /
`series` (and `aria`) on a dashboard widget's `chartConfig`
(`DashboardWidgetChartConfigSchema`). Measured on the installed spec:
`safeParse` refuses each of the five and accepts `title`.
- The react `ObjectChart` tier keeps `xAxis` / `yAxis` / `series`.
- `mergeAuthoredPresentation` and `axisPresentation` no longer exist.
objectui#11372 removed them (squash `f9c8c4e45`).
- objectstack#17385 has been in state `completed` since 2026-09-20, and
the spec PR that ended it is the retirement. objectstack#5175 has been
in state `not_planned` since 2026-08-10: it moved to objectui#4044,
which is also done.

A sentence that is true history ("was re-exported here until
objectui#11315") stays as written. Where a history sentence names the
removed function, it now also says the function is gone.

## The sites, sentence by sentence

Population: `git grep -n -E
'mergeAuthoredPresentation|axisPresentation|objectstack#17385|objectstack#5175'`
at `31987bd50` gives the seat's list exactly (21 hits, changelogs and
the `11372` / `10132` / `11315` changesets included). A broader grep for
`full ChartConfigSchema`, `chartConfig.series|xAxis|yAxis` and `authored
axis|axes|series` turned up nothing beyond those sites. Its other hits
are true or belong to another carrier: the list view's
`chartConfig.xAxisField` in app-shell, and report and ObjectChart pins.

| Site | Before | After |
|---|---|---|
| `plugin-dashboard/src/DatasetWidget.tsx`, `chartConfigPresentation`
re-export docblock | "…see that module's header." / "…so this widget no
longer calls it." | The admission criteria and the `aria` reason live in
`chartConfigPresentation`'s docblock, not the module header, so the
pointer names both. "…so this widget stopped calling it, and
objectui#11372 then removed it from `@object-ui/core`." |
| `DatasetWidget.tsx`, "The chart's STRUCTURE is the dataset's" |
"(objectstack-ai#4229, through `@object-ui/core`'s `mergeAuthoredPresentation`)" |
"(objectstack-ai#4229, through `mergeAuthoredPresentation`, which objectui#11372 has
since removed from `@object-ui/core`)" |
| `plugin-dashboard/src/DashboardRenderer.tsx`, inline relay | "declared
as the spec's full `ChartConfigSchema` on EVERY dashboard widget" and
"keeps objectstack#17385's open precedence question … out of this
change" | Declared on every widget, since 17.5.0 as
`DashboardWidgetChartConfigSchema` (appearance keys, the four structure
keys refused at parse). The whitelist emits no `xAxisKey` / `series`,
and there is no authored axis to weigh against the derived bindings:
17.5.0 refuses them, which answered the question objectstack#17385
carried. |
| `__tests__/DashboardChart.chartConfig-4044.test.tsx`, header "What was
measured" | "`chartConfig` is declared as the spec's full
`ChartConfigSchema`" | "was declared … ; since spec 17.5.0 it is
`DashboardWidgetChartConfigSchema`" |
| same header, "keys deliberately NOT here" | `xAxis` / `yAxis` /
`series`: "an open PROTOCOL question … objectstack#17385 … cannot
pre-empt it"; `aria`: "declared by the spec" | 17.5.0 refuses all three
at parse, which answered objectstack#17385: the derived binding wins.
`type` is also refused at parse. `aria` is no longer declared: a
`retiredKey` tombstone (objectstack#17751). |
| same file, refusal block (`emits no xAxis / yAxis / series from
chartConfig`) | "objectstack#17385 owns the precedence … Until it
answers, the three keys must not travel" | 17.5.0 refuses them and the
derived binding wins. A widget that reaches the relay still carrying
them (stored metadata never re-validated) must not have them travel. |
| `__tests__/DatasetWidget.chartConfig.dom.test.tsx`, the `aria`
negative pin | "the current contract, pending objectstack#5175" | The
protocol settled the same way: 17.5.0 retired `ChartConfig.aria`
(objectstack#17751). The pin holds the renderer side for a widget that
reaches it without that parse. |
| `__tests__/DatasetWidget.comboPresentation.test.tsx`, History |
"through `@object-ui/core`'s `mergeAuthoredPresentation`. This file
pinned that merge." | Same, plus "(since removed by objectui#11372)". |
| `plugin-report/src/DatasetReportRenderer.tsx`, import comment |
"`mergeAuthoredSeries` — not `mergeAuthoredPresentation` — because…" |
The chrome and the per-series presentation. No axis is lowered, and none
needs to be: a report's axes are bare name strings. |
| `DatasetReportRenderer.tsx`, `authoredSeriesLabel` docblock | "the
same ruling the dashboard's `mergeAuthoredPresentation` makes for its
own `chartConfig.series`" | "the same ruling `mergeAuthoredSeries`
(`@object-ui/core`) makes over this same `chart.series`". Read in core:
"The FIRST entry naming a measure wins". |
| `DatasetReportRenderer.tsx`, the series-merge call site |
"`mergeAuthoredSeries` is the same merge the dashboard makes" and
"Deliberately NOT `mergeAuthoredPresentation`: that entry point also
reads…" | `mergeAuthoredSeries` merges by `series[].name`. The dashboard
made the same merge until 17.5.0 (objectui#11315). There is no axis to
lower on this surface. The object-axis lowering was deliberately not
used here and objectui#11372 removed it. |
| `types/src/zod/data-display.zod.ts`, `ChartSchema` axis comment | "a
dashboard's dataset-bound chart hands it this node's `xAxis` / `yAxis`
straight from the widget's `chartConfig` (`DatasetWidget` →
`mergeAuthoredPresentation`)" | The react `ObjectChart` tier, where the
spec keeps both keys, hands `ChartRenderer` the author's own axes. A
dashboard widget no longer does, because 17.5.0 refuses them there
(objectui#11315). |
| `plugin-charts/src/ObjectChart.axisTitleLocale-10132.test.tsx` | "used
to build its schema through … `mergeAuthoredPresentation` … removed by
objectui#11372" | **Unchanged**: true history, written by objectui#11372
itself. |
| `content/docs/plugins/plugin-charts.mdx`, Axis options (**published**)
| "the `ChartAxis` a dashboard widget's `chartConfig` uses" |
"`ChartAxis` — the same object the react `ObjectChart` tier (an
`object-chart` node) takes for its own `xAxis` / `yAxis`". Measured:
`ObjectChartSchema.xAxis` / `yAxis` in `objectql.zod.ts` take the same
`stripImportedDefaults(SpecChartAxisSchema)` as `ChartSchema`. |
| `.changeset/dashboard-inline-chartconfig-4044.md` (**publishes
verbatim**) | "declared as the spec's full `ChartConfigSchema`"; "Five
of the fourteen declared keys"; `xAxis` / `yAxis` / `series` "an open
protocol question … objectstack#17385"; `aria` "needs a reader … a
separate decision" | "declared on every dashboard widget"; "Five of the
fourteen keys of the spec's `ChartConfigSchema` are not forwarded, and
17.5.0 refuses all five on a dashboard widget's chart config at parse";
the three are refused by `DashboardWidgetChartConfigSchema`, which
answered objectstack#17385; the spec retired `aria` instead of giving it
a reader (objectstack#17751). The front matter is untouched. |

### The pending changeset: one PM assumption did not hold

The triage direction says this changeset's correction is "the one PR
objectstack-ai#11371 already made". **PR objectui#11371 did not touch this file.** Its
file list (REST, 13 files) has no
`dashboard-inline-chartconfig-4044.md`. Its own body lists the file
under "Comments stale since 17.5.0 that this change does not touch". At
`31987bd50`, the only commit in reach that touches the file is the
shallow boundary.

The two false sentences (the open protocol question, and `aria` waiting
on a separate decision) would have published in the same release as the
`11315` changeset, which says the opposite. Following the ruling's
intent ("re-read, not edit twice"), this PR edits the file once, and it
is the only edit.

### Bounded edits beyond the listed sites

These are the same defect class, in files already on the claim's
surface, comments only:

- the `aria` bullet in `DashboardChart.chartConfig-4044.test.tsx`'s
header ("declared by the spec");
- the `aria` sentence in the pending changeset.

The spec's own CHANGELOG puts the `ChartConfig.aria` tombstone in the
17.5.0 section.

## Acceptance notes

Noted, not filed:

-
`plugin-report/src/__tests__/DatasetReportRenderer.chartChrome.test.tsx`
header calls `aria` "the one declared key with no reader anywhere on
this path". It is no longer declared, since 17.5.0 retired it on
`ReportChartSchema` too. This is the `aria` family, not this card's axis
/ series family, and the file is outside the claim. Carrier: none.
- `.changeset/11315-core-plugin-charts-no-release.md` says
`mergeAuthoredPresentation` "keeps its signature and its behaviour".
That was true of objectui#11371's own diff and became false with
objectui#11372. The front matter is empty, so it publishes nothing. The
dispatch excluded it. Carrier: none.
- The `aria` refusal test's own comment in
`DashboardChart.chartConfig-4044.test.tsx` ("Delivering `aria` therefore
needs a READER … Reported rather than guessed") was left as written. It
records the measurement history of that card. The header bullet above
now carries the retirement.
- Cross-file `path:line` citations elsewhere in `data-display.zod.ts`
(the `data` comment) were not swept. They are outside the rewritten
blocks, and the gate's ruling says not to sweep them. This diff adds
none (gate verdict below).

## Verification

All runs are on head `f1a7fa00f`, the only commit on the branch.

- Tests, from the worktree root, through the shared verify lock: `pnpm
exec vitest run --maxWorkers=2` on the three edited test files plus
every `scripts/markdown-test-inputs.mjs --list` reader of
`content/docs/**` and `.changeset/**` (17 files). Result: `Test Files 20
passed (20)` · `Tests 809 passed (809)` · lock `VERDICT command-exit 0`.
- `pnpm check:doc-fences`: ✅ exit 0.
- `pnpm check:doc-example-ids`: `✅ 415 real reference(s) all resolve`,
exit 0.
- `node scripts/check-doc-links.mjs`: `Links are valid across 17 scan
roots.`, exit 0.
- `pnpm check:control-bytes`: `✅ check-control-bytes: OK`, exit 0.
- `pnpm check:new-line-citations`: `VERDICT
new-cross-file-line-citations: 0 new citation(s)`, exit 0.
- `pnpm check:changeset-claims`: exit 0. It is report-only and names 9
pending changesets that mention `data-display.zod.ts` /
`DatasetWidget.tsx`. I read each one. None describes a comment this diff
changed.
- `node scripts/check-changeset-presence.mjs`: `✅ 7 source file(s) of 3
released package(s) changed … EMPTY frontmatter`, exit 0.
- `pnpm check:doc-types`, `pnpm check:doc-example-readers`, `pnpm
check:spec-symbols`, `node scripts/check-installed-spec-pin-claims.mjs`:
exit 0.
- `pnpm check:doc-examples`: **NOT MEASURED**. Reason: `THE GATE COULD
NOT RUN — the packages these examples import are not built`, which is a
prerequisite, not a verdict. The `.mdx` change is prose outside every
code fence, which is the only thing that gate type-checks. CI runs it
after its build.
- ESLint, narrowed to the 7 changed `.ts` / `.tsx` files:
- Population, from `eslint.config.js`: rules are scoped to
`**/*.{ts,tsx}` minus the `ignores` list. The `.md` / `.mdx` files are
in no `files` glob.
- Count, from `--format json`: 7 files, 0 errors, 32 warnings. Per file,
base vs head through `--stdin` is identical for all 7.
- Invariance: `eslint.config.js` has 0 `projectService` /
`parserOptions` / `TypeChecked` hits, so linting is not type-aware. No
rule in `eslint-rules/` reads the filesystem. So this diff cannot move
the verdict of an untouched file.
- With `--no-inline-config`, the run reports 1 error:
`react-hooks/static-components` in `DatasetReportRenderer.tsx`. It is
the site carrying an `eslint-disable-next-line` that exists at the base
too. The package `lint` script is `eslint .`, which honours the
directive.
- Type-check: not run. No `.ts` / `.tsx` line outside a comment changed.
- Governed surface: `check-governed-queue-guard.mjs --test` reports `✅
NOT GOVERNED`.

Dev session: `https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37`.
Dispatched by the `domain:ui` seat 1 PM loop; this PR stays draft for
the seat.

---
_Generated by [Claude
Code](https://claude.ai/code/session_01YLg8XqWGJ785fwQ5v4pH37)_

Co-authored-by: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants