Skip to content

Commit 14e9685

Browse files
feat(spec,app-shell)!: a form view's type is its layout only — simple, tabbed or wizard; split, drawer and modal are retired with pane, splitSize and splitResizable (#27229)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} --> _Requested by **Yibo Wang** · [project thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBVBQa5cSAGPWu1xigsGp9it)_ Fixes #27155 Ruling-ref: 6464210455 Refs #27153 Clause-②: no (narrowing) Metadata-protocol: narrowed Skills-docs: follow-up owed — #27235 (hotcrm src/sales/views/lead.view.ts) ## 维护者速读 - 改了什么:表单视图的 `type` 只表示版式,可选 `simple`(默认)、`tabbed`、`wizard`。`split`、`drawer`、`modal` 退役,写了会被拒并附改法。只服务分栏表单的 `pane`、`splitSize`、`splitResizable` 一起退役。Studio 新建视图不再提供这三个值。 - 为什么改:落实 ADR-0050 裁决 A′。表单在哪里打开(抽屉或弹窗)由打开它的一方决定,不是表单自己声明;分栏已经在 #26804 变成用户个人偏好。 - 风险与回滚:这是收窄,旧元数据里的这三个值和三个键会被拒。已存的 `sys_metadata` 行在读取时自动转换,`os migrate meta --from 17` 改写源码。本仓只有 showcase 用到,已改写;hotcrm 的 `lead.view.ts` 由 hotcrm 席位跟进。回滚就是 revert 本 PR。 - 先看:① `packages/spec/src/ui/view.zod.ts`,`FORM_VIEW_TYPE_RETIRED` 的三条改法文案和 `type` 枚举;② `packages/spec/src/conversions/registry.ts`,`formViewPresentationTypesRemoved` 的 `apply`;③ `ui/packages/app-shell/src/views/metadata-admin/package-schema.ts`,`modal` 改为 `simple`。 - 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run --project repo src/ui/form-view-type-layout-retirement.test.ts`,预期 31 个测试全部通过。 - 需要你做的:无,席位落地。 ## What changes ### Spec (`@objectstack/spec`) - `FormViewSchema.type` is `simple | tabbed | wizard`, still defaulting to `simple`. It is built with `enumWithRetiredValues`, so `split`, `drawer` and `modal` are refused by name, each with its own prescription. The prescriptions name live replacements only: - `split` → `tabbed` or the `simple` default, with `subforms` for editing child records beside the parent. Opening a record beside its list is the reader's Display-menu preference. - `drawer` / `modal` → the caller's open mode: the list's `navigation.mode`, the console's New/Edit dialogs (sized by `modalSize`), or an action `type: 'modal'` opening a page. - The split-only keys `FormSectionSchema.pane` (and the legacy `groups[].pane`), `FormViewSchema.splitSize` and `FormViewSchema.splitResizable` are `retiredKey()` tombstones, refused on every layout. The superRefine check that refused `pane` off a split form is gone, because the tombstone refuses it everywhere. - **Kept, as a stated boundary:** `splitDirection`, `drawerSide`, `drawerWidth` and `modalSize`. See Acceptance notes. - ADR-0087 kit (`spec-property-retirement`, value-level route plus key tombstones): - D2 conversion `form-view-presentation-types-removed` (`retiredFromLoadPath: true`, `retiredAfter: '18.2.0'`, MAJOR_18 order 78). It deletes a retired `type`, so the form lands on `simple`, and strips `splitSize`, `splitResizable` and every section's or group's `pane`. It covers a view container's `form` and `formViews.*`, a form view item's `config`, and a flattened form overlay, both in stored rows and in existing sources. Its fixture has 9 notices. - D3 semantic entry `ui-form-view-type-layout-only` (its `conversionIds` names the conversion), plus a STEP18_RATIONALE fragment. - Three `RETIRED_KEYS_BY_MAJOR` rows: `ui/FormSection:pane`, `ui/FormView:splitSize`, `ui/FormView:splitResizable`. - Liveness (`liveness/view.json`): `splitSize` and `splitResizable` are `dead` with REMOVED notes; the `type` row is noted NARROWED; `splitDirection`, `drawerSide` and `drawerWidth` are noted inert on a form view; the `sections` note records the `pane` removal. - Removal-version pin: one JOIN row (three value refusals plus three tombstones). - Regenerated: `authorable-surface/ui.json` (three `[RETIRED]` rows), `content/docs/references/**`, `liveness/state-counts/view.md`. `api-surface`, `react-blocks.md` and `skills/**` are byte-identical (`check:react-blocks` and `check:skill-refs` are in sync). - Docs: `content/docs/ui/views.mdx` drops the three rows from the Form Types table and adds a paragraph on where a form opens. `protocol/objectui/layout-dsl.mdx` drops the `pane` mention. ### Showcase (`examples/app-showcase`, private) - In `task.view.ts`, `formViews.split` is now `type: 'tabbed'` with its panes removed, and `formViews.quick` is now `type: 'simple'`. The default list declares `navigation: { mode: 'drawer' }` to show the open mode where it now lives. - `coverage.ts` has `FORM_VIEW_TYPES = ['simple', 'tabbed', 'wizard']`. Comments are updated in `translations/index.ts` and `new-project-wizard.page.ts`. - The lint package's frozen showcase snapshot (`packages/lint/src/showcase-shape.fixtures.ts`, test-only, not shipped) follows, with a header note on the protocol-forced edit. ### Studio (`@object-ui/app-shell`): a defect this PR would otherwise create The precedent is #26804, which fixed its own Studio inspector in the same PR. Claim amendments 2 and 3 cover this. - `metadata-admin/anchors.ts`: the New View form's `formType` picker offers `simple | tabbed | wizard`. It offered six values, and the three retired ones would now build a body the server refuses at save. - `metadata-admin/default-schemas.ts`: the view `type` enum drops the same three values. - `metadata-admin/package-schema.ts`: `getPackageForm()` returns `type: 'simple'`. It returned `type: 'modal'`, a spec-typed literal that fails `tsc` (TS2322) after the narrowing. `SectionedSchemaForm` reads only `tabbed`, so the render is unchanged. `PackageFormDialog` is what opens it as a dialog. - ui test pins whose assertion the narrowing made false are re-pinned to the refusal, never deleted: - `view-create-body.test.ts`: the three layouts are accepted; a new case shows each retired type refused at `config.type`. - `formSectionGroupReference-7051.test.tsx`: `pane` beside `group` and the three retired types are refused. - `form-spec.containers.test.tsx` and `FormPage.viewSpec.test.ts`: `pane` leaves the "was undeclarable" literals, and a `@ts-expect-error` pins it as refused. ## Why ADR-0050 was Accepted by ruling 6464210455 on #27032 (letter A′, maintainer 「其他同意」). `type` is the form's layout. `drawer` and `modal` described where a form is opened, which the caller already models. `split` was a list-beside-record view mode, not a layout. The ruling mapped `split` to the list's `navigation.mode: 'split'`, but #26804 (17b5152) retired that the day before and made the side-by-side pane a reader's preference. Following the seat's ruling, the prescriptions apply the ruling's intent to the current tree and name live values only. ## Tests - New pin `packages/spec/src/ui/form-view-type-layout-retirement.test.ts` (31 tests, `repo` project, added to `vitest.repo-tests.json`): - five doors refuse each retired type with `invalid_value` at `type`, plus the prescription's first sentence and migrate sentence. The doors are `FormViewSchema`, `view.form`, `view.formViews.*`, a ViewItem form record and the flattened form overlay; - the five doors accept all three layouts; - a misspelt value keeps zod's own message; - `defineStack` refuses with `STACK_SCHEMA_INVALID` / 422 at `views.0.formViews.quick.type`; - `pane` is refused on every layout, in `sections` and in `groups`; `splitSize` and `splitResizable` are refused too; - the kept keys still parse; - `@ts-expect-error` checks at the authoring site; - the conversion over a stored view container and a stored form view item, its idempotence, its absence from the load path, its step-18 wiring and its D3 entry; - a tree-scoped absence walk with an anti-vacuity matcher test. As a probe, I put a copy of the pre-change `task.view.ts` in the tree; the walk named it, and I removed the copy. - Updated spec tests: - `view.test.ts`: the `pane` block now pins the tombstone; - `view-form-features-root.test.ts`: tabbed/wizard replace split; - `section-group-reference.test.ts`; - `view-union-diagnostics.test.ts`: three form types, plus a new case where a retired type settles nothing on its own and `viewKind` still does; - `view-metadata-schema.test.ts`: reads the vocabulary through the kept `splitDirection`; - `retired-key-removal-version.pin.test.ts`: JOIN row. - `objectstack build` of the showcase with `formViews.quick` changed to `type: 'modal'` (a temporary edit, restored with a trap) exits 2. The refusal sits at `formViews.quick.type` and names `os migrate meta --from 17`. The unmodified showcase builds with exit 0. ## Acceptance notes - **Kept keys.** `splitDirection`, `drawerSide` and `drawerWidth` stay declared because the React `ObjectForm` block contract (`react-blocks.ts`) takes its `dataProps` from `FormViewSchema`. Tombstoning them would regenerate `skills/objectstack-ui/references/react-blocks.md` and make this PR Tier H. They are #27159's, together with the renderer branches (`ObjectForm.tsx`, `ModalForm.tsx`, `SplitForm`), the `object-form` `formType` in `component.zod.ts`, and the block-contract keys. `modalSize` stays because the console's record dialog reads it on every layout. - **Survey of stored and authored metadata for the retired values, measured on this tree at base:** - 2 form views authored a retired type, both in `examples/app-showcase/src/ui/views/task.view.ts`: `formViews.split` (`split`) and `formViews.quick` (`drawer`). The lint package's frozen copy of that file repeats them. - 2 sections authored `pane`, in the same `split` form view. - 0 `modal` form views and 0 `splitSize` / `splitResizable` authorings. - 0 JSON fixtures or stored-row seeds. - 1 spec-typed Studio literal (`package-schema.ts`, `modal`) and 2 Studio enum lists offering the three values. - hotcrm was not reachable from this session. The card's reading is 3 form views in `src/sales/views/lead.view.ts` (L747, L796, L839 at hotcrm 6c52f22), owed to the hotcrm seat per the Skills-docs line, filed as #27235. - `selectViewMetadataBranch` reads its form vocabulary off `FormViewSchema.type`, so a bare `{ type: 'modal' }` body now settles no branch, as an unknown type does. With `viewKind: 'form'` it still reaches the form overlay, whose parse names the prescription. This is diagnostic routing only, and it is pinned. - No `skills/**` edit, so the PR is not Tier H. `docs/adr/**` is untouched. ## Verification Head: `edd4570f5`. The final-head gate union and full dogfood ran on `cd275780d`. The last commit (`edd4570f5`) changes two lines in one ui test file, and the ratchet families plus plugin-form `type-check` were re-run on it (listed under Derived gates). <details><summary>Type-checks and build</summary> Each was run separately, with its exit code read before any pipe: - `pnpm build --concurrency=2` (whole workspace, under os-verify-lock): `VERDICT command-exit 0`, 116/116 tasks. The first two runs exposed the `package-schema.ts` TS2322 and the console test literals; both are fixed in this PR. - `pnpm --filter @objectstack/spec typecheck` (tsc, scripts and the test layer): exit 0. `check:test-typecheck` is OK, with 132 pinned signatures held. - `type-check` for `@object-ui/types`, `@object-ui/plugin-form`, `@object-ui/app-shell` (including `tsconfig.test.json`) and `@object-ui/console`: exit 0 each. plugin-form was re-run on `edd4570f5`: exit 0. - `pnpm --filter @objectstack/example-showcase typecheck`: exit 0. - `pnpm --filter @objectstack/example-showcase build`: exit 0. With `formViews.quick` changed to `type: 'modal'`: exit 2, with an `invalid_value` at `formViews.quick.type` whose message is the prescription, ending in the `os migrate meta --from 17` sentence. - `check:react-blocks` (1 generated file in sync) and `check:skill-refs` (9 generated files in sync): `react-blocks.md` and `skills/**` are unchanged. </details> <details><summary>Tests</summary> - `@objectstack/spec` `pnpm test` (local project): 740 files, 22234 passed and 1 failed. The failure was `view-metadata-schema.test.ts`, which read the vocabulary through `splitSize`; I re-pointed it to the kept `splitDirection`, and the re-run passed 50/50. - `@objectstack/spec` `pnpm test:repo`: 82 files, 1355 tests passed. - The new pin, on its own: 31/31. With a copy of the pre-change `task.view.ts` placed in the tree, the absence walk names it. - `@objectstack/lint` `validate-translation-references` and `validate-translatable-sections`: 2 files, 252 tests passed. - `@objectstack/example-showcase` `test`: 41 files, 505 tests passed. - ui, from `ui/`: every test file that reads a spec view or form schema (371 files, chosen by grep for `FormViewSchema|FormSectionSchema|ViewItemSchema|ViewSchema|ViewMetadataSchema|defineStack|FormView|FormSection`). 370 passed and 1 skipped; 7978 tests passed and 9 skipped; `VERDICT command-exit 0`. - `pnpm --dir ui exec vitest run scripts/`: 175 files passed and 2 skipped; 5019 tests passed. </details> <details><summary>Derived gates</summary> - `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` derived 112 commands on `cd275780d`. Each ran with `eval "$cmd" > log 2>&1; status=$?`, and all 112 recorded `exit 0`. - `dispatch-gates --ran`: `✓ 112 derived famil(ies) accounted for — 112 run, 0 NOT-MEASURED (a DERIVED zero — all 112 recorded an exit code and none of them is 3)`. - An earlier run, before the full build, had 11 gates exit 3 with PREREQUISITE NOT MET because they read built output. After `pnpm build` all 11 exited 0, and the final union above includes them. - Re-run on `edd4570f5`, all exit 0: `check:type-check-debt`, `check:type-check-coverage`, `check:nul-bytes`, `check:test-source-alias`, `check:undeclared-dep-imports`, `check-comment-mask-adoption.mjs` and `check:issue-citations`. - eslint on the changed files: root exit 0. ui exit 0, with warnings only; `formSectionGroupReference-7051.test.tsx` carries the same 18 warnings as at base. </details> <details><summary>Full dogfood</summary> - `pnpm --filter @objectstack/dogfood test` under os-verify-lock, on `cd275780d`, after a whole-workspace build: `VERDICT command-exit 0`. - 351 files: 348 passed and 3 skipped. 2824 tests: 2804 passed and 20 skipped. No failures. </details> ## Human Minimum - [x] 1. Schemas start as Zod; `packages/spec` holds no runtime logic. - [x] 2. Every name I added uses the casing for its kind. - [x] 3. No formatter ran; `pnpm lint` passes. - [x] 4. One issue, one branch off an up-to-date `main`, one pull request. - [x] 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset. - [x] 6. `packages/spec` touched: its generated artifacts are regenerated and committed. - [x] 7. No hand edits to `content/docs/references/`, `packages/*/CHANGELOG.md` or `content/docs/releases/`. - [x] 8. Title, body and comments are in English. - [x] The *Before Submitting* list in CONTRIBUTING.md is done. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01WGufziVeBj5ENSWqsDTaYe --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent d745bb5 commit 14e9685

