REQ-C-004: Destructive Commands Must Support --dry-run
Tier: Command Contract | Priority: P0
Source: §23 Side Effects & Destructive Operations
Addresses: Severity: Critical / Token Spend: Medium / Time: High / Context: Medium
Description
Every command with danger_level: "destructive" MUST implement a --dry-run mode. In dry-run mode, the command MUST perform all validation and computation but MUST NOT commit any irreversible change. The dry-run response MUST include effect: "would_delete" (or analogous would_* prefix), and MUST include a would_affect object describing what would be changed. The framework MUST register --dry-run as a standard flag for all destructive commands and MUST enforce that dry-run responses contain no actual mutations.
Acceptance Criteria
- A destructive command with
--dry-runnever modifies any external state - The dry-run response includes
effectwith a"would_"prefix - The dry-run response includes a
would_affectobject with human-readable and machine-readable impact description - The framework raises a registration error if a destructive command does not implement
--dry-run
Schema
Types: response-envelope.md · manifest-response.md
Dry-run responses use a would_*-prefixed effect value and a would_affect object. The --dry-run flag is declared in the command's flag schema.
{
"effect": {
"type": "string",
"pattern": "^would_",
"description": "Dry-run effect; always prefixed with 'would_' to distinguish from live execution"
},
"would_affect": {
"type": "object",
"description": "Machine-readable and human-readable description of what a live run would change"
}
}
Wire Format
$ tool delete-account --user 42 --dry-run
{
"ok": true,
"data": {
"effect": "would_delete",
"would_affect": {
"user": { "id": 42, "name": "Alice" },
"related_records": 234,
"reversible": false
}
},
"error": null,
"warnings": [],
"meta": { "duration_ms": 31 }
}
The --dry-run flag appears in --schema:
{
"flags": {
"dry-run": { "type": "boolean", "required": false, "default": false, "description": "Validate and preview impact without executing" }
}
}
Example
A destructive command must implement a dry_run path that returns affected scope without mutating state.
register command "delete-account":
danger_level: destructive
dry_run: supported
exit_codes:
SUCCESS(0): description: "Account deleted or dry-run completed", retryable: false, side_effects: complete
execute(args, dry_run=False):
user = fetch_user(args.user)
related = count_related_records(args.user)
if dry_run:
return response(
effect="would_delete",
would_affect={"user": user, "related_records": related, "reversible": False}
)
delete_user(args.user)
return response(effect="deleted")
Related
| Requirement | Tier | Relationship |
|---|---|---|
| REQ-C-002 | C | Provides: danger_level: destructive triggers --dry-run enforcement |
| REQ-C-003 | C | Extends: dry-run would_* effect values are the preview counterpart to live effect values |
| REQ-O-021 | O | Composes: --confirm-destructive is the companion flag that enables the live run after a dry-run preview |
| REQ-F-004 | F | Wraps: dry-run response uses ResponseEnvelope with ok: true |