Factory Station Map

Counted in evidence 20 while tasks 013–015 were still building, so Built is understated today. Spot-checked on 12 rows, of which 2 were corrected. 52 of the Not addressed rows are premises, not work.

Total: 346

v3 now: a program runs one task; the rest is not built yet

Merged on factory-v3 main: tasks 001–019. Only the factory-v3 repo runs on it.

Changes from v2, and why:

Source: draft version summary

DECIDES THE LINE JOBS RUN INSIDE KEEPS IT RUNNING CHANGED Owner gate Once per phase by ruling; a deck… Intake Not present CHANGED Planner Opus writes specs and rules on stops REMOVED Ruling step No ruler yet; the loop stops CHANGED Task file Adds issue: and a global ID CHANGED Claim Started by command; no queue yet CHANGED Coder Fresh container; only the diff come… CHANGED Checks Rerun on a clean tree CHANGED Reviewer Own job; sees every ruling CHANGED Merge Host tests, local fast-forward, push REMOVED Deploy No deploy step yet CHANGED Model route Usage proxy logs each call CHANGED Job isolation One container per job; --sysbox gives a job its own Docker daemon CHANGED Forge Issues, main ruleset; no PRs CHANGED Driver loop/run_task.py; calls no model CHANGED State state.json, lock, enum stop reasons REMOVED Watchdog None; rerun resumes CHANGED Wake-ups Brief on disk, exit code 3 CHANGED Measurement Event log and report CLI REMOVED Status view None; read the report
  1. Owner gate

    CHANGED

    D18 sets one approval per phase, on a deck. Deck factory-v3-r4 closed 15 of 15 go. The loop has no gate of its own: it runs when someone starts it.

    Changed: Per-task go is gone. The per-phase gate is a rule, with no code behind it yet.

    Why: Owner-go holds of 13–14 h for under 20 min of work.

    Measured: Not measured: owner hold time is not recorded. [not measured]

    Source: design D18, loop README

  2. Intake

    Not present. One Opus planning pass writes task files.

    Measured: Not recorded in draft. [not measured]

    Source: evidence 20

  3. Planner

    CHANGED

    Opus writes task specs and rules when the loop stops. On task 016 the planner read the diff, then reran with --auto-merge.

    Changed: Dispatching, reading results, writing round notes and merging moved into a program.

    Why: D15: running out of subscription usage is what halted the factory.

    Measured: <b>Not recorded.</b> The report shows "planner: not recorded" on every task, so the main goal has no v3 number yet. [not measured]

    Source: loop README; instrument report

  4. Ruling step

    REMOVED

    The loop stops, writes a brief of at most 4 KB and exits 3. A person answers with --ruling and reruns. That ruling goes into every later coder and reviewer task, and the reviewer may not reopen it without new evidence.

    Changed: Bot and ledger removed. Rulings now reach every round.

    Why: O-325: rulings never reached the reviewer or later rounds in v2.

    Measured: 7 interventions recorded, all on tasks 012 and 013. None on 014–019. Eight tasks, so this is thin. [loop README; instrument report]

    Source: loop README; instrument report

  5. Task file

    CHANGED

    issue: N names the issue the task resolves. The ID becomes factory-v3-017 across events, state directory and worktree. risk, escalate_if and caps are ruled by D18 but not in the task files yet (019 has status, issue, files).

    Changed: One ID now follows a task through its events.

    Why: D12 and D18: an ID must be unique before any second repo.

    Measured: Not recorded in draft. [not measured]

    Source: loop README; task 019

  6. Claim

    CHANGED

    A person or planner runs loop.run_task on one task. Each task has its own state directory and lock, and the loop runs no tasks in parallel.

    Changed: The claim queue is gone for now.

    Why: Build order: the task path first, the workflow around it later (D15).

    Measured: Not measured: there is no queue to measure. [not measured]

    Source: loop README

  7. Coder

    CHANGED

    Each round is a new job in a fresh container. The loop applies its diff with git apply --index and commits it. Runtime gap: the bake-off chose opencode (8 of 8, $0.33), but tasks 016 to 019 ran runtime: claude according to their result.json.

    Changed: The coder has no commit rights or checkout. A job dies without harming the branch.

    Why: About 23 of about 70 v2 stops were sandbox or permission denials.

    Measured: Implement stage median 11.4 min across all rounds, eight tasks. Last four tasks: 3.3, 7.8, 3.1 and 7.9 min. Not comparable with v2's latest-attempt figure. [loop README; instrument report; result.json]

    Source: loop README; instrument report; result.json

  8. Checks

    CHANGED

    After the coder finishes, the job stops its leftover processes, removes gitignored files it made and reruns setup, then the checks. A failure goes back as a round with the commands, exit codes and output tails.

    Changed: Checks no longer see the coder's leftovers.

    Why: Task 012 found checks had run in the coder's tree.

    Measured: Bake-off re-score: 35 of 35 passing runs still pass when rebuilt from base plus diff. [design D9; loop README]

    Source: design D9; loop README

  9. Reviewer

    CHANGED

    A separate job that may write only .agents/reviews/NNN-roundK.md. Any must-fix sends the task back, even under Verdict: approve. Haiku 5.5 stays (D18).

    Changed: Rulings included in every round. Verdict read by a parser.

    Why: O-325.

    Measured: Review stage median 6.3 min across all rounds, eight tasks. Last four: 0.8, 1.6, 0.8 and 4.0 min. Reviews per task: 4, 4, 3, 2, 1, 1, 1, 2. [loop README; instrument report]

    Source: loop README; instrument report

  10. Merge

    CHANGED

    Rebase on main, run the host tests, then git merge --ff-only and push. Without --auto-merge it stops at READY_TO_MERGE. If main moved after the tests, it tests again.

    Changed: Delegation moved from a repo registry to a flag on the run.

    Why: D17: local merge now, PRs next.

    Measured: Eight accepted tasks. [loop README, D17]

    Source: loop README, D17

  11. Deploy

    REMOVED

    The loop ends at the push. The only repo on it is factory-v3 itself.

    Measured: Not recorded in draft. [not measured]

    Source: loop README

  12. Model route

    CHANGED

    Every model call in a job goes through a usage proxy, which records tokens and cost.

    Changed: Spend is measured per call rather than rebuilt afterwards.

    Why: D12 and D16: instrument first.

    Measured: Bake-off relay audit: 0 bypass lines. Task 017 replay: compacting gave 0 requests over 100k against 20, for $0.17 against $0.25, but 735 s against 469 s. [design D9, D12, D19]

    Source: design D9, D12, D19

  13. Job isolation

    CHANGED

    A fresh container for every job, with the coder free inside it. --sysbox gives a job its own Docker daemon.

    Changed: The container is the fence, not a list of commands.

    Why: v2's text denies stopped real work and did not stop bash.

    Measured: <code>supabase start</code> in a job: 3m17s cold, 12.1 GB of images per cold container. RAM caps studio at one or two such jobs. Peak memory 163–953 MiB on measured tasks. [design § 6 step 2; instrument report]

    Source: design § 6 step 2; instrument report

  14. Forge

    CHANGED

    Issues track the owner's inquiries. Ruleset protect-main blocks deletion and non-fast-forward, with no bypass. No PRs or check reporting yet. The forge inventory is still in progress under Issue #3.

    Why: D17.

    Measured: Not recorded in draft. [not measured]

    Source: design D17; Issue #3

  15. Driver

    CHANGED

    A deterministic program: implement, check, review, route, test, merge. Models run only inside jobs. Rerunning the same command resumes it. It replaces the Sonnet hand loop.

    Changed: Plain state machine with named stop reasons. Runs one task per command.

    Why: R4: v2's fragility pulled Opus into repair, 12% of planner tokens.

    Measured: Four real runs, tasks 016–019, no interventions. 016–018 took 1 coder round and 1 review; 019 took a repair round and 2 reviews. [loop README; change log]

    Source: loop README; change log

  16. State

    CHANGED

    One state.json per task, written atomically after each transition, a lock, and a fixed list of stop reasons.

    Changed: No ledger, so nothing is left to go stale. Reasons are an enum, not text.

    Why: v2's 80 stale escalations and substring matching.

    Measured: Crash resume is covered by tests. Not measured under real load. [loop README]

    Source: loop README

  17. Watchdog

    REMOVED

    No supervisor. Every job records why it died (signal, Docker events). Rerunning the command reuses finished jobs. Nothing notices a loop that died until someone looks.

    Changed: Supervisor and its restart faults removed.

    Why: Its faults are the case against keeping it.

    Measured: 2 kills recorded: SIGKILL on 013, SIGABRT on 015. 013 and 017 peaked at 111 and 428 pids. [instrument report; loop README]

    Source: instrument report; loop README

  18. Wake-ups

    CHANGED

    A stop is a brief on disk and an exit code. There is no notification and no event. Whoever started the run has to look.

    Changed: Polling sessions removed from the task path. Nothing replaces the push yet.

    Why: Slice order (D15).

    Measured: Prototypes only: see the Events view. [loop README]

    Source: loop README

  19. Measurement

    CHANGED

    Every stage, model call and container sample goes to events.jsonl; instrument.cli report reads it. Prompts, outputs and env values are not in the event log. Since task 019 each job keeps a redacted transcript, and instrument.cli reads reports what it read and re-read.

    Changed: Cost per accepted task is the headline.

    Why: D12, D16.

    Measured: $0.43 per accepted task over eight tasks (report headline $0.4431 includes two probes). A lower bound: 3 stages have no cost. Planner tokens not recorded. [instrument report]

    Source: instrument report

  20. Status view

    REMOVED

    No status view. State is in files and the report CLI.

    Measured: Not recorded in draft. [not measured]

    Source: loop README