36 files changed

Lines changed: 1072 additions & 177 deletions
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
---
2+
'@objectstack/spec': minor
3+
'@object-ui/app-shell': minor
4+
---
5+
6+
feat(spec)!: a form view's `type` is its layout only — `simple` (the default), `tabbed` or `wizard`; `split`, `drawer` and `modal` are retired, and the split-only `sections[].pane`, `splitSize` and `splitResizable` keys go with them
7+
8+
Clause-②: no (narrowing)
9+
10+
Metadata-protocol: narrowed (`FormViewSchema.type` refuses `split`, `drawer` and `modal`; `FormSectionSchema.pane`, `FormViewSchema.splitSize` and `FormViewSchema.splitResizable` are tombstones)
11+
12+
<!-- adr-0087: registered form-view-presentation-types-removed, ui-form-view-type-layout-only -->
13+
14+
**BREAKING** accept-set narrowing on the form view surface (`@objectstack/spec`), ADR-0050.
15+
16+
The model: `type` says how the form lays out its sections. Where a form opens is decided by whoever opens it, so `drawer` and `modal` were never the form's to declare, and a side-by-side split is a reader's preference since `navigation.mode` dropped `split`.
17+
18+
- **FROM → TO.** `type: 'split'` → `type: 'tabbed'` (one tab per section) or delete the value for `simple`; delete each section's `pane` and the form's `splitSize` / `splitResizable`; declare `subforms` to edit child records beside the parent. `type: 'drawer'` or `type: 'modal'` → delete the value for `simple`, or write the layout you want. A list opens a clicked record as its `navigation.mode` says (the drawer by default), and the console's New and Edit entries open the record form themselves, sized by the form's `modalSize`.
19+
- **The one-line fix.** Delete the retired value and the three split-only keys. `os migrate meta --from 17` lists the edits for existing sources, and `--write` applies them. A stored `sys_metadata` row is converted when it is read (the `form-view-presentation-types-removed` conversion), so a form view saved with one of the three values keeps loading as a `simple` form.
20+
- **What the parse says.** Each retired value is refused by name with its prescription, for example "'modal' was removed from the form view `type` enum in @objectstack/spec 18.3.0 (ADR-0050) — …", at every form view door (a view's `form` and `formViews.*`, a form view item record, a flattened form overlay) and at `defineStack` as `STACK_SCHEMA_INVALID` / 422. `pane`, `splitSize` and `splitResizable` are refused on every layout. TypeScript rejects each at the authoring site. A misspelt value keeps zod's own message.
21+
- **Unchanged.** `splitDirection`, `drawerSide`, `drawerWidth` and `modalSize` stay declared: the console reads `modalSize` for its record dialog, and the React `ObjectForm` block contract still takes the other three from this schema until its renderer follows.
22+
23+
The console (`@object-ui/app-shell`): Studio's New View form offers the three form layouts only. It offered all six, and a form view created with `split`, `drawer` or `modal` would now be refused when saved.

‎content/docs/protocol/objectui/index.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ import { defineView } from '@objectstack/spec/ui';
231231

232232
defineView({
233233
form: {
234-
type: 'tabbed', // or 'simple' | 'wizard' | 'split' | 'drawer' | 'modal'
234+
type: 'tabbed', // or 'simple' | 'wizard' — the layout; where it opens is the caller's choice
235235
data: { provider: 'object', object: 'project' },
236236
// In tabbed forms each section renders as its own tab.
237237
sections: [

‎content/docs/protocol/objectui/layout-dsl.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ sections:
560560

561561
The tab *is* the section, so a section's keys are the whole surface: `name`,
562562
`label`, `description`, `fields`, `columns`, `collapsible`, `collapsed`,
563-
`visibleWhen` — plus `pane`, which split forms alone accept. There is **no**
563+
`visibleWhen`. There is **no**
564564
per-tab `lazy`, `source`, `badge` or `badgeVariant` key, and no
565565
`layout: { mode: tabbed, ... }` wrapper: `FormViewSchema.layout` is a string
566566
enum (`vertical` / `horizontal` — multi-column is the separate `columns` key,

‎content/docs/references/api/protocol.mdx‎

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1828,9 +1828,9 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
18281828
| **label** | `string \| Record<string, string>` | optional | Human-readable label shown in metadata lists. |
18291829
| **object** | `string` | optional | Object this container binds to — how a stack-level `views: [...]` entry says which object its views belong to; read by `getViewsByObject()` / `GET /meta/view?object=`. |
18301830
| **list** | `{ name?: string; label?: string \| Record<string, string>; type?: Enum<'grid' \| 'kanban' \| 'gallery' \| 'calendar' \| 'timeline' \| 'gantt' \| 'map' \| 'chart' \| 'tree'>; data?: object \| object \| object; … }` | optional | |
1831-
| **form** | `{ type?: Enum<'simple' \| 'tabbed' \| 'wizard' \| 'split' \| 'drawer' \| 'modal'>; layout?: Enum<'vertical' \| 'horizontal'>; columns?: integer; title?: string; … }` | optional | |
1831+
| **form** | `{ type?: Enum<'simple' \| 'tabbed' \| 'wizard'>; layout?: Enum<'vertical' \| 'horizontal'>; columns?: integer; title?: string; … }` | optional | |
18321832
| **listViews** | `Record<string, { name?: string; label?: string \| Record<string, string>; type?: Enum<'grid' \| 'kanban' \| 'gallery' \| 'calendar' \| 'timeline' \| 'gantt' \| 'map' \| 'chart' \| 'tree'>; data?: object \| object \| object; … }>` | optional | Additional named list views (views mode — dropdown userFilters allowed, no tabs; ADR-0047) |
1833-
| **formViews** | `Record<string, { type?: Enum<'simple' \| 'tabbed' \| 'wizard' \| 'split' \| 'drawer' \| 'modal'>; layout?: Enum<'vertical' \| 'horizontal'>; columns?: integer; title?: string; … }>` | optional | Additional named form views |
1833+
| **formViews** | `Record<string, { type?: Enum<'simple' \| 'tabbed' \| 'wizard'>; layout?: Enum<'vertical' \| 'horizontal'>; columns?: integer; title?: string; … }>` | optional | Additional named form views |
18341834
| **protection** | `{ lock: Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>; reason: string; docsUrl?: string }` | optional | Package author protection block — lock policy for this view. |
18351835
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
18361836
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |
@@ -1895,7 +1895,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
18951895

18961896
| Property | Type | Required | Description |
18971897
| :--- | :--- | :--- | :--- |
1898-
| **type** | `Enum<'simple' \| 'tabbed' \| 'wizard' \| 'split' \| 'drawer' \| 'modal'>` | optional (default: `"simple"`) | |
1898+
| **type** | `Enum<'simple' \| 'tabbed' \| 'wizard'>` | optional (default: `"simple"`) | Form layout: 'simple' (stacked sections, the default), 'tabbed' (one tab per section) or 'wizard' (one gated step per section). Where the form opens is the caller's choice, not the form's |
18991899
| **layout** | `Enum<'vertical' \| 'horizontal'>` | optional | Field layout direction — 'vertical' (the renderer default) or 'horizontal'. Multi-column is not a layout value: set `columns` |
19001900
| **columns** | `integer` | optional | Number of columns for the form body |
19011901
| **title** | `string` | optional | Form title |
@@ -1905,8 +1905,8 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
19051905
| **allowSkip** | `boolean` | optional | Wizard step-gate opt-out: allow entering a later step without submitting the one before it. Navigation freedom, NOT a validation exemption — the final submit still re-validates every step and returns to the first failing one. Default (absent/false): steps unlock in array order as each prior step submits validly. UI admission only, never authorization. |
19061906
| **showStepIndicator** | `boolean` | optional | Show the wizard step indicator (renderer default: shown). Step progress (completed/current/upcoming/invalid) is derived from the step gate — this boolean is the only authorable progress surface. |
19071907
| **splitDirection** | `Enum<'horizontal' \| 'vertical'>` | optional | Split orientation (split forms) |
1908-
| **splitSize** | `number` | optional | Primary split panel size, % (split forms) |
1909-
| **splitResizable** | `boolean` | optional | Whether the split is resizable (split forms) |
1908+
| **splitSize** | `never` | optional | [REMOVED] `view.form.splitSize` was removed in @objectstack/spec 18.3.0 (ADR-0050) — it sized the primary pane of a `type: 'split'` form, and `split` left the form view `type` in the same release, so no form view draws panes. Delete the key. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
1909+
| **splitResizable** | `never` | optional | [REMOVED] `view.form.splitResizable` was removed in @objectstack/spec 18.3.0 (ADR-0050) — it made the panes of a `type: 'split'` form resizable, and `split` left the form view `type` in the same release, so no form view draws panes. Delete the key. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
19101910
| **drawerSide** | `Enum<'top' \| 'bottom' \| 'left' \| 'right'>` | optional | Drawer side (drawer forms) |
19111911
| **drawerWidth** | `string` | optional | [DEPRECATED → size buckets] Drawer width, e.g. "480px". A pixel width cannot be chosen without knowing the client viewport — the renderer derives it. |
19121912
| **modalSize** | `Enum<'sm' \| 'default' \| 'lg' \| 'xl' \| 'full'>` | optional | Modal size (modal forms) |
@@ -1974,7 +1974,7 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
19741974

19751975
| Property | Type | Required | Description |
19761976
| :--- | :--- | :--- | :--- |
1977-
| **type** | `Enum<'simple' \| 'tabbed' \| 'wizard' \| 'split' \| 'drawer' \| 'modal'>` | optional (default: `"simple"`) | |
1977+
| **type** | `Enum<'simple' \| 'tabbed' \| 'wizard'>` | optional (default: `"simple"`) | Form layout: 'simple' (stacked sections, the default), 'tabbed' (one tab per section) or 'wizard' (one gated step per section). Where the form opens is the caller's choice, not the form's |
19781978
| **layout** | `Enum<'vertical' \| 'horizontal'>` | optional | Field layout direction — 'vertical' (the renderer default) or 'horizontal'. Multi-column is not a layout value: set `columns` |
19791979
| **columns** | `integer` | optional | Number of columns for the form body |
19801980
| **title** | `string` | optional | Form title |
@@ -1984,8 +1984,8 @@ The published metadata item body, opaque by ruling (1C). Shape is the item's own
19841984
| **allowSkip** | `boolean` | optional | Wizard step-gate opt-out: allow entering a later step without submitting the one before it. Navigation freedom, NOT a validation exemption — the final submit still re-validates every step and returns to the first failing one. Default (absent/false): steps unlock in array order as each prior step submits validly. UI admission only, never authorization. |
19851985
| **showStepIndicator** | `boolean` | optional | Show the wizard step indicator (renderer default: shown). Step progress (completed/current/upcoming/invalid) is derived from the step gate — this boolean is the only authorable progress surface. |
19861986
| **splitDirection** | `Enum<'horizontal' \| 'vertical'>` | optional | Split orientation (split forms) |
1987-
| **splitSize** | `number` | optional | Primary split panel size, % (split forms) |
1988-
| **splitResizable** | `boolean` | optional | Whether the split is resizable (split forms) |
1987+
| **splitSize** | `never` | optional | [REMOVED] `view.form.splitSize` was removed in @objectstack/spec 18.3.0 (ADR-0050) — it sized the primary pane of a `type: 'split'` form, and `split` left the form view `type` in the same release, so no form view draws panes. Delete the key. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
1988+
| **splitResizable** | `never` | optional | [REMOVED] `view.form.splitResizable` was removed in @objectstack/spec 18.3.0 (ADR-0050) — it made the panes of a `type: 'split'` form resizable, and `split` left the form view `type` in the same release, so no form view draws panes. Delete the key. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; `--write` applies the ones it can prove, and you apply the rest by hand. |
19891989
| **drawerSide** | `Enum<'top' \| 'bottom' \| 'left' \| 'right'>` | optional | Drawer side (drawer forms) |
19901990
| **drawerWidth** | `string` | optional | [DEPRECATED → size buckets] Drawer width, e.g. "480px". A pixel width cannot be chosen without knowing the client viewport — the renderer derives it. |
19911991
| **modalSize** | `Enum<'sm' \| 'default' \| 'lg' \| 'xl' \| 'full'>` | optional | Modal size (modal forms) |

0 commit comments

Comments
 (0)