Skip to content

fix(bootstrap): write metadata.json and config.yaml after sync clone#3203

Merged
maphew merged 1 commit into
mainfrom
fix/bootstrap-sync-writes-config
Apr 12, 2026
Merged

fix(bootstrap): write metadata.json and config.yaml after sync clone#3203
maphew merged 1 commit into
mainfrom
fix/bootstrap-sync-writes-config

Conversation

@julianknutsen

Copy link
Copy Markdown
Collaborator

Summary

executeSyncAction created .beads/ and cloned remote Dolt data but never wrote metadata.json or config.yaml, leaving the workspace in a broken state where bd status, bd where, and bd dolt push all failed with no beads configuration found and Error 1105: no database selected. This broke the documented session-close protocol (git pull --rebasebd dolt pushgit push) on every fresh clone and worktree.

Every other bootstrap action path (init, restore, jsonl-import) writes these files through newDoltStore + createConfigYaml. The sync path was a "just clone the data" shortcut that never received the post-clone setup steps.

This PR adds a finalizeSyncedBootstrap helper that runs after cloneFromRemote and mirrors what init does: save cfg as metadata.json, create the config.yaml template, and persist sync.remote. The helper is idempotent so bootstrap retries remain safe.

Fixes gastownhall/gascity#561

Root Cause

cmd/bd/bootstrap.go:executeSyncAction previously:

func executeSyncAction(ctx context.Context, plan BootstrapPlan, cfg *configfile.Config) error {
    if err := os.MkdirAll(plan.BeadsDir, 0o750); err != nil {
        return fmt.Errorf("create beads directory: %w", err)
    }
    dbName := cfg.GetDoltDatabase()
    return cloneFromRemote(ctx, plan.BeadsDir, plan.SyncRemote, dbName)
}

It returned immediately after cloneFromRemote, leaving no metadata.json or config.yaml. On the next command, configfile.Load() returned nil, cfg.GetDoltDatabase() fell back to the default, and opening the embedded database failed.

Changes

  • cmd/bd/bootstrap.go: new finalizeSyncedBootstrap helper called at the end of executeSyncAction. Writes metadata.json (with dolt_database, dolt_mode, backend, database), creates the config.yaml template via createConfigYaml, and persists sync.remote via config.SetYamlConfig.
  • cmd/bd/bootstrap_test.go: two new regression tests:
    • TestFinalizeSyncedBootstrapWritesConfigFiles — verifies metadata.json records the cloned dolt_database name, config.yaml exists, and sync.remote persists.
    • TestFinalizeSyncedBootstrapIsIdempotent — verifies re-running the finalize step over an already-finalized workspace is a no-op.

The helper is idempotent by design: createConfigYaml skips existing files, and metadata.json is a safe rewrite that preserves upstream cfg fields.

Test plan

  • go test -tags gms_pure_go -run TestFinalizeSyncedBootstrap ./cmd/bd — new tests pass
  • go test -tags gms_pure_go -run 'TestBootstrap|TestDetectBootstrapAction|TestFinalizeSyncedBootstrap' ./cmd/bd — all bootstrap tests pass
  • go test -tags gms_pure_go -short ./cmd/bd — full cmd/bd short suite passes
  • go vet -tags gms_pure_go ./... clean
  • golangci-lint run --timeout=5m --build-tags=gms_pure_go ./... — 0 issues
  • End-to-end manual repro: built bd-main and bd-fixed, created a source project + bare git remote, dolt push-ed, and bootstrapped into a fresh clone with each binary:
    • bd-main: reproduced the bug — .beads/ only contained embeddeddolt/; bd status / bd dolt push failed with "no beads configuration found" / "Error 1105".
    • bd-fixed: .beads/ contained metadata.json + config.yaml + embeddeddolt/; bd status, bd list, and bd dolt push all succeeded.

Related

executeSyncAction created .beads/ and cloned remote Dolt data but never
wrote the two files bd needs to open the database afterwards:

  - metadata.json (dolt_database, dolt_mode, backend)
  - config.yaml (sync.remote and other yaml-backed settings)

Every other bootstrap action path (init, restore, jsonl-import) writes
these via newDoltStore + createConfigYaml. The sync path was a "just
clone the data" shortcut that never received the post-clone setup
steps. The result: bd bootstrap --yes in a fresh clone appeared to
succeed, but bd status / bd where / bd dolt push all failed with
"no beads configuration found" and "Error 1105: no database selected",
leaving the user stuck — bd bootstrap --dry-run then reported
"Database already exists" and refused to retry.

This directly blocks the documented session-close protocol in AGENTS.md:
  git pull --rebase → bd dolt push → git push

Add a new finalizeSyncedBootstrap helper that runs after cloneFromRemote
and mirrors what init does: save cfg as metadata.json, create the
config.yaml template, and persist sync.remote. The helper is idempotent
(createConfigYaml skips existing files; metadata.json is a safe rewrite)
so bootstrap retries remain safe.

Regression coverage:
  - TestFinalizeSyncedBootstrapWritesConfigFiles — verifies metadata.json
    records the cloned dolt_database name, config.yaml exists, and
    sync.remote persists.
  - TestFinalizeSyncedBootstrapIsIdempotent — verifies re-running the
    finalize step over an already-finalized workspace is a no-op,
    matching the clone-retry expectation.

Verified end-to-end by building bd-main and bd-fixed binaries, creating
a source project + bare git remote, dolt-pushing issues to it, then
bootstrapping into a fresh clone with each binary. bd-main left the
workspace in the reported broken state; bd-fixed produced a workspace
where bd status, bd list, and bd dolt push all succeed.

