Skip to content

Commit d19cbad

Browse files
committed
chore(spec): regenerate artifacts, correct the per-user wording, add the changeset
- api-surface / export-origins record the new `VIEW_CONSOLE_ROUND_TRIP_KEYS` export; the view reference page carries the declared meanings. - A stored `view` row has no per-user scope, so the column-layout descriptions no longer call it per-user state. - Changeset: `@objectstack/spec` minor, with the Clause-② declaration. Claude-Session: https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH Co-authored-by: Claude <noreply@anthropic.com>
1 parent 8315ac6 commit d19cbad

6 files changed

Lines changed: 50 additions & 17 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
feat(spec): the console's round-trip keys on a stored `view` row are declared on the wire, so a parse keeps them (#20456)
6+
7+
Clause-②: yes
8+
9+
`saveMetaItem` stores a `view` body exactly as it was sent (ADR-0005 appendix (c)), and the members of `ViewMetadataSchema` that judge a stored row `.strip()` every key they do not declare. So the keys the console writes onto a stored view and reads back were in the store and nowhere in the contract. A census of objectui's console (at the `.objectui-sha` pin) measured which ones the parse dropped:
10+
11+
- `isPinned` and `sortOrder` on a flattened list overlay (they were already declared on the ViewItem record);
12+
- `visibility`, on both the flattened list overlay and the ViewItem record;
13+
- `_isOverride`, the marker that tells the console a row is the settings overlay of a code-defined view and not a saved view of its own.
14+
15+
## What it does now
16+
17+
- The ViewItem wire member (`ViewItemWireSchema`) and the flattened list overlay (`VIEW_METADATA_MEMBERS.listOverlay`) declare `isPinned`, `sortOrder` and `visibility` from one shared declaration, each with its meaning. The flattened list overlay also declares `_isOverride: true`, and its existing `isDefault` now carries its meaning. A parse of a console-written row keeps every one of them.
18+
- **New export `VIEW_CONSOLE_ROUND_TRIP_KEYS`** (`@objectstack/spec/ui`): each round-trip key, mapped to the members whose rows the console writes it on (`isDefault`, `isPinned`, `sortOrder`, `visibility`, `columnState`, `_isOverride`).
19+
- `visibility` is display grouping only (`private` / `team` / `organization` / `public` in the view switcher). It restricts nobody, and its declared meaning says so.
20+
- None of these keys is authorable. `defineViewItem` still refuses each of them by name, and `visibility` now gets a prescription that says what it is.
21+
22+
## What does not change
23+
24+
- **What is persisted.** The save still stores the request body verbatim. Storing the parsed body is a later, separate change.
25+
- The alias spellings the census found keep their declared spellings: `objectName` is `object`, and a top-level `id` is `name`. The console's filter / sort builder row ids stay `VIEW_CONSOLE_ROW_DECORATIONS`, removed before the parse.
26+
27+
## Values that are now refused
28+
29+
A declared key is typed, so a stored-row write carrying one of these keys with a value of the wrong type is now refused `422 INVALID_METADATA` at that key, where the key used to be dropped from the parse and the body stored as sent: a non-boolean `isPinned`, a non-integer `sortOrder`, a `visibility` outside the four groups, or an `_isOverride` other than `true`. The console writes none of these: its pin toggle writes a boolean, its reorder an integer index, and it stamps the marker as `true`.

