Skip to content

Commit 85708bd

Browse files
committed
docs(spec): step-18 entries, exportOptions TSDoc and the stored-shapes row say a page's next save meets the component-props gate at the metadata save door
Since validateComponentProps / validateComponentTypes run on the runtime-publish surface for `page`, the metadata save door judges a page component's `properties`. Three shipped step-18 `reason` strings, the `exportOptions` TSDoc, twenty-one step-18 entry header comments and the stored-shapes table in deployment/cli.mdx still said the save path does not parse it. The load half stays: a stored page still loads. Text only; no accept set moves. Claude-Session: https://claude.ai/code/session_01BYTVP3rWqkXqY8sfHuWGCD Co-authored-by: Claude <noreply@anthropic.com>
1 parent b485122 commit 85708bd

26 files changed

Lines changed: 159 additions & 132 deletions

‎content/docs/deployment/cli.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,7 @@ can produce, because both supply a live one.
14911491
| Rows that still fail the current schema after conversion | That is a genuine contract violation, not chain-owned history. The write path's rejection is correct; fix the row in Studio |
14921492
| A flow whose rename the conflict guard refused | The old node-type token is a live name something else owns here. Rewriting would clobber that owner, so the row fails loudly naming the token — never a silent skip |
14931493
| A site the conversion chain leaves as stored because no lossless rewrite exists — above all a page filter carrying `$and` / `$or` / `$not` | Flattening a combinator changes which rows the page selects, so it is never done. Each site is printed as a `TODO` line under its row — path, block, and what blocks the rewrite — whatever the row's outcome; a row with nothing but TODOs is reported `skipped`. It does not fail the run, since no run of this pass can clear it: rewrite each site by hand |
1494-
| A stored shape protocol 18 refuses and no conversion rewrites — a `controlled_by_parent` object without exactly one required `master_detail` (two masters, none, a required `lookup` only, an explicit `required: false`), a view bound to the retired view-level API data source or a page component whose `data` prop binds it (`object-grid`, `object-map`, `object-gantt`, `object-tree`), a flow value slot carrying a `{…}` token (a `get_record` / `update_record` / `delete_record` filter operand among them) | Each would need a choice only its owner can make — which master, which page, what an absent value should do — so it is never rewritten. Each site is printed as a `TODO` line under its row, naming the step-18 entry that refuses it, the object, view, page component or flow node, and the owner's choices; a binding's request headers are withheld. Until it is rewritten by hand, the row is refused: an object registers but derives no master, a view registers under `[metadata_spec_invalid]`, a page still loads and saves while `os validate`, `os build` and `os lint` refuse the binding in source, and a flow is not registered |
1494+
| A stored shape protocol 18 refuses and no conversion rewrites — a `controlled_by_parent` object without exactly one required `master_detail` (two masters, none, a required `lookup` only, an explicit `required: false`), a view bound to the retired view-level API data source or a page component whose `data` prop binds it (`object-grid`, `object-map`, `object-gantt`, `object-tree`), a flow value slot carrying a `{…}` token (a `get_record` / `update_record` / `delete_record` filter operand among them) | Each would need a choice only its owner can make — which master, which page, what an absent value should do — so it is never rewritten. Each site is printed as a `TODO` line under its row, naming the step-18 entry that refuses it, the object, view, page component or flow node, and the owner's choices; a binding's request headers are withheld. Until it is rewritten by hand, the row is refused: an object registers but derives no master, a view registers under `[metadata_spec_invalid]`, a page still loads, its next save is refused at the metadata save door (422), and `os validate`, `os build` and `os lint` refuse the binding in source, and a flow is not registered |
14951495
| Any other site `registerFlow` refuses in a stored flow the parse accepts — a text slot's template hole, a decision's `mode`, a malformed predicate, a plugin node's undeclared config key, an `api` flow with no secret | The flow's own definition is wrong, and no conversion rewrites it. Each site is printed as a `TODO registerFlow` line under its row, in the words `registerFlow` refuses it with; a site already listed under a step-18 entry is not repeated. Until it is fixed, the flow is not registered at boot and never runs. A flow the parse itself refuses is reported among the rows that could not be rewritten, in the parse's words |
14961496
14971497
**One thing it lists and never writes: decision nodes that changed meaning at

