Skip to content

feat(console): an action's confirm and param dialogs name their dismiss button (cancelText drawn in place of Cancel) - #28983

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-ewid2r
Nov 2, 2026
Merged

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

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Steedos · project thread

Fixes #28879
Refs #28519

Clause-②: yes (widening)
Metadata-protocol: none

File surface: the card's Wanted 1-4 list (claim 6568809663), plus two mechanical consequences the seat added in amendment 6568954960 (ui/packages/types/src/ui-action.ts, and the one RecordDetailView useObjectLabel() destructure line outside paramCollectionHandler).

维护者速读

  • 改了什么:动作弹出的确认框和参数框,其「取消」按钮现在显示动作自己声明的 cancelText;没声明时仍是「Cancel」。翻译包里的 _actions.ACTION.cancelText 优先,其次是作者写的多语言文本,最后是作者写的字符串。设置 → 邀请里的「Cancel Invitation」确认框,取消按钮现在写「Keep invitation」(中/日/西已翻译)。Studio 的动作表单可以填这个键,os i18n extract 会为带 confirmText 或参数的动作生成它的翻译位。
  • 为什么改:cancelText 已在 feat(spec): an action's confirm and param dialogs can name their dismiss button — cancelText #28859 写进 spec,但控制台一直画「Cancel」;动作本身就叫「取消……」时,两个按钮分不清哪个是保留。
  • 风险与回滚:只改按钮文字,确认与取消的逻辑不变;没写 cancelText 的动作完全不变。回滚即 revert 本 PR。
  • 先看:ui/packages/core/src/actions/ActionRunner.ts 的 confirmOptions(把 cancelText 交给确认框);ui/packages/react/src/hooks/useActionTextLocalizer.ts 新增的 cancelText 解析;packages/spec/liveness/action.json 的 cancelText 行。
  • 两分钟验证:pnpm --dir ui exec vitest run packages/core/src/actions/__tests__/ActionRunner.confirmCancelText-28879.test.ts packages/app-shell/src/views/ActionConfirmDialog.cancelText-28879.test.tsx packages/app-shell/src/views/ActionParamDialog.cancelText-28879.test.tsx,预期 3 个文件全绿。
  • 需要你做的:无,席位落地。

What changes

The confirm path. ActionDef declares cancelText?: SpecActionInput['cancelText'] (derived) and ACTION_DEF_KEYS lists it. useActionTextLocalizer resolves it only when the action declares one: the authored I18nLabel is collapsed to the active language, then offered as the fallback to useObjectLabel().actionCancelText, so the bundle's objects.OBJECT._actions.ACTION.cancelText (globalActions.ACTION.cancelText for an object-less action) wins. ActionRunner#confirmOptions hands the resolved non-empty string to the confirm handler as options.cancelText, which ActionConfirmDialog already draws over actionConfirm.cancel. The runner has no language, so it passes on a string only; a surface that skips the localizer keeps the host's "Cancel".

