Artem PetrovSearch
Pen-and-ink drawing of a long tailor's workshop. A cutter chalks cloth at a table in front, two seamstresses work at machines in the middle, and at the far end a fitter checks a jacket on a dress form; a small chalk mark on its sleeve is drawn in orange.
Illustration generated with GPT Image
ContentsA software factory, column by column
  1. The board is the program
  2. Before the first card
  3. The planner
  4. The conveyor
  5. Tech Design
  6. Design
  7. Implementation
  8. CI check
  9. Code Review
  10. Merge and deploy to staging
  11. After Deploy (Human action)
  12. Verify
  13. Side doors
  14. When a card is stuck
  15. Human-gated

A software factory, column by column

In eleven weeks 2,457 cards went onto our agent factory's board, and 1,677 of them went through every stage from spec to a browser check on staging. Column by column: what each stage leaves for the next, how the planner keeps up to fifteen stories in flight from colliding, and where stuck cards go.

A typical story on the factory’s board collects seven comments on its way to Done. Each is written by a different agent session, for the next one to read:

Comment Left by Median length, characters
tech_design Tech Design 9,400
branch Implementation 820
implementation Implementation 3,100
ci CI check 1,400
review_findings Code Review 5,200
deploy Merge and deploy to staging 3,900
verify_report Verify 4,900

From first entering Tech Design to Done, that took four hours on the median. Between 6 August and 26 October, 2,457 cards went onto the board of the software factory from the first post, and 1,677 of them went through every stage: a spec, code, CI, a review, a deploy to staging and a check in a real browser.

This is one of the first factories our company built on Flow, and I want to show how it works inside.

This post goes through the board column by column: what each column is for, how its workflow is wired, and what it leaves for the next one. I left out the instructions themselves. Together they run to about 200,000 characters, the planner’s alone to about 41,000 with its sub-steps, and the meaning of each fits in a paragraph.

The board is the program

The factory is one Flow board with thirteen columns. Ten form the conveyor from Backlog to Done, counting two detours. Needs Planning takes asks from outside the plan, and two columns hold cards that are stuck.

Column What happens on entry
Backlog Nothing. The planner releases from here
Needs Planning Scope Change slices an oversized ask into stories
Tech Design A spec and a technical approach
Design A UI spec, only when Tech Design asks for one
Implementation Code, tests, a pull request
CI check Waits for GitHub Actions, then reads the verdict
Code Review A static review against the acceptance criteria
Merge and deploy to staging Merge into the staging branch, deploy, prove it’s up
After Deploy (Human action) Nothing. A person runs a seed or a backfill
Verify Every criterion walked in a browser on staging
Done Nothing
Blocked Nothing. The planner sorts it every cycle
Human-gated Nothing, and nobody sorts it. It waits for a person

Every column has its purpose written into the board itself, in plain English, from two lines for Tech Design to a page for Human-gated. A card entering a column with a workflow bound to it starts that workflow at once. (How triggers like this work is the subject of “Who presses Run?”.) Backlog’s text spells out what follows from that:

Holding area — no automation, which is exactly why every producer creates here first: entering a column with a workflow bound to it fires that workflow immediately, against whatever the card holds at that instant. Cards are created, filled in completely, then moved.

Each workflow step is a fresh agent session in its own container, and between columns nothing carries over except the repository and the card.

So the card’s comments are the factory’s memory. There are 41,753 of them, and agents wrote 39,916. Each stage tags what it writes, and the next stage’s instructions name the tags it reads. The tags are the factory’s own convention; Flow doesn’t define them.

Before the first card

The plan lives in the repository as files: a PRD (the product requirements document), epics.md with every epic, the large features, split into stories with their acceptance criteria, a dependency graph, a testing strategy, and sprint-status.yaml with one line per story.

Most of those files come from the BMAD method, an open-source set of commands for coding agents that walks a project from a PRD to stories to code. In Flow, BMAD is one switch on a workflow step. When it’s on, Flow installs BMAD 6.11.0 into the step’s container before the agent starts, and the step’s instructions open with the BMAD command to run: /bmad-spec in Tech Design, /bmad-build-auto in Implementation, /bmad-code-review in Code Review. Flow wires in nothing more. Which command runs in which column is the factory’s choice, written in the steps.

