diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index 47f19fba..bfcaa877 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -608,7 +608,7 @@ {"id":"bd-rfj","content_hash":"c38dcf80ea98c965123622c1ff4c9faddefd226609c1f3ad59143182b1f00114","title":"Add unit tests for hasJSONLChanged() function","description":"The hasJSONLChanged() function has no unit tests. Should test:\n- Normal case: hash matches\n- Normal case: hash differs\n- Edge case: empty file\n- Edge case: missing metadata (first run)\n- Edge case: corrupted metadata\n- Edge case: file read errors\n- Edge case: invalid hash format in metadata\n\nAlso add performance benchmarks for large JSONL files.","status":"closed","priority":3,"issue_type":"task","created_at":"2025-11-20T21:31:10.389481-05:00","updated_at":"2025-11-20T21:40:02.664516-05:00","closed_at":"2025-11-20T21:40:02.664516-05:00","source_repo":".","dependencies":[{"issue_id":"bd-rfj","depends_on_id":"bd-khnb","type":"blocks","created_at":"2025-11-20T21:31:10.39058-05:00","created_by":"daemon"}]} {"id":"bd-ri6d","content_hash":"62b887c13232eeabf1d1b25a514b6044ff6ea7b510a06cbd5a736beabe722c43","title":"bd message: Fix inefficient client-side filtering for --unread-only","description":"","status":"closed","priority":2,"issue_type":"task","created_at":"2025-11-08T12:54:28.614867-08:00","updated_at":"2025-11-08T12:58:59.551512-08:00","closed_at":"2025-11-08T12:58:59.551512-08:00","source_repo":".","dependencies":[{"issue_id":"bd-ri6d","depends_on_id":"bd-6uix","type":"parent-child","created_at":"2025-11-08T12:55:55.012455-08:00","created_by":"daemon"}]} {"id":"bd-rpn","content_hash":"4b0c8a22edcf49b95f1caba51397f5a6289bc170a69084bc3c39267a8f43d888","title":"Implement `bd prime` command for AI context loading","description":"Create a `bd prime` command that outputs AI-optimized markdown containing essential Beads workflow context. This provides an alternative to the MCP server for token-conscious users and enables context recovery after compaction/clearing.","design":"## Implementation\n\nCreate `cmd/bd/prime.go` that outputs AI-optimized markdown with **adaptive content based on MCP detection**.\n\n## MCP-Aware Output Strategy\n\n`bd prime` detects if MCP server is active and adjusts output accordingly:\n\n**With MCP detected** (~500 tokens):\n- Workflow reminders only\n- \"Use bd MCP tools, not markdown TODOs\"\n- Session management tips\n- NO CLI command syntax (user has native MCP tools)\n\n**Without MCP** (~1-2k tokens):\n- Full workflow rules\n- Complete CLI command reference with examples\n- All command syntax and options\n\n**Why this matters:**\n- MCP users don't need CLI docs (they have native function calls)\n- Non-MCP users need full command reference\n- Same hook works for all users, adapts to their environment\n- Reduces token waste for MCP users\n\n## MCP Detection Logic\n\n```go\nfunc isMCPActive() bool {\n // Check environment variables that Claude Code sets when MCP servers are loaded\n // Options to investigate:\n // 1. Check for CLAUDE_MCP_SERVERS environment variable\n // 2. Check for specific MCP server process indicators\n // 3. Parse ~/.claude/settings.json for enabled MCP servers\n // 4. Check for MCP socket/connection availability\n \n // Fallback: assume MCP if we can't determine\n // (safer to output less and have user call with --full flag)\n return checkMCPEnvironment()\n}\n```\n\n**Alternative: Command flag for explicit control:**\n```bash\nbd prime # Auto-detect MCP\nbd prime --full # Force full output (ignore MCP detection)\nbd prime --mcp # Force MCP mode (minimal output)\n```\n\n## Discovery Logic\n\n**Skip PersistentPreRun database initialization:**\nAdd \"prime\" to noDbCommands list in main.go so it doesn't require .beads/ upfront.\n\n**Silent, cross-platform execution:**\n```go\nvar primeCmd = \u0026cobra.Command{\n Use: \"prime\",\n Short: \"Output AI-optimized workflow context\",\n Run: func(cmd *cobra.Command, args []string) {\n // Find .beads/ directory (walks up tree like bd does)\n dbPath := beads.FindDatabasePath()\n if dbPath == \"\" {\n // Not in a beads project - silent exit with success\n // CRITICAL: No stderr output, exit 0\n // This enables cross-platform hook integration\n os.Exit(0)\n }\n \n // Detect MCP mode (unless overridden by flags)\n mcpMode := isMCPActive()\n if fullFlag {\n mcpMode = false\n }\n if mcpFlag {\n mcpMode = true\n }\n \n // Output workflow context (adaptive based on MCP)\n if err := outputPrimeContext(mcpMode); err != nil {\n // Suppress all errors - silent exit with success\n // Never write to stderr (breaks Windows compatibility)\n os.Exit(0)\n }\n },\n}\n```\n\n**Why silent execution matters:**\n- **Cross-platform**: No shell-specific syntax needed (`2\u003e/dev/null`, `|| true`)\n- **Hook-friendly**: Can be called directly from JSON: `\"command\": \"bd prime\"`\n- **Windows compatible**: Works in cmd.exe, PowerShell, bash\n- **Non-beads projects**: No error noise when run outside beads projects\n\n## Output Formats\n\n### MCP Mode (~500 tokens)\n```markdown\n# Beads Workflow Context\n\n\u003e **You have native bd MCP tools available** - use them instead of markdown TODOs\n\u003e Run `bd prime --full` for complete CLI reference if needed\n\n## Core Workflow Rules\n\n**Task Tracking:**\n- Use bd MCP tools for ALL work tracking (never markdown TODOs)\n- Check available work: use `mcp__plugin_beads_beads__ready` tool\n- Create issues: use `mcp__plugin_beads_beads__create` tool\n- Update status: use `mcp__plugin_beads_beads__update` tool\n\n**Session Management:**\n- Start: Check `ready` tool for available work\n- During: Keep issues updated with `update` tool\n- End: Verify sync status, close completed issues\n\n**Git Integration:**\n- Hooks auto-sync issues with git commits\n- Run `sync` tool at session end to push to remote\n\n**Need help?** \n- Use `show` tool for issue details\n- Check AGENTS.md for complete workflow\n- Run `bd prime --full` for CLI command reference\n```\n\n### Non-MCP Mode (~1-2k tokens)\n```markdown\n# Beads Workflow Context\n\n\u003e **Context Recovery**: Run `bd prime` after compaction, clear, or new session\n\u003e Hooks auto-call this in Claude Code when .beads/ detected\n\n## Core Rules\n- Track ALL work in bd (no markdown TODOs)\n- Git workflow: hooks auto-sync, run `bd sync` at session end\n- Session management: check `bd ready` for available work\n\n## Essential Commands\n\n### Finding Work\n- `bd ready` - Show issues ready to work (no blockers)\n- `bd list --status=open` - All open issues\n- `bd list --status=in_progress` - Your active work\n- `bd show \u003cid\u003e` - Detailed issue view with dependencies\n\n### Creating \u0026 Updating\n- `bd create --title=\"...\" --type=task|bug|feature` - New issue\n- `bd update \u003cid\u003e --status=in_progress` - Claim work\n- `bd update \u003cid\u003e --assignee=username` - Assign to someone\n- `bd close \u003cid\u003e` - Mark complete\n- `bd close \u003cid\u003e --reason=\"explanation\"` - Close with reason\n\n### Dependencies \u0026 Blocking\n- `bd dep \u003cfrom\u003e \u003cto\u003e` - Add blocker dependency (from blocks to)\n- `bd blocked` - Show all blocked issues\n- `bd show \u003cid\u003e` - See what's blocking/blocked by this issue\n\n### Sync \u0026 Collaboration\n- `bd sync` - Sync with git remote (run at session end)\n- `bd sync --status` - Check sync status without syncing\n\n### Project Health\n- `bd stats` - Project statistics (open/closed/blocked counts)\n- `bd doctor` - Check for issues (sync problems, missing hooks)\n\n## Common Workflows\n\n**Starting work:**\n```bash\nbd ready # Find available work\nbd show \u003cid\u003e # Review issue details\nbd update \u003cid\u003e --status=in_progress # Claim it\n```\n\n**Completing work:**\n```bash\nbd close \u003cid\u003e # Mark done\nbd sync # Push to remote\n```\n\n**Creating dependent work:**\n```bash\nbd create --title=\"Implement feature X\" --type=feature\nbd create --title=\"Write tests for X\" --type=task\nbd dep beads-xxx beads-yyy # Feature blocks tests\n```\n\nFor complete docs: AGENTS.md, QUICKSTART.md, `bd --help`\n```\n\n## Behavior Guarantees\n\n1. **Never writes to stderr** (all errors suppressed internally)\n2. **Always exits 0** (success) even when:\n - Not in a beads project\n - .beads/ not found\n - Any internal error occurs\n3. **Cross-platform compatible** (no shell-specific syntax)\n4. **Hook-safe** (can be called directly without wrappers)\n5. **MCP-aware** (adapts output based on user's tool environment)\n\n## Files\n- `cmd/bd/prime.go` - Command implementation with MCP detection\n- `cmd/bd/prime_test.go` - Tests (including MCP mode switching)\n- Update `cmd/bd/main.go` - Add \"prime\" to noDbCommands list","acceptance_criteria":"- `bd prime` outputs markdown format\n- Output is ~1-2k tokens\n- Output includes workflow rules and command reference\n- Command has unit tests\n- Documentation updated in AGENTS.md","status":"closed","priority":2,"issue_type":"feature","created_at":"2025-11-11T23:28:42.74124-08:00","updated_at":"2025-11-12T08:30:15.711595-08:00","closed_at":"2025-11-12T08:30:15.711595-08:00","source_repo":".","dependencies":[{"issue_id":"bd-rpn","depends_on_id":"bd-90v","type":"parent-child","created_at":"2025-11-11T23:31:20.357861-08:00","created_by":"daemon"}]} -{"id":"bd-rsua","content_hash":"4aee94c40640ad5371b3ddc2d0b16650dc64ec93937dee5671e3413247173147","title":"bd doctor should detect and fix missing sync.branch config","description":"## Problem\n\nExisting beads repositories initialized before commit a4c38d5 (bd-flil fix) don't have sync.branch configured. This causes 'bd sync --status' to fail with a confusing error.\n\n## Root Cause\n\n- bd-flil (commit a4c38d5) added auto-configuration of sync.branch during 'bd init'\n- But existing repos don't get this config migrated\n- There's no migration/upgrade path for legacy repos\n\n## Impact\n\nUsers get this error even though their repo has been working fine:\n```\nError: sync.branch not configured (run 'bd config set sync.branch \u003cbranch-name\u003e')\n```\n\n## Solution\n\nAdd to 'bd doctor' (or create 'bd migrate' command):\n1. Detect when sync.branch is missing\n2. Auto-set it to current branch (via git symbolic-ref --short HEAD)\n3. Warn user that it was auto-configured\n\n## Workaround\n\nManually run: `bd config set sync.branch main` (or whatever your main branch is)","status":"open","priority":1,"issue_type":"bug","created_at":"2025-11-23T21:01:40.434592-08:00","updated_at":"2025-11-23T21:01:40.434592-08:00","source_repo":"."} +{"id":"bd-rsua","content_hash":"ff7530f602ff1f92164f457282d75728546802e6a63a9db6c6a354274a4fdb21","title":"bd doctor should detect and fix missing sync.branch config","description":"## Problem\n\nExisting beads repositories initialized before commit a4c38d5 (bd-flil fix) don't have sync.branch configured. This causes 'bd sync --status' to fail with a confusing error.\n\n## Root Cause\n\n- bd-flil (commit a4c38d5) added auto-configuration of sync.branch during 'bd init'\n- But existing repos don't get this config migrated\n- There's no migration/upgrade path for legacy repos\n\n## Impact\n\nUsers get this error even though their repo has been working fine:\n```\nError: sync.branch not configured (run 'bd config set sync.branch \u003cbranch-name\u003e')\n```\n\n## Solution\n\nAdd to 'bd doctor' (or create 'bd migrate' command):\n1. Detect when sync.branch is missing\n2. Auto-set it to current branch (via git symbolic-ref --short HEAD)\n3. Warn user that it was auto-configured\n\n## Workaround\n\nManually run: `bd config set sync.branch main` (or whatever your main branch is)","status":"in_progress","priority":1,"issue_type":"bug","created_at":"2025-11-23T21:01:40.434592-08:00","updated_at":"2025-11-23T21:05:35.767189-08:00","source_repo":"."} {"id":"bd-rtp","content_hash":"302e1b77241830b37c9bcc6758da110c797bab2481877ebaa9bff931628e97f9","title":"Implement signal-aware context in CLI commands","description":"Replace context.Background() with signal.NotifyContext() to enable graceful cancellation.\n\n## Context\nPart of context propagation work (bd-350). Phase 1 infrastructure is complete - sqlite.New() now accepts context parameter.\n\n## Implementation\nSet up signal-aware context at the CLI entry points:\n\n```go\nctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)\ndefer cancel()\n```\n\n## Key Locations\n- cmd/bd/main.go:438 (marked with TODO(bd-350))\n- cmd/bd/daemon.go:317 (marked with TODO(bd-350))\n- Other command entry points\n\n## Benefits\n- Ctrl+C cancels ongoing database operations\n- Graceful shutdown on SIGTERM\n- Better user experience during long operations\n\n## Acceptance Criteria\n- [ ] Ctrl+C during import cancels operation cleanly\n- [ ] Ctrl+C during export cancels operation cleanly\n- [ ] No database corruption on cancellation\n- [ ] Proper cleanup on signal (defers execute)","status":"closed","priority":1,"issue_type":"feature","created_at":"2025-11-20T21:26:34.621983-05:00","updated_at":"2025-11-20T21:32:26.288303-05:00","closed_at":"2025-11-20T21:32:26.288303-05:00","source_repo":"."} {"id":"bd-ry1u","content_hash":"013464efc3cf53d47c2a170040c66099ce77b13d3d1b34c3729e6a5208122799","title":"Publish official devcontainer configuration","description":"","design":"Problem: GH issue #229 shows git hooks aren't available in devcontainers because bd CLI isn't installed. Solution: Provide official .devcontainer/devcontainer.json that installs Go, builds bd from source, runs bd init --quiet, and installs hooks automatically. Benefits: New contributors start immediately, AI assistants work out-of-box, no manual setup. Implementation: Create .devcontainer/devcontainer.json with Go feature, postCreateCommand to build bd and install hooks, environment variables. Acceptance: Container builds with bd installed, hooks functional, documentation updated, tested with Codespaces and VSCode.","notes":"Devcontainer configuration implemented. Manual testing required in actual devcontainer environment (Codespaces or VSCode Remote Containers). All code changes complete, tests pass, linting clean.","status":"closed","priority":2,"issue_type":"epic","created_at":"2025-11-05T15:02:21.783666-08:00","updated_at":"2025-11-05T17:46:42.70998-08:00","closed_at":"2025-11-05T17:46:42.70998-08:00","source_repo":"."} {"id":"bd-s02","content_hash":"911d456e4dabae028dd615b643c99058ef12e55ea523cb81cc933783c7b13546","title":"Manual task","description":"","status":"open","priority":2,"issue_type":"task","created_at":"2025-11-03T20:15:10.022202-08:00","updated_at":"2025-11-03T20:15:10.022202-08:00","source_repo":"."} diff --git a/cmd/bd/doctor.go b/cmd/bd/doctor.go index 0ce5d8e9..f3d5c842 100644 --- a/cmd/bd/doctor.go +++ b/cmd/bd/doctor.go @@ -196,6 +196,8 @@ func applyFixes(result doctorResult) { err = fix.SchemaCompatibility(result.Path) case "Git Merge Driver": err = fix.MergeDriver(result.Path) + case "Sync Branch Config": + err = fix.SyncBranchConfig(result.Path) default: fmt.Printf(" ⚠ No automatic fix available for %s\n", check.Name) fmt.Printf(" Manual fix: %s\n", check.Fix) @@ -348,6 +350,11 @@ func runDiagnostics(path string) doctorResult { result.Checks = append(result.Checks, metadataCheck) // Don't fail overall check for metadata, just warn + // Check 17: Sync branch configuration (bd-rsua) + syncBranchCheck := checkSyncBranchConfig(path) + result.Checks = append(result.Checks, syncBranchCheck) + // Don't fail overall check for missing sync.branch, just warn + return result } @@ -1724,6 +1731,103 @@ func parseVersionParts(version string) []int { return result } +func checkSyncBranchConfig(path string) doctorCheck { + beadsDir := filepath.Join(path, ".beads") + + // Skip if .beads doesn't exist + if _, err := os.Stat(beadsDir); os.IsNotExist(err) { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusOK, + Message: "N/A (no .beads directory)", + } + } + + // Check if we're in a git repository + gitDir := filepath.Join(path, ".git") + if _, err := os.Stat(gitDir); os.IsNotExist(err) { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusOK, + Message: "N/A (not a git repository)", + } + } + + // Check metadata.json first for custom database name + var dbPath string + if cfg, err := configfile.Load(beadsDir); err == nil && cfg != nil && cfg.Database != "" { + dbPath = cfg.DatabasePath(beadsDir) + } else { + // Fall back to canonical database name + dbPath = filepath.Join(beadsDir, beads.CanonicalDatabaseName) + } + + // Skip if no database (JSONL-only mode) + if _, err := os.Stat(dbPath); os.IsNotExist(err) { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusOK, + Message: "N/A (JSONL-only mode)", + } + } + + // Open database to check config + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusWarning, + Message: "Unable to check sync.branch config", + Detail: err.Error(), + } + } + defer db.Close() + + // Check if sync.branch is configured + var syncBranch string + err = db.QueryRow("SELECT value FROM config WHERE key = ?", "sync.branch").Scan(&syncBranch) + if err != nil && err != sql.ErrNoRows { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusWarning, + Message: "Unable to read sync.branch config", + Detail: err.Error(), + } + } + + // If sync.branch is already configured, we're good + if syncBranch != "" { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusOK, + Message: fmt.Sprintf("Configured (%s)", syncBranch), + } + } + + // sync.branch is not configured - get current branch for the fix message + cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD") + cmd.Dir = path + output, err := cmd.Output() + if err != nil { + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusWarning, + Message: "sync.branch not configured", + Detail: "Unable to detect current branch", + Fix: "Run 'bd config set sync.branch ' or 'bd doctor --fix' to auto-configure", + } + } + + currentBranch := strings.TrimSpace(string(output)) + return doctorCheck{ + Name: "Sync Branch Config", + Status: statusWarning, + Message: "sync.branch not configured", + Detail: fmt.Sprintf("Current branch: %s", currentBranch), + Fix: fmt.Sprintf("Run 'bd doctor --fix' to auto-configure to '%s', or manually: bd config set sync.branch ", currentBranch), + } +} + func init() { rootCmd.AddCommand(doctorCmd) doctorCmd.Flags().BoolVar(&perfMode, "perf", false, "Run performance diagnostics and generate CPU profile") diff --git a/cmd/bd/doctor/fix/sync_branch.go b/cmd/bd/doctor/fix/sync_branch.go new file mode 100644 index 00000000..354d4a7c --- /dev/null +++ b/cmd/bd/doctor/fix/sync_branch.go @@ -0,0 +1,43 @@ +package fix + +import ( + "fmt" + "os/exec" + "strings" +) + +// SyncBranchConfig fixes missing sync.branch configuration by auto-setting it to the current branch +func SyncBranchConfig(path string) error { + if err := validateBeadsWorkspace(path); err != nil { + return err + } + + // Get current branch + cmd := exec.Command("git", "symbolic-ref", "--short", "HEAD") + cmd.Dir = path + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("failed to get current branch: %w", err) + } + + currentBranch := strings.TrimSpace(string(output)) + if currentBranch == "" { + return fmt.Errorf("current branch is empty") + } + + // Get bd binary + bdBinary, err := getBdBinary() + if err != nil { + return err + } + + // Set sync.branch using bd config set + setCmd := exec.Command(bdBinary, "config", "set", "sync.branch", currentBranch) + setCmd.Dir = path + if output, err := setCmd.CombinedOutput(); err != nil { + return fmt.Errorf("failed to set sync.branch: %w\nOutput: %s", err, string(output)) + } + + fmt.Printf(" Set sync.branch = %s\n", currentBranch) + return nil +} diff --git a/cmd/bd/doctor_test.go b/cmd/bd/doctor_test.go index 310f2923..9ab5ee29 100644 --- a/cmd/bd/doctor_test.go +++ b/cmd/bd/doctor_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "fmt" "os" + "os/exec" "path/filepath" "strings" "testing" @@ -848,3 +849,124 @@ func TestParseVersionParts(t *testing.T) { } } } + +func TestCheckSyncBranchConfig(t *testing.T) { + tests := []struct { + name string + setupFunc func(t *testing.T, tmpDir string) + expectedStatus string + expectWarning bool + }{ + { + name: "no beads directory", + setupFunc: func(t *testing.T, tmpDir string) { + // No .beads directory + }, + expectedStatus: statusOK, + expectWarning: false, + }, + { + name: "not a git repo", + setupFunc: func(t *testing.T, tmpDir string) { + beadsDir := filepath.Join(tmpDir, ".beads") + if err := os.Mkdir(beadsDir, 0750); err != nil { + t.Fatal(err) + } + }, + expectedStatus: statusOK, + expectWarning: false, + }, + { + name: "sync.branch configured", + setupFunc: func(t *testing.T, tmpDir string) { + // Initialize git repo + cmd := exec.Command("git", "init") + cmd.Dir = tmpDir + if err := cmd.Run(); err != nil { + t.Fatal(err) + } + cmd = exec.Command("git", "config", "user.email", "test@example.com") + cmd.Dir = tmpDir + _ = cmd.Run() + cmd = exec.Command("git", "config", "user.name", "Test User") + cmd.Dir = tmpDir + _ = cmd.Run() + + // Create .beads directory and database + beadsDir := filepath.Join(tmpDir, ".beads") + if err := os.Mkdir(beadsDir, 0750); err != nil { + t.Fatal(err) + } + dbPath := filepath.Join(beadsDir, "beads.db") + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + // Create config table and set sync.branch + if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)`); err != nil { + t.Fatal(err) + } + if _, err := db.Exec(`INSERT INTO config (key, value) VALUES ('sync.branch', 'main')`); err != nil { + t.Fatal(err) + } + }, + expectedStatus: statusOK, + expectWarning: false, + }, + { + name: "sync.branch not configured", + setupFunc: func(t *testing.T, tmpDir string) { + // Initialize git repo + cmd := exec.Command("git", "init") + cmd.Dir = tmpDir + if err := cmd.Run(); err != nil { + t.Fatal(err) + } + cmd = exec.Command("git", "config", "user.email", "test@example.com") + cmd.Dir = tmpDir + _ = cmd.Run() + cmd = exec.Command("git", "config", "user.name", "Test User") + cmd.Dir = tmpDir + _ = cmd.Run() + + // Create .beads directory and database + beadsDir := filepath.Join(tmpDir, ".beads") + if err := os.Mkdir(beadsDir, 0750); err != nil { + t.Fatal(err) + } + dbPath := filepath.Join(beadsDir, "beads.db") + db, err := sql.Open("sqlite3", dbPath) + if err != nil { + t.Fatal(err) + } + defer db.Close() + + // Create config table but don't set sync.branch + if _, err := db.Exec(`CREATE TABLE IF NOT EXISTS config (key TEXT PRIMARY KEY, value TEXT)`); err != nil { + t.Fatal(err) + } + }, + expectedStatus: statusWarning, + expectWarning: true, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + tmpDir := t.TempDir() + tc.setupFunc(t, tmpDir) + + result := checkSyncBranchConfig(tmpDir) + + if result.Status != tc.expectedStatus { + t.Errorf("Expected status %q, got %q", tc.expectedStatus, result.Status) + } + + if tc.expectWarning && result.Fix == "" { + t.Error("Expected Fix field to be set for warning status") + } + }) + } +}