The param path. ParamDialogState gains cancelText?: string. Both param-collection handlers (useConsoleActionRuntime#paramCollectionHandler, RecordDetailView#paramCollectionHandler) resolve it exactly as they resolve submitText, and ActionParamDialog draws it on the dismiss button, actionDialog.cancel when absent or empty.

The resolver. @object-ui/i18n useObjectLabel().actionCancelText, the sibling of actionSubmitText on the same actionSuffixes node rule (a bound action never reads globalActions). The member-count pin in useObjectLabel-identity-5564.test.tsx goes from 29 to 30.

Types and forwarding. UIActionSchema.cancelText?: SpecAction['cancelText'] (amendment 6568954960). action:button, action:icon, action:group and action:menu forward cancelText to the runner; action-forward-precedence.test.tsx lists it after submitText.

os i18n extract (pushActionCancelText). Scaffolds cancelText under an action's translation root for an action that declares confirmText or a non-empty params, the population translateAction overlays it onto, and no slot for any other action.

First consumer. sys_invitation's cancel_invitation declares cancelText: 'Keep invitation'. The platform-objects bundles regenerate (node scripts/check-i18n-bundles.mjs --write); zh-CN 保留邀请, ja-JP 招待を保持, es-ES Conservar invitación are hand-translated, as is the new Studio action-form row.

Spec rows. liveness/action.json cancelText flips planned → live with the read points above and drops authorWarn / authorHint; state-counts/action.md regenerates. The action / cancelText omit row leaves metadata-form-zod-reconciliation.test.ts, and action.form.ts gains a plain cancelText row ("Cancel button label"), not gated, because the parse refuses the key on an action that opens neither dialog. "(declared ahead of its console reader: no renderer shows it yet)" leaves the ActionSchema.cancelText describe and "no renderer shows it yet" leaves the translation slot's; content/docs/references/** regenerates. In actions.mdx both "The console does not draw it yet" sentences flip, the cancelText one and the stale submitText one.

Why

ActionSchema.cancelText landed spec-first in #28859 with no reader, so an author who wrote it still saw "Cancel". On Setup → Invitations, Cancel Invitation asked with two buttons reading Cancel and Cancel Invitation.

Tests

  • ActionRunner.confirmCancelText-28879.test.ts (5): the bag carries cancelText beside the label; a label-less action still hands it on; control, no key without it; '' hands nothing; dismissing stops the run.
  • ActionConfirmDialog.cancelText-28879.test.tsx (3) and ActionParamDialog.cancelText-28879.test.tsx (4): the label is drawn, the fallback is Cancel (also for '' on the param dialog), and the labelled button dismisses.
  • useActionTextLocalizer.cancelText-28879.test.tsx (6): authored string, bundle first, inline map collapsed, the global node for an object-less action, nothing for an undeclared key even with a bundle entry, a nameless action's map collapsed.
  • useConsoleActionRuntime.cancelText-28879.test.tsx (5) and RecordDetailView.paramCancelText-28879.test.tsx (5), one per param reader.
  • useObjectLabel-actionCancelText-28879.test.tsx (5): the object node, the global node, no global read for a bound action (with a control), the miss answers.
  • RecordDetailView.paramRuntimeParity-6431.test.tsx: cancelText joins DIALOG_READS, and the two runtimes write the same value.
  • packages/cli/test/i18n-extract-action-cancel-text.test.ts (7): top-level, global and inline actions; the seed-less slot; no slot without confirmText or params; bundle seed and coverage gating; one fixture through ActionSchema → extract → ObjectTranslationDataSchema.
  • Sibling pins that mock useObjectLabel partially gain an actionCancelText passthrough (seven useConsoleActionRuntime.* files), and object-lifecycle-panel-echo-decisions.test.ts counts the new action-form row (698 → 699).

Acceptance notes

  • Two lines outside the claim's first file surface, both added by amendment 6568954960: ui/packages/types/src/ui-action.ts (the renderers' schema.cancelText does not type-check without it), and in RecordDetailView.tsx the useObjectLabel() destructure gains actionCancelText (the handler cannot call the hook). Nothing else in RecordDetailView.tsx moves.
  • cancelText is not interpolated: it is a button label, like submitText.
  • Wanted 5 (inline actions) and the two skills files are out of scope, as the card says.
  • metadata-admin/MetadataTypeActions.tsx, a third ActionParamDialog consumer (Setup metadata-type actions), passes neither submitText nor cancelText, so its buttons keep Confirm / Cancel. The card names the two business-action handlers only. Noted, not filed.

Verification

Readings at head f566aa2e (after merging origin/main at 00d0668d) unless a line says otherwise.

Derived gates: 112 derived, 111 run exit 0, 1 NOT MEASURED
  • node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 112 families at e389f7f7 (the pre-merge head; the merge brought nothing that overlaps the diff). Each ran with stdin closed and its exit captured before any pipe.
  • dispatch-gates.mjs --ran: 112 accounted, 111 run, 1 NOT-MEASURED: pnpm check:dual-build-cjs-loads exit 3 (PREREQUISITE NOT MET, needs a whole-workspace build), left to CI.
  • check:skill-examples and check:i18n-coverage first exited 3 on unbuilt prerequisites; after building client-react and the i18n-coverage closure both exit 0 (check-i18n-coverage: OK (14 config(s), 426 baselined untranslated string(s), none new)).
  • At f566aa2e: node scripts/check-sdui-manifest.mjs exit 0; node scripts/check-i18n-bundles.mjs exit 0; pnpm --filter @objectstack/spec check:generated "All 14 generated artifacts are up to date" after a spec rebuild; ui check:new-line-citations 0 new; check-nul-bytes exit 0.