BMAD assumes a person at the keyboard, answering its menus and deciding what to build next. The factory has nobody there, so it kept BMAD’s documents and commands and replaced the parts that wait for a person.

BMAD In the factory
Project brief, from the analyst persona Written by people before the factory starts
PRD, from the product manager persona Written before the factory starts, changed only by Scope Change
Epics and stories in epics.md Same file, same format
sprint-status.yaml Kept, with fixed writers: Implementation moves a story to in progress and to review, Verify moves it to done
A rule that stories must not depend on future ones A dependency graph as a CSV the planner parses. No BMAD command produces it
Scrum master picking the next story The planner, on a schedule. BMAD itself has since dropped its scrum master
Architect persona The architecture, also written before the factory starts. Each story then gets its own technical design
UX designer, /bmad-ux The Design column, only for stories that need it
Developer, /bmad-build-auto Implementation, minus running the suite and waiting for CI
QA persona CI check, then Verify in a real browser. BMAD is switched off there
Course correction Scope Change, on the Needs Planning column
Menus that wait for a person’s answer Blocked, Human-gated, and a Slack reply with the word triage

The dependency graph is the factory’s own invention. No BMAD command produces it; here, people wrote it before the factory started.

We’ve since copied this factory to two newer projects. On the first, the plan comes from two inception workflows. The first reads the client’s requirements and the kickoff notes, drafts the PRD with /bmad-prd and posts numbered questions in Slack. Then it stops, and the card waits in Blocked. A person answers, the answers land on the card, and the card moves to a column that starts the second workflow. That one finishes the PRD and writes the epics, the graph, the testing strategy and the status file into a pull request, which a person reads end to end and merges before the planner is switched on. Its instructions remind the agent that the planner can’t run without the graph, and that one cycle in it would stall the planner for good, with no error.

It used to be one workflow. That version posted its questions in Slack, and its next step started two minutes later, found no answers and committed five assumptions: a gate holds back the workflow bound to a column, not the next step of a run that is already going. So it was split in two, with a person in between.

Two stages run with no agent persona at all, only BMAD switched on: Tech Design and the first step of Design.

BMAD’s skills take a lot of tokens on their own, and I want to look at other ways to run an agentic SDLC from a spec too.

The planner

Nobody releases work by hand. A planner workflow runs on a schedule. It reads the dependency graph and sprint-status.yaml, and releases the next stories whose dependencies are done. It has run 1,734 times, 1,711 of them on its schedule, and its instructions are the longest in the factory.

Three rules decide what it may release:

  • A cap of fifteen. No more than fifteen cards may sit between Tech Design and Verify; cards waiting in After Deploy don’t count. It caps the work in flight, not one release: an empty pipeline can be filled in one cycle. We tried more: as the cap grew, we ran into constant conflicts and contradictions between the stories in flight.
  • No path between any two. No released story may depend on another one in the same batch, directly or through a third.
  • No shared files. Each candidate’s footprint, the files and modules it will touch, must not overlap with anything already in flight. Two stories that edit the same file collide on merge, so they don’t run at the same time.

What limits a single batch is the other two rules, and the instructions say so:

The pairwise tests are what bound the batch, not the number 15. Disjointness is quadratic: a batch of 15 is 105 pairs to check, and one unchecked pair puts two stories on the same files.

And they forbid releasing work just to reach the cap: a story released only to fill a slot is one Tech Design will have to reject. Among the candidates that pass, the planner ranks by what each would unblock.

When the graph has nothing ready, the planner works down a ladder instead of idling: loose cards in Backlog first, then stories in epics.md that never made it into the graph. It also reports when the status file and the board disagree, and starts again any card that has been sitting in an automated column with nothing running.

It never writes to the repository and keeps no state of its own. A story counts as released when its card exists on the board. Only two stages may write sprint-status.yaml: Implementation and Verify. Most cycles release nothing, and those cycles post nothing either. It also refuses to create a second card for a story that already has one, a rule added after it created duplicates of four stories in mid-October, two of which conflicted with code already on staging.

The second newer copy keeps its backlog in Jira and has no dependency file at all: its planner reads the dependencies from the board and from Jira links.

The conveyor

