Skip to content

feat(plugin-charts): the dataKey arm of ChartRendererProps.series declares type (objectui#8086) - #10554

Merged
objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-8086-chart-series-type-arm
Sep 25, 2026
Merged

objectstack-fleet[bot] merged 1 commit into
mainfrom
claude/issue-8086-chart-series-type-arm

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #8086

Clause-②: yes

A published TS union widens to what ChartDataSeriesSchema and the renderer already accept. This executes ruling 5809905710 (align) on objectui#8086, dispatched by the domain:ui seat 2 (claim comment 5828875764).

What changed

  • packages/plugin-charts/src/ChartRenderer.tsx: the dataKey arm of ChartRendererProps.schema.series gains type?: string, the same member type the name arm declares. No other member of either arm changes. The docblock that PR fix(plugin-charts): honour series[].type override on a dataKey-shaped series too #8080 added ("This TS union stays as declared …") is replaced by the aligned statement. The component-body comment said normalizeSeries round-trips "every key the internal arm declares" unchanged. That comment is updated for the new member, because type is translated to chartType and not passed through.
  • ChartRenderer.specSeries.test.tsx: the as any on the { dataKey, type } case is removed. The one other as any, on the categories case, stays, and a comment now gives the reason: it covers categories, which ChartRendererProps.schema does not declare. Leg B below measures this.
  • ChartRenderer.seriesTypeArm-8086.test.ts (new, in the same folder): compile-time pins against each arm.
  • packages/types/src/objectql.ts: a prose-only change. The ObjectChartSchema.series docblock said its element type is the internal arm "VERBATIM". After this change that arm carries type and the copy does not, so the sentence is rewritten. The type itself is unchanged. See Deviations.
  • .changeset/8086-chart-series-type-arm.md: '@object-ui/plugin-charts': minor.

Premise measurement: the old as any was not needed

series is a union with no literal discriminant. TypeScript's excess-property check on an object literal therefore only asks whether each key is known to at least one arm. On the base declaration, the specSeries literal with { dataKey: 'margin', type: 'line' } compiles without the cast, because type is known to the name arm. Leg A shows this: with the cast already removed, ChartRenderer.specSeries.test.tsx reports zero errors. For the same reason, a { name, chartType } literal compiles against the whole union on both base and head, because chartType is known to the dataKey arm.

The gap the card names is real, but it is in the arm, not the union. On base, a value typed as the dataKey arm cannot carry type (TS2353), and an entry narrowed with 'dataKey' in entry cannot read it (TS2339). So each new pin is written against a single arm, the Extract of the union on its binding key. Pins written against the whole union would pass on both sides of the change.

Evidence (HEAD ff726c28c)

Red on base (leg A). This leg removes only the new member from ChartRenderer.tsx, which restores the base declaration, and keeps every test at HEAD. The edit went through node ../objectstack/scripts/ablation-replace.mjs: the anchor went from 1 hit to 0, the blob changed from e0dac80c6e03 to ad7bb3165aac, and after restore the blob matched HEAD and git diff HEAD was empty. It then ran tsc -p tsconfig.test.json --listFiles in packages/plugin-charts. The file list includes ChartRenderer.tsx, ChartRenderer.seriesTypeArm-8086.test.ts and ChartRenderer.specSeries.test.tsx. Line and column numbers are removed from the output below:

ChartRenderer.seriesTypeArm-8086.test.ts  TS2339  Property 'type' does not exist on type '{ dataKey: string; ... }'          (the Equal pin)
ChartRenderer.seriesTypeArm-8086.test.ts  TS2353  'type' does not exist in type '{ dataKey: string; ... }'                      (value typed as the dataKey arm)
ChartRenderer.seriesTypeArm-8086.test.ts  TS2339  Property 'type' does not exist on type '{ dataKey: string; ... }'          (reading it back)
ChartRenderer.seriesTypeArm-8086.test.ts  TS2339  Property 'type' does not exist on type '{ dataKey: string; ... }'          (read after narrowing with 'dataKey' in e)
TSC_EXIT=2

Leg A also reports no error in ChartRenderer.specSeries.test.tsx, which is the premise measurement above. The control @ts-expect-error raised no TS2578, so a { name, chartType } value typed as the name arm is refused on base, as it is on head.

Green on head. pnpm run type-check in packages/plugin-charts (tsc --noEmit && tsc -p tsconfig.test.json) printed VERDICT command-exit 0.

Kept cast (leg B). With the as any removed from the categories case at HEAD, tsc reports ChartRenderer.specSeries.test.tsx TS2353: Object literal may only specify known properties, and 'categories' does not exist in type '{ type: string; ... }' and TSC_EXIT=2. After restore, the blob matched HEAD.

Tests (repo-root form, through the shared verify lock):

  • pnpm exec vitest run --maxWorkers=2 packages/plugin-charts/: Test Files 73 passed (73), Tests 831 passed (831).
  • pnpm exec vitest run --reporter=verbose on the two touched test files: 2 files, 9 tests passed, 3 of them in the new pin file.
  • pnpm run type-check in packages/types: exit 0.
  • pnpm exec vitest run --maxWorkers=2 packages/types/: Test Files 233 passed (233), Tests 5184 passed (5184).

Gates (verdict lines as printed):

  • node scripts/check-changeset-presence.mjs: ✅ 4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s).
  • check-changeset-no-major ✅, check-changeset-overwrite ✅, check-changeset-fixed ✅, check-changeset-claims: both sections PASS.
  • pnpm check:new-line-citations: VERDICT new-cross-file-line-citations: 0 new citation(s).
  • pnpm check:control-bytes: ✅ check-control-bytes: OK.
  • check:pending-changeset-literals, check:test-path-roots, check-vi-mock-specifiers / -inherit / -override-shape, check-type-check-coverage (43/43), check:published-tsconfig-exclude and check:unreferenced-sources: all OK.
  • node scripts/check-governed-queue-guard.mjs --test on the five paths: NOT GOVERNED.
  • Lint was scoped to the four touched source files, with three pieces of evidence that the scoping skips nothing. First, eslint --print-config finds the root eslint.config.js for each file (117 to 118 rules), so none is ignored. Second, --format json shows 4 files and 0 errors; the only warnings are the existing no-explicit-any ones, and the specSeries file has one fewer than on base. Third, eslint.config.js has no parserOptions.project or projectService, so type-aware linting is off, and no local rule under eslint-rules/ reads the filesystem. This diff therefore cannot change the lint result of any file it does not touch. The full pnpm lint runs in CI.
  • Doc and README gates: not reached. The diff touches no doc, README or doc fence, and the charts doc already lists type as a per-series key for either binding.

