Repository navigation
Commit 3a734e0
Fixes #24326
Clause-②: yes (narrowing)
The seat's ruling (route: refuse) is carried out here. A page region
named `main` now takes only `width: 'full'`. `'small'`, `'medium'` and
`'large'` on `main` are 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
| Door | Before | Now |
| :--- | :--- | :--- |
| `definePage` / `PageSchema` parse | `main` with `width: 'large'`
parses | refused at `regions.N.width` (`code: 'custom'`), naming the
region and `'full'` |
| `defineStack` (`os validate`, `os build`) | accepted |
`STACK_SCHEMA_INVALID` 422 at `pages.N.regions.M.width` |
| metadata save door | accepted | 422 `INVALID_METADATA` (the same
`PageSchema` parse) |
| a stored `sys_metadata` page row, an artifact inside its
declared-floor window, a package body registered with its declared range
| accepted | converted on read: the width is deleted, with one
`OS_METADATA_CONVERTED` notice per site, and the page renders as before
|
| `width: 'full'` on `main`; any width on `sidebar`, `aside`, `header`,
`footer` or a dashboard cell | accepted | unchanged |
- `packages/spec/src/ui/page.zod.ts`: a new exported check,
`checkPageMainRegionWidth`. It is attached to `PageSchema` by
identifier, after `checkPageRecordBindingPlacement`, so every door that
runs `checkPageRegionPlacement` runs it too. It judges every page kind
and template, because no layout reads a non-`'full'` width on `main` on
any of them. It matches the region name case-insensitively, as the
renderer does (`name.toLowerCase() === 'main'`).
`PageRegionSchema.width` gains a `.describe()` that states the rule.
- The message, as it ships: "Region `main` is refused with `width:
'large'` (since @objectstack/spec 18): a `main` region takes only
`width: 'full'`. It is drawn at the width its template leaves it —
beside `sidebar` and `aside` it takes the space they leave, and on
`dashboard` it 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. Delete `width` to keep the page
exactly as it renders, or write `width: 'full'` for a full-width page."
- ADR-0087 D3:
`packages/spec/src/migrations/entries/semantic/18.page-main-region-width-refused.ts`.
- ADR-0087 D2, the data-at-rest half: `page-main-region-width-removed`
in `packages/spec/src/conversions/registry.ts`. It sets `toMajor: 18`,
`retiredFromLoadPath: true`, `retiredAfter: '17.7.0'` and `order: 69`.
It is inserted at its id-sorted position and defined directly above
`pageRequiresNonCompiledKindRemoved`, the entry after it. It is a pure
lossless delete, scoped exactly as the refusal is.
- Generated: `packages/spec/api-surface/ui.json` and
`packages/spec/export-origins/ui.json` (one added export each), and
`content/docs/references/ui/page.mdx` (the new `width` description). All
three came from `check: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 new
`conversions/page-main-region-width-removed.test.ts` and in a `JOIN` row
in `shared/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:
- Every page with a `main` region in
`packages/objectql/src/__fixtures__/hotcrm-4.0.1-published-pages.artifact.json`
declares `width: 'large'` (5 of its 8 pages). Without a conversion, the
strict body parse in `resolveArtifactPackageOrder` refuses them. That is
the door the whole option-B artifact case in
`registry-installed-package-retired-page-declarations.test.ts` drives.
It is the same kernel-build stop that the isDefault and full-width
narrowing had to fix after it landed.
- The delete is lossless (no layout read the value), so this is the
precedent's own shape: a D3 entry plus a retired D2 entry that data at
rest replays. Dispatch note G1 named "a conversion that drops a
non-'full' width from main" as the possible owed entry.
## Premises (verified before code)
- **P1: true.** At the console pin `.objectui-sha` `0707b4ae3712`,
objectui `packages/components/src/renderers/layout/page.tsx` draws
`main` as `flex-1 min-w-0`. That happens in `RegionLayout` (`:434`, used
by the `default` template and by every template-less page type), in
`HeaderSidebarMainTemplate` (`:507`) and in `ThreeColumnTemplate`
(`:541`). `DashboardTemplate` draws `main` as one grid cell with no
width class (`:574`). The only read of a `main` region's `width` is
`PageRenderer`'s full-bleed, for `'full'` (`:889`–`:894`). It removes
the max-width cap. Re-read at objectui `origin/main` `43efd078aa`, as
fetched in this container: the same lines.
- **P2: true.** `git grep -n "width" 0707b4a -- packages | grep -i
main` gives 21 hits. Every hit outside `page.tsx` is a test, a
CHANGELOG, a comment, or an unrelated `width` (Gantt geometry, mobile
copy). A wider read of region width readers at the pin (`region.width`,
`main.width` and similar) finds `page.tsx` (`header`/`footer` via
`getRegionWidthClass`, `sidebar`/`aside` via `getSidebarWidthClass`, and
the `main` full-bleed `=== 'full'`) and one display-only label in
Studio's `PageBlockCanvas.tsx:760`. That label prints the value and does
not lay anything out. No renderer honours `small`, `medium` or `large`
on `main`.
- **P3: true.** `PageRegionSchema.width` is
`z.enum(['small','medium','large','full'])` (`page.zod.ts:49`), shared
by every region. The refusal is a `PageSchema`-level check keyed on the
region's name, so `PageRegionSchema` itself accepts exactly what it did,
and so does every other region. `findRegion` matches case-insensitively
(`r.name?.toLowerCase() === name.toLowerCase()`, `page.tsx:90`), and so
does the full-bleed read. The check uses the same `toLowerCase()`
comparison against `'main'`.
## In-repo sites that declared a non-`'full'` width on `main` (G2)
Each change drops `width: 'large'` from `main` and 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.ts`
- `examples/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.ts`
- `packages/mcp/src/connect-ui.ts` (the shipped Connect an Agent page)
and `packages/cloud-connection/src/cloud-connection-ui.ts` (the shipped
Cloud Connection page). Both are published, so both carry a `patch` in
the changeset.
- `content/docs/ui/pages.mdx`: three snippets, one of them `os:check`,
plus the `width` row of the region table, which now states the rule.
- `packages/spec/src/ui/page.test.ts`: the two parse fixtures (`:187`,
`:377`).
- Left as is:
`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.
- Searched with no hit: `skills/**`. In
`content/docs/protocol/objectui/layout-dsl.mdx`, the multiline search
matched a `sidebar` width only.
## Tests
- `packages/spec/src/ui/page-default-and-region-placement.test.ts`, new
block "a region named `main` takes only `width: 'full'`":
- one refusal per refused value on a `three-column` `main`, asserting
`path` `regions.2.width`, `code: 'custom'`, the message's first
sentence, `'full'`, `@objectstack/spec 18`, and no tracker number;
- the case-insensitive match (`Main`);
- the refusal on a template-less page, on `header-sidebar-main`, on
`dashboard`, on a `slotted` page and on an `html` page;
- the stack door: `{ code: 'STACK_SCHEMA_INVALID', status: 422 }` at
`pages.1.regions.1.width`;
- controls: `'full'` on `main` is accepted with its value kept; `large`
on `header`, every width on `sidebar` and `aside`, `small` on a
dashboard cell, and a `main` with no width are all accepted;
- the ledger: the D3 entry is in step 18, and a stored row with a
`large` main converts on read with the named notice and then parses.
- `packages/spec/src/ui/object-refinement-check-exports.test.ts`:
`checkPageMainRegionWidth` joins 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`:
the `JOIN` row. The prescription names `@objectstack/spec 18`, which is
what `retiredAfter: '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 covers `tsc
--noEmit`, `check:scripts-typecheck` and `check:test-typecheck`. The
test layer compiles under `tsconfig.test.json`, and the debt ledger is
unchanged (52 files, 242 errors, 132 pinned signatures). None of the new
test files is in it.
- Build of the dependency closure of `@objectstack/mcp`,
`@objectstack/cloud-connection`, `@objectstack/example-crm` and
`@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
includes `marketplace-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.
- The suites that read the hotcrm 4.0.1 artifact fixture: objectql
`registry-installed-package-retired-page-declarations.test.ts` (8
passed) and service-package `hydration-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)".
- Gate families: `node scripts/pm/dispatch-gates.mjs --commands` (no
paths, merge base `dd72a047f`, 22 paths) derived 114 commands. All 114
were run, and every one exited 0. The reconciliation with `--ran` and
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.
- Left to CI: `pnpm lint`, `test:repo`, and the rest of the farm.
### Ablations (each committed first, restored from `HEAD` and proved by
blob hash)
- **A1: the check refuses nothing.** In `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 is
`448b9617687d`, equal to `HEAD`, and `git diff HEAD` is empty.
- **A2: the D2 entry never fires.** The conversion's width guard was
replaced, and the spec package was rebuilt. The marker was present in 8
`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 artifact `packages[0]` is …" refused). That
is the kernel-build stop this entry exists to prevent. Restored: the
blob is `4f56b2223e64`, equal to `HEAD`. After a rebuild the marker is
absent from all 232 `dist/` 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 merges `main` through `scripts/pm/os-regen-merge.sh`. This
branch already merged `main` that way at `7263210e1`.
## Acceptance notes
- **The known external author.** `objectstack-ai/hotcrm`
`src/sales/pages/home.page.ts` declares `width: 'large'` on its
three-column page's `main` (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.
- **An objectui fixture that pins the refused shape as spec-valid.**
objectui
`packages/types/src/__tests__/passing-keys-arms-11440.test.ts:83`
asserts that `SpecPageSchema.safeParse(page).success` is `true` for a
`main` region with `width: 'large'` (three cases: `record`, `home`,
`utility`). It is at the pin `0707b4ae37` and at objectui `origin/main`
`43efd078aa`. It goes red at objectui's next `@objectstack/spec` bump,
not before. The Console Pin Gate builds the pin against this tree and
runs no objectui tests. Its build reads `PageRegionSchema.width`'s type,
and that type is unchanged. The same document appears in
`packages/cli/src/__tests__/validate-passing-keys-11440.test.ts:98`, run
through `objectui 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.
- **Same family, not filed (no producer pulls it).** At the pin, `width`
on `header` and `footer` is read only by `RegionLayout`, which serves
the `default` template and template-less pages. `ThreeColumnTemplate`,
`HeaderSidebarMainTemplate` and `DashboardTemplate` draw `header` and
`footer` without a width class. `DashboardTemplate` draws 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`/`footer` width on those templates, or any width on a dashboard
cell, so nothing on screen differs today. Owner: none.
- `os migrate meta` replays the retired D2 entry against sources by id,
as for every step-18 retirement. This PR makes no claim about `--write`
rewriting a TypeScript source.
---
_Generated by [Claude
Code](https://claude.ai/code/session_019oFY6Hi35J7atvgVFnmoaK)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
1 parent a2a3777 commit 3a734e0
22 files changed
Lines changed: 492 additions & 26 deletions
File tree
- .changeset
- content/docs
- references/ui
- ui
- examples
- app-crm/src/pages
- app-showcase/src/ui/pages
- packages
- cloud-connection/src
- mcp/src
- spec
- api-surface
- export-origins
- src
- conversions
- migrations/entries/semantic
- ui
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
250 | 250 | | |
251 | 251 | | |
252 | 252 | | |
253 | | - | |
| 253 | + | |
254 | 254 | | |
255 | 255 | | |
256 | 256 | | |
| |||
468 | 468 | | |
469 | 469 | | |
470 | 470 | | |
471 | | - | |
| 471 | + | |
472 | 472 | | |
473 | 473 | | |
474 | 474 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
38 | 37 | | |
39 | 38 | | |
40 | 39 | | |
| |||
141 | 140 | | |
142 | 141 | | |
143 | 142 | | |
144 | | - | |
145 | 143 | | |
146 | 144 | | |
147 | 145 | | |
| |||
150 | 148 | | |
151 | 149 | | |
152 | 150 | | |
153 | | - | |
| 151 | + | |
154 | 152 | | |
155 | 153 | | |
156 | 154 | | |
| |||
336 | 334 | | |
337 | 335 | | |
338 | 336 | | |
339 | | - | |
340 | 337 | | |
341 | 338 | | |
342 | 339 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | 31 | | |
33 | 32 | | |
34 | 33 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
54 | 54 | | |
55 | 55 | | |
56 | 56 | | |
57 | | - | |
58 | 57 | | |
59 | 58 | | |
60 | 59 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
63 | 63 | | |
64 | 64 | | |
65 | 65 | | |
66 | | - | |
67 | 66 | | |
68 | 67 | | |
69 | 68 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
30 | | - | |
31 | 30 | | |
32 | 31 | | |
33 | 32 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
21 | 21 | | |
22 | 22 | | |
23 | 23 | | |
24 | | - | |
25 | 24 | | |
26 | 25 | | |
27 | 26 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
45 | 45 | | |
46 | 46 | | |
47 | 47 | | |
48 | | - | |
49 | 48 | | |
50 | 49 | | |
51 | 50 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
35 | 35 | | |
36 | 36 | | |
37 | 37 | | |
38 | | - | |
39 | 38 | | |
40 | 39 | | |
41 | 40 | | |
| |||
0 commit comments