Bondsmith: Hard Bonds for Soft Claims
"All tests pass." I have read that sentence at the end of an agent session more times than I can count, and more than once it was false. The tests weren't run, or they were run before the last edit, or they were never written.
Bondsmith is the workflow engine I built around one rule: no claim counts without a proof bonded to it. I still want the model to do what it is good at, which is generating code. But whatever it tells me about that code afterwards, I check with something which is not an LLM.

Where the name comes from
Brandon Sanderson's The Way of Kings, the first book of the Stormlight Archive. The Bondsmiths are one of the ten orders of the Knights Radiant, and the rarest: there are never more than three of them. The other orders fight, heal or travel. A Bondsmith's power is connection, binding things and people together. Highly recommend this one.
Work is admitted only when its entry conditions hold, and it counts as done only when its exit conditions are shown to hold against real evidence. The LLM is free inside one step. Whatever it says about that step afterwards is a soft claim, and a soft claim needs a hard bond. Let me show you.
What the system must do#
Six requirements decide whether a system for agentic development works for me.
1. Deterministic proof. I don't want to take the LLM's word that an action happened. For every action I care about, a tool runs a check and writes the result by deterministic action.
2. No single harness. I don't want to be chained to one LLM provider, or to one agentic coding harness like Claude Code or Codex. I want to be able to use different ones. Models change every few months and the pricing and limits change with them, so I don't want to rebuild my whole workflow every time I switch.
3. Generic workflows. There is no one generic approach, one SDD definition or one flow we can use for every implementation, every feature delivery and every piece of tech debt. A new feature needs a spec and a breakdown into tasks. A bug fix needs neither. I should be able to define my own workflows and change them without touching the engine that runs them.
4. A CLI. I want to run the commands for the different actions from the terminal. I can repeat a command, I can put it in a script, and it was the simplest thing to build first. Everything else can be built on top of it.
5. Observability. I want to see anything and everything. I want to see agent logs. I want to see the effects of the deterministic gates. I want to see the definition of each task. Everything under my control. When a run goes wrong I need to know what the agent read, what it changed and which check stopped it, without asking the agent.
6. One place to look. I want a UI which presents the different features in one place, so I can easily check where I am with a given implementation. When I have a few features running in a few repos at the same time, I lose track pretty quickly, and I don't want to open five terminals to find out where I was.
Not all six are equally important to me. The first one is the reason I built Bondsmith at all, and the other five are there to make it hold no matter what kind of work I do or which model I use.
How Bondsmith is built#
Bondsmith is a Rust workspace: one core library, a thin CLI, and a read-only UI. The crate layout is on the Bondsmith project page. Here I care about who does what during a run.
A Worker is a disposable LLM session spawned for exactly one step of work, which then exits. A Gate is a deterministic check, a test or a lint run, whose verdict comes from the tool. The Driver is the state machine in the middle: it spawns Workers, runs Gates, and is the only thing that writes state.
Each repo configures its own set of Gates, such as lint, type-check and the test suite. That set is the repo's gate pool, and a Flow can ask for the whole pool to run at once.
There is no arrow from the Worker to state in the figure. The Worker edits files and makes commits, that's all it can do, and nobody asks it whether it passed or failed.
Deterministic proof#
Every step in Bondsmith declares Postconditions: assertions over the state of the world after the step has run. The Driver gathers the facts itself, from disk, from git and from Gate results, and evaluates each Postcondition in plain Rust code. The vocabulary is closed. A few of the verbs:
exit_zero. The Gate's command exited with code 0.sha_is_ancestor. One commit is in the history of another.non_empty: spec.md. The file exists and has content.gate_pool_pass. Every Gate in the gate pool passed.
A missing fact fails closed: if the Driver can't find the evidence a verb needs, the Postcondition fails.
A Postcondition either passes or hard-blocks the run, there is nothing in between.
This way "the tests were run" is exit_zero: role:test, checked against the exit code of a
test command which the Driver ran itself.
No single harness#
Nothing in a workflow definition knows which harness runs it, or which model. The contracts and the prompts carry no harness-specific text, and Workers are launched through one port in the core. Claude Code or Codex, or anything else with a headless mode, can sit behind that port.
Today one launcher sits there: Claude Code. Pi is the next target on the Roadmap.
Generic Flows#
A workflow in Bondsmith is called a Flow: a named, ordered chain of Phases, each with its own Steps and Postconditions. A Flow is plain data, JSON contracts and Markdown prompts in a directory, read from disk at run time. Nothing is compiled into the binary, and no Flow is built in. I can copy a Flow, drop a Phase, add a Gate, and the next run uses it.

