Skip to content

bd bootstrap fails in a fresh second clone even when origin already has refs/dolt/data (v0.62.0) #2792

@yogurtearl

Description

@yogurtearl

Versions

  • bd version 0.62.0 (dev)
  • dolt version 1.84.0
  • git version 2.53.0

Summary

A simple two-clone local smoke test fails on v0.62.0.

Clone 1 can initialize Beads, create issues, and push Beads state to a local bare git origin. The origin then contains refs/dolt/data.

But in clone 2, bd bootstrap does not initialize Beads from that existing remote state. It prints:

No .beads directory found.
To create a new project, use: bd init
Bootstrap is for existing projects that need database setup.

After that, bd list fails with no beads database found.

This seems to break the expected zero-state -> clone1 push -> clone2 bootstrap flow.

CI-friendly smoke test repro

set -euo pipefail

need_cmd() {
  command -v "$1" >/dev/null 2>&1 || {
    echo "ERROR: missing command: $1" >&2
    exit 1
  }
}

fail() {
  echo "ERROR: $*" >&2
  exit 1
}

capture() {
  local out
  out="$("$@" 2>&1)" || {
    printf '%s\n' "$out" >&2
    fail "command failed: $*"
  }
  printf '%s\n' "$out"
}

json_issue_count() {
  local out
  out="$(capture bd list --json)"
  [[ "$out" != *'"error"'* ]] || fail "bd list returned error JSON: $out"
  printf '%s\n' "$out" | jq -e 'type == "array"' >/dev/null || fail "bd list --json did not return an array"
  printf '%s\n' "$out" | jq -r 'length'
}

json_has_title() {
  local title="$1"
  local out
  out="$(capture bd list --json)"
  [[ "$out" != *'"error"'* ]] || fail "bd list returned error JSON: $out"
  printf '%s\n' "$out" | jq -e --arg t "$title" 'map(.title) | index($t) != null' >/dev/null \
    || fail "missing expected issue title: $title"
}

need_cmd git
need_cmd bd
need_cmd dolt
need_cmd jq

WORKDIR="$(mktemp -d -t beads-v062-smoke-XXXXXX)"
trap 'rm -rf "$WORKDIR"' EXIT

ORIGIN_REPO="$WORKDIR/origin.git"
CLONE1="$WORKDIR/clone1"
CLONE2="$WORKDIR/clone2"

git init --bare --initial-branch=main "$ORIGIN_REPO"

git clone "$ORIGIN_REPO" "$CLONE1"
cd "$CLONE1"
git config user.name "Test User"
git config user.email "test@example.com"
printf '# test\n' > README.md
git add README.md
git commit -m "initial commit"
git push origin main

[[ ! -e .beads ]] || fail ".beads should not exist before init"

INIT_OUT="$(printf 'n\n' | bd init --quiet --skip-hooks --skip-agents 2>&1)" || {
  printf '%s\n' "$INIT_OUT" >&2
  fail "bd init failed"
}

[[ -d .beads ]] || fail ".beads was not created in clone1"
[[ -f .beads/config.yaml ]] || fail ".beads/config.yaml missing in clone1"

capture bd dolt remote add origin "file://$ORIGIN_REPO" >/dev/null

capture bd create "alpha from clone1" -t task -p 1 --json >/dev/null
capture bd create "beta from clone1" -t bug -p 2 --json >/dev/null

[[ "$(json_issue_count)" == "2" ]] || fail "clone1 should have 2 issues"

capture bd dolt push >/dev/null

REFS_OUT="$(capture git ls-remote "$ORIGIN_REPO" 'refs/dolt/*')"
[[ -n "$REFS_OUT" ]] || fail "origin missing refs/dolt/* after push"
[[ "$REFS_OUT" == *"refs/dolt/data"* ]] || fail "origin missing refs/dolt/data after push"

git clone "$ORIGIN_REPO" "$CLONE2"
cd "$CLONE2"
git config user.name "Test User"
git config user.email "test@example.com"

if git show-ref | grep -q 'refs/dolt/'; then
  fail "plain git clone unexpectedly has refs/dolt/* locally"
