Skip to content

fix(spec): the page liveness ledger grades the app page record binding live, citing its console readers - #30611

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-30094-page-record-liveness
Nov 5, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-30094-page-record-liveness

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Sales · project thread

Fixes #30094

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:packages/spec/liveness/page.json 里 app 页面 record 绑定及其全部子键(object/view/filter 及 field/operator/value、sort 及 field/order)从 planned 改判 live,每行引用控制台里真正读取它的代码;同步重生成 state-counts/page.md(live 35→42,planned 9→2),并加一条 @objectstack/spec patch changeset。
  • 为什么改:旧行写着「还没有渲染器读页面级 record」,但 ui/ 里的 PageView 已经按这个绑定读出首行并作为 record 交给每个组件的 visibleWhen;过期的 planned 会让作者和 AI 以为这个键不起作用。
  • 风险与回滚:只改台账数据与生成计数,不动 schema、不动 ui/、无运行时行为变化;回滚即 revert 本 PR。
  • 先看:packages/spec/liveness/page.json 的 record 块(容器行的 evidence 与 note);packages/spec/liveness/state-counts/page.md;.changeset/30094-page-record-liveness-regrade.md。
  • 两分钟验证:pnpm --filter @objectstack/spec check:liveness,预期末行 ✓ … every path#symbol anchor names a symbol its file contains … 且 state-counts/ is current,退出码 0。
  • 需要你做的:无,席位落地。

What changes

Each props.record.* row of the page ledger is regraded from the renderer in this checkout, following the README regrade recipe: a path#symbol citation at the reader, verifiedAt: 2026-11-05, evidenceScope: in-repo, and a producer where the read depends on a second input (the page's adapter). No row stays planned: every key has a reader.

Row Old New Reader citation
record planned live PageView.tsx#appPageRecordBinding (an app page only, a binding that names its object) → the AppPageRecord element PageView.tsx#PageView mounts around the page body → PageView.tsx#AppPageRecordRead (first row, $top: 1) → ExpressionProvider.tsx#PageRecordScope (publishes it as record) → SchemaRenderer.tsx#buildNodeEvaluationScope (spread into every component's visibleWhen)
record.object planned live PageView.tsx#AppPageRecordGate lands it on objectName (element-data-source.ts#composeElementDataSource); PageView.tsx#AppPageRecordRead calls dataSource.find(object, params)
record.view planned live useElementDataSource.ts#useElementDataSource reads config.view, lists the saved views and resolves the name (an unresolved name draws the gate's error panel, never a wider read); element-data-source.ts#composeElementDataSource AND-combines the view filter and takes the view sort when the binding has none
record.filter planned live ElementDataSourceGate.tsx#useElementDataSourceSchema (the page mapping's filter: true lowers it with toFilterNodeSafely); PageView.tsx#AppPageRecordRead resolves placeholder tokens and sends it as $filter
record.filter.field planned live filter-converter.ts#viewFilterRuleToNode (left operand of the lowered comparison)
record.filter.operator planned live filter-converter.ts#viewFilterRuleToNode (normalised by normalizeFilterOperator)
record.filter.value planned live filter-converter.ts#viewFilterRuleToNode (right operand)
record.sort planned live PageView.tsx#AppPageRecordRead sends it as params.$orderby beside $top: 1; element-data-source.ts#composeElementDataSource (binding sort overrides the view's)
record.sort.field planned live data-objectstack/src/index.ts#serializeOrderBy (field / -field on the wire sort)
record.sort.order planned live data-objectstack/src/index.ts#serializeOrderBy (desc, case-insensitive, selects -field)

All ui/ paths are rooted at ui/packages/: app-shell/src/views/PageView.tsx, app-shell/src/providers/ExpressionProvider.tsx, react/src/SchemaRenderer.tsx, react/src/hooks/useElementDataSource.ts, react/src/element-data-source/ElementDataSourceGate.tsx, core/src/data-scope/element-data-source.ts, core/src/utils/filter-converter.ts, data-objectstack/src/index.ts.

The predicate scope the old note said was missing. The old note measured objectui main @79cb8c03eb, where record came only from RecordContext, which a record block provides to its own body. The page host now mounts PageRecordScope, which adds the page row as record to the ambient predicate scope; buildNodeEvaluationScope spreads that scope first, so every component predicate on the page reads it, and a record block that binds its own row still overrides record for its own subtree.

Changeset. packages/spec/package.json files[] lists liveness, so liveness/page.json ships in the npm tarball: @objectstack/spec patch, with both declaration lines.

Why

