Skip to content

fix(ui): a destructive action block confirms through the destructive button - #26776

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-173d81
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-173d81

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread

Fixes #26665

Clause-②: no
Metadata-protocol: none

维护者速读

  • 改了什么:四个动作块(action:button / action:icon / action:group / action:menu)把 variant 转发给动作执行器,destructive 在这一处转成 danger;UIActionSchema.variant 按引用取 spec 块行的词表(补上 link)。
  • 为什么改:块里声明 destructive 的危险动作,确认框一直是中性按钮;执行器只认动作方言里的 danger,而四个块从不转发 variant。
  • 风险与回滚:只影响带 confirmText 且 variant 为 destructive 的块,确认按钮改为红色;default / outline 不转发,行为不变。回滚即 revert 本 PR。
  • 先看:ui/packages/components/src/renderers/action/runner-variant.ts(映射本身)、action-button.tsx 的 forward 一行、ui/packages/types/src/ui-action.ts 的 variant。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/components/src/renderers/action/__tests__/action-variant-confirm-26665.test.tsx,预期 9 个测试全部通过。
  • 需要你做的:无,席位落地。

What changes

@objectstack/spec spells variant in two dialects, each on its own surface (seat ruling: premise correction comment 6444873110 on #26665):

  • the four action blocks' rows (ActionButtonPropsSchema, ActionIconPropsSchema, ActionGroupPropsSchema, ActionMenuPropsSchema, and the group/menu member items) take the Button primitive's vocabulary plus primary, and refuse danger;
  • ActionSchema, which the runner dispatches, takes primary | secondary | danger | ghost | link. The runner reads variant for one purpose: danger paints the confirm button destructive (confirmOptions in ActionRunner.ts).

The four block renderers forwarded no variant, so a block declared destructive confirmed through a neutral button. Now:

  • runner-variant.ts (new, @object-ui/components): toRunnerVariant translates the block value into the action value at the one forward: destructive becomes danger; primary, secondary, ghost and link pass through; default and outline, which the action dialect has no word for, forward nothing. It reads no danger: it is not a reader-side alias.
  • The four renderers forward variant: toRunnerVariant(...) beside confirmText. action:group and action:menu forward each member's own variant, not the container's.
  • UIActionSchema.variant (@object-ui/types) is typed by reference to the spec's ActionButtonProps['variant'], the way size beside it already is. That adds link, which the block rows accept, and keeps refusing danger, which they refuse.
  • ui/scripts/check-action-forward-parity.mjs: the four SURFACE:variant JUSTIFIED entries are removed; the gate now finds variant forwarded on all four surfaces.

Why

The goal of the card is unchanged: a destructive action declared through one of the four blocks confirms through the destructive button. The method follows the seat's premise correction: the block rows keep their own spec dialect, and the translation lives at the forward, because each value is spec-declared on its own surface.

Tests

  • New action-variant-confirm-26665.test.tsx: per surface, a destructive action asks the confirm handler with destructive: true; the outline control asks without it; each row first asserts the handler was called once and that nothing ran after the cancel. One row pins the mapping table of toRunnerVariant.
  • action-forward-precedence.test.tsx: variant added to the button and icon key-order lists, beside confirmText (one added name, no neighbour moved).

Acceptance notes

  • action:group's inline members draw the group's variant when they declare none, but the forward carries only the member's own variant: the group's value is the container's style, not a declaration about each action.
  • The action:button / action:icon registration inputs still publish the variant enum without link. Publishing it is a registry change with a manifest regeneration and is not in this card.
  • @object-ui/types has no zod mirror of UIActionSchema.variant; the public-block zod faces take the spec rows by reference already.

Verification

All local runs on head 631c2c46.

Ablation (two mutations, both restored by blob hash)
  • runner-variant.ts, return 'danger'; replaced with return undefined; (anchor 1 to 0, blob changed): 5 failed, the four destructive rows and the mapping row. Restored: blob equals the HEAD blob, git diff HEAD empty.
  • action-group.tsx, the variant: toRunnerVariant(action.variant), forward deleted: 1 failed (the action:group destructive row), 8 passed. Restored the same way.
Reverse type check

A scratch file in ui/packages/types/src typed two UIActionSchema literals: variant: 'link' compiled, variant: 'danger' failed with TS2322 (the union printed is the block rows' seven values). The file was removed; the tree is clean.

Tests and type-check
  • pnpm --dir ui exec vitest run --maxWorkers=2 packages/components/src/renderers/action/ (under the verify lock, before the main merge): Test Files 35 passed (35), Tests 581 passed (581).
  • The new pin alone: Test Files 1 passed, Tests 9 passed.
  • pnpm --filter @object-ui/types --filter @object-ui/components run type-check (under the lock): both packages Done, VERDICT command-exit 0.
  • pnpm --dir ui run check:action-forward-parity: exit 0, 26 justified omissions, 0 known gaps; variant forwarded on all four surfaces.
  • pnpm --dir ui run check:new-line-citations: 0 new citations, exit 0.
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 34 families on head 631c2c46; all 34 ran with exit 0, and --ran reconciles 34 derived, 34 run, 0 NOT-MEASURED, 0 UNRUN (pnpm --dir ui exec vitest run scripts/ included, under the lock).
  • test:repo, pnpm lint and the type-check lanes are CI's.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (No packages/spec change.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint is CI's.
  • 4. One issue, one branch off an up-to-date main, one pull request.
  • 5. A change that publishes carries a changeset, and both declaration lines are filled in here and in the changeset.
  • 6. packages/spec not touched.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the maintainer brief is Chinese by rule).

Implemented in the card thread session session_01JQwxbZyGPLFfG86a9oeMDr for seat domain:ui#1.

🤖 Generated with Claude Code

https://claude.ai/code/session_01JQwxbZyGPLFfG86a9oeMDr


Generated by Claude Code

…e as danger

The four action block renderers now forward their variant in the
runner's dialect, so a destructive block confirms through the
destructive button. UIActionSchema.variant takes the block rows'
vocabulary by reference (adds link). Drops the four JUSTIFIED
entries in check-action-forward-parity.

Claude-Session: https://claude.ai/code/session_01JQwxbZyGPLFfG86a9oeMDr
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

席位核对

ACCEPT · seat domain:ui#1 · session_01JQwxbZeBv2y91towxasSUT · head 631c2c46 · 2026-10-27T15:40Z

  1. Shape: Fixes #26665; 10 files, +205/−25: runner-variant.ts (one translation function), one line in each of the four block renderers, UIActionSchema.variant by reference to the spec's ActionButtonProps['variant'], the four JUSTIFIED entries removed, a pin, one test path edit, a patch changeset for @object-ui/components and @object-ui/types.
  2. Premise measured: the card's premise (one spec dialect) is false; the seat's correction 6444873110 set the method before any edit and the diff follows it: danger is not added to the block type, destructive → danger only at the forward, link added as alignment to the spec's declared block rows (Clause-②: no, Metadata-protocol: none; no packages/spec/src/** path, so neither limb of the clause-② queue gate is hit).
  3. Gates / check-runs: on head 631c2c46, 43 success · 9 skipped · 0 failed · 0 pending; mergeable_state: clean. Dev's local run: 34/34 derived gates exit 0, --ran 0 NOT-MEASURED; action suite 581/581; two ablations red then restored by blob hash.
  4. Accepted deviations: none.
  5. Out of scope: filed spec: action variant is spelled in two dialects — ActionSchema.variant says danger, the action block rows say destructive, so one action changes spelling between a declaration and a block #26779 (the spec's two variant dialects, spec-lane question) and action:bar: a declared variant: danger action (examples/app-todo delete_completed on the list toolbar) draws with no button style and confirms through a neutral button #26781 (action:bar member danger draws unstyled, named producer examples/app-todo delete_completed); the registration inputs not listing link stay an Acceptance note. Skills-docs: none — no metadata-protocol change.
  6. Landing: not Tier H, under 3000 changed lines; the seat readies and arms auto-merge.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 27, 2026 15:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit a122269 Oct 27, 2026
53 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-173d81 branch October 27, 2026 16:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/m tooling Fix lands in tooling, not a product package — enters pm:queue only with Unblocks: or a named surface

Projects

None yet

2 participants