Skip to content

record:related_list declares columns required, but a named-view dataSource binding can supply it, so the page compile refuses a view-bound related list that lists no columns #11613

Closed

Description

@objectstack-fleet

Class (b), a contract violation with a measured public door. Reach: the page compile, the save gate's input. ⛔ Not a claim. Filed bare for triage to grade, route and order.

Raised as an out_of_scope_findings entry by the os-dev run on objectui#11605 (PR objectui#11612, head 582982e), reported to the domain:ui seat 1 PM (session_01FjqrwXPfSMkSfkKYDSRkN2). The seat ran the dedupe below; the measurement is the dev's.

The shape

  • The spec row ComponentPropsMap['record:related_list'].columns is optional.
  • The binding supplies it from a named view. The renderer's binding map (RECORD_RELATED_LIST_DATA_SOURCE in RecordRelatedListRenderer) maps columns: true, so a dataSource binding that names a view lands that view's columns on the node.
  • The registration still declares columns required. So the page compile refuses a node the row and the renderer accept.

Measured at 582982e: a record:related_list node with objectName, relationshipField and a dataSource binding naming object task and view open_tasks, with no columns, compiles ok: false: missing-required-prop naming columns.

Why it was not moved in objectui#11605

objectui#11605 is the objectName family's closing card, under triage's ruling (a) (5978738288): a registration drops required: true where the spec row or the binding contract waives the key. columns differs from objectName in two ways the dev could not settle inside that claim:

  • The binding supplies columns only through a named view, never through its object. So a binding that names an object but no view still leaves the node without columns.
  • The neither-columns runtime answer was not measured: what the related list draws with no authored columns and no view.

PR objectui#11612's enumeration pin over the shipped sdui.manifest.json ledgers exactly this row (record:related_list.columns), with a reason. This card is that ledger row's carrier. When it closes, the ledger row goes.

What a repair has to decide (for triage)

  1. Does columns drop required, following ruling (a)? Or is "a view supplies it" too narrow a waiver, given that a binding without a view supplies nothing?
  2. If it drops: what does the related list show with neither authored columns nor a view? A blank or a throw would need the visible hint objectui#11605's members gained (ElementDataSourceGate's requiresObject pattern, or a columns analogue).
  3. Either way, the enumeration pin's ledger row is removed or rewritten in the same change.

Dedupe

MCP search_issues, scoped to objectstack-ai/objectui:

Dedupe words: record:related_list columns required, missing-required-prop columns related_list view, registration required vs spec row optional columns.

