Guard Jobs Are Free: Four Agents, Three Human Gates, and Zero Model Calls on a Bad Request

· 5 min read · by Michael Kaminski

A four-agent pipeline turns a sentence in Slack into a planned, tested, reviewed feature in GitHub. The design constraint is that no code can be written from an unrefined request: a bash guard job checks the Definition of Ready before any model is invoked, and a story with no Given-When-Then never reaches the dev agent. Labels are the only orchestration primitive, GitHub issues are the database, and a five-story feature costs 16 model runs. Three gates stay human on purpose: scope, sequencing, and merge.

A feature of five stories costs sixteen model runs in my software-delivery pipeline: one for the product agent, then five each for dev, QA and acceptance. A request that fails the Definition of Ready costs zero, because the check that fails it is a bash script that runs before any model is invoked.

The conclusion first: put the preconditions in a guard job, not in the prompt. A guard job is free, it is deterministic, and it fails in a way the issue timeline records. A precondition written into a prompt costs a model call to evaluate and is honoured most of the time.

The shape

Four agents, each owning exactly one gate, with no authority over the others' work. The product agent asks whether this is a real, sliced, testable feature; it cannot write code. The dev agent turns one story into one pull request with tests; it cannot start from an unrefined issue. The QA agent asks whether the tests actually prove the criteria; it cannot fix the code. The acceptance agent asks whether the requester would call this done, and looks at the rendered screens at 375, 768 and 1440 pixels to answer; it cannot merge.

Every agent is the same three parts: a role file, a bash guard job, and one model invocation with a per-agent tool allowlist. Adding a fifth agent means copying that pattern.

Labels are the control flow

There is no database, queue or persistent service. GitHub issues are the state, and labels are the only orchestration primitive. An intake issue carries the raw request verbatim, never edited. The product agent replies with a proposal. A human adds gate:approved. The agent creates a feature issue and story issues in stage:ready. A human adds agent:dev to one story. Dev, QA and acceptance run unattended from there, and a person gets a message on done, failure or a question.

Every state transition is a label change, and every label change is a visible event in the issue timeline. That is the single biggest reason the system stays cheap to run and easy to debug. When something goes wrong, the answer to "what happened" is on the issue, in order, with timestamps.

The guard

The dev agent's guard checks three things before spending a model call: is this a story, is it in ready, and does it carry at least one Given-When-Then acceptance criterion. If any check fails, the guard adds gate:failed and stops. No model, no branch, no cost.

That guard is the design constraint made concrete: no code can be written from an unrefined request. Everything else in the pipeline is a consequence. The product agent exists because something has to produce the criteria the guard demands. The QA agent traces each criterion to a test because the criteria are machine-checkable. The acceptance agent reads the original intake rather than the refined story, because refinement loses information and that edge is the check on the loss.

The three human gates

Three decisions stay with a person, and each has a reason that is not "we were nervous".

  • Scope. Approving the proposal is a business decision. An agent approving its own scope is the vibe-coding problem with extra steps.
  • Sequencing. Stories sit in ready until a person pulls one. Priority is theirs.
  • Merge. The acceptance agent recommends. One irreversible action, one human.

Everything between those three points runs unattended. The kill switch is a label: agent:paused on any item stops every agent for that item before any model call. Disabling Actions stops everything.

What it costs

One model call per agent per item. A five-story feature is 1 + 5 + 5 + 5 = 16 runs, plus one extra dev-and-QA cycle for each block. The guard jobs are not in that count because they cost nothing. That arithmetic is why the preconditions live in bash and not in the prompt, and it is the same arithmetic as the tool-surface essay: the cheapest place to prevent a bad call is before it is made.

What is not proven

The pipeline was built on 2026-09-07 and has run on my own repositories. I have no throughput numbers worth publishing yet, and the honest claim is the structural one: a request without acceptance criteria cannot reach a model that writes code. The count of features shipped through it is a later essay, with the count.

All writing · Projects · Michael Kaminski


Book a Call · Download Resume · MKaminski1337@gmail.com · (404) 838-8613 · LinkedIn · GitHub · DEV · X