|
| 1 | +--- |
| 2 | +'@objectstack/spec': minor |
| 3 | +'@objectstack/lint': minor |
| 4 | +'@objectstack/metadata-protocol': minor |
| 5 | +--- |
| 6 | + |
| 7 | +**BREAKING** — retire the `type: 'page'` list-view mount and its `pageName` binding. |
| 8 | + |
| 9 | +A list view could declare `type: 'page'` and name a published page in `pageName`, |
| 10 | +and the view was to render nothing of its own and delegate to the page renderer. |
| 11 | +Only the spec half of that was ever built. **No renderer ever routed the member**: |
| 12 | +objectui's list-view switch shares its `default:` arm with `case 'grid'`, so a page |
| 13 | +view has always drawn an empty table where the page was supposed to be, and the |
| 14 | +three parse refusals that policed the binding policed a mount that never mounted |
| 15 | +anything. ADR-0049 enforce-or-remove; maintainer ruling 2026-09-09. |
| 16 | + |
| 17 | +## FROM → TO |
| 18 | + |
| 19 | +| you wrote (17.4 and earlier) | write instead | |
| 20 | +| --- | --- | |
| 21 | +| `{ type: 'page', pageName: 'sales_home', columns: [] }` on a list view | nothing on the view. Delete it, and reach the page from the app's `navigation`: `{ id: 'nav_sales_home', type: 'page', pageName: 'sales_home', label: 'Sales' }` | |
| 22 | +| `pageName` beside any other list-view `type` | delete the key — it was refused already, and is now a tombstone | |
| 23 | +| a list view that wanted rows | pick a row-drawing `type` — `grid` and its siblings, all unchanged | |
| 24 | + |
| 25 | +**The one-line fix:** delete `type: 'page'` and `pageName` from the list view; put |
| 26 | +the page behind an app navigation item, which is a different key on a different |
| 27 | +surface (`PageNavItem.pageName`) and is the page mount that has always rendered. |
| 28 | + |
| 29 | +`os migrate meta --from 17` lists the mechanical edits for existing sources; apply |
| 30 | +them by hand. |
| 31 | + |
| 32 | +## The retirement kit |
| 33 | + |
| 34 | +- **`pageName`** — a `retiredKey()` tombstone on `ListViewSchema` and |
| 35 | + `ObjectListViewSchema`. `tsc` types the key `never`, and a value reaching a parse |
| 36 | + raises the prescription rather than a bare unrecognized-key report. |
| 37 | +- **`'page'`** — an enum VALUE, so there is no tombstone to hang a prescription on |
| 38 | + (the def survives, one value lighter, and the four generated-surface ratchets are |
| 39 | + blind to that by construction). The `type` enum's own `error` map carries it, |
| 40 | + keyed on `issue.input` so only the value that used to be legal gets the |
| 41 | + "was removed" message; every other invalid `type` keeps zod's default text. |
| 42 | +- **`checkListViewPageMount`** — the exported object-level refinement existed only |
| 43 | + to police this mount, so it is removed with it, along with its three refusal |
| 44 | + messages. A downstream mirror that re-attached it (the reason it was exported) |
| 45 | + should drop the `.superRefine` line; the compiler delivers this one. It held no |
| 46 | + `ERROR_CODE_LEDGER` row — the three refusals were message constants, not codes. |
| 47 | +- **`validateViewPageRefs` / `VIEW_PAGE_UNRESOLVED`** (`@objectstack/lint`) — the |
| 48 | + `os validate` and publish-gate rule that resolved a mount against `stack.pages`. |
| 49 | + Removed: there is no reference left to resolve. Its nav twin |
| 50 | + (`validateNavTargetRefs`, on the app navigation item) is **untouched**. |
| 51 | +- **`RuntimeStackContext.pages`** (`@objectstack/lint`) and the `page` row of |
| 52 | + `CLOSURE_CONTEXT_KEY_BY_TYPE` (`@objectstack/metadata-protocol`) — the live page |
| 53 | + universe joined the per-write snapshot for that one rule, and leaves with it. A |
| 54 | + `PUT /api/v1/meta/view` publish no longer pays a `sys_metadata` round trip for a |
| 55 | + collection nothing consults. Hosts calling `runRuntimeAuthoringRules` / |
| 56 | + `evaluateRuntimeAuthoringGate` with an explicit `context.pages` drop that key. |
| 57 | +- **`defineStack`** — the `validateCrossReferences` branch that resolved a mount's |
| 58 | + `pageName` against `stack.pages` is gone. The surviving three page references in |
| 59 | + that function (an app nav item's `pageName`, a modal action's `target` at two |
| 60 | + rungs) keep their own policy. |
| 61 | +- **The metadata form** — `view.form.ts`'s `page` section, whose one input was |
| 62 | + `pageName`, is removed. A form input for an unwritable key is the false-compliant |
| 63 | + UI half of a retirement. |
| 64 | + |
| 65 | +## What an operator with a STORED page view sees |
| 66 | + |
| 67 | +A `sys_metadata` `view` row written before this release can carry `type: 'page'` and |
| 68 | +a `pageName`. Nothing breaks at read: the ADR-0087 conversion |
| 69 | +`view-page-mount-removed` (protocol 18) replays on rehydration and strips both keys, |
| 70 | +so the row is served canonical. `type` is **stripped, not rewritten** — it defaults |
| 71 | +to `grid` in the schema, so the row lands on exactly what it already rendered |
| 72 | +without the platform guessing a view type. |
| 73 | + |
| 74 | +The strip is announced once per row per process, on whichever seam served it. |
| 75 | +Grep for `carries a pre-protocol shape` — there are **three** emitters, one per |
| 76 | +rehydration seam, and they differ: |
| 77 | + |
| 78 | +- `[DatabaseLoader] stored view/<name> carries a pre-protocol shape; <notice>` |
| 79 | +- `[ObjectQLPlugin] stored view/<name> carries a pre-protocol shape; <notice>` |
| 80 | +- `[Protocol] stored view/<name> carries a pre-protocol shape; <notice> The row |
| 81 | + itself is unchanged — re-save it (Studio edit -> save, or run |
| 82 | + "os migrate meta --stored --apply") to persist the canonical shape.` |
| 83 | + |
| 84 | +`os migrate meta --from 17` lists the same edits for authored sources; |
| 85 | +`os migrate meta --stored --apply` rewrites the stored rows so the warn stops, and |
| 86 | +the next save through `PUT /api/v1/meta/view` heals one row the way it heals any |
| 87 | +pre-protocol shape. |
| 88 | + |
| 89 | +⚠️ The conversion walks `stack.views[]` in all three persisted spellings; it does |
| 90 | +**not** reach `objects[].listViews.*`, which no conversion in the registry reaches. |
| 91 | +An object body still carrying a page mount is refused at its own door with the |
| 92 | +prescription rather than converted. Measured population for both at the ruling: |
| 93 | +**zero** authored `type: 'page'` list views in this repository or any consuming app |
| 94 | +the seats can read — the in-tree `type: 'page'` hits are all app nav items. |
| 95 | + |
| 96 | +<!-- adr-0087: registered view-page-mount-removed --> |
0 commit comments