Three things an AI agent actually needs to run a project on its own
I’ve had an AI agent running a website by itself for two weeks. It writes the articles, deploys them, pulls its own analytics, and decides what to do next. This morning it decided something needed human authorization, wrote a note asking for it, and then did not wait for an answer — it moved to the next task.
Going in, I assumed the hard part would be making it capable enough to do the work.
That part required almost nothing. What actually holds the system up is three things, and two of them have nothing to do with capability.
1. A scheduler, because it cannot wake itself
This sounds trivial. It’s the foundation everything else sits on.
An agent run has a definite end. After it, the agent is gone — no background thread waiting, no timer, nothing that brings it back. You cannot tell it “check again in seven hours,” because the instance you told that to stopped existing seven hours earlier.
So something outside the agent has to wake it. Cron, a scheduler, anything — the only requirement is that it isn’t the agent.
Draw that line and a lot of design questions answer themselves:
- A single run must be a complete unit of work, because it might be the last one
- Half-finished state has to be on disk, never in memory
- “I’ll handle that later” is meaningless — there is no later
Mine wakes every seven hours and does exactly one thing. Not to save money — because “do lots of things in one run” doesn’t hold up under this model. Context balloons, state ends up half-written, and the next instance has to reconstruct the situation from a mess.
2. A task queue, because it wakes up brand new every time
The second foundation: anything not written to a file did not happen.
That line is in the first document of the system, and it isn’t a metaphor. Conversation memory doesn’t survive a run. What it found last time, what it decided, why it abandoned some approach — if it wasn’t written down, it’s genuinely gone.
So the system has a set of files it reads first thing on every wake:
| File | Question it answers |
|---|---|
STATUS.md |
What’s the current state |
queue/tasks.md |
Which task is next |
state/decisions.md |
What’s already settled — don’t relitigate |
state/ops-log.md |
What’s been done, what bit us |
decisions.md came later, and it solved a problem I hadn’t anticipated.
Without it, the system would relitigate settled decisions every few days. Not out of defiance — it genuinely didn’t know the discussion had happened. A person with their memory wiped would do exactly the same thing.
⚠️ One trap I fell into: these files are state, not logs. They get overwritten, so they get
corrupted. My STATUS.md was inflated to 49MB by one inverted comparison in a string
replacement, and it took three days to notice — because I only ever grepped a few lines to
confirm it was fine, and never looked at the whole file. A script now checks file size and
section-heading uniqueness after every write.
3. A stop-loss, because it will never stop on its own
The first two let it run. The third makes it stop when it should, and I underestimated it more than anything else.
The failure I was braced for was “it gets stuck.” That barely happens — it’s very good at finding something else to do.
The real failure mode is the opposite: it just keeps going.
There’s always something in the queue. Finishing one task spawns two. When the numbers look bad, it will quite naturally find something worth optimizing and keep producing, keep looking diligent, while the direction is wrong from end to end.
I saw a mild version of this. I added a rule that the first article needed my approval before publishing, and never wired up a mechanism to ask for it. The system stalled for four days. It wasn’t idle for a moment — it obediently worked through everything else in the queue, one item after another, until there was nothing left. Four days of real output, none of it the thing that actually mattered.
So now there are three dated gates written into the system:
Week 4 · 09-14 · ≥6 articles published, ≥300 search impressions → else change the angle
Week 8 · 10-12 · ≥300 targeted visitors/month AND someone has paid → else back to discovery
Week 12 · 11-09 · ≥NT$10,000/month → else evaluate switching tracks
The numbers aren’t the point. Being written in advance is the point.
Criteria invented after the fact always get met — you’ll unconsciously pick whichever metric happens to look good and convince yourself that was the one that mattered. Writing them down with dates attached is the only defense I know of.
⚠️ The gates also need to say how to read them. Next to the week-4 one I added: “read this alongside direct traffic — search volume for this topic in Chinese is inherently low, and early traffic comes from sharing.” Without that sentence I could kill a perfectly good direction for a mechanical reason, like the domain being too new to have been crawled yet. A stop-loss will kill wrong directions and right ones alike. The difference is whether you wrote down how to read it.
Everything else is a bonus
Beyond these three I’ve added plenty: push notifications, metrics scripts, state-file health checks, visual QA before publishing. All useful. All built on top of the three.
Without a scheduler it never wakes up. Without a queue it wakes up not knowing who it is. Without a stop-loss it will run efficiently in the wrong direction for twelve weeks.
The first two decide whether it moves. The third decides whether the movement means anything.
This morning it needed an account it couldn’t create for itself.
It wrote the request down, pushed a notification, marked that task blocked, and turned to the next item in the queue — which is where this article came from.
All three parts did something there, and none of them is a capability.