The pitch that got me into this was simple: define what you want, go to sleep, wake up to working code. In December 2025 I wrote that from where I was standing you would need to hire someone to babysit the process around the clock for that to work. I spent the following months making it possible to leave my own runs alone for hours.
Much of that work has been outside the model. Runs died when I closed a browser tab. Agents edited files they had been told to leave alone. I also killed a healthy long run because I mistook it for a hung one.
Before walking away, I need to know where the agent can write and whether it is alive, finished, stuck, or waiting on me. A separate checkout keeps ordinary coding edits out of the live tree. The output still needs verification, and the process retains whatever access its host account permits.
Give coding runs their own checkout
The merge behavior below describes aidd v3.2.0, my coding-agent orchestrator, released September 25, 2026.
For a long time, agents edited my live working tree.
On June 18, 2026 running aidd’s own start script during verification did a git reset against my live working tree and threw away an in-flight commit of mine. I got it back with git merge --ff-only; the commit object was still recoverable. Worktree mode would not have prevented that start-script failure, but the incident sharpened the broader question of checkout separation for routine coding runs.
Worktree mode landed the next day. It remains opt-in in aidd v3: Use isolated worktrees in Settings applies to coding runs launched through the web or Director, not every recipe or pipeline run. A CLI coding run can request --worktree directly. When enabled, aidd creates a linked git worktree on a branch named aidd/run-<id>. The coding run gets a separate checkout rather than my live one. The normal outcomes are:
- Merge. The work is clean and the live tree is clean, so the run branch is merged back into the project. aidd fast-forwards when possible; if the branches have diverged, it merges in a scratch checkout at the live branch tip, then fast-forwards the live checkout.
- Park. The live tree is dirty, the scratch merge cannot be resolved, or the live branch has moved before the result can be fast-forwarded. The branch is held as
waiting_approvalwith its own exit code, and I look at it when I get to it. - Discard. The run failed, so its evidence is copied to the canonical run store before the checkout is removed. If that copy fails, aidd preserves the checkout rather than losing the evidence.
The merge path is automatic: a successful run does not wait for me to inspect its branch before merging into a clean live tree. I review the landed result afterward. When a merge needs conflict resolution, an agent works in a scratch checkout at the live branch tip. The live checkout only fast-forwards onto that result. Worktrees are not a host-access sandbox: agents can still write wherever their host account permits, and the setting does not cover every kind of run.
For this repository boundary, worktrees are a better fit than full clones. They share the object database, so creating one is cheap even on a large repository. When a run goes wrong, I can inspect, diff, and merge its branch with the Git tools I already know.
Separate checkouts also removed the obvious working-tree collision when two runs target the same project. That roughly doubled the throughput of an overnight window, but it did not make parallel work free of conflicts. aidd still needed feature leases and conflict-aware metadata write-back so two runs could not quietly claim or overwrite the same work.
This is also where my design converged with the wider tooling. Codex now uses dedicated background worktrees for parallel and scheduled work, with a human reviewing the result. I arrived at the same shape independently in June.
A linked worktree separates checkouts, not machines. It still shares Git metadata with the main repository, and the process can reach whatever its host account and backend permissions allow: SSH keys, API credentials, other directories, network access. aidd currently invokes several backends in approval-bypass modes so they can work unattended, which I treat as trusted-input automation. The worktree gives me reviewable merge, park, and discard behavior. An OS sandbox or microVM limits what the process can reach. As of August 2026, Claude Code can sandbox Bash commands with filesystem and network limits on macOS and Linux, Codex has native sandboxing on Windows as well as macOS and Linux, and Docker Sandboxes can put the entire agent inside a microVM. For untrusted repositories or prompts, add that outer boundary and narrowly scoped credentials; do not substitute a worktree for it.
Restricting writes
A separate checkout protects the live working tree from ordinary run edits. A second, narrower control protects specific parts of the checkout from specific kinds of run.
When my orchestrator adopts a codebase it did not write, the promise is that it does not modify the application’s own code. It examines the project and writes metadata and reports. Metadata-only runs get a write allowlist of exactly one directory, and the orchestrator snapshots the worktree before each step and mechanically reverts any write outside it.
I tried that lane on StarSync, another of my own projects that aidd had not created, the same day it was built. The first run found a hole within the hour. The guard watched the working tree for uncommitted changes. So the agent committed the forbidden file, and the guard never saw a thing.
I had not considered that path. The guard needed to examine staged changes and commits as well as the working tree. An agent routinely moves changes between all three, so checking only uncommitted files was never going to hold.
Test each of those paths, then try the guard on a real project. That first outside run found two more gaps in an hour.
Keeping runs alive
For months, closing a browser tab could take live work down with it. A web-process hiccup could do the same. That is a silly way to lose an hour of progress.
The CLI now owns the run and writes heartbeats to disk. The panel reads that state when it is open. You can start a run from the terminal, panel, or a scheduled job, and the panel will find it. The twelve-factor app’s Processes and Disposability guidance covers the related lifecycle concerns. Here, the important part is that closing the panel no longer takes the worker with it. That was painful to retrofit after building the panel first.
Detaching a process sounds like a one-line change. On Windows it was four separate bugs, and I proved each fix with a minimal reproduction before trusting it.
taskkill /Fdelivers no catchable signal. Graceful shutdown cannot exist unless you build it yourself. I added aPOST /api/v1/admin/shutdownendpoint so the process can be asked politely from the outside, instead of pretending a kill signal is a request.- Child processes inherit the parent’s listen socket. On Windows under Bun 1.3.14, calls through the
node:child_process.spawncompatibility API let children inherit the parent’s HTTP listen socket. A launched app pinned the orchestrator’s own port; after a restart the port stayedLISTENING, attributed to a dead PID, until reboot. I moved the affected long-running backend launches toBun.spawn, which restricts inherited handles. That does not mean every helper uses it: process cleanup and provenance checks still have Node child-process calls. - Children live in the parent’s job object, and parent exit kills the job.
unref()does not help. A “detached” run died the instant the panel restarted, proven with a repro where the child wrote zero lines and died with an empty stderr, which is exactly as fun to debug as it sounds. The fix is ugly but it holds: a transient hidden PowerShell bridge usingStart-Processbreaks the new process out of the job object, the real argv travels in a JSON payload file so user arguments cannot inject shell metacharacters, and the run’s PID is reconciled from its first heartbeat. - A healthy long run and a hung one look identical. This caused a real misdiagnosis during beta. I killed something that was working.
On Windows, test what happens when you close the panel, end the terminal, and restart the backend. A child that survives one of those may still die during another.
For the fourth problem, I added a heartbeat. The run writes a timestamp at a known interval, and the panel compares it with the current time. That gives me an indication of whether the process is still reporting, which is much more useful than guessing how long a task ought to take. I still need the run’s output to tell me whether it is making progress.
I added failure-reason recording to the coordinator one release before automatic retries. I wanted to be able to see why an attempt failed before letting the next one start on its own.
Give the run a way to stop and ask
Not every blocker is a bug. Some are decisions no agent should make on your behalf, and the correct behavior is to stop with a written explanation rather than guess.
My runs can mark work as waiting_approval and file a written analysis of the ambiguity. The first real workout of that loop produced three product decisions across template work, all genuinely ambiguous. The agents wrote up the options and stopped. Two weeks later my decisions came back and were implemented in a day.
The most interesting one was a security tradeoff. An attacker could lock a victim’s account with a few bad password guesses, and the lockout also ejected the victim from sessions they were already signed into. Three fixes were possible at different costs, and I picked the surgical one: a lock that blocks new sign-in attempts but leaves live sessions alone. That is not a decision I want inferred from a prompt, and it is not one worth stalling an entire overnight run over either.
The run needs a way to record that question and continue with other work until I answer it.
Watch the budget, warn before you enforce
Runs get cost and token budgets. I shipped them warn-only on purpose: they record and they warn, but they do not kill a run.
The reason is that I did not yet trust the numbers, and I was right not to. My benchmark harness was double-counting one backend’s cached tokens, quietly handing that stack composite scores it had not earned.
Here is the corrected snapshot. The aidd leaderboard was generated July 2, 2026 from five agentic task types: interview, audit, remediation, validation, and quiz. Its composite weights were 45% correctness, 25% reliability, 20% time, and 10% cost.
| Benchmark stack | Composite score | Five-task suite cost |
|---|---|---|
native-glm51-high | 0.972 | $0.63 |
kilocode-glm52-low | 0.955 | $0.50 |
claude-code-opus-max | 0.947 | $3.01 |
The $0.63 native GLM 5.1 run topped that composite ranking. KiloCode with GLM 5.2 was the cheapest of the three at $0.50 but did not rank first, while the Claude Code Opus run cost nearly five times as much as the leader. Those prices and scores belong to that run set, manifest, and pricing table; fixing the accounting changed the ranking. Enforcing budgets against the bad numbers could also have stopped runs that were still within budget.
I left enforcement off while checking those measurements against the underlying runs.
Know what your agents can actually reach
After freezing my template, I built a read-only scanner to inventory what my agents could reach, including remote endpoints, credential paths, dynamic tools, and broad permissions. It reports that access; it doesn’t restrict it.
I had added access over several months and couldn’t reliably remember the full set. I wanted to read the configuration together before leaving those agents running.
You do not need a product for this. A script that walks your agent configuration and prints every endpoint, key path, and permission scope, run occasionally, will tell you things you did not know.
What unattended still does not mean
I run agents unattended for hours at a time. Some things have not moved:
- Merges are gated on a clean live tree. If I have work in progress, the run parks rather than merging. That keeps the automatic merge from landing over my unfinished changes.
- Review still happens. I read the result after the run.
- The first run of anything new is watched. Every guard hole described on this page was found by watching a run I could have walked away from.
Where this came from
- Coding Agents on Autopilot - the original problem statement, before any of this existed.
- Detached Runs and Harder Prompts - the run/UI split.
- Four Windows Process Bugs, Each Proved with a Reproduction - what an unattended run hits on the Windows process model.
- I Deleted the Winner of My Own Bake-Off - the June 18 git reset that forced the isolation work.
- Give Every Agent Run Its Own Worktree - worktree isolation as shipped, with the three outcomes.
- The Guard Watched the Working Tree. The Agent Committed. - the write allowlist, the guard hole, and the blocker loop’s first real test.
- The Pivot: Going Public - what happened when all of it ran on a clean machine against code I did not write.