Updated June 2026

The moment you run a coding agent unattended (auto mode, overnight runs, background tasks), one question matters more than any prompt: what happens when it decides to do something you didn’t want? The answer shouldn’t depend on the model choosing well. Constraints come in three layers, and knowing which layer you’re standing on is the whole game.

Layer 1: instructions (the polite request)

Project instruction files (CLAUDE.md and equivalents in other tools) tell the agent what to do and not do: “use pnpm,” “don’t touch the migrations folder,” “never force-push.” Cheap to write, and good models follow them most of the time.

But “most of the time” is the operative phrase. Instructions are suggestions the model weighs, not walls it hits. They can be forgotten deep in a long session, outweighed by a confusing goal, or overridden by injected content. Use instructions for preferences and conventions. Do not use them as your only defense for anything you’d genuinely mind happening.

Layer 2: permission rules (the enforced gate)

Agent harnesses enforce permission rules outside the model: the tool call is checked against your config before it executes, and the model’s opinion doesn’t enter into it. In Claude Code these live in settings.json (global at ~/.claude/, or per project) as three lists:

  • allow: runs without prompting. Put read-only commands here to cut prompt fatigue.
  • ask: always prompts, even in auto mode. Right for things you do want sometimes: deploys, package installs.
  • deny: blocked outright. Right for things an agent should never do on its own.

A minimal starter set that covers most of the real risk:

{
  "permissions": {
    "deny": [
      "Bash(git push*)"
    ],
    "ask": [
      "Bash(rm -rf*)",
      "Bash(npm publish*)"
    ]
  }
}

The principle for sorting actions into lists is reversibility. Local commits, branches, and file edits in a repo are cheap to undo; they can flow freely. Pushing, publishing, deleting, and deploying are visible to other people or hard to reverse; they get a gate. This is the same rule that governs tool design, applied at the config layer.

One honest caveat: pattern rules match command shapes, and a command launched indirectly (buried in a script, wrapped in another shell) can slip past a pattern. Treat permission rules as a strong guardrail, not a sandbox. Actual sandboxes (containers, VM isolation, the sandboxing modes some harnesses ship) are the heavier tool when you need a real wall.

Layer 3: capability (what it literally cannot do)

The deepest layer is what the agent’s environment makes impossible. An agent whose git remote has no credentials cannot push, no matter what it runs or what a prompt injection tells it. An agent with a read-only API token cannot delete. An agent in a container without network access cannot exfiltrate.

This is the only layer that holds against every failure mode at once: model confusion, instruction drift, pattern-rule gaps, and injection. Scope tokens to the minimum the task needs, keep secrets out of the agent’s reach entirely, and reserve the powerful credentials for the human terminal.

The working rule

Match the layer to the stakes. Conventions go in instructions. Anything you’d be annoyed about goes in permission rules. Anything you’d be paged about gets handled at the capability layer, where the agent’s good judgment is no longer load-bearing. Set it up once, globally, before the first unattended run rather than after the first surprise; the config takes five minutes and the surprise never does.