Repository navigation
Commit 76bf6c5
feat(spec): an action's confirm and param dialogs can name their dismiss button —
<!-- claude-projects-attribution: {"github_login":"os-steve"} -->
_Requested by **Steve** · [project
thread](https://claude.ai/code/project/chan_01FdL1XeUyvVNxLreh3F4Qi9?thread=cmsg_01FdL1XeUyvVNxLreh3F4Qi9Ut7hw65Gi64QMYVAVzsgLi)_
Part of #28519
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: follow-up owed — `skills/objectstack-ui/rules/actions.md`
(the action key list teaches `submitText` beside `confirmText`) and
`skills/objectstack-i18n/SKILL.md` (the `_actions.{action_name}` key
row). Both are governed (Tier H), so they ride a separate PR rather than
this one; neither turns false here, they only lack the new key.
## 维护者速读
- 改了什么:动作(action)新增可选键
`cancelText`,给动作弹出的确认框或参数框的「取消」按钮起名字;可写纯字符串或多语言映射,翻译路径
`objects.对象._actions.动作.cancelText`;空字符串、或在不弹框的动作上写它,解析时直接拒收。
- 为什么改:动词本身就是「取消……」的动作(如邀请列表的「Cancel
Invitation」)弹确认框时,两个按钮是「Cancel」和「Cancel Invitation」,管理员分不清哪个是保留邀请。这次先落
spec 半边,控制台画按钮、`sys_invitation` 声明「Keep invitation」留给后续卡(与 `submitText`
当初的拆法相同)。
- 风险与回滚:纯放宽,不写这个键的动作解析结果完全不变;目前没有界面读它,liveness 标 `planned` 并让 `os
validate` 提醒作者「按钮还显示 Cancel」。回滚即撤掉本 PR。
- 先看:`packages/spec/src/ui/action.zod.ts`(`actionCancelText` 与新加的第三条
refine:没有 `confirmText` 也没有非空 `params`
时拒收);`packages/spec/src/system/i18n-resolver.ts` 的 `translateAction`
叠加条件;`packages/lint/src/validate-translation-references.ts` 的
`checkActionCancelText`。
- 两分钟验证:`pnpm --filter @objectstack/spec exec vitest run
src/ui/action-cancel-text.test.ts`,应看到 20 条全过。
- 需要你做的:确认新公开键名 `cancelText`(席位已在项目里问你);其余无,席位落地。
## What changes
- **`ActionSchema.cancelText`** (`@objectstack/spec`,
`@objectstack/spec/ui`): an optional `I18nLabel`, the label of the
dismiss button on the dialog an action opens before it runs, in place of
the console's default "Cancel". Two dialogs carry that button and an
action opens at most one: the confirm dialog of a param-less action that
declares `confirmText`, and the param dialog of an action with a
non-empty `params` (beside that dialog's `submitText`).
- A blank string (`''` or whitespace only) is refused at parse, through
`NON_BLANK_STRING`, so the published JSON Schema carries the rule too.
- A new refinement refuses the key on an action that declares neither
`confirmText` nor a non-empty `params`: no dialog opens, so the label
would parse clean and never show (ADR-0078). It mirrors `submitText`'s
no-`params` refusal, widened to the two hosts this button has.
- `cancelLabel` is an alias that renames to `cancelText`, on the action
and in a translation bundle. It is the spelling of the same button on
the console's `form` and `alert-dialog` renderers, the `*Label` twin of
`submitLabel`.
- `InlineActionSchema` does not pick the key, as it does not pick
`submitText`: no inline host forwards either.
- **Translation slot.** `objects.OBJECT._actions.ACTION.cancelText` and
`globalActions.ACTION.cancelText` (`translation.zod.ts`).
`translateAction` overlays it for an action that opens either dialog,
judged on the AUTHORED `confirmText` / `params`, and never gives a
button to an action that opens none.
- **Lint.** `validateTranslationReferences` reports a `cancelText`
translation under an action that declares neither `confirmText` nor
`params` as `translation-target-unknown` at `error`
(`checkActionCancelText`), at both addresses.
- **Ledgers.** `liveness/action.json` gains a `planned` row with
`authorWarn` (nothing draws the key yet), and `state-counts/action.md`
is regenerated (planned 2 → 3). The metadata-form reconciliation ledger
gains an `omit` row (no Studio control is offered until the console
draws the key). `authorable-surface/ui.json` lists
`ui/Action:cancelText`. `SPEC_ACTION_KEYS` in `@object-ui/core` lists
it.
- **Docs.** A `cancelText` section in
`content/docs/protocol/objectui/actions.mdx` beside `submitText`'s, the
interface listing line, and the `content/docs/ui/translations.mdx`
action row. The `content/docs/references/**` pages are regenerated.
- The `submitText` docblock now names `actionCancelText` as its pair.
`object-form`'s own `submitText` / `cancelText` describes in
`component.zod.ts` are left as they are: `submitText`'s precedent added
no back-reference there either.
### Forced test edits
-
`packages/metadata-protocol/src/protocol.meta-types-degenerate-derivation.test.ts`:
the served `action` key count 51 → 52, and the sample names
`cancelText`.
- `packages/spec/src/system/metadata-form-zod-reconciliation.test.ts`:
the new `omit` row for `cancelText` (a schema key the action form does
not offer must be ledgered).
- `packages/lint/src/validate-translation-references.test.ts`: the
action-entry coverage table classifies every key the translation shape
declares, so `cancelText` gets a `reference-checked` row (host
`noPreRunDialog`), and the real-instance leg carries it.
- `ui/packages/core/src/actions/actionKeys.ts`: the `SPEC_ACTION_KEYS`
inventory, pinned against the spec's declared keys by
`actionKeys.pin.test.ts`.
## Why
#28519: on Setup → Invitations, the row menu's **Cancel Invitation**
opens a confirm whose two buttons read **Cancel** and **Cancel
Invitation**. `ActionSchema` has the param dialog's submit label
(`submitText`) but no key for the dismiss button. This PR is the spec
half (Wanted 1). Wanted 2 (the console's `ActionConfirmDialog` /
`ActionParamDialog` draw it) and Wanted 3
(`sys_invitation.cancel_invitation` declares `cancelText: 'Keep
invitation'` with its translations) are the ui / platform-objects half,
split the way `submitText` was split.
## Tests
- `packages/spec/src/ui/action-cancel-text.test.ts` (new, 20 tests):
both I18nLabel forms on both host dialogs, optional, the non-I18nLabel
shapes `label` refuses, the blank refusal on both hosts, the no-dialog
refusal (no `params` and `params: []`, string and locale map) with its
control, the `cancelLabel` rename, not on `InlineActionSchema`, the
translation slot at both addresses with the bundle alias, and
`translateAction` on the confirm host, the param host, `globalActions`,
the locale fallback, an action that authors none, and an action that
opens no dialog.
- `packages/lint/src/validate-translation-references.test.ts`: a
`cancelText` describe (errors at both addresses under an action that
opens no dialog; the controls: accepted under a confirm host and under a
param host), plus the coverage rows above.
## Acceptance notes
- **Implementation choice, raised for the seat:** the no-dialog refusal
is not in the card's Wanted 1 text. It is the `submitText` precedent's
second rule carried over, because without it `cancelText` on a
click-to-run action would parse clean and never show. It judges
`confirmText != null` (the declaration), not the console's truthiness
test: a blank `confirmText` already fails `os validate`, `compile` and
the publish gate, so the refusal does not add a second message for it.
- **Default stays the console's.** Absent, both dialogs keep their own
"Cancel" (`actionConfirm.cancel` / `actionDialog.cancel`). The console's
confirm dialog already draws `options.cancelText` over its default; what
it lacks is the runner handing the action's key to it
(`ActionRunner.confirmOptions` passes `title` / `confirmText` /
`destructive` only, and `ActionDef` does not declare the key). That is
the follow-up's first file.
- **Migrations checked, nothing owed:** `git grep -c
"cancelLabel\|cancelText" -- packages/spec/src/migrations
packages/spec/src/conversions` answers 0 files (control: `submitLabel`
hits 4 files, 17 lines). `element:form`'s retired keys are six and none
is a cancel label (`retired-keys/18.ui__ElementFormProps__*`), and the
`translation-component-submit-label-retired` D3 entry concerns the
component copy key `submitLabel` alone. `cancelText` moves no existing
key, so no conversion or semantic entry is owed.
- **Stale sentence noted, not fixed here:**
`content/docs/protocol/objectui/actions.mdx`'s `submitText` section
still ends "The console does not draw it yet", which #28392 made false
(the liveness row is `live`). Not this card's defect class; carrier: the
console follow-up of this card, which edits the same file to flip
`cancelText`'s matching sentence.
## Verification
All readings on head `6e2b3ddb` (this branch with `origin/main`
`3e62a22c` merged in) unless a line says otherwise; every heavy run went
through `os-verify-lock.sh`, whose `VERDICT command-exit` line is
quoted.
<details><summary>Package suites and type checks (the packages this diff
touches)</summary>
- `pnpm --filter @objectstack/spec test`: Test Files 789 passed (789),
Tests 23311 passed, 1 todo. VERDICT command-exit 0.
- `pnpm --filter @objectstack/lint test`: Test Files 189 passed (189),
Tests 7829 passed (7829). VERDICT command-exit 0.
- `pnpm --filter @objectstack/metadata-protocol test`, re-run after the
`main` merge (main moved `metadata-protocol/src`): Test Files 297
passed, 4 skipped (301), Tests 22093 passed, 26 skipped. VERDICT
command-exit 0.
- `pnpm --filter @object-ui/core test`: Test Files 228 passed (228),
Tests 4226 passed, 27 skipped. VERDICT command-exit 0.
- `typecheck` for `@objectstack/spec`, `@objectstack/lint`,
`@objectstack/metadata-protocol` (spec's includes
`check:test-typecheck`): all Done, VERDICT command-exit 0;
`@object-ui/core` `type-check`: VERDICT command-exit 0.
- `eslint --no-inline-config` over the eight changed `.ts` files under
`packages/`: exit 0; `ui` eslint over
`ui/packages/core/src/actions/actionKeys.ts`: exit 0.
- `pnpm --filter @objectstack/spec check:generated --fix`: regenerated
exactly `content/docs/references/**` and `liveness/state-counts/`, then
re-checked both green; `check:api-surface` green against a dist that run
built (no export moved).
</details>
<details><summary>Ablations (one per changed tested behaviour; each
landed on disk through ablation-replace and restored to the HEAD
blob)</summary>
- `action.zod.ts`, the no-dialog refinement's `data.cancelText != null`
→ `false`: `action-cancel-text.test.ts` 2 failed | 18 passed (the two
no-dialog refusals). Restored: blob == HEAD.
- `validate-translation-references.ts`, `checkActionCancelText`'s
`confirmText` guard → bare `return`:
`validate-translation-references.test.ts` 2 failed | 231 passed (the
`cancelText` error leg and the coverage table's reference-checked leg).
Restored.
- `i18n-resolver.ts`, `const cancelText = opensDialog` → `true`:
`action-cancel-text.test.ts` 1 failed | 19 passed ("gives no button to
an action that opens no dialog"). Restored.
- Reverse verification of the cross-package pin: `'cancelText'` deleted
from `SPEC_ACTION_KEYS` → `actionKeys.pin.test.ts` 1 failed | 18 passed,
`missing: ['cancelText']`, so the pin reads the rebuilt spec. Restored.
</details>
<details><summary>Derived gate families (dispatch-gates)</summary>
- `node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` at `6e2b3ddb`: 109 commands, change set 21
paths vs merge base `3e62a22c4`, 525 changed lines (+513 / -12).
- All 109 exit 0. `check-plugin-teardown-shape.mjs --self-test` first
exited 3 (its pinned fixture commit is outside the shallow clone); after
`git fetch --depth=1 origin 621a487…` it reports 48 cases pass, exit 0.
- `dispatch-gates --ran`: "109 derived famil(ies) accounted for — 109
run, 0 NOT-MEASURED (a DERIVED zero)".
- `check-changeset-no-major.mjs --base origin/main --event` (this body,
offline): exit 0, `@objectstack/spec: minor` for `Clause-②: yes
(widening)` + `Metadata-protocol: added`.
- Local-only families: `check:skill-frame-freshness` exit 0;
`check:dev-prereqs` exit 1 as a box precondition (34 `ui/` workspace
dists not built in this container; the `packages/**` build is 78/78
tasks), not a reading about this diff.
</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 by [Claude
Code](https://claude.ai/code/session_01ADHkmNhdF5oapRdMKNBctQ)_
---------
Co-authored-by: Claude <noreply@anthropic.com>cancelText (#28859)1 parent 69c9f7c commit 76bf6c5
21 files changed
Lines changed: 513 additions & 12 deletions
File tree
- .changeset
- content/docs
- protocol/objectui
- references
- data
- kernel
- system
- ui
- ui
- packages
- lint/src
- metadata-protocol/src
- spec
- authorable-surface
- liveness
- state-counts
- src
- system
- ui
- ui/packages/core/src/actions
| 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 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
231 | 231 | | |
232 | 232 | | |
233 | 233 | | |
| 234 | + | |
234 | 235 | | |
235 | 236 | | |
236 | 237 | | |
| |||
539 | 540 | | |
540 | 541 | | |
541 | 542 | | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
542 | 563 | | |
543 | 564 | | |
544 | 565 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
491 | 491 | | |
492 | 492 | | |
493 | 493 | | |
| 494 | + | |
494 | 495 | | |
495 | 496 | | |
496 | 497 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
336 | 336 | | |
337 | 337 | | |
338 | 338 | | |
| 339 | + | |
339 | 340 | | |
340 | 341 | | |
341 | 342 | | |
| |||
0 commit comments