Fixes gastownhall/gascity#561

@maphew maphew left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Verdict: Clean, well-scoped fix for a real bootstrap gap. Ready to merge.

Strengths

  • Correctly mirrors what bd init does: same cfg.SavecreateConfigYamlSetYamlConfig("sync.remote", ...) sequence
  • isEmbeddedMode() usage is correct — reads runtime state rather than stale config
  • createConfigYaml naturally short-circuits on existing files, making the idempotency claim valid
  • Tests are thorough: proper env isolation via t.Setenv, regression + idempotency coverage, BEADS_DIR override to avoid CWD leakage

Minor nit (I'll fix post-merge)

  • cfg == nil guard in finalizeSyncedBootstrap is unreachable — the caller chain guarantees non-nil via DefaultConfig() fallback at bootstrap.go:178-180. Harmless but unnecessary; I'll clean it up.

Thanks @julianknutsen — great fix and thorough testing! 🎯

@maphew maphew merged commit 4aa573d into main Apr 12, 2026
37 checks passed
maphew added a commit to maphew/beads that referenced this pull request Apr 12, 2026
…ootstrap

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to gastownhall#3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e
Co-authored-by: Amp <amp@ampcode.com>
maphew added a commit that referenced this pull request Apr 12, 2026
…ootstrap (#3222)

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to #3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e

Co-authored-by: Amp <amp@ampcode.com>
maphew added a commit that referenced this pull request Apr 13, 2026
* chore(bootstrap): remove unreachable nil-cfg guard in finalizeSyncedBootstrap

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to #3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e
Co-authored-by: Amp <amp@ampcode.com>

* fix(ci): remove Microsoft apt repos before apt-get update

GitHub's ubuntu-latest runners include Microsoft package repos
(azure-cli, microsoft-prod) that intermittently return 403 Forbidden,
causing apt-get update to fail and breaking the Install ICU4C step.

Remove these unused repo files before apt-get update in all workflows
(ci.yml, cross-version-smoke.yml, nightly.yml).

Amp-Thread-ID: https://ampcode.com/threads/T-019d83a9-4273-711e-be59-1ed089de1c65
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
@maphew maphew deleted the fix/bootstrap-sync-writes-config branch April 13, 2026 03:34
osamu2001 pushed a commit to osamu2001/beads that referenced this pull request Apr 13, 2026
…l#3224)

* chore(bootstrap): remove unreachable nil-cfg guard in finalizeSyncedBootstrap

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to gastownhall#3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e
Co-authored-by: Amp <amp@ampcode.com>

* fix(ci): remove Microsoft apt repos before apt-get update

GitHub's ubuntu-latest runners include Microsoft package repos
(azure-cli, microsoft-prod) that intermittently return 403 Forbidden,
causing apt-get update to fail and breaking the Install ICU4C step.

Remove these unused repo files before apt-get update in all workflows
(ci.yml, cross-version-smoke.yml, nightly.yml).

Amp-Thread-ID: https://ampcode.com/threads/T-019d83a9-4273-711e-be59-1ed089de1c65
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
mzlee pushed a commit to mzlee/beads that referenced this pull request Apr 22, 2026
…astownhall#3203)

fix(bootstrap): write metadata.json and config.yaml after sync clone

executeSyncAction created .beads/ and cloned remote Dolt data but never
wrote metadata.json or config.yaml, leaving the workspace in a broken
state where bd status, bd where, and bd dolt push all failed.

Adds finalizeSyncedBootstrap helper that runs after cloneFromRemote and
mirrors what init does: save cfg as metadata.json, create the config.yaml
template, and persist sync.remote. The helper is idempotent so bootstrap
retries remain safe.

Fixes gastownhall/gascity#561

Co-authored-by: Julian Knutsen <julianknutsen@users.noreply.github.com>
mzlee pushed a commit to mzlee/beads that referenced this pull request Apr 22, 2026
…ootstrap (gastownhall#3222)

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to gastownhall#3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e

Co-authored-by: Amp <amp@ampcode.com>
mzlee pushed a commit to mzlee/beads that referenced this pull request Apr 22, 2026
…l#3224)

* chore(bootstrap): remove unreachable nil-cfg guard in finalizeSyncedBootstrap

The caller chain (bootstrapCmd.Run → executeBootstrapPlan → executeSyncAction)
guarantees cfg is non-nil via DefaultConfig() fallback at bootstrap.go:178-180.

Follow-up to gastownhall#3203.

Amp-Thread-ID: https://ampcode.com/threads/T-019d7fee-1326-74af-a566-9c45ad5b349e
Co-authored-by: Amp <amp@ampcode.com>

* fix(ci): remove Microsoft apt repos before apt-get update

GitHub's ubuntu-latest runners include Microsoft package repos
(azure-cli, microsoft-prod) that intermittently return 403 Forbidden,
causing apt-get update to fail and breaking the Install ICU4C step.

Remove these unused repo files before apt-get update in all workflows
(ci.yml, cross-version-smoke.yml, nightly.yml).

Amp-Thread-ID: https://ampcode.com/threads/T-019d83a9-4273-711e-be59-1ed089de1c65
Co-authored-by: Amp <amp@ampcode.com>

---------

Co-authored-by: Amp <amp@ampcode.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bd dolt push fails in fresh worktrees; bootstrap sync leaves unusable .beads

2 participants