How I Built Foundry, Part 2: A Passing Audit Is Not Delivery
In Part 1, I described how Foundry began as a bounded pilot: Architect plans, Worker implements, Auditor independently checks.
That is the clean version.
The difficult part began when the process had to move something from “a Worker changed some files” to “the correct system changed, the correct authority approved it, and the record says exactly what happened.”
Those are different claims. Foundry learned this one adapter, one failed assumption, and one uncomfortable read-back at a time.
A Worker could finish the code and still not finish the work
The first serious operational problem was closure.
A Foundry Worker could make a scoped change, run the required checks, and push a candidate. But the Worker could not be allowed to certify its own success by closing the governing work record. That would turn independent review into a formality.
The answer was not to hand every agent a general GitHub client and hope its instructions stayed polite.
Foundry built narrow, task-bound adapters instead:
an Architect adapter that could create or read back exactly one initial tracking issue from an assigned card;
a Closure Auditor adapter that could close exactly one bound issue, and only after the necessary independent evidence existed;
profile-local authenticated Git transport restricted to fetch,
ls-remote, and non-force pushes;mandatory read-back after every external write.
This looks like friction because it is friction. It is friction placed exactly where an agent could otherwise convert “I think this is complete” into a durable external claim.
The cards changed too. GitHub Issues or AI.Contract records became the sole work-contract bodies. Worker and Auditor cards could hold only a stable contract reference, their role, dependency, and receipt pointer. They were not allowed to copy the full contract into card prose and then treat the copy as current authority.
That is not bureaucratic purity. A card lingers. A contract changes. An agent follows the stale copy. Then everyone gets to enjoy a postmortem about a process that technically had the right information somewhere else.
Bad start #3: we confused “candidate verified” with “work delivered”
This was the most important workflow correction.
A branch can have a clean diff. It can pass an independent audit. It can have a delighted agent report that the task is finished. None of that proves the intended default branch contains the change.
Foundry had evidence of audited sibling candidates that had never been integrated. The system could prove “candidate exists” and “candidate audit passed.” It could not prove “the product now contains this work.”
So every source-changing contract gained an explicit integration disposition:
candidate_onlymerge_required(the policy at the time)human_approval_required
The lifecycle became deliberately literal:
candidate produced → candidate verified → integration pending → integrated on main → milestone closed
A candidate audit PASS could certify the exact candidate SHA. It could not certify a merge, deployment, or milestone closure. A local commit could not certify remote delivery. Even a merged pull request could not certify that the actual default branch had been read back and checked afterward.
This sounds like a lot of words for Git. It is really a lot of words for the difference between a plausible story of progress and evidence that a specific state transition happened.
The delivery policy changed, so an old adapter had to die
Later, Karell changed the delivery policy: audited commits should go directly to main through a non-force fast-forward, not through pull requests.
That meant Foundry’s old PR-based closure flow was no longer merely old. It was unsafe. A legacy closure adapter could have applied PR-era assumptions to a direct-main workflow.
So Foundry changed the contract vocabulary to direct_main_required and retired the PR-based Closure Auditor adapter. It failed closed rather than attempting to be helpful.
That is a small but important distinction. Systems like this should not degrade by guessing which historical procedure is “close enough.” When the authority model changes, the old procedure should stop.
Bad start #4: the first launch adapters were restored, then reverted
There is a short, instructive period in Foundry’s history where it restored bounded “Phase 0” launch adapters, added target-worktree binding, and then reverted both within minutes.
I do not want to turn that into a heroic tale of rapid iteration. It was evidence that the attempted path had been assembled before the governing model was stable enough to support it.
The correction was not to keep patching the reverted path until something ran. Foundry moved to an AI.Contract V2 launch binding with stricter identities and narrower responsibilities.
That exposed several bugs that would otherwise have become haunted machinery:
cards needed to bind to the correct task worktree;
authenticated target reads had to be safe for the actual profile performing them;
the API returned
contract_version, not the assumedversionfield;idempotency had to survive retries across cards, not merely inside one card;
Watchdog logic had to recognize V2 contract identities and route auditor verdicts correctly;
Delivery and Integration Auditor stages had to re-read the active V2 contract instead of trusting a stale handoff.
None of those fixes makes for a flashy demo. They are what separate “works once in the author’s context” from “can be resumed without inventing its past.”
Recovery turned out to have blind spots too
The harshest test of a recovery system is whether it notices the failure already sitting in front of it.
Foundry’s Watchdog initially had three problems: it ignored Capability Auditor failures, read summary prose instead of structured verdicts, and could confuse historical completed repair cards with active recovery work.
That creates two bad outcomes in opposite directions. A real failure can go unnoticed, or a closed historical failure can be resurrected as if it were current work.
The repair work added structured receipt identity, exact current contract links, stable repeat-scan identity, and a distinction between direct maintenance and product correction. The source-level scanner correction received an independent review and a read-only replay that exposed a real unresolved REQUEST_CHANGES verdict.
But this is where the honest version of the story matters most.
The recovery fix is still a draft pull request. The scanner code passed a bounded independent review, but protected role-policy edits hit a timed-out approval prompt. The repository’s own record says the fix is not installed: runtime installation, source/install parity, and end-to-end operational recovery are incomplete. No alternate write path was used to force it through.
That is not a disappointing epilogue. It is a test of whether the process means anything.
If a system bypasses a protected approval because it is tired of waiting, it has not solved governance. It has proved that governance is decorative.
What Foundry is, without the mythology
Foundry is not a magic swarm of agents. It is a set of constraints intended to make autonomous work easier to inspect and harder to bluff:
Start from current evidence, not remembered context. A failed command says something about that command, not automatically about a credential, repository, or service.
Use one bounded contract. Scope, non-goals, acceptance criteria, authority, verification, and delivery disposition must be explicit.
Keep one active writer. Parallel opinions are useful. Parallel writers in a shared checkout are usually just a more theatrical form of corruption.
Separate implementation from acceptance. A Worker can produce a receipt. An independent Auditor decides whether the receipt supports the claim.
Treat a card as routing, not proof. Re-read the external contract and the actual system state.
Recover small failures; invalidate false premises. A bad path or stale checkout can be repaired. A contradicted scope or obsolete contract is not a retry bucket.
Read back external writes. A local commit, a successful API response, and a claimed delivery are different things.
Preserve unfinished truth. Candidate code can be good while integration is pending. A reviewed recovery fix can be correct while not installed.
What I would do differently
I would make the integration state machine explicit much earlier.
Most of the complexity came from discovering that planning, coding, testing, pushing, integrating, installing, and closing are separate claims with separate evidence. We learned that a PR-based closure mechanism becomes dangerous when delivery policy changes. We learned that recovery needs structured verdicts, not summary text. We learned that profile guidance can accidentally mutate into runtime power unless the boundary is both written down and technically enforced.
The question I would front-load now is simple:
What exact evidence upgrades a piece of work from “candidate” to “integrated,” and who is allowed to make each upgrade?
That would not remove the hard problems. Hard problems have an irritating resistance to paperwork. It would make them visible sooner.
The real achievement of Foundry is not its Architect/Worker/Auditor diagram. It is the insistence that a convenient assumption is still a hypothesis—and that “not done” is sometimes the most accurate result a system can produce.
That last answer is not a process failure.
It is the process working.