Skip to content
Title card for the guide "How to Build an AIOS: Nine Prompts and a Two-Week Order" on brian-macdonald.com

← Free GuidesPart 4 of 4, the AIOS series

How to Build an AIOS: Nine Prompts and a Two-Week Order

September 8, 2026

The AIOS series, part 4 of 4. Also in this series: The Operating System Built for You · 48 Jobs You Can Hand to an AI Operating System · What an AIOS Is Made Of

This is the companion to How to Start Building an AIOS. That page is the architecture — the three parts of an AI operating system and why each exists. This one is the build.

Eight pieces, a paste-ready prompt for each, where to run the thing, and the order I'd do it in if I were starting Monday with nothing.

The prompts are written to hand straight to a coding agent. They're deliberately specific about the constraint rather than the implementation, because the constraint is the part that matters and the implementation is the part it's good at.

Part one: the machine

Five pieces. Build them once, in service of one real job, and the second job costs a fraction of the first.

One door in

Every request enters through a single endpoint. Not a webhook here and a form there and a cron job somewhere else — one door, authenticated, that logs everything that passes through it.

It sounds like a constraint and it's a gift: when something misfires at 3am you have one place to look. Mine is a single endpoint called /dispatch. Anything that wants the system to do something knocks there.

The other half of a door is that it can't be knocked twice by accident. Every request carries a key, and a request arriving with a key you've already seen returns the first answer instead of doing the work again. Networks retry. Yours should be boring about it.

Prompt: Build me a single authenticated HTTP endpoint called /dispatch. It takes a bearer token from an environment variable, rejects any request without it, and logs every request and response with a timestamp. Each request carries an idempotency key — if I've seen that key before, return the stored result instead of doing the work again. Nothing else in this system accepts inbound requests. Show me the whole file.

One brain

Every call to a model goes through one function. Not "mostly." Every one.

That single choke point is where you set which model does what, where you cache prompts, and where you count what you spent. The moment one module imports the SDK directly and calls out on its own, your cost numbers are fiction and you won't find out until the bill arrives.

Prompt: Write me one function, think(task, job_id), that is the only place in this codebase allowed to call a language model. Model choice comes from a config file, not from the caller. It logs the job id, the tokens in and out, and the cost of every call. Then add a test that fails if any other file in the project imports the model SDK directly.

That last sentence is the one that matters. A rule nobody can break by accident is worth ten rules everyone agrees to.

One ledger

Money is metered before the call and recorded after it, with a key that makes the record impossible to write twice.

This matters more than it sounds. A timeout does not mean nothing happened — it means you don't know. If a retry can charge you twice, you'll eventually retry a hundred things at once and find out the expensive way. So before you resubmit anything that costs money, you go and ask the vendor what they have under that key.

I haven't seen this written about much anywhere, and it's the part that has saved me the most actual money.

Prompt: Add a spend ledger. Before any call that costs money, check this month's total against a ceiling from the config file and refuse the call if it would cross it. After the call, record what it cost as insert-or-ignore against an idempotency key so the same spend can never be written twice. Add a reconcile function that, given a key, asks the vendor what actually happened — I'll call it before any resubmit.

One memory

Its own database, on its own box, holding its own state. No shared account, no borrowed identity, nothing that breaks when a system you don't control changes.

If you can't rebuild the whole thing from a git clone, an env file and one command, you don't own it — you're renting it from your own past self.

Prompt: Set up a database this system owns outright, with a migration that runs on deploy and snapshots the database before it does. Every piece of state the system needs lives here. Then write me a bootstrap script that takes a fresh empty machine to a running system using only a git clone, an env file and one command, and tell me honestly what's still missing from it.

One rulebook

Every number the system judges your work against, in one plain file you can edit — not buried in code. What counts as too long. What counts as too expensive. When a thing is finished, and when it has failed.

This is the part I got wrong for months, and it's easy to get wrong because it doesn't look like a mistake while you're making it. You write a check into the code as you build, the number seems obvious at the time, and six weeks later it's blocking work at nine in the morning and you cannot remember agreeing to it. Mine trimmed a piece to fit a word limit I had never set. Changing that number took a code change, a review and a deploy — for a figure I invented on a Tuesday and never decided.

So put every one of them in a file. Then the rule that makes the file worth having:

When you disagree with the machine, you edit the file. You don't argue with the machine.

That one habit turns the file into the place your judgement accumulates. Every time a gate stops you and you decide it was wrong, the correction lands somewhere permanent instead of somewhere you re-litigate next month.