fi

BOOTSTRAP_OUT="$(capture bd bootstrap)"
printf '%s\n' "$BOOTSTRAP_OUT"

[[ -d .beads ]] || fail "bd bootstrap did not create .beads in clone2; origin had refs/dolt/data; bootstrap output: $BOOTSTRAP_OUT"
[[ -f .beads/config.yaml ]] || fail "bd bootstrap did not create .beads/config.yaml in clone2; bootstrap output: $BOOTSTRAP_OUT"
[[ -d .beads/dolt ]] || fail "bd bootstrap did not create .beads/dolt in clone2; origin had refs/dolt/data; bootstrap output: $BOOTSTRAP_OUT"

[[ "$(json_issue_count)" == "2" ]] || fail "clone2 should see 2 issues after bootstrap"
json_has_title "alpha from clone1"
json_has_title "beta from clone1"

capture bd create "gamma from clone2" -t task -p 1 --json >/dev/null
[[ "$(json_issue_count)" == "3" ]] || fail "clone2 should have 3 issues after create"

capture bd dolt push >/dev/null

cd "$CLONE1"
capture bd dolt pull >/dev/null

[[ "$(json_issue_count)" == "3" ]] || fail "clone1 should have 3 issues after pull"
json_has_title "gamma from clone2"

echo "PASS"

Expected

  • clone1 initializes Beads and pushes state
  • origin contains refs/dolt/data
  • clone2 runs bd bootstrap
  • clone2 gets .beads initialized from remote state
  • clone2 can list clone1 issues
  • clone2 can add a new issue, push it
  • clone1 can pull and see the new issue

Actual

Clone 1 succeeds end-to-end:

  • bd init creates .beads
  • issues can be created in clone1
  • bd dolt push completes
  • git ls-remote "$ORIGIN_REPO" 'refs/dolt/*' shows refs/dolt/data

So the shared origin clearly contains Beads/Dolt state.

But in clone 2, bd bootstrap behaves as if no bootstrap source exists and does not create any local Beads state:

No .beads directory found.
To create a new project, use: bd init
Bootstrap is for existing projects that need database setup.

After that:

  • .beads/ does not exist in clone2
  • .beads/config.yaml does not exist
  • .beads/dolt/ does not exist
  • bd list fails because no database was initialized

Observed follow-on error:

Error: no beads database found
Hint: run 'bd doctor' to diagnose, or 'bd init' to create a new database
      or set BEADS_DIR to point to your .beads directory

Why this looks wrong

This repro is specifically the “fresh second clone” case:

  • origin already has refs/dolt/data
  • plain git clone does not fetch refs/dolt/*
  • bd bootstrap should be the step that detects remote Beads state and materializes .beads locally

Instead, bd bootstrap appears to require local .beads to already exist, which defeats the purpose of bootstrapping a fresh clone.

Example failing output

Initialized empty Git repository in /private/var/folders/_v/.../origin.git/
Cloning into '/private/var/folders/_v/.../clone1'...
warning: You appear to have cloned an empty repository.
done.
[main (root-commit) a342724] initial commit
 1 file changed, 1 insertion(+)
 create mode 100644 README.md
Enumerating objects: 3, done.
Counting objects: 100% (3/3), done.
Writing objects: 100% (3/3), 436 bytes | 436.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
To /private/var/folders/_v/.../origin.git
 * [new branch]      main -> main
Cloning into '/private/var/folders/_v/.../clone2'...
done.
No .beads directory found.
To create a new project, use: bd init
Bootstrap is for existing projects that need database setup.
ERROR: bd bootstrap did not create .beads in clone2; origin had refs/dolt/data; bootstrap output: No .beads directory found.
To create a new project, use: bd init
Bootstrap is for existing projects that need database setup.

Notes

  • This repro uses only a local bare git repo; no network remote is required.
  • The script is idempotent because it uses a fresh temp directory every run.
  • The script exits non-zero on failure and only prints PASS on full success.
  • This should be suitable as a CI/regression smoke test.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions