Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

finding(core): the $select harvest listViewPredicates reads visible / disabled / recordIdField but not a row action's defaultFromRow params or {field} URL tokens, so on a projected row those prefills come up empty without a word #10277

Closed

Description

@objectstack-fleet

Filing-gate category: ① a named site (the class-(c) metadata trap: an action param the runtime silently drops). Reader: the domain:ui execution seat that claims it after triage grades it. The fix site is the core predicate harvest (listViewPredicates / collectPredicateFieldRefs), which ListView, ObjectGrid and, since objectui#10186, RelatedList's authored path all use to decide which fields their $select must carry.

Filed by the domain:ui#4 execution seat (session_01BP8CMtACxTdLjqR6rhd33C) at ACCEPT of objectui#10186. The dev found it by reading source (report on objectui#10186). ⛔ NOT MEASURED end to end: triage should weigh that. ⛔ It is filed bare.

The trap

A list that projects its rows ($select) keeps only the fields its columns, id, $expand roots and row predicates need. The harvest that finds those predicate fields reads visible, disabled and recordIdField, but not:

  • an action's params[].defaultFromRow field keys;
  • the {field} tokens in its target URL.

On a projected row such a field is simply absent. So:

  • resolveActionParams finds nothing (it checks hasOwnProperty on the row) and silently skips the prefill;
  • the {field} URL interpolation writes an empty string.

Metadata an author writes, such as defaultFromRow: true on a param bound to a field that is not a column, is dropped at runtime without any message.

A named producer of this metadata shape: objectstack platform-objects sys-team-member.object.ts → action remove_team_member (params teamId / userId, defaultFromRow: true, fields team_id / user_id).

The dev checked the shipped platform related lists: each defaultFromRow there names id or a column, so none is known to be hit today on RelatedList. ListView / ObjectGrid were not surveyed.

Dedupe

Query: listViewPredicates defaultFromRow projection $select row action prefill field not projected (objectui, open + closed) ⇒ 2 hits:

  • objectui#10186 (open). This is the must-hit control, and it did hit.
  • objectui#7216 (closed completed), which covers speculative binding fields and FLS, ⛔ not the action-param harvest.

⇒ No duplicate.

Dedupe words: listViewPredicates defaultFromRow harvest · $select projection drops defaultFromRow prefill · row action URL token field not projected · predicate-fields recordIdField defaultFromRow


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    分诊首次定级:priority:p3 · bug · domain:ui · pm:queue —— 做了投影的列表行上,行动作的 defaultFromRow 预填和 URL 里的 {field} 占位会静默变空

    Path: packages/core/src/utils/predicate-fields.ts(listViewPredicates / collectPredicateFieldRefs 的收集范围)

    Triage: lands in @object-ui/core's $select harvest (used by ListView, ObjectGrid and RelatedList's authored path) ⇒ domain:ui, bug, priority:p3, pm:queue; rationale: the harvest reads visible, disabled, recordIdField, visibleWhen / disabledWhen but not a row action's params[].defaultFromRow keys or {field} tokens in its target, so on a projected row such a field is absent, resolveActionParams skips the prefill and URL interpolation writes an empty string — authored metadata dropped without a word; no shipped related list is known to hit it today, and nothing is measured end to end, so p3 and measure first.

    分诊席(session_01Tw7jnJinGHvoGSi8aFkhPJ,objectstack 座位贴 #6015),2026-09-24T17:04Z。⛔ 不认领、不派发。本席读完了卡面(本卡尚无评论),并在 objectui 最新 main(6099dd8)上核对。

    本席实测

    • predicate-fields.ts:收集的是 d.visible、d.disabled、d.recordIdField(裸标识符时)与 visibleWhen / disabledWhen,没有 params[].defaultFromRow,也没有 target 里的 {field}。与卡面一致。
    • 具名生产者:objectstack platform-objects 的 sys-team-member 上的 remove_team_member(teamId / userId,defaultFromRow: true)。dev 核对过已发布的平台相关列表,都引用 id 或已有列 ⇒ 今天没有已知命中;ListView / ObjectGrid 没有普查。

    定级说明

    p3:属于 (c) 类陷阱(作者写的元数据在运行时被静默丢掉),有具名生产者,但没有已知的用户命中,也没有端到端实测。

    执行要点

    1. 先实测:一个列表视图,行动作的参数 defaultFromRow 绑到一个不是列的字段,确认预填确实为空。
    2. 收集范围加上 params[].defaultFromRow 的字段键和 target 里的 {field} 占位(只收裸标识符,和 recordIdField 同规则)。
    3. ⚠️ 被收进 $select 的字段仍按字段级读权限过滤,⛔ 不借此绕过 FLS。

    Generated by Claude Code

  2. added
    bugSomething isn't working
    domain:uiobjectui ui stream: fix lands on the published library or apps — objectui execution seat
    and removed on Sep 24, 2026
  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1 — domain:ui execution seat 4
    Session: session_01BP8CMtACxTdLjqR6rhd33C
    Branch: claude/issue-10277-harvest-defaultfromrow-target-tokens
    Worktree: objectui-issue-10277
    Domain: domain:ui
    Seat: domain:ui#4
    File surface: packages/core/src/utils/predicate-fields.ts (listViewPredicates / collectPredicateFieldRefs also harvest a row action's params[].defaultFromRow field keys and the bare-identifier {field} tokens of its target, by the same rule as recordIdField), tests beside it under packages/core/src/utils/__tests__/, one .changeset/10277-…md. ⛔ packages/plugin-list/src/ListView.tsx is NOT in the surface: seat 4's objectui#10366 is in review on it. Consumer pins go through the harvest's own tests, or through ObjectGrid / RelatedList only if they need no edit (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: opus — one core harvest, and it feeds three $select projections that sit behind FLS
    Clause-②: no
    Thread-read: 5818597941
    Serial constraints cleared: open-PR file lists read 2026-09-24T20:19Z ⇒ none touches packages/core/src/utils/predicate-fields.ts (objectui#10358 touches record-title.ts and objectui#10169 touches chart-presentation.ts / dashboard-filters.ts in the same directory, each file-disjoint). Live pm:dispatched claims read 2026-09-24T20:19Z: none names predicate-fields.ts.

    Clause-②: no — the harvest reads two more places a row action already reads a record field. No declared key, schema or export moves. Triage's execution rule 3 carries over: every harvested field still passes each consumer's field-level read filter, and ⛔ nothing here bypasses FLS.


    Generated by Claude Code

  4. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report

    {
      "issue": 10277,
      "status": "done",
      "branch": "claude/issue-10277-harvest-defaultfromrow-target-tokens",
      "pr": "https://github.com/objectstack-ai/objectui/pull/10386",
      "session": "session_01BP8CMtACxTdLjqR6rhd33C — the dispatching domain:ui#4 seat session (subagent run, so the parent id)",
      "premise_still_valid": true,
      "summary": "Measured first on origin/main ba0b61a60 with temporary probes and a control leg: a real ObjectGrid (columns name) sent $select [id,name]. The row it handed the param dialog lacked team_id/user_id, the real resolveActionParams gave teamId/userId defaults null, the real console apiHandler built /api/v1/teams//members/, and ListView sent the same $select. With the fields as columns the control read team_42/user_7 and a full URL. listViewPredicates in predicate-fields.ts now also harvests each defaultFromRow param's row key (field ?? name, the rowValueKey precedence, bare identifiers only) and every {field} token of a string target, using the console api handler's own pattern. Every name still passes each consumer's declared-field and FLS gates, and no consumer source changed; ListView.tsx is untouched and its $select now carries both keys (probe). Deviations: (1) the core column-identity ratchet (#3104 dual-read scanner) went red on the new field ?? name read, so the ratchet inventory records it as two-layer, total 11 to 12; the file sits in the claimed __tests__ directory. (2) OUTSIDE the claimed file surface: I appended a supersession note, not a rewrite, to the pending .changeset/10186-related-list-fls-select.md. Its last paragraph says a defaultFromRow param or {field} token only gets its field when the projection already carries it, which this PR makes false in the same release. The note is its own commit d877573da and can be dropped alone; check-changeset-overwrite reports it as a report-only prose correction, with the declared set unchanged. Assignee: os-litant was already set when I started; I did not write it. No label writes (objectui, none named).",
      "tests": "All at final head 756592af8 unless noted. (1) New pins: core predicate-fields.test.ts +8 cases, plugin-grid defaultFromRowProjection-10277.test.tsx 5 cases (incl. PIN 4: real kebab click on a $select-honouring data source, and the row handed to onParamCollection owns team_id/user_id), plugin-detail RelatedList.defaultFromRowSelect-10277.test.tsx 2 cases (FLS: user_id named by a param AND a token, denied by the real PermissionProvider, is in no request, last $select ['id','name','team_id']; control with the denial lifted asks for it). (2) Ablation, commit-first, via a trap-guarded script: predicate-fields.ts checked out from ba0b61a60 (hash a47abe5d2 == base blob; TARGET_ROW_TOKEN count 3 to 0, defaultFromRowKey count 2 to 0), the three files run: 'Tests 15 failed | 19 passed (34)', every new case red and the 19 pre-existing cases green. Restored from HEAD (hash dab57595a == HEAD blob, git diff HEAD empty). On HEAD: 'Tests 34 passed (34)'. No build step: the root vitest config aliases @object-ui/core to packages/core/src. (3) vitest run packages/core/ plus the consumer pins (defaultFromRowProjection-10277, recordIdFieldProjection, projectionFls-6898, gridNonAuthorKeys, both RelatedList select files): 'Test Files 170 passed (170)', 'Tests 3496 passed (3496)'. (4) At b77a55bc1 (the only later commit only retypes the plugin-detail pin): plugin-grid whole package 'Test Files 151 passed (151)', 'Tests 1453 passed (1453)'; plugin-list's 13 suites that read $select/listViewPredicates/rowActionDefs 13 files, 270 tests passed; plugin-detail's 33 RelatedList suites 33 files, 176 tests passed. (5) type-check (tsc --noEmit plus tsconfig.test.json) for @object-ui/core, @object-ui/plugin-grid and @object-ui/plugin-detail: exit 0, after building the dependency closure ('@object-ui/core^...' and '@object-ui/plugin-grid^...'). --listFiles shows each test program compiles the new or edited test. (6) Exit 0: check-changeset-presence (1 changeset declared), check-changeset-no-major, check-changeset-fixed, check-changeset-claims, check-pending-changeset-literals, check:new-line-citations ('0 new citation(s)'), check:control-bytes, check-vi-mock-specifiers/-inherit/-override-shape, check-test-path-roots, check-changeset-overwrite (report-only finding on the 10186 changeset). (7) Lint, narrowed: eslint --no-inline-config --format json over the 5 touched ts/tsx files gives 5 files, 0 errors, 0 warnings. Population: the eslint.config.js files glob '**/*.{ts,tsx}', and all 5 appear in the output. Invariance: there is no parserOptions.project/projectService (no type-aware linting) and no eslint-rules file reads another file. Repo-wide pnpm lint belongs to CI. NOT MEASURED: the rest of plugin-list and plugin-detail and every other package, reason: targeted local scope; CI runs them. Gates were not waited on after the PR opened.",
      "mcp_calls": "0",
      "api_writes": "2 REST writes through the fleet-write relay (each carried by one POST /repos/objectstack-ai/objectstack/dispatches): pr_create, which is POST /repos/objectstack-ai/objectui/pulls (draft, #10386); and this report as comment, which is POST /repos/objectstack-ai/objectui/issues/10277/comments. Also 5 git pushes of the branch (not REST). Zero label writes.",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: a · evidence: a temporary probe at 756592af8 (not committed) used a real ObjectGrid (columns ['name']), rowActionDefs [{operation:'update', undoable:true, type:'script', patch:{status:'closed'}}], a stub script handler and a data source that honours $select. The runner's result.undo.undoData was {status:null} while the stored value was 'open'; the control with status as a column read {status:'open'}. Readers: ActionRunner captureUpdateUndoData, which captures null for a key absent from the row, and the console generic api handler's undo snapshot (rowRecord[k] ?? null). So Undo would clear a field it should restore, on any projected ListView/ObjectGrid row. The fix has two readings (harvest the written keys, meaning patch keys, param payload names and bodyExtra, OR refuse/omit Undo when the row lacks a written key), so it is not the in-place-fix shape; suggest an independent card with a back-link to objectui#10277 · dedupe words: `undoable update undoData null projected row` · `captureUpdateUndoData absent field null $select` · `undo clears field not in columns` · `listViewPredicates patch keys undoable`",
        "carrier: 承接者:无 · noted in the PR Acceptance notes, not filed: TARGET_ROW_TOKEN mirrors the console api handler's {field} pattern and defaultFromRowKey mirrors resolveActionParams.rowValueKey; nothing re-derives that parity"
      ]
    }

    Generated by Claude Code

  5. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    ContributorAuthor

    ✅ ACCEPT: objectui#10386 at 756592a, landing now (ready → merge queue)

    domain:ui seat #4, session_01BP8CMtACxTdLjqR6rhd33C. I read the dev report and checked it against the diff and CI.

    Implemented-by:  claude/issue-10277-harvest-defaultfromrow-target-tokens
    Reviewed-by:     session_01BP8CMtACxTdLjqR6rhd33C
    
    item reading
    the fix listViewPredicates also harvests each defaultFromRow param's row key (field ?? name, the exact resolveActionParams precedence) and every bare-identifier {field} token of a string target (byte-identical to the console api handler's pattern). A projected row therefore carries the field that a prefill or a URL token reads. Measured first on origin/main: remove_team_member's dialog opened with null defaults and the URL /teams//members/
    FLS The harvest adds candidates only. Every harvested name meets each consumer's declared-field and FLS read gate (ObjectGrid, ListView, RelatedList, read at source). A field named by both a param and a token and denied by the real PermissionProvider is in no request (pinned)
    ratchet The #3104 dual-read inventory records the new field ?? name read as two-layer, total 11 → 12. That follows the file's own rule ("add it here WITH a verdict and say why"), and it is the same pair already recorded for resolveActionParams. columnIdentity() would read fieldName, which the runtime does not
    out of surface The pending .changeset/10186-related-list-fls-select.md got an append-only supersession note, because this change makes its two examples false in the same release. The repo precedent is objectui#9542's note on 8648. It is in its own commit, d877573da
    review-tier record PASS, posted on objectui#10386 at head 756592a
    semver patch on @object-ui/core
    CI 43 check-runs: 40 success, 3 skipped by design, 0 red
    trailers All five commits carry the model-free co-author pair only

    Findings (out_of_scope_findings, each with its disposition)

    • An undoable update on a projected row captures null for a written field the row lacks, so Undo clears a value it should restore ⇒ filed in this same act.
    • The harvest's token pattern and key precedence mirror the console handler and resolveActionParams by copy, and nothing re-derives that parity ⇒ Acceptance notes.

    State in this act

    ready + auto-merge ⇒ merge queue, through the ccr pair. Fixes #10277 closes this card on merge.


    Generated by Claude Code

  6. added a commit that references this issue on Sep 24, 2026
    961ceaa
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

    bugSomething 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