Two conditions make it hold. A missing file falls back to sane defaults, so a fresh copy of the system still runs. And a missing number is never guessed — the system says that rule has no threshold and skips the check rather than inventing one, because a made-up limit is worse than no limit.

One more detail decides whether anyone actually uses it: when a check refuses something, the message names the rule and the file it came from. "Body is 2,218 words" starts an argument. "body_max_words is 2000, in your rulebook" ends one, because you already know where to go.

Prompt: Move every hard-coded threshold in this project into one config file. Each check reads its number from there. If the file is missing, fall back to documented defaults; if a single number is missing, skip that check and say so out loud rather than inventing a value. Every refusal message must name the rule and the file it came from.

---

Part two: the workshop

One repo

Here is the piece almost nobody writes about, and it's the one that changes how the whole thing feels to work in.

Your repository is not storage. It's the room where your agents and you work together.

Think about what you actually need when more than one worker — human or not — is changing a system that's live. You need to see what someone is about to change before it lands. You need a record of why. You need work to be reviewable by someone who wasn't there when it was written. You need to be able to undo it. And you need something that refuses bad changes automatically, so that being asleep isn't the same as being careless.

Every one of those already exists, and it's been sitting in your GitHub account the whole time.

So the rule is: no agent edits the running system. Agents open pull requests.

The work goes into a branch. The change arrives as a diff you can read on your phone. Another agent reviews it adversarially — its job is to find what's wrong, not to agree. Tests run automatically and the merge is blocked if they fail. You approve, it merges, and the box pulls the new code down.

That last step matters as much as the rest: the server is a place the truth gets copied to, never the place it lives. If you fix something directly on the live box at midnight, you'll forget, and the next deploy will quietly delete it. Or worse, that fix will be the only copy that ever existed. Git is the truth.

And this is where your tests live, because tests are what let you approve a change you didn't fully read.

Most people write tests that pass and feel good. The rule I hold instead: a test doesn't count until you deliberately break the code and watch that test fail. A test that has never failed has never proven anything — it might be asserting nothing at all, and you would not know. Break the thing on purpose, watch red, put it back, watch green. Now it's real.

That's how one person reviews the output of a crew working faster than they can read. Not by reading everything. By having a gate that has already proven it catches things.

The flexibility comes free. Because everything is a diff with a reason attached, you can go back and see why a decision was made a year later. Because it's a branch, an experiment doesn't threaten the running system. Because it's a repo, a new agent joins by reading it rather than by you explaining it — and a new human joins the same way.

Prompt: Set this project up so no agent ever edits the running system directly. Every change is a branch and a pull request with a description of why. Add a CI workflow that runs the test suite on every pull request and blocks merge on failure. Give the server read-only access to pull approved code, and no ability to push. Then write me a CONTRIBUTING file, addressed to an AI agent, explaining exactly how to open a change here.
Prompt: Take the three most important guards in this code. For each one, break the thing it protects on purpose, run the tests, and show me the failure output. If any of those tests still passes with the code broken, that test is asserting nothing — tell me and rewrite it.

---

Part three: the leash

One proof

My content system once ran for four days without a single error and published nothing.

Every step worked. The publisher fired every two minutes and reported success every single time. Zero alerts. And not one thing reached a customer.

Here's what I learned, and it's the most useful thing in this article. The automation almost never breaks. What breaks is the step before it — the one where a person has to be present for anything to start. Nobody draws that box on the diagram. Nobody labels it "Dave opens his laptop." So when Dave is out, nothing runs. And nothing tells you.

Silence is not success. Silence is just silence.

Two things fix it, and they're both cheap.

Measure the outcome, not the machine. Don't check whether the job ran. Check whether something reached a customer. Those are different questions and only one of them is worth anything. The count that belongs on your screen every morning is things that got all the way out yesterday, and if it's zero, that's an alarm — even if nothing failed.

Ask the process, don't believe the report. Anything that reports on itself can report wrong. Put one address on your system, public and needing no password, that answers exactly one question: what version are you running right now? After a deploy you read it until it says the thing you approved. Not a summary, not a promise — the running process, asked directly.

Prompt: Add a public health endpoint that reports the exact commit this process is running, with no authentication. Then add a daily digest that counts completed outcomes only — things that actually reached a customer, not jobs that ran — and alerts me when that count is zero, even if nothing errored. Treat a silent day as a failure.

One gate

An AIOS can spend your money and publish under your name. So before you leave it alone, you decide what it may never do without you.

