Skip to content

75 critical safe default execution

Part III: Security | Challenge §75

75. Safe-Default Execution Mode Absent

Severity: Critical | Frequency: Situational | Detectability: Hard | Token Spend: Low | Time: Critical | Context: Low

The Problem

High-stakes commands (trading, infrastructure provisioning, mass data deletion) execute immediately with real side effects. Agents have no way to distinguish a preview invocation from a live one without an explicit --dry-run flag — which must be remembered at every callsite.

This is distinct from §23: --dry-run may be available (REQ-C-004), but its absence is a silent live run, not a safe default. An agent that omits --dry-run causes real impact with no warning.

The unsafe default:

$ trade execute --symbol BTC --amount 10000
# Immediately places a real order. No preview. No opt-in required.

The confirmation-gate pattern (REQ-O-021) is also insufficient for this case:

$ trade execute --symbol BTC --amount 10000
# Exits 2: CONFIRMATION_REQUIRED — agent must catch error and retry

The agent enters error-recovery flow instead of a natural preview → commit workflow.

The gap: no canonical way to say "preview unless told otherwise":

$ trade execute --symbol BTC --amount 10000         # preview by default, exit 0
$ trade execute --symbol BTC --amount 10000 --live  # opt into real execution

Impact

  • Agent places a real trade while intending to preview scope and cost
  • Unrecoverable financial or infrastructure damage occurs in seconds
  • Agent retry logic on transient errors may re-execute a live command that partially succeeded
  • Each callsite must independently remember to pass --dry-run — a single omission causes harm

Solutions

Declare safe_default: true at command registration:

register command "execute":
  danger_level: destructive
  safe_default: true   # dry-run is the default; --live opts into real execution

Framework injects --live and enforces dry-run default:

$ trade execute --symbol BTC --amount 10000
{
  "ok": true,
  "data": {
    "effect": "would_execute",
    "would_affect": {
      "order": { "symbol": "BTC", "side": "buy", "amount": 10000 },
      "estimated_cost_usd": 847230.00,
      "reversible": false
    }
  },
  "error": null,
  "warnings": [],
  "meta": { "dry_run": true, "duration_ms": 42 }
}
$ trade execute --symbol BTC --amount 10000 --live
{
  "ok": true,
  "data": { "effect": "executed", "order_id": "ord_abc123" },
  "error": null,
  "warnings": [],
  "meta": { "dry_run": false, "confirmed": true, "duration_ms": 381 }
}

--schema and manifest expose safe_default:

{
  "command": "execute",
  "danger_level": "destructive",
  "safe_default": true,
  "flags": {
    "live": {
      "type": "boolean",
      "default": false,
      "description": "Execute for real; omit to preview scope without side effects"
    }
  }
}

For framework design: - Commands declare safe_default: true to activate this mode - Framework registers --live (not --dry-run) as the opt-in flag — the name signals positive intent - Without --live, the framework routes all execution through the dry-run path - meta.dry_run is always present in the response envelope - safe_default is exposed in the manifest so agents can detect it programmatically

Evaluation

Score Condition
0 High-stakes commands execute immediately with no dry-run default; safe_default not declared
1 --dry-run is available but opt-in; no safe_default mode; default is live execution
2 Commands support safe_default: true; framework injects --live; dry-run exits 0 with would_* effect
3 safe_default declared in manifest; meta.dry_run always present; agent can detect and enforce safe-default mode programmatically

Check: Invoke a safe_default: true command without any flags — verify it returns a would_* effect, exits 0, and causes no side effects. Then invoke with --live — verify the effect field lacks the would_ prefix and meta.dry_run is false.


Agent Workaround

Check safe_default in the manifest before calling:

manifest = json.loads(run(["tool", "manifest"]).stdout)
cmd = next(c for c in manifest["commands"] if c["name"] == "execute")

if cmd.get("safe_default"):
    # Natural preview → commit workflow
    preview = run(cmd_args)                      # dry-run by default
    verify_scope(json.loads(preview.stdout))
    result = run([*cmd_args, "--live"])
else:
    # No safe-default mode — pass --dry-run explicitly at every callsite
    preview = run([*cmd_args, "--dry-run"])
    verify_scope(json.loads(preview.stdout))
    result = run([*cmd_args, "--confirm-destructive"])

If the tool supports neither safe_default nor --dry-run:

# No preview path exists — require human authorization before proceeding
require_human_approval(cmd_info)

Limitation: If the tool neither declares safe_default nor supports --dry-run, the agent cannot preview impact — do not invoke high-stakes commands speculatively in any automated flow