Skip to content

fix(plugin-list): the gantt chart query honours the toolbar Filter and UserFilters chips (objectui#10037) - #10249

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10037-gantt-toolbar-filter
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-10037-gantt-toolbar-filter

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10037

What was wrong

On a gantt list view, the toolbar offers the Filter control and the UserFilters chips. Neither one changed the chart. Every other view draws the rows ListView fetched, and that fetch applies buildEffectiveFilter(schema.filter, currentFilters, userFilterConditions). The registered object-gantt renderer takes only the node's schema and runs its own query with $filter: schema.filter (ObjectGantt.reload). The gantt node was built from baseProps, which carries only the authored filter: schema.filter. So both controls changed ListView's own fetch and nothing the user could see.

The change (forwarding arm, as the dispatch suggested)

  • packages/plugin-list/src/ListView.tsx: a new ganttChartFilter value, computed only on the gantt view. It is the same buildEffectiveFilter(...) value that ListView's own fetch sends. The object-gantt branch now writes filter: ganttChartFilter after spreading baseProps. Nothing else in baseProps or in the other branches changed. The toolbar flags did not change either, because the controls now work.
  • The value is cached by its serialised payload, in a ref. ObjectGantt's reload effect lists schema.filter as a dependency. If every re-render handed it a fresh ['and', …] array for the same filter, the chart would re-query each time. This follows AGENTS.md [WIP] Enhance every detail of the designer #10: the cache is keyed on the data, not on the identity of a memo result.
  • A FilterOperatorError from buildEffectiveFilter does not turn into "no filter". That would widen the chart to every row. The node keeps its last filter (or, before it has one, the authored filter it carried before this change). ListView's own fetch hits the same refusal inside its load try and shows the load-error panel, which replaces the chart.
  • Shape check: ObjectGantt.reload passes schema.filter to dataSource.find unchanged as $filter. The effective filter is the spec filter AST that ListView's grid fetch already sends, and ValueDataSource (the inline provider) matches the AST form. One side effect: an authored ViewFilterRule[] or MongoDB-style object filter now reaches the chart already lowered by toFilterNode, the same lowering every other view's query uses. Before, the chart passed it through as authored.
  • The row ceiling from objectui#7210 (ruling a′) is untouched.

Tests and measurements (final head 6260932)

  • New pin: packages/plugin-list/src/__tests__/ListView.ganttToolbarFilter-10037.test.tsx. Its stand-in matches the registered renderer's contract (reads { schema } only, queries $filter: schema.filter, keyed on schema.filter) and asserts what the CHART queries: a CONTROL case, toolbar forwarding, a UserFilters dropdown chip, and a stability case. 4 passed.
  • Reverse verification, run with ablation-replace.mjs (the mutation was proven on disk, then the blob was restored to match HEAD and git diff HEAD was empty):
    • Deleting filter: ganttChartFilter: 3 failed / 1 passed. Both FORWARDS cases failed. STABILITY failed too, because its precondition waits for the forwarded toolbar condition. CONTROL stayed green.
    • Disabling the payload-keyed cache: 1 failed / 3 passed. STABILITY failed with expected 5 to be 1.
  • One-time end-to-end proof with the REAL @object-ui/plugin-gantt renderer (a temporary probe, not committed: it imports a package plugin-list does not declare). Toolbar status equals open, chip priority: high, authored owner = ada. The chart's query was told apart by its $top (the NON_GRID_ROW_CEILING_TOP ceiling):
    • fix: chart $filter = ["and",[["owner","=","ada"]],["status","=","open"],["priority","=","high"]], identical to the host fetch; 1 chart query.
    • defect (fix line deleted): chart $filter = [["owner","=","ada"]], while the host fetch carried all three.
  • Gates on 6260932:
    • pnpm exec vitest run packages/plugin-list/: 83 files / 1009 tests passed.
    • pnpm --filter @object-ui/plugin-list type-check: exit 0. The new test file is in tsconfig.test.json's file list (--listFilesOnly).
    • pnpm --filter @object-ui/plugin-list lint: exit 0 (0 errors).
    • check:vi-mock-specifiers / -inherit / -override-shape, check:changeset-claims (report-only), check:new-line-citations (0 new), check:control-bytes, check:test-path-roots, check:phantom-deps, changeset:check, check:unreferenced-sources, check-changeset-presence.mjs: all exit 0.
  • Full CI is left to CI.

Sibling views (measured, not assumed)

A temporary probe rendered ListView with the toolbar filter status equals open over the REAL registered renderers:

  • kanban, calendar, timeline and gallery issue no query of their own. They draw the host rows, so they reflect the toolbar.
  • tree DOES query for itself: ObjectTree's object-provider branch runs before its host-data branch. Its query carried only the authored filter.
  • chart aggregates from schema.filter alone.

Those two are the same defect class on other views. They are reported to the dispatching seat and not fixed here.

Acceptance notes

  • ListView.tsx still calls objectui#7210 half 2 "an open maintainer decision" in the ganttOwnsData and surfaceDrawsFetchedRows comment blocks. That has been out of date since the a′ ruling (comment 5508048888). This change does not edit those blocks, which are outside its file fence, so the sentences are left as they are.
  • check:changeset-claims reported pending changesets that name ListView.tsx. I re-read each paragraph: they describe the kanban groupField node, the userActions harvest, comment corrections and the gantt binding route. None of them is falsified by moving the gantt node's filter.
  • The toolbar Search box is also offered on gantt, and the chart's query carries no $search. The probe measured host $search: "needle" and no search on the chart query. That is outside this card, which is about Filter and UserFilters, and is reported separately.

Generated by Claude Code

…bar Filter and UserFilters chips reach the chart

The registered object-gantt renderer forwards no host prop and queries for
itself from schema.filter, so a gantt node carrying only the authored filter
left the toolbar Filter control and the UserFilters chips changing
ListView's own fetch and nothing drawn. The node now carries the same
buildEffectiveFilter value ListView's fetch sends, cached on its serialised
payload so an equal filter keeps its identity across re-renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LkCKMa5bvrw3L4ezcNXEXW
…ecord observed ablation directions

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

Copy link
Copy Markdown
Contributor

changeset-claim-re-read

⚠️ 7 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/7070-no-invented-gantt-date-fields.md

  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • app-shell/src/views/ObjectView.tsx — the console object page. The inline branch becomes ganttViewOptions, the sibling of calendarViewOptions and timelineViewOptions: the declared block spread whole, title floored at 'name', no date field invented. - plugin-list/src/ListView.tsx — the render branch AND the capability gate. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7499-gantt-non-axis-floors-omitted.md

  • names plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • plugin-list/src/ListView.tsx — the object-gantt render branch. - plugin-view/src/ObjectView.tsx — generateViewSchema, the authored object-view element route, which bypasses ListView entirely.

.changeset/7773-kanban-adapter-groupfield-write.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Who is NOT affected — the boundary is node-local. Every VIEW-LEVEL groupField read is untouched and still live: it is a legacy alias of the spec's groupByField on the kanban view config, mapped by normalize-list-view.ts, and both adapters still resolve lanes through it (ObjectView.tsx's kanbanCfg.groupField ||, ListView.tsx's groupByField || groupField). Authoring options.kanban.groupField on a list-view or object-view keeps working exactly as documented in packages/plugin-list/README.md. groupField is dead only on the generated object-kanban NODE.

.changeset/7780-object-kanban-record-source.md

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

  • names packages/plugin-list/src/ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    • packages/plugin-list/src/ListView.tsx generates the node as groupBy: laneField. objectDef loads asynchronously, so laneField is undefined on every load until it lands, and stays undefined whenever the object offers no stageField hint and none of status / stage / state / phase. The renderer serves that node; both published faces refused it. - content/docs/utilities/data-objectstack.mdx documents an object-kanban node that is exactly { type, dataSource }, with no groupBy. ⚠️ This one is weaker and is cited for what it is: that fragment is still refused after this change, at RECORD_SOURCE_REQUIRED, because dataSource is not a rung of the record-source ladder. It shows a lane-less board is a documented authoring; it is not a document this change admits.

.changeset/list-user-actions-collision-5398.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    The harvest now reads the object block only. Both userActions read sites in ListView.tsx carry a comment naming the collision, and __tests__/ListView.userActionsCollision.test.tsx pins each clause of it: the two shapes, a producer that manufactures the view one, the harvest's blindness to it, and the projection that must keep the object's operand with a toolbar block — or an empty block — present on the view.

.changeset/listview-comment-pair-4559.md

  • names ListView.tsx → packages/plugin-list/src/ListView.tsx — edited by this change

    Two comment corrections in ListView.tsx (objectui#4559, objectui#4966). No runtime behaviour changes and the emitted bundle is byte-identical; the published .d.ts does change, which is why this is a patch rather than an empty frontmatter.

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.

Compared the checked-out tree with 0aacecc08 (merge-base with origin/main): 2 file(s) changed outside .changeset/, read against 1275 pending declaration(s) that publish a body (1843 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3036.6 KB 3104.5 KB
Main entry chunk (gzip) 147.5 KB 350 KB
Entry file index-ChRelooq.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) 25.05KB 9.16KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.46KB 3.43KB
auth (index.js) 3.19KB 1.44KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 26.08KB 7.56KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 546.87KB 130.98KB
core (index.js) 8.94KB 3.59KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 222.47KB 61.83KB
fields (index.js) 253.40KB 63.99KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 1.22KB 0.64KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.22KB 2.26KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 33.36KB 10.88KB
i18n (useDisplayLocale.js) 2.85KB 1.45KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.02KB 11.00KB
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.39KB 3.10KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 14.81KB 3.63KB
plugin-calendar (index.js) 51.44KB 14.62KB
plugin-charts (index.js) 71.82KB 20.13KB
plugin-chatbot (index.js) 198.27KB 47.18KB
plugin-dashboard (index.js) 133.00KB 35.19KB
plugin-designer (index.js) 215.98KB 44.34KB
plugin-detail (index.js) 257.23KB 66.95KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 144.16KB 36.92KB
plugin-gantt (index.js) 167.99KB 41.37KB
plugin-grid (index.js) 214.80KB 58.65KB
plugin-kanban (index.js) 48.71KB 15.17KB
plugin-list (index.js) 113.90KB 28.11KB
plugin-map (index.js) 21.74KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.68KB 8.95KB
plugin-tree (index.js) 10.58KB 3.72KB
plugin-view (index.js) 85.18KB 21.05KB
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) 106.60KB 35.16KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.63KB 2.18KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.38KB 1.98KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.74KB 2.54KB
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) 15.71KB 5.30KB
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
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 13:50
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit 0427036 Sep 24, 2026
45 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-10037-gantt-toolbar-filter branch September 24, 2026 14:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

1 participant