Repository navigation
fix: resolve context tokens in every directly authored data node's own filter (objectui#10666) - #10729
Conversation
…d data node (objectui#10666) One enumeration table: the eleven census nodes (calendar, map and tree on both query paths; number on both the aggregate and the find fallback) plus object-gallery, object-grid and list-view as regression rows. Each row renders the node through the real SchemaRenderer and its package registration, under FilterScopeProvider, over a recording data source. Committed RED against the unfixed nodes: 38 failed, 34 passed. Co-Authored-By: Claude <noreply@anthropic.com>
…n filter (objectui#10666) @object-ui/react exports useResolvedFilter beside useFilterScope: the hold plugin-list used privately (useResolvedAuthoredFilter), moved here so one copy serves every node. plugin-list's module is deleted and its two call sites import the shared hook. The one addition: a filter the resolver leaves unchanged is handed out as authored, so a token-free filter reaches the query by identity, as before. The eleven census nodes read their own filter through the hold, and feed the query and the fetch effect's key from the held value: object-calendar, object-map and object-tree (object and inline paths), object-gantt, object-kanban, object-timeline, record:related_list, record:line_items, and the repeater, number and record_picker elements. @object-ui/types: the filter docblocks that said "forwarded verbatim as $filter" now say context tokens are resolved first. Prose only. Co-Authored-By: Claude <noreply@anthropic.com>
…ontext-token hold (objectui#10666) The objectui#8174 source pin looked for the literal `$filter: schema.filter`. Both renderers now read `schema.filter` through useResolvedFilter and send the held value, so the pin reads both halves of that chain. Co-Authored-By: Claude <noreply@anthropic.com>
…lter-token-sweep # Conflicts: # packages/plugin-detail/src/RelatedList.tsx
|
changeset-claim-re-read
|
✅ ACCEPTED — PR objectui#10729 at
|
✅ 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
|
Fixes #10666
Clause-②: yes —
@object-ui/reactgains one hook export,useResolvedFilter. It is additive, and existing callers are unchanged.This is the sweep half of the family closure card, under seat answer B (comment 5838380906) and the re-scoped claim 5838679906. PR objectui#10705 fixed
object-gallery(part 1). This PR fixes the eleven remaining census nodes and closes the card.What changed
One shared hold, exported from
@object-ui/react.useResolvedFilter(filter, scope)sits besideuseFilterScope. It isplugin-list'suseResolvedAuthoredFiltertogether with its comparatorisSameAuthoredFilter, moved intopackages/react/src/hooks/useResolvedFilter.ts(git records a rename). The plugin-list module is deleted, andListViewandObjectGalleryimport the shared hook. The hold, its key (the authored filter compared by structure, plus the scope's three members read one by one) and the comparator are unchanged.@object-ui/reactalready depended on@object-ui/core, so it can importresolveFilterPlaceholdersdirectly.$filterby identity, as it did before.packages/react/README.mdhas a new "useFilterScope / useResolvedFilter" section.check:readme-exports --listjudges both namesreal.The eleven nodes. Each node now reads its own filter through the hold. The query and the fetch effect's key read the held value, never the raw filter.
object-calendarplugin-calendar/src/ObjectCalendar.tsxfind, the inlineValueDataSourcefind, and the effect depsobject-ganttplugin-gantt/src/ObjectGantt.tsxreload'sfind, and both dependency listsobject-kanbanplugin-kanban/src/ObjectKanban.tsxfind, and the effect depsobject-mapplugin-map/src/ObjectMap.tsxfinds, the committed-query key, and the effect depsobject-timelineplugin-timeline/src/ObjectTimeline.tsxfind;filterKey(already a content key) is now taken over the held valueobject-treeplugin-tree/src/ObjectTree.tsxfinds, and the effect depsrecord:related_listplugin-detail/src/RelatedList.tsxtoFilterNodeSafelyandfilterKey, before the merge with the parent scoperecord:line_itemsplugin-form/src/LineItemsPanel.tsxtoFilterNodeSafelyandfilterKey, before the merge with the parent scope (no save-in-flight hunk is touched)element:repeatercomponents/src/renderers/basic/data-list.tsxquery.$filterandfilterKeyelement:numbercomponents/src/renderers/basic/elements.tsxaggregatefilter, thefindfallback's$filter, andfilterKeyelement:record_pickercomponents/src/renderers/basic/record-picker.tsxcomposed?.filter ?? props.filter, then$filterandfilterKey@object-ui/types, prose only. Thefilterdocblocks onObjectMapSchema,ObjectTreeSchema,ObjectGanttSchema,ObjectCalendarSchema,ObjectKanbanSchema,ObjectChartSchemaandObjectGallerySchemaused to say "forwarded verbatim as$filter". They now say the context tokens are resolved first. The five matching zod.describe()strings and one import comment were changed the same way. No type changed.Changeset:
.changeset/10666-filter-token-sweep.md:@object-ui/reactminor,@object-ui/typespatch, and each node package patch (plugin-calendar,plugin-gantt,plugin-kanban,plugin-map,plugin-timeline,plugin-tree,plugin-detail,plugin-form,plugin-list,components). None is major.The pin table: red, then green
apps/console/src/__tests__/filterContextTokensSweep-10666.test.tsxis onedescribe.eachtable with 18 rows and 4 cells per row. It lives inapps/consolebecause that is the one package that declares all nine node packages (the objectui#4409 dependency-direction method).Each row renders the directly authored node through the real
SchemaRendererand its package registration, underFilterScopeProvider(usr_42,org_7), over a data source that records every query. For the inline paths, a spy onValueDataSource.prototype.findrecords the queries instead.The four cells are:
{current_user_id}goes out resolved, and the whole query equals the query the literal id sends.{current_org_id}goes out resolved.The pins-only commit
5ae7e1159dran against the unfixed code: 38 failed, 34 passed (exit 1). The fix is69d68598df.5ae7e1159dAt head
507214a742(after mergingorigin/main), the table passed 72/72 in one run with the hook test, both objectui#10607 pins, the gallery pin, all four objectui#10657 masked-column pins, the objectui#8176, #7711 and #9136 member pins, and the machine-locale census: 13 files, 172 tests passed, exit 0.Hook unit test.
packages/react/src/hooks/__tests__/useResolvedFilter.test.tsxhas 10 cells:{now}is held;Ablation on the shared hook
ablation-replace.mjsreplacedreturn held.resolved as T;with the bare per-renderreturn resolveFilterPlaceholders(authored, scope) as T;.372374462041to8468e2e161e7.object-timelineUSER went red once.372374462041, equal to HEAD, andgit diff HEADwas empty.@object-ui/*resolves tosrc/through the root vitest alias, so this ablation has nodist/leg.The fork (flagged for the seat)
The existing member pins read
$filterBY IDENTITY: objectui#8176 onobject-kanban(3 cells), objectui#7711 onobject-calendar(3 cells) and objectui#9136 onobject-tree(1 cell).resolveFilterPlaceholderscopies arrays, so with the hold moved exactly as it was, those 7 cells went red on a token-free filter.toEqual. That would weaken pins whose authors chosetoBeon purpose.The one existing test that was edited is the objectui#8174 SOURCE pin in
packages/types. It looked for the literal text$filter: schema.filterin the kanban and calendar sources, and it now reads both halves of the new chain:useResolvedFilter(schema.filter, filterScope)and$filter: queryFilter.Verification at head
507214a742Heavy runs went through the shared verify lock.
turbo run buildover the dependency closure of the 12 touched packages: 14 tasks successful.pnpm --filterover the 12 touched packages,run type-check: 12 Done, exit 0.react'stsconfig.test.json --listFilesOnlylists the new hook test once.plugin-detail(214 files passed and 1 skipped; 2102 tests passed and 8 skipped);plugin-formplustypes(368 files; 6747 tests passed and 1 skipped); andcomponentsin two halves (renderers, notifications and ui: 135 files, 1289 tests passed and 10 skipped;__tests__: 177 files passed and 1 skipped, 1767 tests passed and 7 skipped).plugin-calendar,plugin-ganttandplugin-timeline, in one run withtypesand the pins.react,plugin-kanban,plugin-map,plugin-treeandplugin-list. Neither merge changed a source file in these packages.check:changeset-claims(report-only),check:pending-changeset-literals,check:new-line-citations(0 new),check:control-bytes,check-changeset-presence(20 source files of 13 released packages, 1 changeset),changeset:check(no major),check-changeset-overwrite(1 added, 0 modified),check:vi-mock-specifiers,check:vi-mock-inherit,check:vi-mock-override-shape,check:phantom-deps,check:self-import,check:test-path-roots,check:unreferenced-sources,check-type-check-coverage,check:side-effects-array, andcheck-governed-queue-guard --test(NOT GOVERNED, 22 paths).check:readme-exports --listjudgesuseFilterScopeanduseResolvedFilterinpackages/react/README.mdreal. The whole-tree run exits non-zero only because 22 packages were not built in this worktree (0 fabricated after the rebuild), so the whole-tree verdict is NOT MEASURED.record-picker.tsx, which went from 11 to 10. The new files have 0, except the pin table, which has 1.check:eager-closureandcheck:sdui-registration-pins(both need a console production build); repo-widepnpm lintand the full test farm, which belong to CI.Merges
Two merges of
origin/main:bd254f7637: one import-line conflict inRelatedList.tsx, resolved as a union. It keeps objectui#10690'suseSettledSchemaand addsuseFilterScopeanduseResolvedFilter.507214a742: brings in objectui#10657 (masked columns). It merged with no conflict, and both behaviours hold:RelatedList'smaskedcolumn stamping, and its held resolved scope filter. Draft PR objectui#10650's save-in-flight hunks inLineItemsPanel.tsxare not touched. This PR changes only thefilterread there.Acceptance notes
list-view,object-gridandobject-view. Before, the server resolved them in the tenant's time zone.plugin-grid'sObjectGrid.tsxstill holds its own copy of the hold and the comparator (useResolvedGridFilters, from PR objectui#10656). The claim did not cover that file. It could becomeuseResolvedFilter({ filter, defaultFilters }, scope), with the same behaviour.filterinput both renderers read #8186 describe the renderers as "loweringschema.filteronto$filter"; the objectui#8176 pin's header says "nothing between the schema and that call reads a member key"; andcontent/docs/api/schema-reference.mdstill carries "forwarded verbatim as$filter" in itsObjectKanbanSchematable. None of these was edited here.{current_user_id}for a signed-in request, so there this is defence in depth.The session behind this change is
https://claude.ai/code/session_01D486C1axmXnrkJMNUfz2eb(domain:ui seat 3, dispatched dev).Generated by Claude Code