Repository navigation
feat!: add a destructive marker to #[Command] and refuse marked commands in mcp - #524
Merged
Merged
Conversation
…ds in mcp #[Command] takes a new `destructive: bool = false` argument. It is copied onto CommandDefinition (and the discovery cache, whose format version goes to 4) so any caller can tell a state-changing command from a read-only one without guessing from its name or options. Every shipped command that changes or deletes stored state is now marked: db:migrate, db:reset, db:rollback, db:rebuild, db:seed, cache:clear, page-cache:clear, page-cache:purge, queue:clear, log:clear, session:gc, auth:clear-tokens, discovery:clear, indexer:rebuild, admin-auth:permissions:sync and devai:install. The MCP run_console_command tool now refuses commands marked destructive unless mcp.console.allow_destructive is on, instead of inferring destructiveness from a declared `force` flag. That closes the gap where cache:clear, queue:clear, log:clear and page-cache:clear were guarded only by the allowlist. --force stays the CLI confirmation mechanism via DestructiveCommandGuard. A repo-level test fails when a shipped command named *:clear, *:reset, *:rollback, *:rebuild, *:fresh, *:seed, *:truncate, *:purge or *:clear-tokens, or one that depends on DestructiveCommandGuard, lacks the marker. Fixes #516 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyjR4cJTfro54iFZ9CWkv1
1 task done
etienne-scherrer
pushed a commit
to etienne-org/marko
that referenced
this pull request
Oct 7, 2026
…and marker marko-php#524 made run_console_command read #[Command(destructive: true)] instead of inferring from a force flag; the config comment still described the old rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KyjR4cJTfro54iFZ9CWkv1
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.
Summary
#[Command]takes a newdestructive: bool = falseargument (picked over a separate#[Destructive]attribute so the command's whole contract lives in one attribute and flows through the existingCommandDefinition/discovery pipeline). It is exposed asCommandDefinition::$destructiveand stored in the discovery cache.db:migrate,db:reset,db:rollback,db:rebuild,db:seed,cache:clear,page-cache:clear,page-cache:purge,queue:clear,log:clear,session:gc,auth:clear-tokens,discovery:clear,indexer:rebuild,admin-auth:permissions:sync,devai:install. (db:migrate,devai:installandadmin-auth:permissions:syncwere already treated as destructive by MCP because they declareforce; marking them keeps that.)run_console_commandreads$definition->destructiveinstead of looking for aforceflag, socache:clear,queue:clear,log:clear,page-cache:clear... now also needmcp.console.allow_destructive.--forcestays the CLI confirmation mechanism.DestructiveCommandGuarditself never inferred anything from option names (it is called explicitly by each command), so its runtime behaviour is unchanged. Its docblock now requires guarded commands to carry the marker, and the repo test enforces that for shipped commands.tests/DestructiveCommandMarkerTest.phpreflects every#[Command]class underpackages/*/srcand fails when a command named*:clear,*:reset,*:rollback,*:rebuild,*:fresh,*:seed,*:truncate,*:purgeor*:clear-tokens, or one that depends onDestructiveCommandGuard, lacks the marker.Breaking
forceflag (e.g.cache:clear) inmcp.console.allowed_commandsnow gets it refused. Escape hatch: setmcp.console.allow_destructivetotrue(MCP_ALLOW_DESTRUCTIVE=true). That enables only destructive commands that are also allowlisted.DiscoveryCache::CACHE_VERSIONgoes from 3 to 4 because command records gain adestructivefield. A cache compiled by an older version fails boot with the usual version-mismatchDiscoveryCacheException. Runmarko discovery:cacheon deploy (already the documented step).Test plan
tests/DestructiveCommandMarkerTest.php: suffix rule, guard-dependency rule, and read-only commands stay unmarked (red before the commands were marked)RunConsoleCommandToolTest: refuses a destructive command with no force flag (cache:clear), refuses one reached through an alias, runs a force-flag command that is not marked destructive, existing allow_destructive casescomposer test,composer cs:check,composer phpstangreen locallyFixes #516
🤖 Generated with Claude Code
https://claude.ai/code/session_01KyjR4cJTfro54iFZ9CWkv1