Skip to content

docs(spec): record:related_list's parentDataSource is described as rendered - #24578

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23585-parent-data-source-live
Oct 19, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-23585-parent-data-source-live

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #23585
Clause-②: no

record:related_list's parentDataSource is rendered by the console at its pin, and @objectstack/spec now says so. Before this change, the key's describe opened with [PLANNED — not yet rendered], its TSDoc said no renderer reads it, and the liveness _note recorded it as planned. All three told an author, an AI author first, not to use a binding the console renders. The change is describe, TSDoc and ledger text only: RecordRelatedListProps accepts exactly what it accepted before, no key is added, and every refusal keeps its issue code and path.

The new describe, verbatim

The PARENT record this related list hangs off, bound by a query of its own: an object binding { object, view?, filter?, sort?, limit? }, the shape dataSource takes. Read by the single-record rule: the FIRST row of the bound set by its sort is the parent (declare limit: 1 to say one row is meant), and when no row answers, the block renders a loud absence, never an empty list, which would read as "this record has no related records". relationshipValueField is read on that row. When this key is absent, the record page's record context remains the parent. The node-level dataSource on this block keeps its published meaning, the CHILD rows the list draws (it fills objectName, columns, filter, sort and limit), and is never read as the parent.

Only the marker left. Every sentence after it holds at the pin (P1 below), so no sentence was added or removed.

P1 — the pin carries the renderer, and the renderer does what each clause says

  • .objectui-sha on origin/main is 4016370af3c9c9dafec2bc5299afd8e8079701b8. In ../objectui, git merge-base --is-ancestor d81e46043 4016370af3c9 exits 0, and git merge-base --is-ancestor d81e46043 fe11eb12e5 (objectui origin/main) exits 0. An exit 0 proves itself, so no control leg is owed. git diff --stat 4016370af3 fe11eb12e5 over the two renderer files prints nothing: objectui main reads the key the same way.
  • scripts/sdui-manifest.record.json records objectuiSha 4016370af3…, and sdui.manifest.json lists parentDataSource as an input of record:related_list. The console this repository builds is built from that pin.
  • Every read below is git show 4016370af3:PATH in ../objectui. R is packages/plugin-detail/src/renderers/.
    • The key is read. R record-related-list.tsx:810 const parentBinding = schema?.parentDataSource;, handed to ParentRecordBinding at :814–:824.
    • The FIRST row by its sort is the parent. R recordBinding.tsx:429 const params: QueryParams = { $top: 1 }; and :431 if (sortKey) params.$orderby = JSON.parse(sortKey);. The row is read through useResolvedDataSource (:384), the page's own adapter, so under the caller's identity (module doc :45–:46).
    • No row is a loud absence, never an empty list. R recordBinding.tsx:475: when absent is given and the settled read has no row, BoundRecordProvider returns absent in place of its children. The related list passes absent={NoParentRecord …} at R record-related-list.tsx:821, and NoParentRecord (:772–:786) draws a role="status" notice. The absence replaces the children, so no child query is sent. A read the server refuses as forbidden reads the same as no row (R recordBinding.tsx:449).
    • relationshipValueField is read on that row. R record-related-list.tsx:259–:263 reads it on the record context (ctx?.recordId, or ctx?.data[relationshipValueField]). BoundRecordProvider provides the bound row as that context.
    • Absent, the record page's record is the parent. R recordBinding.tsx:274: when isElementDataSourceConfig(binding) is false (no binding), ParentRecordBinding returns its children as they are, so the record page's record context still reaches the list.
    • The node-level dataSource is still the CHILD rows. R record-related-list.tsx:724–:729 maps columns, filter, sort and limit onto the list. The parent gate is given the parent binding alone (R recordBinding.tsx, ParentRecordBinding docblock).
  • objectui's own pin, R __tests__/record-related-list.parentDataSource-12294.test.tsx, carries a case for each clause: first row by sort, relationshipValueField on the bound row, no row is the absence with no child query, 403 is the same absence, no parentDataSource keeps the page record, and the registration publishes the input.

