softwareaifactory.sh
selftest: 20 passed, 0 failed MIT licensed break/fix proven opencode · Claude Code · Codex

Your AI agents follow rules.
Ours exit non-zero if they don't.

Gates that block instead of ask. Roles that can't grade their own work. A commit lint that bans the word "verified" unless a command and its output back it up. We pulled this out of the factory we build our own product with — it's what stuck after an agent told us a change was verified when the check had never run.

For a solo dev driving one coding agent or a team whose agents open PRs on opencode, Claude Code, or Codex
watch a gate fire bash

# Actual behavior of scripts/hooks/test-edit-denial.sh and scripts/selftest/run.sh — not a mock.

$ [1/4] why this exists

Prose rules decay. Exit codes don't.

Across four review rounds of real agent-written code, one pattern held:

4

consecutive review rounds before the pattern was undeniable

100%

of false "verified" claims were checks that had never been run

0

checks that actually ran and lied

a rule that asks
# INSTRUCTIONS.md
- never edit test files
- always verify before claiming done

compliance: whatever the model
remembers today
a rule that blocks
$ ./scripts/hooks/test-edit-denial.sh
DENIED: implementer role cannot
edit test files.
exit code: 2

compliance: not optional

The underlying idea — computational controls beat inferential ones — comes from Birgitte Böckeler at Thoughtworks. We kept applying it until nothing important was left as prose. Read the full Verification Contract →

break FAIL revert PASS

A check you've only seen passing proves nothing. Every gate ships with this proof in CI — and the installer runs it on your machine before it will say "done."

To be clear: this is how we build, not how you should. There are other serious takes on agent reliability worth your time. This is just the one we can vouch for, since we live in it daily.

$ [2/4] how this approach differs

Six places we chose differently.

The left column is the common default across today's agent tooling — instruction files, trusted-because-green checks, one session doing everything. It exists for good reasons. The right is what survived our failures.

·Rules live in prompts. Compliance is inference.
$Rules are hooks in the write path. exit 2 is a fact.
·Checks are trusted because they pass.
$Every gate proven break → FAIL → revert → PASS, in CI.
·"Verified" means the agent said so.
$WROTE / RAN / OBSERVED — no evidence, no "verified." The commit lint enforces it.
·One session writes the code, the tests, and the approval.
$Spec-writer writes tests. The implementer can't touch them. The reviewer is a different model.
·One platform. The workflow is the lock-in.
$One canon generates adapters for opencode, Claude Code, and Codex. Models are swappable.
·"Self-improving" agents grade their own improvements.
$The ratchet: tightens automatically, loosens only by recorded human decision.
$ [3/4] what's inside

Small, inspectable, and honest about its own maturity.

scripts/hooks/

Deterministic gates

Test-edit denial, evidence-checking commit lint, decision gate, drift checks — all configured by one flat factory.yaml. Hooks stay byte-identical across adopters; upgrades are file copies. Hook reference →

.opencode/ · .claude/ · .codex/

Five agent roles

Defined once, generated per harness, separated by permissions the hooks enforce — not by politeness. One keeps a cited wiki of your codebase — Karpathy's LLM-wiki pattern, with the provenance enforced rather than hoped for.

scripts/selftest/

Break/fix proofs

The suite violates each rule on purpose and asserts the gate fires. factory-init won't claim success until it passes on your repo.

Language packBlessed stackMaturity
GoGinkgo + Gomega · golangci-lint · gosec · govulncheck · gremlinsbattle-tested
TypeScriptVitest · Biome · tsc · Stryker · OSV-Scannerexperimental
Java / Spring BootJUnit 5 + AssertJ + Testcontainers · Spotless · Error Prone · SpotBugs · OSV-Scanner · PITexperimental

battle-tested = a production service shipped under it. experimental = no proven adopter yet. Labels change on real adoption — not before. The core — commit gates, push gates, role separation, the verification contract — is language-agnostic and works with no pack at all. A pack only arms the test-file pattern and check command for your language, so Python, Rust, or Ruby projects get everything except those two.

Configuration is one flat file — no nesting, no schema to learn:

# factory.yaml project_name: myproject protected_paths: "internal/billing scripts/hooks" test_file_patterns: "_test\.go$" check_command: "make check"
$ [4/4] install

One command in. Proof before "done."

Bare, the installer only fetches — a pinned copy into one folder, running nothing it downloaded. Add init and it also sets up the current repo. The word is your consent; the plain command never touches your project.

Needs git, a coding harness (opencode, Claude Code, or Codex), and your language's tools. factory-init runs prereq-check first and tells you what's missing.

# one shot: fetch, then set up the current repo (prompts you for a few values) $ cd your-project $ curl -fsSL https://softwareaifactory.sh/install.sh | sh -s -- init # arm a language pack in the same step (go is battle-tested): $ curl -fsSL https://softwareaifactory.sh/install.sh | sh -s -- init --pack go # or fetch only, run the setup yourself later: $ curl -fsSL https://softwareaifactory.sh/install.sh | sh $ ~/.software-factory-template/scripts/factory-init.sh # rather read before you run? so would we: $ curl -fsSLO https://softwareaifactory.sh/install.sh && less install.sh && sh install.sh
1

installer fetches a pinned copy — and executes none of it

2

factory-init asks a few questions, writes factory.yaml, installs the gates

3

init breaks a rule on purpose, watches the gate fire, and only then says "done"

After it says "done"

  1. make sync-harnesses — generate the Claude & Codex adapters from the canon
  2. ./factory doctor — see which gates are armed, inert, or stale, and watch each armed one fire its break/fix proof
  3. Open your harness (opencode, Claude Code, or Codex) — the roles are already wired
  4. Point the spec-writer at a change; it writes red tests, the implementer makes them pass, a different-model reviewer checks it
  5. Install the push gate: git config core.hooksPath .githooks

Or just watch it fire — no install

# clone and run the break/fix suite; nothing touches your machine $ git clone https://github.com/anoop2811/software-factory-template $ cd software-factory-template && ./scripts/selftest/run.sh # 20 gates break, fire, and pass in front of you
Does it work with my existing CI?

The gates are plain shell scripts, so you can call them from any CI — or from the git pre-push hook, which needs no CI at all. One caveat: the ready-made workflow we ship is GitHub Actions. On GitLab CI, Jenkins, CircleCI, and the like, you invoke the same scripts from your own pipeline config — a handful of lines, but that part is yours to write.

Can I take just the commit lint, without the role separation?

Yes. Every gate is independent. Adopt them one at a time — the adoption guide covers going incremental in a brownfield repo.

How do I keep it up to date?

./factory upgrade refreshes the framework — the hooks, scripts, and gates — in place. It never touches your factory.yaml, your code, or your customized files; it just reports which of those changed upstream so you decide. Then ./factory doctor confirms every gate is still armed and fires its own proof. Nothing is committed for you.

What if I don't use opencode?

The canonical config lives in opencode.json, but a sync script generates equivalent Claude Code and Codex configs. The enforcement layer is plain shell — it doesn't care which harness drives.

My language isn't Go, TypeScript, or Java.

The core works anyway. Commit gates, push gates, role separation, and the verification contract are language-agnostic. A pack only arms the test-file pattern and check command — set those two in factory.yaml by hand and you have the full thing.

Will it slow down my dev loop?

The gates are the fast local rail — a hook that exits in milliseconds. They run where you'd otherwise wait on a human reviewer to notice the same mistake. That trade goes the other way.

Clone it. Break a rule.
Watch the gate fire.

The fastest way to evaluate this template is to try to violate it. That is also the point.