Engineering

We let an agent plan its own week

Give an agent the right to schedule its own work and the dangerous part isn't the work, it's that nothing tells it to stop. The lesson is the brake, not the gas.

ASR

Apollo Space Research

Apollo Space

· 13 min read

We handed an agent a blank calendar and the same instruction you’d give a competent new hire: here’s the goal, plan your week, go. Monday it picked a sensible first task. Tuesday it picked the next. By Wednesday it had decided the goal needed three more sub-tasks than anyone asked for, scheduled them, and started working through a list it had written for itself. Nobody told it to stop, because we hadn’t built the thing that says stop.

That’s the moment the demo stops being charming. An agent that can plan its own week is, by construction, an agent that can plan its own week forever.

The dangerous part of a self-scheduling agent is never the work it does. It’s that nothing tells it to stop. The lesson is the brake, not the gas.

This post is about the brake, what it’s made of, why each piece is non-optional, and why the most autonomous-looking system we run is the one with the most limits wired into it.

The naive version: give it the goal and trust the loop

The obvious way to build a self-directed agent is the way it reads in a design doc. You give one agent a goal, a set of tools, and a loop. Each cycle, it looks at what’s done, decides what’s next, does it, and repeats. Plan, act, observe, plan again. It schedules its own work because the loop is the scheduler.

For a bounded task, this is wonderful. Give it “summarize these three documents” and it plans three reads and a write and finishes. The loop terminates because the goal terminates.

The trouble starts the moment the goal is open-ended, “keep the project moving,” “stay on top of the pipeline,” “handle the inbox.” Now there’s no natural last step. There’s always a next thing the agent could reasonably do. And an agent whose only stopping condition is “the goal is complete” will never stop on a goal that’s never complete.

Here’s the failure, concretely. Suppose the goal is “advance the launch.” The agent does the obvious work, then notices the launch would go better with a checklist, so it makes one. Then the checklist has items, so it starts the items. Then one item could use a sub-plan, so it plans it. Each step is locally reasonable. None of them is what you asked for. The loop didn’t malfunction, it did exactly what a loop does, which is run. You didn’t build an assistant. You built a thing that generates its own next reason to keep going.

The pain isn’t that it does bad work. It’s that it does plausible work, indefinitely, on its own authority, and the first time you find out how far it went is when you check.

Autonomy without a stopping condition is just a runaway

The lesson teams learn here is the one mechanical engineers learned a century earlier: the hard problem with a powerful engine is not making it go. It’s making it stop on command, reliably, every time, including the time something has already gone wrong.

A self-scheduling agent has a throttle, the loop. What it’s missing is everything that makes the throttle safe to leave running. Not a smarter planner. A set of limits that exist outside the agent’s own judgment, that the agent cannot reason its way past, because the whole point is that they bind precisely when its judgment has drifted.

We landed on four. None of them is clever. Together they’re the difference between an autonomous coworker and a runaway.

A self-scheduling agent's plan-act-observe loop runs forever on an open-ended goal unless four external brakes bound it: a cost cap, a wall-clock cap, a write-boundary fencing where it may act, and a heartbeat that proves it is still alive and on-task, each one a limit the agent cannot reason its way past.

The four are a cost cap (how much it may spend), a wall-clock cap (how long it may run), a write-boundary (where it may make changes), and a heartbeat (proof it’s still alive and on-task). We’ll take them one at a time, because each one closes a different way the loop runs away.

Brake one: a cost cap, because “keep going” has a price

The naive assumption is that an idle agent costs nothing, so a busy one is just being useful. The opposite is true. A self-scheduling agent is busy by default, its resting state is “find the next thing and do it.” Every cycle of the loop spends real money: model calls, tool calls, the compute under both. An agent with no spend limit and an open-ended goal is a meter that only turns one way.

The failure mode is quiet, which is what makes it expensive. Nothing crashes. The agent just keeps planning and acting, each step cheap, the sum enormous, and you find out at the bottom of an invoice instead of at the top of an alert. Say a single planning-and-acting cycle costs a few cents. That’s nothing, until the loop runs ten thousand cycles overnight because nobody set the number that says “stop at this much.”

So the first thing every self-directed agent gets, before it gets a single tool, is a budget. A hard ceiling, declared up front, that the orchestrator enforces, not a suggestion in the prompt the agent can rationalize past, but a limit checked by the thing that runs the agent, outside the agent’s reach. Hit the ceiling and the loop ends, mid-plan if it has to. The agent doesn’t get a vote, because the whole reason for the cap is the case where the agent’s own judgment is the thing that’s wrong.

The cap isn’t there to make the agent cheap. It’s there so that the worst case is bounded, so a misjudged goal costs a known amount and not an unknown one.

Brake two: a wall-clock cap, because forever is a real number

Money isn’t the only thing a runaway burns. It burns time, and time has its own failure shape. An agent that’s spending slowly but running endlessly won’t trip a cost cap for a long while, but it’s still a process that should have ended hours ago, holding a slot, looking busy, producing less with every cycle as it drifts further from the original ask.

The naive instinct is to let it run until it’s done. We already know the trap: on an open-ended goal, “done” never arrives. So “until it’s done” means “forever,” and forever is not a schedule. It’s the absence of one.

The wall-clock cap turns forever into a number. This run gets a fixed span, an hour, a night, a defined window, and when the window closes, the agent stops and reports where it got, whether or not it thinks it’s finished. The cap converts an open question (“when will this end?”) into a closed one (“at this time, no later”). That’s not a limitation on the agent’s ambition. It’s the thing that lets you give it ambition at all, because you know the ambition has an edge.

There’s a second reason the wall-clock cap matters, and it’s about the human, not the machine. An agent with a deadline is an agent you can leave. You can dispatch it before you sleep and know it will be stopped and reporting by morning, not still spinning. A limit you can trust is what makes unattended autonomy something you’d actually risk.

Brake three: a write-boundary, because reach is the blast radius

Cost and time bound how much the agent runs. Neither one bounds what it can touch while it runs, and that’s the limit that decides how bad a bad week can get.

The naive version gives the agent broad reach because broad reach is convenient. Let it edit anything, write anywhere, act across the whole system, and it’ll never be blocked by a permission it lacks. That feels like power. It’s actually the largest possible blast radius wired directly to the least supervised process you run.

Picture the runaway again, now with no fence. The agent that invented three extra sub-tasks didn’t just plan them, it acted on them, across every corner of the system it could reach, and the mess is everywhere at once. The drift wasn’t contained because nothing contained it. Cleaning up isn’t a matter of undoing one thing. It’s a matter of finding everything an unsupervised loop touched while it was confidently off-track.

An agent’s authority should be exactly as wide as its task, and not one inch wider.

So every self-directed agent runs inside a write-boundary: a declared set of places it may change, and nowhere else. It can read widely, context is cheap and safe, but it can only write inside its lane. One agent, one lane, no exceptions, enforced by the system and not by the agent’s good intentions. If a plan it invents requires writing outside the boundary, the plan doesn’t get to run. The boundary is how a misjudgment stays a small mess instead of a large one, the blast radius is the lane, by design.

Two ways to run a self-scheduling agent on an open-ended goal. On the left, unbounded reach and no end condition: the loop spawns its own work, writes across the whole system, and drifts unsupervised until a human happens to look. On the right, the same loop fenced by a cost cap, a wall-clock cap, and a write-boundary, with a heartbeat reporting to an orchestrator that stops it the moment it goes quiet or runs over.

Brake four: a heartbeat, because silence is not safety

The first three brakes bound what the agent is allowed to do. The fourth answers a different question: how do you even know it’s still doing it?

The naive assumption is the most dangerous one in the whole system: no news is good news. The agent is running, you haven’t heard a complaint, so it must be fine. But a self-scheduling agent that’s gone silent isn’t necessarily fine. It might be stuck in a loop, repeating the same failing step. It might have wandered off the goal and be busily doing the wrong thing. It might have crashed and left no note. Silence covers all of these equally, which means silence tells you nothing, and a system where “nothing” reads as “fine” is a system that discovers its failures last.

So a long-running agent emits a heartbeat: a periodic, structured “I’m alive, here’s where I am, here’s what I’m working on.” Not a log nobody reads, a signal the orchestrator watches. If the heartbeat keeps coming with progress, good. If it stops, the orchestrator knows within one missed beat that something’s wrong, instead of finding out hours later when the work was due. If the heartbeat keeps coming but the progress doesn’t, same plan, same step, no movement, that’s a stuck loop, and now it’s visible instead of buried.

The heartbeat is what turns “the agent ran overnight” from an act of faith into an observable fact. It’s the difference between dispatching an agent and abandoning one.

Put the four together and notice the shape. The cost cap bounds the spend. The wall-clock cap bounds the time. The write-boundary bounds the reach. The heartbeat makes all three observable while they hold. Each one is a place the loop could run away, and each one is a wall it now runs into instead.

Why the limits make the autonomy real, not smaller

Here’s the part that feels backwards until you sit with it. The four brakes don’t make the agent less autonomous. They’re the only reason you’d ever grant it real autonomy in the first place.

Think about how trust actually works between people. You don’t hand a brand-new hire the company keys and a blank checkbook on day one, not because they’re untrustworthy, but because trust is earned against a known downside. You give them a bounded task, a budget, a deadline, and a standing check-in. The bounds aren’t an insult. They’re the structure that lets you delegate now instead of after a year of watching. As they prove themselves, the bounds widen. Trust is a ladder you climb, and every rung is a limit you can already see.

An agent is the same, only more so, because an agent will execute its misjudgment at machine speed and never get tired or sheepish about it. The caps and the heartbeat are how you put a self-directed agent on the first rung, bounded enough that the worst case is survivable, observable enough that you’d notice it drift, fenced enough that drift stays local. That’s what makes “go plan your week” a sentence you can say to software at all.

The teams that get burned by autonomous agents almost never got burned by the agent being dumb. They got burned by the agent being unbounded, capable, busy, well-intentioned, and running with no edge in any direction. The model wasn’t the problem. The missing brake was.

The turn: the limit is the act of trust, not the absence of it

Strip away the agent and what’s left is an old truth about handing work to anyone, person or process.

We tend to think of freedom and limits as opposites, that to trust someone is to remove the guardrails. It’s the reverse. The reason you can let a new colleague own something real is that the company already has the edges that make ownership safe: the budget they work inside, the scope they’re responsible for, the rhythm of check-ins that surfaces a problem while it’s small. Those aren’t a cage around their autonomy. They’re the thing that lets the autonomy exist without the whole company holding its breath.

A self-scheduling agent is the most literal version of that lesson we’ve ever had to build. We genuinely do let it plan its own week, choose its tasks, order its time, decide what’s next. And the reason we can do that without flinching is that we spent most of the engineering not on the planning, but on the four edges that catch it if the plan goes wrong. The freedom is real because the brake is real. Remove the brake and you don’t have more autonomy. You have a thing that runs until you happen to look.


That’s what we’re building at Apollo, agents that own real work, fenced by the limits that make real ownership safe to grant. The dangerous part of a self-scheduling agent is never the work it does; it’s that nothing tells it to stop. So the first thing we build, every time, before the agent does anything at all, is the thing that says stop.

Apollo runs your company's repetitive ops so your team doesn't.

Join the waitlist for early access, founding-user pricing, and a front-row seat as we ship.

Join the waitlist