The line is not "important." It's reversible. Anything the system can undo, let it do freely — a draft, a file, a database row, a report. Anything it cannot take back needs your hand on it: money leaving the account, a message to a customer, something published in your name, anything deleted.

This one has real agreement behind it. Every serious writeup on running agents in production names the same short list — sending messages, deleting data, charging cards — as the place a human must stay in the loop. It's the one piece of consensus in the whole field that's about judgement rather than engineering.

And make the gate structural, not a rule anyone has to remember. In my system the gates are three columns nobody but me is allowed to write. The agents can prepare everything up to that line, perfectly, and stop. They aren't trusted to hold back. They're unable.

The same principle covers deployment: nothing can push to production except from hardware I control. A cloud agent can decide whatever it likes about its own work and still cannot ship it.

That's what makes leaving possible. Not discipline. Wiring.

Prompt: List every action in this system that cannot be undone — anything that spends money, sends a message to a customer, publishes publicly, or deletes. For each, add an explicit approval step that only I can complete, and make it structural rather than a convention: the code path should not exist for an agent to complete it alone. Everything reversible runs without asking.

---

Where to run it

Two options, and the honest answer is that either works.

An always-on machine you already own. A spare desktop, a Mac mini in a closet. Free, and you own it completely. The catch is that "always on" has to be actually true — a machine that sleeps, or that lives on a home internet connection that reboots, will fail in the most confusing way possible: intermittently, at night, with no error. If you go this route, disable sleep first and test it by unplugging your monitor and walking away for two days.

A VPS. Mine is a twelve-dollar-a-month DigitalOcean box. One CPU, and that's enough — most of what an operating system does all day is waiting on an API, not computing. The advantage isn't power, it's that it doesn't live in your house. It doesn't get unplugged, it doesn't sleep, it isn't affected by your router.

Start on whichever you have today. Because everything lives in the repo, moving from one to the other later is a clone and a bootstrap, not a migration.

What matters more than the choice: it must be a machine you own, not capability you rent inside somebody else's product. Rented capability changes its pricing in March and deprecates the feature you built on in June.

Your first two weeks

If I were starting Monday with nothing:

The machine in week one, the workshop and the leash in week two.

Day one. Write down the one job. One sentence, with the dollar figure it costs you today in time or missed work. Create the repo before you write any code, because the repo is the workspace, not the archive.

Day two. Build the door and the brain. An endpoint that authenticates, logs and refuses a repeated key, and a single function every model call goes through. Nothing clever. A hundred lines.

Day three. Add the ledger and the ceiling. Check before you spend, record after, refuse when the month's number is hit — and put that ceiling in the rulebook file rather than in the code, because it is the first number you will want to change.

Day four and five. Build the one job end to end, using only those pieces. Make it finish and tell you it finished.

Day six. Draw your gates. List everything the system could do that you couldn't undo, and make each one impossible without you. Do this before the first real run, not after.

Day seven. Run it on real work. Not test data — the actual thing, the actual channel, your actual name on it.

Week two, day one. Add the proof. The version endpoint, and a daily count of outcomes that reached a customer. Set it to shout at you on a zero.

Week two, day two. Turn on the gate in CI: tests run on every pull request, merge blocked on red. Then break three things on purpose and confirm the tests actually catch them.

Week two, day three. Stop editing the box. From here everything goes through a branch, even your own fixes. This will feel slow for about a day and then it will feel like the only sane way to work.

Week two, the rest. Add the second job. Notice how much less it costs than the first.

What you'll have

At the end of two weeks: one real job that runs every day without you, a workshop where changes get reviewed before they land, and a leash that makes the irreversible things impossible to do alone.

Then add the second job, and notice how much less it costs than the first — because the machine, the workshop and the leash are already there, and the second job only has to be the job.

If you want help deciding which job to start with, book a working session and we'll pick it together.

---

The AIOS series

  1. The Operating System Built for You — why nothing you already use was built for the person doing the work
  2. 48 Jobs You Can Hand to an AI Operating System — every job it can take off you, grouped by what each one costs
  3. What an AIOS Is Made Of — the machine, the workshop and the leash
  4. How to Build an AIOS — nine prompts, where to run it, and a two-week order ← you are here

Once yours is running, the next series takes it further — the org chart, twenty agents staying out of each other's way, the shared brain, the watcher, and shipping to the box that runs your company from wherever you happen to be: Run Your Company From Your Beach Chair.

Which part of your week should a machine be doing?

Six questions, about a minute. At the end you get a straight read on what I'd build inside your company — even if the honest answer is “nothing yet”.