Repository navigation
Commit 01fafde
docs(spec): the page element's api data source is described as rendered, and visibleWhen names where data is mounted (#24460)
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 c4dd707 0707b4a` exits
0 in `../objectui`, an exit 0 that proves itself. Every read below is
`git show 0707b4a: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 #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 (#23016 …)" becomes a "LIVE RECORD", measured at the pin, with
the `objectui:` read points. Only the `_note` changes; the `regions` row
that PR #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 #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 #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 #24427 and PR #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
9112bcd 0ea4d85` and `git diff -U0 ff48407 f0ce6bc`, 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` (#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 #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](https://claude.ai/code/session_01WnW7HvfnfHbLg1fpkHX3Mu)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent fb9c22a commit 01fafde
9 files changed
Lines changed: 258 additions & 145 deletions
File tree
- .changeset
- content/docs
- references/ui
- ui
- packages/spec
- liveness
- src/ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1543 | 1543 | | |
1544 | 1544 | | |
1545 | 1545 | | |
1546 | | - | |
| 1546 | + | |
1547 | 1547 | | |
1548 | 1548 | | |
1549 | 1549 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
34 | | - | |
35 | | - | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
41 | | - | |
42 | | - | |
| 41 | + | |
| 42 | + | |
43 | 43 | | |
44 | 44 | | |
45 | 45 | | |
| |||
311 | 311 | | |
312 | 312 | | |
313 | 313 | | |
314 | | - | |
| 314 | + | |
315 | 315 | | |
316 | 316 | | |
317 | 317 | | |
| |||
338 | 338 | | |
339 | 339 | | |
340 | 340 | | |
341 | | - | |
| 341 | + | |
342 | 342 | | |
343 | 343 | | |
344 | 344 | | |
345 | | - | |
346 | | - | |
347 | | - | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
348 | 348 | | |
349 | 349 | | |
350 | 350 | | |
| |||
483 | 483 | | |
484 | 484 | | |
485 | 485 | | |
486 | | - | |
| 486 | + | |
487 | 487 | | |
488 | 488 | | |
489 | 489 | | |
| |||
0 commit comments