Skip to content

fix(spec): the page-template-closed-region-unplaced-refused entry says what the console shows for a stored row - #24215

Merged
objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24195-page-template-entry-reason
Oct 17, 2026
Merged

objectstack-fleet[bot] merged 2 commits into
mainfrom
claude/issue-24195-page-template-entry-reason

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Fixes #24195
Clause-②: no

What changes

packages/spec/src/migrations/entries/semantic/18.page-template-closed-region-unplaced-refused.ts: only the last sentence of the entry's reason. acceptanceCriteria, surface and replacement are unchanged, and no other entry is touched.

Before (published in @objectstack/spec 18.0.0-next.2 and next.3):

A page row already stored with an unregistered template or an unplaced region is served unchanged and renders as before, while saving it again is refused until it is renamed. ADR-0087.

After:

A page row already stored with an unregistered template or an unplaced region is served unchanged, and saving it again is refused until it is renamed. Until then the console draws a refusal in its place: an unregistered template draws no layout, only a refusal naming the template and the registered templates; on a default, header-sidebar-main or three-column page an unplaced region is not drawn and moves into no other slot, and where it used to stack, under main, a refusal names it and the slots that template places. The exception is a page that names no template: it takes its page type's layout, which still stacks such a region under main. ADR-0087.

Plus one changeset, .changeset/24195-page-template-entry-reason.md (@objectstack/spec patch, Clause-②: no).

Why, and where the new wording comes from

The old sentence was true of the console when the entry shipped. objectstack-ai/objectui#12901 (merge commit 044f23193068, objectui main) removes that fallback, per the maintainer ruling on #23610 (record 6190536588, Q2 A: the renderer keeps no fallback for a shape the spec refuses). The new sentence describes what that commit's packages/components/src/renderers/layout/page.tsx draws. It was read at 044f23193068, and git diff 044f23193068 origin/main on that file is empty in objectui today:

  • resolveTemplate: a template name TEMPLATE_REGISTRY does not hold resolves to UnregisteredTemplateRefusal, which replaces the whole layout. Its text: "Not drawn: this page's template is not registered", then the template and the four registered names.
  • DefaultTemplate (through RegionLayout with template set), HeaderSidebarMainTemplate and ThreeColumnTemplate: each region outside PAGE_TEMPLATE_SLOTS[template] is left out of every slot. UnplacedRegionsRefusal is drawn under main, naming each region and the template's slots.
  • A page with no template takes the pageType dispatch (RecordPageLayout / HomePageLayout / AppPageLayout / UtilityPageLayout, all calling RegionLayout with no template), which still stacks such a region under main. objectui's own changeset for that PR says the same (.changeset/12476-page-template-slots.md, "Unchanged" paragraph). The spec still refuses that page's save, because checkPageRegionPlacement judges a missing template as default. So the card's Expected wording ("the console draws a refusal in its place") holds for a page that names a template, and the last sentence names the exception. Without it, the sentence would be wrong in the other direction for template-less rows.
  • "Served unchanged" still holds. The save door keeps a page body verbatim, with no parsed defaults (packages/metadata-protocol/src/protocol.ts, the non-view branch after the save-door safeParse). No D2 entry rewrites an unregistered name or an unplaced region, and a stored full-width row is rewritten to default on read, as the entry's earlier sentence already says.

The text names no tracker number, per AGENTS.md's runtime-string rule.

Transition window (not handled here)

objectstack's .objectui-sha reads 5e15928b46, 38 commits behind objectui 044f23193068. git merge-base --is-ancestor 5e15928b46 044f23193068 exits 0, so the pin is older than the change. Until the pin moves to a commit that includes objectstack-ai/objectui#12901, the console this repo serves still lays an unplaced region out under main, and still lays an unregistered template out as the page type's layout. The new text describes the console the upgrade guide is written for. This PR does not touch .objectui-sha: the pin bump is a single-claim path and belongs to another card.

Generated artifacts

None is committed for this text. The upgrade guide is generated at publish and at docs build into gitignored paths. packages/spec/src/migrations/registry.ts is gitignored and was regenerated by the build. pnpm --filter @objectstack/spec check:generated exits 0, with all 14 artifacts up to date. A scratch render (tsx scripts/build-upgrade-guide.ts --out SCRATCH_PATH) carries the new sentence, and no copy of "renders as before, while saving it again" is left in it.

