Running it

Running the machine

./factory list              # what is configured here, what is up, last beat
./factory cleanup <name>    # remove one factory and everything it left behind
./factory stop --all        # the machine-wide sweep
./scripts/factory-health.sh # what is actually beating

cleanup shows you every path and asks first, then stops the factory’s sessions, removes its child worktrees, and deletes its state under ~/.factory, its config, and the watermark in its workspace. It never touches GitHub, and it leaves a worktree with uncommitted work standing. cleanup --all takes the machine back to a fresh clone, which is how you start the onboarding conversation over.

./factory is idempotent: it starts what is down and leaves what is up alone. Run it on a timer and a factory survives a reboot. launchd/com.hev.factory.plist is the template, and it runs ./factory --no-picker every 300 seconds. Fix the path inside it, copy it to ~/Library/LaunchAgents/, then:

launchctl load ~/Library/LaunchAgents/com.hev.factory.plist

Five minutes is the sensing cadence, not spend. Each fire runs a deterministic sensor — a git fetch and a few scoped API calls, no model — and a beat runs only when the sensor saw something move: an approved plan landing, an inbox message, a worker changing state, the board being touched. A tick where nothing moved closes in bash for zero model invocations, so the number buys how fast a change is noticed. An hourly resync runs a full beat regardless, which is what bounds the cost of anything the sensor cannot see. On the resident runtime — the rollback — the interval is a ceiling on how long a dead loop stays dead.

Every config names a home_host, and ./factory skips any gaffer that does not belong to this machine. Two gaffers against one RFC source produce duplicate dispatch: two agents on the same task, one of them wasted. A stale clone on a laptop is the usual way that happens.

esc