Tests
  • ui, at f566aa2e: the 18 new or touched test files plus RecordDetailView.childWriteRefreshesParent-12244.test.tsx (the one overlap with main) → 19 files, 100 passed.
  • ui, at e389f7f7: packages/core/src/actions/, packages/i18n/, packages/components/src/renderers/action/, every useActionTextLocalizer*, useConsoleActionRuntime*, ActionParamDialog*, ActionConfirmDialog* and RecordDetailView* test → 330 files, 3779 passed, 13 skipped. packages/types/ → 386 files, 10456 passed. pnpm --dir ui exec vitest run scripts/ → 169 passed, 2 skipped, exit 0 (includes check:action-forward-parity).
  • ui type-check (turbo, @object-ui/app-shell, components, core, i18n, react, types) → 70/70 tasks, exit 0.
  • spec: vitest run src/ui/ src/system/ → 238 files, 8155 passed; scripts/liveness/ → 12 files, 359 passed; typecheck exit 0.
  • cli: vitest run --project unit test/i18n-extract → 12 files, 96 passed; typecheck exit 0. The integration tier is declared to CI.
  • platform-objects: test → 108 files, 1503 passed; typecheck exit 0.
  • Ablation (scripts/ablation-replace.mjs, committed first): deleting the cancelText spread in confirmOptions reds ActionRunner.confirmCancelText-28879 (2 failed | 3 passed); replacing state.cancelText || t('actionDialog.cancel') with t('actionDialog.cancel') reds ActionParamDialog.cancelText-28879 (2 failed | 2 passed). Both restored, blob == HEAD, git diff HEAD empty.

