Hello and welcome to Beads 1.1! If you’re new here:
beads is the database your coding agent uses to
remember what it’s doing. Instead of holding a plan in its context window —
where it evaporates the moment the session ends — an agent writes each unit of
work to bd, a small database that lives in your project. Each unit is a
bead: it has a status (open, in_progress, blocked, deferred, closed,
plus a couple of specialty states), and beads can depend on each other, so a big
feature becomes an ordered graph of smaller ones instead of one giant markdown
file.
A basic beads session looks like:
bd create "Add rate limiting to the API" --priority 1
bd ready # what's unblocked and ready to work on
bd close <id> --reason "Shipped in #142"
Because the state lives in the database and not in the agent’s head, an agent can get stuck, crash, or hand off to a different agent entirely, and the next one picks up exactly where the last one left off. That’s true whether it’s one agent on your laptop, a small team sharing a database across their machines and CI, or a whole fleet of agents running in parallel — and it matters most the moment more than one clone starts writing to the same store.
Here’s why: beads stores it’s state in a Dolt
repository under the hood, and Dolt repositories can fork. If two clones each
migrate their own copy of the schema independently, they can no longer merge —
Dolt refuses outright. cannot merge because table dependencies has different primary keys in its common ancestor is not a sentence you want to see at 2am.
If you only ever run beads locally with no remote, this specific risk doesn’t
apply to you. If you sync a beads database across more than one machine — a
teammate, a CI runner, a fleet of agents — keep reading.
None of this is hypothetical. Every fix below started as a bug report from
someone who hit the edge in the wild: two clones forking on the same dependency
edge (#4259), a half-finished
migration bricking a database on open
(#4502), an orphaned row
failing every bd create
(#4534), compaction quietly
eating an issue’s original text
(#4464). That 2am error
message is a real one.
Beads 1.1.0 was built to close that off, and it shipped on July 4th after eight weeks and two release candidates. Since v1.0.4, more than 30 contributors merged 260+ pull requests, closed 80+ issues, and landed 600+ commits. Top of the commit count: Dustin Brown and Matt Wilkie, with beads creator Steve Yegge still in the trenches, and Jim Wordelman, who sent more than 20 of those pull requests — also on the list. Thank you, all of you. None of this happens without people showing up to file the issues and send the PRs, so thank you to the community for that!
How to upgrade safely
If you take one thing from this post, take this. Running solo with no remote?
Install the new binary and go — the next bd command migrates in place, same as
always. If more than one clone syncs the same remote, upgrade in this order:
# 0. Back up first — cheap insurance before you touch a remote-backed database
bd export --all -o backup.jsonl
# 1. On every clone, with your CURRENT binary — get everyone in sync first
bd dolt push && bd dolt pull # repeat until clean, then stop editing
# 2. On the designated migrator only — install the new binary, then:
BD_ALLOW_REMOTE_MIGRATE=1 bd migrate
bd dolt push
# 3. On every other clone — install the new binary, then:
bd bootstrap # adopts the migrated database; do NOT run bd migrate here
A word about that env var, because it’s the sharpest tool in this post.
BD_ALLOW_REMOTE_MIGRATE=1 declares this clone is the designated migrator and
switches the gate off for that one run — all of it, including the forked-schema
checks everything below celebrates. That’s the point: migrating a database other
clones depend on is a judgment call bd needs you to make explicitly. So handle
it accordingly: exactly one clone, only after step 1 has everyone in sync, and
never wired into every clone’s upgrade job — two clones running it at once is
the independent-migration fork this whole release exists to prevent. (At a
keyboard, plain bd migrate is often enough — when the gate can prove you’re a
safe first mover it proceeds on its own, and when it can’t it stops and lists
your options.)
The full procedure — including upgrades from much older versions — is in the upgrade guide.
The rest of this post is why that recipe exists — and what 1.1 does so that getting it wrong no longer costs you your database.
Migrations that work, even with a dozen clones
The problem in one sentence: when more than one clone shares the same remote, an
uncoordinated schema migration on one of them can fork the whole thing —
permanently, without manual surgery. A solo bd database with no remote never
hits this. Everyone else is exposed the moment any two clones migrate
independently:
The state-aware remote-migrate gate is on by default now
(#4516,
#4551). Every time bd would
silently auto-migrate a database that has a remote configured, it now checks its
state against the remote and picks a lane instead:
The skew case is detectable at all because schema_migrations now records a
hash of each migration’s content alongside its version, so bd doctor can spot
when two clones both claim “v53” but got there differently. And if you do hit
Dolt’s hard fork refusal, bd now recognizes it and prints the actual recovery
playbook
instead of just the raw error.
This release also heals databases that already got bitten. A v53 migration
that failed partway used to leave a database bricked — every command failing
on open. Now it self-heals automatically on your next bd command: it repairs
issues tables that predate the rig/agent columns
(#4502) and backfills
missing dependency columns before v53 runs
(#4558). A separate self-heal
cleans up an orphaned counter row that could brick every bd create on an
otherwise healthy database
(#4534). And a dirty working
set no longer deadlocks recovery: bd dolt commit and bd vc commit now open
past the guard that used to trap them too
(#4567).
At the keyboard, the new moving parts are small:
bd doctor # catches two clones claiming "v53" with different content
bd dolt commit # commits a dirty working set that used to deadlock recovery
Add it up and upgrading a shared beads database is no longer a coordination problem you solve with a wiki page and good timing: the dangerous move gets refused, the safe move happens on its own, and an already-bricked database heals on the next command. Bringing a second machine into the loop — a teammate, a CI runner, a fleet — stops being the moment you hold your breath.
Safer syncing across every clone
The same “clones need to agree” problem showed up in smaller ways too, and this
release closes several of them. Two clones that independently add the same
dependency edge, or that delete an issue on one side while adding a comment to
it on the other, used to fail to merge outright
(#4259) — now they converge
cleanly instead (in the delete-vs-comment case the delete wins, taking the
comment with its issue the way a cascade delete would have). Pulls that used to
strand half-merged — foreign-key violations left behind by issue deletes, or
clones whose schema_migrations tables came from different eras — now
cascade-repair or auto-resolve on their own. And when a merge genuinely can’t be
fixed automatically, bd prints the recovery path instead of a stack trace and
best wishes. The net: two clones doing perfectly ordinary work no longer need a
human on call to unstick their merges — which was the point of syncing through
a remote in the first place.
The is_blocked flag — which tells you whether an issue is waiting on something
— used to go stale silently across a merge, hiding ready work or showing blocked
work as ready. Pulls now recompute it automatically, and if a database’s already in a
bad state, the new bd recompute-blocked command does a full repair in both
embedded and server mode. Day to day:
bd dolt pull # merges settle themselves; is_blocked recomputed on the way in
bd recompute-blocked # full repair for a database already in a bad state
bd ready # the queue your agents can take at face value
That flag is what bd ready reads, and bd ready is what your agents act on.
With every pull recomputing it, a fleet can trust the queue without a human
auditing it first, which means no more idling past work that was secretly ready,
or piling onto work that wasn’t.
Restore actually restores
The recipe up top says to back up first, which raises an uncomfortable question
this release finally answers: does the restore path actually work? Start with
compaction. bd admin compact — the space-saver that squashes long text on old
issues — used to overwrite descriptions and clear notes in place with no archive
at all, and bd restore was display-only, reconstructing content from a
Dolt-history heuristic that garbage collection could quietly erase. Compact an
issue, and its original content could be gone for good. In 1.1, compaction
archives a full snapshot before touching anything — if the archive fails,
compaction aborts with your content intact — and bd restore --apply writes the
original straight back
(#4464). Beads also stopped
advertising a compaction tier that promised 95% savings and was never
implemented (#4465) —
honesty in the help text counts too.
bd import — the restore half of every bd export backup — grew up alongside
it. The stale guard that keeps an old snapshot from clobbering newer work is now
enforced inside the import transaction, ties on timestamps keep your local row,
and import reports what it actually changed instead of finishing silently. And
when rolling back to an older snapshot is the whole point, --allow-stale lets
you do that:
bd admin compact # squash old closed issues — snapshot taken first
bd restore <id> # inspect the archived original (display-only)
bd restore <id> --apply # write it back
bd import --allow-stale backup.jsonl # deliberately roll back to an older snapshot
Now compaction becomes routine maintenance on a long-lived database, and the bd export at step 0 of the upgrade recipe is insurance that actually pays out.
Breaking changes
Beads works hard to keep this list short, but three behavior changes are worth knowing before you upgrade a database you care about.
Remote-backed migrations are gated by default. Before this release, running
bd against a database with a remote configured would silently apply pending
migrations on whichever clone got there first — exactly how two clones fork a
schema. bd now refuses that and tells you what to do instead — the recipe
above is the order that keeps every clone mergeable.
JSONL auto-export flipped from on to opt-in. .beads/issues.jsonl used to
refresh and get committed automatically after most writes. It’s now an opt-in
export for tools that want it, not the default mutation path. If your setup
depends on that file staying fresh automatically, turn it back on:
bd config set export.auto true
bd config set export.git-add true
The dependencies table got re-keyed. The old depends_on_id column is
gone — the target now lives in three typed columns, with depends_on_id
surviving only as a computed value on reads — and every row carries a primary
key derived deterministically from the edge itself, which is exactly what makes
the same-edge merges above converge. Code that writes dependency rows with raw
SQL instead of bd dep add will break; if that’s not you, there’s nothing to
do.
And More!
bd init --init-if-missingmakes init idempotent — for scripts and agents that shouldn’t have to care whether they’re first (#3490).bd metricsis a new command to see and control anonymous usage metrics (which commands run,bdversion, OS — never your issue content). On by default with a one-time heads-up;bd metrics offturns it off for good.bd remember <existing-key>now recalls the memory instead of silently overwriting it — a real footgun before.- Piped
bd listoutput no longer gets truncated, which matters if you script against it. - Read-only now means read-only, all the way down to the embedded store — so a dashboard or an observer agent can watch a database with no chance of writing to it.
For the full technical details, check out the full release notes.
Where are we?
Beads’ whole reason for existing is that an agent can get stuck, or you can close your laptop, and the work picks up exactly where it left off — because the state lives in a database, not in a context window. This release makes the Dolt database more robust in the case that you more than one clone, sharing one remote, over time.
Beads has crossed 25,000+ stars on GitHub. Thank you again to the Gas Town Hall community — go upgrade, run the recipe above if you sync more than one clone, and let me know how it goes. If you hit something the gate didn’t catch, file it — that’s how the next release gets built.
Have thoughts about this post? Join the Gas Town Hall Discord!