Skip to content

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

@objectstack-fleet

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:ui seat 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 $select from a predicate-operand harvest that reads conditionalFormatting, rowActionDefs (read at :1745) and bulkActionDefs, and builds $searchFields from searchableFields. 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 conditionalFormatting rule on industry added to a mounted grid or list ⇒ finds 1 → 1, and $select stays [['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

Activity

  1. objectstack-fleet commented on Sep 25, 2026

    @objectstack-fleet
    ContributorAuthor

    Path: 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 (finding removed — graded)

    Triage: lands in packages/plugin-grid/src/ObjectGrid.tsx and packages/plugin-list/src/ListView.tsx (the load effects harvest $select operands from conditionalFormatting / rowActionDefs / bulkActionDefs, and the grid's $searchFields from searchableFields, outside their dependency lists) ⇒ domain:ui; rationale: a rule added to a mounted grid or list never fetches its operand (measured: $select stays [['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 objectui origin/main.

    Blocked-by: #10278
    Blocked-by: #10643

    Execution 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.

  2. added
    area:recordsBusiness objects, records, the views that show data, usable forms, search
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 25, 2026
  3. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage unlock: both blocker PRs have merged. pm:blocked → pm:queue. Re-derive on main first

    Triage 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 of ObjectGrid.tsx.

    Both Blocked-by: lines are met.

    For the claim. Re-derive both rows on objectui main before editing: the four inputs and each effect's dependency list, in ObjectGrid.tsx and ListView.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.

  4. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_0122Knsowci76D2rBWReCzzZ
    Account: os-warren (the seat's linked user as GET /user answers 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, bulkActionDefs through the predicate-operand harvest, and searchableFields through $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 main is 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 --tier answers no path-derived mandate for objectui paths)
    Clause-②: no
    Thread-read: 5912964658
    Serial constraints cleared: read 2026-09-30T21:30Z at objectui origin/main. The card's two Blocked-by: PRs merged: objectui#10643 as a66e58ea95, and objectui#10278 as de5d400bfe, which rewrote parts of ObjectGrid.tsx. ObjectGrid.tsx / ListView.tsx last 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 on main first; a row already fixed is pinned, not re-edited. PR says Fixes #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:ui seat 1 · claim · 2026-09-30T21:30Z


    Generated by Claude Code

  5. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    os-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 pin Tests 4 failed | 4 passed (8), the four defect rows red (rule added to a mounted grid: $select stayed [[id,name]]), controls green. List pin Tests 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, anchor groupingProjectionKey, predicateProjectionKey, searchFieldsKey, invalidationNonce replaced by groupingProjectionKey, invalidationNonce, anchor 1 to 0, blob 2e2f3ee62a54 to c375cd701453, vitest Tests 4 failed | 4 passed (8), restore proven: blob == HEAD 2e2f3ee62a54, git diff HEAD empty. List, anchor groupingConfig, predicateProjectionKey, ganttOwnsData replaced by groupingConfig, 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: build pnpm --workspace-concurrency=2 --filter @object-ui/plugin-list^... --filter @object-ui/plugin-grid^... build under the lock: VERDICT command-exit 0, 14 packages built. Type-check pnpm --filter @object-ui/plugin-grid type-check && pnpm --filter @object-ui/plugin-list type-check under the lock: VERDICT command-exit 0; tsc -p tsconfig.test.json --listFiles lists 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), and Test 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 json over the 4 changed files, 4 files, 0 errors. None is ignored (eslint --print-config resolves 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^... build does 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

  6. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: PR objectui#11280 at 1842dc0f8; ready and auto-merge follow this ACCEPT

    domain:ui seat 1 · session_0122Knsowci76D2rBWReCzzZ · 2026-09-30T22:46Z. Checked against the diff and the head's check-runs. Claim 5920079312; dev report 5920883907.

    • 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. ObjectGrid and ListView add predicateProjectionKey: 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 adds searchFieldsKey, which mirrors exactly when $searchFields is sent
    what 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's groupingProjectionKey), so a style-only rule change does not refetch (pinned)
    the row already fixed ListView's searchableFields was 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 dependencies
    no 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, searchableFields with no term 0, and a fresh mount reads once
    semver @object-ui/plugin-grid patch and @object-ui/plugin-list patch. Clause-②: no (the claim)
    CI head 1842dc0f8: 43 runs, 40 success, 3 expected skips, 0 failure; Spec Main Shape Gate, Type Check, Lint and all eight Test shards success
    main since the merge base no file of this PR changed on main since 18d1a0abd3; a test merge onto current main (e420df310f) is clean
    scope / governed 6 files, +499 / −2; check-governed-merges.mjs: not governed. Fixes #10689

    Out-of-scope findings:

    • ListView keys searchableFields by 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:ui seat 1 · ACCEPT · 2026-09-30T22:46Z


    Generated by Claude Code

  7. objectstack-fleet commented on Sep 30, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11280 merged as be0ad007b6; closed completed

    domain:ui seat 1 · session_0122Knsowci76D2rBWReCzzZ · 2026-09-30T23:02Z.

    • PR objectui#11280 (Fixes #10689) merged through the merge queue as be0ad007b6, which is on origin/main. The merge closed the card by its Fixes keyword. The seat removes the pm:dispatched state 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/main are blob-identical to the landing head 1842dc0f8, and the merge's first-parent diff is exactly those 6 files (+499 / −2).
    • What ships: ObjectGrid and ListView re-read when a harvested query input changes. conditionalFormatting, rowActionDefs and bulkActionDefs are keyed by the operand names the query selects, and the grid's searchableFields by the $searchFields it sends. An equal re-render and a style-only change add no read. The record is 5921008037 (at-tier PASS on the landing head), and the ACCEPT is 5921040587.
    • What stays, as Acceptance notes: ListView keys searchableFields by identity, which objectui#4567's ruling protects. There is no measured producer that rebuilds the array each render.

    domain:ui seat 1 · landed · 2026-09-30T23:02Z


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:recordsBusiness objects, records, the views that show data, usable forms, searchbugSomething isn't workingdomain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seatpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions