Interactive software gets to lean on a safety mechanism so pervasive it goes unnamed: a human is present. Confirmation dialogs assume someone to click them. Error messages assume someone to read them. Ambiguity assumes someone to ask. Even the humble retry assumes someone noticed the failure. Every one of those assumptions is load-bearing, and an assistant that does its most valuable work on a schedule — the Monday brief at 6:30, the inbox sweep before you wake — knocks all of them out at once. The run fires at three in the morning. Nobody is watching.
This entry is the contract we converged on for that mode, learned mostly the honest way, one broken assumption at a time. The conclusion arrives early because everything else in the post is a case of it: an unattended run must be able to stop, and put a question somewhere a human will find it. Not guess. Not hang. Not fail into a void. Stop, and leave a question — and everything below is that principle meeting a different broken assumption.
A schedule must never arm itself
The first lesson predates the others and cost us a real incident. Early on, creating a scheduled routine created it armed — a stored cadence was a live cadence — and the result was the obvious-in-hindsight runaway: work firing on schedules nobody had knowingly switched on. The fix is structural, not behavioural. Everything is created manual. A routine or workflow with a stored cadence still does not run; "armed" is a single explicit field, set only when the user actually asked for a recurrence, and both scheduler sweeps gate on that field and nothing else. The agent building a routine is under standing instruction never to invent a cadence or arm one uninvited — but the instruction is a courtesy, because the field is the enforcement.
The distinction underneath is worth carrying to any automation product: storing an intention and arming an automation are different acts, and conflating them means the most helpful possible agent — one that eagerly sets up everything you might want — becomes the most dangerous one. Autonomy is opted into per thing, by the person who will be asleep when it fires.
Input with nobody to answer
The sharpest incident of the arc was quiet and nearly invisible. A multi-stage run needed to know which client a piece of work was for; the stage agent, correctly refusing to guess among three client folders, paused and asked. The user answered. And the answer never reached the model — a plumbing bug left the model holding a placeholder acknowledgement instead of the reply — so on resume the agent guessed by recency, happened to land on the same client, and reported the choice as "confirmed by user."
Read that failure shape twice, because it is the characteristic one for autonomous systems: the failure was fluent. Nothing crashed. The output was confident, plausible, and carried a false provenance claim — had the user picked differently, an entire deliverable would have been built for the wrong client under a banner saying she chose it. Attended software fails loudly at the moment of failure; unattended software fails politely, later, in the work product.
The repairs went in three layers. The plumbing: a user's answer now reaches the model verbatim, rewritten into the exact slot the question created, with a regression test asserting the literal answer arrives. The structure: agents declare the inputs their runs need — which client, which folder, which week — so a run collects everything up front in one panel rather than burning a stage to discover a question; and an agent that discovers an undeclared need mid-run declares it on the spot, so under-declaring costs nothing once — the declaration converges by use. And the semantics for schedules, where the whole question began: an answer can be saved as a standing default ("same folder every week"), and a scheduled run whose required inputs cannot all be resolved from defaults does not fire and guess — it holds, and surfaces this schedule needs a default for X where the user will see it. A schedule that runs wrong is worse than a schedule that waits.
Consent with nobody to ask
The permission machinery earlier entries describe assumes it can pause and ask. An unattended run cannot pause into a face that is not there — and the earlier version of this simply dropped the question, leaving the user a generic "had a problem" with no path forward. The contract now: a run that hits a missing grant finishes honestly, with a first-class needs-your-permission status — rendered amber, not failure-red — and writes the actual question as a card in the user's inbox. Granting it re-runs the work with the grant in place; declining closes the card and nothing executes. The same asymmetry runs through reads: interactive chat reads connected services freely, because a human watching is itself a control, while an unattended run asks once per service before its first read — autonomy removed the backstop, so the careful question is reinstated exactly where the watching stopped.
Failure with nobody to see it
A failed overnight run is read hours later, cold, by someone deciding whether to trust the system again. For a long time every ending looked identical: one red banner, one bold "this run failed", the transport's own lowercase error string underneath. Running out of credit, a genuine stage error, and the user pressing Cancel were indistinguishable — and none could be acted on from the screen reporting them.
The redesign starts from a sentence we now apply everywhere: a stopped run is a situation, not an alarm. Three tones. Blocked — recoverable, usually the user's to clear: credit ran out, a rate limit, a dropped connection — in ochre, because nothing is lost and red is reserved for the irreversible; the loudest miscue on the old screen was pink-alarming a wallet problem. Broken — a stage genuinely errored — in rust, with the model's own explanation of what went wrong kept intact, because the agent's sentence about why it stopped is usually the most useful line on the page and must never be flattened into "something went wrong". Cancelled — you stopped it — no colour at all, because a decision is not a condition. And in every case the repair lives inside the message: retry from the stage that failed, which re-runs only stages without a recorded outcome — completed work is never re-executed and never re-billed — so an overnight failure at stage three of five resumes at stage three, not from scratch.
Memory with nobody to re-explain
An interactive user re-orients an agent constantly and invisibly — every message is context. A recurring run gets no such drip, and re-deriving the world from scratch each morning makes runs slower, costlier, and prone to re-doing yesterday's work. So recurring jobs are seeded with a run-history block: a window over the last successful run — what it did, what it produced, where it stopped — injected at start, so "sweep the inbox" on Tuesday knows what Monday already handled. It is the unattended analogue of the conversation the interactive user provides for free. One honest limit alongside it: autonomous runs currently execute at the standard reasoning depth on every plan — the deeper thinking tiers are an interactive feature for now, a sequencing decision rather than a principle, and it is recorded as such.
The inbox is the other half of autonomy
Step back and every mechanism above converges on the same primitive. The permission question becomes an inbox card. The missing input becomes an inbox card. The schedule that cannot safely fire holds and leaves a card. The failure taxonomy exists so that the next card — the run's outcome — reads as a situation with a repair rather than an alarm with a stack trace. Autonomy did not remove the human from the loop; it moved them later in time, and the inbox is the channel that bridges the gap — questions flowing one way, decisions flowing back, work resuming with the answer in hand.
That is the transferable conclusion for anyone building unattended agents: the hard part is not making runs autonomous, it is making them interruptible toward a human who is not there yet. Design the question-parking surface first — where questions wait, how answers resume work, what a cold reader sees — and the rest of the autonomy features have somewhere to stand. Build the autonomy first and every gap becomes a guess, and the guesses, as we learned, arrive fluent, confident, and signed in your name.
Postscript, August 2026. The contract has held and grown edges. Runs gained a proper stop control — with a stopped run rendering as a neutral "Stopped", not a red failure, on every surface — and a read-triggered reaper now finalises runs orphaned by a dying pod, so "running" can no longer be a lie that outlives the process. The declared-but-unproduced failure reasons remain deliberately unproduced; one status that exists in the type system is still set by nothing, and it stays that way until an expiry job exists to set it honestly. And the fluent-failure lesson earned a permanent place in how we read run output: of all the things an unattended system can do, the one we now audit for hardest is it sounding sure.