feat(cli): destructive-command tiers — confirm + interactive-only deletes - #119
feat(cli): destructive-command tiers — confirm + interactive-only deletes#119joshdholtz wants to merge 6 commits into
Conversation
DX-948 Add evals verifying agents ask for human approval before destructive CLI operations
The CLI now gates destructive paywall operations behind Ask: build an eval suite that verifies agents pause for human approval before destructive operations (e.g.
Inspired by Railway's approach (they run evals to ensure agents ask for human approval before destructive actions). Context / discussion: https://revenuecat.slack.com/archives/C0AG3QP8PFA/p1786455572433679?thread_ts=1786440167.594769&cid=C0AG3QP8PFA Related: PWAI-352 (delete guard), WFL-386 (restore deleted paywalls), EXP-314 (protect experiment-linked paywalls). |
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 0db4c8a. Configure here.
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 1f25d1e. Configure here.
1f25d1e to
ac1d7fa
Compare
ac1d7fa to
0f5c6dd
Compare
|
bugbot run |
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit ec7953b. Configure here.
3fd6422 to
ec7953b
Compare
… (DX-948) Enumerate commands whose verb mutates or removes real state (delete/revoke/refund/cancel/transfer/grant/push/publish/unpublish/apply/ discard/simulate-purchase) and assert each routes through confirmOrAbort so a new unguarded destructive command fails CI. A static AST guard follows helper delegation (e.g. products store apply -> applyStoreStatePlan); a behavioral test drives real commands and proves each refuses under --no-input without --yes before any state-changing call, and that --yes lets it through. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…uard The verb list named grant and transfer but the behavioral test never drove them, so a regression that confirmed after mutating would pass CI. Both now run under --no-input and must hit the confirmation gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…d check The static AST enumeration looked thorough but had silent blind spots (variable Use strings, verb-list gaps, defeatable by refactoring) — false confidence. The behavioral test that drives each destructive command and asserts it refuses under --no-input without --yes (and --yes bypasses) is the honest guard; new destructive commands are covered by adding them to the driven list. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
apps/paywalls/offerings/entitlements delete now require a person at a real terminal: --json, --no-input, and non-TTY are refused, and --yes can't bypass it, so automation can't fire the deletes that are hardest to undo. Adds a shared requireInteractive gate next to confirmOrAbort and extends the destructive-guard test with a second tier: destructive commands still allow --yes, interactive-only commands refuse even with it. Extracts the paywall force-check so it stays unit-tested. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the interactive-only tier to products delete and packages delete — both irreversible like the other four — so no destructive delete is agent-drivable. Moves them out of the --yes-bypass list and into the interactive-only guard; switches the yes-bypass test to webhooks delete. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CanPrompt is also false in a plain non-TTY session with no flags, but the gate error only named --json/--no-input. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1e448bc to
9fc277a
Compare
|
bugbot run |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit 9fc277a. Configure here.
Two tiers for destructive commands, with behavioral guards for both:
Destructive (products push, publish/unpublish, webhooks delete, refunds, subscriptions cancel, store discard, customer grant/revoke/transfer): refuse under
--no-inputwithout--yes;--yeslets them through. A fake API that only allows GET, so nothing mutates before consent.Interactive-only (apps / paywalls / offerings / entitlements / products / packages delete): must be run by a person in a terminal.
--jsonand--no-inputare refused, and--yescan't bypass it. These are irreversible, so we don't want agents firing them — manual, deliberate use only.A shared
requireInteractivegate sits next toconfirmOrAbort. The guard test has one list per tier; a new human-only command is a gate line plus a list entry. The paywall force-check was extracted so it stays unit-tested now that the command itself is interactive-only.🤖 Generated with Claude Code
Note
Medium Risk
Changes consent and automation paths for destructive CLI commands, including a breaking restriction that scripts can no longer delete apps, paywalls, offerings, entitlements, products, or packages.
Overview
Splits destructive CLI actions into two consent tiers so irreversible catalog deletes cannot be fired by automation.
Interactive-only (
apps/paywalls/offerings/entitlements/products/packagesdelete): newrequireInteractiverefuses--json,--no-input, and non-TTY sessions.--yesstill skips the prompt only in a real terminal.Confirm-with-
--yes: other mutating commands (push, publish/unpublish, webhooks delete, refunds, cancel, store discard, customer grant/revoke/transfer) still refuse--no-inputwithout--yes.Adds a guard test that drives the real commands against a GET-only fake API, and extracts
checkPaywallDeletableso the--forcerule stays unit-tested now that paywall delete is interactive-only.Reviewed by Cursor Bugbot for commit 9fc277a. Bugbot is set up for automated code reviews on this repo. Configure here.