Files
beads/docs/ROUTING.md
Steve Yegge be306b6c66 fix(routing): auto-enable hydration and flush JSONL after routed create (#1251)
* fix(routing): auto-enable hydration and flush JSONL after routed create

Fixes split-brain bug where issues routed to different repos (via routing.mode=auto)
weren't visible in bd list because JSONL wasn't updated and hydration wasn't configured.

**Problem**: When routing.mode=auto routes issues to a separate repo (e.g., ~/.beads-planning),
those issues don't appear in 'bd list' because:
1. Target repo's JSONL isn't flushed after create
2. Multi-repo hydration (repos.additional) not configured automatically
3. No doctor warnings about the misconfiguration

**Changes**:

1. **Auto-flush JSONL after routed create** (cmd/bd/create.go)
   - After routing issue to target repo, immediately flush to JSONL
   - Tries target daemon's export RPC first (if daemon running)
   - Falls back to direct JSONL export if no daemon
   - Ensures hydration can read the new issue immediately

2. **Enable hydration in bd init --contributor** (cmd/bd/init_contributor.go)
   - Wizard now automatically adds planning repo to repos.additional
   - Users no longer need to manually run 'bd repo add'
   - Routed issues appear in bd list immediately after setup

3. **Add doctor check for hydrated repo daemons** (cmd/bd/doctor/daemon.go)
   - New CheckHydratedRepoDaemons() warns if daemons not running
   - Without daemons, JSONL becomes stale and hydration breaks
   - Suggests: cd <repo> && bd daemon start --local

4. **Add doctor check for routing+hydration mismatch** (cmd/bd/doctor/config_values.go)
   - Validates routing targets are in repos.additional
   - Catches split-brain configuration before users encounter it
   - Suggests: bd repo add <routing-target>

**Testing**: Builds successfully. Unit/integration tests pending.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* test(routing): add comprehensive tests for routing fixes

Add unit tests for all 4 routing/hydration fixes:

1. **create_routing_flush_test.go** - Test JSONL flush after routing
   - TestFlushRoutedRepo_DirectExport: Verify direct JSONL export
   - TestPerformAtomicExport: Test atomic file operations
   - TestFlushRoutedRepo_PathExpansion: Test path handling
   - TestRoutingWithHydrationIntegration: E2E routing+hydration test

2. **daemon_test.go** - Test hydrated repo daemon check
   - TestCheckHydratedRepoDaemons: Test with/without daemons running
   - Covers no repos, daemons running, daemons missing scenarios

3. **config_values_test.go** - Test routing+hydration validation
   - Test routing without hydration (should warn)
   - Test routing with correct hydration (should pass)
   - Test routing target not in hydration list (should warn)
   - Test maintainer="." edge case (should pass)

All tests follow existing patterns and use t.TempDir() for isolation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(tests): fix test failures and refine routing validation logic

Fixes test failures and improves validation accuracy:

1. **Fix routing+hydration validation** (config_values.go)
   - Exclude "." from hasRoutingTargets check (current repo doesn't need hydration)
   - Prevents false warnings when maintainer="." or contributor="."

2. **Fix test ID generation** (create_routing_flush_test.go)
   - Use auto-generated IDs instead of hard-coded "beads-test1"
   - Respects test store prefix configuration (test-)
   - Fixed json.NewDecoder usage (file handle, not os.Open result)

3. **Fix config validation tests** (config_values_test.go)
   - Create actual directories for routing paths to pass path validation
   - Tests now verify both routing+hydration AND path existence checks

4. **Fix daemon test expectations** (daemon_test.go)
   - When database unavailable, check returns "No additional repos" not error
   - This is correct behavior (graceful degradation)

All tests now pass:
- TestFlushRoutedRepo* (3 tests)
- TestPerformAtomicExport
- TestCheckHydratedRepoDaemons (3 subtests)
- TestCheckConfigValues routing tests (5 subtests)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* docs: clarify when git config beads.role maintainer is needed

Clarify that maintainer role config is only needed in edge case:
- Using GitHub HTTPS URL without credentials
- But you have write access (are a maintainer)

In most cases, beads auto-detects correctly via:
- SSH URLs (git@github.com:owner/repo.git)
- HTTPS with credentials

This prevents confusion - users with SSH or credential-based HTTPS
don't need to manually configure their role.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(lint): address linter warnings in routing flush code

- Add missing sqlite import in daemon.go
- Fix unchecked client.Close() error return
- Fix unchecked tempFile.Close() error returns
- Mark unused parameters with _ prefix
- Add nolint:gosec for safe tempPath construction

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Roland Tritsch <roland@ailtir.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-21 21:22:04 -08:00

6.4 KiB

Multi-Repo Auto-Routing

This document describes the auto-routing feature that intelligently directs new issues to the appropriate repository based on user role.

Overview

Auto-routing solves the OSS contributor problem: contributors want to plan work locally without polluting upstream PRs with planning issues. The routing layer automatically detects whether you're a maintainer or contributor and routes bd create to the appropriate repository.

User Role Detection

Strategy

The routing system detects user role via:

  1. Explicit git config (highest priority):

    git config beads.role maintainer
    # or
    git config beads.role contributor
    
  2. Push URL inspection (automatic):

    • SSH URLs (git@github.com:user/repo.git) → Maintainer
    • HTTPS with credentials → Maintainer
    • HTTPS without credentials → Contributor
    • No remote → Contributor (fallback)

Examples

# Maintainer (SSH access)
git remote add origin git@github.com:owner/repo.git
bd create "Fix bug" -p 1
# → Creates in current repo (.)

# Contributor (HTTPS fork)
git remote add origin https://github.com/fork/repo.git
git remote add upstream https://github.com/owner/repo.git
bd create "Fix bug" -p 1
# → Creates in planning repo (~/.beads-planning by default)

Configuration

Routing is configured via the database config:

# Auto-routing is disabled by default (routing.mode="")
# Enable with:
bd init --contributor
# OR manually:
bd config set routing.mode auto

# Set default planning repo
bd config set routing.default "~/.beads-planning"

# Set repo for maintainers (in auto mode)
bd config set routing.maintainer "."

# Set repo for contributors (in auto mode)
bd config set routing.contributor "~/.beads-planning"

CLI Usage

Auto-Routing

# Let bd decide based on role
bd create "Fix authentication bug" -p 1

# Maintainer: creates in current repo (.)
# Contributor: creates in ~/.beads-planning

Explicit Override

# Force creation in specific repo (overrides auto-routing)
bd create "Fix bug" -p 1 --repo /path/to/repo
bd create "Add feature" -p 1 --repo ~/my-planning

Discovered Issue Inheritance

Issues created with discovered-from dependencies automatically inherit the parent's source_repo:

# Parent in current repo
bd create "Implement auth" -p 1
# → Created as bd-abc (source_repo = ".")

# Discovered issue inherits parent's repo
bd create "Found bug in auth" -p 1 --deps discovered-from:bd-abc
# → Created with source_repo = "." (same as parent)

This ensures discovered work stays in the same repository as the parent task.

Multi-Repo Hydration

⚠️ Critical: When using routing to separate repos, you must enable multi-repo hydration or routed issues won't appear in bd list.

The Problem

Auto-routing writes issues to a separate repository (e.g., ~/.beads-planning), but by default, bd list only shows issues from the current repository's database. Without hydration, routed issues are "invisible" even though they exist.

The Solution

Add routing targets to repos.additional in config.yaml:

routing:
  mode: auto
  contributor: ~/.beads-planning
repos:
  primary: "."
  additional:
    - ~/.beads-planning

Automatic Setup

bd init --contributor now automatically configures both routing AND hydration:

cd ~/my-forked-repo
bd init --contributor

# This sets up:
# 1. routing.mode=auto
# 2. routing.contributor=~/.beads-planning
# 3. repos.additional=[~/.beads-planning]

Manual Setup (Advanced)

If you configured routing before this feature, add hydration manually:

# Add planning repo to hydration list
bd repo add ~/.beads-planning

# Verify configuration
bd repo list

How It Works

Multi-repo hydration imports issues from all configured repos into the current database:

  1. JSONL as source of truth: Each repo maintains its own issues.jsonl
  2. Periodic import: Daemon imports from repos.additional every sync cycle
  3. Source tracking: Each issue tagged with source_repo field
  4. Unified view: bd list shows issues from all repos

Requirements

For optimal hydration, run daemons in all repos:

# In main repo
bd daemon start

# In planning repo
cd ~/.beads-planning
bd daemon start --local

Without daemons, JSONL files become stale and hydration only sees old data.

Troubleshooting

Run bd doctor to check for configuration issues:

bd doctor

# Checks:
# - routing.mode=auto with routing targets but repos.additional not configured
# - Routing targets not in repos.additional list
# - Daemons not running in hydrated repos

Common Issues:

  1. Routed issues not appearing in bd list

    • Cause: repos.additional not configured
    • Fix: bd repo add <routing-target>
  2. Issues appear but data is stale

    • Cause: Daemon not running in target repo
    • Fix: cd <target-repo> && bd daemon start --local
  3. After upgrading, routed issues missing

    • Cause: Upgraded before hydration was automatic
    • Fix: bd repo add <routing-target> to enable hydration manually

Backward Compatibility

  • Single-repo workflows unchanged: If no multi-repo config exists, all issues go to current repo
  • Explicit --repo always wins: --repo flag overrides any auto-routing
  • No schema changes: Routing is pure config-based, no database migrations

Implementation

Key Files:

  • internal/routing/routing.go - Role detection and routing logic
  • internal/routing/routing_test.go - Unit tests
  • cmd/bd/create.go - Integration with create command
  • routing_integration_test.go - End-to-end tests

API:

// Detect user role based on git configuration
func DetectUserRole(repoPath string) (UserRole, error)

// Determine target repo based on config and role
func DetermineTargetRepo(config *RoutingConfig, userRole UserRole, repoPath string) string

Testing

# Run routing tests
go test -v -run TestRouting

# Tests cover:
# - Maintainer detection (git config)
# - Contributor detection (fork remotes)
# - SSH vs HTTPS remote detection
# - Explicit --repo override
# - End-to-end multi-repo workflow

Future Enhancements

See bd-k58 for proposal workflow:

  • bd propose <id> - Move issue from planning to upstream
  • bd withdraw <id> - Un-propose
  • bd accept <id> - Maintainer accepts proposal