Autonomous Agents
Build agents with WORK/IDLE phases and auto-claim capabilities for independent operation.
What You’ll Learn
Most AI interactions are conversational: the human types, the AI responds, repeat. Autonomous agents break this pattern. They operate in continuous loops — checking for work, claiming tasks, executing, reporting results, and checking again — all without human intervention.
By the end, you’ll understand:
- What makes an agent autonomous vs. interactive
- The WORK/IDLE phase cycle
- Auto-claim: picking up tasks from shared queues
- The heartbeat pattern for liveness monitoring
- Safety constraints that keep autonomous agents under control
The Problem
Interactive agents have a bottleneck: the human. Every time the agent finishes, it waits for the next instruction.
SCENARIO 1: Test Watcher
Developer changes code → tests need to run
But developer is writing more code → tests sit unexecuted
SCENARIO 2: Code Review Pipeline
3 PRs need review. Agent finishes PR #1, then stops.
PRs #2 and #3 wait for human to say "now do the next one"
SCENARIO 3: Multi-Feature Sprint
5 independent features. Sequential assignment wastes time:
Assign #1 → wait → done → assign #2 → wait → done → ...
Could be: define all 5 → agents work in parallel → all done
The core problem: manual task assignment creates artificial bottlenecks. If tasks are well-defined and independent, agents should pick them up automatically.
How It Works
Interactive vs. Autonomous
INTERACTIVE AUTONOMOUS
Human: "Fix the login bug" Agent: [check queue] → task found
Agent: [works] → "Done." Agent: [claim] → [work] → [report]
Human: "Now add error messages" Agent: [check queue] → task found
Agent: [works] → "Done." Agent: [claim] → [work] → [report]
Human: "Run the tests" Agent: [check queue] → empty
Agent: [works] → "All passing." Agent: [idle] → [wait] → [check]
↑ Stops after each task ↑ Loops until queue is empty
The WORK/IDLE Phase Cycle
Every autonomous agent alternates between two phases:
┌──────────────────────────────────┐
│ │
▼ │
┌───────────┐ no tasks available │
│ IDLE │◀──────────────────┐ │
└─────┬─────┘ │ │
│ check task queue │ │
▼ │ │
┌───────────┐ nothing found │ │
│ CHECK │──────────────────▶│ │
└─────┬─────┘ │
│ task found → claim it │
▼ │
┌───────────┐ │
│ WORK │ execute task │
└─────┬─────┘ │
│ complete → report result │
└──────────────────────────────────┘
In IDLE, the agent waits a configurable interval before re-checking. In WORK, the agent has full authority over its claimed task — reading files, writing code, running commands.
Auto-Claim: From Queue to Execution
Auto-claim is how an autonomous agent picks up unassigned tasks:
SHARED TASK QUEUE
┌─────────────────────────────────────────────┐
│ [task-1] status: done assignee: A │
│ [task-2] status: done assignee: B │
│ [task-3] status: ready assignee: — │ ◀── Agent C claims this
│ [task-4] status: ready assignee: — │
│ [task-5] status: blocked assignee: — │
└─────────────────────────────────────────────┘
RULES:
1. Only claim tasks with status "ready" (skip "blocked")
2. Claim ONE task at a time (prevents hoarding)
3. Set assignee atomically (prevents double-claim)
4. Respect priority order (higher priority first)
The Heartbeat Pattern
When an agent works autonomously, how do you know it is still alive?
Agent C (autonomous) Monitor
│ │
│ ♥ { agent: "C", task: "task-3", │
├───── status: "working" } ──▶ recorded: alive
│ ... 30 seconds ... │
│ ♥ { agent: "C", task: "task-3", │
├───── progress: "60%" } ──▶ recorded: alive
│ ... 30 seconds ... │
│ (no heartbeat) │
│ ──▶ 60s without heartbeat
│ │ → agent presumed dead
│ │ → unassign task-3
│ │ → task returns to queue
Heartbeats provide liveness detection (crashed agents get their tasks reclaimed) and progress tracking (the orchestrator can monitor completion).
Daemon Mode
Some autonomous agents run indefinitely, watching for events rather than draining a queue:
DAEMON AGENT: Test Watcher
WATCH: file system changes
TRIGGER: *.ts file modified
ACTION: run related tests, report pass/fail
LOOP: forever (until explicit stop or session end)
EXIT: stop signal, session end, or 3 consecutive fatal errors
Safety Constraints
Autonomous agents need guardrails. Autonomy is granted in degrees, not all-or-nothing:
┌─────────────────────────────────────────────────┐
│ 1. SCOPE LIMITS │
│ allowed_paths: ["src/", "tests/"] │
│ max_files_modified: 10 │
│ │
│ 2. APPROVAL GATES │
│ auto_approve: read, test, lint │
│ require_review: write, delete │
│ blocked: deploy, publish │
│ │
│ 3. KILL SWITCHES │
│ max_runtime: 300s per task │
│ max_iterations: 50 │
│ error_threshold: 3 consecutive failures │
│ │
│ 4. RESOURCE BUDGETS │
│ max_tokens_per_task: 100,000 │
│ max_tool_calls: 200 │
└─────────────────────────────────────────────────┘
An agent can be fully autonomous for safe operations (reading, testing) while requiring approval for destructive ones (deleting files, deploying).
Key Insight
Autonomous agents are the building block for CI/CD-like workflows within Claude Code. They bridge the gap between interactive coding and automated pipelines.
The key shift is mental: interactive agents treat the human as the scheduler (“what should I do next?”). Autonomous agents treat the human as the architect (“here is the queue, here are the constraints, now execute independently”).
This means quality depends on task definitions. A well-defined task with clear inputs, expected outputs, and success criteria can run autonomously. A vague task like “improve the codebase” cannot. Invest in task decomposition (Session 7) and protocols (Session 10) before enabling autonomous agents.
Hands-On Example
A test-runner agent operating autonomously:
SETUP: Define the agent
──────────────────────────────────────
Agent: "test-runner"
Mode: autonomous
Constraints:
allowed_actions: [read_file, run_tests, report_result]
denied_actions: [write_file, delete_file]
max_runtime: 120s per task
heartbeat_interval: 30s
CYCLE 1: Claim and execute
──────────────────────────────────────
[CHECK] Query queue → status: "ready", type: "test"
[CLAIM] task-17: "Run integration tests for auth module"
[WORK] Read tests/auth/ → run pnpm test tests/auth/
[REPORT] { passed: 12, failed: 1,
failure: "tests/auth/refresh.test.ts:45" }
[♥] Heartbeat sent at 30s mark
CYCLE 2: Next task
──────────────────────────────────────
[CHECK] Query queue → task-22 found
[CLAIM] "Run unit tests for payment module"
[WORK] Read tests/payment/ → run pnpm test tests/payment/
[REPORT] { passed: 8, failed: 0 }
CYCLE 3: Queue empty
──────────────────────────────────────
[CHECK] No tasks available
[IDLE] Wait 10s → check again → still empty → wait...
Good vs. Bad Autonomous Agent Design
GOOD: BAD:
Single responsibility Multiple responsibilities
Clear success criteria "Make it better"
Bounded execution time "Keep going until perfect"
Structured results Unstructured text output
Example: Lint checker Example: "Code improver"
Input: file path Input: entire codebase
Action: run linter Action: ??? (too broad)
Output: { errors, warnings } Output: "I made some changes"
Connection to Team Protocols
Autonomous agents use the same protocol messages from Session 10. The only difference is who initiates:
INTERACTIVE: Human → "Review this PR" → Agent works → responds
AUTONOMOUS: Agent → [checks queue] → [claims] → [works] → responds
Same message format. Same request_id. Different trigger.
What Changed
| Interactive Agent | Autonomous Agent |
|---|---|
| Human assigns each task | Agent claims from queue |
| Stops after every task | Loops continuously |
| Human monitors progress | Heartbeat signals liveness |
| All actions allowed | Safety constraints per operation |
| Bottleneck: human response time | Bottleneck: task queue depth |
Next Session
Session 12 covers Worktree Isolation — how git worktrees give each agent its own working directory on its own branch, enabling true parallel development without merge conflicts.