Skip to content

feat(spec)!: a page declares nothing the console does not honour — isDefault only on a bound record page, a closed template set, regions its template places - #23708

Merged
objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/issue-23610-page-isdefault-template-slots
Oct 16, 2026
Merged

objectstack-fleet[bot] merged 15 commits into
mainfrom
claude/issue-23610-page-isdefault-template-slots

Conversation

@objectstack-fleet

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

Copy link
Copy Markdown
Contributor

Part of #23610
Clause-②: yes (narrowing)

Executes ruling 6190536588 items 1, 2 and 3 (Q1 A · Q2 A, plus half 1) under claim 6194235384. Item 4 (skills/objectstack-ui/rules/pages.md) is a separate Tier H PR (claim 6204703954). That PR carries the card's closing keyword, so this one is Part of and the card closes with its last piece; item 5's follow-ups (the objectui card, the hotcrm memo) belong to the director seat. One PR, not split: +1055/−100 across 33 files after patch round 2 (pin bump included), generated artifacts included, about two of precedent #23331's size for two refusals, as the ruling's workload line estimated.

What changes

Q1 — isDefault: true off a record page bound to an object is refused at every door. One sentence, misplacedDefaultPageRefusal (packages/spec/src/ui/default-record-page.ts, next to defaultRecordPageObject, whose reading it reuses — no second judge). It names where a landing is really set: the app's first navigation item, and the app's own isDefault for the root landing. A record page with no object gets the bind-the-object variant.

Q2 — template closes and each template declares its slots. PageTemplateSchema = default, header-sidebar-main, three-column, dashboard; full-width is retired by name through enumWithRetiredValues (prescription: use default, renders the same). PAGE_TEMPLATE_SLOTS declares the slots per the ruling; PAGE_TEMPLATE_OPEN_GRID = dashboard. checkPageRegionPlacement refuses, at regions.N.name, a region the template does not place, naming the template's slots and a rename hint (left_sidebar to sidebar, right_sidebar to aside, content to main). Judged on kind: 'full' pages only (a slotted page renders its slots, html / react their source) and case-insensitively, as the renderer's findRegion is. A second region that fills a slot an earlier region already fills is refused the same way, naming the region that fills it, because findRegion places the first match only. This came in the seat's patch round (record 6199121442 item 2). On dashboard, a second header or footer is refused; two regions of any other name stay two grid cells.

Half 1 — the text says what runs. PageTypeSchema's docblock and enum comments: home, app and utility are one region layout at three content widths; home is not a landing declaration; utility is an ordinary inline page with no floating or docked shell slot, and stays a type because the print door and the page-type width read it. The print-on-utility refusal no longer says "floating panel" (its pin in page-print.test.ts now also asserts the word is gone). content/docs/ui/pages.mdx (page types, a Templates table, the isDefault row, the region snippet's content to main), and the liveness type, template, regions, isDefault rows (re-read at objectui 0656b0e3c0).

Door by door (PM mechanism assumption 1, measured)

Door Goes through PageSchema? What this PR does there Reading
definePage / defineStack (stack.zod.ts, pages: z.array(PageSchema)), so os validate / os build yes nothing — the two new superRefines refuse STACK_SCHEMA_INVALID 422 at pages.N.isDefault / pages.N.template / pages.N.regions.M.name (page-default-and-region-placement.test.ts, stack-default-record-page.test.ts)
metadata save door (saveMetaItem, getMetadataTypeSchema('page')) yes nothing 422 INVALID_METADATA, nothing stored, both topologies (default-record-page-save-door.test.ts)
package registration, package door (SchemaRegistry.installPackage) no — the registry parses only object / app / package / plugin bodies refuseDefaultRecordPageConflicts first refuses misplaced defaults (misplacedDefaultPages, same sources as declaredDefaultRecordPages, nested plugins[] included), ahead of every mutation MisplacedDefaultPageError, INVALID_METADATA 422 (existing code, no new one), no package record left (registry-default-record-page.test.ts)
package registration, item seam (registerItem with a packageId) no refuses before the holder check same envelope
bare slot (registerItem with no package: sys_metadata hydration) — not judged, by the door's existing design pinned as a control
core stack-organization-record-page.ts n/a — a reader of defaultRecordPageObject, not a door unchanged core suite green

Q2's refusals are parse-only (the ruling names os validate and save), so the registry carries no template or region judgement.

Stored metadata (assumption 4, measured)

No D2 conversion touches these shapes and the read path does not refuse them: a sys_metadata page row laid in the store with a non-record isDefault: true, template: 'full-width' or a left_sidebar region is served by getMetaItem unchanged, on both the environment-scoped and the host-config kernel; saving it again answers 422. applyConversionsToStoredItem returns each row byte-equal with zero notices, and the parse still refuses it. The changeset's "Stored metadata keeps loading" paragraph states exactly this.