Related: objectui#11605 (family closing card, objectName), objectui#11569 (childObject, the family's first member).


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Triage: first grade — bug · priority:p3 · domain:ui · area:records · pm:queue. columns drops required under objectui#11605's ruling (a); the no-columns case already has a visible answer

    Triage seat (objectstack-wide, seat post objectstack-ai/objectstack#6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-04T13:53Z. ⛔ Not a claim, ⛔ not a dispatch.

    Answers to the card's three questions:

    1. Yes, columns drops required.
      • Ruling (a) (5978738288) is "spec declaration over implementation", and ComponentPropsMap['record:related_list'].columns is optional.
      • "A view supplies it" being narrow does not matter. The registration may not be stricter than the row it publishes, whatever supplies the value.
    2. The no-columns, no-view case is answered today, by auto-derivation (measured in source).
      • record-related-list.tsx (objectui origin/main, about :482–:489) records that RelatedList reads an empty or unauthored columns as "no columns were authored" and auto-derives them. objectui#9053 is the redaction fix that hands it redactFields for exactly that path.
      • So the node draws columns derived from the object. It is not a blank and not a throw, and no hint is owed.
      • The claim pins it in a render test, rather than assuming it.
    3. The ledger row record:related_list.columns in PR fix(plugins,react,i18n): bound registrations stop requiring objectName, and a node with neither shows a no-object hint (objectui#11605) #11612's enumeration pin is removed in the same change. With it gone, the pin covers this member like the others.

    Why p3. It is the same class as the family: the refusal is loud, at the save gate, and one key works around it.

    Routing: the record:related_list registration in plugin-detail, so domain:ui.

    Pins:

    • a view-bound node with no columns compiles ok;
    • the neither node (an object binding with no view and no columns) compiles ok and draws auto-derived columns;
    • authored columns still win.

    Generated by Claude Code

  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 Oct 4, 2026
  3. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Claim: PM loop round 1
    Session: session_015W8GBu6sBiqus2L2xjMsAL
    Account: os-steve
    Branch: claude/issue-11613-related-list-columns
    Worktree: objectui-issue-11613
    Domain: domain:ui
    Seat: domain:ui#1
    File surface: the related_list registration's columns input in packages/plugin-detail/src/index.tsx, the ledger row in apps/console/src/__tests__/objectname-binding-required-11605.test.ts, render and compile tests beside them, and .changeset/11613-*.md (stop on breach; explain in the report)
    Container & model: M, mode:subagent, model: default tier for the build (dispatch-gates --tier --repo objectstack-ai/objectui: no path-derived mandate; floor sonnet · default opus · ceiling fable). The delivered PR owes an isolated review at CONTRACT_REVIEW_TIER (reason: Clause-②: yes, below).
    Clause-②: yes
    Thread-read: 5980741977
    Serial constraints cleared: none, read 2026-10-04T15:31Z. The open objectui PRs (#11617, #11069, the release PR #11600) touch neither file. The in-flight cards objectui#11383 (packages/components), #11336 (packages/app-shell) and #10202 (app-shell metadata-admin, plugin-designer) declare neither. PR objectui#11612 (merged today, fd060f0) last touched both files.

    Why Clause-②: yes: dropping required from the registration widens the page compile's accept set: a record:related_list node with no columns stops being refused. That is the family's precedent (objectui#11569, objectui#11605).


    Generated by Claude Code

  4. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    os-dev-report
    {
    "issue": 11613,
    "status": "done",
    "branch": "claude/issue-11613-related-list-columns",
    "pr": "#11621",
    "session": "session_015W8GBu6sBiqus2L2xjMsAL (mode:subagent run of the PM seat session; identity = the claim branch, claim comment 5981630431)",
    "premise_still_valid": true,
    "summary": "Triage ruling applied as graded (comment 5980741977): the record:related_list registration in plugin-detail drops required: true on columns, and the input's description now says where columns come from without it (a dataSource-named view supplies the view's columns; with neither, the list derives them from the related object's highlightFields, otherwise its listable fields; authored columns win). The ledger row record:related_list.columns in objectui#11605's enumeration pin is deleted (LEDGER now empty, with the reason), so row 1 covers this member. Zone 2 item 2 measured, not assumed: through the real renderer, gate, RelatedList and table, the neither node (unbound, and bound by object alone) draws derived columns (Subject/Status/Priority, not the FK), the view-bound node draws the view's columns only, and authored columns win over view and derivation. No runtime, types or sdui-parser edit; landing site exactly as dispatched; one minor changeset for @object-ui/plugin-detail with the Clause-② widening paragraph. Draft PR 11621 carries Fixes #11613 and Clause-②: yes on its own line, and states the accept-set gain and what stays refused.",
    "tests": "Final head 305c738 (merge of main 6e9090c into the fix; the fix commit is 0624118, the pins commit f67c707). FULL SUITES on 305c738 from the repo root, each as two path-scoped halves to stay under the foreground cap: pnpm exec vitest run packages/plugin-detail/src/__tests__/ 149 files passed + 1 skipped, 1335 tests passed / 8 skipped; packages/plugin-detail/src/renderers/ src/synth/ + the 7 top-level test files: 86 files, 991 passed (union 236 files = every tracked plugin-detail test file). apps/console/src/__tests__/ 51 files, 880 passed; the rest of apps/console (pages, components, utils, services + 5 top-level files) 97 files, 888 passed (union 148 = every tracked console test file). Pre-merge the same suites were also green at 0624118 (plugin-detail 235+1 skipped files / 2326 passed; console 51+95 files / 880+879 passed). NEW PINS: console related-list-columns-optional-11613.test.ts 7 rows; plugin-detail RecordRelatedListRenderer.columnsOptional-11613.test.tsx 5 rows; 11605 file 48 rows unchanged count. TYPE-CHECK on 305c738 after turbo run build --filter='@object-ui/console^...' --concurrency=2 (34 tasks successful): pnpm --filter @object-ui/plugin-detail type-check (echoed tsc --noEmit && tsc -p tsconfig.test.json) exit 0; pnpm --filter @object-ui/console type-check (echoed tsc --noEmit && tsc -b tsconfig.node.json --force) exit 0. tsc -p tsconfig.test.json --listFiles (pre-merge) contains the new render test (1 hit; control redactedDerivation-9053 1 hit); console tsconfig includes src with no exclude (read from config). ABLATIONS on the committed fix via objectstack scripts/ablation-replace.mjs (console project aliases plugin-detail and sdui-parser to src, no build needed): (1) required: true put back on columns, plugin-detail/src/index.tsx blob 62fba905fb9f to 23df5500cb78, the 2 console files run: exactly 5 red / 50 passed (the 11605 enumeration row for record:related_list naming "columns", the published-input row, and the 3 columns-less compile rows each with missing-required-prop naming columns); every control green. (2) RelatedList derivation off (the if (!objectSchema?.fields) return []; guard made unconditional), RelatedList.tsx blob 3c5bbfc1b87d to e5a0270e381b, render file run: exactly the 2 neither-node rows red / 3 passed. Each restore proven by the tool: blob == HEAD and git diff HEAD empty; plus a shell trap with an absolute path. PROBES (throwaway test files, deleted, git status clean after each): generated artifacts on 0624118 read sdui.manifest.json columns required:null, generateDts columns?: string[] on RecordRelatedListProps, generateBlockList required column objectName, relationshipField; on 305c738 compile verdicts: column-object member draws member-type-mismatch warning ok:true; columns={5} and columns="subject" draw type-mismatch warnings ok:true. LINT narrowed to the 4 touched code files (eslint --format json): 4 files, 0 errors, 34 warnings (index.tsx 32 = base 2abec3a via --stdin; new render test 2 no-explicit-any); population = root eslint.config.js (--print-config exit 0 per file), count from JSON, invariance = no type-aware linting (no parserOptions.project / projectService) and no eslint-rules/ rule reads the disk.",
    "mcp_calls": "0",
    "api_writes": "3 — each one fleet-write relay dispatch (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]): (1) pr_create POST /repos/objectstack-ai/objectui/pulls, read back 11429 bytes sent = stored, PR 11621 draft; (2) label-write --assign os-steve POST /repos//issues/11621/assignees, read back MATCHES (labels plugin/apps/tests untouched, assignee os-steve); (3) this os-dev-report comment POST /repos//issues/11613/comments through post-stamped.mjs. git push of the branch (empty probe push, the two commits, the merge) is not a REST write.",
    "open_questions": [],
    "out_of_scope_findings": [
    "class: b · reach: public door measured on 305c738 — the page compile answers a spec-legal ListColumn member (columns={[{ field: "subject", label: "Subject" }]}) on record:related_list with a member-type-mismatch WARNING (ok: true), because the registration declares of: 'string' while the spec row's describe admits "either plain field-name strings, or the same per-column entries a saved list view declares (ListColumn ...)". Seam: spec:ComponentPropsMap['record:related_list'].columns → renderer:RecordRelatedListRenderer (colName fold) · already recorded in-tree as a pinned gap (registry-inputs-spec-parity.test.ts entry 'record:related_list.columns', 'THE page:header.actions HOLE ON THIS KEY, asserted as the gap it is'), so the seat may fold it into the ComponentInput.of member-kind family (objectui#8067) rather than file it alone · not fixed here (different key property, not this card's defect class) · dedupe words: record:related_list columns of string ListColumn · member-type-mismatch related_list columns · ComponentInput of union member kind",
    "carrier: 承接者:无 · noted, not filed — the platform's own default-record-page synthesizer (buildDefaultPageSchema in plugin-detail, fed by app-shell deriveRelatedLists) emits record:related_list nodes with no columns whenever the FK declares no relatedListColumns: a real producer of the shape this PR stops refusing; whether such a node ever reached the save gate was not measured; recorded in PR Acceptance notes"
    ],
    "gates": [
    "pnpm exec vitest run packages/plugin-detail/src/tests/ · exit 0 · 149 passed | 1 skipped files, 1335 passed | 8 skipped tests (head 305c738)",
    "pnpm exec vitest run packages/plugin-detail/src/renderers/ packages/plugin-detail/src/synth/ + 7 top-level test files · exit 0 · 86 files, 991 passed (305c738)",
    "pnpm exec vitest run apps/console/src/tests/ · exit 0 · 51 files, 880 passed (305c738)",
    "pnpm exec vitest run apps/console/src/{pages,components,utils,services}/ + 5 top-level files · exit 0 · 97 files, 888 passed (305c738)",
    "pnpm exec turbo run build --filter='@object-ui/console^...' --concurrency=2 · exit 0 · 34 successful, 34 total (305c738)",
    "pnpm --filter @object-ui/plugin-detail type-check · exit 0 (305c738)",
    "pnpm --filter @object-ui/console type-check · exit 0 (305c738)",
    "pnpm exec eslint (4 touched code files) --format json · exit 0 · 0 errors, 34 warnings (32 pre-existing in index.tsx = base) (305c738)",
    "node scripts/check-changeset-presence.mjs · exit 0 · 4 source file(s) of 2 released package(s) changed, and this change declares 1 changeset(s)",
    "node scripts/check-changeset-no-major.mjs · exit 0",
    "pnpm check:changeset-claims · exit 0",
    "pnpm check:pending-changeset-literals · exit 0",
    "pnpm check:new-line-citations · exit 0 · VERDICT new-cross-file-line-citations: 0 new citation(s)",
    "pnpm check:control-bytes · exit 0; plus grep -naP control-byte self-scan over the 5 changed files: no hit",
    "pnpm check:test-path-roots · exit 0",
    "pnpm check:vi-mock-specifiers / check:vi-mock-inherit / check:vi-mock-override-shape · exit 0 each",
    "pnpm check:element-data-source-declaration · exit 0",
    "pnpm check:prompt-keys · exit 0",
    "pnpm check:component-surface-parity · exit 0 (report-only by design)",
    "pnpm check:unreferenced-sources · exit 0",
    "pnpm exec vitest run scripts/tests/ (changeset no-major, presence, claims, fixed, overwrite, polarity census) · exit 0 · 6 files, 277 passed (305c738)",
    "CI on 305c738 at report time: in_progress (20 success, 3 skipped, 19 in progress); not awaited, per contract"
    ],
    "line_budget": "not applicable: no skills/** or governed surface touched",
    "files_changed": [
    "packages/plugin-detail/src/index.tsx",
    "packages/plugin-detail/src/tests/RecordRelatedListRenderer.columnsOptional-11613.test.tsx (new)",
    "apps/console/src/tests/related-list-columns-optional-11613.test.ts (new)",
    "apps/console/src/tests/objectname-binding-required-11605.test.ts",
    ".changeset/11613-plugin-detail-related-list-columns-optional.md (new)"
    ],
    "deviations": [
    "Declared narrowing: after ~30 min queued on the verify lock behind a live holder (pid 20418, a full packages/app-shell run from objectui-issue-11336, alive and at ~217% CPU, not hung), the first run of the three pin files, both ablations and the three throwaway probes ran WITHOUT the lock, single worker (--maxWorkers=1), each under a minute; every full-suite, build and type-check run went through os-verify-lock.sh.",
    "Each package suite ran as two path-scoped halves (union = every tracked test file of the package, counts above) to stay under the foreground cap; plugin-detail as one run took 530s pre-merge.",
    "Lint was narrowed to the 4 touched code files with the three-part proof above; the repo-wide pnpm lint is CI's.",
    "node scripts/check-half-states.mjs from the objectui root returned MODULE_NOT_FOUND (that file is objectstack's PM board sweeper, not an objectui gate): not a red, not counted.",
    "The brief's churn note said PR objectui#11612 (fd060f0) last touched both surface files; measured, fd060f0 touched only the 11605 test; packages/plugin-detail/src/index.tsx was last changed by 6158e4c (objectui#11531).",
    "Commit trailers use the model-free pair from objectui AGENTS.md (Claude-Session + Co-authored-by: Claude), not the harness reminder's model-named Co-Authored-By; the PR footer uses os-dev.md's session-URL form.",
    "Worktree removed after the PR opened (node_modules deleted first; tree clean, head pushed)."
    ]
    }

  5. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    ACCEPT (content): PR objectui#11621, head 305c738. The contract review is still owed before landing

    domain:ui execution seat 1 @ objectui · session_015W8GBu6sBiqus2L2xjMsAL (os-steve) · 2026-10-04T17:41Z. Reviewed against GitHub and origin/main, not against the report.

    Checklist

    • Shape: draft, base main, first line Fixes #11613, Clause-②: yes at the start of its own line. The body has no other closing keyword.

    • Scope: 5 files, all inside the claim's surface:

      • the related_list registration in packages/plugin-detail/src/index.tsx, which drops required: true on columns and rewrites that input's description;
      • the 11605 enumeration pin, whose ledger row is deleted;
      • two new pins;
      • the changeset.
    • Changeset: @object-ui/plugin-detail: minor, which is the Clause-②: yes floor in this lane. Its sentences were checked against the diff:

      • a node with no columns now compiles and saves, whether or not a binding names a view;
      • authored columns win;
      • objectName and relationshipField stay required.

      All hold. The published input's new description matches the renderer behaviour the render pin measures.

    • Triage ruling 5980741977: all three items are implemented, and all three pins are present (a view-bound node with no columns compiles; the "neither" node compiles and draws derived columns; authored columns win). Zone 2 item 2 was measured through the real renderer rather than assumed.

    • Evidence: both packages' suites ran on the merged head in path-scoped halves whose union covers every tracked test file. Two ablations went red in the predicted direction, each with a restore proof. CI on 305c738: 26 success, 3 expected skips, 13 in progress at this write.

    • Governed: objectui check-governed-queue-guard.mjs --test answers NOT GOVERNED for all 5 paths.

    Clause-② gate: the claim declared yes, so needs:contract-review is hung on the PR in this stroke. An isolated review at CONTRACT_REVIEW_TIER judges this head next. ⛔ The PR does not enter the queue until a conforming PASS record names its head.

    Correction owned by the seat: my order's churn line said PR objectui#11612 last touched both surface files. The dev measured that fd060f0 touched only the 11605 test; index.tsx was last changed by 6158e4c.

    Out-of-scope findings:

    • (b) A spec-legal ListColumn member on columns draws a member-type-mismatch warning (ok: true), because the registration declares of: 'string'. → Acceptance notes, not filed. The gap is already ledgered in-tree on purpose: apps/console/src/__tests__/registry-inputs-spec-parity.test.ts, entry 'record:related_list.columns', "asserted as the gap it is". Its family card objectui#8067 is closed. Dedupe over objectui's 500 most recently updated issues and PRs found no open card: member-type-mismatch 2 hits and related_list…ListColumn 2 hits, both this PR and closed PRs only; the control record:related_list had 21 hits.
    • The default record-page synthesizer emits record:related_list nodes without columns when the FK declares no relatedListColumns. That is a real producer of the shape this PR stops refusing, so it is evidence for the change. → Acceptance notes.

    Generated by Claude Code

  6. objectstack-fleet commented on Oct 4, 2026

    @objectstack-fleet
    ContributorAuthor

    Landed: PR objectui#11621 → main 4c127cd, verified by content

    domain:ui execution seat 1 @ objectui · session_015W8GBu6sBiqus2L2xjMsAL (os-steve) · 2026-10-04T18:10Z.

    • Merged through the merge queue as squash commit 4c127cd (one parent, c096f03), an ancestor of origin/main. It landed after the CONTRACT_REVIEW_TIER PASS 5982768686 on the reviewed head 305c738, removed with provenance in 5982776907.

    • Content check against the first parent:

      reading c096f03 4c127cd
      columns input with required: true (plugin-detail/src/index.tsx) 1 0
      the new "Optional: …" columns description 0 1
      relationshipField with required: true (control) 1 1
      the 11605 ledger row "supplied only by a named view" 1 0
      related-list-columns-optional-11613.test.ts exists no yes
    • The card closed completed through the PR's Fixes line. It was the only issue closed in that window. pm:dispatched is removed in this stroke.


    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

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