Deviations

  1. File surface. The claim listed ChartRenderer.tsx (only the dataKey arm and the docblock), the specSeries test with a pin next to it, and the changeset. Two edits fall outside that list. Both are prose that this change would otherwise leave false:

    • the component-body comment in ChartRenderer.tsx (same file, but not the docblock);
    • the ObjectChartSchema.series docblock in packages/types/src/objectql.ts (the "VERBATIM" claim).

    The dispatch order says to stop on a breach. The os-dev role file says published text that the change makes false must be fixed, and that the role file wins when the two conflict. So both edits are made and listed here. The claim's file surface needs the seat to add them. If the seat wants them out, removing those two hunks is enough, and the type change does not depend on them.

  2. Commit trailer. Commit ff726c28c ends with the harness-supplied Co-Authored-By trailer, which names the model, instead of the model-free pair. History is not rewritten, because force-push is banned here.

Acceptance notes

  • ChartRendererProps is not re-exported by name from the @object-ui/plugin-charts entry. Consumers reach it as the props type of the exported ChartRenderer. In the published 17.6.0 dist it is declared in dist/ChartRenderer.d.ts and used by the registry map type in dist/index.d.ts, and its dataKey arm has no type. The runtime half, objectui#7681, is still a pending changeset, so the declaration and the behaviour ship in the same release.
  • ObjectChartSchema.series in @object-ui/types (the TS interface and its zod mirror) is a copy of the internal arm and does not have type. Whether that published copy should follow is a separate Clause-② decision that this PR does not take. Carrier: none.
  • A comment in widget-schema-anchors-7946.test.ts says series' element type "is" the internal arm. Its assertion only pins dataKey: string, which still holds. The comment is test-only and not published, so it is left unchanged.
  • The series union has no literal discriminant, so object literals get union-wide excess-property checks, and a { name, chartType } literal type-checks against the union. This PR leaves that as it is, because the ruling allows no other arm change.

