my-city running with my-project rigged, and agents for mayor
and reviewer (along with the corresponding prompts):
agents/<name>/prompt.template.md.
Beads are fundamental to the system. You’re going to be working with crew to
turn plans into beads that can be executed in parallel by polecats.
What is a bead
A bead is a unit of work with an ID, a title, a status, and a type. We use thebd tool to work with beads directly.
bd list renders a tree, with parent beads grouping their children.
The leading glyph is the bead’s status, followed by ID, priority (P2), and
title. Pass --flat for a single-level list and --all to include closed
beads.
Every bead has:
- ID — unique identifier prefixed with two letters derived from the city or
rig name (e.g.,
mc-194for a city named “my-city”,ma-12for a rig named “my-app”) - Title — human-readable name
- Status —
open,in_progress,blocked,deferred, orclosed - Type — what kind of bead it is
Bead types
The type determines what a bead represents:| Type | What it is | Created by |
|---|---|---|
| task | A unit of work | bd create, formula steps |
| message | Inter-agent mail | gc mail send |
| session | A running agent session | gc session new |
| molecule | Persistent formula instance | gc formula cook |
| wisp | Ephemeral formula instance | gc sling --formula |
| convoy | Container grouping related beads | gc convoy create, auto-created by sling |
Creating beads
Most beads are created indirectly:gc session new my-project/reviewercreates a session beadgc mail send mayor "Subject" "Body"creates a message beadgc formula cook reviewcreates molecule + step beadsgc sling mayor review --formulacreates a wisp bead + convoy
bd to create them manually:
Bead lifecycle
Beads move through a small set of states:- open — work hasn’t started yet. Discoverable by agents via hooks.
- in_progress — claimed by an agent, being worked on.
- closed — done.
- blocked — has an open
blocksdependency. Set automatically. - deferred — explicitly snoozed until a date.
blocked and deferred are derived states the system manages for you.
--status (--state is a different command for state
dimensions).
Beads as execution state
The bead store is effectively the execution state of the entire system. Every session that’s running, every message in flight, every formula step being worked on — all of it is a bead with a status. If you want to know what the city is doing right now, you query the store. The exact output depends on what is currently active in your city. For example:Labels
Labels are how beads get organized and routed:bd label add takes a single label per call — apply multiples one at a time.
Some labels have special meaning in Gas City:
gc:session— marks session beadsgc:message— marks mail beadsthread:<id>— groups mail messages into conversationsread— marks a message as read
Metadata
Beads carry arbitrary key-value metadata for structured state:session_name,
alias), routing (gc.routed_to), merge strategies, and formula references.
You can use it for anything you want to attach to a bead without changing its
title or description. Use --unset-metadata <key> to remove one.
Dependencies
Beads can depend on other beads. You’ve already seen this in formulas — when a step declaresneeds = ["design"], that’s a blocking dependency. The step bead
can’t start until the design bead closes. Dependencies are how Gas City enforces
ordering without a central scheduler: each bead knows what it’s waiting for, and
agents only see work that’s ready.
mc-xp7 won’t appear in any agent’s work query until mc-a4l is closed.
This is the same mechanism that makes formula step ordering work — needs
declarations become blocks edges between step beads.
The dependency types are blocks (must close before the other can start),
tracks (informational — “I care about this”), related (loose
association), parent-child (containment), and discovered-from (work
that surfaced while doing other work). Only blocks affects work visibility.
Beads also have a separate parent-child relationship — a bead can set a
parent_id linking it to a container. This is how convoys and molecules group
their children. The difference: dependencies express ordering (“do A before B”),
while parent-child expresses containment (“these beads belong to this group”). A
convoy’s children don’t depend on each other — they’re just members of the same
batch.
Convoys
If you’ve slung a formula, you’ve already created a convoy without knowing it — Gas City automatically wraps dispatched formula work in one. You’ll see them inbd list as beads with type convoy, and in gc convoy list with progress
summaries. They matter when you need to track a batch of related work as a unit:
“are all five of these tasks done yet?” is a convoy question.
You can also create them by hand to group arbitrary work — say, a set of beads
you want to track together as a sprint or a deploy:
convoy. The child beads are linked via their
ParentID — the same parent-child mechanism used by molecules, just for
grouping instead of step ordering.
Auto-close
When a bead closes, Gas City checks whether its parent is a convoy with all children now closed. If so, the convoy closes automatically. This happens in the background via theon_close hook — no polling, no manual intervention.
Convoys with the owned label skip auto-close. These are for workflows where
you want explicit control over when the convoy completes:
Adding beads and checking convoys
Sometimes work grows after a convoy is created — a new bug surfaces mid-sprint, or a dependency gets discovered after the plan is set. You can add beads to an existing convoy:Stranded work
To find open beads in convoys that have no assignee — work that’s stuck waiting for someone to pick it up:Convoy metadata
Convoys carry metadata that controls how grouped work behaves:convoy.owner— which agent manages this convoyconvoy.notify— who to notify when the convoy completesconvoy.merge— merge strategy for PRs (direct,mr,local)target— target branch inherited by child beads
How agents find work
This is where beads connect to the runtime. Agents discover work through hooks — shell commands that run between turns and check for available beads. The typical flow:- Work is created (via
bd create,gc sling, formula cook, etc.) - Work is routed to an agent (via assignee or
gc.routed_tometadata) - Agent’s hook runs a work query and looks for matching ready beads
- If work is found, the hook injects it into the agent’s context as a system reminder
- The agent sees the work and acts on it (GUPP: “if you find work on your hook, you run it”)
mc-xp7 is blocked by mc-a4l right now, this query won’t return
anything yet. That’s the point: blocked work is invisible to agent work
queries. Once mc-a4l closes, rerun the same query and mc-xp7 becomes
eligible.
This is the “pull” model — agents check for work rather than having work pushed
to them. It’s simple, crash-safe (queued work survives restarts), and scales
naturally.
The bead store
Beads are persisted in a store. Gas City supports several backends:- bd (default) — Dolt-backed database via the
bdCLI. Full-featured, good for production. - file — JSON file on disk. Simple, good for tutorials and small setups.
- exec — Delegates to a custom script. For integration with external systems.
city.toml:
You don’t usually work with beads directly. The higher-level commands —
gc session, gc mail, gc sling, gc formula — handle bead creation and
management for you. But when you want to query what work is outstanding across
the city, create ad-hoc tasks for agents, inspect the dependency graph of a
formula, or debug why an agent isn’t picking up work — that’s when you reach for
bd directly.
What’s next
- Orders — formulas and scripts on autopilot, gated by time, schedule, conditions, or events