Repository navigation
fix(plugin-charts): ObjectChart decides its missing-category-axis refusal before it fetches, so a refused chart reads nothing (objectui#12061) - #12068
Merged
objectstack-fleet[bot] merged 3 commits intoOct 9, 2026
Conversation
…usal before it fetches, so a refused chart reads nothing (objectui#12061) The objectui#8168 refusal was a render-time return only; the fetch effect still issued the object-bound query (find with no $top, or aggregate with no groupBy) and discarded the rows. Every chart list view naming no dataset composes that node since objectui#6152 round 15. The refusal's predicate is now one value, refusesMissingCategoryAxis, declared beside the fetch gate: the render-time return, the fetch effect and the invalidation subscription all read it (objectui#7390's shape: decide the binding first, then fetch). The gate is a dependency of the fetch effect, so a chart refused at mount fetches once a category is declared. No $top cap; the refusal's code, role and wording are unchanged. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
…s suppression; type the 12061 pin's renderer stand-in (objectui#12061) Listing fetchesForItself made the fetch effect's dependency list exhaustive, so its eslint-disable directive was reported unused (an error under the repo config). The pin's ChartRenderer stand-in is typed instead of taking any. Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8 Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
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
Size Limits
|
…tegory, so it measures objectName's data reach again (objectui#12061)
Since objectui#12061, ObjectChart refuses a chart with no category axis
before it fetches. The probe built the chart's aggregate from the generic
object sample, {}, which names no category and is not an aggregate the
spec's ChartAggregateSchema accepts (function and groupBy are required).
So the chart was refused and made no data call, and the probe read that
as objectName not reaching the data layer.
The probe now samples the smallest aggregate that schema accepts,
{ function: 'count', groupBy: 'name' }, for object-chart alone, keyed by
block type because object-metric declares an aggregate input with its
own requiredness. With a category the chart also reads its option
colours through the global fetch, so a module-scope double answers that
one URL and forwards every other request to the network-escape guard.
object-chart stays out of NO_DATA_REACH: a chart with a category fetches.
Claude-Session: https://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8
Co-authored-by: Claude <noreply@anthropic.com>
Contributor
✅ Console Performance Budget
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
Size Limits
|
objectstack-fleet
Bot
deleted the
claude/issue-12061-chart-refuse-before-fetch
branch
October 9, 2026 18:04
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #12061
Clause-②: no
ObjectChartnow decides its missing-category-axis refusal (objectui#8168,chart-missing-category-axis) before it fetches, so a chart that refuses reads nothing. This is the shape objectui#7390 gaveObjectGallery: decide the binding first, then fetch. There is no$topcap, and the refusal's code,role="alert"and wording are unchanged.Written by a dispatched os-dev run of the
domain:ui#3seat, sessionhttps://claude.ai/code/session_01CGZy1BGCjdN5cXqL9cnvB8. Claim6085092086, triage direction6082360035. Base:mainat8a55f0cc, which carries PR objectui#12060.What changes
packages/plugin-charts/src/ObjectChart.tsx. The refusal's predicate is one value,refusesMissingCategoryAxis(drawsFetchedObjectRows && !resolveChartCategoryField(schema)). It is declared besidefetchesForItself, and three readers use it:fetchesForItself, so the fetch effect does not callfetchDatafor a refused chart;useDataInvalidationkey, which readsfetchesForItself, so a refused chart is not subscribed and a write to its object does not start a read.hasAuthoredRowsanddrawsFetchedObjectRowsmove up beside the gate unchanged, because the fetch effect sits above where they were declared.groupByCategoryKeystill readsdrawsFetchedObjectRows.The fetch effect lists
fetchesForItself. A specxAxis: { field }added later, for example in a designer preview, moves none of the effect's other dependencies. Without this, a chart refused at mount would never fetch once its category was declared. Listing it also makes the dependency list exhaustive, so itseslint-disable-next-line react-hooks/exhaustive-depsdirective was reported unused (an error) and is removed.New pin:
ObjectChart.refuseBeforeFetch-12061.test.tsx. See below.Changeset:
.changeset/12061-chart-refuse-before-fetch.md,patchon@object-ui/plugin-charts.No export, prop, type or language-pack key changes.
The dispatch's mechanism hypotheses, measured
if (drawsFetchedObjectRows && !resolveChartCategoryField(schema)). The fetch effect was gated only byfetchesForItself, which did not read that predicate. Both now readrefusesMissingCategoryAxis, the single copy of the rule.ds.aggregate(the aggregate leg ofrunAggregate). The probe node does not reach it, because it has noaggregate. A refused node that carries a measure and nogroupBydoes reach it, and was measured issuingaggregateonce onmain. It is the same object-bound query, inside the samefetchData, behind the same gate. The ruling says a chart that will refuse reads nothing, so it is gated too, with no second copy of the rule.compareTocomparison window runsrunAggregatea second time insidefetchData. It is behind the same gate and pinned.findinresolveGroupByLabels, andds.getObjectSchema, run only whengroupByFieldresolves (aggregateGroupByKey(...) || schema.xAxisKey). An unbound node does not resolve it:getObjectSchemawas called 0 times onmain, and is called 0 times now./api/v1/meta/object/...) already returns before fetching whenresolveChartCategoryFieldanswers nothing. The globalfetchwas called 0 times for the probe node, before and after. It is unchanged.queryDataset,/api/v1/meta/dataset/...) is never refused, and is untouched.mainit re-ranfindfor a refused chart on every write to the object: 2findcalls after onenotifyDataChanged. It is now unsubscribed.loadingbranch in render order. The first paint is the refusal: right after the synchronousrender, the refusal is present andchart-loadingis absent, both onmainand now. TheuseStateinitializer still startsloadingas true for this node, which cannot show, and the effect's non-fetching branch now clears it.The pins and the probe reading
ObjectChart.refuseBeforeFetch-12061.test.tsx, through the realObjectChart, withChartRendererstubbed:mainat8a55f0cc{ objectName: 'task', chartType: 'bar', filter: [] }:find/aggregate/getObjectSchema/ metadatafetch, refusal on the first paintfindx1 (RED)groupByaggregatex1 (RED)compareToon a refused chartmain; RED under ablation Afindx2 (RED)xAxisKeyfindx1,('task', { $filter: [] })aggregate.groupByaggregatex1, same argumentsxAxisis addedmain; RED under ablations A and Bfindx1 after the category arrivesThe pre-fix run, against unmodified
ObjectChart.tsx, reproduces the card's probe:Tests 3 failed | 2 passed (5). The file then held the probe, measure, write and two control cases; thecompareToand transition cases were added after the fix.Reverse verification
Predictions were written before each run. Each mutation went through
ablation-replacein WRAP mode, with the restore proven by blob hash plus an emptygit diff HEAD. The pin imports./ObjectChartrelatively, so the mutated source is what runs, and no build is involved.!schema.data && !refusesMissingCategoryAxis;becomes!schema.data;. Predicted: 5 red in the new file, the 2 bound controls green, and all 23 in the 8168 file green. Observed:Tests 5 failed | 25 passed (30), the 5 named reds exactly. Run at2a98113fe, and again at3c953d7dewith the same result. Restore: blob9bdf6c1ff5aaequals HEAD, andgit diff HEADis empty.xAxiscase goes red. Observed:Tests 1 failed | 6 passed (7), that case. Restore proven the same way.Tests and gates, at head
3c953d7depnpm exec vitest run packages/plugin-charts/plus the four relay-side chart pins (listViewLegacyChartRetired-6152,ObjectView.chartConfigForward-7891,ListView.chart-capability-7544, plugin-view'sObjectView.legacyChartRetired-6152):Test Files 95 passed (95),Tests 1283 passed | 9 skipped (1292). The 9 skips are the pre-existingdescribe.runIf(DRIVEN)timezone pins (objectui#10866).pnpm --filter @object-ui/plugin-charts type-check(tsc --noEmit && tsc -p tsconfig.test.json): exit 0, after building the dependency closure withpnpm --filter '@object-ui/plugin-charts^...' build.--listFilesshows the new pin in the test program.pnpm exec eslinton the two touched sources: 0 errors.ObjectChart.tsx's rule histogram is identical to the base file's (35 warnings, read through stdin from8a55f0cc). The new test file has 0 messages.check:control-bytes,check:test-path-roots,check:changeset-claims,check:pending-changeset-literals,check:new-line-citations(0 new citation(s)),check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:shell-escape-residueandcheck:unreferenced-sources.scripts/check-changeset-presence.mjsreports 2 source files of 1 released package and 1 changeset.check:comment-mask-corpuswas read at2a98113fe: a report within its held residue.check-governed-queue-guard --testover the three paths reports NOT GOVERNED.check:eager-closure, because it reads a built consoledist/and is CI's run. This diff adds no import, and moves none.Acceptance notes
content/docs/and no package README describes the refusal's fetch behaviour, so no docs change.runAggregatecomment says a missinggroupBycolumn means the chart "is already refused" by the 8168 screen. That holds only when nothing else names a category. A node withaggregate: { field, function }and anxAxisKeyis not refused, and still sendsgroupBy: undefinedtods.aggregate.ChartAggregaterequiresgroupBy, so the authoring door refuses that document, and no producer that composes it was found.Generated by Claude Code