On this page12 sections
- What a software factory actually is
- The map: city, rigs and packs
- The unit of work: beads
- The procedure: formulas, molecules and workflows
- How work moves: sling, hooks and propulsion
- Who works here: the roles
- Grouping work: convoys
- Keeping the lights on: orders, mail and nudges
- Following one bead: how this post was made
- Why bother, and when not to
- Cheat sheet
- Where to start
Most of us met AI coding the same way: one developer, one assistant, one chat window. It's a craftsman with a very fast apprentice, and it works — right up until you want ten things built at once, each one properly reviewed, none of them lost when a laptop goes to sleep. At that point a faster apprentice doesn't help. You need a factory.
Gas City is one way to build one. It's an orchestration toolkit for multi-agent coding: a CLI called gc, plus a small set of building blocks for describing who does what, in what order, and what happens when something goes wrong. It builds on Steve Yegge's Gas Town project (Gas Town's roles now ship as a pack you can import into a city) and keeps Gas Town's vocabulary: mayors, polecats, refineries, convoys. The names are memorable, and the ideas underneath them are ordinary engineering.
This post covers those ideas one at a time, each with an analogy, and then follows one real piece of work through the factory: the one that produced this post.
What a software factory actually is
A software factory is a system where work moves through defined stations. Specialized workers run each station and follow written procedures, quality checks sit between stations, and everything that happened is recorded.
Compare a master carpenter with a furniture factory. The carpenter keeps the whole job in their head: the measurements, the order of cuts, which joint is still drying. That's brilliant, but it doesn't scale. They can't be in two places at once, and when they're out sick the job stops. A factory writes all of that down. The order, the procedure and the inspection all live on paper, so any trained worker can pick up any job at any step without asking anyone.
The factory doesn't replace skill. Every station still needs a skilled worker, and in a software factory that worker is a capable coding agent. What changes is where the coordination lives: it moves out of anyone's head and into the system.
A factory needs three things, and Gas City has a building block for each:
Each job is written down so any worker can pick it up mid-flight. In Gas City that's a bead.
The same steps run every time, and you improve them in one place. In Gas City that's a formula.
Something has to sit between "I'm done" and "it shipped." In Gas City that's the refinery.
The map: city, rigs and packs
Before looking at the work, here's the ground it runs on.
City: the industrial park
A city is a directory that holds a city.toml (the plan) and a .gc/ folder (the runtime state). Think of it as the whole industrial park: roads, power, the management office. When you run gc start, a supervisor brings the city up and keeps it running.
Rig: a production line
A rig is a project, usually a git repository, that's registered with the city. Each rig gets its own work ledger (a .beads/ database) with its own ID prefix.
A car plant might build sedans on line 1 and trucks on line 2. It's one site under one management, but each line stamps its own serial numbers. This site's repo is a rig called baraths84, so its work items have IDs like ba-us8.
Pack: a prefab production cell
A pack bundles agents, formulas, prompts and scheduled jobs into one unit you can import. It's like buying a prefabricated production cell, with the robots, tooling and safety procedures already included, instead of designing one yourself. Packs can import other packs. The factory behind this site comes from a single import, base-factory, which pulls in a chain behind it: an architect reviewer, a pull-request gate, and the core polecat and refinery agents.
# city.toml — one production line, one prefab import
[[rigs]]
name = "baraths84"
default_branch = "main"
[rigs.imports.base-factory]
source = "file:///…/packs/base-factory"The unit of work: beads
In Gas City, everything is a bead: tasks, messages between agents, workflow steps, and groups of work. They're all recorded the same way, in the same kind of ledger.
The best analogy comes from machine shops: the job traveler. A part moves through the shop with a sheet of paper attached that lists every operation, who did it and when. Anyone can walk up to the part, read the sheet, and know exactly where it is in the process. A bead works the same way. It has a title, a status (open, in progress, closed), an assignee, notes, dependencies on other beads, and metadata such as which git branch holds the work.
The analogy breaks down in a useful way, too. A paper traveler can get lost or covered in coffee. Beads live in a versioned database (Dolt, in this setup), so every ticket's history can be queried, and nothing disappears when a worker's session dies.
gc bd create "Write a post on prompt injection" --rig baraths84
gc bd show ba-us8 # read the traveler
gc ready # what can be picked up right now?The procedure: formulas, molecules and workflows
If the bead says what to do, the formula says how. A formula is a reusable recipe, written in TOML, that lists the steps for one kind of work.
Think of a recipe card in a bakery. The bread recipe doesn't change between loaves, and each loaf is a new run of it. Attach the recipe to a specific order and you have a run in progress, which Gas City calls a molecule. Newer (v2) formulas compile into a workflow, where each step is a bead of its own. (There's also the wisp, a lightweight, ephemeral run used for routine loops such as patrols, like a checklist on a whiteboard that gets wiped when it's done.)
Why do steps need to be beads? If the baker goes home sick halfway through, the next baker can see exactly which steps are ticked off, and nobody has to start the loaf over.
Formulas also let you match the amount of process to the job. A sandwich order doesn't need the inspection an aircraft part does:
| Formula | Isolation | Who lands the change |
|---|---|---|
mol-do-work | None: works in the current directory | The agent commits and closes the bead itself |
mol-polecat-commit | Its own git worktree | The agent commits straight to the base branch |
mol-polecat-work | Its own worktree and feature branch | The agent hands off to the refinery for review |
This post used mol-polecat-pr, which is mol-polecat-work with one extra stamp on the bead telling the refinery to open a pull request instead of merging directly. Gas City's own advice applies: use the lightest procedure that fits the job.
How work moves: sling, hooks and propulsion
Sling: dispatching the ticket
gc sling routes a bead to a worker and can attach a formula on the way. It's the dispatcher at a depot dropping a job ticket into a crew's tray. You can sling to one specific agent or to a pool, in which case any free worker in that pool can take it.
# hand a bead to the polecat pool, with a procedure attached
gc sling baraths84/base-factory.polecat ba-us8 --on mol-polecat-prHook: the ticket hook at each station
Every agent has a hook, the place where its assigned work hangs. The first thing an agent does when its session starts is check the hook. gc hook --claim claims one item atomically, so two workers can never grab the same ticket.
The propulsion principle
The rule that ties this together fits in one line: if there's work on your hook, you run it. The agent doesn't ask for confirmation or wait for a human to say "go." The polecat role prompt compares the factory to a steam engine where agents are the pistons, and a piston that waits for permission stalls the whole engine.
A relay runner who already has the baton doesn't turn around to ask whether they should run. The human made that decision when they slung the work. Making them make it again is how factories end up full of agents politely waiting for someone who has gone to lunch.
Who works here: the roles
The workers are ordinary coding agents (this city runs Claude Code), each in its own session and each given a role through a prompt. gc prime prints that role prompt for any agent. The roles are where the factory metaphor is most useful:
| Role | Factory analogy | What it actually does |
|---|---|---|
| Mayor | Plant manager | Talks to the human, turns requests into beads, dispatches them, and handles escalations. It doesn't work the line. |
| Polecat | Line worker from a staffing pool | Spawned on demand. Works one bead in its own git worktree and branch, hands it off, and clocks out. |
| Architect | Building inspector | Reads each branch against the rig's architecture decision records (ADRs) and writes a verdict back onto the bead. |
| Refinery | Quality control and shipping dock | The merge queue: takes finished branches one at a time, runs checks and gates, then merges or opens a pull request. |
| Witness | Floor supervisor | Watches a rig for stuck workers and orphaned work, and recovers it. |
| Controller | Building management system | Keeps reality matched to the config: starts sessions when there's demand and restarts crashed ones. |
| Dogs | Maintenance crew | Run housekeeping jobs, such as cleaning up stale databases. |
Two roles deserve a closer look.
Polecats are disposable on purpose. Each one gets an isolated workbench, a git worktree on its own feature branch, so ten of them can work in the same repo without bumping elbows. When a polecat finishes, it pushes its branch, records what it did on the bead, hands the bead to the refinery and exits. A polecat that sits idle after finishing is holding a pool slot that another bead could be using. (If the names sound like Mad Max: Fury Road, that's deliberate. The pool even hands out names like nux and furiosa.)
The refinery is the only way out. A real refinery turns crude oil into fuel you can put in an engine. This one turns raw branches into changes that are safe to merge. In this factory, the refinery runs the architect's review and an approval step before anything reaches main. Only the refinery closes work beads. If it rejects a branch, the bead goes back to the pool with a rejection_reason and the branch left intact, so the next polecat fixes the specific problem instead of starting over. It's like QC sending a part back with a red tag explaining the defect and leaving the half-finished work on the bench.
Grouping work: convoys
A convoy groups related beads, tracks their progress as one unit, and closes when every bead in it is done. Think of a shipment: a pallet of orders that leaves the dock together while the dock keeps count of "7 of 9 packed."
Even a single bead gets a convoy when it's slung with a v2 formula. The bead behind this post travels in convoy ba-qpa, a convoy of one. gc convoy stranded finds convoys that have ready work but no workers: pallets sitting on the dock with nobody assigned to them.
Keeping the lights on: orders, mail and nudges
Orders: the shift bell and the tripwire
An order pairs a trigger with an action, so the factory can start work without anyone typing a command. A factory has a wall clock that rings the shift bell, and sensors that fire when a box crosses a line. Gas City orders work the same way. Triggers can be a cron schedule, a cooldown, a condition, an event, or a manual run.
The base factory ships two orders, one on a clock and one on an event, and they're worth reading side by side because the trigger is the only thing that differs:
# factory-pulse — the clock: hourly bead counts
[order]
trigger = "cron"
schedule = "0 * * * *"
exec = "$PACK_DIR/assets/scripts/factory_pulse.sh"
# bead-closed-log — the tripwire: fires when any bead closes
[order]
trigger = "event"
on = "bead.closed"
exec = "$PACK_DIR/assets/scripts/bead_closed_log.sh"(Trimmed for space; each one lives in its own file under orders/.) Both append to the same log file, so by the afternoon it holds interleaved lines from the clock and the tripwire.
Mail and nudges: a memo versus a tap on the shoulder
Agents talk to each other in two ways. Mail is durable: messages are stored as beads, with a sender, subject, body and thread. It's the memo left in someone's pigeonhole, and it's used for things that must not get lost, such as escalations and handoffs. A nudge is a lightweight, ephemeral message pushed into a running session: a tap on the shoulder. Polecats are expected to send at most one mail per session and use nudges for everything else. The permanent, more expensive channel is reserved for messages that matter.
Following one bead: how this post was made
Theory is easier to follow when you can watch it run, so here's the bead behind this page.
The request. The whole brief was a single sentence in bead ba-us8: create a blog about software factory using GasCity covering fundamental concepts with clear explanations and analogies explained clearly.
The sling. The bead was routed to the polecat pool with mol-polecat-pr attached. That created a workflow root bead (ba-hhu) and wrapped the work in an input convoy (ba-qpa).
The steps. The formula expanded into seven step beads, and each one is claimed off the hook by whichever polecat is free. Here's the workflow as it stood while this section was being written:
ba-hhu mol-polecat-pr
✓ ba-mlr Load context and verify assignment nux
✓ ba-853 Set up worktree and feature branch nux
✓ ba-9wl Verify pre-flights pass on base branch nux
◐ ba-10b Implement the solution furiosa ← this post
○ ba-p9y Self-review and run tests
○ ba-7j3 Submit work to refinery and exit
○ ba-5h8 Finalize workflowA polecat named nux loaded the context, created a worktree on branch polecat/ba-us8 from main, and recorded both on the bead. It also ran lint, type-checking and a full production build on main before touching anything, so any later failure would clearly belong to this change. A different polecat, furiosa, then claimed the implementation step. It read the worktree path and branch name off the bead and wrote the words you're reading.
The handoff. After self-review, the branch gets pushed and the bead goes back to the pool, this time addressed to the refinery. From there, the architect reviews the change against the repo's ADRs, the refinery runs its checks and an approval gate, and only then does a pull request get opened. If you're reading this on the site, it made it through.
Notice what didn't happen. Nobody re-explained the task when the work passed from nux to furiosa, and nobody had to remember which branch it was on. The pre-flight result, the branch name and the worktree path were all written on the bead. That's the main reason a software factory works: any worker can be replaced because the work order is never lost.
Why bother, and when not to
A factory buys you four things:
- Parallelism. Many polecats work at once, each in its own worktree, without stepping on each other.
- Recoverability. A crash costs one step, not the whole job, and rejected work comes back with a reason attached and the branch intact.
- Auditability. Every handoff, verdict and closed bead is in the ledger, with history.
- Consistency. Formulas and ADRs write down "how we do things here," so agents follow the conventions instead of inventing their own.
It isn't free, though. A five-minute fix doesn't need a convoy, a worktree and a review gate, which is why Gas City offers mol-do-work for the trivial cases. A factory also amplifies whatever you feed it: vague beads produce vague work, just faster and in larger volume. Once agents do the typing, the quality of your tickets and decision records becomes the bottleneck.
- You have more work than one agent can get through serially
- Your conventions are written down well enough to review against
- An unreviewed change would be expensive (security, money, trust)
- You're willing to write tickets that a stranger could act on
If most of those are true, a factory will pay off. If none are, stick with the craftsman and the fast apprentice.
Cheat sheet
| Gas City term | Factory analogy | In one line |
|---|---|---|
| City | Industrial park | city.toml plus runtime state; everything runs inside it |
| Rig | Production line | A project repo with its own bead ledger and ID prefix |
| Pack | Prefab production cell | An importable bundle of agents, formulas, orders and prompts |
| Bead | Job traveler | The unit of work: status, owner, notes, metadata, history |
| Formula | Recipe card | Reusable TOML steps for how a kind of work gets done |
| Molecule / workflow | One run of the recipe | A formula instance whose (v2) steps are beads themselves |
| Sling | Dispatching a ticket | Routes a bead, with an optional formula, to an agent or pool |
| Hook | The ticket hook at a station | Where an agent's assigned work waits |
| Propulsion | A piston firing | Work on your hook? Run it, no permission needed |
| Convoy | A shipment | Related beads tracked and closed as a group |
| Mayor | Plant manager | Talks to humans, plans and dispatches |
| Polecat | Line worker from the pool | Works one bead in its own worktree, then leaves |
| Architect | Building inspector | Reviews changes against the ADRs |
| Refinery | QC and shipping dock | The merge queue: checks, gates, then merge or PR |
| Witness | Floor supervisor | Finds stuck or orphaned work and recovers it |
| Controller | Building management system | Keeps running sessions matched to config |
| Order | Shift bell and tripwire | Clock or event triggers that start work automatically |
| Mail / nudge | Memo vs. tap on the shoulder | A durable message vs. a passing ping |
Where to start
If you have a city running, these read-only commands show the whole factory:
gc status # everything running across the city
gc rig list # production lines and their bead prefixes
gc ready # work that can be picked up right now
gc convoy list # shipments in flight
gc formula list # the procedures on file
gc order list # the clocks and tripwiresBuilding your own takes five verbs: gc init creates the city, gc rig add registers a repo, gc import add brings in a pack, gc start brings it up, and gc sling hands it its first bead.
The vocabulary is the least important part. Without the polecats and convoys, Gas City comes down to three ideas: write the work down (beads), write the procedure down (formulas), and never let the worker who built something be the one who ships it (the refinery). Everything else exists to make those three ideas cheap enough to use every day.