Skip to content

fix(platform-objects): one decision, one dialog — carry identity confirm questions on description (#7309) - #7827

Merged
huangyiirene merged 2 commits into
mainfrom
claude/issue-7309-confirmtext-params
Aug 11, 2026
Merged

huangyiirene merged 2 commits into
mainfrom
claude/issue-7309-confirmtext-params

Conversation

@claude

@claude claude Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Closes #7309 (partially — see Scope split below; the showcase 4 are a different surface and are deliberately not touched).

What changed

The console action runner chains confirmation then param collection, both awaited, so an action declaring confirmText and params opened two sequential dialogs for one click — nothing sent until the second, while the first already read as "the action ran".

Following the maintainer's 2026-08-10 ruling on #7278 (shipped in PR #7592): the confirm question moves to the action's top-level description (#7367), which the param dialog renders under its title, and confirmText is dropped. 14 action sites converted, all in packages/platform-objects/src/identity/:

object actions
sys_user ban_user, delete_my_account, disable_two_factor, generate_backup_codes
sys_oauth_application enable_oauth_application, disable_oauth_application, rotate_client_secret, delete_oauth_application
sys_two_factor disable_two_factor, regenerate_backup_codes
sys_account unlink_account
sys_organization change_slug
sys_sso_provider delete_sso_provider
sys_team_member remove_team_member

Every question moves verbatim — no warning reworded, none dropped. confirmText is left in place on param-LESS actions (delete_organization, leave_organization, impersonate_user), where the confirm is the only dialog and stripping it would delete the user's only warning.

rotate_client_secret collapses three dialogs to two: one param dialog (question + client_id), then the existing post-run resultDialog that reveals the new secret. That reveal is output shown once after the rotation, not a second pre-run decision, so it stays.

Census — the card's prose was wrong, its table was right

Re-derived on current main (098b629) with a brace-depth block parse over non-test *.ts, requiring confirmText and params: [ at the same nesting level inside one object. 18 sites, not the 16-in-platform-objects the title and prose claim:

area measured
packages/platform-objects/src/identity/** 14
examples/app-showcase (bulkActionDefs) 4
packages/plugins/plugin-approvals 0 — #7278 landed

The card's table (14) is correct; its prose (16) overcounts by 2. The card's own arithmetic confirms it: the stated total of 20 equals 14 + 4 + 2, not 16 + 4 + 2 (= 22). After this PR the platform-objects count is 0.

Schema probe — both nodes open, no fork

Re-run on current main; the strictness controls prove the probe bites:

ActionSchema: action.description                => success=true
ActionSchema: description + params              => success=true
Translation:  _actions.{action}.description     => success=true
Translation:  globalActions.{action}.description => success=true
Translation:  bogus key (control)               => success=false  unrecognized_keys
ActionSchema: bogus key (control)               => success=false  unrecognized_keys

The translation node — flagged unverified on the card — is closed. Translated confirm questions are authorable, so no translation-side workaround was needed or invented.

Scope split: the showcase 4 are a different surface

Not touched, and not a diff-size decision. The 4 showcase sites are bulkActionDefs, which validate against BulkActionDefSchema, not ActionSchema. Measured:

Applying the remedy would therefore delete a real warning from a mass-mutation surface and gain nothing. This has a consequence for #7428: its authoring-time guard must be scoped to ActionSchema and must not fire on bulkActionDefs, or it lands red on 4 correct sites. Flagging for sequencing rather than widening this card.

Translation carryover

The en / zh-CN / ja-JP / es-ES bundles move the same 14 leaves by hand. os i18n extract treats a renamed key as a new gap and this repo extracts with --fill=default, which seeds English over curated translations — invisible to check:i18n, whose fresh extract would agree with the English it just wrote. Same trap #7278 hit; pinned the same way.

Tests — the consequence, in both directions

A grep for the absence of confirmText is not enough; the risk is that a genuine warning quietly disappears. Reverse-verified — both go red:

  • deleting ban_user's warning outright → red ("the confirm question was dropped, not moved — the user now gets a param dialog with no warning at all")
  • restoring confirmText alongside params → red (the two-dialog pairing)

Destructive wordings are pinned phrase-by-phrase on ban_user, delete_my_account, rotate_client_secret, delete_oauth_application. Over-application is pinned too: the param-less neighbours must keep confirmText.

Measured gate results

gate result
pnpm build exit 0
pnpm lint clean
typecheck (platform-objects) clean
check:i18n 9 packages, all bundles in sync
check:i18n-coverage 12 configs, 660 baselined, none new
check:empty-changeset / no-major / adr-0087-registration pass
check:nul-bytes 7139 files scanned, OK
platform-objects tests 339 passed (15 files)
cli tests 1182 passed (109 files)
spec tests 9947 passed, 1 pre-existing failure

The spec failure is api-methods-batch-conformance flagging sys_api_key — a file this branch does not touch, and the diff contains zero apiMethods occurrences. Pre-existing on main, not introduced here.

One measured finding, reported not fixed

actionLooksDestructive (packages/runtime/src/action-execution.ts:620) treats confirmText as a destructiveness signal. Dropping the key flips 6 of 14 actions from "destructive" to not (generate_backup_codes, regenerate_backup_codes, change_slug, and the three non-delete oauth actions) — they had no mode: 'delete' or variant: 'danger' to fall back on.

Latent, not live. Its only caller is summarizeAction, reached only from MCP listActions, which is gated twice over: it fails closed on sys_* objects (all 14 qualify) and requires ai.exposed === true (false on all 14). So nothing changes today. It is left unfixed deliberately — declaring ai: { requiresConfirmation: true } on non-exposed system actions would add dead metadata to an unreachable path. Worth its own card: a heuristic keyed off UI dialog copy now has one fewer signal.

🤖 Generated with Claude Code

https://claude.ai/code/session_01YbeQws2wekpZRPEGs1HKWt


Generated by Claude Code

…irm questions on `description` (#7309)

The console action runner chains confirmation THEN param collection, both
awaited, so an action declaring `confirmText` *and* `params` opened two
sequential dialogs for one click — nothing sent until the second, while the
first already read as "the action ran".

Follows the maintainer's 2026-08-10 ruling on #7278 (shipped in PR #7592):
carry the confirm question on the action's top-level `description` (#7367) and
drop `confirmText`. Sweeps the 14 remaining in-repo action sites, all in
`identity/`. Each question moves verbatim — no warning reworded, none dropped.

`confirmText` is left in place where it is still correct: a param-LESS action
has no param dialog to fold into, so the confirm is its only dialog.

The en/zh-CN/ja-JP/es-ES bundles move the same 14 leaves by hand, because
`os i18n extract --fill=default` would seed English over the curated
translations for a renamed key — invisible to `check:i18n`.

Tests pin the consequence in both directions: params ⇒ one dialog, and the
question is still shown. Deleting a warning instead of moving it goes red.
@vercel

vercel Bot commented Aug 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
objectstack Ignored Ignored Aug 11, 2026 6:20pm

Request Review

@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/platform-objects.

2 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:

  • content/docs/plugins/packages.mdx (via @objectstack/platform-objects)
  • content/docs/ui/setup-app.mdx (via @objectstack/platform-objects)

Advisory only. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs origin/main → pass the list as args.docs.

@huangyiirene
huangyiirene added this pull request to the merge queue Aug 11, 2026
Merged via the queue into main with commit 098f4bb Aug 11, 2026
26 checks passed
@huangyiirene
huangyiirene deleted the claude/issue-7309-confirmtext-params branch August 11, 2026 20:09
os-zhuang pushed a commit that referenced this pull request Aug 12, 2026
`content/docs/protocol/objectui/actions.mdx` showed `disable_oauth_application`
and `rotate_client_secret` pairing `confirmText` with `params`. Both were
already stale — the real actions moved the question to `description` in #7827 —
and the guard in this PR makes the documented shape fail to parse.

They are labelled "Real-World Examples", so they now mirror what the platform
objects actually ship, and the `confirmText` bullet above them states the
constraint: param-LESS actions only, question on `description` otherwise, not
`ai.description`, and bulk defs unaffected.

Found via the PR's docs-drift list; the brace-balanced census could not see
these because the examples are YAML.
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 17, 2026
… scoped by schema boundary (objectstack-ai#7972)

* feat(spec): refuse `confirmText` + non-empty `params` on ActionSchema

An action declaring both opens TWO sequential dialogs for one decision —
the console runner awaits the confirm, then the param prompt, so the first
already reads as "the action ran" while nothing has been sent.

objectstack-ai#7278 and objectstack-ai#7309 repaired the 16 shipped sites (PRs objectstack-ai#7592, objectstack-ai#7827); neither
stops the next one being written. This is the structural half: a refusal on
ActionSchema whose message names both keys and points the confirm question
at the action's top-level `description`.

Scoped by schema boundary, not by a param-optionality heuristic. The pair is
INTENDED on `BulkActionDefSchema`, where params and confirm render one dialog
(a `required` param blocks that dialog's own Confirm button), so the four
`examples/app-showcase` bulk defs are correct as written. The refusal lives on
ActionSchema's refine chain and is structurally incapable of reaching either
`BulkActionDefSchema` or `InlineActionSchema`; both directions are pinned.

* docs(spec): declare the confirmText/params refusal on the key, + changeset

The `confirmText` describe() now names the refusal, so an author meets the
constraint in the generated reference rather than only in the error. Worded
"on a registered action" deliberately: the same field factory renders the
InlineAction reference table, where the guard does not apply — an unqualified
claim there would be a documented refusal that does not exist.

Regenerates content/docs/references/ui/action.mdx (gen:docs).

* docs(objectui): two action examples taught metadata the spec now refuses

`content/docs/protocol/objectui/actions.mdx` showed `disable_oauth_application`
and `rotate_client_secret` pairing `confirmText` with `params`. Both were
already stale — the real actions moved the question to `description` in objectstack-ai#7827 —
and the guard in this PR makes the documented shape fail to parse.

They are labelled "Real-World Examples", so they now mirror what the platform
objects actually ship, and the `confirmText` bullet above them states the
constraint: param-LESS actions only, question on `description` otherwise, not
`ai.description`, and bulk defs unaffected.

Found via the PR's docs-drift list; the brace-balanced census could not see
these because the examples are YAML.

---------

Co-authored-by: Claude <noreply@anthropic.com>
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Aug 17, 2026
… only (objectstack-ai#8128)

* fix(runtime): actionLooksDestructive classifies on declared semantics only

Drops the confirmText leg from actionLooksDestructive (packages/runtime/src/
action-execution.ts). mode === 'delete' || variant === 'danger' remain the
signal -- closed, declared enumerations an author sets on purpose, not UI
dialog copy a heuristic was never meant to read as an AI-facing safety
property.

confirmText is being withdrawn by design: objectstack-ai#7278/objectstack-ai#7309 moved identity-object
confirm questions onto `description` instead, and measured on objectstack-ai#7309's merged
branch (PR objectstack-ai#7827), 6 of its 14 migrated actions flipped from destructive to
not-destructive the moment their confirmText was dropped, because none of
them declares mode:'delete' or variant:'danger' to fall back on.

Maintainer ruling: issue objectstack-ai#7828, comment 5265943521 (Option A adopted).

Fixes objectstack-ai#7828

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3Kurx8qufrDzNjk4rag7V

* test(runtime): resolve @objectstack/platform-objects to source in vitest

action-execution-destructive.test.ts reads the REAL sys_* identity
declarations to prove today's platform objects are excluded before
actionLooksDestructive ever runs on them. That import resolved through
`exports` to platform-objects/dist -- a build artifact -- so all 66 pins
were a verdict about build state rather than about the declarations in
the checkout. `pnpm check:test-source-alias` (objectstack-ai#7668/objectstack-ai#7778) reported it as
a NEW unaliased artifact import on @objectstack/runtime.

Aliases platform-objects to source in packages/runtime/vitest.config.ts.
resolve.alias becomes the ARRAY form because only that form accepts a
RegExp find; the pre-existing string entries keep the prefix-match
semantics they had as object keys (Vite normalizes an alias object into
exactly this list, in this order), so no other resolution changes.

The new entries are ANCHORED, one rule for every namespace rather than an
enumeration of the ones reached today -- the PR objectstack-ai#7778 constraint, same
shape as @objectstack/spec in packages/qa/downstream-contract (PR objectstack-ai#8129).
`/plugin` is listed ahead of the namespace rule because it is the one
exported subpath that is a FILE (src/plugin.ts) and not a directory.

The registry entry in scripts/check-test-source-alias.mjs is untouched.

Measured, both directions:

  - artifact-resolved (before): 66 passed
  - source-resolved (after):    66 passed
  - per-test diff of the two verbose runs: IDENTICAL, name for name.
    The 14-action pins read `type`/`ai.exposed` off the imported objects
    through actionByName(), which throws when an action is missing, so
    an identical name+verdict set means source and dist agree on every
    declaration these pins touch. No pin changed verdict; none modified.

Reverse verification (the alias is live, not decorative): with
sys_user.ban_user's `type` flipped 'api' -> 'script' in SOURCE only and
no rebuild, the suite reports 1 failed / 65 passed --
`expected 'script' to be 'api'` at :309. dist/identity/index.mjs:81 still
carries `type: "api"`, i.e. the identical tree read green through the
pre-alias config. Injection reverted; no test was weakened.

Part of objectstack-ai#7828

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B3Kurx8qufrDzNjk4rag7V

---------

Co-authored-by: Claude <noreply@anthropic.com>
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/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

platform-objects: 16 more actions declare both confirmText and params, so one click opens two sequential dialogs (same shape as #7278)

3 participants