In a previous article I wrote about Beads, the git-backed issue tracker for AI coding agents. My Matrix project is split across several repositories, each with its own Beads database, and the repos are configured so that when one of them needs a change in another, the agent files a bead in the target repo and blocks on it.
That works well, but it leaves one manual step: someone has to open a Claude Code session in the other repo, work the ticket, and come back. With five or six repos, that “someone” spends the day juggling terminals.
Gas Town automates that step. It is a workspace manager, built by the author of Beads, that coordinates many Claude Code instances on top of your beads: a coordinator agent dispatches work, workers run inside the right repo with that repo’s CLAUDE.md, and blocked tickets get picked up as soon as their dependency closes.
This guide walks through the full setup. Everything was tested on macOS; Linux commands are given where they differ.
The vocabulary
Gas Town has a lot of its own terms. These are the ones you need:
| Term | What it is |
|---|---|
| Town | The workspace folder (e.g. ~/gt-matrix). One per project. |
| Rig | One repository inside the town, with its own beads and agents. |
| Crew | Your personal clone inside a rig, where you work by hand. |
| Mayor | The coordinator: a Claude Code instance that sees every rig and dispatches work. |
| Polecat | A worker agent spawned in a rig to complete one bead, in its own git worktree. |
| Convoy | A group of beads tracked together, usually one feature across several rigs. |
| Refinery | A per-rig merge queue for polecat branches. |
| Witness / Deacon | Watchdogs: per-rig and town-wide health monitoring. |
In short: you talk to the Mayor, polecats do the work, the Refinery merges, the watchdogs keep things alive.
Prerequisites
- Git 2.20+
- tmux 3.0+ (the agents run in tmux sessions)
- Claude Code CLI, logged in
- Existing repos that already use Beads (optional, but that is the point)
Step 1: Install
On macOS, Homebrew installs gt, bd (Beads) and dolt (the database Beads uses) together:
brew install gastown
On Linux, install Dolt first, then:
go install github.com/steveyegge/gastown/cmd/gt@latest
go install github.com/steveyegge/beads/cmd/bd@latest
Check that everything is there:
gt --version
bd --version
tmux -V
If you already had Beads installed, run which -a bd to make sure only one bd is on your PATH.
Step 2: Create the town
gt install ~/gt-matrix --git
cd ~/gt-matrix
gt up
gt doctor --fix
--gitmakes the town itself a git repo, so its configuration can be versioned.gt upstarts Dolt, a background daemon, the Mayor and the watchdogs.gt doctor --fixchecks around ninety things and repairs what it can.
If doctor reports a stale .claude/settings.json that it refuses to delete because it is tracked in git, untrack it and run doctor again:
git rm --cached .claude/settings.json
echo ".claude/settings.json" >> .gitignore
git commit -am "Untrack generated Claude settings"
gt doctor --fix
gt up --restore
There is also a --shell flag that installs a hook offering to add every git repo you cd into to the town. I recommend skipping it, especially if you plan to have more than one town.
Step 3: Add your repos as rigs
Each repo becomes a rig. gt rig add clones it into the town:
cd ~/gt-matrix
gt rig add matrix_api https://github.com/you/matrix-api.git \
--prefix mapi \
--local-repo ~/code/matrix-api
A few rules learned the hard way:
- Rig names accept letters, digits and underscores only:
matrix_api, notmatrix-api. - Always pass
--prefix. Without it, Gas Town derives one from the name, and two rigs likematrix_apiandmatrix_adminboth end up asmaand collide. - Use the prefix your existing beads already use, so old IDs and cross-repo references stay valid. Find it with
bd list | head -3in the original repo. - Prefer short prefixes without hyphens (
mapi, notmatrix-api). Hyphenated prefixes work inside the rig, but town-level lookups only read up to the first hyphen, so the Mayor cannot dispatch those beads. --local-reposhares git objects with your existing clone, which saves a lot of disk.
Check the result:
gt rig list
Step 4: Bring your existing beads
Gas Town stores every rig’s beads in its own Dolt server, not in the repo. If your old beads were local (not committed), the clone will not bring them. Export them from the old repo and import them into the rig:
cd ~/code/matrix-api
bd export -o /tmp/matrix-api.jsonl
cd ~/gt-matrix/matrix_api
bd import -i /tmp/matrix-api.jsonl
bd list | head
Check bd import --help on your version first. After the import, check one bead that depends on another repo (bd show <id>) to confirm the cross-repo links survived.
From now on, the rigs are the source of truth. Stop using the old clones, or you will end up with two diverging sets of tickets.
Step 5: Create your crew workspace
For hands-on work in a single repo, you still use a normal Claude Code session, just from your crew clone:
gt crew add sami --rig matrix_api
cd ~/gt-matrix/matrix_api/crew/sami
claude
It loads that repo’s CLAUDE.md and reads and writes the rig’s beads as usual. Crew names follow the same rule as rigs: no hyphens.
Step 6: Let the Mayor dispatch work
Attach to the Mayor:
cd ~/gt-matrix
gt mayor attach
Then describe what you want, or point it at existing beads. To group a feature across repos:
gt convoy create "Billing export" mapi-a1b mweb-c2d mwrk-e3f --notify
And tell the Mayor: “Work convoy Billing export: sling the ready beads to their rigs, and as each one closes, dispatch the beads it unblocks.”
That is the whole point of the tool: a polecat spawns in the right rig, works the bead, and when it closes, the blocked bead in another repo becomes ready and gets dispatched next. You can also dispatch a single bead yourself:
gt sling mapi-a1b matrix_api
Detach from the Mayor with Ctrl-b then d; it keeps working.
Watching what happens
gt agents # who is running
gt convoy list # progress per feature
gt feed # live event stream
gt dashboard --port 8090
The dashboard is a web page at http://localhost:8090 showing polecats, convoys, the merge queue and escalations. Use localhost rather than 127.0.0.1 if your browser refuses the latter.
To ask the Mayor something without letting it act, attach and switch to plan mode (Shift+Tab), or send it mail:
gt mail send mayor -s "Status" -m "Which beads are in progress?"
Step 7: Pick a model per task
Define one agent alias per Claude model:
gt config agent set claude-haiku "claude --dangerously-skip-permissions --model haiku"
gt config agent set claude-sonnet "claude --dangerously-skip-permissions --model sonnet"
gt config agent set claude-opus "claude --dangerously-skip-permissions --model opus"
gt config default-agent claude-sonnet
Keep --dangerously-skip-permissions: polecats run unattended in tmux, and without it they stop at the first permission prompt with nobody to answer. Put your hard limits in a PreToolUse hook instead (see the next step).
Then let the Mayor choose. Write a policy file and reference it from the town CLAUDE.md:
cat > mayor/MODEL_POLICY.md <<'EOF'
# Model selection when slinging
Always pass --agent to gt sling:
- claude-haiku: docs, renames, config tweaks, small isolated changes
- claude-sonnet: normal features and bug fixes (default)
- claude-opus: migrations, auth/permissions, cross-repo contracts, or a bead that already failed once
Write the chosen agent and a one-line reason in the bead notes.
If a polecat fails or stalls, re-sling with the next model up.
EOF
printf "\n## Model selection\nFollow mayor/MODEL_POLICY.md whenever you sling work.\n" >> CLAUDE.md
git add mayor/MODEL_POLICY.md CLAUDE.md && git commit -m "Model selection policy"
Keeping the policy in its own file protects it from gt doctor --fix, which manages the town CLAUDE.md.
Step 8: Keep the agents in their lane
Agents that run unattended will happily make decisions for you. A few guardrails:
- Enforce, don’t just ask. A
PreToolUsehook that blocksgit push,sshand deploy commands is enforced; a sentence in a prompt is only advice. - List the decisions that are yours in the town
CLAUDE.md: deploys, merges, migrations, closing beads, changing rigs. Add: “If a bead is ambiguous, escalate instead of guessing.” - Write precise beads: acceptance criteria, decisions already made, open questions marked as such. Vague beads are where agents improvise.
- Ask for evidence: a commit hash, test output or the verifying command in every “done” note.
- Keep a human merge gate on shared repos: polecats open PRs, you merge. Use
--merge=localwhen slinging so the Refinery does not merge either. - Cap concurrency while you build trust:
gt config set scheduler.max_polecats 2
Day to day
cd ~/gt-matrix && gt up # start of day
gt down # end of day
Each rig runs a witness and a refinery that patrol continuously and query Dolt. With many rigs, that keeps the CPU busy even when nothing is happening. Stop the rigs you are not using:
gt rig stop matrix_admin matrix_docs
You do not lose anything: slinging to a stopped rig boots it automatically.
Tips and pitfalls
- One Dolt port per town. A second town needs its own:
gt install ~/gt-other --git --dolt-port 3308. - New rigs ask for folder trust. The first time an agent starts in a new rig, Claude Code asks whether you trust the folder, and Gas Town times out waiting. Attach to the session (
tmux -L <town-socket> attach -t <session>) and accept once. - Pin the toolchain. A
brew upgradecan bumpbd, which then requires a schema migration on every database. Rungt downbefore upgrading, and pin the tools so routine upgrades skip them:brew pin beads gastown dolt. - Migrations can exceed Dolt’s timeout. Gas Town’s Dolt config cuts queries at five minutes, and a Beads schema migration on the town database can take longer. Back up
.dolt-data(with Dolt stopped) before migrating, and if it fails withcontext canceled, rerun it on a server with a longer timeout. - Patrol warnings are known. In the 1.x releases, witnesses may report “no active patrol found”. It is an open upstream issue in the patrol bookkeeping and does not affect beads, slinging or polecats.
- Uncommitted scaffolding. Finished polecats can leave
.beads,.claudeand.runtimefolders behind, which blocksgt rig stop. Check withgit statusthat it is only scaffolding before using--force. - Watch the disk. Every rig and polecat worktree is a checkout.
--local-repohelps a lot.
Is it worth it?
For a single repo, no: one Claude Code session is enough. Gas Town pays off when work regularly crosses repository boundaries and you want tickets to flow between repos without you opening every session.
It is also a young tool that moves fast, so treat it like infrastructure: pin versions, back up the town database, stop what you do not use, and keep a human in the merge loop. With that in place, the cross-repo ticket dance I used to do by hand now runs on its own.