No describe clause is untrue at the pin, so the prohibition does not apply.

P2 — the marker and the note were present on origin/main 5b14b7f3ad

component.zod.ts:1743 (docblock) and :1747 (describe) carried [PLANNED — not yet rendered]. liveness/page.json:3's _note carried the PLANNED RECORD (#23134 …) sentence.

The card's criterion, on this head: git grep -n "PLANNED — not yet rendered" -- packages/spec/src/ui/component.zod.ts names :1170 and :1175 only, both on the emptyState description (out of scope, G4). git grep -c "PLANNED RECORD (#23134" -- packages/spec/liveness/page.json answers nothing (exit 1).

P3 — every text here that called parentDataSource planned, unread or not rendered

Measured with git grep -n parentDataSource and git grep -n "not yet rendered" over the whole tree.

Rewritten here:

  1. packages/spec/src/ui/component.zod.ts, the describe: the marker is gone.
  2. The same file, the key's TSDoc: the "PLANNED, not yet read" paragraph is now "RENDERED by the console", naming the read points with the objectui: realm marker and the pin. The same docblock said the api arm of dataSource "is still unrendered". That is false since objectui c4dd7078ed (recorded in this ledger's _note), so the clause now says why the key takes the object binding only: it is the shape the sibling blocks read, and the shape objectui's parent read takes (isElementDataSourceConfig asks the binding for an object).
  3. packages/spec/liveness/page.json, _note: the PLANNED RECORD (#23134 …) sentence is a LIVE RECORD, in the form the ElementApiDataSource record took when its renderer shipped. It cites the pin and each objectui path with the objectui: realm marker.
  4. The same file, the regions row's component-member read-point list, entry [10]: record:related_list.parentDataSource (PLANNED) pointed at the old measurement (objectui @f0532d8a, "the record context read"). It now lists the read points at @4016370af3c9 in the list's own objectui @SHA FILE#SYMBOL spelling. The ledger keeps no parentDataSource row; this entry is its one piece of parentDataSource evidence, so I read it as inside the claim's "the parentDataSource row's status, evidence" item. Named here so a reviewer can disagree.
  5. content/docs/references/ui/component.mdx:1971, regenerated by gen:docs.
  6. packages/spec/src/ui/component-record-related-list-parent-23134.test.ts, pin E: it asserted the describe opens with the marker. Re-trued, not deleted: it now asserts that check-liveness's planned matcher (MARKER_RE.planned, copied with its source named) reads nothing in the describe, with a DARK case showing the same matcher catches the replaced marker.

Named, not edited:

  • .changeset/pre/23134-related-list-parent-data-source.md:13 and packages/spec/CHANGELOG.md:2200 ("Planned, not yet rendered"). Both are the consumed changeset and the released entry for 4cc3ff9. They were true when that release was cut, so they are not factual errors, and both are release-owned.
  • content/docs/** (hand-written) and skills/**: no hit for parentDataSource.

G1–G4

  • G1. The ledger carries no parentDataSource row, so there is no row status to move and no author warning. The liveness record is the _note (item 3) and the evidence entry (item 4). pnpm --filter @objectstack/spec run check:liveness exits 0 (in the gate run below).
  • G2. pnpm --filter @objectstack/spec build, then check:generated named one stale artifact, content/docs/references/**. gen:docs rewrote component.mdx:1971 (1 line). A second check:generated: "All 14 generated artifacts are up to date". authorable-surface.base.json did not move.
  • G3. .changeset/23585-related-list-parent-data-source-rendered.md, patch on @objectstack/spec, Clause-②: no.
  • G4. At the pin and at objectui main, git grep -n emptyState -- packages/plugin-detail/src outside tests answers nothing. The renderer for the :1170/:1175 emptyState markers (objectui#12932) has not shipped, so those markers are still true.

Tests

  • pnpm --filter @objectstack/spec test on 131a5615e9: 701 files passed, 21312 tests passed, 1 todo.
  • pnpm --filter @objectstack/spec typecheck on 131a5615e9: exit 0; check:test-typecheck OK.
  • The pin file alone: 18 passed.
  • Reverse verification, one time. With the change committed, scripts/ablation-replace.mjs put the marker back into the describe (anchor 1 → 0, blob 2d039146d858 → d8a66adf1db4). The pin file then failed in the expected direction: 1 failed, 17 passed, the failure being E's "no planned marker" case. The tool restored the file: blob equal to HEAD, git diff HEAD empty.
  • The merge of origin/main (04669a467e, packages/objectql and packages/services/service-automation only) touches no package this diff touches, so the spec suite was not re-run on the merge commit.

Gates

Run on the final head, git rev-parse --short HEAD = 53624a10cf. node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack (no paths; change set from the merge base 04669a467) derives 111 commands from the 5 changed paths. Each ran with its exit code captured before any pipe: 111 of 111 exit 0. --ran on that record: "111 derived, 111 run, 0 NOT-MEASURED, 0 UNRUN".

  • A first run on 131a5615e9 recorded six PREREQUISITE NOT MET (exit 3): the two @objectstack/lint doc gates, check:skill-examples, check:docs-transcript-drift, check:dual-build-cjs-loads and check:lean-entry-closure. After the @objectstack/lint and @objectstack/objectql closures were built, all six measured on 53624a10cf and exit 0. That earlier run is not the reading; the one above is.
  • Verdict lines on 53624a10cf: check:generated "All 14 generated artifacts are up to date"; check:docs "227 generated files in sync with packages/spec"; check:liveness exit 0 ("1038 live · 1 experimental · 1 live-elsewhere · 112 dead · 23 planned = 1175 classified"); check:nul-bytes OK; check:authorable-surface exit 0.
  • Local-only, outside the derivation: pnpm check:dev-prereqs exit 0.
  • Every family CI runs is read from this PR's check-runs, not re-run here.

Acceptance notes

  • The two emptyState markers in component.zod.ts (:1170, :1175) stay. Their renderer has not shipped (G4).
  • The released 4cc3ff9 text that calls the key planned stays as released (P3, "Named, not edited").

Generated by Claude Code

…ered

The console pin carries objectui's parentDataSource reader, so the
describe drops its PLANNED marker, the TSDoc names the read points, and
the liveness note's PLANNED sentence becomes a LIVE record.

Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 2 documentable anchor(s). ⚠️ 1 changed file(s) yielded no anchor (packages/spec/liveness/page.json), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/protocol/objectui/layout-dsl.mdx (via RecordRelatedListProps (symbol, a top-level const object))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v12.mdx (via RecordRelatedListProps (symbol, a top-level const object))
  • content/docs/releases/v14.mdx (via RecordRelatedListProps (symbol, a top-level const object))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/liveness/page.json) — pages documenting those are invisible to this run
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 143 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d7e482efbdca3cdb6ea0ce99e529c1f9cb2aa6a7 — the merge of head 53624a10cfb6f28c2b4de4c50df1bde17a404fbd into base ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d7e482efbdca3cdb6ea0ce99e529c1f9cb2aa6a7 && git checkout d7e482efbdca3cdb6ea0ce99e529c1f9cb2aa6a7
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1 53624a10cfb6f28c2b4de4c50df1bde17a404fbd && git checkout -B drift-repro ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1 && git merge --no-ff 53624a10cfb6f28c2b4de4c50df1bde17a404fbd

node scripts/docs-audit/affected-docs.mjs --json ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs ba4afcc2391922a86678bf5e47b1a0e4d2c87ff1 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 (seat check) · PR #24578 · domain:spec seat 2 · zhuangjianguo · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T14:08Z

ACCEPT at head 53624a10cfb6f28c2b4de4c50df1bde17a404fbd (claim 6267435237, os-dev subagent, report on #23585). A contract review at CONTRACT_REVIEW_TIER is owed (the schema face: packages/spec/src/ui/component.zod.ts). It runs on this head, isolated from the seat. needs:contract-review is on the PR until its record is adopted.

item reading
Shape Draft, base main, assignee zhuangjianguo. Line 1 is Fixes #23585. Clause-②: no starts a line. 5 files, +49 / −17. Not governed. The changeset is patch on @objectstack/spec.
The change parentDataSource's describe drops [PLANNED — not yet rendered] and is otherwise unchanged. Its TSDoc names the console's read points with the objectui: realm mark. liveness/page.json's _note sentence becomes a live record, and the regions row's read-point entry drops its (PLANNED) tag. The pin (case E) is re-trued to assert no planned marker, with a control. component.mdx is regenerated. No accept set moves.
Premises P1 true: objectui d81e46043 is an ancestor of this repository's .objectui-sha (4016370af3) and of objectui main, and every describe clause kept is read at that pin. P2 true: the marker and the note were present. P3: two other texts say "Planned, not yet rendered", the released .changeset/pre/23134-… and CHANGELOG.md:2200. Both are release-owned and true when released, so they are left alone.
Seat re-read Deviation (2) rewrites the docblock clause "that arm is still unrendered" about dataSource's api arm. The seat checked it in this act: objectui c4dd7078ed (the API binding's renderer) is an ancestor of the .objectui-sha pin, so the old clause was false, and the rewrite claims no rendering of its own.
Pins and gates Pin file: 18 passed. Re-planting the marker turns exactly case E red. Full spec suite: 701 files passed. The 111 derived gate commands all exit 0, including check:liveness and check:generated.
CI On this head at this act: 38 runs, 18 success, 5 skipped, 15 in progress, 0 failure. Landing waits for green.
Accepted deviations (1) The regions-row read-point entry tagged parentDataSource (PLANNED) is the ledger's one evidence entry for the key, which keeps no row of its own, so it was inside the claim's "status, evidence" item. (3) The main merge 53624a10cf touches objectql and service-automation only. (4) and (5) carry no contract effect.
Findings Both are noted, not filed. The two emptyState markers (component.zod.ts about :1170 and :1175) are still true: no emptyState reader at the pin. Their carrier is objectui#12932. The released changelog texts are release-owned.
Landing After a contract-review PASS and a green head: pr_ready then automerge_enable. Fixes #23585 closes the card.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 53624a10cfb6f28c2b4de4c50df1bde17a404fbd
Local-runs: none
Scope: packages/spec/src/ui/component.zod.ts + packages/spec/liveness/page.json (the _note LIVE RECORD sentence and the regions-row read-point entry [10]), packages/spec/src/ui/component-record-related-list-parent-23134.test.ts, content/docs/references/ui/component.mdx, .changeset/23585-related-list-parent-data-source-rendered.md, packages/spec/scripts/liveness/check-liveness.mts (the planned matcher pin E copies); at the objectui pin 4016370af3c9c9dafec2bc5299afd8e8079701b8: packages/plugin-detail/src/renderers/record-related-list.tsx, packages/plugin-detail/src/renderers/recordBinding.tsx, packages/plugin-detail/src/renderers/tests/record-related-list.parentDataSource-12294.test.tsx, packages/core/src/data-scope/element-data-source.ts, packages/react/src/element-data-source/ElementDataSourceGate.tsx, packages/react/src/SchemaRenderer.tsx

Inputs: card #23585 (body and all six comments, the os-dev-report 6269164297 included), PR #24578 (body, five-file list, net diff against merge base 04669a467e), the objectui tree at the .objectui-sha pin (identical on origin/main and on this head; git merge-base --is-ancestor d81e46043 4016370af3c9 exits 0, and so does it for c4dd7078ed), and the head's check-runs. The seat check 6269208627 was not weighed. R below is objectui packages/plugin-detail/src/renderers/ at the pin.

① Derived judgments

D1 — the describe: only the marker left. Byte comparison of the parentDataSource describe on base and head: head equals base with the 29-character [PLANNED — not yet rendered] prefix removed and nothing else. component.zod.ts carried four PLANNED — not yet rendered spellings on base and carries two on head, both the emptyState ones (D8). Right.

D2 — every describe clause is true at the pin. Each clause, with the code that honours it:

  • bound by a query of its own, the object binding { object, view?, filter?, sort?, limit? }, the shape dataSource takes — spec ElementDataSourceSchema (page.zod.ts:298, unchanged) by reference; objectui R record-related-list.tsx:810 const parentBinding = schema?.parentDataSource; handed to ParentRecordBinding at :814–:824, which resolves it through ElementDataSourceGate with the binding ALONE as the gate's node (R recordBinding.tsx:273 gateSchema = { dataSource: binding }, :276–:284), so the child list's own filter/sort never compose into the parent query.
  • the FIRST row of the bound set by its sort is the parent — R recordBinding.tsx:429 const params: QueryParams = { $top: 1 };, :431 if (sortKey) params.$orderby = JSON.parse(sortKey);, :437 firstRow(result) (defined :339–:346, rows[0]). objectui pin test R tests/record-related-list.parentDataSource-12294.test.tsx:162–:181 asserts { $top: 1, $orderby: BY_RANK } on the parent read.
  • (declare limit: 1 to say one row is meant) — authoring advice, not a renderer claim: the parent read maps filter and sort only (R recordBinding.tsx:35 RECORD_BLOCK_DATA_SOURCE = { filter: true, sort: true }; :32–:33 "limit is deliberately unmapped: one row is read whatever the binding caps the set at"), and objectui validate warns without limit: 1 (module doc :42–:43). True as written.
  • read under the caller's identity (TSDoc) — R recordBinding.tsx:384 useResolvedDataSource returns the SchemaRenderer context's adapter (objectui packages/react/src/element-data-source/ElementDataSourceGate.tsx:709–:715), the page's own; module doc :45–:46; and the provided row is readableRow(row, perms, object) (:319–:333, :480), the field-level policy applied.
  • when no row answers, the block renders a loud absence, never an empty list — R recordBinding.tsx:475 if (absent !== undefined && row === undefined) return absent (the children are not mounted, so no child query is sent); R record-related-list.tsx:821 absent={NoParentRecord …}; NoParentRecord :772–:786 draws a role="status" notice naming the bound object. Pin test :197–:208 (absence drawn, zero child reads, no row text). A read the server refuses as forbidden is the same absence: :449 classifyLoadError(err) === 'forbidden' settles as loaded with no row; pin test :210–:217.
  • relationshipValueField is read on that row — R record-related-list.tsx:259–:263 reads it off useRecordContext() (ctx?.recordId, else ctx?.data[relationshipValueField]), and BoundRecordProvider provides the bound row as that context (R recordBinding.tsx:477–:481, recordId={rowId(row)}, data={readableRow(...)}). Pin test :183–:195.
  • When this key is absent, the record page's record context remains the parent — R recordBinding.tsx:274 if (!isElementDataSourceConfig(binding)) returns the children as they are: no provider, no element, no fetch. Pin test :254–:264 (page record is the parent, zero parent-object reads, no parent test id in the DOM); :266–:274 (with the key, the binding wins).
  • The node-level dataSource … the CHILD rows (it fills objectName, columns, filter, sort and limit), never read as the parent — R record-related-list.tsx:724–:729 RECORD_RELATED_LIST_DATA_SOURCE = { columns: true, filter: true, sort: true, limit: 'limit' } on the child gate (RecordRelatedListChildRows, :742–:762); objectName is the gate's default landing for the binding's object (ElementDataSourceGate.tsx:377). The parent scope sits outside the child gate and is given the parent binding only (:808–:824).
  • RENDERED by the console … measured at the console pin 4016370af3, which carries the reader d81e46043 (TSDoc) — .objectui-sha is 4016370af3c9c9dafec2bc5299afd8e8079701b8 on origin/main and on this head; d81e46043 is its ancestor; scripts/sdui-manifest.record.json records that sha as objectuiSha, and sdui.manifest.json:1521 lists parentDataSource as an input of record:related_list.

No clause is one the pinned renderer does not honour (Prime Directive #10): none to name. Two nuances, named and not findings: with a named view and no binding sort, the composed sort is the view's (ComposedElementDataSource.sort, objectui packages/core/src/data-scope/element-data-source.ts:142–:144), so "its sort" reads as the binding's effective sort; and a read that fails for any reason other than forbidden draws the family's error panel, not the absence (R recordBinding.tsx:471–:473; pin test :219–:228), which the describe's "when no row answers" does not cover and the TSDoc separates correctly. Right.

D3 — the rewritten api-arm clause (dev deviation 2). Old text: "that arm is still unrendered". False at the pin: objectui packages/react/src/SchemaRenderer.tsx:1174 useElementApiDataSource(schema) binds the API arm for every node, landed in c4dd7078ed, an ancestor of the pin (the ledger's own #23016 LIVE RECORD says so). New text: the key takes the object binding only, "the one the sibling blocks read and the one objectui's parent read takes (isElementDataSourceConfig asks the binding for an object)". At the pin, objectui packages/core/src/data-scope/element-data-source.ts:169–:176 returns true only for a plain object with no find function and a non-empty string object; an API binding { provider: 'api', read: { url } } has no object, so ParentRecordBinding (R recordBinding.tsx:274) leaves the children untouched, and the three sibling record blocks gate on the same predicate (R recordBinding.tsx:41–:43 isBound). On the spec side ElementDataSourceSchema is a strict object with object required, so the API arm is refused at parentDataSource at parse — the released #23134 text still holds. The clause is true and claims nothing the code does not do. Right.

D4 — the ledger _note. The PLANNED RECORD (#23134 …) sentence and its three followers ("PLANNED, not live: measured at objectui @f0532d8a … nothing reads the key", the carrier, "When that leg lands … this sentence is rewritten") are replaced by a LIVE RECORD (#23134 …; rewritten from its PLANNED record by #23585 once the renderer shipped) sentence in the exact form of the #23016 LIVE RECORD beside it ("LIVE: measured at the console pin .objectui-sha @…, which carries the renderer, objectui @…"). Both read points carry the objectui: realm mark (objectui: packages/plugin-detail/src/renderers/record-related-list.tsx, objectui: packages/plugin-detail/src/renderers/recordBinding.tsx), and every clause of the sentence (handed to ParentRecordBinding; BoundRecordProvider asks the page's own adapter for one row, $top: 1, by the binding's sort; relationshipValueField included; no row or forbidden draws NoParentRecord and queries no child rows; absent, the record page's context remains the parent) is the code read in D2. The closing sentence now says the describe no longer carries the marker — true (D1). check-liveness.mts scans evidence and producer pointers for scanned statuses (:903–:911) and gives note prose no scan of its own (:541–:548), so the sentence is prose the gate parses as JSON only; Spec property liveness 118104511477 is success, so the ledger still parses and every scanned pointer resolves. True and rightly cited.

D5 — the regions-row read-point entry [10] (dev deviation 1). Base: record:related_list.parentDataSource (PLANNED): the objectui measurement is the file _note's (objectui @f0532d8a, the record context read in RecordRelatedListBody); no new evidence here;. Head: (the read, since objectui d81e46043, objectui#12407; R = objectui @4016370af3c9 packages/plugin-detail/src/renderers/): R record-related-list.tsx#RecordRelatedListRenderer … and its no-row answer R record-related-list.tsx#NoParentRecord; R recordBinding.tsx#ParentRecordBinding (unbound: the children as they are) and R recordBinding.tsx#BoundRecordProvider ($top: 1 under the binding's $orderby); the LIVE record is the file _note's;. All four symbols exist at the pin (R record-related-list.tsx:808, :772; R recordBinding.tsx:263, :375). The list's own framing sentence says each entry names the ComponentPropsMap row and member it reads, then its read points in the spelling objectui @SHA FILE#SYMBOL; entries [11] and [33] use the same R = …/E = … abbreviation; entry [8] (the NAME's measurement, recordBindingBlock at @f0532d8a) is left standing. The home is right: the ledger keeps no parentDataSource row (component-level keys sit under the undrilled page/regions container, _note and the regions note's fourth sentence), so this list IS the ledger's per-key evidence index (entry [109]: "the per-key read points are recorded under each props schema below"), and the claim's "the parentDataSource row's status, evidence … if the ledger keeps one" lands here. Dropping (PLANNED) with the read points attached is the right edit. Right.

D6 — pin E re-true. PLANNED_MARKER = /\[planned|not yet implemented|coming soon/i is byte-identical to MARKER_RE.planned at check-liveness.mts:501. The live case asserts the describe is a string and the matcher reads nothing in it; the marker returning ([PLANNED — …) matches \[planned case-insensitively and fails it — the dev's one-time reverse verification reported exactly that direction (1 failed, 17 passed), consistent with the code. The DARK control runs the same matcher over the old describe's opening and asserts a hit, so a matcher that silently matched nothing would fail the control: meaningful. Residual, not a finding: the regex is copied because MARKER_RE is a module-private const in a .mts script; the test's docblock names its source, so a later change to the checker's matcher is a drift to re-true by hand. Right.

D7 — generated reference. content/docs/references/ui/component.mdx:1971 changes by the marker's removal and nothing else, regenerated by gen:docs in its own commit (131a561); no other generated shard moves. Type Check · source gates 118104515198 (check:authorable-surface, check:docs, check:generated --reconcile-only) is success. Right.

D8 — card criterion on the head. git grep -n "PLANNED — not yet rendered" -- packages/spec/src/ui/component.zod.ts names :1170 (docblock) and :1175 (BOUND_RECORD_BLOCK_EMPTY_STATE_DESCRIPTION) only, neither on parentDataSource; git grep -c "PLANNED RECORD (#23134" -- packages/spec/liveness/page.json exits 1. Met.

② Semver level

.changeset/23585-related-list-parent-data-source-rendered.md: '@objectstack/spec': patch, body line Clause-②: no (…), no (widening)/(narrowing) arm; the PR body carries the same Clause-②: no. What the diff publishes: a .describe() string and a TSDoc block on RecordRelatedListProps.parentDataSource, whose declaration ElementDataSourceSchema.optional() is unchanged; page.zod.ts (the binding and the dataSource union at :799) is untouched. No schema shape, export, error code or issue path moves; no authorable-surface, api-surface or json-schema shard is in the diff. Check Changeset 118104513054 and 118106960089 are success; Type Check · consumer gates 118104515628 (check:api-surface) is success. Clause-②: no and patch are right.

③ Boundary flags

  • Deviation 1 (regions entry [10]) — answered in D5: right home, right spelling.
  • Deviation 2 (api-arm clause) — answered in D3: true at the pin, no over-claim.
  • Deviation 3 (merge commit 53624a1 without the trailer pair) — the two authored commits (14621d7, 131a561) end with the model-free Claude-Session: + Co-authored-by: pair; the merge commit carries git's default subject and no trailers; no model identifier appears in any commit, the PR title or body, the changeset, the ledger or the diff. The merge pulled origin/main (04669a467e, objectql and service-automation only), outside every package this diff touches. Accepted as declared; not a contract item.
  • Deviation 4 (harness attribution vs AGENTS.md) — AGENTS.md wins on its own terms; the PR body footer is the session-URL form a PR body takes. Right.
  • Deviation 5 (worktree removed) — not a contract item.
  • Out-of-scope finding 1 (emptyState markers :1170/:1175 still true) — confirmed at the pin: git grep emptyState over objectui packages/plugin-detail/src with tests excluded answers nothing; widened over packages/react/src and packages/core/src for record/bound reads, nothing. The three bound record blocks' emptyState (spec(ui): the bound record blocks (record:details / record:highlights / record:quick_actions) accept no authored emptyState, so a 0-row binding draws only the renderer placeholder (objectui#12932) #24266) has no reader at the pin; the markers stay and are not this card's. Holds.
  • Out-of-scope finding 2 (release-owned texts) — the diff touches neither .changeset/pre/23134-related-list-parent-data-source.md nor packages/spec/CHANGELOG.md nor content/docs/releases/. Both are RELEASE-OWNED under the Documentation Guardrails and never edited in a code PR; the released sentence ("Planned, not yet rendered … objectui reads it once a release carrying this key is installable there") was true when the entry was cut and is dated by it, so it is not a factual error and no docs-only amendment is owed. Holds.
  • PR hygiene — draft, head repo equals base repo, 49 additions / 17 deletions over five files, no governed path (Governed Surface Queue Guard 118104512653 success), Fixes #23585, needs:contract-review; the card's newest Claim: (6267435237) names this branch and the claim guards are success.
  • Check-runs on the head, read at 2026-10-19T14:21Z — completed success: Lint & Repo Gates 118108004065, Lint · ESLint, Lint · repo & tooling gates, Lint · docs & code guards, Lint · CI release & engine gates, the three Lint · scoped jobs, Lint · PM dispatch-gates, Type Check · source gates, Type Check · consumer gates, Type Check · debt ledger, Build Core 118104720086, Build Docs 118104720215, Check Changeset (both runs), Check PR Size 118104513683, Check Documentation Links, Flag docs affected by code changes, Spec property liveness 118104511477, Governed Surface Queue Guard, Dogfood Regression Gate 118104721135, filter, Auto Label, and the three claim/closing/single-writer guards. In progress: Test Core (1/6) through (6/6) and Type Check · workspace 118104515400. Completed skipped (main-battery or opt-in on a PR run): Console Pin Gate, Temporal Conformance, Dogfood Verify CLI, the Dogfood shard matrix, Packed-tarball smoke, and the second Auto Label and Check PR Size runs. No run is red. This verdict stands on the diff and the pinned code; the landing waits for the required set to finish green (Test Core and the Type Check aggregate), which the owning seat reads before it arms anything.

Implemented-by: claude/issue-23585-parent-data-source-live
Reviewed-by: session_019oFY6Hi35J7atvgVFnmoaK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review adopted · domain:spec seat 2 · session session_019oFY6Hi35J7atvgVFnmoaK · 2026-10-19T14:40Z

  • Adopted: contract review 6269492683.
    • Served-tier: CONTRACT_REVIEW_TIER, the schema face.
    • Head-sha 53624a10cfb6f28c2b4de4c50df1bde17a404fbd is the current head.
    • PASS, with no FAIL item.
    • Every describe and TSDoc clause is read at the objectui .objectui-sha pin 4016370af3 and quoted to file and line.
    • The rewritten api-arm clause is true and claims nothing more.
    • The ledger entries are right.
    • needs:contract-review is removed in this act.
  • The review's residual note: pin E copies check-liveness's planned matcher rather than importing it (MARKER_RE is module-private). Not a finding, and it stays as is.
  • Pre-landing on this head:
    • 45 check-runs: 37 success, 8 skipped. The required contexts are green.
    • check-expected-skips: OK.
    • check-governed-merges: not governed, +49/−17.
    • mergeable_state: clean.
  • Landing: pr_ready then automerge_enable, in this act. Fixes #23585 closes the card.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 19, 2026 14:41
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 19, 2026 14:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit ffba02e Oct 19, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-23585-parent-data-source-live branch October 19, 2026 15:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ui size/s tests tooling

Projects

None yet

2 participants