The CLI#
Every action is a command:
init. Creates a feature and clones its repos.implement. Runs the implement Phase. Every Phase is a command under its own name.status. Prints where the run stands and which Postconditions hold.resume. Re-enters a step that was interrupted.
There are others. Between Phases it waits for me.

Observability#
Every run leaves three things on disk. A state file, which is the single source of truth. An append-only event log, one JSON line per event: step started, Gate finished, Postconditions checked, step advanced or held. And for every headless Worker, a Session log with its raw output.
For a headless Worker the raw log is a stream of JSON, so Bondsmith also derives a Session transcript from it: every file the Worker read, every edit, every command, in order, with the model, the tokens and the duration.

One place to look#
The trail on disk is complete. The Bondsmith UI is a local, read-only page over the same files. Its first tab lists every feature across my projects with its current status.

Open a feature and you see where the implementation is: the Phase it is in, the Task graph, and the progress of every step for every Task. Open a step and you are in that Worker's Session transcript.
The flows#
I use two Flows. feature is the main one, of course: it takes a feature from an
interview to a set of merged pull requests. address-issue takes one GitHub issue to one
pull request. A third, file-fix, is address-issue with a diagnosis Phase in front.
The Phases are different because the work is different. A feature needs an exploration, a spec and a graph of Tasks. An issue already says what is wrong, so a plan is enough and we can go straight to code. I described the Phases of the feature flow in Bounded Chaos, back when it was a pile of bash and slash commands. The Phases are mostly the same as back then. The difference is that now each of them is a typed contract, enforced by the engine.
Both Flows keep one Phase: a test-first implement. In the feature Flow it runs nine Steps
for every Task.
Here is a real run where one Postcondition of the second test Gate didn't hold:

Going through the checks from the bottom up:
gate_failed_at: red_test_sha,role:test found true. The Driver checked out the commit where the Worker claimed to have written a failing test, ran the tests there, and they failed.sha_is_ancestor: red_test_sha,HEAD found true. That commit is in the history of the current code. It wasn't written afterwards and slipped in.gt: test_count,baseline found 1711,1708. The suite had 1708 tests before the Task and has 1711 now. Tests were added, and I know how many.sha_recorded found true. The red commit exists at all.exit_zero: role:test required 0 found 1. And still one test fails right now.
So four checks out of five hold, and it is still not enough. The step is held until the failing test passes.
It learns from its mistakes#
The review Steps produce Findings: one issue each, with a type, a severity, a file and a proposed fix. Each reviewing agent writes its Findings into a report, one per Task. Ruling on them by hand, I kept making the same ruling again and again. So in the feature Flow the system learns from its mistakes, and from my decisions.
The first mechanism is Triage rules. Each repo carries its own, ordered, first match wins. This is a real one:
{
"id": "auto-accept-low-style",
"when": { "severity": ["low"], "type": ["style"] },
"then": "accept",
"reason": "Low-severity style findings (naming/query-key drift, minor duplication, re-rolled boilerplate) were consistently accepted and fixed by human review across the todo-lists feature."
}An accepted Finding gets fixed, without asking me first. A Worker groups related Findings into Review units and applies the rules. The Driver doesn't trust the Worker's triage either: it re-derives every claimed decision from the rules, in code, and a mismatch hard-blocks. There is also a floor no rule can cross. A rule may never decide a security Finding, or anything above medium severity. Those always come to me.
The second mechanism is the learn-from-reports Phase. The reports are the ones the review
Steps wrote during implement. The Phase mines their Findings and my decisions on them for
patterns, and puts each candidate in front of me to accept, reject or
edit. A decision I keep repeating is proposed as a new Triage rule, in a pull request
against that repo. A lesson general enough to reuse is written as a rule into the
Knowledge base.
The mechanism for feeding these rules back is fully there. What I'm working on now is how to use it in the different implementation and review Phases, so the next Task starts with the rule already in place.
A lot still to cover#
There is a lot, lot, lot still to cover. The system learns from the codebase, but the system also needs to evolve, and the observability I built for myself is the best source for that. Every Worker run already leaves a Session log and its cost behind. Analyzing them tells me why a single step went wrong, and where model routing can make the whole Flow cost-effective. Reading them all should tell me what to change in Bondsmith.
So this is the Roadmap from here.
One point for you#
If you take one thing from this post, you don't need Bondsmith for it: pick one action your agent reports on, and replace the report with a check a program runs.
"All tests pass" becomes the exit code of the test command, run by a script after the
session ends. "I committed the fix" becomes a git log on the branch. "The report is written"
becomes a file that exists and is not empty.
Start with the claim which burned you the most. For me it was the tests. One check is enough to feel the difference, because the agent can say whatever it wants and you don't have to believe it anymore.
Bondsmith is the same habit, just applied to every step. Start with one check and see for yourself.