Seven columns between Backlog and Done run a workflow on entry. Each runs as a fresh session with its own wiring: whether BMAD is installed and which of its commands the step opens with, and what else the session gets.

Column BMAD Also gets Leaves for the next
Tech Design /bmad-spec 5 skills, Context7 docs tech_design, ending in a verification plan
Design /bmad-ux, then a design sync 4 frontend skills, Playwright, Context7 design, ending in a routing: line
Implementation /bmad-build-auto 14 skills, Playwright, Context7, a Coder dev machine on demand branch with paths:, a pull request, a CI gate, implementation
CI check off the repositories, nothing else ci
Code Review /bmad-code-review 5 skills, Context7 review_findings
Merge and deploy to staging off git and gh deploy, with the criteria still to check in a browser
Verify off a Playwright skill, Playwright, Sentry, 15 config items verify_report, a screenshot per criterion

BMAD is on in the four columns that write or judge a spec and code, and off in the three that deal with the world outside the repository: CI, the deploy and the browser. Context7 serves current library docs, Playwright is a browser the agent drives, and Sentry shows the app’s errors. A skill is a written set of instructions for one stack or one job, attached to the steps that need it. The skills come with the project, not with BMAD. For every stack in the codebase we added a set, some written in-house and some from the stack’s maintainers, with its conventions and testing rules, and attached it to the stages that write or review that code.

Tech Designtech_designImplementationbranch, PR, gateCI checkciCode Reviewreview_findingsMerge, deploydeployVerifyverify_reportDoneDesignAfter Deploythe verification plan, written before any code
Under each column, the comment it leaves for the next. Dashed: detours a card takes only when it needs them. The line below: Verify walks the plan Tech Design wrote. Any stage after Implementation can send a card back to it, and any stage can send one to Blocked.

Tech Design

The agent reads the story, the PRD, the testing strategy and the code, and writes an implementation-ready spec as a tech_design comment. Then it routes the card: to Design if a screen needs designing, straight to Implementation if not.

The spec is written for Implementation, but its last section is for Verify. The verification plan has one row per acceptance criterion, with the staging page, the test account and where the data will come from: already there, created through the UI during the check, or shipped by the story itself. The instructions make an unreachable criterion this stage’s problem: “An AC no plan can reach is a finding in this comment, before any code is written.”

Design

Two steps. The first runs /bmad-ux and writes a spec for every screen the story touches: which shared components it uses, its one primary button, every visible string, the layout at 375, 768 and 1440 pixels, and the empty, loading and error states, all against the project’s design rulebook. It has no tool to move the card or post in Slack, and ends its comment with a routing: line instead. The second step syncs the design and moves the card where that line says, so the board and Slack can’t disagree about where the card went. 343 cards went through Design.

Implementation

Before it changes a file, the agent posts a branch comment. Its paths: line lists what the story will touch, and that line is the footprint the planner checks for collisions. Then /bmad-build-auto writes the code and the tests the testing strategy asks for. The tech design goes into the repository next to the code too: a comment on the board can’t be reviewed in the pull request and doesn’t travel with the code.

The agent rebases when the branch has gone stale, opens a pull request, creates a CI gate for the GitHub Actions run, a hold on the card that clears when the run reports, and moves the card to CI check. Then the session ends. It never waits for GitHub, polls for a verdict or runs the suite. Its implementation comment ends with an ops_needed: list: anything a person must run on staging before the story can be checked, or none.

Cards come back here with the comment to act on: ci, review_findings, or verify_findings, the list of what Verify rejected. A card that comes back from CI a third time gets a full development machine, through Coder, to reproduce the failure. Implementation is the busiest stage, with 8,770 runs, and the most expensive: $54,000 of the $96,000 from the first post.

CI check

This column is a gate. Its workflow doesn’t start until the GitHub Actions run for the pull request finishes, so nothing holds a machine while CI runs, and no agent re-sends its whole history every few minutes to ask whether CI is done. It runs with no agent persona and no BMAD, on the shortest instructions of any conveyor workflow, about 2,600 characters.

A merge conflict goes back to Implementation with the files named, a red run with the failing job and its first real error, a green one on to Code Review. A run that never started is not a pass: “Never treat a missing run as green.” If GitHub never reports, the gate goes stale after twelve hours, and CI check doesn’t wait again: it sends the card back to Implementation, saying no run happened.

