Guardrails for AI coding agents Open source · Apache-2.0

Your coding agent ignored the prompt.
Again.

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 →

tdd.yaml
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 bug you keep patching with prose

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.

Make the wrong move unreachable, not unlikely

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.

Watch it stop the model cold

The agent is in the red state. It hasn't run a failing test yet. It tries to jump straight to writing implementation anyway.

wire.json
 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.

The rails do the discipline — so the model doesn't have to

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.

Don't hand-write governance. Install it.

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.

gateway.yaml
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.

And yes — two tools, not fifty

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.

Built to run in production

The primitives you need to trust it with real work.

Durable stores

A SQLite store for state that survives restarts — durable governance with zero external services to run.

Structured audit

Every action emits a structured JSON event. Route to file, stdout, or your observability stack — full replay capability.

CI check

mcp-flowgate check catches dangling and unreachable states before you ship. Ship broken state machines to staging, not production.

Hot reload

Send SIGHUP to reload config without restarting. In-flight workflows continue uninterrupted.

Multi-process on one host

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.

Put your agent on rails

One YAML file. A state machine that enforces the process. Governed workflows from Git. Free and open source.

Concept 1 of 10

Next concept →

Links, not lists

The model follows the legal moves you allow — not a menu of tools.

See all concepts