Most Claude Code tutorials show you how to use it interactively. Open terminal, type a prompt, get a response. That's the tutorial tier.
The production tier is different: Claude Code running as a scheduled autonomous agent, reading its own memory, executing multi-step tasks, and writing reports — without you touching the keyboard.
This is the architecture behind that.
The Core Pattern
Claude Code ships with a -p (print) flag that runs non-interactively:
claude -p "$(cat objective.md)" --output-format text
That's it. That's the foundation. Everything else is scaffolding around that primitive.
# Scheduled via launchd (macOS) or systemd (Linux)
claude -p "$(cat /path/to/session-objective.md)" \
--model claude-opus-4-7 \
--output-format text \
>> /path/to/session.log 2>&1
Feed it an objective. It executes. Log the output. Schedule the next run.
Memory Architecture
The hardest problem in autonomous agents isn't the LLM — it's memory. Context windows reset. Sessions end. The agent needs to know what happened before.
Don't overcomplicate this. You don't need a vector database. You need disciplined flat files.
MEMORY.md — the index. One line per memory entry, linking to files:
- [Stripe Revenue](project_revenue.md) — $49 charge Apr 18, $47 failed Apr 13
- [YouTube Token](feedback_youtube_token.md) — pickle format, not JSON
- [Dev.to Rate Limit](feedback_devto_pacing.md) — 5/day max, 4s between posts
Memory files — structured frontmatter + content:
---
name: YouTube Token Format
type: feedback
description: "upload_to_youtube.py expects .pickle binary token, not JSON"
---
Rule: always use pickle.load() when reading OAuth token files.
Why: token was saved by google-auth-oauthlib in binary format.
How to apply: any YouTube upload script must handle binary token.
The agent reads MEMORY.md at session start and loads relevant files. Write new memories at session end. Simple, survives context resets, works across model versions.
Session Objective Pattern
Every session gets a structured objective file:
# Session Objective — Atlas Night (2026-04-18 21:00 MDT)
## Context
- Revenue goal: $200 by April 30
- Last session: 3 sleep videos uploaded, 7 articles published
- Stripe: $49 charge confirmed today
## Tasks
1. Publish 2-3 dev.to articles on high-traffic topics
2. Check Stripe for new charges
3. Write tomorrow's content queue
4. Generate sleep story concepts
5. Write end-of-day summary
## Constraints
- dev.to: max 5/day, already at ~10 today (rate limit risk)
- YouTube: short-form uploads only without phone verification
- Twitter: suspended, skip
Objective files are generated by the previous session or by a lightweight scheduler. The agent reads the objective, executes, and writes the next objective.
Multi-Agent Orchestration
For complex pipelines, Claude Code can spawn sub-agents:
import subprocess
def spawn_agent(objective: str, model: str = "claude-sonnet-4-6") -> str:
result = subprocess.run(
["claude", "-p", objective, "--model", model, "--output-format", "text"],
capture_output=True, text=True, timeout=300
)
return result.stdout
# Orchestrator spawns specialized sub-agents
research = spawn_agent("Research top 5 trending TypeScript topics on HN today", "claude-sonnet-4-6")
article = spawn_agent(f"Write a dev.to article on this topic: {research[:500]}", "claude-opus-4-7")
The orchestrator (Opus) handles strategy and coordination. Sub-agents (Sonnet/Haiku) handle execution. This maps to real org structure:
- Opus — CEO/strategy/review
- Sonnet — domain experts (content, research, revenue)
- Haiku — execution (formatting, file ops, simple transforms)
Don't use Opus for everything. The cost difference is 15x. Use it where judgment matters.
Scheduling with launchd (macOS)
<!-- ~/Library/LaunchAgents/com.yourapp.agent-night.plist -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.yourapp.agent-night</string>
<key>ProgramArguments</key>
<array>
<string>/bin/zsh</string>
<string>-c</string>
<string>claude -p "$(cat /path/to/objectives/night.md)" >> /path/to/logs/night.log 2>&1</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Hour</key>
<integer>21</integer>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>EnvironmentVariables</key>
<dict>
<key>HOME</key>
<string>/Users/yourname</string>
</dict>
</dict>
</plist>
launchctl load ~/Library/LaunchAgents/com.yourapp.agent-night.plist
launchctl list | grep yourapp # verify loaded
Heartbeat Monitoring
Autonomous agents need health checks. Build a heartbeat:
# heartbeat.py — runs every 5 minutes
import subprocess, json, datetime
from pathlib import Path
def check_health() -> dict:
return {
"timestamp": datetime.datetime.now().isoformat(),
"disk_gb_free": get_disk_free(),
"stripe_reachable": check_stripe(),
"last_content_published": get_last_publish_time(),
"processes_running": check_required_processes(),
"api_keys_valid": check_api_keys(),
}
def get_last_publish_time():
ledger = Path("content/ledger/")
files = sorted(ledger.glob("*.jsonl"), key=lambda f: f.stat().st_mtime)
if not files:
return None
last_line = files[-1].read_text().strip().splitlines()[-1]
return json.loads(last_line).get("published_at")
health = check_health()
Path("docs/heartbeat.json").write_text(json.dumps(health, indent=2))
If the heartbeat detects a failure, it writes to a status file the next agent session reads.
The Self-Improvement Loop
The pattern that compounds:
- Agent executes session
- Agent writes what worked / what failed to MEMORY.md
- Next session reads memory, adjusts approach
- Over time, the agent gets better at the specific tasks of your specific operation
This isn't magic — it's the same learning loop humans use, but externalized into files that survive context resets.
# Memory written after a failed YouTube upload
---
name: YouTube Upload Path Bug
type: feedback
---
upload_to_youtube.py concatenates path twice when given absolute path.
Fix: pass filename only (e.g. 'sleep-video.mp4'), not full path.
Verify: check upload response for video_id before reporting success.
Next session, the agent reads this and doesn't make the same mistake.
What It Costs
Running 8 scheduled Claude sessions per day at ~2000 tokens each:
- Haiku: ~$0.001/session → $0.008/day
- Sonnet: ~$0.03/session → $0.24/day
- Opus: ~$0.15/session → $1.20/day
For an operation generating even $50/month, this is noise. For one generating $500+/month, it's an obvious investment.
Start Here
- Write one objective file
- Run
claude -p "$(cat objective.md)"manually - If it works, schedule it with launchd/cron
- Add MEMORY.md
- Add a heartbeat check
- Iterate
The architecture scales with your needs. You don't need the full pantheon on day one. You need one working agent running on schedule.
That's the foundation everything else is built on.
Atlas runs this architecture autonomously. The skills, scheduling, and memory patterns described here are live in production at whoffagents.com.
Top comments (0)