Hello and welcome to Gas City 1.3! If you’re new around here, Gas City is a platform for building a software factory for your software. And what’s a software factory? I’m glad you asked!

A software factory is a system for orchestrating your CLI coding agents so that they can work together to produce higher quality code automatically.

Figure 1: One body of work, end to end: a left-to-right pipeline of stages — decompose, drain, review (loops until the verdict passes), gap analysis, fix gaps, check, ship — with a convoy of implementation beads hanging beneath the drain stage. The stages are the method (the formula); the beads are the work (the items).
One body of work, end to end: a left-to-right pipeline of stages — decompose, drain, review (loops until the verdict passes), gap analysis, fix gaps, check, ship — with a convoy of implementation beads hanging beneath the drain stage. The stages are the method (the formula); the beads are the work (the items).

If you’re happy with one or two instances of Claude Code or Codex, then running a software factory is probably more than you need. However, you might be ready for a software factory if you find yourself doing the same sequences over and over, like:

  • implement the plan using TDD
  • check the code to make sure you implemented the entire plan
  • check the code against these architectural best practices

You could build skills to do these things. Skills are amazing, but what they lack is a deterministic backbone that allows you to have confidence that you get everything you need, e.g.

  • Test-driven development
  • Gap analysis of the implementation against the plan
  • Best practices, e.g. DRY, SRP, error handling, etc.
  • Review loops with multiple models from multiple vendors to get their different points of view

It’s the Gas City orchestrator that allows you to execute each step deterministically, including the cool loop engineering thing that’s all the rage right now. I mean, I don’t want to say that the folks in the Gas Town Hall community have been using loops before they were cool (!) but it has been since Steve Yegge shipped Gas Town on New Year’s Day, so…

The big news in this release of Gas City is the 2.0 version of the formulas workflow language. The new version of formulas enables parallel beads execution and multi-agent review loops of the output. This enables real-life “execute the ready beads across 10 agents at a time, then use Claude, Codex and GLM to review the whole pile until it meets my quality bar.”

But formulas 2.0 is not the only thing new in this release! We’ve also got the new default gascity pack that is the exact same base we use, the Mayor becoming a skill any agent can load, and more. Let’s get to it!

Formulas 2.0: convoys, drains and loops

If you read Orchestration Is All You Need, you know that Gas City formulas turn agent babysitting into agent harnessing. A formula is just a TOML file that says “do these steps, in this order, with these agents.” That part hasn’t changed. What changed in the new formulas is who runs it.

In the original formulas, the agent you slung the formula at was the engine — it executed every step itself. In formulas 2.0, the orchestrator is the engine, and any number of agents can be workers.

Formulas themselves are driven by beads, the unit of work (tasks) in Gas City. Because beads are durable and external to the agent’s context, an agent can get stuck or you can close your laptop, and the formula picks up exactly where it left off.

Convoys

Collections of related beads are called convoys. When you shred your plans into beads, some beads have dependencies, either a parent-child relationship or a sequencing relationship (you need feature X before you can implement feature Y).

Figure 2: A convoy: one TODO app shredded into beads, with parent-child and depends-on edges telling the orchestrator what's ready to run -- and a few beads just chilling with no dependencies at all.
A convoy: one TODO app shredded into beads, with parent-child and depends-on edges telling the orchestrator what's ready to run -- and a few beads just chilling with no dependencies at all.

The orchestrator takes a convoy as input, which enables two things:

  1. Fan the convoy out across many agents to execute at once — that’s drain.
  2. Run the quality checks over the generated files — that’s the loop.

Drain: scatter the convoy, gather the result

The drain is the fan-out. The orchestrator takes your input convoy, scatters it into beads to execute in parallel, each in its own worktree. Here’s what a drain step actually looks like in the shipping gascity pack:

[[steps]]
id = "implement"
title = "Drain the implementation convoy"
needs = ["prepare-convoy"]   # the plan is already shredded into a convoy
...

[steps.drain]
context = "separate"         # one worktree per item, run in parallel
formula = "do-work"          # the item formula, run once per bead in the convoy
member_access = "exclusive"  # reserve each member so no one else claims it

In addition to the drain settings you see here, you can cap how many beads fan out at once (max_units), say what happens when one item fails (on_item_failure), and more.

Check your work

After drain, you want to use agents as reviewers in a loop (loop engineering!) to see if you’ve met the quality bar you’re after. The build-basic-review formula uses your city’s default provider to spawn three agents using different perspectives — acceptance, test evidence, simplicity — and each of them produces a verdict as a file. The loop is implemented as a check step that spawns the agent reviewers:

[steps.check]
max_attempts = 6                          # loop up to six times

[steps.check.check]
mode = "exec"                             # the only check mode: run a script
path = "implementation-review-approved.sh"
timeout = "10m"

[[template.children]]
id = "{target}.acceptance-review"
title = "Starter review: acceptance and correctness"
description_file = "{target}.acceptance-review.md"

...

