The 3-Agent Loop That Runs My Solo Agency (No Team)

You watched a four-hour Claude Code course, shipped something cool, and you're still the one copying leads out of Gmail, briefing the build, opening the invoice template, and chasing payment. Claude built you a tool. It didn't build you a business. The bottleneck in a one-person operation is never the building — it's the handoff between sales, delivery, and billing, and that's where two to four hours a day quietly disappear.
I run this loop for clients every week. Three agents, no shared memory, no shared API. They share folders. That's the whole trick.
Why one mega-agent collapses inside a week
The first instinct most solopreneurs have is to build one giant Claude agent that "runs the business." Sales prompts, delivery prompts, and billing prompts all stuffed into one system message. I've watched this fail in production roughly every time.
Three reasons it dies:
- Context pollution. Sales tone ("warm, curious, close") leaks into the invoice email. The billing agent starts being friendly when it should be boring.
- Failure cascade. When one role fails, the whole agent fails. You lose sales because the invoice template broke.
- No audit trail. When something goes wrong, you can't tell which "personality" of the agent caused it.
A solo founder actually plays three jobs, and they want different things from an LLM:
| Role | What it needs | What ruins it |
|---|---|---|
| Sales | Persuasion, tone, judgment | Strict templates |
| Delivery | Precision, code, tests | Creativity |
| Billing | Zero creativity, 100% reliability | Anything "smart" |
One agent per role. One folder per agent. One file format between them. That's the architecture.
The lead-gen agent and the markdown contract
The sales seat scrapes a defined source (LinkedIn, a directory, an inbox folder), qualifies against a fixed checklist, and writes the qualified lead into a single shared folder: /inbox.
The lead is not a database row. It's a markdown file. Filename is YYYY-MM-DD_company.md. That's deliberate — markdown is human-readable, diff-able, grep-able, and every agent on earth can parse it.
The file has five fields, no more:
---
company: Northwind Logistics
contact: ops@northwind.example
problem: "Spend 6 hrs/week reconciling carrier invoices by hand."
budget_signal: "Mentioned 'a few thousand' in discovery email"
suggested_scope: document_automation
---
If the file doesn't validate against that schema, the next agent ignores it. No fallback, no retry, no "let me guess what you meant." Garbage doesn't flow downstream. That's the contract.
The lead-gen agent runs on a cron — every hour — and drops files into /inbox. It doesn't know Claude Code exists. It doesn't care. That decoupling is the entire point.
Claude Code as the delivery agent
Claude Code watches /inbox. The moment a new markdown file lands, a tiny watcher script (we'll get to it) reads the five fields and kicks off a Claude Code session pointed at a project template.
Templates matter. I keep four scaffolds in /templates:
landing_page/— Next.js + Tailwind starterscraping_script/— Python + Playwright skeleton with a logging contractinternal_tool/— Flask + SQLite single-file appdocument_automation/— pdf parsing + jinja2 output
Claude Code reads suggested_scope from the lead file, picks the matching template, clones it into /projects/<client_name>/, and starts building. The prompt it gets is narrow on purpose:
claude-code \
--project /projects/northwind_logistics \
--template document_automation \
--brief /inbox/2025-03-14_northwind.md \
--commit-on-pass \
--tag-format "invoice-ready/{client}/{amount}"
When the build is done and tests pass, Claude Code commits to git and adds a tag in the format invoice-ready/northwind_logistics/2400. That tag is the second contract.
Notice what just happened. The sales agent never talked to Claude Code directly — it talked through a file. Claude Code never talks to the billing agent directly — it talks through a git tag. Each agent can crash, restart, get upgraded, or get swapped out for a different model, and the contracts don't break.
The billing agent (60 lines, dumb on purpose)
The billing agent watches the git repo for new tags matching invoice-ready/*. When it sees one, it:
- Parses
client_nameandamountfrom the tag - Loads
/clients/<client_name>.jsonfor billing details - Renders an invoice PDF from a Jinja2 template
- Sends the email via SES (or whatever SMTP you trust)
- Appends a row to
/crm/ledger.csv
That's it. Roughly 60 lines of Python:
import re, json, subprocess, smtplib
from pathlib import Path
from jinja2 import Template
TAG_RE = re.compile(r"invoice-ready/([^/]+)/(\d+)")
def handle_tag(tag: str):
m = TAG_RE.match(tag)
if not m:
return
client, amount = m.group(1), int(m.group(2))
info = json.loads(Path(f"clients/{client}.json").read_text())
pdf = render_invoice(info, amount)
send_email(info["email"], pdf)
log_ledger(client, amount)
The billing agent has no LLM call in it. It doesn't need to be smart. It needs to be reliable. All the intelligence already happened upstream in sales and delivery. This is the part 90% of "AI agency" tutorials skip — they wire LLMs into steps that should be deterministic, and then they're confused when the invoice goes out for $2,400.00 to "John (probably?)".
The folder contract — the document that turns scripts into a system
This is the step people skip and then wonder why their setup breaks the second they touch it three months later.
Write a single CONTRACTS.md at the root of the workspace. It defines:
- Exactly what a valid lead markdown file looks like (the five frontmatter fields, types, required vs optional)
- Exactly what a valid git tag looks like (
invoice-ready/<client_slug>/<amount_usd_integer>) - Exactly what a valid client JSON looks like (
name,email,address,tax_id,currency)
Every agent reads CONTRACTS.md on startup. If you change a field, you change it in one place. The agents are dumb; the contract is the truth.
Folder layout looks like this:
/agency
CONTRACTS.md
/inbox # leads land here (markdown)
/templates # project scaffolds
/projects # one folder per client build
/clients # client.json files
/crm
ledger.csv
/agents
lead_gen.py
billing.py
watcher.py
No database. No SaaS workflow tool. No Airtable. Plain files. You can git diff your entire business.
The watcher — the entire orchestration layer in 40 lines
The watcher is the only thing that ties the three agents together. It uses watchdog to listen on /inbox, and a tiny git poll for new tags. New file in /inbox → trigger Claude Code. New invoice-ready/* tag → trigger billing.
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler
import subprocess, time
class InboxHandler(FileSystemEventHandler):
def on_created(self, event):
if event.src_path.endswith(".md"):
subprocess.Popen([
"claude-code", "--brief", event.src_path,
"--commit-on-pass"
])
def poll_tags(seen: set):
out = subprocess.check_output(["git", "tag", "-l", "invoice-ready/*"])
for tag in out.decode().splitlines():
if tag not in seen:
seen.add(tag)
subprocess.Popen(["python", "agents/billing.py", tag])
if __name__ == "__main__":
obs = Observer()
obs.schedule(InboxHandler(), "inbox", recursive=False)
obs.start()
seen = set()
while True:
poll_tags(seen)
time.sleep(30)
That's the entire orchestration. No n8n, no Temporal, no Zapier bill creeping toward $80/month. A watcher script and two triggers.
What a normal day looks like
- Overnight: lead-gen agent drops 3–4 qualified leads into
/inbox - 7:30 AM: I open
/projects— three scaffolded builds are already waiting, each with a git branch and a passing test suite - I spend ~45 minutes reviewing, tightening copy, and accepting or rejecting the Claude Code commits
- When I approve, I push and tag. Billing agent sees the tag within 30 seconds and sends the invoice
- I never open an invoice template. I never write a follow-up email. I never copy a lead from one tool to another
That's the loop. Sales drops a file. Delivery reads the file, builds the thing, tags the commit. Billing reads the tag, sends the invoice.
Where this breaks (and the fixes)
Honest list, from running this with clients:
- Lead-gen agent hallucinates
budget_signal. Fix: require a verbatim quote field. If no quote, file is invalid. - Claude Code picks the wrong template. Fix: make
suggested_scopean enum, not free text. Reject the file if it's not in the enum. - Two leads from the same company in the same week. Fix: lead-gen checks
/clients/and/projects/before writing the file. - Billing fires on a tag I pushed manually for testing. Fix: a
--dry-runtag prefix that the billing agent explicitly ignores.
None of these are model problems. They're contract problems. Tighten the contract, the agents get more reliable. That's the lesson.
Why bizflowai.io helps with this
The 3-agent loop above is the same shape we deploy for clients at bizflowai.io — a sales agent that qualifies leads into a structured format, a delivery layer (often Claude Code for technical work, or a document-automation agent for non-code deliverables), and a billing agent that reads from a deterministic signal and sends invoices. The value isn't a fancier model. It's the folder contracts, the watcher, and the discipline to keep each agent dumb and single-purpose. If you'd rather skip the wiring, that's what we build.
Want more like this?
I publish practical AI automation, GenAI engineering, and faceless content workflows on YouTube every week.
Subscribe to bizflowai.io on YouTube — never miss a new tutorial.
Planning an AI automation project or need a second opinion on your architecture?
Connect with me on LinkedIn — Lazar Milicevic, GenAI Engineer & bizflowai.io Founder.
Visit bizflowai.io for our services, case studies, and AI consulting.
Frequently asked questions
What is a three-agent system for solo founders?
A three-agent system splits a one-person business into three separate AI agents: a sales agent that finds and qualifies leads, a delivery agent (Claude Code) that builds the work, and a billing agent that sends invoices. Each agent owns one role, runs in its own folder, and communicates only through shared files and git tags rather than shared memory or APIs.
Why shouldn't I merge sales, delivery, and billing into one mega-agent?
Merging roles into one mega-agent collapses within a week because context gets polluted. Sales prompts need a closer tone, delivery prompts need precision, and billing prompts need zero creativity. Keeping them separate as one agent per role, one folder per agent, and one file format between them prevents conflicting instructions and lets each agent stay focused on a single job.
How do I structure the lead-gen agent to hand off to Claude Code?
The lead-gen agent scrapes a defined source, qualifies leads against a fixed checklist, and writes each qualified lead as a markdown file into a shared /inbox folder. The filename is the date plus company name, and the file contains five fields: company name, contact, problem in their words, budget signal, and suggested scope. If the file isn't valid, the next agent ignores it.
How does Claude Code know when to start a delivery project?
A watcher script monitors the /inbox folder. When a new markdown lead file lands, it reads the five fields and launches a Claude Code session. Claude Code matches the suggested scope to a scaffold in /templates (such as a landing page, scraping script, or internal tool), clones it into /projects/client-name, builds it, and on passing tests commits to git with an invoice-ready tag.
Why use files and git tags instead of direct agent-to-agent communication?
Files and git tags act as contracts between agents. The sales agent never talks to Claude Code directly—it drops a markdown file. Claude Code never talks to billing directly—it writes a git tag formatted as invoice-ready, client name, and amount. This loose coupling means any agent can fail, restart, get upgraded, or be swapped out without breaking the loop.