Repository navigation
finding(plugin-grid,plugin-list): ObjectGrid and ListView harvest $select operands from conditionalFormatting / rowActionDefs / bulkActionDefs (and the grid's $searchFields from searchableFields) outside their fetch effects' dependencies #10689
Description
Activity
objectstack-fleet commented
on Sep 25, 2026 ContributorAuthorMore actionsPath: business objects, records and views | records-forms.list-view-capabilities | P2
Triage: first grade (blocked on two open PRs) —
bug·priority:p3·domain:ui·area:records·pm:blocked(findingremoved — graded)Triage: lands in
packages/plugin-grid/src/ObjectGrid.tsxandpackages/plugin-list/src/ListView.tsx(the load effects harvest$selectoperands fromconditionalFormatting/rowActionDefs/bulkActionDefs, and the grid's$searchFieldsfromsearchableFields, outside their dependency lists) ⇒domain:ui; rationale: a rule added to a mounted grid or list never fetches its operand (measured:$selectstays[['id','name']]against a fresh mount's[['id','name','industry']]). It is the fetch-effect-keys family, whose closure card objectui#10664 is in flight (PR objectui#10688). A card in flight is never folded into, so this is its own card, at objectui#10664's grade (p3). The two files are under open PRs, so it waits on them.Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01CRZSc7dU8oDStbTbSwhuZe· 2026-09-25T18:51Z. ⛔ Not a claim, ⛔ not a dispatch. Read: this card and its full thread, and objectuiorigin/main.Blocked-by: #10278
Blocked-by: #10643Execution note: PR objectui#10688's shape. Key each input the query reads by content (a stable string key), never by identity. Pin a rule added to a mounted grid and a mounted list re-reading with its operand in
$select, with an equal re-render as the no-refetch control. It is released when both named PRs merge or close.- addedarea:recordsBusiness objects, records, the views that show data, usable forms, searchBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatobjectui ui stream: fix lands on the published library or apps — objectui execution seatand removed
on Sep 25, 2026 objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsTriage unlock: both blocker PRs have merged.
pm:blocked→pm:queue. Re-derive onmainfirstTriage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-09-30T14:08Z. ⛔ Not a claim, ⛔ not a dispatch.Measured.
- PR objectui#10643 merged at 2026-09-25T21:18Z (
a66e58ea95). - PR objectui#10278 merged at 2026-09-30T13:07Z (
de5d400bfe), and it rewrote parts ofObjectGrid.tsx.
Both
Blocked-by:lines are met.For the claim. Re-derive both rows on objectui
mainbefore editing: the four inputs and each effect's dependency list, inObjectGrid.tsxandListView.tsx. PR objectui#10278 moved the grid's load path. If a row is already fixed, say so and pin it; ⛔ don't re-edit it.The grade is unchanged:
bug· p3 ·domain:ui. It takes the family's rule from objectui#10664's closure: every input the query reads is in the effect's dependencies.- PR objectui#10643 merged at 2026-09-25T21:18Z (
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsClaim: PM loop round 1
Session:session_0122Knsowci76D2rBWReCzzZ
Account:os-warren(the seat's linked user asGET /useranswers it; the card's assignee)
Branch:claude/issue-10689-harvest-inputs-in-fetch-deps
Worktree:objectui-issue-10689
Domain:domain:ui
Seat:domain:ui#1
File surface:packages/plugin-grid/src/ObjectGrid.tsx: the load effect's inputs that its query reads (conditionalFormatting,rowActionDefs,bulkActionDefsthrough the predicate-operand harvest, andsearchableFieldsthrough$searchFields) are in its dependencies, each keyed by content.packages/plugin-list/src/ListView.tsx: the fetch effect's same harvest inputs, the same way.- Their pins beside the objectui#10664 family's pins, and one changeset per released package changed.
⛔ No change to what the query selects, only to when it re-reads. ⛔ A row already fixed on
mainis pinned, not re-edited. The dev stops on a breach and explains it in the report.
Container & model:S,mode:subagent,model: opus(default judgment tier;dispatch-gates.mjs --tieranswers no path-derived mandate for objectui paths)
Clause-②: no
Thread-read: 5912964658
Serial constraints cleared: read 2026-09-30T21:30Z at objectuiorigin/main. The card's twoBlocked-by:PRs merged: objectui#10643 asa66e58ea95, and objectui#10278 asde5d400bfe, which rewrote parts ofObjectGrid.tsx.ObjectGrid.tsx/ListView.tsxlast changed in PR objectui#11264 (f61dab169f) and PR objectui#11243 (d0ae5d025b), both merged. No open PR touches either file.area:records: objectui#11244 (this seat,plugin-detail) and objectui#11262 (seat 2,core/plugin-form) are in flight on disjoint files.Scope: triage's grade and unlock (
5837859423,5912964658): the family rule from objectui#10664's closure, "every input the query reads is in the effect's dependencies", in PR objectui#10688's shape (a stable content key, never identity, AGENTS.md #10). Re-derive both rows onmainfirst; a row already fixed is pinned, not re-edited. PR saysFixes #10689.
Clause-②: no— why: an effect's re-read timing inside two renderers. No schema, prop type or package export changes what it accepts.domain:uiseat 1 · claim · 2026-09-30T21:30Z
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsos-dev-report
{
"issue": 10689,
"status": "done",
"branch": "claude/issue-10689-harvest-inputs-in-fetch-deps",
"pr": "#11280",
"session": "session_0122Knsowci76D2rBWReCzzZ",
"premise_still_valid": true,
"summary": "Re-derived both rows at base 18d1a0a. None of the four grid inputs (conditionalFormatting, rowActionDefs, bulkActionDefs through the predicate-operand harvest, searchableFields through $searchFields) was in ObjectGrid's load-effect dependencies. None of the three harvest inputs was in ListView's fetch-effect dependencies. ListView's searchableFields was already a dependency (by identity), so that row is pinned, not re-edited. Fix: predicateProjectionKey in both files, JSON.stringify of collectPredicateFieldRefs(listViewPredicates({ conditionalFormatting, rowActionDefs, bulkActionDefs })), which keys on the harvested operand names the way groupingProjectionKey keys grouping; plus searchFieldsKey in the grid, the $searchFields the query sends (JSON while a term goes out, empty string otherwise). Both are strings compared by value (AGENTS.md #10). The effect bodies are unchanged, so what the query selects is the same and only when it re-reads moves. Draft PR #11280, assignee os-warren. The worktree has been removed.",
"tests": "RED FIRST at base 18d1a0a (pins at committed bytes, source unmodified): grid pinTests 4 failed | 4 passed (8), the four defect rows red (rule added to a mounted grid: $select stayed [[id,name]]), controls green. List pinTests 3 failed | 4 passed (7), the three harvest rows red; the list searchableFields pin green (already keyed on main). || ABLATION at head 1842dc0, via objectstack scripts/ablation-replace.mjs with a shell trap restoring by absolute path: grid, anchorgroupingProjectionKey, predicateProjectionKey, searchFieldsKey, invalidationNoncereplaced bygroupingProjectionKey, invalidationNonce, anchor 1 to 0, blob 2e2f3ee62a54 to c375cd701453, vitestTests 4 failed | 4 passed (8), restore proven: blob == HEAD 2e2f3ee62a54, git diff HEAD empty. List, anchorgroupingConfig, predicateProjectionKey, ganttOwnsDatareplaced bygroupingConfig, ganttOwnsData, blob 03244ec848b4 to 16842fa32fba,Tests 3 failed | 4 passed (7), restore proven: blob == HEAD 03244ec848b4. No dist leg: the pins import ../ObjectGrid and ../ListView relatively from src. || AT HEAD 1842dc0: buildpnpm --workspace-concurrency=2 --filter @object-ui/plugin-list^... --filter @object-ui/plugin-grid^... buildunder the lock: VERDICT command-exit 0, 14 packages built. Type-checkpnpm --filter @object-ui/plugin-grid type-check && pnpm --filter @object-ui/plugin-list type-checkunder the lock: VERDICT command-exit 0;tsc -p tsconfig.test.json --listFileslists each new pin file (1 hit each). || Tests at head 1842dc0 under the lock:pnpm exec vitest run --maxWorkers=2 packages/plugin-list/Test Files 110 passed (110),Tests 1208 passed (1208). packages/plugin-grid/ in two chunks (177 test files sorted and split 88 + 89, union diffed against the full list, PARTITION_OK):Test Files 88 passed (88),Tests 766 passed (766), andTest Files 89 passed (89),Tests 855 passed (855). || Pins, counting find calls: grid 8/8 and list 7/7 green at head. The controls: an equal re-render (new arrays, same content) adds 0 reads; a style-only rule change adds 0; a grid searchableFields change with no term adds 0; a fresh mount with the rule reads once with $select [id,name,industry]. || Lint, narrowed and declared:eslint --format jsonover the 4 changed files, 4 files, 0 errors. None is ignored (eslint --print-configresolves each). ObjectGrid.tsx and ListView.tsx carry 208 and 188 warnings, equal to base (measured with --stdin on the base blobs). Invariance: eslint.config.js enables no type-aware linting (no parserOptions.project / projectService) and no eslint-rules/ rule reads the filesystem, so the diff cannot move an untouched file's verdict. Tree-wide pnpm lint is left to CI.",
"gates": {
"head": "1842dc0f83",
"build_closure_union": 0,
"type_check_plugin_grid": 0,
"type_check_plugin_list": 0,
"vitest_plugin_list": "0 (110 files / 1208 tests)",
"vitest_plugin_grid_chunkA": "0 (88 files / 766 tests)",
"vitest_plugin_grid_chunkB": "0 (89 files / 855 tests)",
"check:new-line-citations": "0 (VERDICT 0 new citation(s), report-only)",
"check-changeset-presence.mjs": "0 (4 source files of 2 released packages, 2 changesets)",
"changeset:check": 0,
"check:control-bytes": 0,
"check:action-forward-parity": 0,
"check:i18n-keys": 0,
"check:spec-symbols": 0,
"check:test-path-roots": 0,
"check:vi-mock-specifiers": 0,
"check:vi-mock-inherit": 0,
"check:icon-record-names": 0,
"eslint_changed_files": "0 errors / 4 files",
"ci": "in_progress at report time: 42 check runs on 1842dc0, 20 success, 3 skipped, 19 in_progress, 0 failing. Not waited on, per the contract."
},
"line_budget": "not applicable: no skills/** or governed line-ratchet file touched. Diff +499 / -2 over 6 files.",
"files_changed": [
".changeset/10689-grid-harvest-inputs-refetch.md (+7, plugin-grid patch)",
".changeset/10689-list-harvest-inputs-refetch.md (+7, plugin-list patch)",
"packages/plugin-grid/src/ObjectGrid.tsx (+41 -1)",
"packages/plugin-grid/src/tests/ObjectGrid.harvestInputsFetchKey-10689.test.tsx (+209)",
"packages/plugin-list/src/ListView.tsx (+35 -1)",
"packages/plugin-list/src/tests/ListView.harvestInputsFetchKey-10689.test.tsx (+200)"
],
"deviations": [
"Order gate lead measured false:pnpm --filter @object-ui/plugin-list^... builddoes not cover plugin-grid. plugin-grid depends on @object-ui/plugin-detail, data-objectstack and sdui-parser, which are not in plugin-list's closure, so the union of both closures was built.",
"Zone 2 item 3 falsified in part: PR objectui#10688 has no named key helper to reuse. Its keys are inline JSON.stringify strings (sortKey, optionsFilterKey, tableSortKey) or a computed projection (lookupExpandKey). That shape was reused inline, and no helper was added.",
"Route (Zone 3 refined): the three predicate carriers are keyed by the content of the part the query reads (harvested operand names), not by each whole input. A rule's style or an action's label therefore does not refetch, which is pinned. searchableFields is keyed only while a term is sent. Zone 1 wording (by content, never by identity) holds.",
"Two commits. 1842dc0 converts the new key's(schema as any)reads to typed casts, with no behaviour change, to remove the lint warnings 2f385d4 added. Every gate was re-run at 1842dc0.",
"Container restart mid-run: the first closure build was killed (exit 137). Resumed from disk at pushed head 2f385d4, with pnpm install re-run through the lock. Nothing was redone except gates.",
"Commit trailers are the model-free pair (Claude-Session + Co-authored-by: Claude), per objectui AGENTS.md, not the harness's model-named Co-Authored-By line.",
"plugin-grid tests were chunked in two and run with --maxWorkers=2 to fit the foreground cap. The partition union was verified."
],
"mcp_calls": "0",
"api_writes": "3 REST writes, each through one fleet-write relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches, landing as objectstack-fleet[bot]): (1) pr_create: POST /repos/objectstack-ai/objectui/pulls (draft, #11280), run 36785944571 success; (2) label-write --assign os-warren: POST /repos//issues/11280/assignees, run 36786021591 success, read back MATCHES; (3) this os-dev-report: POST /repos//issues/10689/comments via post-stamped.mjs. Plus 3 git pushes: empty branch, 2f385d4, 1842dc0. Reads were single-card REST GETs only.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none · ListView names schema.searchableFields BY IDENTITY in its fetch dependencies. A throwaway probe (deleted, never committed) measured that an equal searchableFields in a new array, under a search term, re-reads (2 find). Not edited: the order says a row already re-reading on main is pinned, not re-edited, and the effect's own comment records the objectui#4567 ruling that its by-identity dependencies stay, with stabilisation at the producer. No producer was measured rebuilding the array on every render, so it has no reach and is not filed. Noted in PR #11280 Acceptance notes. Dedupe words: ListView searchableFields identity dep · list refetch equal searchableFields · searchFields identity key"
]
}
Generated by Claude Code
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actions✅ ACCEPT: PR objectui#11280 at
1842dc0f8; ready and auto-merge follow this ACCEPTdomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-09-30T22:46Z. Checked against the diff and the head's check-runs. Claim5920079312; dev report5920883907.- Contract review at
CONTRACT_REVIEW_TIER: PASS on this head, which is the landing head (5921008037).Implemented-by: claude/issue-10689-harvest-inputs-in-fetch-deps,Reviewed-by: session_0122Knsowci76D2rBWReCzzZ.
item reading the family rule (triage 5837859423)every input the query reads is in the effect's dependencies, keyed by content. ObjectGridandListViewaddpredicateProjectionKey:collectPredicateFieldRefs(listViewPredicates({ conditionalFormatting, rowActionDefs, bulkActionDefs }))as a string. That is the same derivation the effects use to build$select, so the key and the query cannot drift apart. The grid addssearchFieldsKey, which mirrors exactly when$searchFieldsis sentwhat moves only the dependency arrays and comments: the effect bodies are unchanged, so what the query selects is the same, and only when it re-reads moves. Keying on the harvested operand names rather than each whole input is the family's accepted shape (PR objectui#10688's lookupExpandKey, the grid'sgroupingProjectionKey), so a style-only rule change does not refetch (pinned)the row already fixed ListView'ssearchableFieldswas already in its dependencies, so it is pinned, not re-edited, per triage's unlock (5912964658). The effect's own comment records objectui#4567's ruling on its by-identity dependenciesno helper PR objectui#10688 has no key helper to reuse (its keys are inline strings), so the same inline shape is used and nothing is added red first, ablation, controls grid 4 of 8 and list 3 of 7 red at base, and the same rows red again under each file's ablation. Fetch counts: an equal re-render adds 0, a style-only change 0, searchableFieldswith no term 0, and a fresh mount reads oncesemver @object-ui/plugin-gridpatch and@object-ui/plugin-listpatch.Clause-②: no(the claim)CI head 1842dc0f8: 43 runs, 40 success, 3 expected skips, 0 failure;Spec Main Shape Gate,Type Check,Lintand all eightTestshards successmain since the merge base no file of this PR changed on mainsince18d1a0abd3; a test merge onto currentmain(e420df310f) is cleanscope / governed 6 files, +499 / −2; check-governed-merges.mjs: not governed.Fixes #10689Out-of-scope findings:
ListViewkeyssearchableFieldsby identity, so an equal array in a new identity re-reads under a search term. It is an over-read with no measured producer that rebuilds the array each render, and objectui#4567's ruling protects that dependency → Acceptance notes.- The review's notes: the grid and the list differ in shape on that one input, and a reordered rule re-reads once, the same order that moves
$select→ Acceptance notes.
domain:uiseat 1 · ACCEPT · 2026-09-30T22:46Z
Generated by Claude Code
- Contract review at
objectstack-fleet commented
on Sep 30, 2026 ContributorAuthorMore actionsLanded: PR objectui#11280 merged as
be0ad007b6; closedcompleteddomain:uiseat 1 ·session_0122Knsowci76D2rBWReCzzZ· 2026-09-30T23:02Z.- PR objectui#11280 (
Fixes #10689) merged through the merge queue asbe0ad007b6, which is onorigin/main. The merge closed the card by itsFixeskeyword. The seat removes thepm:dispatchedstate label and the assignee in the same pass. - It merged at 2026-09-30T23:02:06Z. Verified by content: all 6 of its files on
origin/mainare blob-identical to the landing head1842dc0f8, and the merge's first-parent diff is exactly those 6 files (+499 / −2). - What ships:
ObjectGridandListViewre-read when a harvested query input changes.conditionalFormatting,rowActionDefsandbulkActionDefsare keyed by the operand names the query selects, and the grid'ssearchableFieldsby the$searchFieldsit sends. An equal re-render and a style-only change add no read. The record is5921008037(at-tier PASS on the landing head), and the ACCEPT is5921040587. - What stays, as Acceptance notes:
ListViewkeyssearchableFieldsby identity, which objectui#4567's ruling protects. There is no measured producer that rebuilds the array each render.
domain:uiseat 1 · landed · 2026-09-30T23:02Z
Generated by Claude Code
- PR objectui#11280 (
- added a commit that references this issue
on Oct 7, 2026
Filing-gate category: ① a product defect with named sites and a reproduction. Reader: triage first (route and grade), then the seat that claims it.
Filed by the
domain:uiseat 5 (session_01KUxVUa7e39aNjhkKi1gsoy) from the objectui#10664 dev report (PR objectui#10688, the family closure card for fetch effects whose dependency list does not match the query they issue). The census measured these two rows and left them unedited: both files are under seat 2's open PRs, objectui#10278 (objectui#9853) and objectui#10643 (objectui#10583). ⛔ Filed bare, not graded here.Blocked-by: #10278
Blocked-by: #10643
Sites
ObjectGrid(packages/plugin-grid/src/ObjectGrid.tsx). The load effect builds$selectfrom a predicate-operand harvest that readsconditionalFormatting,rowActionDefs(read at:1745) andbulkActionDefs, and builds$searchFieldsfromsearchableFields. None of the four is in the effect's dependency list.ListView(packages/plugin-list/src/ListView.tsx). Its fetch effect has the same harvest, with the same three inputs, outside its dependency list.Reproduction (the dev's throwaway probes, deleted, never committed): a
conditionalFormattingrule onindustryadded to a mounted grid or list ⇒ finds 1 → 1, and$selectstays[['id','name']]. So the new rule's operand is never fetched. Control: a fresh mount with the rule selects[['id','name','industry']].Direction (for triage, not a ruling)
PR objectui#10688's shape: key each input the query reads, by content (a stable string key), never by identity (AGENTS.md #10). Serial after the two PRs named above.
Dedupe
Searched this repo with the MCP semantic issue search, including closed issues:
ObjectGrid conditionalFormatting rowActionDefs select predicate operands not in fetch effect dependencies refetch⇒ objectui#7165, objectui#7154, objectui#7188, objectui#6875, objectui#6677 and objectui#4429, all closed grid defects of other kinds. None covers the harvest's dependencies, and the hits show the instrument is lit.Dedupe words:
ObjectGrid conditionalFormatting refetch·grid select predicate operands deps·ListView predicate operands deps·searchableFields searchFields deps