Claude Code

How to Give Claude Code Autonomy Without Losing Control

Useful autonomy is engineered, not granted: bounded authority, deterministic validation, explicit stop conditions, and deploy gates that stay human.

Daniel Dayto

Forward Deployed Engineer · Co-Founder & Technical Lead at Playmaker

Published
Reading time
12 min read
Abstract diagram of an agent operating inside nested permission boundaries with a human-controlled gate at the outermost layer

There are two failure modes in how teams run coding agents, and they're mirror images. The first is the babysat agent: every file edit approved by hand, every command inspected — all the cost of supervision with none of the value of automation. The second is the blank check: full permissions, no boundaries, and a quiet prayer. Both come from treating autonomy as a single dial. It isn't. Autonomy that actually works is bounded authority plus deterministic validation plus explicit stop conditions — three separate controls, engineered per task. Get those right and you can walk away from the keyboard for an hour; get them wrong and neither supervision nor luck will save you consistently.

Autonomy is a design problem, not a trust problem

The question "do I trust the agent?" is malformed — it has no stable answer, because the same agent is highly reliable at some operations and unreliable at others, and both change with context. The well-formed question is: what's the blast radius if this specific operation goes wrong, and is that radius contained? A wrong edit on an isolated branch costs a git checkout. A wrong command against a production database costs an incident. Same agent, same session, radically different stakes — which is why authority should attach to operations, not to sessions.

Risk tiers

TierOperationsBlast radiusPolicy
T0 — ReadRead files, search, git log, git diff, list directoriesNoneAlways allowed
T1 — Reversible writeEdit files on a feature branch, create files, local commitsOne git checkout away from undoneAllowed inside branch isolation
T2 — Local executionRun tests, linters, builds, local dev serversLocal environment onlyAllowlist the specific commands
T3 — Outbound effectsPush branches, open PRs, call external APIs, install dependenciesVisible outside the machine; mostly reversibleAsk, or allowlist narrowly per task
T4 — Destructive / statefulDatabase mutations, deleting untracked data, force-push, secret accessReal data, real lossDeny by default; explicit human action
T5 — ProductionDeploys, infrastructure changes, DNS, production configCustomers noticeNever agent-initiated. Human authorizes from a reviewed artifact
Every operation an agent can perform, sorted by blast radius. The tier — not convenience — determines the permission.