Code Review

/bmad-code-review, on the diff. The reviewer doesn’t run the app or the suite and doesn’t merge; CI already said whether the branch is green, so the question here is coverage. It reads the code itself, never only the implementation comment, and the bar is written down: “an AC with no assertion behind it, or one that would pass against a wrong implementation, is blocking.” It also checks the design rulebook’s ten rules, on changed lines only, so old violations stay notes.

The review_findings comment opens with BLOCKING (n) or CLEAN, and each finding is a file:line, what is wrong and the fix, specific enough that Implementation can act on it without working the problem out again. Blocking sends the card back to Implementation, clean sends it on.

Merge and deploy to staging

No BMAD, no skills, no MCP servers: git and gh. The stage merges the story branch into staging, the branch the staging environment deploys from and the only one it writes, and the deploy workflow ships it. Before merging it reads the checks on the pull request’s latest commit: the repository has no branch protection, and the story branch’s CI run doesn’t cover the merge this stage is about to make.

It proves the deploy, not the story: the deploy run green for the merged commit, the app answering, the story’s own route answering, runtime errors checked. “A green health check proves the app booted, not that this story shipped”, so it says “deployed and responding”, never “working”. Merge conflicts are its job: it keeps both lines in sprint-status.yaml, takes both sides of edits that don’t touch each other, and regenerates generated files instead of merging them. Only a conflict that needs a decision about behaviour goes back to Implementation.

The deploy comment ends with the acceptance criteria that still need a browser, which is Verify’s queue. The story’s pull request into pre-develop, the integration branch every story starts from, stays open. It’s merged only after Verify accepts the story, so a rejected story leaves nothing to revert.

After Deploy (Human action)

Some stories need a hand on the environment before anyone can check them: a seed, a backfill, an AWS resource the pipeline is deliberately not allowed to create. When ops_needed: isn’t none, Merge and deploy moves the card here with an ops_runbook comment: the commands, where to run them, the expected output, and when it counts as done. A person runs it, posts what happened as ops_done, and moves the card to Verify.

No workflow runs here, and the card doesn’t count against the planner’s fifteen. Its files still count for collisions, because its pull request is still open.

Verify

No BMAD again. The tester gets Playwright and Sentry, a Playwright skill and fifteen config items with the staging address and test accounts. It first proves the commit on staging contains this story’s merge. Then it logs in as the test accounts, creates whatever data the story needs through the UI, as a user would, and walks the verification plan from Tech Design, one screenshot per criterion, each marked observed or unexercised with the reason.

Then it checks how every screen the story touched looks: at three widths, in the second language, in the dark theme, empty, loading, with errors and with long values. A story that works but looks broken doesn’t pass.

Accepted: it merges the pull request, marks the story done in sprint-status.yaml, writes the page of the operator manual for what it walked, and moves the card to Done. Rejected: numbered findings, each reproducible from a cold start, and the card goes back to Implementation. Its instructions are the longest on the conveyor, about 33,000 characters with sub-steps, and the most revised: version 19.

Side doors

Not all work comes from the plan. Seven more workflows sit around the conveyor.

Workflow Starts from What it does
Chat Intake A Slack message that mentions the app and says triage Answers in the thread, comments on an existing card, unblocks a card whose question the message answers, or creates a card. Sends a small change to Tech Design and anything feature-sized to Needs Planning
Scope Change A card entering Needs Planning Closes the ask if it’s already covered, writes new stories and graph edges if it isn’t, and escalates a real product decision to a person. Nothing else in the factory changes the product’s scope
Progress Report Every morning Checks the status file against the code and posts the client’s daily update
Client Unblock Digest Every morning Collects the questions that wait on the client, and sends a card back only when every blocker on it has a cited answer
Task Q&A Run, pressed on a card Explains one card, checks every claim against the code, and touches nothing
Blocked Sweep By hand Sorts every stuck card and writes a report. Run again with the report a person approved, it moves exactly the rows in it
Design System Sweep Mondays Walks the main staging screens against the design system and files up to ten bug cards

Chat Intake has run only 74 times, and it matters most of these. Clients answer in Slack, and Chat Intake puts each answer onto the right card and into the right requirement quickly.

