The mental model in one breath
You've run pipelines like this with humans in every box. The difference to look for: every hand-off is a record, and nothing passes a gate on vibes.
Every repo is a hive — its own beads database, served by bd's shared local Dolt server and kept out of your tracked tree under .beads/ until you furnish the hive. Issues carry a short, stable prefix(acme-api-1) derived from the stable part of identity (org + repo); the parts that can change (provider, org) live in labels. Issue history publishes torefs/dolt/data on the repo's own git remote — a separate ref namespace that never touches branches or PRs, so there is nothing hosted to run. A workspace-level aggregate (the hub) pulls every hive into one read view, so you can ask "what's ready to work on anywhere?"; HQ is the separate, authoritative fleet store.
bh is deliberately a thin orchestrator: bd (beads) owns issues,git owns history, git-workspace owns cloning. What bhadds is the process — bh work drives a bead through its lifecycle so agents never improvise raw git / gh pr for the lifecycle itself.
The tenets
⬢Planes, kept separate
Control governs the factory. Planning turns ideas into molecules. Development — the CLI still calls it Integration — executes them. Release proves a version and publishes it. Each plane has its own verbs and seats; they hand off sequentially and never step into each other's role. Assurance is the one exception — a cross-cutting gate layer, not a sequential plane.
⬢Merging is not releasing
Development is high-frequency and dirty: each bead gets its own worktree and lands on an always-green line. Release is a separate, deliberate, gated act — preflight, bump, attest, await, push — described below.
⬢Lossless history
Agents do the merging, so history is audited: merge --no-ff at the boundary, never squash there. Workers squash only their own local checkpoints into clean conventional digests before submit (tiered retention).
⬢Unit of work = a bead
Worktree → implement → refine → check → submit → review → merge. One bead, one branch (wt/bead/issue/<id>), one reviewable change.
Six phases, one ring — and one edge nobody closes
A car factory isn't successful because the car left the line — it's successful if the car survives the road. Software is no different: what happens in production is the only signal that says whether the last decision was any good, so it has to come back round to the next one.
Ideation → Planning → Development → Release
Control governs the sequence; Assurance gates across it. Each plane is a state machine with named re-entry doors — the full map shows every state. This is the skeleton.
Inside two planes: planning → dispatch → develop → review → merge
The full state machine, with its three feedback loops: the spike loop (settle feasibility before implementation beads exist), the bounce loop(review → changes-requested → resume), and the replan door (a mid-execution blocker re-enters planning). Hexagons are gates — nothing passes one without the right seat resolving it.
wt/bead/epic/<id>) so bead B sees bead A's merged work; the container lands on main only when the whole molecule is done — one recursive rule that scales to N tiers (issue → epic → workstream → main).The branch tree a molecule leaves behind
The pipeline above is the state machine; this is what it does to git. One workstream, one epic under it, three ways a bead can reach the container, and the twofinish lands that carry the result up a tier at a time.
wt/bead/epic/<id> and every leaf at wt/bead/issue/<id>; a bead forks from — and bh work merge lands on — the nearest started container above it, falling back to main. A batch is the one exception to one-bead-per-worktree: cohesive beads share wt/batch/<group> and complete as a unit. An intermediate, unpushed container can still roll back losslessly if a re-validation fails; only the final land onto main is fixed forward. A second epic under the same workstream (bh-ws.3) runs the identical loop on its own container, dispatched to a nested dispatcher.What keeps the line green
- Serialized merges. Merge is a separate seat (the merger) gated by
bd merge-slot— parallel developers, one merge at a time. - The conflict ladder.
clean → rebased → union → bounce: try a clean--no-ff, then rebase-and-retry, then path-whitelisted union merge (append-only files only, re-validated), else bounce with the branch restored. No work is ever lost. - Validation modes. Every bead validates in isolation at submit;
relaxed/conservative/loosetune how often thecombination is re-tested. If a re-validation fails on a safe-to-rewrite branch, the tip is rolled back before anyone can observe it; a shared branch escalates for a forward fix instead. - Evidence is keyed to the tree. A green result is stored against the exact tree hash and command that produced it. A fresh exact match may be reused where the caller allows it; a graph-derived attest key may carry across a change only when its whole input closure is unchanged. Unknown impact fails closed to the full run, and a reused key is never a claim that the whole tree is green — how reuse works.
- The batch exception. Cohesive linear chains can run as one shared
wt/batch/<group>worktree, merged once — guarded by cohesion, a size cap of 5, no conflicting model preferences, one review gate. A batch fails and bounces as a unit; per-bead commits stay bisectable inside the bubble.
Gate phases × strictness
Seven places a tree can be asked to prove itself between a developer's worktree and the remote. work.validation picks which of them are owed; the tree-keyed ledger decides whether an owed gate must actually run.
main moved underneathskippedrelaxed pays at the two main-boundary gates (molecule, merge-main) plus a staleness backstop when main moved under the land; conservative also re-tests the container after each bead merge; loose trusts the submits. push-main is the one checkpoint a git hook resolves: it asks for a fresh verdict on the exact tree being pushed and runs the full gate itself on any miss, stale entry, red record, or missing bh — no path treats an absent attestation as a pass. Per-boundary commands are overridable (work.validate.<phase>), so the fast tier runs everywhere and the full tier only where main is touched.bh plan file like any other molecule, so nothing skips the kickoff gate. Its states and seat are not defined yet.Merge order is planned, not first-come-first-served
Landing approved beads in whatever order they clear review is fine until a molecule mixeskinds of change — a hotfix for the released version, additive features that should ship as one clean minor bump, and a breaking change that must not leak into a patch. Review order produces a history nobody can version cleanly. So a bead can carry its semantic-version impact, and the merge sequence is computed from it:
release:—breaking,featureorfix. A fixed vocabulary, identical in every hive, so the ordering means the same thing everywhere.wave:— a free-form label grouping additive features that should land together as one bump. Distinct from the worktree batching label.
The default strategy, stable-versioning, orders in four tiers: fixes for the released version flush first, up to a churn budget; then additive features, grouped by wave; then any fixes past that budget; then breaking changes last, so they never leak into a patch or minor window early. Both knobs are per-hive configuration (release.strategy, release.fix_churn_budget).
bh release order # the strategy-preferred merge sequence, read-onlyIt is advisory. It decides sequence and nothing else — it never claims, merges or mutates anything, and the scheduler consults the same scorer so dispatch and merge never disagree about the order. The one hard counterpart is opt-in per hive: withrelease.enforce_hold on, a release:breaking bead gets arelease-hold: gate that blocks its merge until a releaser resolves it — and only a releaser can.
Cutting the version: the guarded bump shipped
Ordering decides what lands; the bump decides what ships. The rule that makes it safe: the release gate runs against the exact tree the bump creates, not the tree that was green before it.
- preflightrequires green proof for the current tree before anything moves
- bump
cz bumpwrites version metadata and a local tag — a new, unproven tree - attestruns the
push-maingate on that exact tree - awaitblocks until the verdict; red refuses, green permits
- pushone atomic push moves
mainand the tag together
bh release preflight # green proof for the current tree, or refuse
cz bump # new tree + local tag — unproven
bh release attest # the push-main gate against that exact tree
bh release await # block until GREEN, then push main + tag atomically
bh release recover # after a failed push: measure the remote before decidingThe tag reaching the remote is the point of no return. Before it, recovery can undo locally; after it, roll forward — published history is never rewritten.
main and the tag together, and the tag reaching the remote is the boundary between undo and roll-forward. The pre-push hook is the same idea applied where a slow gate hurts most: a --no-ff land onto an unmoved main yields a tree byte-identical to the one the land already tested, so the push has nothing left to prove — and every other outcome degrades to the full gate, never to an ungated push. What landsbefore the bump is ordered by the advisory bh release order and, opt-in, arelease-hold: gate that only a releaser can clear on a breaking change.