Skip to content

fix: preserve source dolt_database across .beads/redirect#2605

Open
mrmaxsteel wants to merge 1 commit intosteveyegge:mainfrom
mrmaxsteel:fix/redirect-preserve-database
Open

fix: preserve source dolt_database across .beads/redirect#2605
mrmaxsteel wants to merge 1 commit intosteveyegge:mainfrom
mrmaxsteel:fix/redirect-preserve-database

Conversation

@mrmaxsteel
Copy link
Contributor

@mrmaxsteel mrmaxsteel commented Mar 14, 2026

Problem

When bd follows a .beads/redirect file, it loads metadata.json from the target directory, silently discarding the source directory's dolt_database field. If the source and target have different dolt_database values (e.g., source says "lola", target says "hq"), all subsequent queries hit the wrong database.

This is a contract violation in the redirect feature: the source's metadata.json explicitly declares its database identity, but following a redirect overwrites it with the target's identity.

Why this is a beads bug (not a consumer bug)

The redirect was originally designed for worktrees where source and target share the same dolt_database. But the redirect mechanism doesn't document or enforce that constraint. When a consumer legitimately sets up a redirect between directories with different databases (e.g., a multi-database Dolt server), the source's dolt_database is silently lost. This affects:

  1. CWD path: bd list from a directory with a redirect shows the wrong database's issues
  2. Routing path: bd show <prefixed-id> from a parent directory routes through routes.jsonl, follows the redirect, and queries the wrong database

Any multi-database redirect setup would hit this — the fix belongs in beads' redirect handling, not in consumers.

Why this bug wasn't hit until now

The bug has existed since FollowRedirect was written — it never preserved the source's dolt_database. However, consumers didn't trigger it until recently because the redirect was only used between directories with the same dolt_database (worktrees sharing a parent repo's database).

Gas Town began creating redirects between directories with different dolt_database values in Jan 2026 (steveyegge/gastown@9d7dcde1). Even then, the bug was accidentally avoided because Gas Town called bd from the rig directory, where bd loaded the correct local metadata before following the redirect.

The bug became visible on Mar 12 2026 when steveyegge/gastown@1a1f3f7b changed to calling bd from the parent directory instead (to fix a nested routing issue). This meant bd hit the routing path — prefix match → follow redirect → load target metadata → wrong database.

Downstream impact

This bug affects any consumer using redirects with a centralized multi-database Dolt server. The specific failure mode:

  1. Consumer creates directory A with .beads/metadata.json (dolt_database: "projA") and a redirect to shared directory B (dolt_database: "default")
  2. bd list from A shows B's issues instead of A's
  3. bd show projA-xxx from B (via prefix routing) follows the redirect back to B, queries "default" database → "not found"

In Gas Town (the primary consumer), this manifests as:

Fix

  • internal/beads/beads.go: Add ResolveRedirect() that reads the source's metadata.json (raw JSON, no Dolt connection) to capture dolt_database before following the redirect
  • cmd/bd/main.go: Set BEADS_DOLT_SERVER_DATABASE early in startup when a redirect changes the database, so all store opens use the correct database
  • internal/routing/routes.go: ResolveBeadsDirForID and ResolveBeadsDirForRig use ResolveRedirect and set the env var when a redirect changes the DB
  • cmd/bd/routed.go: Treat env var override as "routes differently" even when the target directory is the same (shared Dolt server serves multiple DBs from one .beads dir)

The env var BEADS_DOLT_SERVER_DATABASE retains highest priority — the fix only sets it when not already set.

Test plan

  • 6 unit tests for redirect database preservation (including env var priority)
  • make build succeeds
  • bd list from a redirected directory shows the correct database's issues
  • bd show <prefixed-id> from a parent directory (routing path) finds the correct issue
  • Pre-existing BEADS_DOLT_SERVER_DATABASE env var is not overridden
  • End-to-end: gt sling successfully dispatches work after fix

🤖 Generated with Claude Code

When bd follows a .beads/redirect file (e.g., from a rig to the Gas Town
root), it loads metadata.json from the TARGET directory. This overwrites
the source rig's dolt_database with the target's (e.g., "hq" instead of
"lola"), causing bd list/show to query the wrong database.

This affects both:
1. CWD path: running bd from a rig directory
2. Routing path: gt sling/bd show <routed-id> from the town root

Fix:
- Add ResolveRedirect() that reads source metadata.json (raw JSON, no
  Dolt connection) before following the redirect
- In main.go: set BEADS_DOLT_SERVER_DATABASE early so all store opens
  use the correct database
- In routing/routes.go: ResolveBeadsDirForID and ResolveBeadsDirForRig
  use ResolveRedirect and set the env var when a redirect changes the DB
- In routed.go: treat env var override as "routes differently" even when
  the target directory is the same (centralized Dolt serves multiple DBs
  from one .beads dir)

The env var BEADS_DOLT_SERVER_DATABASE retains highest priority — the
fix only sets it when not already set.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@mrmaxsteel mrmaxsteel force-pushed the fix/redirect-preserve-database branch from a0deccb to 869b5cc Compare March 14, 2026 22:33
Copy link
Contributor

@DreadPirateRobertz DreadPirateRobertz left a comment

Choose a reason for hiding this comment

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

Thorough analysis of the redirect path losing dolt_database. The fix approach — preserving source metadata across redirect follows — is correct.

One question: does this introduce any risk for the common worktree case where source and target should share the same dolt_database? If the source's metadata is stale (e.g., someone renamed the database in the target but forgot the source), preserving the stale source value could cause issues.

The detailed git archaeology tracing when this bug surfaced is very helpful for reviewers.

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.

2 participants