The problem: an AI coding agent can write code, but a one-person agency can't sit and watch it. If nobody is watching, who stops the wrong PR from merging, the same job from being paid for twice, or a customer being told "it shipped" when it didn't? Chat-agent guardrails only work while you're reading the chat.
The solution in today's radar: cromford-ai/cromford is an open-source software factory that runs the whole loop unattended — ticket in, triaged, planned, built, adversarially reviewed, preview-QA'd, merged, deploy-verified, and production-checked. Its whole design idea is: anything a script can decide, a script decides — the expensive, unreliable model is only asked the questions that genuinely need reading comprehension. Deterministic gates, per-call cost accounting, and a human tier for anything risky.
Format note: single-featured-factory layout (standing focus set Oct 25, 2026); the Ethiopia business-opportunity section remains removed. Prior reports were available through Oct 31 (most recently KbWen/agentic-os; earlier: nexu-io/looper, jmanico/grim-prompter, coleam00/ai-software-factory, obra/superpowers). Cromford has not been featured before. All facts below verified today from the repo's README and commit history via GitHub; star/fork counts are as returned by the GitHub API today. This is a very young project — treat it as a pattern to study, not a tool to trust blindly.
logs/cost.csv; a model missing from catalog/model-prices.json raises a daily alert instead of silently costing $0.bin/run-tests-in-docker.sh, Debian image, needs Docker + network for the first image pull). Windows is not mentioned in the README — running it on your PC means Docker Desktop + WSL2: plausible, untested. Self-hosting additionally expects bash, jq, optionally semgrep/codegraph, and a Linux "watcher box" (a VM).Stage names, scripts, artifacts and refusal conditions verified from the repo README today.
| Agency OS stage | Factory module | Artifact it produces | Which gate consumes it |
|---|---|---|---|
| LEAD / PROPOSAL | — not covered | — | — (tickets must already exist on a board) |
| DISCOVERY / PROBLEM_DEFINITION | triage — bin/run-triage.sh + deterministic bin/triage-gate.sh | RUN/SKIP decision with reasons | Refuses to start when the ticket's last word is an unanswered customer question |
| SPECIFICATION | plan — bin/run-build.sh | A written change plan ending in named acceptance tests | Refuses when it cannot name a test that would fail today |
| ARCHITECTURE | — no separate stage (planning absorbs design; docs cover context composition) | — | — (gap) |
| DEVELOPMENT | build — bin/run-build.sh, on a watcher box or optional ephemeral Fly machine (bin/fly-pool.sh) | Implementation against the plan, on its own box/budget | Refuses when tests were weakened rather than made to pass |
| QA | review (adversarial, never the author's session, bin/review-prs.sh), preview QA (bin/e2e-smoke.sh clicks the PR's own preview), post-merge QA (bin/postmerge-qa.sh drives production) | Review findings; preview smoke result; production check result | Refuses when the verify script doesn't prove its claim, the route 404s, or the shipped behaviour isn't there |
| SECURITY | — no dedicated stage; semgrep only as an optional CI check (SEMGREP_REQUIRED=1), not a merge gate | — | — (gap — see Honest Gaps) |
| CEO_REVIEW | merge tiers — tier 1 auto-merges when review approves + CI green; tier 2 waits for a human; flags/automerge-disabled forces everything to tier 2 (bin/run-build.sh:1592) | PR in one named state with an age budget (bin/merge-queue.sh, bin/merge-sweep.sh) | Refuses to merge when CI is not green, base/head moved after approval, or the tier is wrong |
| RELEASE | deploy-verify — bin/deploy-train.sh | Confirmation a production deployment followed the merge | Refuses when no deployment appeared within the window |
| MAINTENANCE | Rule-miner (bin/rule-miner.sh) turns repeated review findings into guardrails/ that fail next time in CI — "only tightens, never loosens"; restartable stages (state on disk + DB); docs/RESTORE.md box recovery; docs/LESSONS.md | New guardrail rules; recoverable box state | Load-bearing tests protect the fence (iron-law-guard-test.sh, lane-fence-test.sh, alert-loop-test.sh) |
| REUSABLE_COMPONENT | Closest analog: guardrails + repo-managed skills/ (one SKILL.md per competency, deployed to every box) | Tested procedures, not a component library | Partial (gap) |
semgrep is an optional CI check only. Biggest gap, and the one that matters most to you.--apply step "is not built yet" (README's own words) — resources are still built by hand; the dry-run only prints what would be created.flags/automerge-disabled can force every build to human review — matching your rule that release and acceptance need human approval.project-state.json being authoritative. It complements, never replaces, your gate engine.tests/evals-suite-test.sh checks that a prompt change still makes the same triage decisions — an honest answer to "did my agent regress when I changed the prompt?"gate_engine.py stack; the integration proposal below stays additive.flags/automerge-disabled on — and treat any auto-merge adoption as a commercial decision requiring your CEO_REVIEW approval.selfhost.conf.selfhost.conf as a secret; never commit it.--dry-run prints every resource, env line, spend cap and secret name and creates nothing. It's the best available preview of what running an unattended factory actually requires.gh repo fork cromford-ai/cromford --clone (or plain git clone https://github.com/cromford-ai/cromford.git); cp env-templates/selfhost.conf.example selfhost.conf; then bin/selfhost-install.sh --config selfhost.conf --dry-run. Optionally also bin/run-tests-in-docker.sh to run the project's own 121-test suite in a Debian container.--apply; do not enable any auto-merge flag; read any script before executing it. The dry-run is read-only by design — keep it that way.gate_engine.py.project-state.json); every agent LLM call (command, model, tokens, stage). Outputs: a BLOCKED-with-reason verdict when the last word of a request is an unanswered client question; a CSV row per call into a per-project costs.csv that rolls up per delivery.triage-gate.sh), and a priced-catalogue lookup that alerts when a model has no price row instead of logging $0 (Cromford's exact pattern).advance_gate.ps1's DISCOVERY precondition: fail if the latest client-message thread in project-state.json ends in a question with no recorded answer. One fake ticket that ends in a question, one BLOCKED verdict, one passing case — then decide whether to keep it.bin/triage-gate.sh is a status-field comparison, and tests/evals-suite-test.sh fails if a prompt change alters triage decisions. In Agency OS, the same failure class is caught by gate_engine.py making transitions from project-state.json fields — and by your precommit scripts re-running checks instead of trusting the agent's summary.gate_engine.py, replacing nothing.bin/selfhost-install.sh --config selfhost.conf --dry-run (Docker/WSL2) — read what an unattended factory needs before it touches a real account. Then add the one-line unanswered-question check to your DISCOVERY gate.