Repository navigation
Commit b5e7ba5
feat(spec)!: retire FormView drawerSide / drawerWidth — no form view opens in a drawer (#29287)
domain:spec#1 · os-steve · session_01ADHkmPi25XXKx92Xmg4viS
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi98wYJuaJ1vEnWCa6xuHrcSS)_
Fixes #29012
Clause-②: no (narrowing)
Metadata-protocol: narrowed
## 维护者速读
- 改了什么:表单视图(form view)上的 `drawerSide` / `drawerWidth`
两个键退役,写了就在保存时被拒并给出修法;存量数据读取时自动删掉这两个键。React 页面里 `ObjectForm` 组件的同名属性保留不变。
- 为什么改:表单视图早已不能以抽屉方式打开(`type: 'drawer'` 已在本版本退役),控制台、记录表单页和 Studio
都不读这两个键,作者写了能存能发布却毫无效果,属于“声明了但不生效”。
- 风险与回滚:只收窄表单视图的可写键;已存的表单视图读取时被转换,界面上看到的完全一样。回滚即还原本 PR。
-
先看:`packages/spec/src/ui/view.zod.ts`(两个墓碑和提示文案)、`packages/spec/src/ui/react-blocks.ts`(两个属性改由
`ObjectForm` 自己声明)、`packages/spec/src/conversions/registry.ts` 的
`form-view-drawer-keys-removed`。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run --project repo
src/ui/form-view-drawer-keys-retirement.test.ts`,应看到 29 个用例全部通过。
- 需要你做的:无,席位落地。
## What changes
- **`FormViewSchema.drawerSide` / `drawerWidth` are `retiredKey()`
tombstones** (`packages/spec/src/ui/view.zod.ts`). Each is refused at
every form-view door (a view's `form`, `formViews.*`, a form view item
record, a flattened form overlay, `defineStack` as
`STACK_SCHEMA_INVALID` / 422) with a prescription that says why it was
inert, what to do instead (`navigation.mode` / `navigation.size` for a
list's record drawer; the React `ObjectForm` block's own props for a
page drawer) and the `os migrate meta --from 17` sentence. TypeScript
rejects both at a `FormView` position. The describe that deferred the
question to closed #27153 is gone.
- **The React `ObjectForm` block keeps both props**
(`packages/spec/src/ui/react-blocks.ts`). They leave the block's
`dataProps` (which read `FormViewSchema`) and move to its overlay as
`binding` props with the same types; `drawerSide` keeps its description,
and `drawerWidth`'s drops the form view's `[DEPRECATED → size buckets]`
tag (the block's drawer has no size bucket) and says what `DrawerForm`
does with the value: the drawer's width from the left or right edge, its
height from the top or bottom, the #9392 rule for a prop only the react
tier reads. The overlay entry shares the tombstone's name, so
`REACT_OVERLAY_SHADOWS.ObjectForm` ledgers it with that reason. The
regenerated `skills/objectstack-ui/references/react-blocks.md` moves the
two rows from the data group to the binding group;
`validate-react-page-props` reads the overlay, so a React page's
`drawerSide="right"` stays known.
- **ADR-0087 kit.** D2 conversion `form-view-drawer-keys-removed`
(`toMajor: 18`, `retiredFromLoadPath`, `retiredAfter: '18.2.0'`,
`stripKeys` on every form payload and the assembled `viewItems` channel;
never walks page components, so the `object-form` page block keeps its
keys). D3 semantic entry `ui-form-view-drawer-keys-retired`.
`RETIRED_KEYS_BY_MAJOR[18]` entries `ui/FormView:drawerSide` and
`ui/FormView:drawerWidth`. Step-18 rationale fragment. Removal-version
pin rows.
- **Ledger and generated artifacts.** `liveness/view.json`: both rows
`dead` with the tombstone note (view counts 75/8 → 73/10). Regenerated
`authorable-surface/ui.json` (two `[RETIRED]` rows),
`content/docs/references/{api/protocol,ui/view}.mdx`,
`liveness/state-counts/view.md`, the react-blocks contract.
- **Changeset** `.changeset/29012-form-view-drawer-keys-retired.md`:
BREAKING, `minor` on `@objectstack/spec` and `patch` on
`@objectstack/skills` (the regenerated
`skills/objectstack-ui/references/react-blocks.md` ships there, as in
#27159's changeset), FROM → TO, the one-line fix, `registered` ADR-0087
marker.
### Forced test edits
- `src/ui/form-view-type-layout-retirement.test.ts`: the "STATED
BOUNDARY: `drawerSide`, `drawerWidth` and `modalSize` stay declared" pin
now holds `modalSize` only, and its stored-row case uses `modalSize:
'lg'` as the bystander instead of `drawerSide: 'left'` (the new
conversion strips that). The header's bookkeeping line points at the new
pin.
- `src/conversions/registry.ts` `form-view-presentation-types-removed`
fixture: the `quick` form's bystander is `title: 'Quick'` instead of
`drawerSide: 'left'`, because fixtures must stay disjoint under
whole-table replay. Its docblock now says the drawer keys left later in
the same release.
- `src/ui/view-metadata-schema.test.ts`: the "a FormView key nobody
would think to allow-list" probe uses `showStepIndicator` instead of
`drawerSide`.
- `src/system/metadata-form-zod-reconciliation.test.ts`: the `omit` row
for `view.drawerWidth` is dropped, as the ledger asks for a key that
became a tombstone.
### New pin
`src/ui/form-view-drawer-keys-retirement.test.ts` (repo project, listed
in `vitest.repo-tests.json`): both keys refused at five doors with the
prescription's first sentence and the migrate sentence; a `modalSize`
control at each door; `defineStack` envelope; `RETIRED_KEYS_BY_MAJOR`;
`@ts-expect-error` at the authoring site; the block overlay publishes
both and the `object-form` page block still accepts them; the conversion
on a stored container and a form view item record, never on a page
block, idempotent, off the load path, wired into step 18, with its D3
entry; tree-scoped absence over the five roots spec#test already
declares.
## Why
Triage ruling on the card (comment 6571544001): retire under ADR-0049.
Measured at `origin/main` 104cd1a:
- **No form-view surface reads either key.** The only code that hands
them to `DrawerForm` is
`ui/packages/plugin-form/src/ObjectFormContainer.tsx`, which serves the
`object-form` block's `formType='drawer'`; a form view's `type` cannot
be `drawer` since #27155.
`ui/packages/plugin-designer/src/FieldDesigner.tsx` composes its own
`DrawerFormSchema` in code. The console form page, the New / Edit
dialogs (which read `modalSize` only) and the record form page never
read them.
- **The ui prop types are the block's own.** `DrawerForm.tsx` declares
`drawerSide?` / `drawerWidth?` on `DrawerFormSchema`, and
`@object-ui/types` `ObjectFormSchema` declares its own; neither derives
from `FormViewSchema`, so `ui/` needs no edit (checklist 4 measured,
below).
- **`drawerWidth`'s size-bucket deprecation had no conversion.** The
only conversion text naming it was the presentation-types docblock
saying it stays. This entry deletes it; the prescription names
`navigation.size`.
- **Authored uses on a form view: 0.** `examples/`: 0 form views (2 hits
are `ObjectForm formType="drawer" drawerSide="right"` JSX on React
pages, the block, unaffected); `skills/`: 0 (the generated block
contract only). Control leg: the live form-view key `submitBehavior`
reads 8 in `examples/` on the same tree. HotCRM fixtures are not in this
repository: NOT MEASURED.
## Tests
All runs went through `os-verify-lock` (each VERDICT `command-exit 0`
unless stated). `@objectstack/spec`, `@objectstack/lint` and the spec
repo project ran at `0dc8fcc3`, whose tree differs from the head
`8c7bfc30` only by the changeset file; `ui/` plugin-form, the gates and
dogfood ran at `8c7bfc30`.
Round 2 re-ran at the head `ffc3fe89`, under `os-verify-lock`: `turbo
run build --filter='./packages/**'` exit 0; `@objectstack/spec` test
Test Files 790 passed (790), Tests 23373 passed; `@objectstack/lint`
test Test Files 189 passed (189), Tests 7830 passed; `dispatch-gates
--commands` 112 derived, 112 run, reconciled with `--ran` as a DERIVED
zero, every exit 0 except `check-empty-changeset --base origin/main`
(exit 1 by design, above). The spec repo project, ui plugin-form and
dogfood readings are from `8c7bfc30`; since then the code delta is one
description string in `react-blocks.ts` plus its regenerated row, and
the rest is changeset prose.
- `pnpm --filter @objectstack/spec typecheck`: exit 0 (compiles the new
pin's two `@ts-expect-error` lines).
- `pnpm --filter @objectstack/spec test` (local project): Test Files 789
passed, 1 skipped (790); Tests 23371 passed, 1 skipped, 1 todo.
- `pnpm --filter @objectstack/spec test:repo` (repo project, run locally
because this diff adds and edits repo-project tests): Test Files 88
passed (88); Tests 1477 passed.
- Consumer `@objectstack/lint` (`validate-react-page-props` reads the
block overlay; `lint-liveness-properties` reads the ledger): `pnpm
--filter @objectstack/lint test`: Test Files 189 passed (189); Tests
7825 passed, 5 skipped.
- Consumer `ui/packages/plugin-form` (run from `ui/`): `pnpm --filter
@object-ui/plugin-form test`: Test Files 217 passed (217); Tests 2407
passed, 1 skipped. The first run failed 3 files on a prerequisite
(`@objectstack/client` unbuilt in a fresh container: "Failed to resolve
import"); green after building it.
- `ui/` type-check: NOT MEASURED locally (prerequisite:
`@object-ui/plugin-form type-check` needs the ui workspace's own
packages built, TS2307 on `@object-ui/types` and siblings). By source:
no `ui/` type derives `drawerSide` / `drawerWidth` from `FormView`
(`DrawerFormSchema` and `ObjectFormSchema` declare their own; `git grep`
of `ui/` for `FormView[` indexes names only `modalSize`, `defaultTab`,
`tabPosition`, `layout`, `buttons`, `defaults`). `Type Check UI` on this
PR is the reading.
- `packages/metadata`: no tombstone or conversion pin there names these
keys (`git grep drawerSide drawerWidth -- packages/` outside
`packages/spec` is empty), so no run is owed.
<details><summary>Ablations (each restored, blob == HEAD and git diff
HEAD empty, by scripts/ablation-replace.mjs)</summary>
- Tombstone: `drawerSide: retiredKey(FORM_VIEW_DRAWER_SIDE_RETIRED)`
replaced by the old `z.enum([...]).optional()`. The new pin went red:
Tests 7 failed, 22 passed (29) — the five door refusals, the
`defineStack` envelope and the `@ts-expect-error` case. Restored.
- Conversion: the `stripKeys(form, ['drawerSide', 'drawerWidth'], emit,
path)` call replaced by a pass-through. `conversions.test.ts` fixture
replay for `form-view-drawer-keys-removed` and the pin's two stored-row
cases went red: Tests 3 failed, 235 passed (238). Restored. (An earlier
attempt used a replacement that threw instead of passing through and
reddened every form-view fixture; it was discarded and rerun as the
pass-through above.)
</details>
- Dogfood (title carries `!` and the `(narrowing)` arm) at `8c7bfc30`,
after `turbo run build --filter='./packages/**'` (78/78): `pnpm --filter
@objectstack/dogfood test` under the lock, VERDICT `command-exit 0`;
Test Files 388 passed, 3 skipped (391); Tests 3208 passed, 28 skipped.
No failures.
## Acceptance notes
- `.changeset/27155-form-view-type-layout-only.md` (pending, 18.3.0) had
an "Unchanged" bullet saying `splitDirection`, `drawerSide`,
`drawerWidth` and `modalSize` stay declared. All three notes ship in the
same 18.3.0 release, so this PR corrects it: only `modalSize` stays
declared (the console reads it for its record dialog), `splitDirection`
(#27159) and `drawerSide` / `drawerWidth` (#29012) retire from the form
view in the same release, and the React `ObjectForm` block keeps the
drawer pair as its own props. This is a DELIBERATE CORRECTION of a
pending release note: `check-empty-changeset` exits 1 on it by design,
and `Check Changeset` is not a required context. Do not restore the
original sentence and do not add `skip-changeset`.
- `ui/packages/types/src/objectql.ts` (the `ObjectFormSchema` key
taxonomy docblock) still lists `drawerSide` / `drawerWidth` among the
`[spec-aligned]` FormView keys. They are now the block's own props on
both sides; a comment only, outside this card's file surface.
- `docs/qa/platform-checklist/areas/studio-authoring.json` rows that
score the drawer keys as "fail today" now meet a save refusal instead of
an inert save; the QA lane re-scores them.
- `skills/objectstack-ui/references/react-blocks.md` is the
generator-owned `spec-react-blocks` row: this diff regenerates it from
`react-blocks.ts` and does not touch `packages/spec/scripts/`.
## Verification
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `8c7bfc30`: 112 derived families, all
run; reconciled with `--ran`: "112 derived famil(ies) accounted for —
112 run, 0 NOT-MEASURED (a DERIVED zero)". First pass had six
prerequisite exits in the fresh container (`check-plugin-teardown-shape
--self-test` needed its pinned commit fetched;
`check:doc-formula-expressions`, `check:doc-security-posture`,
`check:skill-examples`, `check:dts-closure`,
`check:dual-build-cjs-loads` needed built packages); each exit 0 after
`turbo run build --filter='./packages/**'` (78/78).
- `pnpm --filter @objectstack/spec check:generated` after regeneration:
in the gate list, exit 0 (first pass named react-blocks, docs and
liveness counts stale; regenerated with `gen:react-blocks`, `gen:docs`,
`gen:liveness-counts`).
- `node scripts/check-adr-0087-registration.mjs --base origin/main`: "1
declared-breaking changeset(s), each carrying an ADR-0087 disposition …
registered form-view-drawer-keys-removed,
ui-form-view-drawer-keys-retired (new here: both)".
- `node scripts/check-changeset-no-major.mjs --base origin/main --event`
on this body: LEVEL AXIS ✓ for `no (narrowing)` + `narrowed`, minor.
- `node scripts/check-empty-changeset.mjs --base origin/main`: exit 1 by
design at `ffc3fe89` (DELIBERATE CORRECTION of the pending 27155 note,
see Acceptance notes; the same red is `Check Changeset` on CI).
`--self-test` exit 0.
- `node scripts/pm/check-governed-merges.mjs --test` on the 20-path
list: "0 of 20 path(s) hit the register after 1 generated-artifact
lift(s) … NOT governed"; `react-blocks.md` is a pure regeneration.
- Every family CI runs is read from this PR's check-runs.
## 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 (CI-owned run).
- [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 by [Claude
Code](https://claude.ai/code/session_01ADHkmSVA1CYQ5wh8VMGZ5r)_
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 2f380dd commit b5e7ba5
21 files changed
Lines changed: 709 additions & 65 deletions
File tree
- .changeset
- content/docs/references
- api
- ui
- packages/spec
- authorable-surface
- liveness
- state-counts
- src
- conversions
- migrations
- entries
- retired-keys
- semantic
- system
- ui
- skills/objectstack-ui/references
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
18 | 18 | | |
19 | 19 | | |
20 | 20 | | |
21 | | - | |
| 21 | + | |
22 | 22 | | |
23 | 23 | | |
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1916 | 1916 | | |
1917 | 1917 | | |
1918 | 1918 | | |
1919 | | - | |
1920 | | - | |
| 1919 | + | |
| 1920 | + | |
1921 | 1921 | | |
1922 | 1922 | | |
1923 | 1923 | | |
| |||
1995 | 1995 | | |
1996 | 1996 | | |
1997 | 1997 | | |
1998 | | - | |
1999 | | - | |
| 1998 | + | |
| 1999 | + | |
2000 | 2000 | | |
2001 | 2001 | | |
2002 | 2002 | | |
| |||
0 commit comments