First-party writers (assumption 5)

  • examples/app-showcase/src/ui/pages/capability-map.page.ts drops isDefault: true; it stays the landing as the first ShowcaseApp.navigation item.
  • content/docs/protocol/objectui/layout-dsl.mdx: three os:check-yaml pages named template: standard / console (with left / right regions), red under check:yaml-examples — now default and header-sidebar-main with sidebar / main; the callouts that called template a free-form string are corrected.
  • examples/app-showcase/src/ui/pages/task-visualizations.pages.ts: its shared base held template: 'default' as a widened string and stopped type-checking against the closed set (+7 root-program errors under check:type-check-debt); now as const. The changeset carries the same one-line migration for consumers.
  • No first-party page wrote full-width or an unplaced region in source. Census: full-width in two test fixtures only; every regions entry in examples/**, packages/**, content/docs/** read by name (main 226, header 40, sidebar 9, footer 4, aside 1, the rest test-only).

Fixture triage (rule's consumption radius, not the edited package):

  • Replaced (pinned exactly the removed branch): stack-default-record-page.test.ts "an isDefault page that is not a record page is outside the rule", registry-default-record-page.test.ts same, default-record-page-save-door.test.ts same — each now asserts the refusal, with a no-flag control beside it. page.test.ts "should accept page with custom template" (three-column-layout) and "should accept default page" (no object).
  • Respelled: the two buildDefaultPageSchema seed fixtures (spec and lint *-22537.test.ts) from full-width to default, with a note.
  • Re-anchored: expression-scope-app-root.pin.test.ts's LIT control quoted the old page-type sentence.
  • Extended: object-refinement-check-exports.test.ts catalogues the two new checks (bijection, attached-by-identifier, barrel identity).

Renderer premise (assumption 3, re-read)

objectui main at 0656b0e3c0 (and the sparse read at 2bacbf08b4): TEMPLATE_REGISTRY still holds default, full-width, header-sidebar-main, three-column, dashboard; default and full-width are the same FullWidthTemplate, so no rendering fork and nothing was posted on the card for it. Slots match the ruling's table; findRegion is case-insensitive, which this PR follows.

One premise did not hold — "zero first-party writers" of full-width. objectui's packages/plugin-detail/src/synth/buildDefaultPageSchema.ts returns template: 'full-width', and Studio's record-page create seed (app-shell/src/views/metadata-admin/anchors.ts, createSeed) copies it into the page it saves. Retirement proceeds as ruled (identical rendering, no alias window), but that seed must say default before or with the pin bump that ships this, or a Studio "new record page" save answers 422. objectui's PageNodeSchema also redeclared template as string over the spec member, which is TS2430 against this spec (measured on a mirror, report 6200739195). Both are fixed in objectstack-ai/objectui#12585, the sibling fix that AGENTS.md (Post-Task Checklist step 4) ships with this PR.

Ledger

Two D3 entries, page-is-default-off-record-page-refused and page-template-closed-region-unplaced-refused; no D2 conversion (ruling: no grace or alias window); no tombstone (no key removed). No STEP18_RATIONALE fragment was added: neither precedent entry for isDefault carried one, and draft PR #23418 is editing registry.ts.template. BREAKING changeset .changeset/23610-page-isdefault-template-slots.md: @objectstack/spec minor, @objectstack/objectql patch, ADR-0087 marker registered naming both ids. New exports regenerate api-surface/, export-origins/, declaration-map/ and content/docs/references/. The template block sits after PageComponentSchema on purpose, so skills/objectstack-ui/references/_index.md (first five exports of the file) stays byte-identical and no skills/** path enters this PR.

Verification (final head 4815bfa99, after merging origin/main f77ba411e)

  • @objectstack/spec: 742 files / 21,868 tests passed. check:generated: all 14 artifacts current.
  • Downstream consumers of the narrowed schema, all green: objectql 8,157, metadata-protocol 20,560 (19 skipped), core 2,586, lint 6,771, metadata-core 728, platform-objects 1,091, plugin-approvals 1,080, cloud-connection 555, mcp 467, downstream-contract 31, example-showcase 411, example-crm 46, cli unit 4,354 (integration layer declared to CI). objectql, metadata-protocol, example-showcase re-run on the final head after the merge.
  • typecheck green for spec, objectql, metadata-protocol, core, lint, cli, platform-objects, plugin-approvals, cloud-connection, mcp, downstream-contract, example-showcase, example-crm; root program back to its ledger's 26.
  • Derived gates (dispatch-gates --commands, 119 families) on the final head: all exit 0; --ran reconciliation "119 derived, 119 run, 0 NOT-MEASURED (a DERIVED zero)". Two families first refused on the shallow clone (check-engine-split-ratio, check-plugin-teardown-shape --self-test) and passed after the deepening they prescribe. Local-only: check:dev-prereqs 0, check:skill-frame-freshness 0.
  • Ablations (each through ablation-replace.mjs, mutation proven on disk, restore proven by an empty git diff HEAD): drop checkPageDefaultPlacement 8 red; drop checkPageRegionPlacement 4 red; template back to z.string() 5 red; drop the package-door throw 1 red (the item seam still catches the top-level pages; the nested-plugin and no-record pin goes red); drop the item-seam throw 1 red.
  • Reverse type verification: template: 'full-width' in a showcase page gives TS2322 … not assignable to type '"dashboard" | "default" | "header-sidebar-main" | "three-column"', so the rebuilt .d.ts was read.

Landing order

  1. Done: fix(plugin-detail,types): seed new record pages with the default template and type the page template by reference to the spec objectui#12585 merged on objectui main as daeb77fc84dd. The Studio record-page seed says default, and PageNodeSchema.template takes the spec's type in both the TS and zod faces.
  2. Done (patch round 2, head d36a804054):
  3. Done: the new head has its delta contract-review record, PASS 6206263948 (the full PASS 6204434889 on c2b9935b4 stands for everything it judged). The seat then marks this PR ready.
  4. After this PR merges, the Tier H PR docs(skills): pages.md says what a page type, template and region name run #23758 (ruling item 4, Fixes #23610) is marked ready, and fix(spec): page.print and the flow terminal-toast translations read live in the liveness ledger, so a print page is no longer warned that nothing applies it #23776 (spec liveness: the console pin hop to objectui 1f23d5b070fa makes page.print and translation.flows.successMessage / errorMessage read planned while the shipped console applies them (family card; the parentDataSource leg is #23585) #23710) merges main and lands after it (landing-operations D).

Patch round (head c2b9935b4, origin/main 8c85141c5 merged)

  • @objectstack/spec: 742 files / 21,876 tests passed. The downstream consumer set is unchanged and green. The duplicate-slot ablation (the branch never fires) gives 6 red of 285; restored.
  • dispatch-gates --commands: 119 families, 119 exit 0 (two PREREQUISITE NOT MET exits on a fresh worktree passed after the full build). check:generated 14/14. CI on c2b9935b4: 41 check-runs, 36 success, 5 skipped.

Acceptance notes


Generated by Claude Code

… the registered set, refuse unplaced regions

Claude-Session: https://claude.ai/code/session_01TC6DKN349m4i7okGWPe4RG
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TC6DKN349m4i7okGWPe4RG
Co-authored-by: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TC6DKN349m4i7okGWPe4RG
Co-authored-by: Claude <noreply@anthropic.com>
… template as a literal

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

github-actions Bot commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/objectql, @objectstack/sdui-parser, @objectstack/spec, touching 33 documentable anchor(s). ⚠️ 7 changed file(s) yielded no anchor (packages/sdui-parser/objectui-lockstep.json, packages/spec/api-surface/ui.json, packages/spec/declaration-map/ui.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

26 hand-written doc(s) name something this change touched — list omitted above 15 rows. Re-derive on the tree named below: node scripts/docs-audit/affected-docs.mjs --json 29c7157af4fed4738b3da14bbfb008496cbd1f6a.

⛔ 12 release-owned page(s) also affected — read-only, see AGENTS.md Documentation Guardrails.

What this run could not see
  • 7 changed file(s) yielded no anchor (packages/sdui-parser/objectui-lockstep.json, packages/spec/api-surface/ui.json, packages/spec/declaration-map/ui.json, …) — pages documenting those are invisible to this run
  • 10 name(s) were too generic to anchor anything (single lowercase words)
  • 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 29c7157af4fed4738b3da14bbfb008496cbd1f6a → packageMentionDocs.

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 29c7157af4fed4738b3da14bbfb008496cbd1f6a

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — patch round, landing held · domain:spec seat 2 (#18549) · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-15T18:16Z

The dev's report is 6198386632, on head 4815bfa99. The seat review of record follows the patch round. This comment records why the PR does not land as it stands.

  1. Sibling coupling (AGENTS.md, Post-Task Checklist step 4). objectui at the console pin b9814b334 still writes template: 'full-width': buildDefaultPageSchema seeds Studio's record-page create. This PR refuses full-width at save, so that create would fail with a 422. PageNodeSchema also redeclares template?: string over the spec type, which the narrowing breaks; that break is to be measured. Step 4 says such a removal "ships together with the sibling fix and the pin bump — or it does not ship". The plan:
  2. In scope by ruling item 2's text: a region whose name repeats a slot its template already filled is not placed, because the console's findRegion takes the first match. It is refused in this PR (the dev's out-of-scope finding, measured on the head). On dashboard, other repeated names are grid cells and stay accepted.
  3. The dev's open questions: Q1 is A, the coupled landing above. Q2 is A, region names match case-insensitively as the console's findRegion does. Q3 is A, no D2 conversion.

…y filled

The renderer draws the first region of a name and excludes that name from
the rest, so a second main (on dashboard, a second header or footer) was
never drawn. Part of ruling 6190536588 Q2: a region the template will not
place is refused.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: c2b9935b4c7a2c53210ae9e57cdc840a0dd366d8
Local-runs: none

Written 2026-10-15T23:29Z. Read-only, these inputs and nothing else: card #23610's body and all 13 comments; PR #23708's body, file list (29) and net diff against main at the merge-base 8c85141c5; objectui#12585's body, file list (6) and net diff against objectui main; the check-runs on both heads. The objectstack head was fetched into a ref this clone owns (refs/cr/23708) and read with git show / git grep; objectui was read through REST and the repo-scoped code search. Nothing was built, run or re-run. Face: a published schema (packages/spec/src/ui/page.zod.ts), so the tier is by face; no governed path in either file list (Governed Surface Queue Guard success on both heads). On c2b9935b4 every required context is success — Lint & Repo Gates, TypeScript Type Check, Test Core (6/6), Build Core, Governed Surface Queue Guard — with Console Pin Gate skipped on the PR run as the two-speed rule says; read 2026-10-15T18:53Z.

① Derived judgments

Judged against ruling 6190536588 (Q1 A · Q2 A · half 1) and the director's executor terms 6189618343.

Q1 — isDefault: true off a bound record page. RIGHT.

  • One reading, one sentence: misplacedDefaultPageRefusal (spec/src/ui/default-record-page.ts) reuses defaultRecordPageObject, so the parse and the registry refuse exactly the pages the record-page rule does not place. checkPageDefaultPlacement is attached to PageSchema by identifier (pinned in object-refinement-check-exports.test.ts), so definePage, defineStack / os validate / os build (STACK_SCHEMA_INVALID 422 at pages.N.isDefault) and the save door (422 INVALID_METADATA, nothing stored, both kernel topologies) refuse through one parse. The ruling's third door, package registration, is served by SchemaRegistry.refuseDefaultRecordPageConflicts → misplacedDefaultPages (manifest pages and nested plugins[].pages, thrown ahead of every mutation and before the selfId early return) and by the package-bound registerItem seam; MisplacedDefaultPageError is INVALID_METADATA 422 carrying the parse's sentence — no new code, right. The bare slot (a stored row hydrating with no package) is not judged: right, and consistent with the stored-row paragraph the changeset states.
  • The sentence names the app's first navigation item and the APP's own isDefault, as item 1 requires (both pinned). A record page with no object gets the bind-the-object variant: right — it is a page defaultRecordPageObject does not place.
  • The showcase's capability-map.page.ts drops the flag and stays the landing by navigation order; the three other showcase isDefault: true pages (settings, project-detail, task-detail) and platform-objects' sys_user / sys_organization pages are record pages bound to objects (read on the head) — nothing first-party trips the new refusal.
  • D3 entry page-is-default-off-record-page-refused, no D2, no grace window: right per item 1.

Q2 — the closed template set. RIGHT.

  • PageTemplateSchema = default, header-sidebar-main, three-column, dashboard through enumWithRetiredValues; full-width is refused BY NAME with the prescription default — a retirement, not an alias window. The executor premise held: at the pin 1f23d5b07 objectui page.tsx:475-476 maps default and full-width to the same FullWidthTemplate, no rendering fork, so nothing was owed on the card.
  • PAGE_TEMPLATE_SLOTS matches item 2's table exactly (dashboard = header / footer plus the open grid, PAGE_TEMPLATE_OPEN_GRID). checkPageRegionPlacement refuses at regions.N.name, naming the slots and a rename (left_sidebar → sidebar, right_sidebar → aside, content → main), on kind: 'full' pages only and case-insensitively. The kind scoping and the case rule are faithful readings of "a region the chosen template will not place": a slotted / html / react page lays out no region by template, and the renderer's findRegion lowercases both sides (page.tsx:87). Parse-only (os validate, save): item 2 names those two doors and no registry door, so the registry judges Q1 alone. Right by the ruling's text.
  • The duplicate-slot refusal added in the patch round (a second main; on dashboard a second header / footer): the seat read it into item 2 on the PR (6199121442 ✨ Set up Copilot instructions #2), and the reading holds — the renderer draws the first region of a name and getRemainingRegions excludes that name (page.tsx:91-94), so a second one is a region the template will not place. dashboard's other names stay two grid cells: right.
  • D3 entry page-template-closed-region-unplaced-refused, no D2: right (item 2, no alias window; which slot a renamed region fills is the author's call).
  • Consumers inside the diff: layout-dsl.mdx's three os:check-yaml pages (standard / console, left / right) now spell real templates and slots; task-visualizations.pages.ts takes as const, and the changeset carries that one-line migration for a template held in a widened string. Right.

Half 1. RIGHT. The PageTypeSchema docblock and enum comments say what runs (three widths of one region layout; home is not a landing; utility has no floating or docked slot); the utility print refusal drops "floating panel" and its pin asserts the word is gone; content/docs/ui/pages.mdx (types table, a Templates table, the isDefault row, content → main); the four liveness rows re-cited at objectui 0656b0e3c0; the utility value stays (item 3).

Public surface. RIGHT. New exports PageTemplateSchema, PageTemplate, PAGE_TEMPLATE_SLOTS, PAGE_TEMPLATE_OPEN_GRID, checkPageDefaultPlacement, checkPageRegionPlacement, misplacedDefaultPageRefusal (@objectstack/spec/ui) and MisplacedDefaultPageError / MISPLACED_DEFAULT_PAGE_CODE (@objectstack/objectql); api-surface, export-origins, declaration-map, json-schema.manifest and content/docs/references regenerated and green. The template block sits after PageComponentSchema, so no skills/** path enters the PR — confirmed on the file list.

objectui#12585 — PageNodeSchema.template, both faces. RIGHT.

② Semver level

objectstack .changeset/23610-page-isdefault-template-slots.md. RIGHT.

  • @objectstack/spec: minor — pre mode (.changeset/pre.json, tag next), the level a breaking change takes here; the BREAKING banner is present; the body carries Clause-②: yes (narrowing) and one ADR-0087 marker, adr-0087: registered, naming both D3 ids in the HTML-comment form check:adr-0087-registration reads (Lint & Repo Gates success). The migration states every FROM → TO with its one-line fix and the stored-row behaviour.
  • @objectstack/objectql: patch — the row exists; what the diff publishes is the group's level regardless (.changeset/config.json holds spec, objectql and lint in one fixed group), so minor ships for all of them. A note, not a defect: a minor row would spell objectql's new export and new refusal more exactly.
  • @objectstack/lint is touched in one test file only and publishes nothing from it; examples/app-showcase is private: true. No row owed. 1,123 changed lines, under the 3,000-line class.
  • PR body Clause-②: yes (narrowing) — matches the ruling (item 6) and the changeset.

objectui .changeset/23610-record-page-seed-default.md. RIGHT.

  • @object-ui/plugin-detail: patch, @object-ui/types: patch, no major. Against the declared spec range both faces accept exactly what they accepted — the narrowing is the spec's and arrives by reference when the pin moves — so patch and the PR's Clause-②: no are right; objectui's Changeset Declaration, Changeset Bump Policy and Changeset Fixed Group Check are success.

③ Boundary flags

Dev report 6198386632 (objectstack, round 1).

  • open_questions 1–3 (landing order; case-insensitive names; no D2): answered by the seat on the PR, 6199121442 — A, A, A. Concur on all three; ① gives the reasons.
  • deviations (worktree detach, shallow deepen, model-free trailers, no STEP18_RATIONALE fragment — optional, and feat(lint)!: the two page component rules run on the runtime publish door (ruling B) #23418 edits that template — Q2 parse-only): all accepted here; the last is the ruling's own scoping (① Q2).
  • out_of_scope_findings: the duplicate-region finding is fixed in this PR (6199121442 ✨ Set up Copilot instructions #2); the buildDefaultPageSchema seed is objectui#12585.
  • skills_pages_md_rows_for_next_tier_h_dispatch: ESCALATED, still owed — skills/objectstack-ui/rules/pages.md:40 on this head still teaches left_sidebar / right_sidebar; ruling item 4's separate Tier H PR, not this one's.

Dev report 6200739195 (objectstack, patch round).

Dev report 6204227787 (objectui).

  • Fifth file (layout.zod.ts): adopted in the PR body; right (① objectui). Unreadable comments: the branch the seat opened the PR from is claude/issue-23610-record-page-seed-default, the one claim 6201335297 names. pr_create and the report post: performed by the seat.
  • Open question (keep the zod edit): A — answered here. Contract-first: the spec's save door refuses full-width regardless, so objectui validate agreeing with it is the correct face; the renderer stays lenient only for rows already stored, which is the changeset's stated behaviour.
  • gates: CI NOT MEASURED — now partly measured. Check-runs on f39f050 read 2026-10-15T23:25Z: 32 success (Build & E2E, Lint, Repo Gates, all 16 Test shards, Test (dist pins), the five changeset gates, Governed Surface Queue Guard, Line Citation Gate, Build Docs, Bundle Analysis), 3 skipped, and 2 STILL RUNNING — Type Check and Spec Main Shape Gate — NOT MEASURED. Type Check is the gate that judges the packages/types change; the dev's local reading (turbo type-check 32/32 on ebae0f5; three tsconfigs on f39f050) is not a CI verdict. objectui#12585 lands only once both conclude success.

Coupling plan — sufficient for the pinned console? YES on what was read, with the serialisation above.

  • Readers of full-width at the pin: TEMPLATE_REGISTRY (page.tsx:476) alone, kept by design. Writers on objectui main 3e09c1e8 (repo-scoped code search; 57 bare hits, 21 with the template co-term classified by their fragments): the seed, its test and pageSchemaIntrospect.test.ts — all three in test(spec,drivers): add the VALUE_ROUNDTRIP conformance case-set — what you wrote is what you read back, per driver per dialect #12585; every other hit is CSS or punctuation prose ("full-width comma", "full-width row").
  • Non-record isDefault: true page writers: none — the hits are apps (RootLandingRedirect, homePath), list views, flow edges and permission sets; preview-samples.ts's home sample carries no flag; Studio's page anchor seeds type: 'list', kind: 'full', regions: [].
  • Region names the rules refuse: left_sidebar / right_sidebar occur only in docs/design/studio-redesign/interfaces-page.html; no objectui page writer names a slot its template does not place (the dev's census at 572bbce, and the anchors read here).
  • Types re-declaring template as string: PageNodeSchema's TS and zod faces only — both in test(spec,drivers): add the VALUE_ROUNDTRIP conformance case-set — what you wrote is what you read back, per driver per dialect #12585.
  • What the gates catch: Console Pin Gate (ci.yml console-pin) builds the SPA bundle at the pin with this tree's spec injected and asserts the canary; it runs no objectui tsc, so TS2430 and the Studio seed's 422 would have surfaced in objectui's own CI at the bump and in the full battery's dogfood, not in that gate. The fix lands before either runs.
  • Records: this record names c2b9935b4; the merge of main and the pin-bump commit make a new head, which needs its own record naming it.

Implemented-by: claude/issue-23610-page-isdefault-template-slots
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: d36a804054a170573b12506d797277d6ecdba05f
Local-runs: none

Written 2026-10-16T01:55Z. A DELTA record: the full review PASSED on c2b9935b4 (record 6204434889, which stands for everything it judged); this record judges the four commits since and the head they make. Read-only, these inputs and nothing else: card #23610's body and all 18 comments; PR #23708's body, file list (33) and the delta git diff $(git merge-tree --write-tree 140238a47b c2b9935b4) d36a804054 (the head fetched into a ref this clone owns, refs/cr2/pr-23708-head, objects read with git show / git diff, no checkout); objectui f96f771140f1..daeb77fc84dd read in a scratch checkout fetched for it; the check-runs on d36a804054. Nothing was built, run or re-run. Face: a published schema (packages/spec/src/ui/page.zod.ts), so the tier is by face; no governed path in the file list (Governed Surface Queue Guard success). On d36a804054 all 41 check-runs are completed: 36 success, 5 skipped (Console Pin Gate, Dogfood Verify CLI, the dogfood matrix placeholder, Temporal Conformance, the opt-in tarball smoke — the two-speed rule), 0 failure; every required context is success — Lint & Repo Gates, TypeScript Type Check, Build Core, Test Core (6/6 shards), Governed Surface Queue Guard — the last of them Test Core at 2026-10-16T01:20:34Z, re-read for this record.

① Derived judgments

The merge 6f76056dc7 (origin/main 140238a47b). RIGHT, and clean of hand edits. git merge-tree --write-tree 140238a47b c2b9935b4 gives tree 2841862544…, which IS the merge commit's tree, so the merge carried exactly what the two parents give. The two sides are disjoint: the PR's 29 paths (merge-base 8c85141c5) and main's 146 paths share none, so no merge=os-regen path met the driver and no regeneration was owed (the dev's "no regeneration owed" holds; TypeScript Type Check, the job that runs check:generated, is success on the head). Main's move brought the pin to f96f771140f1 (#23738) and nothing touching page.zod.ts, the ui shards or the PR's docs.

The PR's own delta: 4 files, +25/−7, and nothing else. .changeset/console-daeb77fc84dd.md (+18), .objectui-sha f96f771140f1 → daeb77fc84dd, packages/sdui-parser/objectui-lockstep.json (rev, revDate, recordedAgainstPin: 3 lines), scripts/sdui-manifest.record.json (objectuiSha, modulesRoot, generatedAt: 3 lines). The file list is the PASS record's 29 plus these 4 = 33 (+1055/−100). No source, test, schema or doc line moved since c2b9935b4, so every ① judgment of record 6204434889 carries over unchanged: Q1, Q2, half 1 and the public surface are as reviewed there. sdui.manifest.json is byte-identical to base (its sha256 370153544e9b… and components: 107 stand in the re-recorded record), and origin/main's pin is still f96f771140f1 at 6362c66ca8 (read for this record), so the hop is forward and the head's No other open PR may claim the same single-writer path is success.

Does the pin move carry the owed objectui fix (AGENTS.md Post-Task Checklist step 4)? YES — RIGHT. daeb77fc84dd824e74c95b87cdede85afdb248e7 is the squash of objectstack-ai/objectui#12585, merged 2026-10-15T23:47:53Z, and f96f771140f1 is its ancestor (checked in the scratch checkout: 5 commits, git merge-base --is-ancestor holds). At daeb77fc84dd: packages/plugin-detail/src/synth/buildDefaultPageSchema.ts seeds template: 'default'; packages/types/src/layout.ts PageNodeSchema.template is SpecPage['template']; packages/types/src/zod/layout.zod.ts takes stripImportedDefaults(SpecPageSchema).shape.template; the two fixtures follow; one changeset patch for @object-ui/plugin-detail and @object-ui/types. That is the sibling fix the PASS record's ③ named as owed, shipped in the same landing as the narrowing, with the pin bump and both re-records — step 4 is met.

Does f96f771140f1..daeb77fc84dd bring any other renderer change this PR's spec text would misdescribe? NO — RIGHT. The range is 5 commits / 97 files (27 of them deleted screenshots): 7066dae53 docs (studio-redesign screenshots moved off main, no changeset); 830eb1f2d objectui#12522 (Studio page designer for html / react pages: a numbered source editor, a Properties tab, a disabled Run switch — an editor, not a renderer); f23eb01cb objectui#12514 (Studio empty states); 3e09c1e8d objectui#12510 (accessible names); daeb77fc8 objectui#12585 (above). packages/components/src/renderers/layout/page.tsx, app-shell/src/console/AppContent.tsx and app-shell/src/hooks/usePageAssignment.ts are byte-identical from 1f23d5b070fa (the pin the PASS record read) through daeb77fc84dd: TEMPLATE_REGISTRY (page.tsx:474-479) still maps default and full-width to one FullWidthTemplate and holds exactly header-sidebar-main, three-column, dashboard beside them; findRegion lowercases both sides (:86-87); each template excludes exactly the slots the spec's PAGE_TEMPLATE_SLOTS declares (:285, :384, :417; dashboard header / footer at :455); HomePageLayout / AppPageLayout / UtilityPageLayout (:501 / :509 / :517) are the one region layout at three widths. A grep of the range's non-test diff for full-width, TEMPLATE_REGISTRY, findRegion, isDefault, resolveLandingRoute, left_sidebar, right_sidebar hits only the seed and the two type faces. So the PR's half-1 text, its Templates table, its slot table and its "no rendering fork" premise describe the renderer at the NEW pin as well as at the old one. The console changeset's minor entry (12522) changes what Studio shows for an html / react page, not what the spec says such a page renders (kind: html / react render their source).

Is the lockstep re-record byte-faithful (no port owed)? YES — RIGHT. check-sdui-lockstep.mjs --update records objectui's side only, refuses unless the checkout's HEAD is the pin (judgeRecordability), and stamps revDate from git show -s --format=%cI HEAD: the record's rev is daeb77fc84dd…, its revDate is that commit's committer date (read in the scratch checkout as 2026-10-15T23:34:05Z; the record spells the same instant with a +00:00 offset), and recordedAgainstPin equals .objectui-sha. Its files are the 9 non-test sources of packages/sdui-parser/src at daeb77fc84dd (listed from the tree). objectui's packages/sdui-parser/ is byte-identical between f96f771140f1 and daeb77fc84dd (empty diffstat), so the parity fingerprint — grammarRegion blob 0131f27c… over 214 lines, the 25 diagnosticCodes, the containment predicate blob 76c18fb9… — is unchanged from the record main carries at f96f771140f1, which is exactly what the 3-line diff shows. check:sdui-lockstep (in Lint & Repo Gates, success on the head) holds this repo's copy equal to that record, so no port is owed; the dev's "byte-identical over 214 grammar lines, 25 diagnostic codes agree" is the same reading.

The SDUI manifest re-record. RIGHT. gen-sdui-manifest-node.mjs derives modulesRoot from the pin (.cache/objectui-daeb77fc84dd/apps/console) and reads objectuiWorkspaceVersion off the built tree: 17.7.0, which is what packages/core/package.json declares at daeb77fc84dd (and at f96f771140f1) — check 4 of check-sdui-manifest.mjs, the leg CI cannot run without an objectui checkout, read here against the tree. sha256 and components are unchanged because the artefact is unchanged, which is right: the range touches no registry module's public configs (plugin-detail's change is in synth/). Checks 1–3 ran green in Lint & Repo Gates.

② Semver level

.changeset/console-daeb77fc84dd.md — @objectstack/console: minor. RIGHT. The level is the highest objectui declared over the range: the four changesets added in f96f771140f1..daeb77fc84dd are 12522-page-designer-source-page.md (@object-ui/app-shell: minor), 12514-studio-empty-states.md (patch), 12510-studio-action-icon-picker-name.md (patch) and 23610-record-page-seed-default.md (patch, patch); none is marked breaking, and the one commit without a changeset (7066dae53, docs) is listed by subject. "4 releasing of 4 changesets added across 5 non-merge commits; omitted: 1 commit carrying no changeset" is the range as read. The body is the digest's own form, line for line as .changeset/console-f96f771140f1.md on main (#23738) — no hand edit visible, and the objectui range: line closes at the new pin. A minor console changeset with no breaking entry carries no Clause-② line and no ADR-0087 marker, as the main precedent does; check:adr-0087-registration and Check Changeset are success on the head.

The PR's existing changeset .changeset/23610-page-isdefault-template-slots.md. RIGHT and byte-unchanged by the delta: @objectstack/spec: minor, @objectstack/objectql: patch, the BREAKING banner, Clause-②: yes (narrowing) and the one adr-0087: registered marker naming both D3 ids. It still matches what the PR publishes: .changeset/config.json holds @objectstack/spec, @objectstack/objectql, @objectstack/console and @objectstack/sdui-parser in one fixed group, so the group ships minor in pre mode (next) with or without the console row; packages/sdui-parser/objectui-lockstep.json is outside @objectstack/sdui-parser's files (dist, README.md, CHANGELOG.md), so no sdui-parser row is owed; scripts/sdui-manifest.record.json and .objectui-sha publish nothing of their own — the console changeset is their declaration. 1,155 changed lines, under the 3,000-line class.

Clause-②: yes (narrowing) — stands. The delta adds no accept-set change on this side (a pin move plus two generator records), so the PR's line is the spec narrowing's, as the ruling (item 6) and the PASS record have it.

③ Boundary flags

Dev report 6205895856 (patch round 2, this head).

Flags of the PASS record 6204434889 the delta closes or reopens.

What is measured only after landing. Console Pin Gate is skipped on the PR run by the two-speed rule and runs on main's full battery; objectui's own CI type-checks @object-ui/types against the published spec (^18.0.0-next.1), not this one. That the TS2430 is gone against THIS spec rests on two dev readings — the objectui dev's tsc on the three packages/types tsconfigs with #23708's built spec linked in (6204227787), and this dev's pnpm objectui:build at the new pin with the spec-injection assertion green plus check:console-sha and check-sdui-manifest --require-objectui exit 0 (6205895856) — both consistent with the source read here (the type is the spec's by reference). A full-battery red there after landing is the lane's first work, by the main-red convention.

Implemented-by: claude/issue-23610-page-isdefault-template-slots
Reviewed-by: session_01LFqdRsuM5nkqoJdHd5ztVk

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 — ACCEPT (delta) · objectstack domain:spec seat 2 (#18549) · session session_01LFqdRsuM5nkqoJdHd5ztVk · 2026-10-16T01:57Z

This is the seat review of record for PR #23708 (#23610 items 1–3, ruling 6190536588, claim 6194235384) on head d36a804054a170573b12506d797277d6ecdba05f. It extends the earlier seat review and the full contract review PASS 6204434889 on c2b9935b4 to the four commits of patch round 2 (report 6205895856).

  1. The delta.
    • The merge of origin/main 140238a47b carries no hand edit: its tree equals the clean merge tree. Main's paths and the PR's are disjoint.
    • .objectui-sha moves f96f771140f1 → daeb77fc84dd, through scripts/bump-objectui.sh. The script wrote .changeset/console-daeb77fc84dd.md (@objectstack/console: minor, the highest level objectui declared over the 5-commit range).
    • The SDUI manifest record and the sdui-parser lockstep are re-recorded at the new pin. sdui.manifest.json is byte-unchanged, and the lockstep needs no port.
    • No source, schema, test or doc line moved since c2b9935b4.
  2. The coupling is met (AGENTS.md Post-Task Checklist step 4). daeb77fc84dd is the squash of fix(plugin-detail,types): seed new record pages with the default template and type the page template by reference to the spec objectui#12585, whose objectui half this seat ACCEPTed in 6204474766.
    • The narrowing ships in the same landing as the sibling fix and the pin bump.
    • The renderer files this PR's spec text describes are byte-identical across the range.
  3. Gates on this head. 41 check-runs: 36 success, 5 two-speed skip, none red. The five required contexts are success.
    • The dev's local run derived 132 families: 132 run, 0 NOT-MEASURED.
    • It also ran the local half of the Console Pin Gate (objectui:build with the spec-injection assertion, check:console-sha, check-sdui-manifest --require-objectui): all exit 0.
  4. Contract review: delta PASS at CONTRACT_REVIEW_TIER, comment 6206263948, on this head. No judgment was marked WRONG.
  5. The review's ③ item 1, done in this act. The body's line count (now +1055/−100 across 33 files) and its "Landing order" section are amended to this head, and the body was read back.
  6. Accepted deviations (report 6205895856):
    • bump-objectui.sh --no-commit with a hand-written commit, so the commit carries the model-free trailer pair. The files are the script's own output.
    • A blobless objectui clone in the dev's scratchpad was used read-only.
  7. Residual, by design. Console Pin Gate runs on main's full battery after landing. A red there is the lane's first work.
  8. Landing terms.

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/xl tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants