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.