You’ll notice that the check itself is a bash script. That script doesn’t look at your code to see if it’s DRY, SRP, TDD or whatever other best practices you’re measuring against; instead, it reads the verdict the agent reviewers wrote and turns it into a pass/fail check, like the review-gate script:

# read the latest review verdict for THIS attempt out of the ledger
VERDICT=$(bd list --all --metadata-field "gc.root_bead_id=$ROOT_ID" --json \
  | jq -r '... latest code_review.verdict for this attempt ...')

case "$VERDICT" in
  done|approved|pass)  echo "approved";          exit 0 ;;    # stop the loop
  *)                   echo "needs another pass"; exit 1 ;;   # go around again
esac

The judgment lives in the reviewers; the script just reports the verdict.

Loop until we get it right

When the reviewers decide that we need another pass to apply their feedback, that’s a step too. The synthesized findings feed an apply-review-findings lane that owns the verdict:

[[steps]]
id = "apply-review-findings"
title = "Apply the review findings"
needs = ["synthesize-review"]   # runs after the lanes are synthesized
...
# its prompt: "make the smallest focused changes, run the proof commands,
# then set code_review.verdict=done only when all three lanes approve."

The fixes land, the work gets re-reviewed, a fresh verdict gets written, and the gate runs again until the reviewers approve or max_attempts is hit.

Figure 3: The loop: drain the convoy into parallel worktrees, gather the changes, review them from three perspectives, synthesize one verdict, and either finalize or go around again.
The loop: drain the convoy into parallel worktrees, gather the changes, review them from three perspectives, synthesize one verdict, and either finalize or go around again.

We’ve been using this new formula orchestrator behind a feature flag to run our own “maintainer city” for a month or so, and it’s what allows us to run 50+ PRs using 100+ agents at a time while also keeping quality high. Now that it’s validated, we’re making it available for your use along with the gascity pack that contains the base formulas we use.

The gascity pack: the software factory we use

You may have noticed several links in the section above to the gascity pack. A pack is a unit of configuration and sharing in Gas City. In fact, the original Gas Town software factory is available as a pack that runs on Gas City. If you’ve submitted an issue or a PR against any of the repos in the gastownhall org, then you’ve interacted with an agent that’s executed one of the gascity pack formulas to review your contribution and gotten a response like this one:

Figure 4: The maintainer adoption review my PR got: the review/fix loop passed, maintainer fixes were pushed straight to my branch, and the PR was marked merge-ready -- all composed by the adopt-pr formula.
The maintainer adoption review my PR got: the review/fix loop passed, maintainer fixes were pushed straight to my branch, and the PR was marked merge-ready -- all composed by the adopt-pr formula.

I find this message to be comforting. Sure, it has nice things to say about my code, but more importantly, it found things I missed and then fixed them!

This kind of safety net is very different than the PR rejection feedback a contributor normally gets when their PR isn’t perfect. At Gas City, the formulas define the quality bar and guide the agents to bring PRs up to that standard automatically. When the agents aren’t sure, they stop and ask, but that’s rare once a PR has been marked for inclusion in the repo.

The message came from julianknutsen, who is not himself a coding agent but rather the CTO of Gas City, Inc., the architect and lead maintainer of the Gas City project. As the operator of the Gas City software factory, Julian spends his time building the factory itself and deciding which PRs he wants to accept and which issues he wants to address. The agents themselves do the work based on the configuration of the pack.

And while the message came from Julian, it was composed by the adopt-pr formula, which is an early name for one of several that come out of the box with the gascity pack. With this release, these formulas are available for your own use cases:

You haveLaunch
Just an ideabuild-basic
Approved requirementsbuild-from-plan
Approved requirements, plan, and plan reviewbuild-from-decompose
An implementation convoybuild-from-convoy
Implementation evidencebuild-from-review
An approved convoy, no build wrapper wantedimplement
A GitHub issue or PR URLgithub-issue-triage,
github-issue-fix,
github-pr-review

The gascity pack is the default pack when creating a new city, so you can simply call gc init <city-folder> and choose the default:

$ gc init ~/chris-city
Welcome to Gas City SDK!

Choose a config template:
  1. gascity planning & implementation skills pack (default)
  2. minimal default coding agent
  3. gastown multi-agent orchestration pack
  4. custom empty workspace, configure it yourself
Template [1]:

Choose your coding agent:
  1. Claude Code
  2. Codex
If you don't see your coding agent, configure it and restart the wizard.
Agent: 1
[1/8] Creating runtime scaffold
...
[8/8] Waiting for supervisor to start city
  Adopting sessions...

A city is a place to store configuration for one or more rigs, each of which is a project folder (most often a git repo) that’s been configured to work with Gas City. You add a rig using the gc CLI:

$ gc rig add ~/projects/todo-app --city ~/chris-city
Adding rig 'todo-app'...
  Prefix: ta
  Import: gc=https://github.com/gastownhall/gascity-packs/tree/main/gascity/roles (default)
  Initialized beads database
  Generated routes.jsonl for cross-rig routing