Tests and gates (head fdb41f468, which merges origin/main a237d7f49)

  • pnpm --filter @objectstack/spec build (under the verify lock), before and after the merge: VERDICT command-exit 0. check:generated exits 0 at both heads.
  • pnpm --filter @objectstack/spec test on fdb41f468: 691 files, 20962 passed, 1 todo, SPEC_TEST_EXIT=0. The pre-merge head e402d82e7 read the same counts.
  • pnpm --filter @objectstack/spec typecheck on fdb41f468: SPEC_TYPECHECK_EXIT=0.
  • No test pinned the old sentence (git grep "renders as before, while saving it again" finds only the entry), so no pin moved and none was added. This is prose, and no consumer parses it.
  • Derived gate families: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack gave 81 commands. Each was run on fdb41f468 with its exit code captured before any pipe, and the record was reconciled with --ran: "81 derived famil(ies) accounted for — 80 run, 1 NOT-MEASURED". The one NOT MEASURED is pnpm check:dual-build-cjs-loads, which exited 3 (PREREQUISITE NOT MET). It loads every workspace package's built dist/, so it needs a full workspace build. That narrowing is declared here, and Lint & Repo Gates runs the gate in CI. check:doc-formula-expressions and check:lean-entry-closure refused until the @objectstack/formula, @objectstack/lint and @objectstack/objectql closure was built. Re-run after that build, both exit 0. Gates 27 to 56 were run again because their first pass overlapped that build, which rewrote packages/spec/dist. All 30 exit 0 on the second pass, and the first-pass readings are discarded as unmeasured, not counted as failures. The last derivation flags a stale tree: origin/main moved 3 commits after the merge (rest, service-settings), and the one derivation input that changed is scripts/check-route-envelope.mjs. None of those commits touches this entry.
  • No ablation: no tested behaviour changed.

Acceptance notes

  • The same "unplaced region ... renders as before" wording is also in release-owned text, which this PR does not edit: the "Stored metadata keeps loading." paragraph of the 18.0.0-next.3 section in packages/spec/CHANGELOG.md and packages/objectql/CHANGELOG.md, and the consumed changeset .changeset/pre/23610-page-isdefault-template-slots.md. AGENTS.md sends a factual error in a released entry to a dedicated docs-only PR. It is in the report for the seat.
  • objectui's template-less pageType path still stacks an unplaced region that the spec judges against default. fix(components): a slotted page template draws only the regions it places, and full-width retires (objectui#12476) objectui#12901 recorded this in its own acceptance notes and left it as is. If objectui later retires that path, this entry's last sentence needs the same kind of edit. Noted here, not filed.

Generated by Claude Code

…s what the console shows for a stored row

The entry's reason said a page row stored with an unregistered template
or an unplaced region "renders as before". The console that ships with
18 draws a refusal in its place instead: an unregistered template draws
no layout, only a refusal naming it and the registered templates; on a
default, header-sidebar-main or three-column page an unplaced region is
not drawn, and a refusal under main names it and the template's slots.
A page that names no template still takes its page type's layout, which
stacks such a region under main. acceptanceCriteria is unchanged.