The five boundaries that make autonomy cheap

  • Branch isolation. All agent work happens on a dedicated branch — or, for parallel sessions, in a dedicated git worktree (Claude Code creates these with claude -w ; it's the documented pattern for concurrent sessions). The branch is the undo button that makes T1 nearly free.
  • Filesystem boundaries. Claude Code's OS-level sandboxing (Seatbelt on macOS, bubblewrap on Linux/WSL2) defaults sandboxed commands to read-only outside the working directory, with writes confined to the project and session temp space. That default is correct; widen it per path, deliberately, or not at all.
  • Command permissions. allow/ask/deny rules in settings files, with rule syntax like Bash(yarn build) or Bash(git push *). Allowlists beat denylists: enumerate what the task needs; leave everything else at ask.
  • Environment separation. The agent's shell should not carry production credentials at all. Development environment variables only; production secrets live where the agent isn't. A permission system can't leak what the process never had.
  • Network boundaries. Sandboxed commands prompt on first use of a new domain, and sandbox.network.allowedDomains makes the allowed surface explicit — which converts "the agent could call anything" into a reviewable list.

Permission matrix by task type

Task typeModeAllowedAskDenied
Codebase investigation / auditplanReads, searchesAll writes and state changes (by mode)
Feature work (this article's focus)acceptEdits on a branchEdits; Bash(yarn lint), Bash(yarn build), test runnergit push, dependency installs, new domainsProd credentials, DB mutations, force-push
Mechanical refactor / codemodacceptEdits in a worktreeEdits; build + testsAnything outside the named directoriesEverything T3+
CI / headless automationclaude -p with --allowedTools and --max-turnsExactly the enumerated toolsNothing — headless can't ask; unlisted = unavailableEverything else by construction
Production incident investigationplan / read-onlyLogs, dashboards, code readsAll mutations — diagnosis and remediation are separate sessions
How I actually configure sessions. "Mode" refers to Claude Code's permission modes; rules are allow/ask/deny entries in settings.

The mechanisms, mapped to the tool

Claude Code gives you several layered mechanisms, and it's worth knowing which one to reach for. Permission modes set the session's posture: default prompts, plan restricts to reading and proposing, acceptEdits pre-approves file edits, and bypassPermissions disables checks — the docs are blunt that the last one belongs only in isolated containers or VMs without internet access, offers no protection against prompt injection, and it refuses to run as root. I don't use it on development machines, and nothing in this article requires it. Settings files carry the durable policy: project rules in .claude/settings.json (committed, so the team shares one boundary), personal additions in settings.local.json, with rules merging across scopes. Hooks are the deterministic backstop — a PreToolUse hook that exits with code 2 blocks the call unconditionally, which is how you encode "never run this migration command" as machinery instead of prompt language. Headless flags (--allowedTools, --disallowedTools, --max-turns) bound automation that runs with nobody watching.

jsonc
{
  "permissions": {
    "allow": [
      "Bash(yarn lint)",
      "Bash(yarn build)",
      "Bash(yarn test *)",
      "Bash(git checkout -b *)",
      "Bash(git add *)",
      "Bash(git commit *)"
    ],
    "ask": [
      "Bash(git push *)",
      "Bash(yarn add *)"
    ],
    "deny": [
      "Bash(git push --force *)",
      "Read(./.env)",
      "Read(./secrets/**)"
    ]
  }
}
.claude/settings.json — a committed project policy for the feature-work row of the matrix above.

Stop conditions: the control nobody writes down

Permissions control what an agent can do; stop conditions control what it should do with an open-ended situation. They're the difference between an agent that plows through ambiguity and one that surfaces it. Every autonomous session I run carries a version of this checklist:

  • Stop if an acceptance criterion looks unachievable as scoped — don't redefine the criterion to fit the work.
  • Stop if the task seems to require a schema migration, data backfill, or deletion of anything that isn't in version control.
  • Stop if you need a credential, account, or external service that wasn't provided.
  • Stop if the fix appears to belong outside the agreed scope — name the boundary violation instead of crossing it.
  • Stop if validation fails twice for the same cause — the third identical retry is where agents start damaging things to make errors go away.
  • Stop if you find something alarming — credentials in the repo, a disabled safety check, evidence of a live bug in production — and report before touching it.

Evidence-based completion

Autonomy's last requirement is that "done" mean something. An autonomous session isn't complete when the agent says so — it's complete when deterministic checks pass and the evidence is presented: the exact commands run and their output, routes verified, artifacts inspected, plus a report of what was not verified and what still needs a human decision. Mandatory tests and the production build are the floor. For visual work, rendered screenshots are part of the evidence — on my own projects, screenshot review has caught defects that every automated check passed. The report format I require is in the companion workflow article; the field that matters most is "claims needing me."

Which tasks deserve full autonomy

Good candidates for full autonomyKeep a human in the loop
Feature work on an isolated branch with checkable acceptance criteriaAnything touching authentication, authorization, or payment paths
Mechanical refactors and codemods with full test coverageSchema migrations and data backfills
Test authoring against existing behaviorChanges to CI/CD pipelines and deploy machinery
Read-only investigation, audits, and documentation of current behaviorSecurity-sensitive code, secret handling, permission systems
Build/tooling fixes verifiable by the build itselfAnything where "correct" requires product judgment or unverifiable claims
Autonomy fit is a property of the task, not the agent.

A safe autonomous execution prompt

text
EXECUTION BOUNDARIES
- All work on branch feat/<name>. Never commit to main. Never force-push.
- You may run: yarn lint, yarn build, yarn test — freely and often.
- Ask before: git push, adding dependencies, contacting new domains.
- Do not read .env or anything under secrets/.
- No database mutations. No production config. No deploys.

STOP AND REPORT IF
- An acceptance criterion appears unachievable as scoped
- The task seems to need a migration, backfill, or deletion
- You need a credential or account not provided
- The same validation fails twice for the same cause
- You find credentials, disabled checks, or a live production bug

DONE MEANS
- All acceptance criteria pass, with command output as evidence
- Implementation report delivered (changed / verified / not done /
  claims needing me / risks)
- Branch pushed ONLY after I confirm the report.
The boundary section I append to autonomous task prompts. Pairs with the committed settings policy — prompt language is the soft layer, settings and hooks are the hard one.

The control you keep

Notice what was never on the table: production. Deploys, infrastructure, customer data — those stay behind a human decision made from reviewed artifacts, and no amount of agent capability changes that. This isn't distrust of the tooling; it's the same reasoning I apply to autonomous voice agents in live operations: the autonomy boundary is drawn conservatively, enforced in machinery rather than in model judgment, and widened only as production behavior earns it. Give a coding agent a contained blast radius, deterministic evidence, and sharp edges to stop at — and the autonomy you get inside those lines is not a risk you're tolerating. It's leverage you engineered.

Key takeaways

  • Attach authority to operations, not sessions — tier every operation by blast radius and permission it by tier.
  • Branch/worktree isolation makes write autonomy nearly free — one command undoes anything inside the boundary.
  • Allowlist commands per task; commit the policy in .claude/settings.json so the whole team shares one boundary; use hooks for the non-negotiables.
  • Stop conditions are the highest-leverage control — zero interruptions inside the boundary, guaranteed interruption at the edges that matter.
  • Completion requires evidence — command output, verified routes, screenshots, and an explicit list of what wasn't verified.
  • Production access is not an autonomy tier — it's a human decision made from artifacts the agent prepared.

Sources & further reading

About the author

Daniel Dayto

Forward Deployed Engineer · Co-Founder & Technical Lead at Playmaker

Daniel Dayto builds and deploys production conversational AI systems for customer operations. His work spans voice agents, RAG assistants, CRM and dispatch integrations, multi-tenant infrastructure, and workflow automation.

Related articles

Deploying AI into a real operation?

I work with teams shipping voice agents, RAG systems, and workflow automation into production. Open to Forward Deployed Engineering, Applied AI, and founding technical roles.