Generated by Claude Code

…lares type

Align the published TS union with what ChartDataSeriesSchema and the
renderer accept: the dataKey arm gains `type?: string`, the name arm's own
member type. No other arm changes. The docblock that said the union
"stays as declared" is restated, the body comment's round-trip claim is
corrected for the new member, the specSeries case drops its cast, and an
arm-level compile-time pin sits beside it. The ObjectChartSchema.series
docblock in @object-ui/types no longer claims to copy the arm verbatim.

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 19 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/5903-objectgantt-declared-keys.md

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

    Both halves move together. The TS declaration (packages/types/src/objectql.ts) and its zod mirror (src/zod/objectql.zod.ts) gain the same ten keys at the same requiredness — all optional — and no KnownDrift entry is added. navigation is taken from @objectstack/spec's NavigationConfigSchema by reference rather than restated, matching ObjectGridSchema.navigation.

.changeset/7322-object-kanban-group-by-limit.md

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

    Breaking for authored metadata: ObjectKanbanSchema.groupField is RETIRED (objectui#7322, ADR-0049 enforce-or-remove), and the two keys the object-kanban renderer actually reads — groupBy and limit — are now DECLARED and validated on both published faces: the TypeScript interface in objectql.ts and the Zod mirror in zod/objectql.zod.ts.

.changeset/7363-objectql-union-arms.md

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

    ObjectGallerySchema and ObjectDataTableSchema are members of ObjectQLComponentSchema on both faces — the TS union in objectql.ts and the zod union in zod/objectql.zod.ts — so AnyComponentSchema, and with it validateSchema / safeValidateSchema / objectui validate, has an arm for object-gallery and object-data-table nodes (objectui#7363).

.changeset/7632-shared-record-source-config.md

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

    That ladder is published contract on both faces — packages/types/src/objectql.ts and its zod mirror both ship .describe() strings naming getDataConfig's order (objectui#6939, maintainer ruling 2026-09-02), pinned by objectql-record-source-refinement-6939.test.ts — and it was hand-copied into five plugin components with no gate holding them together. A change to the ruled order had five edit sites and nothing that noticed a missed one; that is the AGENTS.md #0.1 drift class.

.changeset/7804-objectql-handler-key-arms.md

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

    The four plain objectql.ts node faces declare the nine handler keys their registered renderers read (objectui#7804, the objectql.ts slice): ObjectFormSchema.onCancel / .onError / .onOpenChange / .onStepChange / .onSuccess, ObjectGallerySchema.onCardClick / .onRowClick, ObjectGridSchema.onNavigate and ObjectViewSchema.onNavigate.

.changeset/7804-tree-view-handler-slot.md

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

    'runtime-slot' and not 'retired', measured at this key's own channel. 'retired' publishes "no renderer reads this key, so nothing could ever run it" — true of the two siblings already tombstoned on this arm (onSelectChange, onExpandChange) and flatly false here, since the read is live and INVOKED. ⚠️ No in-repo host builds a tree-view node carrying the key: the channel is wired end to end and only the supplier is absent, which is the same shape as ObjectFormSchema.onStepChange in this card's objectql.ts slice and is not evidence of a dead read. The TypeScript declaration is unchanged and still callable, so a programmatic host supplies it exactly as before.

.changeset/7963-alert-dialog-footer-keys-retired.md

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

    Nothing else moves. These spellings are overloaded across the tree and every other owner is a live key on a different declaration — FormSchema.cancelLabel, objectql.ts's confirmLabel, plugin-designer's ConfirmDialog React props, plugin-grid's def.confirmLabel, and plugin-form's ModalForm / DrawerForm, which build a local cancelLabel from schema.cancelText. None is an AlertDialogSchema; none is touched, and a pin asserts it. No fixture, catalog schema, example app or doc fence authored any of the three on an alert-dialog node, so no shipped document is stranded.

.changeset/8767-object-grid-refuses-string-sort.md

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

    Migration. Write the array: sort: [{ field: 'name', order: 'desc' }]. Both keys are required. SortConfig.order carries no ? in @object-ui/types (packages/types/src/objectql.ts) and no .optional() in its zod mirror, and the protocol's own reusable SortItemSchema requires order as well — measured: that schema refuses [{ field: 'name' }] with invalid_value at 0.order. Do not omit it: this block's array arm interpolates whatever is present, so an omitted order lowers to $orderby: 'name undefined' today. That is pre-existing behaviour on the arm this change does not touch, and it is filed as a successor card rather than widened into here.

.changeset/8801-object-kanban-allow-collapse-retired.md

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

    • the declarations retired here — packages/types/src/objectql.ts and its mirror packages/types/src/zod/objectql.zod.ts; - the pins that assert the retirement — object-kanban-allow-collapse-retired-8801.test.ts and bare-kanban-node-key-retired-8802.test.ts; - a comment in packages/types/src/zod/complex.zod.ts, recording that the deleted retiredZeroReadKanbanKey helper once carried this spelling on the SIBLING arm; - one row of content/docs/api/schema-reference.md; - the .changeset/ release notes that discuss it — this one, the two historical entries covering the sibling arm's own spelling, and objectui#9629's note recording the correction to this paragraph.

.changeset/8885-object-chart-drilldown-title-compareto.md

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

    ObjectChart.tsx reads all three off schema, and until now neither published copy declared any of them: not the TS interface (packages/types/src/objectql.ts) and not the zod mirror (packages/types/src/zod/objectql.zod.ts). They rode BaseSchema's index signature / .passthrough() and arrived unvalidated. drillDown was the sharpest case — this component's registry inputs advertise it to the designer palette, and @objectstack/spec publishes ChartDrillDownSchema for exactly this carrier, so an author was offered a key that neither published shape mentioned.

.changeset/8913-object-kanban-columns-declared.md

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

    What moved. ObjectKanbanSchema gains columns on both halves that move together — the TypeScript interface (objectql.ts) and its Zod mirror (zod/objectql.zod.ts). Retiring the bare kanban node type key (objectui#8802) removed the only face that judged a lane, and object-kanban had never declared the key, so it rode BaseSchema's [key: string]: any / .passthrough(): read by the renderer at three sites, named by no published face.

.changeset/8990-object-kanban-groupby-optional.md

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

    @objectstack/spec declares the key optional — groupBy: z.string().optional() on ObjectKanbanPropsSchema — while this package required it on the TypeScript declaration (packages/types/src/objectql.ts) and on the Zod mirror (packages/types/src/zod/objectql.zod.ts). objectui was therefore narrower than the protocol on a published key: ObjectKanbanSchema.safeParse and safeValidateSchema refused an object-kanban node the protocol accepts, and such a node could not be annotated with its own type.

.changeset/9092-inline-locale-declared-face.md

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

    • AppComponentSchema.label (app.ts) - ObjectGridSchema.label and .description (objectql.ts) - PageNodeSchema.aria.ariaLabel (layout.ts)

.changeset/9309-object-gallery-filter-destination-typed.md

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

    ObjectGallerySchema.filter is typed as the destination its own docblock names — QueryParams['$filter'] — on both faces, the TS interface in objectql.ts and the zod mirror in zod/objectql.zod.ts (objectui#9309).

.changeset/9511-record-id-is-a-string.md

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

    The three authorable keys, each on BOTH faces. ObjectFormSchema.recordId (objectql.ts + zod/objectql.zod.ts), DetailViewSchema.resourceId (views.ts + zod/views.zod.ts) and DetailSchema.resourceId (crud.ts + zod/crud.zod.ts). ⚠️ The crud pair is DetailSchema, not DetailViewSchema, and it reaches the same renderer — not by symbol but by data flow: plugin-detail registers the 'detail' node type onto DetailView. A read that follows TypeScript symbols alone finds two keys and is incomplete.

.changeset/9549-tree-filter-declared.md

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

    ObjectTreeSchema.filter is declared on both faces, in the shape objectui#9309 settled for ObjectGallerySchema.filter: QueryParams['$filter'] by indexed access on the TS interface in objectql.ts, and the same two-arm union (array first) on the zod mirror in zod/objectql.zod.ts (objectui#9549).

.changeset/9550-object-tree-root-barrel.md

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

    ObjectQLComponentSchema declares the node types an ObjectQL block may be. Every one of its arms was a named export of this package's root barrel except ObjectTreeSchema, which was declared in objectql.ts, applied by the union, and re-exported by the ./zod barrel (objectui#7917) — while no TypeScript consumer could name it. There is no ./objectql subpath to reach around the barrel: the package's exports map is pinned by packages/types/src/__tests__/package-exports-manifest.test.ts, and the root barrel was the only route to this type.

.changeset/9606-object-kanban-card-title.md

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

    Both published faces of the object-kanban arm now name the key: the zod mirror ObjectKanbanSchema in zod/objectql.zod.ts and its TypeScript twin, the ObjectKanbanSchema interface in objectql.ts. Both declare it OPTIONAL, at the same requiredness the other face uses, so the two faces accept and refuse the same documents. (Located and cited by SYMBOL: line addresses in zod/objectql.zod.ts have drifted before, and this change is itself about a drifted mirror.)

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

  • names objectql.ts → packages/types/src/objectql.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.

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 fee1da595 (merge-base with origin/main): 4 file(s) changed outside .changeset/, read against 1440 pending declaration(s) that publish a body (2022 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3046.2 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-ClU37cVJ.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.68KB 6.20KB
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) 27.95KB 10.04KB
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.52KB 3.45KB
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) 545.50KB 130.49KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.91KB 62.28KB
fields (index.js) 257.41KB 65.16KB
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.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
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.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.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) 51.38KB 14.60KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.50KB 35.37KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.25KB 61.40KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 169.64KB 41.92KB
plugin-grid (index.js) 215.37KB 58.92KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.43KB 28.26KB
plugin-map (index.js) 23.82KB 7.42KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 87.31KB 21.78KB
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) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
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) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
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) 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) 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) 17.15KB 6.32KB
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: ff726c28cdbcca6826ff8b2de173be52183a9ca5

