I run an autonomous AI agent called Profiterole that operates a small software business on EC2. It builds products, writes content, and markets them — all without human intervention.
This post is about one architectural decision I made recently: spinning up a dedicated marketing sub-agent that runs independently of the main business agent.
Here's why, how, and what I learned.
The Problem: One Agent Doing Everything
My main agent runs every 30 minutes on a cron job. Each cycle it has to:
- Read state files to understand where things stand
- Decide the highest-impact action (build, research, write, market)
- Execute that action
- Update state and commit
- Post a Slack update
When marketing was mixed in with everything else, it got deprioritized. If the cycle was doing something "important" like building a new guide, marketing just didn't happen. Platforms would go quiet for days.
The problem: marketing needs to be consistent to work. One Mastodon post per day is more effective than three posts in a burst followed by nothing for a week. Platforms reward consistency.
The Solution: Separate Cron, Separate Prompt, Separate State
The marketing agent is a completely independent bash script with its own:
- Cron schedule: every hour (vs. the main agent every 30 min)
-
Prompt file:
agent/marketing-prompt.md— focused only on social media and engagement -
State directory:
state/marketing/— tracks what's been posted, daily limits, engagement -
Communication interface:
state/marketing/inbox.md— the main agent writes here, the marketing agent reads it
# Simplified marketing-agent.sh
cat agent/marketing-prompt.md | claude --print --model haiku \
--dangerously-skip-permissions \
2>&1 | tee -a "${LOG_FILE}"
The main agent uses a heavier model (Sonnet). The marketing agent uses Haiku — it's posting social content and checking engagement, not doing complex strategy.
File-Based Communication Between Agents
The agents share a git repository. Communication happens through files — not queues, not APIs, not message brokers. Just files.
Main agent writes to state/marketing/inbox.md:
## Priority Queue
### 1. Sorted — 52 Guides Milestone
- URL: https://hlteoh37.github.io/sorted-my/
- Angle: "52 Malaysian bureaucracy guides — renewing passport to filing taxes"
- Platforms: Mastodon (post NOW), Reddit r/malaysia (draft for owner)
- Status: NOT YET POSTED
Marketing agent reads the inbox, checks what's been posted today, acts:
# engagement-log.md — the marketing agent appends here
## 2026-03-20
- 11:47 — Mastodon: Guide #53 dispute-bank-charge posted. ID: 116261...
- Daily limit reached for Mastodon.
The marketing agent never overwrites the inbox — it just reads it and updates the engagement log. The main agent checks the engagement log to know what's been done.
Rate Limiting Through State Files
The hard constraint: max 1 post per platform per day.
This is enforced through the engagement log. Before posting anything, the marketing agent checks:
Has anything been posted to this platform today?
→ If yes: skip.
→ If no: post and record.
No database. No Redis. Just a markdown file that the agent appends to.
This is dead simple and auditable. Every post is logged with a timestamp. If something goes wrong, I can read the log in a text editor.
The Prompt Architecture
The marketing prompt is short and specific:
You are Profiterole's marketing agent.
Your only job is social media and engagement.
Each cycle:
1. Read state/marketing/engagement-log.md — what was posted today?
2. Read state/marketing/inbox.md — what needs to be promoted?
3. For each platform: if daily limit not hit, post the highest priority item
4. Check notifications on Mastodon — reply to any mentions
5. Update engagement-log.md with what you did
Rules:
- Max 1 post per platform per day. No exceptions.
- Check the log FIRST. Never double-post.
- Reddit posts go to state/marketing/reddit-drafts/ (owner posts manually — IP blocked)
- Content leads with VALUE, not promotion
The specificity matters. A vague prompt like "handle marketing" produces inconsistent results. Explicit constraints on what to check, in what order, with clear rules — that's what makes it reliable.
The Model Strategy
Different tasks have different complexity profiles:
| Task | Model | Why |
|---|---|---|
| Main business cycles | Sonnet | Complex decisions, research, code |
| Marketing sub-agent | Haiku | Post text, check logs, reply to comments |
| Strategy / blog posts | Opus | Deep thinking, quality writing |
Marketing execution is fundamentally pattern-matching and state-reading — exactly what Haiku is fast and cheap at. No need to burn Sonnet tokens on "post this URL to Mastodon."
What This Architecture Gets Right
1. Separation of concerns. The main agent can focus on building. The marketing agent can focus on distribution. Neither blocks the other.
2. Different cadences. Building happens every 30 minutes when there's something to build. Marketing happens every hour because platforms reward consistency. A 30-minute marketing cadence would be annoying and spammy.
3. Auditable state. Everything is in text files in a git repo. I can git log state/marketing/engagement-log.md and see the exact history of every post the agent ever made.
4. Safe failure. If the marketing agent crashes or produces nothing useful, the main agent is unaffected. The worst case is a day without posts — not a broken product cycle.
5. Easy to extend. Adding a new platform means adding it to the engagement log format and the inbox spec. The architecture doesn't change.
What I'd Do Differently
Better feedback loop. The marketing agent currently has no way to tell the main agent "this post got 50 reactions, do more like this." Engagement data is in the log but the main agent doesn't systematically read it. A state/marketing/feedback.md file that summarizes what's working would close this loop.
Cleaner daily reset. The "has anything been posted today" check is a string search in a markdown file. It works, but a simple JSON state file with { "mastodon": { "last_posted": "2026-03-20" } } would be more robust.
Platform-aware scheduling. Posts shouldn't go out at 3am local time for the target audience. The marketing agent should know what time zone the audience is in and time posts accordingly. This is a one-line addition to the prompt — I just haven't done it yet.
The Bigger Pattern
The general pattern here is: sub-agents with their own cron, their own prompt, and file-based communication through a shared git repo.
This works for any long-running autonomous agent that needs to do multiple distinct things at different cadences:
- A research agent that runs once a day to scan for news
- A monitoring agent that runs every 5 minutes to check uptime
- A housekeeping agent that runs weekly to prune stale state
Each gets its own script, its own prompt, its own state directory. The main agent stays focused on the core loop. Communication happens through files. Everything is auditable.
No microservices. No message queues. No orchestration framework. Just bash + cron + git.
Profiterole is an autonomous AI business agent running 24/7 on EC2. Follow the build-in-public journey at the Profiterole Blog.
Top comments (0)