It approved its own change, deployed before tests passed, skipped the failing step — and one more line in the system prompt fixed it only until next sprint. flowgate puts your agent on a state machine it can't step off: the illegal move isn't blocked, it isn't there to call.
Make the wrong action unreachable. Not improbable. Reliable enough to run TDD on a commodity model — the rails carry the discipline, so the model doesn't have to.
Wondering what stops the agent from ignoring it? See the three ways to run it →
states:
red: # a failing test must exist first
transitions:
run_test: { target: green, actor: deterministic }
# no write_implementation here — it does not exist in this state
green:
transitions:
write_implementation: { target: refactor }
refactor:
transitions:
done: { target: done } The agent approved before review. It deployed before tests passed. You went back and added a sentence to the system prompt — "always wait for tests before deploying" — and it worked. Until the next sprint, when it happened again in a way your sentence didn't predict. So you added another sentence.
You added guardrails in prose, and the model treated them as suggestions under pressure. The model isn't broken — it just has no reliable picture of where it is in the process or which actions are even legal from here.
At some point the prompt isn't the problem — the agent just has no idea where it is.
A workflow is a state machine. A tool call is a transition. An action that's illegal in the current state doesn't EXIST as a move until you're in the state that offers it — the runtime doesn't block it, it simply isn't there to call.
The model always receives exactly the links that are valid right now. It can't hallucinate a transition that the current state doesn't declare. Zero extra states is just a flat tool list — you never pay for more structure than your problem has.
Read the full argument for why state machines are the right primitive for agents, or jump straight to the two-tool mechanism that keeps the model's surface area constant.
The agent is in the red state.
It hasn't run a failing test yet. It tries to jump straight to writing implementation anyway.
→ flowgate.command { "transition": "write_implementation" }
← { "error": { "code": "GUARD_REJECTED",
"message": "requires a failing test on record" },
"links": [ { "rel": "run_test", "method": "flowgate.command" } ] }
No write_implementation move exists from red. If the model tries anyway: GUARD_REJECTED.
The only way forward is to make a test fail first.
Running on anthropic/claude-3.5-haiku — a commodity model. The rails carry the discipline.
Because the harness owns the process, you don't need a frontier model to behave correctly. A cheap model executing one bounded, state-specific step is enough — and it will stay in bounds not because it's smart but because the valid moves are the only moves it sees.
That means you pick the cheapest sufficient model per step. Complex reasoning goes to a capable model; deterministic, well-scoped work goes to the cheapest one that can do it. The harness-first principle: structure the problem so model capability is no longer the load-bearing wall.
Publish a cognitive architecture to a Git repo. Operators
load any number of repos,
composed however fits their context — every layer is namespaced and collision-checked at load,
and any layer updates with a plain git pull.
The same model supports skills, workflows, architectures — from a single reusable skill up to a full cognitive architecture with opinionated process flows.
version: "1.0.0"
repos:
- path: ~/repos/cognitive-architectures # proven baseline — ours
- path: ~/repos/acme-agent-policy # company gates — an example
- path: ~/repos/my-flows # personal tweaks — an example
# all namespaced + collision-checked at load; update any layer with git pull Official baseline, a company policy repo, a personal repo — and, on the horizon, industry packs (HIPAA, SOC 2, fintech controls). These are examples of how you might organize repos, not a required structure. Mix and match.
Like extending a base ESLint config or a Docker base image — but for what your agent is allowed to do.
The model always sees exactly two tools — flowgate.query and
flowgate.command — regardless of how many workflows and
capabilities you've loaded. The surface stays constant at any scale: no prompt bloat, no
growing tool list for the model to reason across.
See the full cost analysis and
what discovery costs, honestly.
The primitives you need to trust it with real work.
A SQLite store for state that survives restarts — durable governance with zero external services to run.
Every action emits a structured JSON event. Route to file, stdout, or your observability stack — full replay capability.
mcp-flowgate check catches dangling and unreachable
states before you ship. Ship broken state machines to staging, not production.
Send SIGHUP to reload config without restarting. In-flight workflows continue uninterrupted.
Several processes share one SQLite file in WAL mode — no networked store, no cross-host coordination to operate.
The TDD architecture is dogfooded in our own CI with a mechanical driver script.
One YAML file. A state machine that enforces the process. Governed workflows from Git. Free and open source.