49 hours, 124 iterations, no ship
At 7:05 p.m. UTC on April 20 I pointed my autonomous improvement loop at one of my own products and walked away. Just over 49 hours later it had completed 124 iterations, filed 313 findings, fixed 40 of them with regression specs attached, and classified 224 as noise it should not chase. Its last scored pass came in at a composite of 78.85. My ship bar is 80, plus zero task-level failures. The factory looked at two days of its own work and declined to call it done.
That run is the most honest summary of autonomous software delivery I can offer. There are two loud camps right now. One says three engineers can run a software factory and the rest of the org chart is legacy. The other says agents cannot be trusted near production code and the whole exercise is expensive theater. Both camps are arguing about the model. Almost everything that decides whether a dark factory works lives in the harness around the model: the gates, the evidence, the scoring, and one carefully placed human.
I run one of these factories daily. This piece is what I would want someone to tell me before building one: what the thing actually is, where the human boundary survives contact with production, where autonomy gets earned instead of granted, and the specific ways mine has broken.
What a dark factory actually is
The name comes from lights-out manufacturing: a plant that runs with nobody on the floor. Mine is a daemon that runs coding agents through a gated lifecycle. Work enters as a feature or a bug, and moves through phases. No agent can advance past a gate it has not satisfied, and the gates are checked by the orchestrator, never by the agent that did the work.
PLANNING -> HARNESS_DESIGN -> BUILDING -> SPEC_DRIFT_CHECK -> REGRESSION_GATE -> TRIAGING
|
ESCALATED (human off-ramp) <--- FIX_LOOP <-+
|
REVIEW_READY -> PENDING_AUDIT -> COMPLETED| Phase | Exit gates | What it prevents |
|---|---|---|
| PLANNING | codex_plan_review | Building the wrong thing confidently |
| HARNESS_DESIGN | harness_baseline_recorded, codex_harness_review, tier_distribution_check | A definition of done written after the work |
| SPEC_DRIFT_CHECK | spec_drift_check, stub_detection | Scope creep and TODO-stubs shipped as features |
| REGRESSION_GATE | harness_verification | Merging on the worker agent word alone |
| REVIEW_READY | mode-dependent: human, or auto-approve at quality 80+ with zero P0/P1 | Unearned autonomy |
Two of those rows do most of the governance work. The harness, meaning the programmatic checks that define done, is designed and baselined before implementation starts, then adversarially reviewed by a second model. And the regression gate runs those same checks under orchestrator control, so the agent that wrote the code never adjudicates its own success. That is the no-unverified-merge invariant I published as Verification-Gated Orchestration; I wrote about VGO in a research-style piece on this blog. In its substrate simulation, at 200,000 tasks per condition, a claim-trusting baseline produced false accepts among merges at rates up to 0.70, while the gated substrate produced zero unsafe merges. The factory is that paper running as a daemon. VGO is the governance spine; everything else here is muscle attached to it.
The QA department is a roster of simulated people
Gates catch code that fails its spec. They do not catch a product that passes every spec and still cannot be used. For that, the factory runs a persona harness: an improvement loop where LLM pilots impersonate specific users and try to get real jobs done through the actual UI and CLI, while the harness scores every attempt.
The personas are not scripts. Each one is a Pydantic trait sheet, and the pilot drifts inside those traits. It mistypes at the persona typo rate. It gives up when patience runs out. It clicks the wrong thing the way a distracted human clicks the wrong thing. The drift is the point: it surfaces friction no hand-written happy-path spec would ever encode, because I did not know to look for it.
| Persona | Typing speed (cpm) | Typo rate | Patience | Abandonment risk |
|---|---|---|---|---|
| Parent, 38 (careful reader) | 180 | 4% | 20s | 0.15 |
| Parent, 41 (skims, wants speed) | 160 | 6% | 15s | 0.25 |
| Teen, 15 (fast, privacy-skeptical) | 240 | 3% | 8s | 0.35 |
| Child, 11 (rambling, error-prone) | 95 | 12% | 10s | 0.40 |
Every act gets scored on five dimensions: effectiveness at 35 percent of the composite, accuracy 25, quality 15, edge-case handling 15, reliability 10. Effectiveness is checked against a completion signal that exists outside the transcript, a database row or a visible UI state, never against whether the pilot said it succeeded. Ship-ready means a composite of 80 or better with zero task-level failures.
What did the loop actually find? Of 313 findings: 89 reliability, 70 quality, 44 feature gaps, 44 accuracy, 36 bugs, 30 edge cases. The fixed list is unglamorous and real. There was no UI to invite a second family member at all. A sidebar link went to a 404. The signup form failed silently if a session already existed. The chat placeholder still read like the B2B product this platform was converted from. A fast persona harness finds the things your own familiarity has made invisible.
The discipline that makes the numbers trustworthy is the unfashionable one: 224 of 313 findings, 71.6 percent, were closed as WONT_FIX because they were harness noise, turn-budget artifacts, or pilot mistakes a human would not make. An autonomous loop that fixes everything it finds is mostly fixing its own reflection. The triage seat is where the judgment lives, and in my factory that seat is human.
The failure modes I actually hit
The best argument against factory evangelism is my own incident log, so here it is.
In iteration 4, the fix phase scaffolded a new accept-invite page without checking whether the route already existed. It did, under a different route group. Next.js refused to build, the primary chat surface returned 500s, and the next piloting pass dutifully registered 7 downstream findings against a regression the factory itself had caused. The loop spent a chunk of an iteration investigating its own vandalism. The fix was procedural, not clever: before any new page or route, glob for the path first. Cheap rule, real scar.
Same iteration, different lesson: the harness persists state after every act, and a manual WONT_FIX I entered mid-run was silently clobbered by the next per-act save. Two writers, one JSON file, no lock. State now goes through file locks and atomic renames, and the longer-term answer is an append-only event log. If your factory has a state file, it has a race; you just have not run it long enough yet.
Then there is the class of failure that looks like a product bug and is actually a harness budget. The default turn cap was 15 turns per job; wizard-style flows need 25 or more. Early runs kept producing max-turns failures that read as user abandonment. Almost all were the cap. Likewise, pilots kept landing at the signup page and giving up on jobs that required an existing invite, because the harness never created one. The fix was a bootstrap phase that mints real invites through the backend API before piloting starts. Rule of thumb from both: when the factory reports a failure, suspect the factory first. I made that mistake in the other direction once with a broken eval harness and nearly discarded a perfectly good model over a 0.0 score my own plumbing caused.
And the composite trend is not the smooth upward curve the demo videos promise. Sampled passes in this run scored 77.77, then 68.8, then 66.75, then 78.85. Fixes land, new findings land harder, and adding personas or acts resets the denominator. Anyone who shows you a monotonic score curve from an autonomous loop is showing you a loop that stopped looking.
Why the human review boundary survives
Here is the experiment that settled where the boundary belongs, run on another agent platform I operate. I generated the same client-loss post-mortem, an investigation across an 87-location hospitality enterprise, through four frontier models. Same data, same tools, same task.
The count of stalled equipment orders came back as 37, 38, 40 of 69, and 48. Total equipment units came back as 662, 694, and 900-plus across reports. Expected monthly volume was 16.1 million dollars in one report and 20-million-plus in another. Every one of the four reports was fluent, internally coherent, and confident. All four told the same true story in the large, and no two agreed on the numbers a board member would quote.
If four independent frontier models cannot agree on a numerator, then no single agent claim can be a merge authority, in analytics or in code. So the factory treats worker claims as triage input, never as acceptance. When an agent self-reports a gate as PASSED with no supporting harness evidence, a resolver pattern catches it and independently re-runs the harness rather than advancing. When a session hits its resume limit, the resolver refuses to pattern-match at all and passes through to a human, because a terminal that keeps dying is a signal, not a nuisance. And even a fully green feature does not go from REVIEW_READY to done in one hop; it lands in PENDING_AUDIT, which exists so that completed work stays inspectable after the fact.
The human boundary that survives production is narrow and specific: triage judgment on findings, review at the merge gate for anything that has not earned its lane, and every escalation the resolver refuses to eat. Everything else is compute.
Where autonomy is earned, not granted
The factory does have an autonomous mode, and its terms are written in code, not vibes. The resolver auto-approves a REVIEW_READY feature only when the latest quality score is 80 or above and the P0 and P1 finding counts are both zero. Miss any condition and a human reviews it. That is the entire policy, and its precision is the feature: autonomy is a lane, earned per gate with evidence, and revoked by a single P1.
The economics justify the strictness. In the VGO ablation, verification cost nothing in capability: true resolution was identical with and without the anti-false-acceptance machinery. But at equal repair budget, the claim-trusting condition resolved 52.3 percent of tasks while the gated condition resolved 78.6 percent, because accepting a false done terminates the repair loop early and burns the remaining attempts on nothing. That is a 50 percent improvement in genuinely resolved tasks for the same repair spend. Verification reads as overhead on an org chart and as margin in a ledger. For a board: the expensive line item was never the gate. It was every unverified claim the gate would have caught.
Notice what the model never appears in. Nothing in the gate table or the auto-approve rule names a vendor or a parameter count. I have swapped frontier models mid-session in this factory and written up the differences; the error profiles changed, the token economics changed, and the gates did not move. That is the property that makes a factory operable: models are staff, gates are law.
The claim I will defend
The evangelists are wrong about the direction of the headcount story. A working dark factory does not delete the engineers; it concentrates them at the boundary, in triage and at the gates, where one person's judgment now multiplies across 124 iterations instead of one pull request. The skeptics are wrong about the ceiling. My loop found and fixed 40 real product defects in two days, with regression specs, under an invariant that provably prevents the failure mode skeptics fear most, the confident false done.
So here is the position, and it cuts against both camps. Autonomy percentage is a property of your verification substrate, not your model subscription, and you can audit it: if your agent system cannot produce per-gate evidence for its last hundred accepted changes, your autonomy number is a guess dressed as a metric. And no factory should merge unattended before its own composite clears the bar it enforces on the product. Mine has not cleared 80 yet, so mine does not. If that bar feels like it is slowing you down, that feeling is the product working.