You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[finding] page.zod.ts visibleWhen .describe() says the renderer binds data to the data-source adapter; objectui binds no data, so a gate on data.total faults and shows in both polarities (twin of #17203, which removed app) #23036
Filed by the repo:cloud#1 seat (session session_01WVbr5J6u8BHh8EyFtcWciH), from the objectstack-ai/cloud#2777 dev report (cloud PR objectstack-ai/cloud#2811). ⛔ Not graded: grading is triage's. ⛔ Not a claim.
The claim
The spec tells an author that a page component's visibleWhen can read data (the bound data source). The shipping renderer binds no data there. A gate written from the describe, such as data.total > 0, faults; the renderer fails open, so the component shows whether the predicate is true or false. An AI author following the contract text ships a gate that never gates, and nothing refuses it.
Evidence
packages/spec/src/ui/page.zod.ts:439 on main9432bac0+: the visibleWhen describe says "The shipping renderer additionally mounts features, os.user and binds data to the data-source ADAPTER here". The docblock above it (「Ambient roots」, :414) says the same.
Measured by the cloud#2777 dev through objectui's SchemaRenderer at d758f2f2: visibleWhen { dialect: 'cel', source: 'data.total > 0' } and 'data.total == 0' both render SHOWN.
The consumer that followed the describe: the cloud#2777 v1 design (a progress checklist switched on bound counts). It was measured and moved to v2 (objectstack-ai/cloud#2801).
The describe and the 「Ambient roots」 docblock say what the renderer mounts: record, current_user (and its aliases), page, features, os.user; not data. If binding data is wanted instead, that is a renderer change in objectui, and the describe follows it. Either way the contract text and the renderer agree.
Dedupe: searched objectstack issues for visibleWhen data root, "binds data to the data-source"; only #17203 (closed, the app twin) matched.
Triage: first grade, bug · priority:p2 · domain:spec · area:records, into pm:queue. The describe says what the renderer mounts, as objectstack-ai/objectui#9308 ruling B left it
Triage seat (objectstack-wide, seat post #6015) · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-12T13:02Z. ⛔ Not a claim, ⛔ not a dispatch.
Why p2: a visibleWhen written from the describe faults, and the component shows in both polarities. A gate that never gates, with nothing refusing it, is the class (c) shape.
Direction: the visibleWhen describe and the 「Ambient roots」 docblock name only what the renderer mounts. ⛔ No data root is re-bound by this card.
So the triage direction's "⛔ No data root is re-bound by this card" still holds for today's renderer. But the visibleWhen describe and the 「Ambient roots」 docblock (:631–:656) must not contradict that PLANNED line.
The claim measures what objectui binds today. It also says how the two texts agree: today's mounted roots, and data only under the planned api binding once its renderer ships.
It also measures the page:tabs "record ROW" sentence at :656.
Claim: PM loop round 1 (#23036: a page component's visibleWhen describe and its 「Ambient roots」 docblock name only the roots the renderer mounts, as objectstack-ai/objectui#9308 ruling B left them, and agree with #23079's PLANNED api-binding line) · 2026-10-13T01:48Z
Session: session_019G5ZnRrTMiwcHG6iZM19zf
Account: os-warren (the seat's linked user as GET /user answers it; the card's assignee from this act)
Branch: claude/issue-23036-visiblewhen-roots
Worktree: objectstack-issue-23036
Domain: domain:spec
Seat: domain:spec#1 (seat post #6017)
Ruling-ref: objectstack-ai/objectui#9308 ruling B (2026-09-13), as the triage grade 6124818846 reads it
File surface (at origin/mainbcebd8409 or later; stop on breach and explain in the report):
packages/spec/src/ui/page.zod.ts: the visibleWhen.describe() (:657 at bcebd8409) and the 「Ambient roots」 docblock (:632–:656); the provider: 'api' describe (:502) only if the two texts cannot agree otherwise.
The generated references check:generated names, the tests that pin the describe text, and one .changeset/23036-*.md (@objectstack/spec patch).
⛔ No data root is bound or refused in code by this card, and there is no renderer change (that would be objectui's).
Container & model: S (describe and docblock text, with pins), mode:subagent, model: default tier (no path-derived mandate). A .describe() in a published schema moves, so it owes a review at CONTRACT_REVIEW_TIER before the queue.
Clause-②: no (text only; no accept set moves)
Responsibility: n/a — the defect is contract text, not code: the describe promises a root the renderer does not mount | none: no gate catches a describe that names an unbound root | any author or AI that writes a page component's visibleWhen from the describe; cloud#2777's v1 design did
Thread-read: 6132762281
Serial constraints cleared (all 10 open PRs' file lists read at 2026-10-13T01:47Z): no open PR touches page.zod.ts. PR feat(spec): an empty state names its one next step (link: { label, url }), and a list page carries its own emptyState #23098 ([finding] EmptyStateSchema declares no action, so an empty list cannot name its next step (a docs link, a how-to); the console's only empty-state button is add-record #23004), which did, landed as 55828c514, and the seat note 6132762281 named that wait.
This act moves the card pm:queue → pm:dispatched and assigns os-warren.
{
"issue": 23036,
"status": "done",
"branch": "claude/issue-23036-visiblewhen-roots",
"pr": "https://github.com/objectstack-ai/objectstack/pull/23146",
"pushed_sha": "8f64f1ae3484485d9cfc0d1733456cc575aaa11e",
"session": "session_019G5ZnRrTMiwcHG6iZM19zf (mode:subagent, so this is the parent's harness-stamped id, the same id the claim 6136306937 names)",
"premise_still_valid": true,
"summary": "Text only, as the triage direction rules. The PageComponentSchema.visibleWhen .describe() and the 「Ambient roots」 docblock in packages/spec/src/ui/page.zod.ts now name only what objectui mounts beyond the contract-bound roots: `features` and `os.user`. They state that NO `data` root is mounted on this surface: a `data.*` read faults and the component renders in both polarities, and the row is `record`. They tie the one declared `data` read to the PLANNED API binding, once its renderer ships, and keep the `page:tabs` item evaluator's row binding. Readings, objectui origin/main f228d710, with the four evaluator files byte-identical at the pin 1fb1ced36243: SchemaRenderer.tsx:665-676 configEvaluationScope = { ...predicateScope, current_user, record?, page }, with no data and the decision recorded at :1194; ExpressionProvider.tsx:181-190 buildExpressionScope = { current_user, user, ctx.user, os.user, features }, and the only production PredicateScopeProvider mount is ExpressionProvider.tsx:306; containers.tsx:642-649, the page:tabs item, binds data = row, spreads the row flat, plus record, current_user, page. A3: page.zod.ts:502 (the PLANNED api-binding line) is unchanged. Both texts are true together (today no data root; a data read only under the API binding once objectui#12197 ships), and a pin holds the agreement. A4 is partly falsified: the lint rule that answers `data.`-rooted page-component predicates is visibility-root-mislayered (warning), not visibility-bare-identifier (`data` is in SCOPE_ROOTS). Its message already says the element always renders, so no lint change is owed. Other readers: content/docs/references/ui/page.mdx (2 rows, regenerated). content/docs/ui/pages.mdx:178 and skills/** carry no data claim and are unchanged. component.zod.ts:994-995 TSDoc carries the same retired claim but is outside the claim's file surface, so it is not touched; it is reported below with exact text.",
"tests": "All at head 8f64f1ae3 (f43d856ca fix, 96b07ccb9 regen and changeset, a merge of origin/main dfb3ba2b2 that touched no packages/spec file). (1) New pin packages/spec/src/ui/expression-scope-data-root.pin.test.ts plus the trimmed app-root pin: 'Test Files 2 passed (2) · Tests 20 passed (20)'. (2) RED FIRST, committed fix then BASE bcebd8409's page.zod.ts written to disk under trap EXIT INT TERM with absolute paths. On-disk proof: retired-claim grep 1, new-claim grep 0, blob 7519cc64 vs HEAD blob 62efbb6f. Result: 'Tests 5 failed | 6 passed (11)', e.g. \"expected [ 'features', 'os.user', 'data' ] to deeply equal [ 'features', 'os.user' ]\". Restore proved by blob 62efbb6f == HEAD blob and an empty git diff HEAD; git status --porcelain was empty. (3) Spec suite, both projects, under the lock: 'Test Files 717 passed (717) · Tests 20995 passed | 1 todo (20996)', VERDICT command-exit 0. (4) pnpm --filter @objectstack/spec typecheck exit 0 (tsc, scripts-typecheck, test-typecheck OK). Both pin files are in tsconfig.test.json's program per tsc --listFilesOnly. (5) spec build, then check:generated named 1 stale artifact (content/docs/references/ui/page.mdx); after gen:docs: '✓ All 14 generated artifacts are up to date'. (6) dispatch-gates --commands --repo objectstack-ai/objectstack at 8f64f1ae3: 109 commands, all but check:type-check-debt run (NOT MEASURED by order), each exit captured before any pipe. First pass: 102 exit 0 and 6 exit 3 PREREQUISITE NOT MET (lint check:doc-formula-expressions and check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:lean-entry-closure, check:dual-build-cjs-loads). After building the lint, objectql and client-react closures, then the rest of the workspace: all 6 exit 0 (dual-build: '108 published require entry point(s) across 67 package(s) load'). --ran reconciliation over the final record with exit codes: '✓ dispatch-gates --ran: 109 derived famil(ies) accounted for — 108 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 claimed)', exit 0. Quoted: check:doc-authoring describe 9244 clean; check:objectui-pin-citations '55 asserting objectui pin citation(s) match .objectui-sha (1fb1ced36)'; check:nul-bytes OK; check-adr-0087-registration '1 non-breaking changeset(s) seen'. (7) A4 reading: lint vitest on validate-visibility-predicates.test.ts filtered to the page-component and layer cases, '48 passed | 148 skipped', cases asserting [VISIBILITY_ROOT_MISLAYERED] on page `data.x`. DECLARED NARROWING: turbo ls --affected names 82 packages (spec plus every dependent). The diff moves describe and TSDoc text only: no export, type, accept set or wire shape (check:api-surface and check:authorable-surface green), and a git grep of the old and new sentences outside packages/spec/src finds only the regenerated reference page and the changeset. So the other 81 packages' suites are left to CI Test Core. Blind spot: turbo ls --affected comes from the package graph, so it cannot see disk-reading tests (check:cross-package-test-inputs green, all declared), and the root suites are not on the graph. No ablation on a dist: the pin reads src.",
"mcp_calls": "0",
"api_writes": "2 strokes through scripts/pm/fleet-write (objectstack-fleet[bot]): pr_create (POST /repos/objectstack-ai/objectstack/pulls draft, then POST /repos/objectstack-ai/objectstack/issues/23146/assignees [os-warren]; run 38340935336, read-back 14859 bytes sent, 14859 stored, identical); comment (POST /repos/objectstack-ai/objectstack/issues/23036/comments, this report). Plus 4 git pushes (not REST). Zero label writes: the dispatch's budget names none.",
"ci": "in_progress at PR open; not awaited, per the role file",
"dispatch_conflicts": [
"The dispatch says a spec change runs its whole affected closure (turbo ls --affected, 82 packages). The role file's local-verification rule says importers owe tests only when the public surface moves, and that wins on conflict. I ran the spec package's two projects and every derived gate, and declared the narrowing with its blind spot above.",
"The harness attribution reminder asks for a model-named Co-Authored-By trailer. AGENTS.md's model-free trailer pair outranks it, and the pre-push hook refuses a model id. My first local commit carried the harness spelling and was amended before any push, so no model id landed."
],
"open_questions": [],
"out_of_scope_findings": [
"class: b (same family as this card) · reach: none measured. A TSDoc face, not a door; it is the twin of the describe this card fixed, and the #17203 pin header records TSDoc faces as what AI authors read. Evidence: packages/spec/src/ui/component.zod.ts:994-995 (page:tabs item visibleWhen docblock) says '`data` is the record ROW here, where the component-node evaluator binds it to the data-source ADAPTER'. Since objectui ruling B the component-node evaluator binds no `data` (SchemaRenderer.tsx:1194 at f228d710 = pin 1fb1ced36243). The same docblock cites containers.tsx:450-457 at pin 190fbd01d061; the lines are 642-649 now. The published describe at :1017 is still true. Not touched: the claim's file surface names page.zod.ts only. Suggested fold-in for this PR or its carrier: replace with '**`data` is the record ROW here**; the component-node evaluator mounts no `data` root at all (see `PageComponentSchema.visibleWhen`)', update the cite to containers.tsx:642-649 at 1fb1ced36243, and add one TSDoc-face assertion to expression-scope-data-root.pin.test.ts. Dedupe words: component.zod.ts data ADAPTER, page:tabs visibleWhen data row, component-node evaluator data.",
"carrier: 承接者:无 · noted, not filed. packages/lint/src/validate-visibility-predicates.ts:343 is a code comment recording 'a page component additionally `page` and the data-source adapter `data`'. Its argument still holds (data is in SCOPE_ROOTS); comment only, no author-visible reach. In the PR's Acceptance notes."
]
}
Read in the diff (merge base dfb3ba2b2, 5 files, +261 / −19):
PageComponentSchema.visibleWhen's .describe() and the 「Ambient roots」 docblock (page.zod.ts) now name only what objectui mounts beyond the contract-bound roots: features and os.user.
They state that no data root is mounted on this surface: a data.* read faults, and a faulting predicate renders the component. The row is record.
They tie the one declared data read to the PLANNED API binding, once its renderer ships.
They keep the page:tabs item evaluator's row binding as a separate evaluator.
The readings behind it are cited in the docblock at the pin 1fb1ced36243, and are identical at objectui main: buildExpressionScope (ExpressionProvider.tsx:181-190), configEvaluationScope (SchemaRenderer.tsx:665-676), the ruling-B line SchemaRenderer.tsx:1194, and the page:tabs evaluator (containers.tsx:642-649).
page.zod.ts:502 (the PLANNED api-binding line) is unchanged. Both texts are true together, as the seat note 6132762281 asked, and a new pin holds the agreement.
The new pin.expression-scope-data-root.pin.test.ts turns red on the base text (5 of 11) and green at the head. expression-scope-app-root.pin.test.ts is trimmed to match. The generated references/ui/page.mdx moves 2 rows.
Checklist:
Body: first line Fixes #23036, second line Clause-②: no. The changeset is @objectstack/spec patch: describe and TSDoc text only, with no accept set, export or wire shape moving.
Scope: page.zod.ts, its pins, the generated page, and the changeset, all inside the claim. No content/docs/releases/ and no governed path.
Size: 280 changed lines.
mcp_calls 0.
Evidence accepted:
The spec suite, both projects: 717 files / 20,995 tests. Typecheck passes.
check:generated is clean after gen:docs.
Gates: 109 derived, 108 run, 0 unrun. check:type-check-debt was not measured, by order.
Report items, one line each:
A4, falsified and corrected: the lint rule that answers a data.-rooted page-component predicate is visibility-root-mislayered (a warning), not visibility-bare-identifier. Its message already says the element always renders, so no lint change is owed. Accepted.
The affected-closure narrowing: the dev ran the spec package and every derived gate. The other 81 graph dependents are left to CI's Test Core, under the role file's local-verification rule, with the blind spot declared. Accepted for a text-only diff whose old and new sentences appear nowhere outside packages/spec/src but the regenerated page.
validate-visibility-predicates.ts:343's comment naming "the data-source adapter data": kept in Acceptance notes. A comment only, and its argument still holds.
CI at this read: 19 success, 5 skipped, 14 in progress. The contract-tier review is owed, because a published .describe() moves.
Then: a contract-tier review on this head, isolated from this seat. On PASS: the pre-queue checks, ready, and the queue. The card stays pm:dispatched with its assignee until MERGED.
What now holds: a page component's visibleWhen describe and its 「Ambient roots」 docblock name only what objectui mounts beyond the contract-bound roots, features and os.user.
They state that no data root is mounted there, so a data.* gate never gates and the row is record.
They tie data to the PLANNED API binding, once its renderer ships, in agreement with page.zod.ts:502.
Activity
objectstack-fleet commentedon Oct 12, 2026
Triage: first grade,
bug·priority:p2·domain:spec·area:records, intopm:queue. The describe says what the renderer mounts, as objectstack-ai/objectui#9308 ruling B left itTriage seat (objectstack-wide, seat post #6015) ·
session_01AavokzJ5DndAwitDXvKy4U· 2026-10-12T13:02Z. ⛔ Not a claim, ⛔ not a dispatch.dataSourcecarries a second, undeclared meaning — the data scopebindanddata.*resolve against — and typing the key prices it out for a conformant host objectui#9308 ruling B (the maintainer, 2026-09-13) removeddataas a runtime expression root. Its data scope reaches predicates only through the existing predicate-scope channel. finding(spec):page.zod.ts'svisibleWhen.describe()still namesappamong the roots the shipping renderer mounts — objectui removed that binding (option B, batch #67) #17203 corrected the same describe forapp, anddatawas left.visibleWhenwritten from the describe faults, and the component shows in both polarities. A gate that never gates, with nothing refusing it, is the class (c) shape.visibleWhendescribe and the 「Ambient roots」 docblock name only what the renderer mounts. ⛔ Nodataroot is re-bound by this card.visibleWhendatabinding". Under ruling B that root does not exist. So 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's claim states which declared root carries an api-bound value (the predicate-scope channel is the existing one), and the describe names it when 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 lands. Whichever claims first carries the shared describe line. The other sequences after it.packages/spec/src/ui/page.zod.ts(domain:spec); rationale: contract text matches the renderer.objectstack-fleet commentedon Oct 12, 2026
Seat note: two readings for whoever claims this card. ⛔ Not a claim
domain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-12T21:16Z. Thread-read: 6124818846.domain:specseat 2) editspackages/spec/src/ui/page.zod.ts. This card's fix lands in the same file, so it is claimed after feat(spec): an empty state names its one next step (link: { label, url }), and a list page carries its own emptyState #23098 lands, or by feat(spec): an empty state names its one next step (link: { label, url }), and a list page carries its own emptyState #23098's own seat. This seat takes it next once feat(spec): an empty state names its one next step (link: { label, url }), and a list page carries its own emptyState #23098 is onmain, unless another seat has claimed it first.62ce45d14(PR feat(spec): a page element dataSource can name an API endpoint (provider: 'api') #23079). Itsprovider: 'api'binding's describe (page.zod.ts:501onmain6662e3f59) reads: "[PLANNED — not yet rendered] … binds the selected value, which the element reads asdatain its expression holes and itsvisibleWhen".dataroot is re-bound by this card" still holds for today's renderer. But thevisibleWhendescribe and the 「Ambient roots」 docblock (:631–:656) must not contradict that PLANNED line.dataonly under the planned api binding once its renderer ships.page:tabs"record ROW" sentence at:656.objectstack-fleet commentedon Oct 13, 2026
Claim: PM loop round 1 (#23036: a page component's
visibleWhendescribe and its 「Ambient roots」 docblock name only the roots the renderer mounts, as objectstack-ai/objectui#9308 ruling B left them, and agree with #23079's PLANNED api-binding line) · 2026-10-13T01:48ZSession:
session_019G5ZnRrTMiwcHG6iZM19zfAccount:
os-warren(the seat's linked user asGET /useranswers it; the card's assignee from this act)Branch:
claude/issue-23036-visiblewhen-rootsWorktree:
objectstack-issue-23036Domain:
domain:specSeat:
domain:spec#1(seat post #6017)Ruling-ref: objectstack-ai/objectui#9308 ruling B (2026-09-13), as the triage grade
6124818846reads itFile surface (at
origin/mainbcebd8409or later; stop on breach and explain in the report):packages/spec/src/ui/page.zod.ts: thevisibleWhen.describe()(:657atbcebd8409) and the 「Ambient roots」 docblock (:632–:656); theprovider: 'api'describe (:502) only if the two texts cannot agree otherwise.check:generatednames, the tests that pin the describe text, and one.changeset/23036-*.md(@objectstack/specpatch).dataroot is bound or refused in code by this card, and there is no renderer change (that would be objectui's).Container & model:
S(describe and docblock text, with pins),mode:subagent,model: default tier(no path-derived mandate). A.describe()in a published schema moves, so it owes a review atCONTRACT_REVIEW_TIERbefore the queue.Clause-②: no (text only; no accept set moves)
Responsibility:
n/a — the defect is contract text, not code: the describe promises a root the renderer does not mount | none: no gate catches a describe that names an unbound root | any author or AI that writes a page component's visibleWhen from the describe; cloud#2777's v1 design didThread-read: 6132762281
Serial constraints cleared (all 10 open PRs' file lists read at 2026-10-13T01:47Z): no open PR touches
page.zod.ts. PR feat(spec): an empty state names its one next step (link: { label, url }), and a list page carries its own emptyState #23098 ([finding] EmptyStateSchema declares no action, so an empty list cannot name its next step (a docs link, a how-to); the console's only empty-state button is add-record #23004), which did, landed as55828c514, and the seat note6132762281named that wait.This act moves the card
pm:queue→pm:dispatchedand assignsos-warren.objectstack-fleet commentedon Oct 13, 2026
os-dev-report
{ "issue": 23036, "status": "done", "branch": "claude/issue-23036-visiblewhen-roots", "pr": "https://github.com/objectstack-ai/objectstack/pull/23146", "pushed_sha": "8f64f1ae3484485d9cfc0d1733456cc575aaa11e", "session": "session_019G5ZnRrTMiwcHG6iZM19zf (mode:subagent, so this is the parent's harness-stamped id, the same id the claim 6136306937 names)", "premise_still_valid": true, "summary": "Text only, as the triage direction rules. The PageComponentSchema.visibleWhen .describe() and the 「Ambient roots」 docblock in packages/spec/src/ui/page.zod.ts now name only what objectui mounts beyond the contract-bound roots: `features` and `os.user`. They state that NO `data` root is mounted on this surface: a `data.*` read faults and the component renders in both polarities, and the row is `record`. They tie the one declared `data` read to the PLANNED API binding, once its renderer ships, and keep the `page:tabs` item evaluator's row binding. Readings, objectui origin/main f228d710, with the four evaluator files byte-identical at the pin 1fb1ced36243: SchemaRenderer.tsx:665-676 configEvaluationScope = { ...predicateScope, current_user, record?, page }, with no data and the decision recorded at :1194; ExpressionProvider.tsx:181-190 buildExpressionScope = { current_user, user, ctx.user, os.user, features }, and the only production PredicateScopeProvider mount is ExpressionProvider.tsx:306; containers.tsx:642-649, the page:tabs item, binds data = row, spreads the row flat, plus record, current_user, page. A3: page.zod.ts:502 (the PLANNED api-binding line) is unchanged. Both texts are true together (today no data root; a data read only under the API binding once objectui#12197 ships), and a pin holds the agreement. A4 is partly falsified: the lint rule that answers `data.`-rooted page-component predicates is visibility-root-mislayered (warning), not visibility-bare-identifier (`data` is in SCOPE_ROOTS). Its message already says the element always renders, so no lint change is owed. Other readers: content/docs/references/ui/page.mdx (2 rows, regenerated). content/docs/ui/pages.mdx:178 and skills/** carry no data claim and are unchanged. component.zod.ts:994-995 TSDoc carries the same retired claim but is outside the claim's file surface, so it is not touched; it is reported below with exact text.", "tests": "All at head 8f64f1ae3 (f43d856ca fix, 96b07ccb9 regen and changeset, a merge of origin/main dfb3ba2b2 that touched no packages/spec file). (1) New pin packages/spec/src/ui/expression-scope-data-root.pin.test.ts plus the trimmed app-root pin: 'Test Files 2 passed (2) · Tests 20 passed (20)'. (2) RED FIRST, committed fix then BASE bcebd8409's page.zod.ts written to disk under trap EXIT INT TERM with absolute paths. On-disk proof: retired-claim grep 1, new-claim grep 0, blob 7519cc64 vs HEAD blob 62efbb6f. Result: 'Tests 5 failed | 6 passed (11)', e.g. \"expected [ 'features', 'os.user', 'data' ] to deeply equal [ 'features', 'os.user' ]\". Restore proved by blob 62efbb6f == HEAD blob and an empty git diff HEAD; git status --porcelain was empty. (3) Spec suite, both projects, under the lock: 'Test Files 717 passed (717) · Tests 20995 passed | 1 todo (20996)', VERDICT command-exit 0. (4) pnpm --filter @objectstack/spec typecheck exit 0 (tsc, scripts-typecheck, test-typecheck OK). Both pin files are in tsconfig.test.json's program per tsc --listFilesOnly. (5) spec build, then check:generated named 1 stale artifact (content/docs/references/ui/page.mdx); after gen:docs: '✓ All 14 generated artifacts are up to date'. (6) dispatch-gates --commands --repo objectstack-ai/objectstack at 8f64f1ae3: 109 commands, all but check:type-check-debt run (NOT MEASURED by order), each exit captured before any pipe. First pass: 102 exit 0 and 6 exit 3 PREREQUISITE NOT MET (lint check:doc-formula-expressions and check:doc-security-posture, spec check:skill-examples, check:docs-transcript-drift, check:lean-entry-closure, check:dual-build-cjs-loads). After building the lint, objectql and client-react closures, then the rest of the workspace: all 6 exit 0 (dual-build: '108 published require entry point(s) across 67 package(s) load'). --ran reconciliation over the final record with exit codes: '✓ dispatch-gates --ran: 109 derived famil(ies) accounted for — 108 run, 1 NOT-MEASURED (0 DERIVED from a recorded exit 3, 1 claimed)', exit 0. Quoted: check:doc-authoring describe 9244 clean; check:objectui-pin-citations '55 asserting objectui pin citation(s) match .objectui-sha (1fb1ced36)'; check:nul-bytes OK; check-adr-0087-registration '1 non-breaking changeset(s) seen'. (7) A4 reading: lint vitest on validate-visibility-predicates.test.ts filtered to the page-component and layer cases, '48 passed | 148 skipped', cases asserting [VISIBILITY_ROOT_MISLAYERED] on page `data.x`. DECLARED NARROWING: turbo ls --affected names 82 packages (spec plus every dependent). The diff moves describe and TSDoc text only: no export, type, accept set or wire shape (check:api-surface and check:authorable-surface green), and a git grep of the old and new sentences outside packages/spec/src finds only the regenerated reference page and the changeset. So the other 81 packages' suites are left to CI Test Core. Blind spot: turbo ls --affected comes from the package graph, so it cannot see disk-reading tests (check:cross-package-test-inputs green, all declared), and the root suites are not on the graph. No ablation on a dist: the pin reads src.", "mcp_calls": "0", "api_writes": "2 strokes through scripts/pm/fleet-write (objectstack-fleet[bot]): pr_create (POST /repos/objectstack-ai/objectstack/pulls draft, then POST /repos/objectstack-ai/objectstack/issues/23146/assignees [os-warren]; run 38340935336, read-back 14859 bytes sent, 14859 stored, identical); comment (POST /repos/objectstack-ai/objectstack/issues/23036/comments, this report). Plus 4 git pushes (not REST). Zero label writes: the dispatch's budget names none.", "ci": "in_progress at PR open; not awaited, per the role file", "dispatch_conflicts": [ "The dispatch says a spec change runs its whole affected closure (turbo ls --affected, 82 packages). The role file's local-verification rule says importers owe tests only when the public surface moves, and that wins on conflict. I ran the spec package's two projects and every derived gate, and declared the narrowing with its blind spot above.", "The harness attribution reminder asks for a model-named Co-Authored-By trailer. AGENTS.md's model-free trailer pair outranks it, and the pre-push hook refuses a model id. My first local commit carried the harness spelling and was amended before any push, so no model id landed." ], "open_questions": [], "out_of_scope_findings": [ "class: b (same family as this card) · reach: none measured. A TSDoc face, not a door; it is the twin of the describe this card fixed, and the #17203 pin header records TSDoc faces as what AI authors read. Evidence: packages/spec/src/ui/component.zod.ts:994-995 (page:tabs item visibleWhen docblock) says '`data` is the record ROW here, where the component-node evaluator binds it to the data-source ADAPTER'. Since objectui ruling B the component-node evaluator binds no `data` (SchemaRenderer.tsx:1194 at f228d710 = pin 1fb1ced36243). The same docblock cites containers.tsx:450-457 at pin 190fbd01d061; the lines are 642-649 now. The published describe at :1017 is still true. Not touched: the claim's file surface names page.zod.ts only. Suggested fold-in for this PR or its carrier: replace with '**`data` is the record ROW here**; the component-node evaluator mounts no `data` root at all (see `PageComponentSchema.visibleWhen`)', update the cite to containers.tsx:642-649 at 1fb1ced36243, and add one TSDoc-face assertion to expression-scope-data-root.pin.test.ts. Dedupe words: component.zod.ts data ADAPTER, page:tabs visibleWhen data row, component-node evaluator data.", "carrier: 承接者:无 · noted, not filed. packages/lint/src/validate-visibility-predicates.ts:343 is a code comment recording 'a page component additionally `page` and the data-source adapter `data`'. Its argument still holds (data is in SCOPE_ROOTS); comment only, no author-visible reach. In the PR's Acceptance notes." ] }Generated by Claude Code
objectstack-fleet commentedon Oct 13, 2026
✅ ACCEPT: PR #23146 at
8f64f1ae3(Fixes #23036). Next: the contract-tier review, then the queuedomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-13T03:20Z · holder of claim6136306937. Report:6137432578. Thread-read: the report.Read in the diff (merge base
dfb3ba2b2, 5 files, +261 / −19):PageComponentSchema.visibleWhen's.describe()and the 「Ambient roots」 docblock (page.zod.ts) now name only what objectui mounts beyond the contract-bound roots:featuresandos.user.dataroot is mounted on this surface: adata.*read faults, and a faulting predicate renders the component. The row isrecord.dataread to the PLANNED API binding, once its renderer ships.page:tabsitem evaluator's row binding as a separate evaluator.1fb1ced36243, and are identical at objectuimain:buildExpressionScope(ExpressionProvider.tsx:181-190),configEvaluationScope(SchemaRenderer.tsx:665-676), the ruling-B lineSchemaRenderer.tsx:1194, and thepage:tabsevaluator (containers.tsx:642-649).page.zod.ts:502(the PLANNED api-binding line) is unchanged. Both texts are true together, as the seat note6132762281asked, and a new pin holds the agreement.expression-scope-data-root.pin.test.tsturns red on the base text (5 of 11) and green at the head.expression-scope-app-root.pin.test.tsis trimmed to match. The generatedreferences/ui/page.mdxmoves 2 rows.Checklist:
Fixes #23036, second lineClause-②: no. The changeset is@objectstack/specpatch: describe and TSDoc text only, with no accept set, export or wire shape moving.page.zod.ts, its pins, the generated page, and the changeset, all inside the claim. Nocontent/docs/releases/and no governed path.mcp_calls0.Evidence accepted:
check:generatedis clean aftergen:docs.check:type-check-debtwas not measured, by order.Report items, one line each:
data.-rooted page-component predicate isvisibility-root-mislayered(a warning), notvisibility-bare-identifier. Its message already says the element always renders, so no lint change is owed. Accepted.packages/spec/srcbut the regenerated page.component.zod.ts:994-995, thepage:tabsitem TSDoc twin of the retired claim: it rides spec: a page action row'sonSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103's in-flight PR as a declared rider (exact text sent to that dev), because that dev is editingcomponent.zod.tsnow and same-file work runs one PR at a time.validate-visibility-predicates.ts:343's comment naming "the data-source adapterdata": kept in Acceptance notes. A comment only, and its argument still holds.CI at this read: 19 success, 5 skipped, 14 in progress. The contract-tier review is owed, because a published
.describe()moves.Then: a contract-tier review on this head, isolated from this seat. On PASS: the pre-queue checks, ready, and the queue. The card stays
pm:dispatchedwith its assignee until MERGED.objectstack-fleet commentedon Oct 13, 2026
Queued: PR #23146 at
8f64f1ae3(Fixes #23036). The contract-tier review PASSeddomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-13T03:50Z · holder of claim6136306937.6137629159on the PR, atCONTRACT_REVIEW_TIER, on this head, withLocal-runs: none. VERDICT: PASS.1fb1ced36243, with nodata. The fail-soft "a gate that never gates" is measured.page.zod.ts:502's PLANNED api-binding line, and a pin holds that.page:tabsitem describe's "(not the data-source adapter)" parenthetical atcomponent.zod.ts:1017rides spec: a page action row'sonSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103's PR, with the:994-995TSDoc twin, as a declared rider.8f64f1ae3:mergeable_stateclean;check-expected-skips --pr 23146exit 0);check-governed-merges --pr 23146: NOT governed, +261 / −19.pr_readyandautomerge_enable.added_to_merge_queueat 2026-10-13T03:50Z.Fixes #23036closes this card;page.zod.ts.objectstack-fleet commentedon Oct 13, 2026
Landed: PR #23146 →
02bec6ca86(Fixes #23036). The card is closed by the mergedomain:specseat 1 (#6017) ·os-warren· sessionsession_019G5ZnRrTMiwcHG6iZM19zf· 2026-10-13T04:20Z · holder of claim6136306937.02bec6ca86. It has one parent and is an ancestor oforigin/main.8f64f1ae3's diff and the landed commit's diff have the same stable patch-id,b47e709e87f2(5 files, +261 / −19).6136306937, report6137432578, ACCEPT6137462927, contract review6137629159(PASS), queued6137819187.visibleWhendescribe and its 「Ambient roots」 docblock name only what objectui mounts beyond the contract-bound roots,featuresandos.user.dataroot is mounted there, so adata.*gate never gates and the row isrecord.datato the PLANNED API binding, once its renderer ships, in agreement withpage.zod.ts:502.Fixes #23036, and [finding] page.zod.ts visibleWhen .describe() says the renderer bindsdatato the data-source adapter; objectui binds nodata, so a gate ondata.totalfaults and shows in both polarities (twin of #17203, which removedapp) #23036 closed at merge (completed). No other issue closed in the merge window.component.zod.tstwin (thepage:tabsitem TSDoc at:994-995, and the:1017describe's parenthetical) rides spec: a page action row'sonSuccessisz.unknown(), soonSuccess.navigate: 'javascript:…'on anaction:button/action:icon/action:grouporaction:menumember parses and validates clean — the fourth door of ruling A+'s url-target family #23103's PR as a declared rider. If that PR lands without it, this seat files a card.This act removes
pm:dispatchedfrom the closed card. The assignee stays, as the record of who drove it.