Skip to content

fix(plugin-list): withhold the toolbar Search on tree and chart list views (objectui#10326) - #10510

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-10326-withhold-toolbar-search-tree-chart
Sep 25, 2026
Merged

os-litant merged 3 commits into
mainfrom
claude/issue-10326-withhold-toolbar-search-tree-chart

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #10326

Clause-②: no

Executes ruling 5825585515 (letter A): the toolbar Search is not offered on tree or chart list views, because a control that changes nothing is withheld. No new ObjectTreeSchema key and no search on aggregates (option B was not taken).

What changed

packages/plugin-list/src/ListView.tsx, one decision and two readers of it:

  • searchlessViewOnScreen = currentView === 'tree' || currentView === 'chart' is declared beside datasetChartOnScreen, which PR objectui#10477 added for the Filter controls. It is spelled the same way.
  • Reader 1 is the toolbar flag: showSearch: ua?.search !== false && !searchlessViewOnScreen, and the toolbarFlags memo lists the new primitive.
  • Reader 2 is the applied term. The state is now searchInput / setSearchInput, and searchTerm is derived as searchlessViewOnScreen ? '' : searchInput. Every existing reader keeps reading searchTerm: the fetch ($search, and the client-side filter for inline data), the export, the trigger and ganttSearchTerm. So a term that is already set does not narrow ListView's own fetch or export while a tree or chart is on screen. The typed term is kept, so switching back applies it again, and onSearchChange is not called for the switch.

Tests: packages/plugin-list/src/__tests__/ListView.searchWithheldTreeChart-10326.test.tsx, 17 cases, with a stand-in registered for every view node type. Each case waits for its view's stand-in before it asserts anything.

Mechanism assumptions, measured

  • A1 (one flag): holds. toolbarFlags.showSearch is the only visibility decision. The withhold sits there, spelled the way the Filter withhold is. The applied-term gate reads the same const, so there is no second decision.
  • A2 (every chart binding): not already covered on main. PR objectui#10477 withheld only the Filter builder and chips on a dataset-bound chart. showSearch was still ua?.search !== false there. The new const keys on currentView === 'chart' whatever the binding, and the pins cover both the object-bound and the dataset-bound block.
  • A3 (authored key): no key can force Search on. Legacy showSearch folds into userActions.search in normalizeListViewSchema, and the flag reads !== false, so an authored true is the same as the default. On tree and chart, an authored userActions.search: true is now accepted and not honoured. One case pins that. No refusal or warning exists for it, and none was added. This is left as an open question in the report, the same shape PR objectui#10477 left for userActions.filter: true on a dataset chart.
  • A4 (carried term): it applied. Two paths carry a term into a tree or chart view. One is initialSearchTerm: app-shell's ObjectView restores the per-view stored term through readListFilterState, and before this change a tree view could have stored one. The other is a switch inside one list. Both kept $search on ListView's fetch, which feeds the record count and the export. Both are gated now and pinned.
  • A5 (other views unchanged): Search is pinned present on kanban, calendar, gallery, timeline, map and gantt. The objectui#10250 gantt Search suite is green.

Reverse proof (committed tree a966cf01e, mutation via ablation-replace.mjs, each leg restored to the HEAD blob with an empty git diff HEAD)

leg red green
both halves removed 9: the 3 tree/chart absence cases, the 2 userActions.search: true cases, the 3 restored-term cases, the switch case 8: both grid CONTROLs and the 6 other view kinds
visibility only (showSearch back to ua?.search !== false) 6: the 3 absence cases, the 2 true cases, the switch case 11
applied term only (searchTerm = searchInput) 4: the 3 restored-term cases, the switch case 13

My first ablation attempt was a no-op: a shell-quoting error, exit 2, lock held 0s, nothing mutated. It was re-run through a script file, and the three rows above come from those runs.

Changesets

  • .changeset/10326-toolbar-search-withheld-tree-chart.md: @object-ui/plugin-list patch.
  • .changeset/10250-self-querying-views-toolbar-state.md (pending): body only, frontmatter byte-identical. Its last sentence said "the toolbar Search box on tree and chart views" still does not reach those queries. That presupposes a box this change removes, so the sentence now says the box is not offered. check-changeset-overwrite reports this edit as its case 2 (report-only).
  • check-changeset-claims flagged 7 pending changesets that name ListView.tsx (7070, 7499, 7773, 7780, 8990, list-user-actions-collision-5398, listview-comment-pair-4559). I read each paragraph: none says anything about the toolbar Search, and none is falsified. The objectui#10327 changeset is still pending and is about Filter only: still true. A wider grep of pending bodies for toolbar Search found 8317's userActions.search !== false fallback quote, which is still true as the default.

Gates, all at a966cf01e

  • vitest, targeted union of 29 files (the new file; the objectui#10250, objectui#10327, objectui#10037, 7544 and relationalSort suites; every plugin-list suite that names the toolbar search; the source-reading pins over ListView.tsx in app-shell, plugin-grid, types, core, react, plugin-calendar and scripts): 29 files, 875 tests passed.
  • vitest packages/plugin-list/: 92 files, 1094 tests passed.
  • pnpm --filter @object-ui/plugin-list type-check (tsc --noEmit && tsc -p tsconfig.test.json) after the closure build (--filter '@object-ui/plugin-list^...' build, 12 packages): exit 0. The test project compiles the new file. An earlier run went red on a mistyped double in it, which is fixed in a966cf01e.
  • check-changeset-presence, check-changeset-no-major, check-changeset-fixed, check-changeset-overwrite, check-changeset-claims, check-new-cross-file-line-citations (0 new), check-control-bytes, check-test-path-roots, check-pending-changeset-literals, check:unreferenced-sources: all exit 0.
  • Lint: this is a proven narrowing, not a repo-wide run. The population is what the package lint script lints (eslint . under the root eslint.config.js, with no package config). The eslint JSON output counts 2 files: 0 errors; ListView.tsx has 183 warnings, the same per-rule counts as the base blob linted through stdin, and the test file has 0. Nothing about untouched files can change: the config has no parserOptions.project or projectService (no type-aware linting), and no local rule reads other files.
  • Governed-surface guard --test on the 4 paths: NOT GOVERNED.
  • NOT MEASURED: check:sdui-registration-pins exited 2 with its prerequisite unmet (it needs a console build). The diff adds no registration to shipped source; the stand-ins are registered only in test scope.

Acceptance notes

  • Boundary of the ruling (measured with a probe that was not committed). Take a tree view on a ListView that has inline data, no objectName and no data source. There ObjectTree has no object provider and draws the host data, which the list's client-side search narrows. With the withhold removed, a restored term hid the non-matching row. With it, both rows are drawn and Search is absent. So on that configuration this removes a Search that did narrow the tree, though it cut the hierarchy (the triage comment names that cost). Nothing in examples, docs or apps authors a tree list view, and the console always binds objectName and a data source. This is raised as an open question in the report and not acted on.
  • searchableFields on a view that is only a tree or only a chart is now read by nothing on that view. It still rides with the term once the list switches to a view that offers Search.
  • The package README's "Integrated Search" bullet is still true in general. It was not edited, because it is outside the claimed file surface.

Generated by Claude Code

…views (objectui#10326)

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
…ctui#10250 sentence it falsifies

Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BP8CMtACxTdLjqR6rhd33C
@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 00715f5ba (merge-base with origin/main): 2 file(s) changed outside .changeset/, read against 1417 pending declaration(s) that publish a body (1997 pending in total). · run

@github-actions

Copy link
Copy Markdown
Contributor

✅ Console Performance Budget

Metric Value Budget
Eager closure (gzip, 329 chunks) 3044.6 KB 3104.5 KB
Main entry chunk (gzip) 147.8 KB 350 KB
Entry file index-Rbhtj1mQ.js —
Status PASS —

The eager closure is every chunk the entry reaches through static imports — what the browser fetches and parses before the app renders. The entry chunk on its own is a small fraction of it.


📦 Bundle Size Report

Package Size Gzipped
app-shell (consoleActionDispatch.js) 0.20KB 0.19KB
app-shell (index.js) 16.68KB 6.20KB
app-shell (runtime-config.js) 20.68KB 7.36KB
app-shell (types.js) 0.01KB 0.04KB
app-shell (urlParams.js) 10.06KB 3.86KB
auth (ActiveOrganizationStorage.js) 27.95KB 10.04KB
auth (AuthContext.js) 0.31KB 0.24KB
auth (AuthGuard.js) 2.07KB 1.00KB
auth (AuthProvider.js) 40.18KB 10.59KB
auth (AuthShell.js) 3.49KB 1.40KB
auth (ForgotPasswordForm.js) 12.21KB 3.45KB
auth (LoginForm.js) 18.15KB 5.39KB
auth (PreviewBanner.js) 0.90KB 0.50KB
auth (RegisterForm.js) 6.65KB 2.22KB
auth (SocialSignInButtons.js) 9.61KB 3.89KB
auth (UserMenu.js) 3.41KB 1.23KB
auth (auth-gate-events.js) 1.29KB 0.66KB
auth (authStyles.js) 5.04KB 1.72KB
auth (createAuthClient.js) 40.21KB 10.80KB
auth (createAuthenticatedFetch.js) 8.52KB 3.45KB
auth (index.js) 3.63KB 1.64KB
auth (invitation-status.js) 1.22KB 0.70KB
auth (org-roles.js) 6.66KB 2.78KB
auth (phone-identifier.js) 1.11KB 0.66KB
auth (types.js) 0.59KB 0.35KB
auth (useAuth.js) 5.30KB 1.02KB
auth (useWorkspaceAdminStatus.js) 11.08KB 4.58KB
collaboration (CommentThread.js) 27.13KB 7.95KB
collaboration (LiveCursors.js) 3.17KB 1.27KB
collaboration (PresenceAvatars.js) 6.49KB 2.64KB
collaboration (PresenceProvider.js) 2.79KB 1.13KB
collaboration (index.js) 1.68KB 0.73KB
collaboration (useCollaborationTranslation.js) 6.05KB 2.52KB
collaboration (useCommentSearch.js) 1.98KB 0.88KB
collaboration (useConflictResolution.js) 7.75KB 1.86KB
collaboration (useMentionNotifications.js) 1.81KB 0.68KB
collaboration (usePresence.js) 6.33KB 1.84KB
collaboration (useRealtimeSubscription.js) 7.91KB 2.01KB
components (index.js) 544.62KB 130.17KB
core (index.js) 9.22KB 3.71KB
create-plugin (index.js) 27.94KB 9.51KB
data-objectstack (index.js) 223.86KB 62.25KB
fields (index.js) 256.68KB 65.01KB
i18n (LocalizationContext.js) 1.76KB 0.96KB
i18n (builtinAggregateLabels.js) 0.86KB 0.49KB
i18n (currency.js) 2.59KB 1.22KB
i18n (fallbackInterpolation.js) 6.25KB 2.77KB
i18n (i18n.js) 8.87KB 3.64KB
i18n (index.js) 5.24KB 2.27KB
i18n (pickLocalized.js) 9.86KB 3.95KB
i18n (provider.js) 39.40KB 12.91KB
i18n (useDisplayLocale.js) 3.52KB 1.76KB
i18n (useObjectLabel.js) 34.34KB 9.17KB
i18n (useSafeTranslation.js) 5.60KB 2.33KB
layout (index.js) 39.28KB 11.09KB
mobile (MobileProvider.js) 0.92KB 0.49KB
mobile (ResponsiveContainer.js) 0.94KB 0.38KB
mobile (breakpoints.js) 1.51KB 0.70KB
mobile (createOfflineDataSource.js) 5.61KB 1.75KB
mobile (index.js) 1.99KB 0.87KB
mobile (offlineQueue.js) 3.91KB 1.35KB
mobile (pwa.js) 0.97KB 0.49KB
mobile (serviceWorker.js) 1.48KB 0.62KB
mobile (serviceWorkerSource.js) 3.41KB 1.48KB
mobile (useBreakpoint.js) 1.54KB 0.65KB
mobile (useGesture.js) 6.96KB 1.98KB
mobile (useOfflineSync.js) 1.99KB 0.72KB
mobile (usePullToRefresh.js) 6.62KB 2.45KB
mobile (useResponsive.js) 0.72KB 0.42KB
mobile (useSpecGesture.js) 5.52KB 2.10KB
mobile (useTouchTarget.js) 1.01KB 0.54KB
permissions (MePermissionsProvider.js) 13.52KB 4.88KB
permissions (PermissionContext.js) 0.31KB 0.25KB
permissions (PermissionGuard.js) 0.89KB 0.45KB
permissions (PermissionProvider.js) 6.24KB 2.16KB
permissions (discardProofCache.js) 1.04KB 0.55KB
permissions (evaluator.js) 8.33KB 3.07KB
permissions (index.js) 0.93KB 0.41KB
permissions (store.js) 0.91KB 0.42KB
permissions (useFieldPermissions.js) 1.28KB 0.53KB
permissions (usePermissions.js) 4.83KB 2.27KB
plugin-ai (index.js) 16.01KB 3.93KB
plugin-calendar (index.js) 51.22KB 14.56KB
plugin-charts (index.js) 74.95KB 20.88KB
plugin-chatbot (index.js) 198.36KB 47.20KB
plugin-dashboard (index.js) 133.59KB 35.39KB
plugin-designer (index.js) 216.25KB 44.39KB
plugin-detail (index.js) 232.19KB 61.38KB
plugin-editor (index.js) 2.23KB 1.05KB
plugin-form (index.js) 146.50KB 37.51KB
plugin-gantt (index.js) 168.43KB 41.50KB
plugin-grid (index.js) 215.49KB 58.91KB
plugin-kanban (index.js) 49.30KB 15.39KB
plugin-list (index.js) 114.55KB 28.29KB
plugin-map (index.js) 21.83KB 7.07KB
plugin-markdown (index.js) 13.88KB 4.80KB
plugin-report (index.js) 43.55KB 11.99KB
plugin-timeline (index.js) 30.64KB 8.94KB
plugin-tree (index.js) 10.52KB 3.69KB
plugin-view (index.js) 88.00KB 21.97KB
providers (DataSourceProvider.js) 0.75KB 0.39KB
providers (MetadataProvider.js) 1.37KB 0.59KB
providers (ThemeProvider.js) 1.90KB 0.85KB
providers (UploadProvider.js) 11.66KB 3.50KB
providers (index.js) 0.45KB 0.23KB
providers (types.js) 0.01KB 0.04KB
react-runtime (index.js) 5.62KB 2.34KB
react (LazyPluginLoader.js) 4.47KB 1.63KB
react (SchemaRenderer.js) 114.58KB 37.60KB
react (data-invalidation.js) 5.05KB 2.08KB
react (index.js) 4.03KB 1.86KB
react (schema-input.js) 4.25KB 2.04KB
react (spec-input.js) 0.20KB 0.18KB
sdui-parser (body-dialect.js) 4.78KB 2.09KB
sdui-parser (codegen.js) 6.58KB 2.74KB
sdui-parser (dashboard-widget-options.js) 3.08KB 1.30KB
sdui-parser (index.js) 5.78KB 2.56KB
sdui-parser (input-type.js) 2.84KB 1.40KB
sdui-parser (kanban-quick-add.js) 3.89KB 1.87KB
sdui-parser (parse.js) 25.28KB 7.80KB
sdui-parser (provenance.js) 3.66KB 1.82KB
sdui-parser (types.js) 0.28KB 0.23KB
sdui-parser (validate.js) 18.27KB 6.20KB
types (ai.js) 4.11KB 2.06KB
types (api-types.js) 0.20KB 0.18KB
types (app.js) 2.87KB 1.00KB
types (base.js) 0.20KB 0.18KB
types (blocks.js) 0.20KB 0.18KB
types (complex.js) 2.93KB 1.49KB
types (crud.js) 0.20KB 0.18KB
types (dashboard-filter-alias.js) 6.23KB 2.74KB
types (data-display.js) 3.75KB 1.85KB
types (data-protocol.js) 0.20KB 0.19KB
types (data.js) 0.20KB 0.18KB
types (designer.js) 1.85KB 0.85KB
types (disclosure.js) 0.20KB 0.18KB
types (error-code.js) 1.54KB 0.88KB
types (expression.js) 0.20KB 0.18KB
types (feedback.js) 0.20KB 0.18KB
types (field-types.js) 0.20KB 0.18KB
types (form.js) 0.20KB 0.18KB
types (http-inflight.js) 8.87KB 3.73KB
types (http-retry.js) 4.32KB 2.02KB
types (icon-key-migration.js) 4.26KB 1.63KB
types (index.js) 4.74KB 2.25KB
types (layout.js) 0.20KB 0.18KB
types (managed-by.js) 0.19KB 0.18KB
types (mobile.js) 5.00KB 2.39KB
types (navigation.js) 0.20KB 0.18KB
types (objectql.js) 0.20KB 0.18KB
types (overlay.js) 0.20KB 0.18KB
types (permissions.js) 0.20KB 0.18KB
types (plugin-scope.js) 0.20KB 0.18KB
types (record-components.js) 0.20KB 0.19KB
types (record-semantics.js) 1.28KB 0.67KB
types (registry.js) 0.20KB 0.18KB
types (reports.js) 0.20KB 0.18KB
types (select-option.js) 0.20KB 0.19KB
types (spec-report.js) 5.05KB 1.93KB
types (spec-ui-namespace.js) 0.20KB 0.19KB
types (strict-authoring-face.js) 17.15KB 6.32KB
types (system-fields.js) 3.33KB 1.54KB
types (theme.js) 6.28KB 2.87KB
types (ui-action.js) 8.11KB 3.32KB
types (views.js) 0.20KB 0.18KB
types (widget.js) 0.20KB 0.18KB

Size Limits

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: a966cf01edd6ff0154441e41382d5364720b2dd4

Rendered by an isolated review subagent spawned by the domain:ui#4 seat; its served tier was checked against its transcript stamps (97 of 97 model stamps at the review tier). Adopted by this seat.

① Derived judgments

  • (a) The withhold: one decision point, the objectui#10477 spelling. At head, packages/plugin-list/src/ListView.tsx declares const searchlessViewOnScreen = currentView === 'tree' || currentView === 'chart' directly under datasetChartOnScreen (the const PR objectui#10477 added), and exactly two readers consume it: showSearch: ua?.search !== false && !searchlessViewOnScreen inside the toolbarFlags memo, whose dependency list now names the primitive, and the derived const searchTerm = searchlessViewOnScreen ? '' : searchInput. Both toolbar render sites (the separator and the popover trigger) read toolbarFlags.showSearch; the only other showSearch in the file is the pre-existing showSearch: false child-node prop in baseProps. The chart half keys on the view kind alone, so both the object-bound and the dataset-bound binding are withheld, which objectui#10477 had not done (its showSearch stayed ua?.search !== false). The applied-term half follows from the ruling rather than exceeding it: with the control withheld, a restored or carried term would go on narrowing ListView's own fetch (the record-count bar reads that fetch's rows, and the export sends search: searchTerm) with nothing on screen to show or clear it, the same defect inverted (A4). It is documented in the changeset and pinned. One observation for the seat, not a breach: objectui#10477 did not gate the applied Filter on a dataset chart (its own comment names the record-count bar as the effective filter's only reader), so main now carries an asymmetry where a restored Filter still narrows the count bar on a dataset chart while a restored Search does not. That is outside this card.
  • (b) The kept term: sound. The typed or restored value lives in searchInput; handleSearchChange is the only writer of setSearchInput and the only caller of onSearchChange, and no effect calls either on a view change. Every reader in ListView.tsx reads the derived searchTerm: the $search on the fetch, the client-side filter for inline schema.data, the fetch dependency list, the pageResetSignature, ganttSearchTerm, the export's search and its dependency list, the trigger's active keyword, the popover input, and the empty-state hasActiveQuery. So on tree and chart nothing narrows, the empty-state copy does not claim a search, and on a switch back the same value applies again with the keyword visible on the trigger. ListViewHandle exposes only refresh; there is no URL sync or analytics reader in the file. The old name setSearchTerm survives nowhere in plugin-list at head (the three remaining hits are plugin-grid's own unrelated state). App-shell's ObjectView writes storage only through onSearchChange (keyed per user, object and view id) and restores through initialSearchTerm, so a term stored before this change on a tree view is restored, held, and not applied; nothing is dropped from storage. One consequence, stated in the changeset: on tree or chart a held term cannot be cleared until the user returns to a view that offers Search.
  • (c) Other views unchanged (A5). The source diff touches only the new const and its comment, the showSearch line and the memo deps, the state rename and derived term, handleSearchChange, and one comment line above ganttSearchTerm. For grid, kanban, calendar, gallery, timeline, map and gantt, searchlessViewOnScreen is false, so searchTerm equals searchInput and showSearch equals ua?.search !== false, byte-for-byte today's behaviour. Six of the seven are pinned present in the new file, grid is the CONTROL twice.
  • (d) The pins are real. The new file holds 17 cases: 1 grid CONTROL for presence, 3 absence cases (tree, object-bound chart, dataset-bound chart), 6 presence cases (kanban, calendar, gallery, timeline, gantt, map), 2 userActions.search: true cases, 1 grid CONTROL for a restored term reaching the fetch and the trigger, 3 restored-term cases asserting $search undefined on every host fetch after at least one fetch, and 1 switch case. The reverse-proof legs are exactly what that list predicts: both halves removed reddens the 3 absence, 2 true, 3 restored-term and switch cases (9) and leaves the 2 CONTROLs and 6 presence cases green (8); visibility only reddens the 3 absence, 2 true and switch cases (6); applied term only reddens the 3 restored-term cases and the switch case (4). None is vacuous: every case first awaits its view's stand-in, the absence cases assert a testid the CONTROL proves exists, the restored-term cases demand a fetch before asserting on it, and the switch case asserts the term reaches the fetch on grid, leaves it on tree and on chart, returns on grid with the keyword on the trigger, and that onSearchChange was called exactly once with needle. The true pair repeats the absence assertion under an authored key; it pins the fold (A3), which is its stated purpose. I did not re-run the suite (rule); CI Test shards 1 to 8 are green on the head.
  • (e) Changeset truth. .changeset/10326-toolbar-search-withheld-tree-chart.md: every sentence holds at head. The tree's own find in packages/plugin-tree/src/ObjectTree.tsx sends $filter, $top and $expand and no $search; packages/plugin-charts/src/ObjectChart.tsx calls ds.aggregate and ds.queryDataset with no search parameter; the control is not rendered on either view whatever the chart binding; an authored userActions.search: true folds to the default (normalize-list-view.ts maps legacy showSearch to userActions.search, and the flag reads !== false); the seven other views keep it; a set term is not applied to the fetch or the export on those views; the term is kept and onSearchChange is not called for the switch. No line-address citation. One precision note: the defect paragraph is scoped by its antecedent (views that draw what they query for themselves); the unauthored inline-data tree, where the term did narrow drawn rows, sits outside that scope and is covered by the bullets, which are unconditional. The corrected .changeset/10250-self-querying-views-toolbar-state.md: frontmatter sha256 50217e32b34f68c29d9a73407eebbff61cc63d5cbc605ffb722af4c0dbc12876 on both the base blob and the head blob, byte-identical; the corrected last paragraph is true at head (toolbar state still reaches no dataset-chart query; the Search box still reaches no tree or chart query; objectui#10326 stops offering it there); the correction was forced, because the old sentence presupposed a Search box on tree and chart that head no longer offers and would have published that presupposition into the CHANGELOG. The seven pending changesets the claims gate flagged (7070, 7499, 7773, 7780, 8990, list-user-actions-collision-5398, listview-comment-pair-4559) were re-read: none says anything about the toolbar Search; none is falsified. My own grep finds an eighth naming ListView.tsx, gantt-owns-data-comment-7222.md, which the gate skips by design (empty frontmatter, never publishes); it is about ganttOwnsData and is not falsified either. .changeset/10327-dataset-chart-view-filter.md is still pending, Filter-only, still true. The wider mentions (8317's userActions.search !== false fallback quote, 7924's legacy showSearch mapping table, 8254, 8970) are all still true.
  • (f) Clause-② no holds. packages/plugin-list/package.json and the package index are untouched; the export lines of ListView.tsx are identical between the merge-base and head; ListViewProps and ListViewHandle are not in the diff; no @object-ui/types file is touched. No exported symbol, type or accept set moves.
  • (g) The seat's answers. Q1 A is right. Who loses what: a list view with an inline data block, no objectName and no data source, drawn as a tree. That shape is authorable (the TS face has objectName? optional under a record-source presence rule) but authored nowhere in this tree: at head, viewType set to tree occurs only in two test files, none in examples, docs or apps; the console's ObjectView sets objectName: objectDef.name on the schema and passes dataSource={ds}, so every console tree queries for itself. On that unauthored shape, ListView hands its client-side-filtered rows down as data and ObjectTree draws them, so the term used to narrow the rows while cutting the hierarchy, the very cost the triage named. Option B would make ListView mirror ObjectTree's provider precedence, a second reading that can drift, for a configuration nobody authors. Q2 A is right: true is the default, a grid that may switch to tree or chart legitimately carries userActions.search and searchableFields, a refusal would have to read allowedVisualizations and widen ListViewSchema (a decision, its own card), and objectui#10477 added a refusal only for the view filter key, leaving userActions.filter: true on a dataset chart accepted and unhonoured in the same shape.
  • (h) The PR body. Every factual sentence checked holds at head: the const placement and spelling, both readers, the reader list, the 17 cases with a stand-in per view node type and an await before every assertion, A1 through A5, the reverse-proof table, the changeset facts, the claims-gate count of 7, the acceptance notes (no authored tree list view; the console binds objectName and a data source; searchableFields read by nothing on a tree-only or chart-only view; the README's Integrated Search bullet still true in general). Fixes #10326 is the only closing keyword. No line-address citation. No model or version identifier in the title or body. The gate counts in the Gates section (test totals, lint warning count, the tsc runs) were not re-run here per the rules; the head's CI is the independent signal. The sentence about the wider grep names only 8317 where three other pending bodies also mention a search box or showSearch; none is falsified, so the sentence is incomplete, not false.

② Semver level

patch on @object-ui/plugin-list is right. objectui AGENTS.md 版本号策略 at head: 「minor/patch 独立演进 —— objectstack 没动时不必跟发;objectui 自己的改动照常用 changeset 推进」 and 「changeset 里不要声明 major …… objectui 自身的破坏性变更也标 minor(在正文里写清 breaking 语义即可)」, mechanically enforced by scripts/check-changeset-no-major.mjs. The rule constrains only major (never), and reserves minor for objectui's own breaking changes. This change is a bug card: no exported symbol, type or accept set moves (f), and the control it withholds changed nothing drawn on every authored configuration. Precedent is split and both sides are within the rule: objectui#10250 declared plugin-list: patch for the same toolbar family; objectui#10327 declared plugin-list: minor but paired it with a new @object-ui/types refusal in the same changeset. Nothing here narrows a published face, so patch is the honest level. Not major, and check-changeset-no-major is green on the head.

③ Boundary flags

  • Commits on the branch above the merge-base cc139d820fdeade7dcf5931f7430dfb5c31f2f2a: faa70a9335e191da007bf52c744072e0313b404d (fix), 912cce3ba739367306c50a9d499e79e0766d2d29 (changesets), a966cf01edd6ff0154441e41382d5364720b2dd4 (test typing). No model identifier in any message, the title or the body; the trailers carry a product co-author line and a session URL only.
  • File surface, 4 files, exactly the claim plus the 10250 correction: packages/plugin-list/src/ListView.tsx modified (+37 −6); packages/plugin-list/src/__tests__/ListView.searchWithheldTreeChart-10326.test.tsx added (+249); .changeset/10326-toolbar-search-withheld-tree-chart.md added (+21); .changeset/10250-self-querying-views-toolbar-state.md modified (+3 −2, body only). No README, types or plugin-tree/plugin-charts file is touched.
  • Serial: 10 open PRs at read time. None touches the four files. PR 8941 touches packages/plugin-list/package.json (a dependency bump, not a claimed file). PR 5400 (changeset-release/main, 1732 files, all pages scanned) touches none of the four. main has not moved on any of the four files since the fork point.
  • mergeable: true, mergeable_state: clean (read unknown at 05:57Z and 06:04Z while GitHub computed it; clean at 06:05Z). The PR is still a draft.
  • Check-runs on a966cf01edd6ff0154441e41382d5364720b2dd4, polled in the foreground every 60s from 05:57Z until all completed at 06:05Z (7 reads, inside the 20-minute bound): 43 runs, 40 success, 3 skipped, 0 failure, 0 in progress. The three non-success runs are dependabot, Test (coverage) and Test (coverage shard ${{ matrix.shard }}/4), all skipped; the merged PR objectui#10477 head shows the same three skips, so that is the normal PR shape.
  • Reviewer note: my first three read-only git invocations used a directory change into the checkout before I switched to git -C for every later call; nothing was written to either checkout.

Implemented-by: claude/issue-10326-withhold-toolbar-search-tree-chart
Reviewed-by: session_01BP8CMtACxTdLjqR6rhd33C

VERDICT: PASS


Generated by Claude Code

@os-litant
os-litant marked this pull request as ready for review September 25, 2026 06:12
@os-litant
os-litant added this pull request to the merge queue Sep 25, 2026
Merged via the queue into main with commit 59636b3 Sep 25, 2026
45 checks passed
@os-litant
os-litant deleted the claude/issue-10326-withhold-toolbar-search-tree-chart branch September 25, 2026 06:24
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.

finding(plugin-list/plugin-tree/plugin-charts): the toolbar Search is offered on tree and chart list views but reaches neither view's own query

2 participants