A stale planned row tells authors and agents that the key does nothing. The ledger is what the gate, the enforce-or-remove channel and the AI authoring surfaces read as the capability catalogue, and this row was written before the console's reader landed.

Tests

No code changed, so no test was added. The reader is already pinned by ui/packages/app-shell/src/views/__tests__/PageView.appPageRecord-12931.test.tsx (tier and count predicates, one read per page open with the org token resolved in $filter, no binding and a non-app page read nothing, 0 rows, 403 read as no row, failure fails closed). That test covers object and the filter rule keys. view and sort are cited at their readers only.

Acceptance notes

  • The schema's prose is stale too, left alone here. packages/spec/src/ui/page.zod.ts still says "Planned in the renderer: until the console mounts it, record stays unbound on an app page" in the record key's .describe() and in the PageRecordBindingSchema TSDoc. The same file's visibleWhen describe already states the binding is read. The ruling for this card is no schema change, so this is a follow-up for the spec owner (a .describe() edit regenerates check:docs artifacts). The ledger row wins over a describe marker, so the gate's verdict is unaffected.
  • The README page Notes cell ("live, plus the one dead tombstone below") is now exactly true for the record subtree and was not edited. The 2 rows still planned in page are outside record.
  • Gate sweep: the --commands families derived on the committed head, exit codes captured to disk, --ran reconciled. Readings are in Verification.

Verification

All readings on head 7a3032f3b, the final commit; exit codes captured before any pipe.

check:liveness — exit 0

pnpm --filter @objectstack/spec check:liveness before regenerating the counts: exit 1, the only failure being the stale page shard (35 | 0 | 0 | 1 | 9 became 42 | 0 | 0 | 1 | 2). gen:liveness-counts rewrote 1 shard (page.md). Re-run: exit 0, with every path#symbol anchor resolving, every cited file naming its key, and state-counts/ is current.

Liveness vitest — 12/12 files, 359/359 tests

bash scripts/pm/os-verify-lock.sh -c 'pnpm --filter @objectstack/spec gen:migration-registry && pnpm --filter @objectstack/spec exec vitest run --maxWorkers=2 scripts/liveness' printed Test Files 12 passed (12), Tests 359 passed (359) and VERDICT command-exit 0.

Derived gate families — 57 derived, 57 run, 0 NOT-MEASURED (derived zero)

node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 57 commands from the change set (3 paths, 105 changed lines). Every one exited 0, and --ran with command :: exit N records printed 57 derived, 57 run, 0 NOT-MEASURED, 0 UNRUN (a DERIVED zero: all 57 carry an exit code, none of them 3).
Three needed a second run. check:generated: in the first sweep the check:test-typecheck lane's tsc died with no diagnostics, because the sweep ran without NODE_OPTIONS. Run alone with --max-old-space-size=4096, that gate held (52 files / 240 errors / 131 pinned signatures, unchanged), and check:generated re-ran with all 14 artifacts up to date. check:dual-build-cjs-loads and check:lean-entry-closure first exited 3 (PREREQUISITE NOT MET). After turbo run build (120/120 tasks) both exited 0.
Local-only families: check:dev-prereqs exit 0, check:skill-frame-freshness exit 0.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No schema touched.)
  • 2. Every name I added uses the casing for its kind. (None added.)
  • 3. No formatter ran; pnpm lint passes. (No lintable source changed; CI runs it.)
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec touched: its generated artifacts are regenerated and committed (state-counts/page.md; check:generated 14/14).
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (except the 维护者速读 section, which is written in Chinese by design).
  • The Before Submitting list in CONTRIBUTING.md is done.

Generated by Claude Code

🤖 Generated with Claude Code

https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa

…read points

The app page record binding is read by the console's PageView
(appPageRecordBinding -> AppPageRecord -> PageRecordScope); each child
key is cited at the line that reads it.

Claude-Session: https://claude.ai/code/session_01L6bhgTqqeHSwZYJmApbDCa
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface labels Nov 5, 2026
@github-actions

github-actions Bot commented Nov 5, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 2 changed file(s) yielded no anchor (packages/spec/liveness/page.json, packages/spec/liveness/state-counts/page.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/liveness/page.json, packages/spec/liveness/state-counts/page.md) — pages documenting those are invisible to this run
  • 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 — 170 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 5b748dfede3f98fef7aeadbf930dc4536c8d9e06 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:spec#3 · os-sales · session_01L6bhgTqqeHSwZYJmApbDCa

