Skip to content

docs(spec): the page element's api data source is described as rendered, and visibleWhen names where data is mounted - #24460

Merged
objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-24300-api-binding-rendered
Oct 19, 2026
Merged

objectstack-fleet[bot] merged 9 commits into
mainfrom
claude/issue-24300-api-binding-rendered

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #24300
Clause-②: no

The page element's api data source (ElementApiDataSourceSchema) is rendered by the console at its pin, and @objectstack/spec now says so. Before this change, its describe opened with [PLANNED — not yet rendered], and PageComponentSchema.visibleWhen said that no renderer reads the binding. Both told an author, and an AI author first, not to use a binding the console renders. The change is describe and TSDoc text only: no accept set moves, and every refusal keeps its issue code and path.

What the console renders, measured at the pin

The pin is .objectui-sha 0707b4ae37 on main. It carries objectui c4dd7078ed: git merge-base --is-ancestor c4dd7078ed 0707b4ae37 exits 0 in ../objectui, an exit 0 that proves itself. Every read below is git show 0707b4ae37:PATH, not objectui main.

  • packages/react/src/SchemaRenderer.tsx calls useElementApiDataSource(schema) for every node (:1157). Once the read is ready, the node's evaluation scope is { ...ambientScope, data: apiData } (:1231), and this scope evaluates its expression holes and its visibleWhen (:1982). The same value goes to the subtree through PredicateScopeProvider (:1161, :2524). While the read is loading the node draws a loading panel, and when the binding is refused or hits an error it draws an error panel. Both are drawn before visibility, so visibleWhen is decided only once the value is in (:1285, :2165).
  • packages/react/src/element-data-source/useElementApiDataSource.ts parses the binding with ElementApiDataSourceSchema first (refused carries the schema's own message, and nothing is read). It then sends one GET through SchemaRendererContext.apiFetch, the console's signed-in fetch, and selects path.
  • Page regions render each node through SchemaRenderer: packages/components/src/renderers/layout/page.tsx passes the region's components to PageBlockGrid (page-block-grid.tsx:56), and toRenderableSchema is identity for an object.
  • The generic hero, stat, usage-bar, checklist and tile-grid blocks are registered in packages/app-shell/src/console/blocks/productBlocks.tsx (:360–:408).
  • The mounted-roots sentence holds at the pin: buildExpressionScope (packages/app-shell/src/providers/ExpressionProvider.tsx:181) returns current_user, user, ctx.user, os.user and features, and configEvaluationScope (SchemaRenderer.tsx:668) adds record (only when there is a row) and page. No root is claimed that is not mounted.
  • The page:tabs item evaluator (packages/components/src/renderers/layout/containers.tsx:645) writes data: recordData after spreading the ambient scope. So on that surface data stays the row, even inside an element an api data source binds.
  • The framework's own console is built from that pin: scripts/build-console.sh builds @object-ui/console "at the SHA pinned in .objectui-sha", and scripts/sdui-manifest.record.json records objectuiSha 0707b4ae37…. So no "which console" caveat is needed.

What changed

  • packages/spec/src/ui/page.zod.ts
    • ElementApiDataSourceSchema: its describe drops the marker and says what the console renders. Its docblock section "Planned, not yet rendered" becomes "Rendered by the console, on a page element", with the read points cited by the objectui: realm marker.
    • PageComponentSchema.visibleWhen, describe and TSDoc: the renderer mounts no data root on an element that no api data source binds, where a data.* read still faults and the gate never gates. On an element one binds, data is the bound value, there and on every element nested inside it. The TSDoc's measured-scope sentence names that one added root. The section heading is now "data — only where an api data source binds it".
    • PageComponentSchema.dataSource TSDoc: "(planned in the renderer …)" now reads "rendered by the console".
    • Rider 6259026494: the binding's refusal, unknown-key surface and sibling describe sentences no longer name a page element. For example, "read.url of an api data source must be …", the surface "this api data source", and the write, body, method and path messages. The same change also made door-neutral the object guidance, which told an author to "keep object": the navigation entry that PR feat(spec): a navigation entry may declare one API data source, which its visible reads as data #24427 adds admits no object binding, so that advice is now scoped to "where the dataSource also takes the object binding (a page element's does)". Each refusal keeps its issue code and path. The objectui test that reads the refusal text asserts only same-origin (useElementApiDataSource-12197.test.tsx:141), and the new wording keeps it.
  • packages/spec/src/ui/component.zod.ts: only the page:tabs item visibleWhen TSDoc face that the pin reads. It now says that the component-node evaluator mounts no data root, except on an element an api data source binds, and that on this surface data stays the row. Patch round 2 (claim amendment 6259996449): the same key's .describe() parenthetical now agrees with it: "(the component-node evaluator mounts no data root save on an element an api data source binds, where data is that binding's value)".
  • packages/spec/src/ui/expression-scope-data-root.pin.test.ts: re-trued, not deleted. Rule (2) now admits a data sentence only if it says "NO data", ties data to provider: 'api', or is about page:tabs. A sentence that mounts data without the binding is still refused. The "two texts agree" block becomes "three texts agree": the binding's describe and both visibleWhen faces carry no planned reading (six matchers), and both visibleWhen faces name the binding as the one data carrier. Two DARK controls are added: one shows that an overclaiming sentence fails rule (2), and one shows that the three replaced planned texts are caught by the planned matcher. The pin has 16 cases (13 before).
  • packages/spec/liveness/page.json: the _note sentence "PLANNED RECORD (spec(ui): a page element dataSource can name an API endpoint (provider: 'api'), so a product page composes from a control-plane summary in metadata instead of a product-specific React widget (maintainer ruling 2026-10-12) #23016 …)" becomes a "LIVE RECORD", measured at the pin, with the objectui: read points. Only the _note changes; the regions row that PR feat(spec)!: retire a page action row's own patch — a row's operation: 'update' runs the declared action its name names (#24394) #24442 edits is untouched.
  • content/docs/references/ui/page.mdx and content/docs/references/ui/component.mdx: regenerated by gen:docs. content/docs/references/ui/app.mdx: regenerated in patch round 3, because PR feat(spec): a navigation entry may declare one API data source, which its visible reads as data #24427's navigation entry embeds ElementApiDataSourceSchema by reference (18 embeddings × the provider, read and path rows).
  • content/docs/ui/pages.mdx (hand-written; claim amendment 6260050788, declared to domain:devx on [PM seat] domain:devx · seat 2 — 🟢 os-sales · session_016PVjjuVPte5XEJvaQx1E4t · serial (maintainer) #20163): the "Not rendered yet" bullet of "The API binding's rules" now says the console renders the binding and names the blocks that read it. No other edit to the page.
  • .changeset/24300-api-binding-rendered.md: @objectstack/spec patch. Patch round 3 added one bullet for the page:tabs item visibleWhen describe parenthetical.

Not touched, by the claim's fence: packages/spec/src/ui/app.zod.ts (its navigation dataSource stays planned), the other [PLANNED] markers in component.zod.ts, and the binding's accept set. view.zod.ts:170 and the view-data-api-provider-retired migration texts call this binding "the governed door" read with the signed-in fetch. They never call it planned, so they stay as they are (dispatch hypothesis H3, refuted for them).

Verification (at head 8951d79063, unless a line names another sha)

  • Patch round 3, head f0ce6bc1e3: origin/main ff4840756d (carrying PR feat(spec): a navigation entry may declare one API data source, which its visible reads as data #24427 and PR feat(spec)!: retire a page action row's own patch — a row's operation: 'update' runs the declared action its name names (#24394) #24442) was merged with bash scripts/pm/os-regen-merge.sh, with no rebase or force-push. component.mdx and app.mdx were regenerated with check:generated --fix. The hop proof compares the +/- lines of git diff -U0 9112bcd887 0ea4d853a2 and git diff -U0 ff4840756d f0ce6bc1e3, both outside content/docs/references and .changeset. It is empty, so the hop moves only generated pages and the changeset. At f0ce6bc1e3, vitest run src/ui/ gave Test Files 132 passed (132), Tests 5200 passed (5200), and check:generated and check:docs exited 0.

  • Reverse verification at 1dad7bbe4b (the pin file is byte-identical at the final head), one shot, expected direction red. page.zod.ts was restored to the merge base 9112bcd887 (hash-object matched the base blob 448b9617…; the marker count was 1), and the re-trued pin ran against it: Tests 4 failed | 12 passed (16). The four failing cases were the docblock's binding sentence, the describe's binding sentence, the binding's "renders" sentence and the no-planned-reading sweep. The file was then restored with git checkout HEAD --: hash-object matched the HEAD blob 646f9a43…, and git diff HEAD was empty. The observed direction was red.

  • pnpm --filter @objectstack/spec test at 1dad7bbe4b: Test Files 697 passed (697), Tests 21271 passed | 1 todo. The final commit 8951d79063 changes one TSDoc paragraph. At that head, every test that reads page.zod.ts source was re-run: 6 files / 362 tests passed, and scripts/file-description.test.ts plus scripts/schema-index.test.ts gave 2 files / 130 tests passed.

  • pnpm --filter @objectstack/spec typecheck: exit 0 (tsc --noEmit, check:scripts-typecheck, check:test-typecheck OK).

  • pnpm --filter @objectstack/spec check:generated: the only stale artifact was content/docs/references/**, regenerated with gen:docs. check:authorable-surface, check:api-surface, check:liveness and check:strictness-ledger were all green.

  • node scripts/pm/dispatch-gates.mjs --commands at 8951d79063 derived 111 commands, and all were run locally. Reconciliation result: --ran: "111 derived famil(ies) accounted for — 111 run, 0 NOT-MEASURED". 110 exited 0. Six had first refused with PREREQUISITE NOT MET and passed once lint, formula, client-react and objectql were built. One exited 1, pnpm check:platform-checklist, and that red predates this branch: docs/qa/platform-checklist/areas/access-security.json cites packages/core/src/security/resolve-authz-context.ts#legacyGrantAnchorRetired, which is absent there on the merge base 9112bcd887 and on origin/main 7e1ce9d945. The symbol now lives in packages/plugins/plugin-auth, since b2e78701f7. This diff touches no file under packages/core or docs/qa. main's ec951e29e4 (docs(qa): re-point the platform-owner-email item's legacy-grant anchor at grantAnchorsPlatformStanding #24463) re-pointed that anchor, and the check reads OK — 16 areas, 327 items at f0ce6bc1e3.

  • Verdict lines: check:docs "227 generated files in sync"; check:api-surface "public API surface + factory signatures unchanged"; check:doc-authoring "19075 customer-facing string(s) … clean"; check:issue-citations "every citation this change adds resolves"; check:nul-bytes "no raw ASCII control bytes"; check:empty-changeset and check:adr-0087-registration green.

  • Local-only pnpm check:dev-prereqs: "@objectstack/spec … built from the sources on disk".

Acceptance notes

  • The page:tabs item visibleWhen describe was left for the seat. Done in patch round 2: the seat widened the claim's surface (6259996449), and the describe now agrees with its TSDoc face.
  • carrier: none · The committed SDUI manifest (sdui.manifest.json, objectuiSha 0707b4ae37, 107 components) lists none of the five product blocks the pin registers (hero, stat, usage-bar, checklist, tile-grid). The JSX page gate validates components against that manifest. This is a read-only inference: no os validate run measured it, so it is noted, not filed.
  • carrier: none · The docblock's "## The ruling" section still justifies method: 'GET' through @object-ui/core's ApiDataSource, which the shipped reader does not use (its own GET is cited in the new section). The sentence stays true about that adapter. It sits outside the fence and is left as it was.
  • main was merged in once, at ff4840756d (patch round 3). PR feat(spec): a navigation entry may declare one API data source, which its visible reads as data #24427 reuses ElementApiDataSourceSchema on the navigation entry, so this branch's describe change also moves content/docs/references/ui/app.mdx, which the queue's check:docs would otherwise find stale.

Generated by Claude Code

…; visibleWhen names where data is mounted

ElementApiDataSourceSchema drops its planned marker and says what the
console renders at its pin. PageComponentSchema.visibleWhen's describe and
TSDoc, and the page:tabs item TSDoc face, name the api data source as the one
data carrier. The binding's refusal and unknown-key messages no longer name a
page element. The expression-scope data-root pin is re-trued to the shipped
fact, and the page liveness note records the binding as live.

Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu
Co-authored-by: Claude <noreply@anthropic.com>
…ema's own message

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

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/spec, touching 5 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.

2 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 PageComponentSchema (symbol, a top-level const))
  • content/docs/ui/pages.mdx (via /api/v1/cloud/home (route, a path literal in ElementApiDataSourceSchema))

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

  • content/docs/releases/v15.mdx (via PageComponentSchema (symbol, a top-level const), PageTabsProps (symbol, a top-level const object))
  • content/docs/releases/v17/17-7.mdx (via PageComponentSchema (symbol, a top-level const))

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 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb → packageMentionDocs.

Which tree this was computed on

This run read content/docs from bbf5ea9185698eb385d8f6d6856cf087cd511b78 — the merge of head f0ce6bc1e3f2ba0333eaa5709bb5c0c1ca263286 into base 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb, 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 bbf5ea9185698eb385d8f6d6856cf087cd511b78 && git checkout bbf5ea9185698eb385d8f6d6856cf087cd511b78
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb f0ce6bc1e3f2ba0333eaa5709bb5c0c1ca263286 && git checkout -B drift-repro 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb && git merge --no-ff f0ce6bc1e3f2ba0333eaa5709bb5c0c1ca263286

node scripts/docs-audit/affected-docs.mjs --json 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb

⚠️ 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 31ce45c071bbcb6e92a730ce19ebf30dcfa361cb → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…a source's data root, as its TSDoc does

Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu
Co-authored-by: Claude <noreply@anthropic.com>
…nsole renders the api binding

Claude-Session: https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 0ea4d853a264972a2444c52f3705f72013473f1b
Local-runs: none
Scope: packages/spec/src/ui/component.zod.ts, packages/spec/src/ui/page.zod.ts + packages/spec/src/ui/expression-scope-data-root.pin.test.ts (the gate that enforces the rewritten rule), packages/spec/liveness/page.json, .changeset/24300-api-binding-rendered.md, content/docs/references/ui/page.mdx, content/docs/references/ui/component.mdx, content/docs/ui/pages.mdx, .objectui-sha, scripts/sdui-manifest.record.json, scripts/build-console.sh, sdui.manifest.json, .github/workflows/ci.yml (Console Pin Gate), packages/spec/src/shared/strict-object.ts, packages/spec/src/ui/app.zod.ts and content/docs/references/ui/app.mdx on origin/main after #24427; and at objectui 0707b4ae37: packages/react/src/SchemaRenderer.tsx, packages/react/src/element-data-source/useElementApiDataSource.ts, packages/react/src/element-data-source/tests/useElementApiDataSource-12197.test.tsx, packages/components/src/renderers/layout/containers.tsx, packages/components/src/renderers/layout/page.tsx, packages/components/src/renderers/layout/page-block-grid.tsx, packages/app-shell/src/console/blocks/productBlocks.tsx, packages/app-shell/src/providers/ExpressionProvider.tsx

Inputs read: card #24300 body and all 8 comments (triage 6249016520, unlock 6250246512, rider 6259026494, claim 6259115316, dev reports 6259974100 and 6260445006, amendments 6259996449 and 6260050788); PR #24460 body, its 8-file list and the net diff against the merge base 9112bcd887 (+203 / -91); the 48 check-runs on the head: 41 success, 7 skipped (Console Pin Gate, Dogfood matrix, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance, and two skipped duplicates of Auto Label and Check PR Size), 0 failure. Every file below was read with git show at the head, at origin/main, or at the objectui pin; nothing was built, run or re-run.

① Derived judgments

Accept set: unchanged. RIGHT. The diff to page.zod.ts touches surface, history, guidance.* strings, .describe() strings, two error: message functions and TSDoc. strict-object.ts declares those options as message carriers only (guidance: prescriptions for keys the shape does not declare; aliases untouched); the shape, the .strict() closure, SAME_ORIGIN_API_PATH_RE, ELEMENT_API_RESPONSE_PATH_RE and the z.literal('GET') arm are byte-identical. Every refusal keeps its issue code and path. check:api-surface, check:authorable-surface and check:strictness-ledger ran green inside the required TypeScript Type Check job on the head. component.zod.ts changes one TSDoc paragraph and one .describe() parenthetical on the page:tabs item visibleWhen; no key moves.

Published prose, ElementApiDataSourceSchema describe and docblock (the card's class-b premise): RIGHT, each sentence measured at the pin. .objectui-sha is 0707b4ae37 on origin/main and at the merge base; git merge-base --is-ancestor c4dd7078ed 0707b4ae37 in /home/user/objectui exits 0. scripts/sdui-manifest.record.json records objectuiSha 0707b4ae37… and scripts/build-console.sh builds at .objectui-sha, so the framework's own console is the one measured and no "which console" caveat is owed. At that pin: useElementApiDataSource.ts:101 parses the binding with ElementApiDataSourceSchema.safeParse before any read and refused carries the schema's own issue messages joined (:105); the read is one GET with Accept: application/json, credentials: 'include' through SchemaRendererContext.apiFetch (:146-167); a non-2xx answer, a non-JSON body and a path that selects nothing each become error with a sentence naming which (:155, :159, :210). SchemaRenderer.tsx:1157 calls the hook for every node (the hook's own docblock says "Called unconditionally … for every node"); :1161 and :1231 build { ...ambientScope, data: apiData } once ready; :1285 returns the working copy unevaluated while the binding is loading, refused or error, so holes and visibleWhen are decided only once the value is in; :2165-2178 draw the loading panel or ElementApiDataSourcePanel before the _hidden test; :2524 wraps the rendered node in PredicateScopeProvider with the subtree scope. Page regions reach SchemaRenderer through page.tsx:229 and page-block-grid.tsx:56, so "on every page element" holds. The stat, usage-bar and checklist blocks (and hero, tile-grid) each declare API_BINDING_INPUT as their dataSource in productBlocks.tsx:350-408, so "built to take it" holds; the describe names three and content/docs/ui/pages.mdx names five, both true.

PageComponentSchema.visibleWhen, describe and TSDoc: RIGHT. The mounted-roots sentence is unchanged and still true: buildExpressionScope (ExpressionProvider.tsx:181-191) returns exactly current_user, user, ctx.user, os.user, features; configEvaluationScope (SchemaRenderer.tsx:668) adds current_user, record only when a row exists, and page. The new sentence "data is mounted only on an element whose dataSource is an api data source … there, and on every element nested inside it" matches :1231 and :2524; "NO data root on an element that no api data source binds" matches the ruling-B comment block at :1256-1263 and the untouched ambientScope pass-through at :1145. No root is claimed that is not mounted.

page:tabs item visibleWhen, both faces in component.zod.ts (claim amendment 6259996449): RIGHT. containers.tsx:645-651 builds the item evaluator as the row's bare fields, then ...predicateScope, then record: recordData and data: recordData written after the ambient spread, so inside an api-bound element the item evaluator's data is still the row, as the TSDoc now says, while the component-node evaluator's data is the binding's value, as the parenthetical now says. The describe and TSDoc faces agree with each other and with PageComponentSchema.visibleWhen.

Rider 6259026494, door-neutral refusal and strict-surface sentences: RIGHT, including the dev's declared step past its letter. origin/main app.zod.ts:419 (merged in #24427 as 26501ae24f) declares dataSource: ElementApiDataSourceSchema.optional().describe(...) by reference, so the navigation door now raises the same read.url, method, path, body, write and unknown-key messages; none of them names a page element any more. The object guidance is the one sentence that could not be made neutral by deletion alone: the navigation door admits no object binding, so "keep object" was false there; scoping it to "where the dataSource also takes the object binding (a page element's does)" is true on both doors. The navigation key keeps its own [PLANNED — not yet rendered] describe (the claim's hold-off), and gen:docs prints the key's describe rather than the inner schema's, so a navigation author does not read "the console renders it on every page element" on their door: app.mdx on origin/main carries no "API binding of a page element" sentence.

The pin, expression-scope-data-root.pin.test.ts (re-trued, 13 to 16 cases): RIGHT as the gate for the rewritten rule. Rule (2) now admits a data sentence that says NO data, ties it to provider: 'api', or is about page:tabs; the DARK control at :260 shows an overclaim without the binding still fails it, and the DARK control at :273 shows each of the three replaced planned texts is caught by PLANNED_READINGS, so the three-text toEqual([]) at :227-235 is a reading and not a vacuous matcher. The new TSDoc assertion at :189-195 anchors on "The one data this surface has" and requires provider: 'api', useElementApiDataSource and the objectui: read point, all present in the head's docblock. The component twin at :198-209 refuses the old "mounts no data root at all". The dev's one-shot reverse run (page.zod.ts restored to the base blob: 4 failed / 12 passed) is consistent with these matchers; I did not re-run it.

liveness/page.json _note: RIGHT. The PLANNED RECORD for #23016 becomes a LIVE RECORD measured at the pin with the three objectui: read points (SchemaRenderer.tsx, useElementApiDataSource.ts, productBlocks.tsx), each of which exists at 0707b4ae37; the regions row that #24442 edited is untouched; the #23134 and #24266 PLANNED records stay as they were. Spec property liveness is green on the head.

Generated docs: RIGHT at the PR's base, INCOMPLETE against origin/main as of #24427, and the queue will say so. page.mdx and component.mdx are regenerated and check:docs reports 227 files in sync on the head. But #24427 landed after this PR's merge base and gen:docs now also expands ElementApiDataSourceSchema's property describes into content/docs/references/ui/app.mdx: on origin/main that file carries the OLD provider / read / path describes ("Binds the element to one same-origin API endpoint instead of an object", "the same-origin endpoint the element binds", "the element binds the whole response") at 17 nested-shape sites (:49-51, :388-390, … :1343-1345). This PR's net diff cannot regenerate a file its base did not have the reference in, so after the merge app.mdx is stale until gen:docs runs again. lint.yml runs on merge_group, and check:docs sits in the required TypeScript Type Check job, so the queue refuses the stale artifact rather than landing it; it is a landing precondition, not a defect of this diff. Escalated in ③.

Sibling checkout: no pin risk. The Console Pin Gate (ci.yml:2917-2990) is skipped on pull_request and, when it runs, builds the console at the pin and asserts the dist bundles this tree's spec; it runs no objectui test. The one objectui test that reads the refusal text, __tests__/useElementApiDataSource-12197.test.tsx:141 (the PR body cites it without the __tests__/ segment, a citation slip, not a claim error), asserts only same-origin, which the new wording keeps. git grep at the pin finds no objectui test asserting "page element's api", "this element api data source", "A page element only reads" or "a page element's read".

Fence and hold-offs: honoured. app.zod.ts is untouched on the branch; the [PLANNED — not yet rendered] markers left in component.zod.ts at the head (:1175 emptyState, :1747 parentDataSource) are #24266's and #23134's; page.zod.ts:855 and :1965 keep the app-page record binding's own planned wording, a different key.

② Semver level

.changeset/24300-api-binding-rendered.md: @objectstack/spec patch. RIGHT. What the diff publishes is describe text (the JSON schema description fields and the generated reference docs), the strict-surface and refusal message strings, and TSDoc; nothing an author can write is added, removed, renamed or re-typed, and no export changes (check:api-surface: unchanged). A describe and message correction in a released package is a fix, so patch and not skip-changeset is the floor and it is met.

Clause-②: no on the PR body and in the changeset body, with no (widening) / (narrowing) arm. RIGHT and consistent: the accept set is byte-identical (① above), and Check Changeset and check:adr-0087-registration are green on the head. No breaking marker or ADR-0087 disposition is owed.

One nit, not a verdict item: the changeset's bullets name the ElementApiDataSourceSchema describe, the PageComponentSchema.visibleWhen faces and the refusal messages, but not the page:tabs item visibleWhen describe parenthetical that patch round 2 also publishes. The level is unaffected; the dev already offered the one-line addition under pr_body_edits_for_the_seat and it stays the seat's call.

③ Boundary flags

Both dev reports carry open_questions: []. Every declared deviation and out-of-scope finding, and the two claim amendments, answered:

  • Amendment 6259996449 (the page:tabs describe face): delivered in patch round 2, component.zod.ts:984, and judged RIGHT in ①. The PR body's first acceptance note is struck through accordingly.
  • Amendment 6260050788 (content/docs/ui/pages.mdx, declared to domain:devx on [PM seat] domain:devx · seat 2 — 🟢 os-sales · session_016PVjjuVPte5XEJvaQx1E4t · serial (maintainer) #20163): the one "Not rendered yet" bullet becomes "Rendered by the console"; its five block names and its loading / error panel sentence are measured true at the pin (①). No other line of that page changes.
  • Deviation "rider applied a little past its letter" (object guidance scoped to the door that takes an object binding): answered RIGHT in ①; it is the only reading true on both doors and it moves no code or path.
  • Deviation "main not merged" (reported 5, then 10 commits behind): the merge is now owed, not optional. ESCALATED to the landing seat: before queueing, merge origin/main (which carries feat(spec): a navigation entry may declare one API data source, which its visible reads as data #24427 and feat(spec)!: retire a page action row's own patch — a row's operation: 'update' runs the declared action its name names (#24394) #24442), run pnpm --filter @objectstack/spec build then check:generated --fix (expected: only content/docs/references/ui/app.mdx stale, regenerated by gen:docs), and let the pin re-run. GitHub's mergeable: clean answers textual overlap only; check:docs in the queue answers this. Whether the merge-round head needs a fresh stamp is the seat's rule to apply; this record names 0ea4d853a2 and the generator-only additions the merge round is expected to carry.
  • Deviation "--maxWorkers=2 after a bare --": acknowledged by the dev as most likely discarded by vitest (AGENTS.md's own trap); the whole package ran and Test Core 1-6 of 6 are green on the head, so nothing rests on the local reading.
  • Deviations "runner stopped and re-derived", "test:repo not run locally", "4 pushes", "attribution trailer per AGENTS.md", "check:dual-build-cjs-loads NOT MEASURED at the head (exit 3, read exit 0 at 8951d79063)", "check:dev-prereqs refused its whole-workspace precondition": each is a local-run narrowing the head's check-runs cover (Build Core, Lint & Repo Gates and the Type Check lanes are green); none hides a finding.
  • Out-of-scope finding, class a, pnpm check:platform-checklist exit 1 on access-security.json:2823 (legacyGrantAnchorRetired moved to plugin-auth in b2e78701f7, fix(security): the single-posture platform-admin grant is owned by the Default Organization, and every reader of platform standing reads it #24373): independent of this diff (no file under packages/core or docs/qa is touched) and kept out of per-PR CI by lint.yml:2607. ESCALATED to the seat as its own card if none is open yet; the dev's dedupe words stand. Not a condition on this PR.
  • Out-of-scope finding, carrier none, "the committed SDUI manifest lists none of the five product blocks": now MEASURED rather than inferred. sdui.manifest.json on origin/main (record objectuiSha 0707b4ae37…, 107 component keys) has no hero, stat, usage-bar, checklist or tile-grid key, and packages/lint/src/validate-jsx-pages.ts:60-62 compiles a JSX page against that manifest with unknown-component diagnostics. The describe, the _note and pages.mdx say these blocks are built to take the binding, which is true of the console at the pin, so the texts stay; but an author who follows them into a JSX page meets the framework's own gate. This predates the PR (the replaced text named the same three blocks). ESCALATED to the seat as a class-b card on the manifest generator's enumeration (scripts/gen-sdui-manifest-node.mjs, the 16 registry modules) versus app-shell's console block registrations; dedupe words: sdui.manifest.json, productBlocks, hero stat usage-bar checklist tile-grid, validate-jsx-pages unknown component.
  • Out-of-scope finding, carrier none, the docblock's "The ruling" section grounding GET-only in @object-ui/core's ApiDataSource: outside the fence and still true of that adapter; the new section cites the shipped reader's own GET. No action.
  • pr_body_edits_for_the_seat: the PR body as read already carries the round-2 bullets and the struck-through acceptance note; only the optional changeset addition (② nit) remains open.

Implemented-by: claude/issue-24300-api-binding-rendered
Reviewed-by: session_01WnW7HvfnfHbLg1fpkHX3Mu

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Regen-provenance: 6260617692 · 0ea4d853a2 → f0ce6bc1e3 · diff of the +/- lines of git diff -U0 9112bcd887 0ea4d853a2 and git diff -U0 ff4840756d f0ce6bc1e3, both with pathspec . ':!content/docs/references' ':!.changeset' → (empty)

domain:spec seat 1 · os-warren · session session_01WnW7HvfnfHbLg1fpkHX3Mu · 2026-10-19T06:15Z. The hop is the merge of origin/main ff4840756d (2ef79ee4c0, by os-regen-merge.sh), two regeneration commits (content/docs/references/ui/component.mdx and app.mdx, by check:generated --fix), and one changeset bullet. Outside content/docs/references/** and .changeset/**, the PR's own net lines against its new base are line-for-line the ones contract review 6260617692 judged at 0ea4d853a2. Re-run here on the committed trees: exit 0, no output.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT · domain:spec seat 1 · os-warren · session session_01WnW7HvfnfHbLg1fpkHX3Mu · 2026-10-19T06:33Z · head f0ce6bc1e3 · card #24300 (claim 6259115316, amended 6259996449 and 6260050788; rider 6259026494).

  • Form: first line Fixes #24300, no other closing keyword. Clause-②: no at line start, matching the claim and the changeset. Assignee os-warren.
  • Path face (check-governed-merges.mjs --pr 24460): NOT governed; 403 changed lines over 9 files. They are the claim's surface plus the two amendments, and the three regenerated reference pages.
  • Contract review of record: 6260617692: Served-tier: CONTRACT_REVIEW_TIER, Local-runs: none, PASS at 0ea4d853a2. It judges the accept set unchanged, and every describe, TSDoc and refusal sentence measured true at the objectui pin 0707b4ae37.
    • It carries to this head through Regen-provenance: 6260951221. The hop is a merge of origin/main ff4840756d, the regeneration of component.mdx and app.mdx, and one changeset bullet.
    • The seat re-ran the hop proof on the committed trees: outside content/docs/references/** and .changeset/** the PR's net lines are identical; exit 0, no output.
  • Review ③, answered:
    • The merge the review escalated is done (patch round 3). app.mdx now carries this branch's provider / read / path describes at all 18 embeddings, and check:docs reads 227 files in sync at the head.
    • The changeset nit is taken: one bullet names the page:tabs item visibleWhen describe parenthetical.
    • check:platform-checklist: the watchdog's check:platform-checklist is red on main #24438, closed completed by main's ec951e29e4 (docs(qa): re-point the platform-owner-email item's legacy-grant anchor at grantAnchorsPlatformStanding #24463). It reads OK at the head.
    • The SDUI manifest lists none of the five console product blocks. Noted, not filed: it is a code reading with no os validate run and no named producer, so it has no reach: under the filing gate. It is recorded in the PR's Acceptance notes with carrier: none, and in the seat post's pointers. It predates this PR, since the replaced text named the same blocks.
    • The docblock's "The ruling" GET grounding: outside the fence and still true of that adapter. No action.
  • Prose face (the changeset, sentence by sentence, at the head):
    • "dropped its [PLANNED — not yet rendered] marker … the element draws a loading or error panel" matches review ① at SchemaRenderer.tsx:1157, :1231, :1285, :2165.
    • "no longer says that no renderer reads the binding … On an element that one binds, data is the bound value" matches the visibleWhen describe at the head.
    • The new page:tabs bullet quotes the parenthetical exactly as component.mdx prints it, and keeps "that surface's own evaluator still binds data to the record row" (containers.tsx:645).
    • "Each message keeps its issue code and path" holds by review ①. patch is right for a published describe correction.
  • Cross-lane: content/docs/ui/pages.mdx, one bullet, declared to domain:devx (6260050788, on [PM seat] domain:devx · seat 2 — 🟢 os-sales · session_016PVjjuVPte5XEJvaQx1E4t · serial (maintainer) #20163); no objection read.
  • Gates: 55 check-runs on f0ce6bc1e3: 46 success, 9 skipped by design (Console Pin Gate, Temporal Conformance, Dogfood Verify CLI, the Dogfood matrix, Packed-tarball smoke, and the duplicate Auto Label and Check PR Size), 0 failure (read 2026-10-19T06:33Z); mergeable_state: clean.
  • Landing: ready and auto-merge through the relay; MERGED confirmed on the queue branch and origin/main; then pm:dispatched comes off.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 19, 2026 06:34
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 19, 2026 06:34
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 19, 2026
Merged via the queue into main with commit 01fafde Oct 19, 2026
56 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24300-api-binding-rendered branch October 19, 2026 07:08
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/m tests tooling

Projects

None yet

2 participants