‎packages/spec/src/migrations/entries/semantic/18.element-text-variant-heading-subheading-retired.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,11 @@ export const entry: SemanticMigration = {
3232
+ 'and `subheading` in a medium-weight small heading style, and `h2` / `h3` draw their own, larger '
3333
+ 'styles. Whether the page wanted that level is the author\'s call — a heading placed for its '
3434
+ 'size rather than its place in the outline may want a deeper level. Nothing is dropped at rest: '
35-
+ 'a stored page replays the rewrite at rehydration; a page component\'s `properties` is not '
36-
+ 'parsed on the save path, and the component-props gate reports an old spelling as a '
35+
+ 'a stored page replays the rewrite at rehydration and still loads, because a page component\'s '
36+
+ '`properties` is not parsed on the metadata load path, and the next save of the page meets the '
37+
+ 'component-props gate at the metadata save door. The gate reports an old spelling as a '
3738
+ '`component-props-invalid` error, carrying the prescription, on `os validate`, `os build` and '
38-
+ '`os lint`. ADR-0087',
39+
+ '`os lint` and at that save door. ADR-0087',
3940
acceptanceCriteria:
4041
'No `element:text` page component carries `variant` `heading` or `subheading`; `os validate` '
4142
+ 'reports no `component-props-invalid` finding under `properties.variant` for these blocks. For '

‎packages/spec/src/migrations/entries/semantic/18.ui-action-group-menu-member-params-array-only.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,10 @@ import type { SemanticMigration } from '../../types.js';
1010
// key, and #21704's fork 5 A refused the member's `properties.params`, so a
1111
// member has no static-values spelling and the container drops a non-array
1212
// `params` on any other type at run time. D3 only: page-component `properties`
13-
// is not parsed on the metadata save or load path, so a stored page is never
14-
// refused at load; there is no D2 conversion, because the only home for static values
15-
// is a different node (an `action:button`), which no rewrite can build in the
13+
// is not parsed on the metadata load path, so a stored page is never refused at
14+
// load (its next save meets the component-props gate at the metadata save
15+
// door); there is no D2 conversion, because the only home for static values is
16+
// a different node (an `action:button`), which no rewrite can build in the
1617
// author's place; and the census found no writer to respell.
1718
export const entry: SemanticMigration = {
1819
id: 'ui-action-group-menu-member-params-array-only',

‎packages/spec/src/migrations/entries/semantic/18.ui-action-group-menu-members-typed.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@ import type { SemanticMigration } from '../../types.js';
99
// with the rows' prescriptions; `outcomeMessages`, a member `className` and
1010
// `properties.params` refused; `outcomeMessages` undeclared on all four action
1111
// blocks. D3 only: page-component `properties` is not parsed on the metadata
12-
// save or load path, so a stored page is never refused at load; and the authored census
12+
// load path, so a stored page is never refused at load (its next save meets the
13+
// component-props gate at the metadata save door); and the authored census
1314
// found no working member to respell — the refused values are objectui's probes
14-
// of the very reads the ruling refuses (a member `className`, `outcomeMessages`,
15-
// `properties.params`) and of the host's `autoTrigger` flag.
15+
// of the very reads the ruling refuses (a member `className`,
16+
// `outcomeMessages`, `properties.params`) and of the host's `autoTrigger` flag.
1617
export const entry: SemanticMigration = {
1718
id: 'ui-action-group-menu-members-typed',
1819
surface: 'page `action:group` and `action:menu` components — each member of `properties.actions` (whose keys '

‎packages/spec/src/migrations/entries/semantic/18.ui-object-block-grouping-config-typed.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,10 @@ export const entry: SemanticMigration = {
4242
+ 'unpadded name silently equivalent, which is the consumer tolerance the contract refuses, '
4343
+ 'and a bare string, a number or an empty list has no mapping that says what grouping was '
4444
+ 'meant. Metadata AT REST is left exactly as stored — `properties` on a page component is '
45-
+ 'not parsed on the save path, so a stored page keeps loading and renders as it does today; '
46-
+ 'the component-props gate reports such a value as a `component-props-invalid` '
47-
+ 'error at the offending path on `os validate`, `os build` and `os lint`, a padded name '
45+
+ 'not parsed on the metadata load path, so a stored page keeps loading and renders as it does '
46+
+ 'today, and the next save of the page meets the component-props gate at the metadata save '
47+
+ 'door. The gate reports such a value as a `component-props-invalid` error at the offending '
48+
+ 'path on `os validate`, `os build` and `os lint` and at that save door, a padded name '
4849
+ 'with the received value and the unpadded name to write. ADR-0049 / ADR-0087.',
4950
acceptanceCriteria:
5051
'Every `object-grid` and `object-kanban` node in your pages either omits `grouping` or '

‎packages/spec/src/migrations/entries/semantic/18.ui-object-form-custom-fields-typed.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@ import type { SemanticMigration } from '../../types.js';
88
// snake_case. The maintainer ruled on #21704 (fork 2, letter B) that the spec
99
// declares a closed runtime form field of the members the form draws, in
1010
// camelCase, keyed by `name`. D3 only: page-component `properties` is not
11-
// parsed on the metadata save or load path, so a stored page is never refused at load;
12-
// and the authored census, with every inline option list evaluated, found no
11+
// parsed on the metadata load path, so a stored page is never refused at load
12+
// (its next save meets the component-props gate at the metadata save door); and
13+
// the authored census, with every inline option list evaluated, found no
1314
// working member to respell — the refused values are a type-level test's
1415
// `visibleOn` and a fixture pinning that an inline `defaultValue` seeds
1516
// nothing. The shipped object-manager dialog's options (`{ label: 'Box', value:

‎packages/spec/src/migrations/entries/semantic/18.ui-object-form-fields-names-typed.ts‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,13 @@ import type { SemanticMigration } from '../../types.js';
77
// while the form drew a `{ name }` field entry its own page-builder guide
88
// taught. objectstack-ai/objectui#11550 retired that entry from every authoring
99
// face (the form still draws a STORED one, by its name, as tolerance), so both
10-
// rows now take field names — objectui's own declaration of the member — and
11-
// an object entry is refused with what to write instead. D3 only: page-
12-
// component `properties` is not parsed on the metadata save or load path, so a
13-
// stored page is never refused; and the authored census found no authored
14-
// value to respell — the refused values are fixtures probing the stored read,
15-
// the console warning and objectui's own refusal.
10+
// rows now take field names — objectui's own declaration of the member — and an
11+
// object entry is refused with what to write instead. D3 only: page-component
12+
// `properties` is not parsed on the metadata load path, so a stored page is
13+
// never refused at load (its next save meets the component-props gate at the
14+
// metadata save door); and the authored census found no authored value to
15+
// respell — the refused values are fixtures probing the stored read, the
16+
// console warning and objectui's own refusal.
1617
export const entry: SemanticMigration = {
1718
id: 'ui-object-form-fields-names-typed',
1819
surface: 'page `object-form` and `object-master-detail-form` components — `properties.fields` (whose '

‎packages/spec/src/migrations/entries/semantic/18.ui-object-form-members-typed.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ import type { SemanticMigration } from '../../types.js';
1111
// `z.unknown()` (the form draws a `{ name }` field entry and an inline runtime
1212
// field inside a section, which the typed shapes would refuse), and
1313
// `customFields` waits for the spec to declare objectui's runtime form field.
14-
// D3 only: page-component `properties` is not parsed on the metadata save or
15-
// load path, so a stored page is never refused at load; an off-shape value has no
14+
// D3 only: page-component `properties` is not parsed on the metadata load path,
15+
// so a stored page is never refused at load (its next save meets the
16+
// component-props gate at the metadata save door); an off-shape value has no
1617
// rewrite that says what the author meant; and the authored census found no
1718
// authored value to respell — the refused values are fixtures probing that the
1819
// form refuses them.

‎packages/spec/src/migrations/entries/semantic/18.ui-object-form-sections-typed.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ import type { SemanticMigration } from '../../types.js';
99
// 3, letter B) a page-block section shape of its own — the form view's section
1010
// keys plus the three entry arms the form reads, canonical spellings only — and
1111
// the stored form view is unchanged. D3 only: page-component `properties` is
12-
// not parsed on the metadata save or load path, so a stored page is never
13-
// refused at load; and the authored census, with every inline option list evaluated,
12+
// not parsed on the metadata load path, so a stored page is never refused at
13+
// load (its next save meets the component-props gate at the metadata save
14+
// door); and the authored census, with every inline option list evaluated,
1415
// found no working section to respell — the refused values are the two probes
1516
// in objectui's `formSectionGroupReference-7051.test.tsx` (a section declaring
1617
// neither `fields` nor `group`, and a group-owned `label` / `collapsible`

‎packages/spec/src/migrations/entries/semantic/18.ui-object-gantt-markers-typed.ts‎

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,18 @@
22

33
import type { SemanticMigration } from '../../types.js';
44

5-
// #21464 — the `object-gantt` page block's `markers` was `z.array(z.unknown())`:
6-
// its element contract lived only in objectui, so a marker with no `date`, a
7-
// numeric `date` or a misspelled member passed the component-props gate, and
8-
// the chart drew no line, or drew it unlabelled and in the default colour. The
9-
// spec now declares objectui's own authoring declaration of a marker,
10-
// `{ date, label?, color? }` with `date` a string, and the row takes it. D3
11-
// only: page-component `properties` is not parsed on the metadata save or load
12-
// path, so a stored page is never refused at load; a misspelled member has no rewrite
13-
// that says which of the three the author meant; and the authored census found
14-
// no authored value to respell — the one refused value is objectui's own
15-
// compile-time refusal probe.
5+
// #21464 — the `object-gantt` page block's `markers` was
6+
// `z.array(z.unknown())`: its element contract lived only in objectui, so a
7+
// marker with no `date`, a numeric `date` or a misspelled member passed the
8+
// component-props gate, and the chart drew no line, or drew it unlabelled and
9+
// in the default colour. The spec now declares objectui's own authoring
10+
// declaration of a marker, `{ date, label?, color? }` with `date` a string, and
11+
// the row takes it. D3 only: page-component `properties` is not parsed on the
12+
// metadata load path, so a stored page is never refused at load (its next save
13+
// meets the component-props gate at the metadata save door); a misspelled
14+
// member has no rewrite that says which of the three the author meant; and the
15+
// authored census found no authored value to respell — the one refused value is
16+
// objectui's own compile-time refusal probe.
1617
export const entry: SemanticMigration = {
1718
id: 'ui-object-gantt-markers-typed',
1819
surface: 'page `object-gantt` components — `properties.markers` (whose entries used to accept any value)',

0 commit comments

Comments
 (0)