Repository navigation
feat(spec)!: a page region named main takes only width: 'full' - #24362
Conversation
PageSchema refuses width small, medium or large on a region named main (case-insensitive) at regions.N.width, on every page kind and template: no layout reads those values, and the only reader of main.width is the page's full-bleed, for 'full'. Exported as checkPageMainRegionWidth. The data-at-rest half is the retired D2 conversion page-main-region-width-removed (a lossless delete); the D3 entry is page-main-region-width-refused. In-repo pages that declared a large main (examples, the mcp and cloud-connection system pages, docs, spec tests) drop the key. Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
…s for checkPageMainRegionWidth Claude-Session: https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckThis PR changes 3 package(s): 1 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
⛔ 1 release-owned page(s) also name something this change touched. These are read-only:
What this run could not see
Coarse fallback — 150 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d424098f409d5ba71b8819133a4cb400b8a6b4d9 && git checkout d424098f409d5ba71b8819133a4cb400b8a6b4d9
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a21fd6c531e50a53deb26a7d04886fbb073164e8 7263210e149fb5f971b44069e16c1b295daedd69 && git checkout -B drift-repro a21fd6c531e50a53deb26a7d04886fbb073164e8 && git merge --no-ff 7263210e149fb5f971b44069e16c1b295daedd69
node scripts/docs-audit/affected-docs.mjs --json a21fd6c531e50a53deb26a7d04886fbb073164e8
|
席位核对 (seat check) · PR #24362 ·
|
| item | reading |
|---|---|
| Shape | Draft, base main, assignee zhuangjianguo. The body's line 1 is Fixes #24326, line 3 is Clause-②: yes (narrowing); no other closing keyword. 22 files, +492/-26; not governed. The changeset is minor on @objectstack/spec with the ! title, plus patch on @objectstack/mcp and @objectstack/cloud-connection. It has one ADR-0087 marker (registered page-main-region-width-refused, page-main-region-width-removed) and a FROM → TO table. |
| The seat's ruling, against the diff | Refuse route as ruled. checkPageMainRegionWidth (page.zod.ts) refuses small / medium / large on a region named main at regions.N.width; the name match is case-insensitive, as the renderer's is. The message names the region and 'full'. It is attached beside checkPageRegionPlacement, so definePage, defineStack and the save door reach it. 'full' and every other region are unchanged. P1–P3 are answered in the PR body; the seat's own P1 reading at the pin (6250714038) agrees. |
| Accepted deviation 1: the D2 conversion | page-main-region-width-removed in conversions/registry.ts, with its pins, is outside the claim's listed surface. It is the data-at-rest half: without it, the strict artifact parse refuses this repo's hotcrm 4.0.1 fixture (5 main regions with large). The dev's ablation A2 shows it. The brief's G1 named this entry as possibly owed, and it follows the precedent's D3 + retired-D2 shape. The contract review judges it. |
| Accepted deviation 2: two shipped system pages | packages/mcp/src/connect-ui.ts and packages/cloud-connection/src/cloud-connection-ui.ts drop width: 'large' from main. Each is a one-line delete with a patch bump. They are domain:cli files; the seat posts the cross-lane declaration on that lane's seat post in this act. The examples and content/docs/ui/pages.mdx drop the same key, and content/docs/** is domain:devx's: a note goes there too. No open PR touches any of the 22 files (all 7 open PRs' file lists were read). |
Local runs (dev, on 7263210e1) |
spec build, check:generated 14/14, 695 test files and 21225 tests passed, typecheck exit 0. The closure build covers mcp, cloud-connection and both examples: their tests and typechecks are green. Ablations: A1 (the check) turned 89 tests red; A2 (the conversion) turned 58 red plus the objectql artifact case. Both restores were proven by blob hash. Gates: 114 derived, 114 run with exit 0, reconciled with --ran. |
| Out of scope | (1) objectui's passing-keys-arms-11440 test (packages/types, :83) and its packages/cli twin pin a main region with width: 'large' as accepted. The seat re-read both at the pin. They go red at objectui's next @objectstack/spec bump that carries this change. The seat files the objectui card in this act and links it here. (2) The header and footer width family on the non-default templates has no producer: Acceptance notes. (3) hotcrm's home.page.ts must drop the key at its next spec bump; its stored rows convert on read: Acceptance notes. |
| Landing | After the contract review PASS and a fully green head: pr_ready then automerge_enable. The merge closes #24326. |
Generated by Claude Code
|
Carrier for the objectui finding · A correction to the ACCEPT Generated by Claude Code |
Contract reviewServed-tier: Read-only review of the net diff against the merge base ① Derived judgments
② Semver level
③ Boundary flags
Implemented-by: VERDICT: PASS Generated by Claude Code |
|
Contract review adopted ·
Generated by Claude Code |
Fixes #24326
Clause-②: yes (narrowing)
The seat's ruling (route: refuse) is carried out here. A page region named
mainnow takes onlywidth: 'full'.'small','medium'and'large'onmainare refused at publish, with a located message.'full'keeps its page-wide meaning, and every other region accepts the same widths as before.What changes
definePage/PageSchemaparsemainwithwidth: 'large'parsesregions.N.width(code: 'custom'), naming the region and'full'defineStack(os validate,os build)STACK_SCHEMA_INVALID422 atpages.N.regions.M.widthINVALID_METADATA(the samePageSchemaparse)sys_metadatapage row, an artifact inside its declared-floor window, a package body registered with its declared rangeOS_METADATA_CONVERTEDnotice per site, and the page renders as beforewidth: 'full'onmain; any width onsidebar,aside,header,footeror a dashboard cellpackages/spec/src/ui/page.zod.ts: a new exported check,checkPageMainRegionWidth. It is attached toPageSchemaby identifier, aftercheckPageRecordBindingPlacement, so every door that runscheckPageRegionPlacementruns it too. It judges every page kind and template, because no layout reads a non-'full'width onmainon any of them. It matches the region name case-insensitively, as the renderer does (name.toLowerCase() === 'main').PageRegionSchema.widthgains a.describe()that states the rule.mainis refused withwidth: 'large'(since @objectstack/spec 18): amainregion takes onlywidth: 'full'. It is drawn at the width its template leaves it — besidesidebarandasideit takes the space they leave, and ondashboardit is one grid cell — so'small','medium'and'large'change nothing there.'full'is page-wide: the page drops its max-width cap and fills the screen. Deletewidthto keep the page exactly as it renders, or writewidth: 'full'for a full-width page."packages/spec/src/migrations/entries/semantic/18.page-main-region-width-refused.ts.page-main-region-width-removedinpackages/spec/src/conversions/registry.ts. It setstoMajor: 18,retiredFromLoadPath: true,retiredAfter: '17.7.0'andorder: 69. It is inserted at its id-sorted position and defined directly abovepageRequiresNonCompiledKindRemoved, the entry after it. It is a pure lossless delete, scoped exactly as the refusal is.packages/spec/api-surface/ui.jsonandpackages/spec/export-origins/ui.json(one added export each), andcontent/docs/references/ui/page.mdx(the newwidthdescription). All three came fromcheck:generated's stale list, regenerated by its own commands.Why the D2 entry is in this PR (outside the claim's listed file surface)
The claim lists one semantic entry. The D2 conversion lives in
packages/spec/src/conversions/registry.ts, and its pins live in a newconversions/page-main-region-width-removed.test.tsand in aJOINrow inshared/retired-key-removal-version.pin.test.ts. Those three paths are not on the claim's list. They are here because the narrowing alone breaks data at rest:mainregion inpackages/objectql/src/__fixtures__/hotcrm-4.0.1-published-pages.artifact.jsondeclareswidth: 'large'(5 of its 8 pages). Without a conversion, the strict body parse inresolveArtifactPackageOrderrefuses them. That is the door the whole option-B artifact case inregistry-installed-package-retired-page-declarations.test.tsdrives. It is the same kernel-build stop that the isDefault and full-width narrowing had to fix after it landed.Premises (verified before code)
.objectui-sha0707b4ae3712, objectuipackages/components/src/renderers/layout/page.tsxdrawsmainasflex-1 min-w-0. That happens inRegionLayout(:434, used by thedefaulttemplate and by every template-less page type), inHeaderSidebarMainTemplate(:507) and inThreeColumnTemplate(:541).DashboardTemplatedrawsmainas one grid cell with no width class (:574). The only read of amainregion'swidthisPageRenderer's full-bleed, for'full'(:889–:894). It removes the max-width cap. Re-read at objectuiorigin/main43efd078aa, as fetched in this container: the same lines.git grep -n "width" 0707b4ae37 -- packages | grep -i maingives 21 hits. Every hit outsidepage.tsxis a test, a CHANGELOG, a comment, or an unrelatedwidth(Gantt geometry, mobile copy). A wider read of region width readers at the pin (region.width,main.widthand similar) findspage.tsx(header/footerviagetRegionWidthClass,sidebar/asideviagetSidebarWidthClass, and themainfull-bleed=== 'full') and one display-only label in Studio'sPageBlockCanvas.tsx:760. That label prints the value and does not lay anything out. No renderer honourssmall,mediumorlargeonmain.PageRegionSchema.widthisz.enum(['small','medium','large','full'])(page.zod.ts:49), shared by every region. The refusal is aPageSchema-level check keyed on the region's name, soPageRegionSchemaitself accepts exactly what it did, and so does every other region.findRegionmatches case-insensitively (r.name?.toLowerCase() === name.toLowerCase(),page.tsx:90), and so does the full-bleed read. The check uses the sametoLowerCase()comparison against'main'.In-repo sites that declared a non-
'full'width onmain(G2)Each change drops
width: 'large'frommainand nothing else. The page renders the same. None was changed to'full', which would make the page full-bleed.examples/app-crm/src/pages/welcome.page.tsexamples/app-showcase/src/ui/pages/contact-form.page.ts,index.ts,my-work.page.ts,new-project-wizard.page.ts,page-variables.page.ts,project-workspace.page.tspackages/mcp/src/connect-ui.ts(the shipped Connect an Agent page) andpackages/cloud-connection/src/cloud-connection-ui.ts(the shipped Cloud Connection page). Both are published, so both carry apatchin the changeset.content/docs/ui/pages.mdx: three snippets, one of themos:check, plus thewidthrow of the region table, which now states the rule.packages/spec/src/ui/page.test.ts: the two parse fixtures (:187,:377).packages/objectql/src/__fixtures__/hotcrm-4.0.1-published-pages.artifact.json. It is a byte-identical published artifact, and the D2 entry converts it at the data-at-rest doors.skills/**. Incontent/docs/protocol/objectui/layout-dsl.mdx, the multiline search matched asidebarwidth only.Tests
packages/spec/src/ui/page-default-and-region-placement.test.ts, new block "a region namedmaintakes onlywidth: 'full'":three-columnmain, assertingpathregions.2.width,code: 'custom', the message's first sentence,'full',@objectstack/spec 18, and no tracker number;Main);header-sidebar-main, ondashboard, on aslottedpage and on anhtmlpage;{ code: 'STACK_SCHEMA_INVALID', status: 422 }atpages.1.regions.1.width;'full'onmainis accepted with its value kept;largeonheader, every width onsidebarandaside,smallon a dashboard cell, and amainwith no width are all accepted;largemain converts on read with the named notice and then parses.packages/spec/src/ui/object-refinement-check-exports.test.ts:checkPageMainRegionWidthjoins the catalogue with its own failure matrix (parity across three runners, count and bijection, attached by identifier, barrel identity).packages/spec/src/conversions/page-main-region-width-removed.test.ts: registration, copy-on-write, idempotence, the stored-row seam, and an agreement matrix. Over every width, name casing, kind and template in it, the conversion deletes a width exactly where the parse refuses one.packages/spec/src/shared/retired-key-removal-version.pin.test.ts: theJOINrow. The prescription names@objectstack/spec 18, which is whatretiredAfter: '17.7.0'derives.Verification (head
7263210e1, the final head; every reading below is from this run)pnpm --filter @objectstack/spec build: exit 0.pnpm --filter @objectstack/spec check:generated: exit 0. "All 14 generated artifacts are up to date".pnpm --filter @objectstack/spec test: exit 0. 695 files passed, 21225 tests passed, 1 todo.pnpm --filter @objectstack/spec typecheck: exit 0. That coverstsc --noEmit,check:scripts-typecheckandcheck:test-typecheck. The test layer compiles undertsconfig.test.json, and the debt ledger is unchanged (52 files, 242 errors, 132 pinned signatures). None of the new test files is in it.@objectstack/mcp,@objectstack/cloud-connection,@objectstack/example-crmand@objectstack/example-showcase: 63 of 63 tasks, exit 0.@objectstack/mcp: test 40 files, 479 passed; typecheck exit 0.@objectstack/cloud-connection: test 52 files, 614 passed (this includesmarketplace-install-local-convert-forward.test.ts, which reads the hotcrm 4.0.1 artifact); typecheck exit 0.@objectstack/example-crm: test 6 files, 46 passed; typecheck exit 0.@objectstack/example-showcase: test 36 files, 431 passed; typecheck exit 0.registry-installed-package-retired-page-declarations.test.ts(8 passed) and service-packagehydration-convert-forward.test.ts(5 passed).pnpm check:adr-0087-registration: exit 0. Its verdict line: ".changeset/24326-main-region-width-refused.md[BREAKING+bang+clause-②-narrowing] registered page-main-region-width-refused, page-main-region-width-removed (new here: page-main-region-width-refused, page-main-region-width-removed)".node scripts/pm/dispatch-gates.mjs --commands(no paths, merge basedd72a047f, 22 paths) derived 114 commands. All 114 were run, and every one exited 0. The reconciliation with--ranand exit codes recorded: "114 derived famil(ies) accounted for — 114 run, 0 NOT-MEASURED (a DERIVED zero — all 114 recorded an exit code and none of them is 3)".pnpm check:dev-prereqs(a local-only family): exit 0.pnpm lint,test:repo, and the rest of the farm.Ablations (each committed first, restored from
HEADand proved by blob hash)checkPageMainRegionWidth, the guard was replaced so that every string width returns early. The anchor count went from 1 to 0 and the marker count to 1. The four pinning suites then failed: 89 failed, 422 passed. Restored: the blob is448b9617687d, equal toHEAD, andgit diff HEADis empty.dist/files. The spec conversion and placement suites failed (58 failed, 430 passed). The objectql option-B artifact case failed too (1 failed, 7 passed: "Release artifactpackages[0]is …" refused). That is the kernel-build stop this entry exists to prevent. Restored: the blob is4f56b2223e64, equal toHEAD. After a rebuild the marker is absent from all 232dist/files.Landing order with the seat's other narrowing
This seat's other narrowing in this window is PR 24347 (
stack.zod.ts), and it is in review. If both touch the migration registry, whichever lands second mergesmainthroughscripts/pm/os-regen-merge.sh. This branch already mergedmainthat way at7263210e1.Acceptance notes
objectstack-ai/hotcrmsrc/sales/pages/home.page.tsdeclareswidth: 'large'on its three-column page'smain(the card's measurement). It must drop the key when it takes the next spec release. Its built artifact, if it was built against a floor at or below 17.7.0, and its stored rows convert on read. hotcrm is not touched here.packages/types/src/__tests__/passing-keys-arms-11440.test.ts:83asserts thatSpecPageSchema.safeParse(page).successistruefor amainregion withwidth: 'large'(three cases:record,home,utility). It is at the pin0707b4ae37and at objectuiorigin/main43efd078aa. It goes red at objectui's next@objectstack/specbump, not before. The Console Pin Gate builds the pin against this tree and runs no objectui tests. Its build readsPageRegionSchema.width's type, and that type is unchanged. The same document appears inpackages/cli/src/__tests__/validate-passing-keys-11440.test.ts:98, run throughobjectui validate. That command judges with objectui's own authoring face, so whether it goes red is NOT MEASURED. The edit is the same one-key delete. No objectui change is made here, per the claim. The finding is in the report for the seat, to be filed in objectui. Owner: none.widthonheaderandfooteris read only byRegionLayout, which serves thedefaulttemplate and template-less pages.ThreeColumnTemplate,HeaderSidebarMainTemplateandDashboardTemplatedrawheaderandfooterwithout a width class.DashboardTemplatedraws every grid cell without one too. The spec accepts a width on all of them. No in-repo or measured external page declares a non-'full'header/footerwidth on those templates, or any width on a dashboard cell, so nothing on screen differs today. Owner: none.os migrate metareplays the retired D2 entry against sources by id, as for every step-18 retirement. This PR makes no claim about--writerewriting a TypeScript source.Generated by Claude Code