‎content/docs/references/ui/view.mdx‎

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2256,9 +2256,10 @@ This schema accepts one of the following structures:
22562256
| **_packageId** | `string` | optional | Owning package machine id. |
22572257
| **_packageVersion** | `string` | optional | Owning package version. |
22582258
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
2259-
| **isPinned** | `boolean` | optional | Studio round-trip: view pinned in the switcher (per-user state, written by the console — not authored). |
2260-
| **sortOrder** | `integer` | optional | Studio round-trip: position within the switcher (per-user state, written by the console — not authored). |
2261-
| **columnState** | `{ order?: string[]; widths?: Record<string, number> }` | optional | Studio round-trip: per-user column order/widths (runtime-only state, written by the console grid — not authored) |
2259+
| **isPinned** | `boolean` | optional | Console round-trip: the view is pinned in the object's view switcher. Written by the console's pin toggle through the `view` metadata API and read back to draw the pinned group. Stored on the view's row, which has no per-user scope. Not authored. |
2260+
| **sortOrder** | `integer` | optional | Console round-trip: the view's position among the object's saved views in the switcher, 0-based and counted over saved views only (a code-defined view carries none). Written by the console's drag-reorder and read back to order the tabs. Not authored: `order` is the authored default position. |
2261+
| **visibility** | `Enum<'private' \| 'team' \| 'organization' \| 'public'>` | optional | Console round-trip: the group the switcher files this view's tab under (private, team, organization or public). Display grouping only, NOT access control: nothing restricts who can list or open the view by this value. No console control sets it; the console carries a stored value forward when it re-saves the row. Not authored. |
2262+
| **columnState** | `{ order?: string[]; widths?: Record<string, number> }` | optional | Studio round-trip: column order/widths (runtime-only state, written by the console grid and stored on the view's row, which has no per-user scope — not authored) |
22622263

22632264
### Nested Shape: `ViewItemWire[viewKind='list'].config`
22642265

@@ -2327,8 +2328,8 @@ This schema accepts one of the following structures:
23272328

23282329
| Property | Type | Required | Description |
23292330
| :--- | :--- | :--- | :--- |
2330-
| **order** | `string[]` | optional | Column order as field names, leftmost first (runtime-only per-user state — written by the console grid, never authored). |
2331-
| **widths** | `Record<string, number>` | optional | Column widths in pixels, keyed by field name (runtime-only per-user state — written by the console grid, never authored). |
2331+
| **order** | `string[]` | optional | Column order as field names, leftmost first (runtime-only state — written by the console grid, never authored). |
2332+
| **widths** | `Record<string, number>` | optional | Column widths in pixels, keyed by field name (runtime-only state — written by the console grid, never authored). |
23322333

23332334
---
23342335

@@ -2356,9 +2357,10 @@ This schema accepts one of the following structures:
23562357
| **_packageId** | `string` | optional | Owning package machine id. |
23572358
| **_packageVersion** | `string` | optional | Owning package version. |
23582359
| **_lockDocsUrl** | `string` | optional | Optional documentation link surfaced next to _lockReason. |
2359-
| **isPinned** | `boolean` | optional | Studio round-trip: view pinned in the switcher (per-user state, written by the console — not authored). |
2360-
| **sortOrder** | `integer` | optional | Studio round-trip: position within the switcher (per-user state, written by the console — not authored). |
2361-
| **columnState** | `{ order?: string[]; widths?: Record<string, number> }` | optional | Studio round-trip: per-user column order/widths (runtime-only state, written by the console grid — not authored) |
2360+
| **isPinned** | `boolean` | optional | Console round-trip: the view is pinned in the object's view switcher. Written by the console's pin toggle through the `view` metadata API and read back to draw the pinned group. Stored on the view's row, which has no per-user scope. Not authored. |
2361+
| **sortOrder** | `integer` | optional | Console round-trip: the view's position among the object's saved views in the switcher, 0-based and counted over saved views only (a code-defined view carries none). Written by the console's drag-reorder and read back to order the tabs. Not authored: `order` is the authored default position. |
2362+
| **visibility** | `Enum<'private' \| 'team' \| 'organization' \| 'public'>` | optional | Console round-trip: the group the switcher files this view's tab under (private, team, organization or public). Display grouping only, NOT access control: nothing restricts who can list or open the view by this value. No console control sets it; the console carries a stored value forward when it re-saves the row. Not authored. |
2363+
| **columnState** | `{ order?: string[]; widths?: Record<string, number> }` | optional | Studio round-trip: column order/widths (runtime-only state, written by the console grid and stored on the view's row, which has no per-user scope — not authored) |
23622364

23632365
### Nested Shape: `ViewItemWire[viewKind='form'].config`
23642366

@@ -2402,8 +2404,8 @@ This schema accepts one of the following structures:
24022404

24032405
| Property | Type | Required | Description |
24042406
| :--- | :--- | :--- | :--- |
2405-
| **order** | `string[]` | optional | Column order as field names, leftmost first (runtime-only per-user state — written by the console grid, never authored). |
2406-
| **widths** | `Record<string, number>` | optional | Column widths in pixels, keyed by field name (runtime-only per-user state — written by the console grid, never authored). |
2407+
| **order** | `string[]` | optional | Column order as field names, leftmost first (runtime-only state — written by the console grid, never authored). |
2408+
| **widths** | `Record<string, number>` | optional | Column widths in pixels, keyed by field name (runtime-only state — written by the console grid, never authored). |
24072409

24082410
---
24092411

‎packages/spec/api-surface/ui.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,7 @@
402402
"UserFilters (type)",
403403
"UserFiltersParsed (type)",
404404
"UserFiltersSchema (const)",
405+
"VIEW_CONSOLE_ROUND_TRIP_KEYS (const)",
405406
"VIEW_CONSOLE_ROW_DECORATIONS (const)",
406407
"VIEW_FILTER_LIST_VALUE_OPERATORS (const)",
407408
"VIEW_FILTER_OPERATORS (const)",

‎packages/spec/export-origins/ui.json‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@
388388
"UserFilters": "src/ui/view.zod.ts#UserFilters (type)",
389389
"UserFiltersParsed": "src/ui/view.zod.ts#UserFiltersParsed (type)",
390390
"UserFiltersSchema": "src/ui/view.zod.ts#UserFiltersSchema (const)",
391+
"VIEW_CONSOLE_ROUND_TRIP_KEYS": "src/ui/view.zod.ts#VIEW_CONSOLE_ROUND_TRIP_KEYS (const)",
391392
"VIEW_CONSOLE_ROW_DECORATIONS": "src/ui/view.zod.ts#VIEW_CONSOLE_ROW_DECORATIONS (const)",
392393
"VIEW_FILTER_LIST_VALUE_OPERATORS": "src/ui/view.zod.ts#VIEW_FILTER_LIST_VALUE_OPERATORS (const)",
393394
"VIEW_FILTER_OPERATORS": "src/ui/view.zod.ts#VIEW_FILTER_OPERATORS (const)",

‎packages/spec/src/system/metadata-form-zod-reconciliation.test.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,21 +274,21 @@ const LEDGER: ReadonlyArray<OmitEntry | SubsetEntry> = [
274274
type: 'view',
275275
path: ROOT_PATH,
276276
key: 'columnState',
277-
why: "platform-written, never authored — `Studio round-trip: per-user column order/widths (runtime-only state, written by the console grid — not authored)`. Declared on the wire members only so the console's own write parses; the authoring door (`ViewItemSchema`) rejects the key by name",
277+
why: "platform-written, never authored — `Studio round-trip: column order/widths (runtime-only state, written by the console grid and stored on the view's row, which has no per-user scope — not authored)`. Declared on the wire members only so the console's own write parses; the authoring door (`ViewItemSchema`) rejects the key by name",
278278
},
279279
{
280280
kind: 'omit',
281281
type: 'view',
282282
path: ROOT_PATH,
283283
key: 'isPinned',
284-
why: "platform-written, never authored — `Studio round-trip: view pinned in the switcher (per-user state, written by the console — not authored)`; the authoring door (`ViewItemSchema`) rejects the key by name",
284+
why: "platform-written, never authored — `Console round-trip: the view is pinned in the object's view switcher. … Not authored.`; the authoring door (`ViewItemSchema`) rejects the key by name",
285285
},
286286
{
287287
kind: 'omit',
288288
type: 'view',
289289
path: ROOT_PATH,
290290
key: 'sortOrder',
291-
why: "platform-written, never authored — `Studio round-trip: position within the switcher (per-user state, written by the console — not authored)`; the authoring door (`ViewItemSchema`) rejects the key by name and points the author at `order`, the authored default",
291+
why: "platform-written, never authored — `Console round-trip: the view's position among the object's saved views in the switcher … Not authored`; the authoring door (`ViewItemSchema`) rejects the key by name and points the author at `order`, the authored default",
292292
},
293293

294294
// Deprecated or legacy alias — deliberately not offered to new authors, the

‎packages/spec/src/ui/view.zod.ts‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5060,11 +5060,11 @@ type ViewItemWireArmShape<K extends 'list' | 'form', C extends z.ZodTypeAny> = {
50605060
*/
50615061
const ViewColumnStateSchema = z.object({
50625062
order: z.array(z.string()).optional()
5063-
.describe('Column order as field names, leftmost first (runtime-only per-user state — written by the console grid, never authored).'),
5063+
.describe('Column order as field names, leftmost first (runtime-only state — written by the console grid, never authored).'),
50645064
widths: z.record(z.string(), z.number()).optional()
5065-
.describe('Column widths in pixels, keyed by field name (runtime-only per-user state — written by the console grid, never authored).'),
5065+
.describe('Column widths in pixels, keyed by field name (runtime-only state — written by the console grid, never authored).'),
50665066
}).describe(
5067-
'Runtime-only personalization overlay key: the per-user column layout (order/widths) the console grid '
5067+
'Runtime-only personalization overlay key: the column layout (order/widths) the console grid '
50685068
+ 'persists through the `view` metadata API. NOT authorable — authoring doors reject it by name; do not write it in metadata source.',
50695069
);
50705070

@@ -5207,7 +5207,7 @@ function viewItemWireFields() {
52075207
// key arrives at THIS member's top level; declaring it validates the
52085208
// shape where `.strip()` used to let it ride through unchecked.
52095209
columnState: ViewColumnStateSchema.optional()
5210-
.describe('Studio round-trip: per-user column order/widths (runtime-only state, written by the console grid — not authored)'),
5210+
.describe('Studio round-trip: column order/widths (runtime-only state, written by the console grid and stored on the view\'s row, which has no per-user scope — not authored)'),
52115211
};
52125212
}
52135213

0 commit comments

Comments
 (0)