Human Minimum

  • 1. Schemas start as Zod; packages/spec holds no runtime logic. (Only a describe, a form row, a liveness row and a test ledger row change there.)
  • 2. Every name I added uses the casing for its kind.
  • 3. No formatter ran; pnpm lint is CI's run.
  • 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 touched: its generated artifacts are regenerated and committed.
  • 7. No hand edits to content/docs/references/, packages/*/CHANGELOG.md or content/docs/releases/.
  • 8. Title, body and comments are in English (the 维护者速读 section is the Chinese summary the repo asks for).

🤖 Generated with Claude Code

https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg


Generated by Claude Code

…ext on the dismiss button

Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg
Co-Authored-By: Claude <noreply@anthropic.com>
…s bundles for cancelText; changeset

Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg
Co-Authored-By: Claude <noreply@anthropic.com>
…unts the action form's cancelText row

Claude-Session: https://claude.ai/code/session_01QsR2jQtTxhjJ5tQUg79qSg
Co-Authored-By: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/cli, @objectstack/platform-objects, @objectstack/spec, touching 6 documentable anchor(s). ⚠️ 2 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/state-counts/action.md), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

6 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/automation/hooks.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/deployment/self-hosting.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/frontend/plugins/plugin-view.mdx (via cancelText (literal, a string literal in pushActionCancelText; a string literal in sections))
  • content/docs/permissions/system-context.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/protocol/objectui/actions.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects), cancelText (literal, a string literal in pushActionCancelText; a string literal in sections))
  • content/docs/ui/actions.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))

⛔ 3 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17/17-3.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/releases/v18/18-1.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))
  • content/docs/releases/v18/18-2.mdx (via sys_invitation (symbol, a field of const object enObjects; a field of const object esESObjects; a field of const object jaJPObjects; a field of const object zhCNObjects))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 2 changed file(s) yielded no anchor (packages/spec/liveness/action.json, packages/spec/liveness/state-counts/action.md) — pages documenting those are invisible to this run
  • 3 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 60 of 216 client-bound route-ledger rows — the other 156 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 156: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 101 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 177 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 00d0668d380046198dfc9151858b88df463bfd9b → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 6bd81c6aeb45ce93617c163f60d4112426cd1ac3 — the merge of head f566aa2e1c0e895ca3b7d23a2de9626244c2915a into base 00d0668d380046198dfc9151858b88df463bfd9b, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 6bd81c6aeb45ce93617c163f60d4112426cd1ac3 && git checkout 6bd81c6aeb45ce93617c163f60d4112426cd1ac3
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 00d0668d380046198dfc9151858b88df463bfd9b f566aa2e1c0e895ca3b7d23a2de9626244c2915a && git checkout -B drift-repro 00d0668d380046198dfc9151858b88df463bfd9b && git merge --no-ff f566aa2e1c0e895ca3b7d23a2de9626244c2915a

node scripts/docs-audit/affected-docs.mjs --json 00d0668d380046198dfc9151858b88df463bfd9b

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 00d0668d380046198dfc9151858b88df463bfd9b → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:ui#1 · zhuangjianguo · session_01QsR2jUJ19MstFvFhjgiYTu

席位核对 · ACCEPT · 2026-11-02T06:13Z

Head-sha: f566aa2e1c0e895ca3b7d23a2de9626244c2915a

  • Shape:

    • The PR is a draft against main. The first card line is Fixes #28879, and no other card sits beside a closing keyword.
    • 54 files, +1171 / −54, of which 21 are tests. No governed path: check-governed-merges --pr 28983 reports NOT governed, and skills/** is untouched.
    • Clause-②: yes (widening) and Metadata-protocol: none start their lines in the body and the changeset, as claimed (6568809663, amendment 6568954960). The changeset is patch.
  • Read in the diff:

    • The confirm dialog and the param dialog both draw the action's cancelText on the dismiss button, and fall back to "Cancel".
    • The localizer resolves cancelText declared-only, bundle first, and the runner hands it as options.cancelText. Both param handlers resolve it the way they resolve submitText. ActionParamDialog draws it, and the four renderers forward it.
    • UIActionSchema.cancelText derives from the spec (amendment 6568954960).
    • In RecordDetailView, the diff touches only the useObjectLabel() destructure and paramCollectionHandler. It merges cleanly with fix(plugin-detail): the record meta line leaves the owner out only when the strip the page drew carries it #28994 (queued), which edits the same file elsewhere.
    • The extractor scaffolds cancelText beside a declared confirmText or non-empty params, with a test.
    • sys_invitation.cancel_invitation declares cancelText: 'Keep invitation', with zh/ja/es translations regenerated. The platform-objects catalog pin moves from 698 to 699.
    • Liveness and docs flips:
      • The spec liveness row goes from planned to live.
      • The reconciliation omit row is deleted, and action.form.ts offers the key.
      • The "no renderer shows it yet" describes are dropped.
      • The references are regenerated, and both actions.mdx sentences flip, including the stale submitText one.
  • Gates (check-runs on this head): 58 success, 7 skipped, 0 failed or pending. mergeable_state is clean. origin/main 00d0668d was merged in cleanly.

  • Tests (dev):

    Suite Result
    19 new or touched ui files 100 passed
    Touched-module ui sweep 330 files / 3779 tests
    ui types 10456
    spec ui and system 8155
    cli i18n-extract 96
    platform-objects 1503

    Two ablations went red and were restored. Type-check is clean across ui, cli, spec and platform-objects. Derived gates: 111 of 112 exit 0, and check:dual-build-cjs-loads is left to CI's build.

  • Not here: Wanted 5 (inline actions). MetadataTypeActions.tsx passes neither submitText nor cancelText; that is pre-existing and noted in the PR's Acceptance notes. The skills docs are a separate Tier H card.

  • NOT MEASURED: the seat ran no live session.

  • Landing: ordinary queue landing, pr_ready then automerge_enable. Fixes #28879 closes the card on merge.

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review November 2, 2026 06:14
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge November 2, 2026 06:14
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Nov 2, 2026
Merged via the queue into main with commit 462b4f9 Nov 2, 2026
66 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-ewid2r branch November 2, 2026 06:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Action confirm and param dialogs: draw the action's cancelText on the dismiss button, falling back to Cancel; sys_invitation declares "Keep invitation"

1 participant