Claude-Session: https://claude.ai/code/session_01TyRhWbVbbQpQJTDbW8VahJ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Oct 17, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see

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 8259ea03e2959129c688639f69d0782c3a820f89 → packageMentionDocs.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: fdb41f468559eb5c5ce4d0066ac934eaa5855025
Local-runs: none
Scope: packages/spec/src/migrations/entries/semantic/18.page-template-closed-region-unplaced-refused.ts + packages/spec/src/ui/page.zod.ts (checkPageRegionPlacement, PAGE_TEMPLATE_SLOTS, PAGE_TEMPLATE_OPEN_GRID, PageTemplateSchema, PageSchema.template default), packages/spec/src/conversions/registry.ts (page-template-full-width-to-default), packages/metadata-protocol/src/protocol.ts (the save door's verbatim branch and withDeclaredPageTypeDefault), packages/spec/scripts/build-upgrade-guide.ts (how reason is rendered), packages/spec/src/migrations/migrations.test.ts (the generic reason pins), and in objectui at 044f23193068 (the merge of objectstack-ai/objectui#12901) packages/components/src/renderers/layout/page.tsx and .changeset/12476-page-template-slots.md. The remainder of the diff, .changeset/24195-page-template-entry-reason.md, is the lane seat's ACCEPT at TIER_DEFAULT and is judged here under ② only.

Inputs: card #24195, its body and all three comments (triage 6238135277, Claim 6238361195, os-dev report 6239553860); PR #24215, its body, its file list (2 files, +18/-2) and the net diff against main (merge-base a237d7f); the check-runs on the head. The head had not moved at read time: refs/heads/claude/issue-24195-page-template-entry-reason is fdb41f4. Read at 2026-10-17T19:39Z.

① Derived judgments

Accept-set and public-surface changes the diff implies: none. The diff replaces the last sentence of one string literal, the reason of the semantic migration entry page-template-closed-region-unplaced-refused, and adds one changeset. id, surface, replacement and acceptanceCriteria are byte-identical to main; no Zod schema, export, conversion entry or registry template moves. reason is prose: build-upgrade-guide.ts:136 prints it as the "Why not automatic:" list item, the only generic pin on it is reason.length above zero (migrations.test.ts:139), and the old wording occurs nowhere else under packages/spec/src, so no pin moves and none is owed. Judged right.

Each claim the new sentence makes, read in the cited code:

  1. "served unchanged, and saving it again is refused until it is renamed" — carried over from the old sentence, still right. No path outside the spec calls checkPageRegionPlacement, PAGE_TEMPLATE_SLOTS or PageTemplateSchema (grep over packages/** minus spec: no hit); a stored row reaches the registry through applyConversionsToStoredItem (packages/metadata/src/loaders/database-loader.ts, packages/metadata/src/plugin.ts), and the only conversion on page.template is page-template-full-width-to-default (conversions/registry.ts:11444), which rewrites full-width alone. The save door parses through PageSchema, which carries PageTemplateSchema at template (page.zod.ts:1767) and superRefine(checkPageRegionPlacement) (:1974), so the row is refused on save. Right.

  2. "an unregistered template draws no layout, only a refusal naming the template and the registered templates" — objectui page.tsx at 044f23193068: resolveTemplate (:599) returns UnregisteredTemplateRefusal for any string TEMPLATE_REGISTRY lacks, and the dispatch (:849) renders that as the whole layout element, never reaching the pageType switch. Its text names schema.template and TEMPLATE_NAMES, every key of PAGE_TEMPLATE_SLOTS (:302, :348). Right.

  3. "on a default, header-sidebar-main or three-column page an unplaced region is not drawn and moves into no other slot, and where it used to stack, under main, a refusal names it and the slots that template places" — DefaultTemplate passes template="default" into RegionLayout, where extras is empty and unplaced is the remainder (:388, :389), so nothing stacks; HeaderSidebarMainTemplate (:494, :507) and ThreeColumnTemplate (:525, :541) compute unplaced with getRemainingRegions(regions, PAGE_TEMPLATE_SLOTS[template]) and hand it to UnplacedRegionsRefusal only, which sits directly after the main region in the main column in all three (:437, :507, :541). The refusal lists each region's name and PAGE_TEMPLATE_SLOTS[template].join(', ') (:333 to :341). dashboard is correctly left out of the sentence: it is the open grid in the spec (PAGE_TEMPLATE_OPEN_GRID; checkPageRegionPlacement returns before the unplaced issue, :1657) and in the renderer (DashboardTemplate draws every other region as a cell), so a dashboard page has no unplaced region. A second region filling an already filled slot is not an "unplaced region" and is not covered by this sentence; the entry's earlier sentence ("was not drawn at all") still describes it, since findRegion takes the first of a name and getRemainingRegions excludes the name (:90 to :99). Right. Minor: a default page whose regions name no slot at all (!hasStructure, :391 to :403) draws the refusal alone, not under a main; "where it used to stack" still holds, and the text is not wrong.

  4. "The exception is a page that names no template: it takes its page type's layout, which still stacks such a region under main" — resolveTemplate returns null for a falsy template (:600); the dispatch falls to switch (pageType) (:855 to :865); each of RecordPageLayout, HomePageLayout, AppPageLayout and UtilityPageLayout calls RegionLayout with no template, where extras is the remainder and renders under main (:388, :434 to :436). The spec still refuses that row's save: checkPageRegionPlacement reads page.template ?? 'default' (:1629). The exception is a stored-row-only case: a package page is parsed (PageTemplateSchema.default('default'), :1767), so it either carries default or is refused at objectstack validate and build; and the save door stores the authored body verbatim with type as the one grafted default (withDeclaredPageTypeDefault, protocol.ts:2078), so a template-less stored row stays template-less. objectui's own changeset says the same in its "Unchanged" paragraph. This sentence is the dev's one addition beyond the card's Expected; it is right, and without it the entry would claim a refusal a template-less row never draws.

  5. Provenance: the sentence describes objectui at 044f23193068, the merge of fix(components): a slotted page template draws only the regions it places, and full-width retires (objectui#12476) objectui#12901, which is on objectui main (merge-base --is-ancestor exit 0), and whose page.tsx is unchanged on objectui main c83e3fe53 today (git diff 044f23193068 origin/main -- page.tsx is empty). Right as of this read; the pin is in ③.

  6. Runtime-string rule: the new text names no tracker number; ADR-0087. is an ADR citation carried over. No backtick, pipe or angle bracket in the string; apostrophes only, escaped. The ⚠️ marker is kept in the position it had. Right.

Judged wrong: nothing.

② Semver level

Changeset .changeset/24195-page-template-entry-reason.md: '@objectstack/spec': patch, Clause-②: no, no arm. It matches what the diff publishes: a text correction inside a released package (the entry's reason ships in the generated upgrade guide and in os migrate meta output), with no accept-set, export or conversion change, so no with no arm is the right declaration and patch is the right level; skip-changeset would be wrong because the package publishes the text, and minor is not owed because nothing widens. The PR body carries the same Clause-②: no line. No ADR-0087 disposition marker is owed (nothing breaks). The changeset body restates the new sentence consistently with the entry, template-less exception included, so the two texts do not diverge. Check Changeset on the head: success.

③ Boundary flags

The dev declared open_questions: []. Its five deviations and two out-of-scope findings, each answered:

  • D1, wording beyond the card's Expected (the template-less exception): answered in ①.4 — right and measured; the card's Expected alone would be wrong in the other direction.
  • D2, one origin/main merge before the PR opened, main moved since: fine under Multi-agent §10; the merge commit carries no file change of its own (the net diff against main is the two files).
  • D3, check:dual-build-cjs-loads not measured locally: a declared narrowing; every Lint · lane on this head completed success, so the gate's own verdict stands in CI.
  • D4, gates 27 to 56 re-run after an overlapping build: a local-measurement note with no bearing on the contract; CI is the verdict.
  • D5, commit trailers: e402d82 carries the AGENTS.md model-free pair; the merge commit carries none. Fine.
  • OOS-1 (class b, release-text): three released copies of the old sentence remain — packages/spec/CHANGELOG.md:169, packages/objectql/CHANGELOG.md:128 and the consumed .changeset/pre/23610-page-isdefault-template-slots.md:35 ("Stored metadata keeps loading."). Correctly not touched here: release-owned text takes a dedicated docs-only PR. ESCALATED to the seat: it needs a card or that docs-only PR, or the 18.0.0-next.3 CHANGELOG keeps saying "renders as before" beside a guide that says the opposite.
  • OOS-2 (the template-less pageType path still stacks): the entry's last sentence now depends on objectui keeping that path; if objectui retires it, this entry needs a matching edit. Noted; nothing to do now.
  • Transition window (PR body, "not handled here"): .objectui-sha is 5e15928b46, an ancestor of 044f23193068 by 38 commits, so the console this repository builds and serves today still stacks an unplaced region under main and still lays an unregistered template out by page type. The new sentence is ahead of the pinned console and describes the console the 18 guide is written for; the card's Expected and triage's grade both order that direction, and the pin bump is another card's single-claim path. ESCALATED to the seat: the pin bump to a commit that includes fix(components): a slotted page template draws only the regions it places, and full-width retires (objectui#12476) objectui#12901 (manifest regeneration included) must land before 18.0 GA, or the guide published at GA describes a refusal the shipped console does not draw. Until it lands, a next prerelease carrying this text is ahead of the shipped console by exactly this sentence — the window the card accepted when it asked for this wording.
  • Governance: no governed surface in the file list, head repo equals base repo, 20 changed lines; no Tier H word is owed. Governed Surface Queue Guard: success.
  • Check-runs on fdb41f4 at this read, 39 in all: 26 completed success — among them Governed Surface Queue Guard, Check Changeset, Check PR Size, Build Core, every Lint · lane (ESLint, CI/release/engine gates, docs and code guards, repo and tooling gates, PM dispatch-gates, the two scoped lanes and the tooling self-tests), Type Check · consumer gates, Type Check · debt ledger, Type Check · source gates, Spec property liveness, Dogfood Regression Gate, the four claim and card guards, Check Documentation Links, Flag docs affected by code changes, Auto Label and filter; 6 skipped by path filter or opt-in (Build Docs, Console Pin Gate, the Dogfood Regression Gate matrix shards, Dogfood Verify CLI, Packed-tarball smoke, Temporal Conformance); 7 still in_progress — Test Core (1/6) to (6/6) and Type Check · workspace, which carry the required Test Core context and part of the required TypeScript Type Check context. None failed, none cancelled. Their conclusions are the gate verdicts; this record does not pre-judge the ones still running, and the queue guard holds the landing until they are green.

Implemented-by: claude/issue-24195-page-template-entry-reason
Reviewed-by: session_01TyRhWbVbbQpQJTDbW8VahJ

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对 · ACCEPT · PR #24215 @ fdb41f4685 (#24195) · domain:spec seat 1 · os-project-manager · session session_01TyRhWbVbbQpQJTDbW8VahJ · 2026-10-17T20:00Z

Reviewer of record. Every item below was checked against GitHub and the code, not against the report (6239553860 on #24195).

  • Shape: draft, base main. The first line is Fixes #24195, the body's only closing keyword, then Clause-②: no, matching claim 6238361195. 2 files, +18/−2, assignee os-project-manager. Not governed: check-governed-merges.mjs --pr 24215, 20 changed lines.
  • Premise, measured by the seat before dispatch (f963d8e2b): the entry's reason ended "renders as before" (:60). The objectui pin 5e15928b46 is 38 commits behind objectui PR docs(pm): an entry-.d.ts byte-identity claim needs a positive control in the same block #12901 (044f23193068).
  • What it lands:
    • The entry's last sentence now says what the 18 console draws: served unchanged; save refused until renamed; a refusal in the region's place. The dev measured one exception in objectui's renderer: a template-less page still stacks the region under main.
    • acceptanceCriteria, surface and replacement are unchanged. A patch changeset.
    • The changeset's sentences were read against the diff, and both texts say the same thing, the template-less exception included.
  • Contract review: PASS 6239769518 on this head (CONTRACT_REVIEW_TIER). Every claim in the sentence was checked in objectui's page.tsx at 044f23193068 and in checkPageRegionPlacement. It agrees with patch and Clause-②: no.
  • Gates: 41 check-runs on fdb41f4685, all completed: 35 success, 6 skipped (all in check-expected-skips.mjs's roster, matched by name). The five required contexts are success. Mergeable, state clean.
  • Deviations, accepted:
    • the template-less sentence beyond the card's Expected: measured, and without it the text would claim a refusal that never draws;
    • one main merge before opening;
    • check:dual-build-cjs-loads declared to CI;
    • an overlapping build, with all 30 affected gates re-run green;
    • the model-free trailer pair.
  • The review's two escalations, the seat's calls:
    • The 18.0.0-next.3 CHANGELOG paragraphs (packages/spec/CHANGELOG.md, packages/objectql/CHANGELOG.md, .changeset/pre/23610-page-isdefault-template-slots.md) say "renders as before": dropped, no card. They were true of the console next.3 shipped with. The change is the next release's news, and this PR's changeset carries it.
    • The transition window: the pinned console still stacks until the pin carries objectui PR docs(pm): an entry-.d.ts byte-identity claim needs a positive control in the same block #12901. Recorded on the seat post as a rider for the pin bump before 18.0 GA. .objectui-sha is a single-claim path and not this card's.
  • Other out-of-scope note: the template-less pageType path is objectui's: carrier objectui domain:ui seat 1 (PR docs(pm): an entry-.d.ts byte-identity claim needs a positive control in the same block #12901's Acceptance notes). Noted, not filed.

The PR goes to the merge queue in this act.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 17, 2026 20:02
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 17, 2026 20:02
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 17, 2026
Merged via the queue into main with commit 4f11af5 Oct 17, 2026
42 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-24195-page-template-entry-reason branch October 17, 2026 20:35
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/s tooling

Projects

None yet

2 participants