Skip to content

fix(plugin-charts): refuse a second scatter series instead of drawing it at the first one's y - #7400

Merged
os-project-manager merged 5 commits into
mainfrom
claude/issue-7194-scatter-second-series-refusal
Sep 3, 2026
Merged

fix(plugin-charts): refuse a second scatter series instead of drawing it at the first one's y#7400
os-project-manager merged 5 commits into
mainfrom
claude/issue-7194-scatter-second-series-refusal

Conversation

@os-project-manager

@os-project-manager os-project-manager commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes #7194

Maintainer ruling B (comment 5507673384, 2026-09-02): a chartType: 'scatter' with two or more series renders the file's existing refusal shell, stating that a scatter binds one measure and naming the authored fix; a single-series scatter is unchanged; no multi-measure projection (option A waits for a real caller); render-time only — the authoring-time (zod) rejection follows #7113 when that card rules. Clause-② yes ⇒ this PR is a draft; it is not flipped ready and not enqueued.

What changed

  • packages/plugin-charts/src/AdvancedChartImpl.tsx — the scatter arm checks series.length > 1 first (a binding fault is true of the spec whatever the rows carry, so it outranks the finding(plugin-charts): scatter is NOT MEASURED for the degenerate-magnitude class — its all-positive CONTROL drew zero marks, so the sweep's zeros say nothing #7171 positional refusal the same way the missing-category-key guard outranks the series guard) and renders SeriesArityRefusal: the existing ChartRefusal shell under data-chart-error="scatter-multi-series", role="status", no console warning (matching the three scatter/magnitude answers already in the file). The shell fits without modification. The sentence is read from chart.scatterOneMeasure through useSafeTranslate — the hook ObjectChart already imports from @object-ui/i18n — so no module-scope createSafeTranslation map is added and no partial i18n mock is disturbed (census: 91 tests mock @object-ui/i18n; none of the partial ones reaches this renderer). The series keys are rendered as data after the sentence, as code elements, outside the translated copy.
  • packages/i18n/src/locales/{en,zh,ja,ko,de,fr,es,pt,ru,ar}.ts — one key, chart.scatterOneMeasure. en: A scatter plots one measure. Keep exactly one series: — states one measure, names the fix; the keys follow as data.
  • packages/plugin-charts/src/AdvancedChartImpl.scatterSeriesArity.test.tsx — 12 pins: single-series control draws 2 marks / no refusal / no note; series: [] and no series still reach the chart through the value fallback; two series ⇒ refusal, no chart container, 0 symbols, no legend; copy contains the sentence and lists both keys; three series refuse; the binding refusal wins over no-plottable-points; holds with zero rows; no console warning; a two-series bar still draws both series; en pack value equals the inline fallback; every pack defines it and none copies the English; under an I18nProvider (zh) the pack value renders and the keys stay untranslated.
  • .changeset/7194-scatter-one-measure.md — patch for @object-ui/plugin-charts and @object-ui/i18n.

compareTo on scatter — settled by objectui#7402 ruling (b)

objectui#7402 has been ruled (b) by the director seat (comment 5523068497, 2026-09-03): compareTo on a scatter is not supported. supportsCompareTo (ObjectChart.tsx) and the dashboard path (DatasetWidget.tsx, widget types scatter and bubble) exclude scatter exactly as they exclude pie / donut / funnel, so the primary series draws, no …__comparison overlay is synthesised, and this card's renderer guard is never reached by a compare-to document. The ruling also scopes #7194 itself: "two or more series" reaches authored series only — a synthesised comparison overlay was never in its scope.

Per that instruction this PR drops its compare-to pin (AdvancedChartImpl.scatterSeriesArity.test.tsx — 13 pins → 12) and the changeset paragraph that treated the question as still open; see "What changed" above and the changeset diff. One thing the ruling does not change on its own: supportsCompareTo's exclusion ships as a separate change, still to land — until it does, a compareTo document authored against a scatter still reaches this guard as two series and still refuses today, under the same "Keep exactly one series: amount, amount__comparison" copy the author cannot act on directly (the second raised-by-review point, previously filed to #7402). Closing that residual mismatch is #7402's exclusion landing, not a further change to this card.

Byte cost — measured, both halves, against a control build of the base

Control: origin/main at a6e5f7050 built in a second worktree (pnpm --filter @object-ui/console exec vite build, then node scripts/check-eager-closure-budget.mjs), the same recipe for all three builds; per-chunk numbers read from apps/console/dist/eager-closure.json.

build framework gzip bytes delta vs control check:eager-closure
control (a6e5f7050) 523,823 exit 0 — ✅ framework 511.5 KB / 511.7 KB ceiling (headroom 0.2 KB)
mechanism only (renderer + test, packs at base) 523,823 0 exit 0 — same line
mechanism + copy (eacc55913, unchanged at e090fd866) 524,243 +420 exit 1 — ❌ framework 512.0 KB / 511.7 KB ceiling (OVER by 0.2 KB)

Ceiling PER_CHUNK_GZIP_CEILINGS['framework'] = 524,000; headroom on the control is 177 bytes; this branch is over by 243. The code costs the eager closure nothing (plugin-charts is lazy and not in the closure); the ten-pack copy costs 420 bytes, ~42 per pack, for one sentence already at the ruling's floor (one measure + the fix). ⛔ The ceiling is not raised here and the copy is not weakened below the ruling. This is the fork the dispatch anticipated (PM comment 5513454764, zone 2b): a maintainer budget decision, filed as #7399, one card covering #7194 and #7210. This PR stays draft until it rules; landing order against #7391 is the PM's.

Premise re-checks against the merged ref

  • Defect on origin/main a6e5f7050: yes — scatterYKey = series[0]?.dataKey || 'value' and the legend named off series[0]. fix(plugin-charts): draw every categorical x-axis label on short axes #7385 (d6fe1e1f2) touched only the categorical x-axis path (xAxisCommonProps, label rotation and ellipsis); scatter's numeric axes and the refusal surface were untouched.
  • Authored scatters: 0 in examples / content/docs / apps / packages/*/examples (control bar: 3 files). ⭐ The contract review widened this census to the dashboard-widget spelling type: 'scatter' | 'bubble', which the regex above cannot see: still 0, with control type: 'bar' firing at 10 files including five examples/schema-catalog/**/plugin-dashboard/*.json. The zero holds on a stronger basis than first claimed. One two-series scatter exists in a test corpus: packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx renders plugin-charts:scatter-chart with the shared two-series CHART_SERIES. The sweep stays green on this branch (202/202) because that entry never reaches the scatter arm — the SDUI path does not apply a registration's defaultProps, so it renders with no chartType (filed as bug(plugin-charts): a registration's defaultProps.chartType is inert on the SDUI path — scatter-chart, pie-chart, donut-chart, radar-chart all render as a BAR chart #7401). Not an authored product scatter; the ruling's premise holds. No file outside the declared surface was touched.

Overlap

Verification — mechanism at eacc55913, amendments at e090fd866

  • pnpm exec vitest run packages/plugin-charts/ packages/i18n/ (root, under the shared verify lock): Test Files 100 passed (100) / Tests 1323 passed (1323), exit 0.
  • pnpm exec vitest run packages/app-shell/src/__tests__/widget-dom-leak-sweep.test.tsx: 202 passed on this branch and on the control.
  • Reverse verification (fix committed first): with the base's AdvancedChartImpl.tsx checked out over the commit, the new file reads Tests 8 failed | 5 passed (13) — the 8 refusal/pack pins red, the 5 controls green; restored with git checkout HEAD -- FILE, git diff HEAD empty, blob hash 624cba8… on both sides.
  • pnpm --filter @object-ui/plugin-charts run type-check exit 0 (the new test file is in --listFiles); pnpm --filter @object-ui/i18n run type-check exit 0. Dependency closure built first.
  • Gates: check:i18n-keys 0 · check:i18n-drift 0 (1 key added, 0 en values changed) · check:control-bytes 0 · check:vi-mock-specifiers 0 · check:vi-mock-inherit 0 · check:shell-escape-residue 0 · check-changeset-presence 0 · check:eager-closure 1 (above, expected). eslint on the changed files: 0 errors, warnings are the pre-existing no-explicit-any pattern the sibling chart pins also carry.
  • pnpm lint (repo-wide) was not run locally; CI owns it.

Contract review (Clause-②) — PASS, amendments applied at e090fd866

Reviewed at CONTRACT_REVIEW_TIER. Verdict PASS WITH REQUIRED AMENDMENTS; both are applied and verified, so the gate label is off.

  • R1 — the changeset and the compareTo pin comment no longer attribute that behaviour to ruling B; both now describe it as current and unruled, name [Decision] Does #7194's ruling B reach a compareTo scatter — a published capability it never named? The refusal currently swallows it and names a fix the author cannot act on #7402, and state that shape (b) there inverts the pin as a decision landing, not a regression. The pin itself stays: it records what ships. This section of the PR body was corrected on the same grounds.
  • R2 — a test comment claimed check:i18n-keys also enforces the inline-fallback / en-pack equality. ⭐ Verified false by mutation, twice — with the fallback changed to Keep ONE series: all three i18n gates still exit 0, because inlineDefaultValue reads only a defaultValue property off an options object and interpolationOptions skips a positional string. The toBe(EN_COPY) assertion is the sole guard, and the comment now says so. Mutation proven on disk by blob hash and anchored token counts; restore proven by state (git diff HEAD empty, blob back to 624cba845).

Amendments are prose only — 2 files, 35 insertions, 8 deletions, no renderer and no locale packs. Eager-closure figures byte-identical across the amendment: framework 524,243 at both eacc55913 and e090fd866.

Not done here

🤖 Generated with Claude Code

https://claude.ai/code/session_01EMrWaQw3XS5DxTHxp4yRyC


Generated by Claude Code

… it at the first one's y

A scatter binds one measure: `series[0].dataKey` is the YAxis key and every
`<Scatter>` reads the same rows through it, so a second series added a colour
and a legend entry and nothing else. Measured: two series over two rows painted
four symbols at two positions, each drawn twice, and the second measure's values
appeared nowhere on the plot. The data was valid and the picture was confidently
wrong — no existing refusal could see it.

Ruled B (maintainer, 2026-09-02): a `chartType: 'scatter'` with two or more
`series` now renders the file's refusal shell under
`data-chart-error="scatter-multi-series"`, stating that a scatter plots one
measure, naming the fix (keep exactly one series) and listing the series keys as
data. A single-series scatter is unchanged. No multi-measure projection is built;
render-time only (authoring-time rejection follows #7113). A `compareTo` overlay
counts as a second series — it was painted at the primary's y by the same
mechanism.

The sentence resolves from `chart.scatterOneMeasure` in all ten locale packs via
`useSafeTranslate`, the hook ObjectChart already uses (no module-scope defaults
map). Measured against a control build of the base: +420 gzipped bytes in the
console's eager `framework` chunk, 243 over its ceiling. The ceiling is NOT
raised here.

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

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 48 chunks) 3177.5 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-CsrWIwFh.js
Status FAIL

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ❌ over its ceiling
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 514.87KB 117.50KB
core (index.js) 5.80KB 2.32KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.08KB 61.71KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 47.00KB 12.97KB
plugin-charts (index.js) 70.83KB 19.71KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.63KB 34.56KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.20KB 64.18KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 166.93KB 40.82KB
plugin-grid (index.js) 209.10KB 56.65KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.51KB 27.67KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.21KB 8.66KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 21.12KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

Copy link
Copy Markdown
Collaborator Author

Standing down on check:eager-closure — the failure is real, it is the maintainer's to resolve, and I am not making it green

PM record from the domain:ui execution seat (session session_01EMrWaQw3XS5DxTHxp4yRyC). This PR is complete and parked as draft. Saying once what is red and why no fix is coming.

What is failing

Bundle Analysis, job 100360098939, head eacc55913. One verdict of four — aggregate closure, ceiling sensitivity and ceiling freshness all pass; only the framework per-chunk ceiling fails, 512.0 KB / 511.7 KB, over by 243 bytes.

⭐ The measurement that matters, and it differs from the sibling fork

This PR's implementer split the two halves against a control build of the base, which is exactly what makes this decidable:

build framework gzip delta gate
control (a6e5f7050) 523,823 B exit 0 (headroom 177 B)
mechanism only — renderer + tests, packs at base 523,823 B 0 exit 0
mechanism + ten-pack copy (eacc55913) 524,243 B +420 exit 1

The mechanism costs the eager closure nothingplugin-charts is lazy and not in the closure. The entire overage is the ten-pack copy, ~42 bytes per pack, for one sentence already at the ruling's floor (states one measure, names the fix).

⚠️ This is a materially different shape from PR #7391's fork, and the difference is load-bearing for the decision. There, the mechanism's own code cost 230 B against 177 B of headroom, so no version fitted, footnote deleted or not. Here the mechanism fits with room to spare and only the translations do not. ⇒ The two forks are not interchangeable, and an option that resolves one may not resolve the other. I have recorded that asymmetry on the decision card.

Why I am not making it green

  • Raising PER_CHUNK_GZIP_CEILINGS['framework'] is 门禁削弱 — the maintainer's floor, not an execution lane's, however well-precedented.
  • Shipping the mechanism with English only and deferring the nine translations delivers less than ruling B specifies. It is a real option and it is on the decision card as one — but it is the maintainer's to choose, not this lane's to take because it happens to be the one that compiles.

Where the decision lives

#7399 — one maintainer decision covering this PR and PR #7391 (#7210 / #7225), since both need ruled user-facing copy in the same ten locale packs and both meet the same 177 bytes. It now carries both forks' measurements and the asymmetry between them.

#7194 is pm:blocked with Blocked-by: #7399 and Unlock-action: re-check PR #7400. This PR stays draft, keeps needs:contract-review, and is not enqueued.

Separately, and not blocked by any of the above

Clause-② is yes on this card, so an in-seat contract review at CONTRACT_REVIEW_TIER is owed before needs:contract-review comes off. This seat runs opus and is not at tier, so a claude-fable-5 reviewer has been dispatched — as was done for #7182. That review runs now, in parallel with the budget decision, so this PR is ready the moment #7399 rules. ⚠️ The reviewer has been pointed specifically at the compareTo consequence this PR's description raises: supportsCompareTo excludes only pie / donut / funnel, so a compare-to scatter is a two-series scatter in the product and now refuses. That was surfaced by the implementer rather than buried, which is the right instinct.

⚠️ mergeable_state reads behind; the branch needs updating before any enqueue. Not done now — there is no point rebuilding against a moving base while the PR is parked.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review complete — PASS WITH REQUIRED AMENDMENTS. needs:contract-review stays on.

Clause-② in-seat contract review at CONTRACT_REVIEW_TIER (claude-fable-5), commissioned by the domain:ui seat (PM session session_01EMrWaQw3XS5DxTHxp4yRyC). This seat runs opus and is not at tier, so it commissioned the review rather than performing it — the same route used for #7182.

The mechanism implements ruling B faithfully for authored two-series scatters. The gate label cannot come off for two reasons.

⭐ The finding that justifies the tier — filed as #7402

The PR pins and publishes, as though ruled, a behaviour on the compareTo path that ruling B never contemplated. The reviewer raised it explicitly as the maintainer's question, not the PM's and not its own — and that restraint is the right call.

The ruling's literal predicate ("two or more series") reaches a compare-to scatter. Its stated premise does not: "zero authored scatters in-repo" was measured on chartType: 'scatter' literals, and a compare-to scatter is one authored series plus one synthesised overlay from a documented, published product featureCHANGELOG.md:1727 publishes that only pie / donut / funnel ignore compareTo, and comparisonStyle(s, 'scatter') has a scatter-specific branch. The census that justified B cannot see it.

⚠️ A second defect on that path, independent of how #7402 rules: ruling B requires the refusal name "the authored fix". There the tile says "Keep exactly one series: amount, amount__comparison" — the author has exactly one series in their spec and never wrote amount__comparison. The refusal names a fix that cannot be acted on, and the real fix is unknowable at the renderer, which only sees series. A refusal that misdirects is worse than the wrong picture it replaces.

Required amendments

R1 — do not land the compareTo behaviour as decided. The changeset sentence "This applies to a compareTo overlay too — it is a second series…" and the pin comment "The ruling counts series, not variants" (test file 141–155) attribute to the ruling a scope it did not state. The pin may stay — it records what the code actually does today — but it must point at #7402 as open rather than at the ruling as settled.

R2 — correct a false claim about a gate (AdvancedChartImpl.scatterSeriesArity.test.tsx:196): "The gate check:i18n-keys enforces this equality too."Verified false by mutation — with the inline fallback changed to Keep ONE series: the gate still exits 0, because inlineDefaultValue (scripts/check-i18n-call-site-keys.mjs:1368) reads only a defaultValue property in an options object and interpolationOptions explicitly skips a positional string. The toBe(EN_COPY) pin is the only thing holding the fallback and the pack together — the comment must say so, or the next reader deletes the pin as redundant.

A dev is dispatched for both. They are text-only and do not touch the mechanism.

What the review confirmed

  • Precedence is correct and masks nothing. Scatter is excluded from both CATEGORY_AXIS_CHART_TYPES and SERIES_ONLY_CHART_TYPES "on purpose", so the arity check is genuinely the first guard on the scatter path; the binding wins over positional pin goes red on the base. Observation: the cited precedent's actual reasoning is "the more fundamental failure and the more specific message", not "spec fault beats row fault" — same outcome, looser analogy than the comment claims.
  • No new export. SeriesArityRefusal is module-local, packages/plugin-charts/src/index.tsx is unchanged, @object-ui/i18n was already a dependency. ⚠️ data-chart-error="scatter-multi-series" is a de-facto contract — the five existing values are pinned across 8 test files and published in the CHANGELOG; this changeset publishes the sixth. chart.scatterOneMeasure widens TranslationKeys; patch is the right bump per standing convention.
  • 13/13 pins green; reverse verification reproduced exactly — base renderer over head gives 8 red / 5 green, file restored to blob 624cba845.
  • The census holds on a stronger basis than claimed. The PR's bar control (3 files) fires only in content/docs, so it proved the regex rather than the coverage. Widened to the dashboard spelling type: 'scatter' | 'bubble' — which the PR's regex cannot see — still 0, with control type: 'bar' at 10 files including five plugin-dashboard fixtures.
  • ⚠️ None of the four suites the implementer did not run (251 tests, all green on the head) exercises a scatter with compareTo — so nothing pinned the old wrong picture, and nothing outside this PR pins the new refusal on that path.
  • check:i18n-keys and check:i18n-drift both exit 0, reproduced. check:eager-closure was out of scope by dispatch ([Decision] The framework per-chunk eager-closure ceiling leaves 177 bytes for the whole repo — two ruled user-facing fixes cannot land, and no wording of either fits #7399).

Where this leaves the PR

⚠️ Two independent decisions now block it, and neither alone releases it:

decision about
#7399 ruled? no the framework byte ceiling
#7402 ruled? no whether ruling B reaches a compareTo scatter

Draft, needs:contract-review retained, not enqueued.


Generated by Claude Code

…nd name the pin's real guard

Prose only — no behaviour, no pins added or removed, no locale copy touched.

Two amendments required by the Clause-② contract review of this PR.

R1 — the changeset and the `compareTo` pin comment both presented the overlay's
refusal as settled by the maintainer ruling. The ruling's words are "a
`chartType: 'scatter'` with two or more `series`"; it never mentions `compareTo`
or `variant`, and "the ruling counts series, not variants" was the implementer's
reading of it. Both now describe the behaviour as current and unruled, pointing
at objectui#7402, which is open on whether refusing is right on that path. The
pin stays — it records what ships — and its comment now says that shape (b) on
that card would invert it as a decision landing, not as a regression.

R2 — the locale pin carried "The gate `check:i18n-keys` enforces this equality
too", which is false. `inlineDefaultValue` reads a `defaultValue` property off an
options object and `interpolationOptions` skips a positional string, and this
call site passes the English positionally. Re-measured on this head: with the
inline fallback mutated to `Keep ONE series:`, all three i18n gates still exit 0
(`check:i18n-keys`, `check:i18n-drift`, `check:i18n-dead-keys`), and the mutation
was proven on disk by blob hash before each run and the restore by blob equality
plus an empty `git diff HEAD`. The comment now says the assertion is the sole
guard and why no gate sees the pair.

Verified: 13/13 pins in AdvancedChartImpl.scatterSeriesArity.test.tsx pass;
check-changeset-presence and all three i18n gates exit 0.

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

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 48 chunks) 3177.5 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-BFq2aqIU.js
Status FAIL

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ❌ over its ceiling
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 514.87KB 117.50KB
core (index.js) 5.80KB 2.32KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.08KB 61.71KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 47.00KB 12.97KB
plugin-charts (index.js) 70.86KB 19.72KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.63KB 34.56KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.20KB 64.18KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 166.93KB 40.82KB
plugin-grid (index.js) 209.10KB 56.65KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.51KB 27.67KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.21KB 8.66KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 21.12KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
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

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

❌ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 48 chunks) 3178.6 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-DLTVLYV9.js
Status FAIL

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.

Which half objected:

Eager-closure half Verdict
Aggregate closure ceiling ✅ pass
Per-chunk ceilings ❌ over its ceiling
Ceiling sensitivity (headroom) ✅ pass
Ceiling freshness (checkout vs. base branch) ✅ pass

📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 514.87KB 117.50KB
core (index.js) 5.80KB 2.32KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.08KB 61.71KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 33.40KB 8.71KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 47.00KB 12.97KB
plugin-charts (index.js) 70.86KB 19.72KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.63KB 34.56KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.20KB 64.18KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 166.93KB 40.82KB
plugin-grid (index.js) 209.10KB 56.65KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.51KB 27.67KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.21KB 8.66KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 21.12KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
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

@github-actions

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3181.2 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-BtIH2n2h.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 516.24KB 117.82KB
core (index.js) 5.80KB 2.32KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.08KB 61.71KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 48.27KB 13.32KB
plugin-charts (index.js) 70.86KB 19.72KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.79KB 34.62KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.19KB 64.18KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.18KB 40.94KB
plugin-grid (index.js) 209.25KB 56.71KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.53KB 27.66KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 21.12KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
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

…02 ruling (b)

objectui#7402 has been ruled (b) by the director seat (comment 5523068497,
2026-09-03): `compareTo` on a `scatter` is not supported — `supportsCompareTo`
(ObjectChart.tsx) and the dashboard path (DatasetWidget.tsx, widget types
`scatter` and `bubble`) exclude scatter exactly as they exclude pie / donut /
funnel, so no `…__comparison` overlay is ever synthesised for a scatter and
this card's renderer guard is never reached by a compare-to document. The
ruling also scopes #7194 itself: "two or more `series`" reaches authored
series only; a synthesised comparison overlay was never in its scope.

This PR's compare-to pin and changeset sentence treated that question as
still open (objectui#7402 pending) and pinned CURRENT behaviour instead —
required amendment from the contract review now that the ruling landed.

- Delete the `it('a compareTo overlay currently refuses too …')` pin and its
  explanatory comment from AdvancedChartImpl.scatterSeriesArity.test.tsx.
  `Row` stays (used elsewhere in the file); nothing else in the deleted block
  was referenced outside it. Pin count: 13 -> 12.
- Rewrite the changeset's `compareTo` paragraph: this refusal counts authored
  `series` only, `compareTo` on scatter is out of its scope per the ruling,
  and — read honestly — #7402's exclusion ships as a *separate* change, so
  until it lands a compareTo document still reaches this guard as two series
  and refuses here today.

Renderer (AdvancedChartImpl.tsx) and locale packs untouched — prose and test
surface only.

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

github-actions Bot commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 50 chunks) 3181.2 KB 3191.4 KB
Main entry chunk (gzip) 143.2 KB 350 KB
Entry file index-BtIH2n2h.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) 15.67KB 5.75KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 5.13KB 2.35KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 516.24KB 117.82KB
core (index.js) 5.80KB 2.32KB
create-plugin (index.js) 10.08KB 3.26KB
data-objectstack (index.js) 178.20KB 49.60KB
fields (index.js) 244.08KB 61.71KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 4.28KB 1.75KB
i18n (index.js) 3.44KB 1.39KB
i18n (pickLocalized.js) 7.62KB 3.26KB
i18n (provider.js) 26.89KB 9.04KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 38.98KB 10.98KB
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.55KB 0.62KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 2.53KB 0.85KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useResponsiveConfig.js) 1.37KB 0.63KB
mobile (useSpecGesture.js) 4.32KB 1.64KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 11.71KB 4.29KB
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) 5.12KB 1.74KB
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) 15.75KB 3.80KB
plugin-calendar (index.js) 48.27KB 13.32KB
plugin-charts (index.js) 70.86KB 19.72KB
plugin-chatbot (index.js) 196.19KB 46.43KB
plugin-dashboard (index.js) 132.79KB 34.62KB
plugin-designer (index.js) 212.87KB 43.19KB
plugin-detail (index.js) 251.19KB 64.18KB
plugin-editor (index.js) 2.46KB 1.10KB
plugin-form (index.js) 132.87KB 32.66KB
plugin-gantt (index.js) 167.18KB 40.94KB
plugin-grid (index.js) 209.25KB 56.71KB
plugin-kanban (index.js) 53.21KB 14.66KB
plugin-list (index.js) 113.53KB 27.66KB
plugin-map (index.js) 20.20KB 6.66KB
plugin-markdown (index.js) 13.72KB 4.69KB
plugin-report (index.js) 43.51KB 11.94KB
plugin-timeline (index.js) 30.84KB 8.85KB
plugin-tree (index.js) 8.98KB 3.08KB
plugin-view (index.js) 85.90KB 21.12KB
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) 81.07KB 26.86KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 3.11KB 1.48KB
react (schema-input.js) 2.32KB 1.24KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (codegen.js) 5.41KB 2.34KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 4.93KB 2.24KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (parse.js) 20.57KB 5.88KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 10.35KB 3.60KB
types (ai.js) 0.20KB 0.17KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 0.99KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.74KB 1.41KB
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 (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) 2.59KB 1.31KB
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 (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
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

@os-project-manager
os-project-manager marked this pull request as ready for review September 3, 2026 11:27
@os-project-manager
os-project-manager added this pull request to the merge queue Sep 3, 2026

Copy link
Copy Markdown
Collaborator Author

✅ Landing — both blockers ruled, the ruled amendment made, 32/32 green

domain:ui execution seat, session session_01EMrWaQw3XS5DxTHxp4yRyC. Ready + auto-merge, on the in-seat review route (维护者 2026-08-31 裁定: PASS ⇒ 同席剥标、ready、auto-merge).

The two blockers, both gone

blocker outcome
#7399 — the framework per-chunk ceiling ruled A′, landed 177afeba1 (PR #7489). The chunk was 78.7 % @object-ui/i18n locale catalogue; the ceiling was never budgeting core|react|types. Bundle Analysis here is now ✅ PASS (3181.2 KB / 3191.4 KB, 50 chunks) with ⛔ not one byte of this PR's ruled copy changed
#7402 — does ruling B reach a compareTo scatter? ruled (b) (5523068497)

The amendment #7402's ruling required of this PR — made, and verified by content

The ruling instructed: "PR #7400 drops its compare-to pin and the changeset sentence that treated the question as settled." Commit abf4b7208:

  • the it('a compareTo overlay currently refuses too …') pin and its comment deleted — pins 13 → 12, counted (it( + it.each( at line start) at both heads, not taken from the report;
  • the changeset's compareTo paragraph rewritten;
  • PR body's compareTo section rewritten to cite the ruling.

AdvancedChartImpl.tsx and all ten locale packs are byte-identical across that commitgit show --name-only abf4b7208 lists exactly two files, neither of them the renderer or a pack. The mechanism that passed contract review is untouched.

⚠️ The one thing the changeset now says that is easy to get wrong, and says deliberately: #7402's exclusion (scatter joining pie / donut / funnel in ignoring compareTo) ships as a separate change, which is dispatched but has not landed. Until it does, a compare-to scatter still reaches this guard as two series and refuses. The changeset states that rather than implying this PR ships the exclusion — a reader who assumed otherwise would go looking for an exclusion that is not in this diff.

Checks — 32/32, read by name

29 success, 3 skipped by design (Test (coverage), Test (coverage shard …), dependabot). The six that were still running at the amendment push all came back green: Test (shard 1/4) 11:22:42 · 2/4 11:22:29 · 3/4 11:21:52 · 4/4 11:21:35 · Type Check 11:15:29 · Lint 11:13:16. Also read individually: Bundle Analysis, README Export Check, Governed Surface Queue Guard, Changeset Declaration / Bump Policy / Fixed Group / Overwrite Report.

Landing authority and carriers

Clause-② is yes on this card, and the in-seat contract review at CONTRACT_REVIEW_TIER returned PASS WITH REQUIRED AMENDMENTS (5514186581); R1 and R2 were applied and verified at e090fd866, and needs:contract-review came off then. Both carriers re-read by hand just now: this PR carries plugin / plugin: charts / tests; card #7194 carries bug / plugin: charts / pm:dispatched / domain:ui / priority:p1. Neither carries the gate label.

⚠️ Named as the weaker instrument, as on the sibling landings: scripts/pm/check-clause2-carriers.mjs cannot run in this session (exit 2 — it needs its own GitHub token; this seat reaches GitHub only through MCP, and its own output says that is ⛔ not a clearance). ⭐ That gap is not hypothetical — the contract reviewer on PR #7491 ran the same tool's parser today and found a card-side declaration that a hand label-check could not see, because the failure was a claim-line spelling, not a label state. So: ② here is a manual reading, and a manual reading covers strictly less than the tool.

Sibling state

#7194 was relabelled pm:blockedpm:dispatched with a superseding comment (5524937197), since its body still carried Blocked-by: lines for two now-ruled decisions.


Generated by Claude Code

Merged via the queue into main with commit e8c553b Sep 3, 2026
34 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-7194-scatter-second-series-refusal branch September 3, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug(plugin-charts): a multi-series scatter draws every series at the FIRST series' y values — two measures, one cloud painted twice

2 participants