Project Init: Beads/Dolt Database Setup and Recovery
<!-- Based on gastownhall/beads docs/SYNC_SETUP.md (MIT). Attribution: README "Built on". -->
Announce at start: "I'm using the project-init skill to set up or recover the beads database."
Iron Law: NEVER Run bd init --force
NEVER run bd init --force (deprecated in v1.0.4). Use the named-intent alternatives: bd init --reinit-local (preserves remote) or bd init --discard-remote (explicit destruction).
Why: Issue #2363 documents an AI agent that destroyed 247 issues via bd init --force cascade. The root cause was misdiagnosing "server can't connect" as "database missing". bd init --force is a nuclear option that should ONLY be run by a human who explicitly types it.
This Iron Law is the Production-Grade Doctrine applied to your data ledger: never take the shortcut that accepts catastrophic, irreversible risk.
| Action | Safe? | Use When | |--------|-------|----------| | bd init | ✅ Safe | Fresh project, no existing .beads/ | | bd bootstrap | ✅ Safe | Cloned repo with remote beads data | | bd doctor --fix --yes | ✅ Safe | Database exists but seems broken | | bd init --force | ❌ NEVER | Deprecated (v1.0.4) — do NOT use | | bd init --reinit-local | ⚠️ Recovery only | Reinitialize local state, preserve remote data | | bd init --discard-remote | ⚠️ Recovery only | Discard remote data and reinitialize (explicit destruction) |
Diagnostic Phase (Always Run First)
Before taking ANY action, run diagnostics to understand the current state:
bash scripts/diagnose.sh
One Bash call gathers the full read-only battery as labeled RAW DATA (no verdicts, no fixes): bd/dolt versions, .beads/ presence, config.yaml/metadata.json, whether bd list/bd vc status work, and any dolt refs on the git remote. Read the == section == output, then author the diagnosis yourself against the Decision Matrix below:
Diagnosis: <one-line read of what the sections above show> Path: <A/B/C/D/E/F, from the Decision Matrix>
Done when: both lines above are written and a single path letter is chosen.
bd doctor is intentionally NOT part of the battery — --fix --yes can mutate. Run it only after the diagnosis→path block above is emitted and a path is chosen (bd v1.1.0+ bd doctor also flags migration-content skew vs remote; surface that before any sync work).
Decision Matrix
Based on diagnostic results, follow the appropriate path. "Remote" below always means the configured beads remote (bd dolt remote list) — independent of the code repo's git origin; see "Multi-Repo / Private Beads Remote" below.
| State | Action | Path | |-------|--------|------| | No .beads/, no remote data | Fresh init | → Path A | | No .beads/, remote has dolt refs | Bootstrap from remote | → Path B | | .beads/ exists, bd list works, beads remote matches | Already good ✅ | Done | | .beads/ exists, bd list fails | Run bd doctor --fix --yes | → Path D | | .beads/ exists, bd list works, no beads remote configured | Add remote | → Path E | | .beads/ exists, push fails "no common ancestor" | Fix diverged history | → Path C | | .beads/ exists but empty/corrupt, remote has data | Export + re-bootstrap | → Path F |
Path A: Fresh Initialization (New Project)
# 1. Initialize beads
bd init
# 2. Verify
bd list # Should work (empty is fine)
bd create "Test bead" -t task -p 4
bd list # Should show the test bead
bd close <test-id> --reason "Init verification"
# 3. Add remote (if syncing) — RECOMMENDED: a dedicated beads remote (private for public projects),
# separate from the code repo (ADR-0057; bd releases after v1.1.0 refuse a code-repo URL without --allow-git-origin)
bd dolt remote add origin git+ssh://git@github.com/<owner>/<repo>-beads.git
# 4. First push
bd dolt push
Done when: bd list shows the test bead created and closed, and (if a remote was added) bd dolt push succeeds.
Path B: Bootstrap from Remote (Cloned Repo)
# 1. Bootstrap (auto-detects remote dolt data)
bd bootstrap
# 2. Verify
bd list # Should show existing issues
bd vc status # Should show branch + commit hash
# After any pull: repair denormalized blocked flags (bd v1.1.0+)
bd recompute-blocked
If bd bootstrap fails: open references/recovery.md (open when bootstrap auto-detect fails) for the manual 8-step fallback.
Path C: Fix Diverged History
Open references/recovery.md (open when push is rejected) for the v1.1.0 remote-migrate gate, the diverged-history fix, and the GitHub push-protection recovery.
Path D: Database Exists but Broken
# 1. Run doctor (non-destructive diagnostics + auto-fix)
bd doctor --fix --yes
# 2. If doctor fixes it:
bd list # Verify
# 3. If still broken, restart the Dolt server
bd dolt stop
bd dolt start
bd list # Retry
# 4. If still broken, check circuit breaker
rm -f /tmp/beads-dolt-circuit-*.json
bd dolt stop
bd dolt start
bd list # Retry
Path E: Add Remote to Existing Database
# 1. Add the remote — RECOMMENDED: a dedicated beads remote (private for public projects),
# separate from the code repo (ADR-0057; bd releases after v1.1.0 refuse a code-repo URL without --allow-git-origin)
bd dolt remote add origin git+ssh://git@github.com/<owner>/<repo>-beads.git
# 2. Push to establish remote
bd dolt push
# 3. Verify
git ls-remote git+ssh://git@github.com/<owner>/<repo>-beads.git | grep dolt # Should show refs/dolt/data
Path F: Corrupt Local, Remote Has Data
# 1. Export what we can (may fail if truly corrupt)
bd export -o /tmp/beads-backup.jsonl 2>/dev/null
# 2. Remove and re-bootstrap
bd dolt stop 2>/dev/null
rm -rf .beads/
bd bootstrap
# 3. Verify
bd list
bd vc status
# 4. Re-import exported data if needed
bd import /tmp/beads-backup.jsonl 2>/dev/null
Multi-Repo / Private Beads Remote
The Dolt remote is independent of the code repo's git origin — point it anywhere. Choose a dedicated beads remote (a separate, private git repo) when: the code repo is public and beads will hold anything non-public (strategy, unreleased plans, candid notes) — Dolt history retains deleted rows, so "public remote" means the full history is public. Same-repo is an explicit opt-in for private/throwaway projects (bd releases after 1.1.0 refuse a bd dolt remote add URL matching the git origin without --allow-git-origin).
Setup (existing local database):
A brand-new private repo must have an initial branch/commit before the first bd dolt push — an empty repo has no branches, and Dolt's git-remotes backend fails with "git remote has no branches" against it. Create it with an initial commit first:
gh repo create <owner>/<project>-beads --private --add-readme
Then add the remote and push:
bd dolt remote add origin git+ssh://git@github.com/<owner>/<project>-beads.git
bd dolt push
New-machine bootstrap (VALIDATED):
bd init --non-interactive --prefix <prefix> --remote "git+ssh://git@github.com/<owner>/<project>-beads.git"
This clones the database from the dedicated private remote in one step and persists sync.remote — no separate bd bootstrap needed (live rehearsal: hydrated 1,854 records with the private remote correctly wired).
⚠️ Zero-remote trap (v1.1.0): with NO Dolt remote configured, bd dolt push silently adopts the git origin. Never leave zero-remote as a resting state — when swapping remotes, always chain the change in one command: bd dolt remote remove origin && bd dolt remote add origin <url>.
⚠️ Verify after swapping remotes: bd dolt remote remove can leave the old value commented out in .beads/config.yaml, and bd dolt remote add doesn't always rewrite sync.remote to match. After swapping, confirm:
grep "sync.remote" .beads/config.yaml
I