① Derived judgments

Arm change is exactly the ruling's letter. git diff d461b0de8 refs/review/pr-10554 (merge-base to head, 5 files): the dataKey arm line changes only by inserting type?: string; after label?: string;; the name arm line is byte-identical on base and head (sed -n 74,76p base vs 77,79p head). type?: string is the name arm's member, verbatim. Docblock (head ChartRenderer.tsx:66-74) replaces the #8080 "stays as declared" text; every clause checks: normalizeSeries (normalizeChartSchema.ts:393,400) reads dataKey ?? name and chartType ?? type, so type is honoured on either shape and chartType wins when both are written; ChartDataSeriesSchema (packages/types/src/zod/data-display.zod.ts:656-661, type at the enum line) declares name and dataKey each .optional() beside type; chartType remains on the dataKey arm only.

H3 verified true. Scratch probe (probe/pins-base.ts against a verbatim copy of the base arms, tsc 6.0.2 --strict): the union-level literal [{ dataKey: 'revenue' }, { dataKey: 'margin', type: 'line' }] produces no error on base, and so does [{ name, chartType }]; a bogus key in the same position is refused (TS2353), so excess checking is live and the pass comes from type being known to the name arm. Dropping the cast was cosmetic at the union; the gap was at the arm.

H5 verified. Same probe: on base the four arm-level statements are red exactly as the PR lists them (Equal pin TS2339, arm-typed literal TS2353, read-back TS2339, 'dataKey' in e narrowed read TS2339); on head all four are green. The control ({ name, chartType } typed as the name arm) is TS2353 on both base and head, so the @ts-expect-error guards a real refusal. Extract on the binding key isolates each arm cleanly because neither arm carries the other's key.

Kept as any is unrelated to series. Code-level } as any} count: base 2, head 1. The surviving one wraps a literal with no series key at all (categories: ['revenue','margin']); ChartRendererProps.schema declares no categories (published dist/ChartRenderer.d.ts:19-95, head source, no index signature), so the dev's leg B (TS2353 on categories) is consistent. This is a deviation from the ruling's plural "escapes", explained in the PR; substantively correct.

Two out-of-letter prose hunks. (1) Body comment: old text "round-trips every key the internal arm declares unchanged" became false once the arm declares type (normalizeSeries drops type and emits chartType); rewrite matches the code key by key (label through pickLocalized, which returns a string label unchanged). (2) objectql.ts:4351-4361, docblock-only (diff is comment lines only; the series member's nine fields are exactly the dataKey arm minus type, same value types). Old "VERBATIM" claim became false; rewrite is true. Nuance the PR does not state: this docblock and the series member are NOT in the published @object-ui/types@17.6.0 (awk over the published ObjectChartSchema: 0 series?, 0 xAxisKey); they are pending via objectchart-schema-anchor-7946.md, shipping in the same release. So the dev's "published text" premise is pending-to-publish text; the fix is still right for that release. Hunk-disjoint: open PRs touching objectql.ts are #10549 (line 3727), #10546 (1633), #8941 (515); #10550 (2938-3326) has since merged to main; main moved 15 commits since the merge-base with 3 touching objectql.ts at 846/2928-3315/4645, and git apply --check of the PR's hunk on current main is clean. No open PR touches packages/plugin-charts/src.

Pending changesets. 2013 pending .changeset/*.md on head; grep for stays as declared, dataKey arm, ChartRendererProps, 7681, as any, series-union phrasing: no sentence this PR makes false. 7744 ("declares it on that arm and on no other") refers to chartType, still true. 7681 ("normalizeSeries is a no-op on a well-formed entry") and 7946 ("value types taken from their READ sites") read stale next to this PR but are qualified by their own headlines and are provenance, not false. The dev's report does not record a sentence sweep of pending changesets; recorded here instead.

② Semver level

minor is right: a published type widens (dist/ChartRenderer.d.ts declares ChartRendererProps; dist/index.d.ts:9 types chartComponents.chart with it; the published dataKey arm has no type), AGENTS.md §9 forbids major in the fixed group, and the runtime half 7681 is minor in the same release. Clause-②: yes is right under either reading of the directional question. Changeset prose, sentence by sentence: all seven claims hold (arm member and type; schema and renderer accept; chartType stays and wins; accept set only widens, head errors are a strict subset of base errors; ChartRendererProps is not exported by name from src/index.tsx, only ChartRenderer, ObjectChartProps, BarChartSchema, NormalizedChartSchema; union-level literal already accepted; types docblock restated with type unchanged). A @object-ui/types entry is not required: check-changeset-presence.mjs demands one declaration per change ("a DECLARATION, once", header line 118), and the text it would describe has never been published. Minor nit: the types CHANGELOG will not mention the restatement; acceptable.

③ Boundary flags

  • CI on head: 43 check-runs, 40 success, 3 skipped (dependabot, coverage matrix), 0 failed, 0 in progress at final poll (Spec Main Shape Gate completed success after the first poll).
  • Dev findings are real: ObjectChartSchema.series (TS and zod mirror, objectql.zod.ts:2444-2453) lacks type; the union has no literal discriminant (probe: { name, chartType } passes at the union on both sides). Neither blocks: the ruling forbids other arm changes and the types copy is its own Clause-② decision. The dev left both "noted, not filed"; the seat may want a card for the types copy, since its .describe text ("the arm ChartRendererProps declares") will ship beside a copy that now knowingly lags that arm.
  • PR assignee unset (classifier refused the write); seat action.
  • PR is a draft (relay-forced); seat marks ready.
  • Commit trailer names a model (Claude Opus 5.5): explicit exemption in objectstack AGENTS.md:451-455 and os-dev.md:285 (report, do not rewrite); PR title and body carry 0 model identifiers, 0 angle brackets, one close keyword (Fixes #8086); squash takes the PR body.
  • Claim's file surface omitted objectql.ts; the seat should add it (serial constraints verified clean above).

Implemented-by: claude/issue-8086-chart-series-type-arm
Reviewed-by: session_014mXUNuFomfj24w7s1pZzhN

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 25, 2026 08:59
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit aa6be30 Sep 25, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-8086-chart-series-type-arm branch September 25, 2026 09:11
akarma-synetal pushed a commit to akarma-synetal/objectui that referenced this pull request Sep 28, 2026
…ies type and names chartType (objectui#10584) (objectstack-ai#10768)

Fixes objectstack-ai#10584
Clause-②: no. The accept set does not move; this is a `.describe` and
docblock wording fix. `yes` would be owed only if a named producer
turned the card to widening, and then the dev stops and reports.

## What changed

- `packages/types/src/zod/objectql.zod.ts`, the `series` member of
`ObjectChartSchema`: the `.describe()` no longer calls the element "the
arm ChartRendererProps declares". It now says the copy is that `{
dataKey }` arm minus the arm's per-series `type`, which this copy does
not declare, and that this copy's per-series family override is
`chartType` (`bar` | `line` | `area`). A short comment above the member
points at the TS twin for the ground.
- `packages/types/src/objectql.ts`, the `series` member docblock: "this
copy has not taken up" (which read as a lag) now says the omission is
deliberate. Taking `type` up widens a published accept set and waits for
a named producer that writes it on an `object-chart` node. The docblock
also states what a `type` written anyway meets on each face (measured
below).
- `.changeset/10584-object-chart-series-describe.md`: `@object-ui/types`
`patch`, since the `.describe()` string is published text.

Both edits stay inside the `ObjectChartSchema` block, which is the file
surface the claim names. No member, type or schema node changes.

## Premise check on `origin/main` at `0896838deb`

- **H1 holds.** The zod `.describe()` said "the arm ChartRendererProps
declares", while both arms of `ChartRendererProps.schema.series` carry
`type?: string` (objectui#8086). The TS docblock already named the
omission ("every member of it except `type` ... this copy has not taken
up"). It now says the omission is deliberate.
- **H2 holds.** PR objectstack-ai#10734 (objectui#10608) and PR objectstack-ai#10601
(objectui#10518) are both on `main`, so this card's serial predecessors
on the block have landed. The text was re-read on `main`; the card's
line numbers were not used.
- **H3: no pin.** `git grep` for the old describe text
("ChartRendererProps declares", "plotted series in the renderer", "has
not taken up") matches only the two source files. No test, generated
artifact or doc asserts the string, and no describe-verdict pin
(`INTERNAL` / `AUTHORABLE` prefix) reads it.

## Stop condition: producer census for a per-series `type` on
`object-chart`

No producer found, so the work did not stop. Each reading has a control
that hits:

- **objectui (in-repo):** every `type: 'object-chart'` literal outside
tests is in one of the relays: app-shell `ObjectView`, plugin-view
`ObjectView`, plugin-list `ListView`, plugin-dashboard
`DashboardRenderer` and `DashboardGridLayout`. Each writes `series` as
`{ dataKey, label }`, and none writes `type`. app-shell forwards
`viewDef.chart.series` verbatim, but the spec's `ListChartConfigSchema`
is a `strictObject` with no `series` member, so no conforming stored
view carries one. `chartConfigPresentation` emits no `series`. The only
`{ dataKey, type }` literals are in `plugin-charts` tests of the `chart`
renderer.
- **objectstack showcase (at `16c5a33`):** `command-center.page.ts`
writes `object-chart` nodes with no `series`.
`renewals-pipeline.page.ts` writes `series={[{ name: 'total', label:
'Invoice value' }]}` on the ObjectChart react block: the `{ name }` arm,
with no `type`.
- **hotcrm (shallow clone at `2f7b232`):** zero `object-chart` /
`ObjectChart` hits, and zero `series` in the 15 files under
`src/**/dashboards` and `src/**/reports`. Control: 34 chart `type:`
literals across those files (five dashboards and two reports).

## What each face does with a per-series `type` (measured, unchanged by
this PR)

Measured on the rebuilt `dist` at HEAD `5dc9a71e02`, with the same
readings on the base build:

- **zod mirror:** `ObjectChartSchema.safeParse` of `series: [{ dataKey:
'margin', type: 'line' }]` gives `success: true`, and the parsed series
is `[{"dataKey":"margin"}]` (the plain `z.object` element strips
`type`). `{ dataKey, chartType: 'line' }` keeps `chartType`. `{ label }`
alone is still `invalid_type` at `series.0.dataKey`. The base build
gives the same result for each.
- **TS face:** `tsc --strict` on a literal typed `ObjectChartSchema`
gives `TS2353` ("'type' does not exist in type ...") for `{ dataKey,
type }` and compiles `{ dataKey, chartType }`.
- **`dist` diff, base vs head:** the non-comment lines of
`objectql.d.ts` are byte-identical. `zod/objectql.zod.d.ts` and
`zod/index.zod.d.ts` differ only in union-member emission order on
members this PR does not touch (`"json" | "csv" | "xlsx"` versus `"json"
| "xlsx" | "csv"`, `position` and `operator` unions).
`zod/objectql.zod.js` differs only in the comment and the describe
string.

## Verification (all on HEAD `5dc9a71e02`, working tree clean)

- `pnpm --filter @object-ui/types build`: `VERDICT command-exit 0`. The
dependency closure `@object-ui/types^...` has no build script, so it is
empty.
- `pnpm --filter @object-ui/types type-check`: `VERDICT command-exit 0`
(the script name is echoed as `type-check`).
- `pnpm exec vitest run --maxWorkers=2 packages/types/`: `Test Files 245
passed (245)`, `Tests 5327 passed (5327)`, exit 0.
- `pnpm exec vitest run --maxWorkers=2 packages/plugin-charts/`: `Test
Files 82 passed (82)`, `Tests 964 passed (964)`, exit 0. Direction: the
downstream consumer of `@object-ui/types` named in the dispatch, run to
confirm no fixture reads the describe string.
- Out-of-package tests that parse `objectql.ts` / `objectql.zod.ts` from
disk (app-shell `relayRungCensus-7559`, `chartConfigForward-7891`;
plugin-grid, plugin-kanban, plugin-tree and react census pins; and eight
`scripts/__tests__` suites): `Test Files 15 passed (15)`, `Tests 602
passed (602)`, exit 0.
- ESLint `--no-inline-config --format json` on the two touched sources:
exit 0, 2 files linted, 0 errors, and 35 `no-explicit-any` warnings,
none on an edited line. Type-aware linting is not enabled
(`eslint.config.js` has no `parserOptions.project` / `projectService`),
so this diff cannot move a verdict on any untouched file.
- `node scripts/check-changeset-presence.mjs` 0 · `node
scripts/check-changeset-no-major.mjs` 0 · `pnpm
check:new-line-citations` 0 (`VERDICT new-cross-file-line-citations: 0
new citation(s)`) · `pnpm check:control-bytes` 0 · `pnpm
check:spec-symbols` 0 · `pnpm check:test-path-roots` 0 · `pnpm
check:changeset-claims` 0 (report-only). The pending objectui#8086
changeset's sentence "that copy of the internal arm does not carry
`type`, and its type is unchanged" is still true.
- `pnpm check:component-surface-parity` 0 ·
`check:designer-field-key-parity` 0 · `check:doc-types` 0 ·
`check:installed-pin-claims` 0 · `check:handler-key-reads` 0 ·
`check:action-forward-parity` 0.
- `node scripts/check-governed-queue-guard.mjs --test` on the three
paths: `NOT GOVERNED`.
- **NOT MEASURED: `pnpm check:doc-examples`**, exit 2, prerequisite not
met. It needs the dist of about 30 workspace packages, and that full
build is CI's. This diff adds no `@example` block or fence (zero added
lines match), so the gate's population is unchanged.
- **NOT RUN: Spec Main Shape Gate.** The touched member is not
spec-derived: the `series` element is a local copy of
`ChartRendererProps`' arm and binds no `@objectstack/spec` symbol. The
emitted `objectql.d.ts` is non-comment byte-identical, so compiling
against any spec cannot move.

## Acceptance notes

- **Kept verbatim: the `INTERNAL (relay-composed)` prefix.** It is
objectui#7946's ruled verdict. The census above measured a channel it
does not name: the ObjectChart react block. The spec's `react-blocks`
entry has `schemaType: 'object-chart'` and `schema: ChartConfigSchema`,
and it lists `series` in `dataProps`. `ChartSeriesSchema` is the `{ name
}` arm and carries the per-series `type`. The showcase's
`renewals-pipeline` page writes `series={[{ name, label }]}` through
that block onto an `object-chart` node, and the published zod face
refuses that node (`invalid_value` at `chartType`, `invalid_type` at
`series.0.dataKey`). That belongs to the same family as objectui#10518 /
objectui#10608 (the `object-chart` copy against the spec's ChartConfig
contract), and it is reported to the seat rather than acted on here. It
does not change this card's answer. On this node the spec's per-series
`type` rides the `{ name }` arm, and the spec refuses `dataKey` by name,
so a `{ dataKey, type }` entry is not spec-authorable on `object-chart`
either.
- The describe names `chartType` because it is this copy's declared
per-series override. It is not a new authoring recommendation: the
member stays `INTERNAL`.
- No pin was added. The dispatch owed one only if the wording was
already pinned, and it was not.

Refs: objectui#8086 · objectui#7946 · PR objectstack-ai#10554

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

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

1 participant