You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
ui predicate harvest: record.?x (CEL optional) is not harvested, and kanban/calendar/gantt queries may not take the harvest at all #29409
Filing gate: ① a measured gap in the view projection harvest, named as follow-up D.2 of the #29094 ruling (option A, maintainer 「同意」, ruling comment 6579342813). It does not block anything, so it is filed as p3.
Reader: domain:ui, ui/packages/core/src/utils/predicate-fields.ts and the views that build their own queries.
Dedupe (open AND closed): "harvest record.?" and "predicate-fields harvest kanban" → #28542 (open, cloud invitations page projection), #28960 (closed, docs). Neither covers these two gaps.
reach: a list or board view whose action visible / disabled reads a field the view does not show.
type: Bug
Business reason: the harvest is what keeps a sparse list row from hiding a button by accident. A predicate it does not read, or a view that does not use it, puts the author back on the has() guard without any sign that this happened.
What the code does on main
RECORD_REF = /\b(?:record|data)\.([A-Za-z_][A-Za-z0-9_]*)/g (ui/packages/core/src/utils/predicate-fields.ts line 91). The CEL optional spelling record.?status does not match it, so status is not harvested.
Make the regex accept record.?x and data.?x, and pin it.
Read the kanban, calendar and gantt views' queries. Wherever a view that renders row actions does not take the harvest, wire it in, or file the gap. Keep the field-level-security filter, because a harvest is not a read grant.
Claim: PM loop round 21 · 2026-11-02T15:31Z
Session: session_01QsR2jTLzhrs8dyC8QCdCKg (card thread, os-dev); dispatched by seat session session_01QsR2jV4dWohojjbTJTQq5m
Account: zhuangjianguo
Branch: claude/project-thread-vzj2zv (platform-assigned to the card thread; it can push only this branch)
Worktree: objectstack-issue-29409
Domain: domain:ui
Seat: domain:ui#3
File surface: ui/packages/core/src/utils/predicate-fields.ts, the kanban / calendar / gantt query-building files that need the harvest (one hunk each, named in the report), their tests, and a changeset. A view whose wiring is larger than a small change, or that would change a public prop or export, is reported, not edited (stop on breach; explain in the report)
Container & model: S–M, mode:thread, model: default tier (dispatch-gates --tier: no path-derived mandate), effort: high
Clause-②: no
Metadata-protocol: none
Responsibility: open — recorded at review
Thread-read: none
Premise: holds (at origin/main6fe99f86: predicate-fields.ts:91RECORD_REF matches record.x / data.x only, so record.?x is not harvested; harvest consumers are plugin-grid/src/objectGridRowLoad.ts and app-shell/src/views/RecordDetailView.tsx).
Serial constraints cleared: none. Follow-up D.2 of the #29094 ruling (6579342813), non-blocking.
{
"issue": 29409,
"status": "done",
"branch": "claude/project-thread-vzj2zv",
"pr": "https://github.com/objectstack-ai/objectstack/pull/29480",
"session": "session_01QsR2jTLzhrs8dyC8QCdCKg",
"premise_still_valid": true,
"head": "5e043f9a",
"summary": "RECORD_REF in ui/packages/core/src/utils/predicate-fields.ts now accepts an optional '?' after the record dot, so collectPredicateFieldRefs harvests the CEL optional selector record.?x / data.?x as field x beside record.x / data.x; every harvest consumer (objectGridRowLoad, ListView, RelatedList) picks it up unchanged, and the declared-field and FLS filters still apply. Item 2: kanban, calendar and gantt send no $select (whole-row fetches) and render no row/card actions with visible/disabled predicates, so nothing is wired; recorded as findings below.",
"files_changed": [
".changeset/29409-predicate-optional-field-harvest.md",
"ui/packages/core/src/utils/__tests__/predicate-fields.test.ts",
"ui/packages/core/src/utils/predicate-fields.ts"
],
"line_budget": "68 changed lines (+67/-1) over 3 files vs threshold 3000: under",
"tests": "592019e4: ui vitest packages/core/ 232 files passed, 4294 tests passed, 27 skipped (lock VERDICT command-exit 0); turbo type-check @object-ui/core 4/4 successful; 5e043f9a (after merging origin/main cafe8ee6, nothing under ui/packages/core changed): packages/core/src/utils/ 113 files, 2001 tests passed. Six new cases in predicate-fields.test.ts (33 to 39): five positive optional-selector cases, one of them through listViewPredicates, and one negative case with five spellings. Ablation via scripts/ablation-replace.mjs: RECORD_REF reverted to the main spelling, anchor 1 to 0, blob 7b0e2947 to fa1fda93; 5 failed / 34 passed (all five positive optional-selector cases red); restored blob == HEAD 7b0e2947, git diff HEAD empty. No dist rebuild needed: the test imports ../predicate-fields from source.",
"gates": "592019e4: dispatch-gates --commands derived 34 families; all 34 ran exit 0 (incl. node scripts/check-sdui-manifest.mjs exit 0 and its --self-test exit 0, and pnpm --dir ui exec vitest run scripts/ 169 files passed / 2 skipped, 4921 tests). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. ui check:new-line-citations: 0 new. Body gates against the PR body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-empty-changeset, check-partof-closing-keyword all exit 0. CI check-runs on #29480: in_progress at report time.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm",
"deviations": [
"Brief asked for a negative pin 'no harvest from strings'. Not implemented and not pinned: today the harvester reads record.x inside a CEL string literal, and the file's own asymmetry rule (an extra declared column costs bytes, a missing one fails the predicate closed) argues against masking literals, which would under-harvest on an unbalanced quote. Negatives pinned instead: record.? alone, record.?.x, data.?1st, myrecord.?x, record?.x. Told the seat before editing.",
"No view test added: no view was wired (see findings), so the per-view projection and FLS pins the brief asked for have no subject.",
"PR body says seven new cases and '7 个用例'; the count is six (33 to 39 cases). A corrected body is at /mnt/project-files/issue-29409/pr-body-seat.md (seat signature first) for the seat to apply; the dev cannot patch the body."
],
"mcp_calls": "0 — no GitHub MCP calls",
"api_writes": "2 — fleet-write relay pr_create (objectstack-ai/objectstack#29480) and this os-dev-report comment via post-stamped; plus 2 git pushes (not REST)",
"open_questions": [],
"security_findings": "none",
"out_of_scope_findings": [
"carrier: none · noted, not filed — kanban needs no harvest: ObjectKanban.tsx L949 query object and L877 lane-paging params carry $filter/$orderby/$top/$expand/$count/host search, no $select; no action visible/disabled evaluation in plugin-kanban/src.",
"carrier: none · noted, not filed — calendar needs no harvest: ObjectCalendar.tsx L940 dataSource.find carries $filter/$orderby/ceiling/$expand, L1022 recordAnchorQuery the anchor filter, no $select; no action predicate evaluation in plugin-calendar/src.",
"carrier: none · noted, not filed — gantt needs no harvest: ObjectGantt.tsx L1053 adapter.find carries $filter/$orderby/ceiling/$expand/search, no $select; no action predicate evaluation in plugin-gantt/src.",
"carrier: none · noted, not filed — bracket spellings record['x'] and CEL optional index record[?'x'] are not harvested (unchanged, outside this card)."
]
}
PR #29480 merged at 2026-11-02T16:43Z as 86d36e99. Fixes #29409 closed this card; pm:dispatched removed. Delivered: the view projection harvest reads the CEL optional-field spelling record.?x / data.?x beside the plain one, so an action predicate on a field the list does not show finds it on the row; FLS and declared-field filters unchanged. Kanban, calendar and gantt were read and need no wiring (none sends $select or evaluates action predicates; file:line in report 6581921518). Nothing left on this card.
Activity
objectstack-fleet commentedon Nov 2, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Path: none
Triage: p3 ·
pm:queue· 2026-11-02T15:00Z⛔ Not a claim.
record.?x, and kanban, calendar and gantt may not take the harvest at all. Follow-up D.2 of the skills(objectstack-formula): "every predicate reads a record total over declared fields" contradicts the canonical sparse action-binding rule (declared-fields.ts, #4953), so agents drop the has() guard a list-row action needs #29094 ruling; non-blocking, p3.Generated by Claude Code
objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Claim: PM loop round 21 · 2026-11-02T15:31Z
Session:
session_01QsR2jTLzhrs8dyC8QCdCKg(card thread, os-dev); dispatched by seat sessionsession_01QsR2jV4dWohojjbTJTQq5mAccount:
zhuangjianguoBranch:
claude/project-thread-vzj2zv(platform-assigned to the card thread; it can push only this branch)Worktree:
objectstack-issue-29409Domain:
domain:uiSeat:
domain:ui#3File surface:
ui/packages/core/src/utils/predicate-fields.ts, the kanban / calendar / gantt query-building files that need the harvest (one hunk each, named in the report), their tests, and a changeset. A view whose wiring is larger than a small change, or that would change a public prop or export, is reported, not edited (stop on breach; explain in the report)Container & model: S–M,
mode:thread,model: default tier (dispatch-gates --tier: no path-derived mandate),effort: highClause-②: no
Metadata-protocol: none
Responsibility:
open — recorded at reviewThread-read: none
Premise: holds (at
origin/main6fe99f86:predicate-fields.ts:91RECORD_REFmatchesrecord.x/data.xonly, sorecord.?xis not harvested; harvest consumers areplugin-grid/src/objectGridRowLoad.tsandapp-shell/src/views/RecordDetailView.tsx).Serial constraints cleared: none. Follow-up D.2 of the #29094 ruling (6579342813), non-blocking.
objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jTLzhrs8dyC8QCdCKg
os-dev-report
{ "issue": 29409, "status": "done", "branch": "claude/project-thread-vzj2zv", "pr": "https://github.com/objectstack-ai/objectstack/pull/29480", "session": "session_01QsR2jTLzhrs8dyC8QCdCKg", "premise_still_valid": true, "head": "5e043f9a", "summary": "RECORD_REF in ui/packages/core/src/utils/predicate-fields.ts now accepts an optional '?' after the record dot, so collectPredicateFieldRefs harvests the CEL optional selector record.?x / data.?x as field x beside record.x / data.x; every harvest consumer (objectGridRowLoad, ListView, RelatedList) picks it up unchanged, and the declared-field and FLS filters still apply. Item 2: kanban, calendar and gantt send no $select (whole-row fetches) and render no row/card actions with visible/disabled predicates, so nothing is wired; recorded as findings below.", "files_changed": [ ".changeset/29409-predicate-optional-field-harvest.md", "ui/packages/core/src/utils/__tests__/predicate-fields.test.ts", "ui/packages/core/src/utils/predicate-fields.ts" ], "line_budget": "68 changed lines (+67/-1) over 3 files vs threshold 3000: under", "tests": "592019e4: ui vitest packages/core/ 232 files passed, 4294 tests passed, 27 skipped (lock VERDICT command-exit 0); turbo type-check @object-ui/core 4/4 successful; 5e043f9a (after merging origin/main cafe8ee6, nothing under ui/packages/core changed): packages/core/src/utils/ 113 files, 2001 tests passed. Six new cases in predicate-fields.test.ts (33 to 39): five positive optional-selector cases, one of them through listViewPredicates, and one negative case with five spellings. Ablation via scripts/ablation-replace.mjs: RECORD_REF reverted to the main spelling, anchor 1 to 0, blob 7b0e2947 to fa1fda93; 5 failed / 34 passed (all five positive optional-selector cases red); restored blob == HEAD 7b0e2947, git diff HEAD empty. No dist rebuild needed: the test imports ../predicate-fields from source.", "gates": "592019e4: dispatch-gates --commands derived 34 families; all 34 ran exit 0 (incl. node scripts/check-sdui-manifest.mjs exit 0 and its --self-test exit 0, and pnpm --dir ui exec vitest run scripts/ 169 files passed / 2 skipped, 4921 tests). --ran: 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN. ui check:new-line-citations: 0 new. Body gates against the PR body: check-changeset-no-major, check-adr-0087-registration, check-closing-keyword-parity, check-empty-changeset, check-partof-closing-keyword all exit 0. CI check-runs on #29480: in_progress at report time.", "dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm", "deviations": [ "Brief asked for a negative pin 'no harvest from strings'. Not implemented and not pinned: today the harvester reads record.x inside a CEL string literal, and the file's own asymmetry rule (an extra declared column costs bytes, a missing one fails the predicate closed) argues against masking literals, which would under-harvest on an unbalanced quote. Negatives pinned instead: record.? alone, record.?.x, data.?1st, myrecord.?x, record?.x. Told the seat before editing.", "No view test added: no view was wired (see findings), so the per-view projection and FLS pins the brief asked for have no subject.", "PR body says seven new cases and '7 个用例'; the count is six (33 to 39 cases). A corrected body is at /mnt/project-files/issue-29409/pr-body-seat.md (seat signature first) for the seat to apply; the dev cannot patch the body." ], "mcp_calls": "0 — no GitHub MCP calls", "api_writes": "2 — fleet-write relay pr_create (objectstack-ai/objectstack#29480) and this os-dev-report comment via post-stamped; plus 2 git pushes (not REST)", "open_questions": [], "security_findings": "none", "out_of_scope_findings": [ "carrier: none · noted, not filed — kanban needs no harvest: ObjectKanban.tsx L949 query object and L877 lane-paging params carry $filter/$orderby/$top/$expand/$count/host search, no $select; no action visible/disabled evaluation in plugin-kanban/src.", "carrier: none · noted, not filed — calendar needs no harvest: ObjectCalendar.tsx L940 dataSource.find carries $filter/$orderby/ceiling/$expand, L1022 recordAnchorQuery the anchor filter, no $select; no action predicate evaluation in plugin-calendar/src.", "carrier: none · noted, not filed — gantt needs no harvest: ObjectGantt.tsx L1053 adapter.find carries $filter/$orderby/ceiling/$expand/search, no $select; no action predicate evaluation in plugin-gantt/src.", "carrier: none · noted, not filed — bracket spellings record['x'] and CEL optional index record[?'x'] are not harvested (unchanged, outside this card)." ] }objectstack-fleet commentedon Nov 2, 2026
domain:ui#3 · zhuangjianguo · session_01QsR2jV4dWohojjbTJTQq5m
Landed · 2026-11-02T16:44Z
PR #29480 merged at 2026-11-02T16:43Z as
86d36e99.Fixes #29409closed this card;pm:dispatchedremoved. Delivered: the view projection harvest reads the CEL optional-field spellingrecord.?x/data.?xbeside the plain one, so an action predicate on a field the list does not show finds it on the row; FLS and declared-field filters unchanged. Kanban, calendar and gantt were read and need no wiring (none sends$selector evaluates action predicates; file:line in report 6581921518). Nothing left on this card.