You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2655ebd
Browse filesBrowse the repository at this point in the historyBrowse files
feat(spec,plugin-form)!: a form view's buttons hold submit and cancel only — buttons.reset is retired, because no form ever drew a reset button (#27269)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwBDeyq5DkFHhU9RodAzdP53y)_
Refs #27153
Ruling-ref: 6469111181
Clause-②: no (narrowing)
Metadata-protocol: narrowed
Skills-docs: none — no `skills/**` file or hand-written docs page
authors or teaches `buttons.reset` (0 hits at base); only the generated
references move
## 维护者速读
- 改了什么:表单视图 `buttons` 里的 `reset` 退役,写了会被拒并附改法。`buttons` 只剩 `submit` 和
`cancel`。ObjectForm 不再读 `buttons.reset.show`,ui 的
`ObjectFormSchema.buttons` 镜像同步去掉 `reset`。
- 为什么改:ADR-0049 声明即执行。六处表单按钮栏都只画提交和取消,从没有画过重置按钮,`reset.label` 无人读取;唯一读
`reset.show` 的地方把它当成"保存后清空表单",与"重置按钮"的含义不符。
- 风险与回滚:这是收窄,写了 `buttons.reset` 的元数据会被拒。本仓除两个测试夹具外没有任何生产者;已存的
`sys_metadata` 行读取时自动去掉该键,`os migrate meta --from 17` 改写源码。回滚就是 revert 本
PR。
- 先看:① `packages/spec/src/ui/view.zod.ts` 的
`FORM_VIEW_BUTTONS_RESET_RETIRED` 改法文案;②
`packages/spec/src/conversions/registry.ts` 的
`formViewButtonsResetRemoved`;③
`ui/packages/plugin-form/src/ObjectForm.tsx` 删掉的那一行折叠。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run --project repo
src/ui/form-view-buttons-reset-retirement.test.ts`,预期 22 个测试全部通过。
- 需要你做的:无,席位落地。
## What changes
### Spec (`@objectstack/spec`)
- `FormViewSchema.buttons.reset` is a `retiredKey()` tombstone inside
the live, strict `buttons` block. Its prescription:
"`view.form.buttons.reset` was removed in @objectstack/spec 18.3.0
(ADR-0049) — no form ever drew a reset button, and its label was never
read. Delete the key: a form has `submit` and `cancel` buttons only, and
Cancel leaves the form without saving. `reset.show: true` used to clear
the form after each save (the ObjectForm fold that ObjectView and the
record form page render); a form converted by this release no longer
clears after a save, and a form that should declares `submitBehavior: {
kind: 'continue' }`." followed by the `os migrate meta --from 17`
sentence. `buttons.submit` and `buttons.cancel` are unchanged.
- **Behaviour after conversion (M1).** `reset.show: true` used to clear
the form after each save, through the ObjectForm fold that ObjectView
and the record form page render. A converted stored row no longer clears
after a save. The D2 conversion does not map the key onto
`submitBehavior`, because the row cannot say whether the author wanted
the clear. The prescription, the D3 `replacement` and `reason`, the
conversion docblock and summary, the STEP18 rationale fragment and the
changeset's FROM → TO line now all say this. They name `submitBehavior:
{ kind: 'continue' }`, which ObjectForm and the console `FormPage`
honour, for an author who wants the clear.
- ADR-0087 kit (`spec-property-retirement`, key-tombstone route):
- **D2 is owed**, so it ships: the block accepted `reset` through 18.2.0
and `PUT /meta/view` persists form views, so a stored `view` row can
carry the key. Conversion `form-view-buttons-reset-removed`
(`retiredFromLoadPath: true`, `retiredAfter: '18.2.0'`, MAJOR_18 order
79) strips `reset` from every form payload's `buttons`: a container's
`form` and `formViews.*`, a form view item's `config`, a flattened form
overlay, and the assembled `viewItems` channel. A block that held only
`reset` stays an empty block. Its fixture has 4 notices.
- D3 semantic entry `ui-form-view-buttons-reset-retired` (its
`conversionIds` names the conversion), plus a STEP18_RATIONALE fragment.
- One `RETIRED_KEYS_BY_MAJOR` row: `ui/FormView:buttons.reset` (a nested
key of an inline block, so no `authorable-surface/` line of its own).
- Removal-version pin: one JOIN row (the tombstone).
- Liveness (`liveness/view.json`): the `props/form/children/buttons` row
stays `live` for `submit` / `cancel`, and its note no longer claims
`reset` folds. It records the removal and the measurement, because
sub-keys cannot be drilled.
- Regenerated: `content/docs/references/ui/view.mdx` and
`references/api/protocol.mdx`. `authorable-surface`, `api-surface`,
`react-blocks.md` and `skills/**` are byte-identical (`buttons` is not
in the React `ObjectForm` block's `dataProps`).
### Renderer (`@object-ui/plugin-form`, `@object-ui/types`): the reader
of the refused key, in the same PR
- `ObjectForm.foldFormButtons` no longer folds `buttons.reset.show` onto
`showReset`. That fold was the key's only reader, and it landed on
`resetOnSubmit` (clear the form after a save), not on a button. The
fold's comment no longer lists `reset` among the structured `buttons`
keys.
- The hand-written `ObjectFormSchema.buttons` mirror drops `reset` in
both the TypeScript type (`types/src/objectql.ts`) and the zod mirror
(`types/src/zod/objectql.zod.ts`), so the ui side no longer offers a key
nothing reads.
- The ui types fixture `object-form-unmirrored-members-6152.test.ts` now
uses `cancel` where it used `reset`.
- Comment-only: `FormPage.readFormButton`, `resolveFormViewChrome` and
the plugin-form fold test header no longer describe `reset` as an unread
key.
- **Not touched:** the flat `showReset` prop and its `resetOnSubmit`
mapping. That is the block contract's own defect (`component.zod.ts` and
`react-blocks.ts` describe it as "Show the reset button"), filed by the
seat as #27247.
## Why
#27153 asked for enforce-or-remove on `buttons.reset` under ADR-0049.
Measured at `origin/main` `fbe344b3`, six separate button bars draw a
form's submit and cancel: the components form action bar (simple,
sectioned, tabbed and split ObjectForm), console `FormPage`, the
`ModalForm` and `DrawerForm` footers, `WizardForm` and
`MasterDetailForm`. None of them draws a reset button. Two of them would
need a semantic decision before one could (whether the wizard resets the
step or the whole wizard, and whether master-detail resets line items).
In-repo producers: 0 outside two test fixtures. Seat 1 ruled **retire**
(claim 6469111181).
## Tests
- New pin
`packages/spec/src/ui/form-view-buttons-reset-retirement.test.ts` (22
tests, `repo` project, added to `vitest.repo-tests.json`):
- five doors refuse `buttons.reset` (with `show: true`, `show: false`, a
label, and empty) at `…buttons.reset`, with the prescription's first
sentence and the migrate sentence. The doors are `FormViewSchema`,
`view.form`, `view.formViews.*`, a ViewItem form record and the
flattened form overlay;
- the five doors still accept `submit` and `cancel`;
- a control: an unknown button keeps the strict block's
`unrecognized_keys`, not "was removed";
- `defineStack` refuses with `STACK_SCHEMA_INVALID` / 422 at
`views.0.formViews.quick.buttons.reset`;
- a `@ts-expect-error` 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 wrote a temporary `packages/spec/src/ui/zz-probe-27153.ts`
authoring `buttons: { submit: {}, reset: { show: true } }`; the walk
named it (1 failed), and the probe was removed with a trap.
- `view.test.ts`: the buttons block test drops `reset`, and a new case
pins the tombstone.
- `retired-key-removal-version.pin.test.ts`: JOIN row.
## Acceptance notes
- **Producer census, at base `fbe344b3`:** `buttons.reset` was authored
by 0 files in `examples/`, platform sources, `skills/**`, Studio
`*.form.ts` and ui examples. Two test fixtures authored it: spec
`view.test.ts:3222` and ui types
`object-form-unmirrored-members-6152.test.ts:112`, both moved here.
**hotcrm, measured** on a public shallow clone at `353dace` (1055
tracked files, at 2026-10-28T08:39Z): 0 files author a `buttons: {`
block at all, and no `reset:` key appears outside a `package.json`
script name. If a hotcrm source authored after that commit carries the
key, it is refused at parse with the prescription, `os migrate meta
--from 17 --write` deletes it, and a stored row is converted on read.
- **Kept, as stated in the claim:** `drawerSide` and `drawerWidth` stay.
They retire with #27159, which owns the React `ObjectForm` block
contract; hence `Refs #27153`, not `Fixes`.
- **Install note:** the worktree install ran with `--ignore-scripts`,
because the `better-sqlite3` header download from nodejs.org was cut off
twice. Its shipped `prebuilds/linux-x64.node` loads (`select 1`
answered), so nothing measured here depended on the skipped script.
## Verification
Head: `a443f1228`. It is `f101f23dd` plus `3684cdf07` (review 6471309095
round 2: M1, S2, and the `conversions-major18-merge` placement fix,
which moves `formViewButtonsResetRemoved` directly above
`formViewOptionDefaultRemoved`), a merge of `origin/main` at `696ee79f5`
(made with `os-regen-merge.sh`), and the `protocol.mdx` regeneration it
owed. On the merged tree: the spec build, `check:generated` (14/14), the
spec suite (749 files, 22381 tests passed), and `test:repo` at 1376 of
1377. The one failure is `def-key-collisions.test.ts`: its sandbox copy
cannot find tsx's `cli.mjs` because this worktree was installed with
`--ignore-scripts`; it passed in CI on `f101f23dd`. Full dogfood ran on
`5026fd569`. The round-1 notes below were measured on `f101f23dd`.
<details><summary>Spec build, generated artifacts and source
audits</summary>
- `pnpm --filter @objectstack/spec build` (under os-verify-lock):
`VERDICT command-exit 0`.
- `check:generated`: 1 of 14 stale (`check:docs`), fixed with `--fix`;
re-run: "All 14 generated artifacts are up to date".
- `check:liveness` (exit 0, 1209 classified), `check:empty-state`,
`check:exported-any`, `check:dual-source-exports`, `check:variant-docs`:
exit 0 each.
- `pnpm --filter @objectstack/spec typecheck`: exit 0;
`check:test-typecheck` OK, 132 pinned signatures held.
</details>
<details><summary>Tests</summary>
- `@objectstack/spec` `pnpm test`: 744 files and 22314 tests passed, 1
todo (`5026fd569`); after the merge, 748 files and 22353 tests passed, 1
todo (`f101f23dd`).
- New pin (`--project repo`): 22/22.
- `retired-key-removal-version.pin.test.ts` and
`retired-key-migrate-sentence.test.ts`: 70/70.
- ui `@object-ui/types`: `type-check` exit 0;
`object-form-unmirrored-members-6152`, `zod-mirror-parity` and
`object-view-slot-key-lists`: 165/165.
- ui `@object-ui/plugin-form`: `type-check` exit 0;
`formButtonsDefaults`, `formViewTabKeysAndButtons-26843` and
`ObjectForm.test`: 19/19.
- Full dogfood (`'@objectstack/dogfood^...'` closure build, then `pnpm
--filter @objectstack/dogfood test`, under os-verify-lock, on
`5026fd569`): `VERDICT command-exit 0`; 348 test files passed and 3
skipped (351); 2804 tests passed and 20 skipped.
</details>
<details><summary>Derived gates</summary>
`node scripts/pm/dispatch-gates.mjs --commands` (tree
`objectstack-ai/objectstack`, merge base `fbe344b3c`, 20 paths, 589
changed lines): all 32 `node scripts/…` commands exit 0 on both
`5026fd569` and `f101f23dd`, `check-adr-0087-registration --base
origin/main` included. `pnpm --dir ui exec vitest run scripts/`: 175
files passed, 2 skipped; 5020 tests passed. Every other family is CI's.
</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.
- [ ] 3. No formatter ran; `pnpm lint` is CI's run (not run locally).
- [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 (the 维护者速读 block is the
dispatch contract's Chinese summary).
---
_Generated by [Claude
Code](https://claude.ai/code/session_01WGufzg7PvSmSGxVnCGiGfZ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>
**BREAKING** accept-set narrowing on the form view surface (`@objectstack/spec`), ADR-0049 enforce-or-remove.
16
+
17
+
The console draws a form's Submit and Cancel buttons on every entry point (the form page, the New and Edit dialogs, the record form page, master-detail forms). None of them drew a reset button, and `buttons.reset.label` was never read. The one renderer fold that read `buttons.reset.show` passed it to ObjectForm's clear-after-save flag, so `show: true` emptied the form after a successful save instead of adding a button.
18
+
19
+
-**FROM → TO.**`buttons: { submit, cancel, reset }` → `buttons: { submit, cancel }`. No button replaces it: Cancel leaves the form without saving. `reset.show: true` used to clear the form after each save on ObjectView and the record form page; a converted form no longer clears after a save. A form that should clear after a save declares `submitBehavior: { kind: 'continue' }`, which ObjectForm and the console form page honour.
20
+
-**The one-line fix.** Delete `buttons.reset`. `os migrate meta --from 17` lists the edit for existing sources, and `--write` applies it. A stored `sys_metadata` row is converted when it is read (the `form-view-buttons-reset-removed` conversion), so a form view saved with the key keeps loading.
21
+
-**What the parse says.** "`view.form.buttons.reset` was removed in @objectstack/spec 18.3.0 (ADR-0049) — no form ever drew a reset button, and its label was never read. …", 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. TypeScript rejects the key at the authoring site.
22
+
-**Unchanged.**`buttons.submit` and `buttons.cancel` parse and render as before.
23
+
24
+
The renderer (`@object-ui/plugin-form`, `@object-ui/types`): `ObjectForm` no longer folds `buttons.reset.show` onto `showReset`, and the `ObjectFormSchema.buttons` mirror (type and zod) drops `reset`. The flat `showReset` prop itself is unchanged.
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
1920
1920
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
1921
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1921
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1922
1922
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
1999
1999
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
2000
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2000
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2001
2001
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
410
410
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
411
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
411
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
412
412
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
|**reset**|`never`| optional |[REMOVED]`view.form.buttons.reset` was removed in @objectstack/spec 18.3.0 (ADR-0049) — no form ever drew a reset button, and its label was never read. Delete the key: a form has `submit` and `cancel` buttons only, and Cancel leaves the form without saving. `reset.show: true` used to clear the form after each save (the ObjectForm fold that ObjectView and the record form page render); a form converted by this release no longer clears after a save, and a form that should declares `submitBehavior: { kind: 'continue' }`. 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.|
508
508
509
509
510
510
---
@@ -1788,7 +1788,7 @@ Tab configuration for multi-tab view interface
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
1790
1790
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
1791
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1791
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1792
1792
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
1793
1793
1794
1794
### Nested Shape: `View.listViews[string]`
@@ -1867,7 +1867,7 @@ Tab configuration for multi-tab view interface
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
1869
1869
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
1870
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1870
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
1871
1871
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
1872
1872
1873
1873
### Nested Shape: `View.protection`
@@ -2104,7 +2104,7 @@ This schema accepts one of the following structures:
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
2106
2106
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
2107
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2107
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2108
2108
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
|**sharing**|`{ enabled?: boolean; publicLink?: string; password?: string; allowedDomains?: string[]; … }`| optional | Public sharing configuration for this form |
2290
2290
|**submitBehavior**|`{ kind: 'thank-you'; title?: string; message?: string } \| { kind: 'redirect'; url: string; delayMs?: integer } \| { kind: 'continue' } \| { kind: 'next-record' }`| optional | Post-submit behavior. On the `redirect` arm, `url` is relative-only and interpolates only declared record fields as `{{record.field_name}}`, URL-escaped. |
2291
-
|**buttons**|`{ submit?: object; cancel?: object; reset?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2291
+
|**buttons**|`{ submit?: object; cancel?: object }`| optional | Form action-button visibility & labels; folded onto the flat renderer props by ObjectUI ObjectForm. |
2292
2292
|**defaults**|`Record<string, any>`| optional | Initial field values for create-mode forms (folded into ObjectUI ObjectForm initial values;). |
Copy file name to clipboardExpand all lines: packages/spec/liveness/view.json
+1-1Lines changed: 1 addition & 1 deletion
Original file line number
Diff line number
Diff line change
@@ -370,7 +370,7 @@
370
370
},
371
371
"buttons": {
372
372
"status": "live",
373
-
"note": "objectui: ObjectForm.tsx foldFormButtons() reads schema.buttons and folds submit/cancel/reset {show,label} onto the flat showSubmit/submitText/showCancel/cancelText/showReset props (framework#1894 / #2998); ObjectView + RecordFormPage forward it. Flat keys kept as deprecated back-compat (explicit flat key wins)."
373
+
"note": "objectui: ObjectForm.tsx foldFormButtons() reads schema.buttons and folds submit/cancel {show,label} onto the flat showSubmit/submitText/showCancel/cancelText props (framework#1894 / #2998); ObjectView forwards it, and every console form entry point resolves submit/cancel through resolveFormViewChrome / readFormButton (#26843). Flat keys kept as deprecated back-compat (explicit flat key wins). SUB-KEY buttons.reset REMOVED 2026-10-28 (#27153, ADR-0049 enforce-or-remove, ruled retire): measured at origin/main fbe344b3, six button bars (components form.tsx, console FormPage, ModalForm, DrawerForm, WizardForm, MasterDetailForm) drew submit/cancel and none drew reset; reset.label had no reader; the one fold of reset.show landed on ObjectForm showReset, which means clear-after-save (resetOnSubmit), not a button. Tombstoned inside the buttons block (retiredKey carries the prescription) and stripped from stored rows and sources by the protocol-18 form-view-buttons-reset-removed conversion; the fold was removed in the same change. Sub-keys cannot be drilled (one level only), so the removal is recorded here; submit and cancel keep this row live."
0 commit comments