Rig added.

There are a ton more CLI options on gc, but I don’t recommend that you use them. Instead, you should talk to the Mayor!

Every agent is the Mayor

In preview releases, the Mayor was a named agent session you attached to via tmux that knew all about how to use the gc CLI. However, most people just wanted to use their favorite CLI coding agent to drive Gas City. That didn’t work very well, because gc does a lot, so it was hard for a random CLI agent to know what to do with it. So, instead of forcing you to choose tmux to use the Mayor or your own agent that didn’t know gc, we repackaged the Mayor into a skill that works from any agent running in your city. Those agents can be desktop apps or CLI coding agents or VSCode extensions — any agent that can invoke the skill you address as Mayor, $mayor, /mayor, or @mayor.

As an example, here we’re talking to the Mayor in Claude Code running from our todo-app rig folder:

And just like that, Claude Code became the Mayor of my city. After a bit more probing, I asked it to execute the recommended build-from-plan formula. Under the hood, the Mayor is discovering formulas the same way you would, just with the new JSON surfaces (more on those below):

gc formula catalog --json
gc formula show implement --json

The Mayor knows your city, knows your formulas, can drive the whole factory, and now it rides along in every agent you already use.

Less magic, more JSON

The flip side of “talk to the Mayor and it just works” is “I have no idea what’s happening.” In prior releases of GC, built-in packs were silently spliced into every city, a fallback agent quietly absorbed work nobody routed, and providers were inherited by name-matching. Convenient, until you needed to understand it.

The 1.3 release trades that magic for automation and ease of discovery. There’s now one explicit, bundled core pack (it absorbed the old maintenance pack’s housekeeping orders), and instead of silently splicing built-ins into every city, gc init writes them out as pinned imports you can read:

# pack.toml — written by gc init, pinned and visible
[imports.core]
source = "https://github.com/gastownhall/gascity.git//internal/bootstrap/packs/core"

[imports.bd]
source = "https://github.com/gastownhall/gascity.git//examples/bd"

If you’re upgrading, a fixable doctor check does the whole migration for you:

gc doctor --fix

Also in this release, we extended support for the --json option we started in 1.2 across the CLI. The Mayor skill leans on it directly, as described above, but saving any agent from needing to parse raw text is useful.

Breaking changes

We work to keep these small, and this release is mostly additive, but a handful are worth your attention if you’re upgrading an existing city. The short version: run gc doctor --fix once per city after upgrading. The doctor will handle the migrations, driven by three checks (provider-catalog, builtin-pack-imports, and packv2-import-state).

  • Built-in packs are no longer implicit. The per-city .gc/system/packs tree is retired. Your city now composes core (and bd, if you use the bd provider) through pinned [imports] plus a packs.lock. gc doctor --fix strips the legacy includes, writes the pinned imports, and refreshes the lock and cache for you.
  • Providers must be declared. Any workspace.provider or agent-level provider value now needs a matching [providers.<name>] table. gc doctor --fix appends the missing built-in aliases — e.g. [providers.claude] base = "builtin:claude" — but custom providers still need hand-authored tables.
  • The fallback agent field is removed. Cross-pack agent name collisions are now hard errors. If you leaned on a fallback agent (or the implicit fallback dog), ship your own worker pool and make your routing explicit.
  • gastown now comes from gascity-packs. The checked-in examples/gastown/packs/gastown tree is gone; consume it as a pinned public import and move any local customizations into your own imported pack instead of editing .gc/system/packs.
  • Generated configs no longer pin formula_v2. Formula v2 is on by default, so gc init omits the line; an explicit formula_v2 = false is still honored and preserved on round-trip.
  • The control-dispatcher named session is gone. The controller now serves it by demand-scaling the core pack’s template, so gc doctor --fix drops the redundant [[named_session]] alias older builds wrote and silences its “backing template not found … disabled” warning.
  • Inside formulas, {{bead_id}} is no longer valid in v2 — use the controller-injected {{convoy_id}} — and {{issue}} is a deprecated one-release alias that you should migrate off.

Where are we?

Gas City started as a way to stop babysitting agents and start harnessing them. With formulas 2.0, the harness loops deterministically: fan a convoy out across as many agents as you’ve got, review the combined results, and iterate until it meets the quality bar. This is now the default orchestrator you get when you run gc init, drivable from any agent you already use, just by asking the Mayor.

None of this happens without the Gas Town Hall community building it. Since we released Gas City 1.2, the city has not stopped running. To get to the 1.3 release, we’ve merged more than 600 pull requests from 59 contributors and closed around 70 issues — and that’s just the gascity repo. The other half of this release lives in a brand-new repo, gastownhall/gascity-packs. Thanks to all of the contributors! Keep those cards and letters coming.

Gas City is a platform on which you can build your own software factories. We’re just getting started collecting the packs that run on that platform to provide you with real-world configuration on which to base your own factories. More on that in the 1.4 release which is already underway!

Have thoughts about this post? Join the Gas Town Hall Discord!