Repository navigation
fix(platform-objects): one decision, one dialog — carry identity confirm questions on description (#7309) - #7827
Merged
Conversation
…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.
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 2 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
This was referenced Aug 11, 2026
os-zhuang
marked this pull request as ready for review
August 11, 2026 17:30
This was referenced Aug 11, 2026
This was referenced Aug 11, 2026
This was referenced Aug 11, 2026
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
confirmTextandparamsopened 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, andconfirmTextis dropped. 14 action sites converted, all inpackages/platform-objects/src/identity/:sys_userban_user,delete_my_account,disable_two_factor,generate_backup_codessys_oauth_applicationenable_oauth_application,disable_oauth_application,rotate_client_secret,delete_oauth_applicationsys_two_factordisable_two_factor,regenerate_backup_codessys_accountunlink_accountsys_organizationchange_slugsys_sso_providerdelete_sso_providersys_team_memberremove_team_memberEvery question moves verbatim — no warning reworded, none dropped.
confirmTextis 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_secretcollapses three dialogs to two: one param dialog (question +client_id), then the existing post-runresultDialogthat 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, requiringconfirmTextandparams: [at the same nesting level inside one object. 18 sites, not the 16-in-platform-objects the title and prose claim:packages/platform-objects/src/identity/**examples/app-showcase(bulkActionDefs)packages/plugins/plugin-approvalsThe 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-objectscount is 0.Schema probe — both nodes open, no fork
Re-run on current
main; the strictness controls prove the probe bites: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 againstBulkActionDefSchema, notActionSchema. Measured:BulkActionDefSchemarejectsdescription(unrecognized_keys) — the approvals:sys_approval_request.approval_reject/approval_recalldeclare bothconfirmTextandparams, so one decision opens two sequential dialogs #7278 remedy has no slot on this surface.paramsare "inputs collected once before the run",confirmTextis "shown above the affected-record summary", and arequiredparam "blocks the Confirm button" — params and confirm share one dialog, soconfirmText+paramsis the intended pairing here.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
ActionSchemaand must not fire onbulkActionDefs, 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-ESbundles move the same 14 leaves by hand.os i18n extracttreats a renamed key as a new gap and this repo extracts with--fill=default, which seeds English over curated translations — invisible tocheck: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
confirmTextis not enough; the risk is that a genuine warning quietly disappears. Reverse-verified — both go red: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")confirmTextalongsideparams→ 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 keepconfirmText.Measured gate results
pnpm buildpnpm linttypecheck(platform-objects)check:i18ncheck:i18n-coveragecheck:empty-changeset/no-major/adr-0087-registrationcheck:nul-bytesThe spec failure is
api-methods-batch-conformanceflaggingsys_api_key— a file this branch does not touch, and the diff contains zeroapiMethodsoccurrences. Pre-existing onmain, not introduced here.One measured finding, reported not fixed
actionLooksDestructive(packages/runtime/src/action-execution.ts:620) treatsconfirmTextas 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-deleteoauth actions) — they had nomode: 'delete'orvariant: 'danger'to fall back on.Latent, not live. Its only caller is
summarizeAction, reached only from MCPlistActions, which is gated twice over: it fails closed onsys_*objects (all 14 qualify) and requiresai.exposed === true(false on all 14). So nothing changes today. It is left unfixed deliberately — declaringai: { 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