维护者速读

  • 改了什么:packages/spec/liveness/page.json 里 app 页面 record 绑定及其全部子键(object、view、filter 及 field / operator / value、sort 及 field / order)从 planned 改判 live,每行引用控制台里真正读取它的代码;重生成 state-counts/page.md(live 35→42,planned 9→2),加一条 @objectstack/spec patch changeset(liveness/ 随 npm 包发布)。
  • 为什么改:旧行写着「还没有渲染器读页面级 record」,但控制台的 PageView 已经按这个绑定读出首行,并作为 record 交给页面上每个组件的 visibleWhen;过期的 planned 会让作者和 AI 以为这个键不起作用。
  • 风险与回滚:只改台账数据与生成计数,不动 schema、不动 ui/、无运行时行为变化;回滚即还原本 PR。
  • 先看:packages/spec/liveness/page.json 的 record 块(容器行的 evidence 与 note);packages/spec/liveness/state-counts/page.md。
  • 两分钟验证:pnpm --filter @objectstack/spec check:liveness,预期退出码 0,末尾提示每个 path#symbol 锚点都能在文件里找到、state-counts/ 是最新的。
  • 需要你做的:无,席位落地。

席位意见:本 PR 不碰契约四面(无 schema、无收窄、Clause-②: no、无受管路径),按规则由 CI 加席位自读验收,不另起复审。page.zod.ts 里 record 的 .describe() 和 PageRecordBindingSchema 注释仍写「渲染器尚未实现」,这是已发布文字的过时,已另立 #30614 跟进。

席位核对 — ACCEPT · domain:spec#3 · head 7a3032f3b6f8206ba806ad9afee3597662b1c9a7

  • Shape: 3 files (+64 −41), draft, base main, first card reference Fixes #30094: the ten props.record.* rows of liveness/page.json regraded planned → live with verifiedAt: 2026-11-05, evidenceScope: in-repo and a reader citation each; state-counts/page.md regenerated (live 35 → 42, planned 9 → 2: the seven leaf rows; the three containers are not counted, as the gate counts them); a @objectstack/spec patch changeset. No schema, ui/ or content/docs/releases/ file.
  • Premise measured: on origin/main the readers the rows cite exist: ui/packages/app-shell/src/views/PageView.tsx appPageRecordBinding :88, AppPageRecordGate :147, AppPageRecordRead :208 (the first row as $top: 1 under sort, :202); ui/packages/app-shell/src/providers/ExpressionProvider.tsx PageRecordScope :392; ui/packages/react/src/SchemaRenderer.tsx buildNodeEvaluationScope :699, spread into every node's evaluation scope at :776. The old note's "nothing reads a page-level record yet" is false at this tip.
  • Gates: check-runs on this head: 64, 55 success, 9 skipped, 0 red; check-expected-skips --pr 30611: all 9 skips in main's roster (exit 0). Lint & Repo Gates, TypeScript Type Check, Spec property liveness (every path#symbol anchor resolves) and Check Changeset all success. Dev report 6665354382: 57/57 derived gates exit 0, --ran 0 NOT-MEASURED; liveness vitest 12 files / 359 tests. git merge-tree against main 135c2fb8 merges clean. No ablation (ledger data) and no dogfood battery (no !, no (narrowing)), both right.
  • Contract review: not owed. reviewFace reads no schema path and no governed path, and the changeset declares Clause-②: no with no narrowing, so none of the four faces is touched; CI plus this seat's read is the review.
  • Declarations: Clause-②: no, Metadata-protocol: none, @objectstack/spec patch, in the body and the changeset alike. packages/spec/package.json files[] lists liveness, so the ledger ships and the patch changeset is owed. Changeset sentences checked: "read once per page open as the first row under sort" (PageView.tsx :202-208), "every component's visibleWhen sees that row as record" (SchemaRenderer.tsx :699, :776), "No key, schema shape or export changes" (the file list).
  • Accepted deviations: the worktree created inside the shared checkout and moved before any edit (process only); the body's attribution-first order is the dispatch's form. mcp_calls: 0.
  • Carried: spec(ui/page): the app page record binding's describe and PageRecordBindingSchema TSDoc still say "Planned in the renderer", though the console reads it #30614 (the record describe and the PageRecordBindingSchema TSDoc still say "Planned in the renderer"; published wording, after this PR merges).
  • Landing: not governed (check-governed-merges --pr 30611: 0 of 3 paths, 105 lines ≤ 3000). The hand-off is pr_ready plus automerge_enable, through the merge queue.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 5, 2026 18:33
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 5, 2026 18:33
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 5, 2026
Merged via the queue into main with commit dc51811 Nov 5, 2026
65 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-30094-page-record-liveness branch November 5, 2026 18:57
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 size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec liveness/page.json: props.record.* rows say planned while app-shell PageView reads record.sort

1 participant