When a card is stuck

No step in the factory retries on its own: every one keeps Flow’s default, which is to fail on the first error. All the recovery happens on the board, through a set of rules the first post called the blocking protocol. There’s no file for it: the rules are written into the Blocked and Human-gated columns and in the planner’s instructions, and every stage restates them in a line of its own.

In the five stages that build and check a story, Tech Design, Design, Implementation, Code Review and Verify, most things an agent can’t settle don’t stop it. A bug it notices on the way becomes a new card in Backlog. A small decision, one that touches no legal text, money, credentials, client request, irreversible data or epic scope, it takes itself and records as an assumption comment; there are 237. Only a big decision that nobody has made yet becomes a card for a person.

A stage that can’t go on moves its card to Blocked with a blocked comment saying what is missing, what it tried and who it needs it from, and posts the same message once in one Slack channel for escalations. The rule is one escalation per blocker, repeated only after twelve hours without an answer. The answer has to come back through the intake channel, in a message to the app with the word triage, or a person moves the card. A reply in the escalation channel doesn’t reach the card by itself, and Blocked’s text warns about exactly that.

Blocked has no workflow of its own. The planner sorts it every cycle into two kinds:

  • Retryable. The environment failed: an expired machine, no Docker, a run that died, staging down for a minute. The card goes back to where it was blocked, at most twice per blocker; a cause that comes back twice no longer counts as transient.
  • Work-gated. The blocker is code that doesn’t exist yet: an endpoint, a migration, a fixture. The planner finds or creates the card that owns that work, releases it first, and leaves a watchdog comment on the stuck card naming it. When the owner reaches Done, the stuck card comes back. There are 1,532 watchdog comments on the board.

Blocked’s text gives the test for whether a card belongs there: it has to come back on its own, on the planner’s next cycle or when the card it waits on reaches Done. A card that won’t is human-gated.

586 cards were blocked at least once, and 502 of them reached Done later.

Human-gated

The third kind used to live in Blocked too: a missing credential, a product decision, a legal ruling. By late August it made up three quarters of the column. Every planner cycle re-read those cards and reached the same verdict, nobody can retry this, and Blocked stopped meaning “the pipeline is stuck”. On 28 August they got their own column, with one rule of thumb: “if the answer would arrive from a person rather than from a merge, it goes here.”

Nothing runs on Human-gated and the planner doesn’t touch it. That makes it a place where cards disappear, and its description says so:

Because no automation and no triage touches this column, a misfiled card here is invisible forever.

Two days after the split, a hygiene pass found six of the twenty-three cards there were ordinary engineering work parked by mistake. The client digest now reads the column every morning. 465 cards have been through it; 44 are there now. The first newer copy has no Human-gated column yet, and its Blocked column says to split one out if human-gated cards grow to three quarters of it, as they did here.

The questions are answered by a business analyst who talks to the client all the time. A card waits in Human-gated for a median of 38 hours. The client has to learn a new way of working, and that is a heavy cognitive load. In return they get results fast.


We’ve packed this factory into a Flow template, Agentic Dev Pipeline, merged from this board and its two newer copies. It has the same thirteen columns, and at install it asks for your branches, the staging address, the cap and whether an agent or a person merges an accepted pull request. It installs with every trigger off, no skills and BMAD off on every step: attach your own stack’s skills to Tech Design, Implementation and Code Review, then turn the triggers on.

If you run agents this way, or tried and gave up, write to me. Your experience is worth a lot to us, and it will shape what we build into the product next.

Flow is open source under Apache 2.0 and runs on your own servers. See how it works, or clone it from GitHub. If you’d rather have a factory like this set up on your own systems, talk to us.

Written by Artem Petrov. Checked by Claude (research, fact-check, first draft).

More writing

Engineering

Who presses Run?

Our software factory ran 31,634 agent workflows in eleven weeks, and people asked for 1,149 of them. How the rest start, and what it takes to start each one exactly once, about the right card, with the right keys and not too early.

Essays

Nobody is conducting your agents

Models got better fast, but some of the biggest jumps in AI coding came from the tooling built around them. The next one has to come from the tooling around the agents, and most teams don't have it yet.