feat: Enhance bd doctor with force repair and source selection

Add comprehensive database corruption recovery capabilities to bd doctor.

## Changes

### New Command Flags

- --force: Force repair mode that bypasses database validation
- --source: Choose source of truth (auto/jsonl/db) for recovery

### Enhanced Error Classification

Improved CheckDatabaseIntegrity() to detect and classify:
- Database locked errors (suggests killing processes, removing locks)
- Invalid SQLite files (suggests JSONL recovery with exact commands)
- Migration/validation failures (orphaned dependencies, etc.)
- Generic database errors (context-aware suggestions)

Each error type provides:
- Specific diagnosis
- Step-by-step recovery instructions
- Appropriate command examples with new flags

### Force Recovery Implementation

New DatabaseCorruptionRecoveryWithOptions() function:
- Bypasses database validation when --force is used
- Supports explicit source of truth selection
- Auto-detects best recovery path when source=auto
- Comprehensive rollback on failure
- Uses --force --no-git-history in import during force mode

### Integration

Updated fix orchestration to pass force and source flags to recovery.

## Usage Examples

```bash
# Unopenable database with validation errors
bd doctor --fix --force --source=jsonl

# Choose specific source of truth
bd doctor --fix --source=jsonl  # Trust JSONL
bd doctor --fix --source=db     # Trust database
bd doctor --fix --source=auto   # Auto-detect (default)

# Force recovery with auto-detection
bd doctor --fix --force
```

## Problem Solved

Before: When database had validation errors (orphaned dependencies,
foreign key violations), all bd commands failed in a catch-22 situation.
Could not open database to fix database. Users had to manually delete
database files and reinit.

After: bd doctor --fix --force detects unopenable databases, provides
clear recovery steps, and forces rebuild from JSONL even when database
validation fails.

## Backward Compatibility

- All new flags are optional with safe defaults
- --source defaults to 'auto' (existing behavior)
- --force is opt-in only
- Existing bd doctor behavior unchanged when flags not used
- DatabaseCorruptionRecovery() still exists for compatibility

Fixes: bd-pgza
This commit is contained in:
kraitsura
2025-12-29 22:59:48 -08:00
parent aa759c06aa
commit 602c59eb48
4 changed files with 202 additions and 11 deletions
+6
View File
@@ -48,6 +48,8 @@ var (
doctorOutput string // export diagnostics to file
doctorFixChildParent bool // opt-in fix for child→parent deps
doctorVerbose bool // show detailed output during fixes
doctorForce bool // force repair mode, bypass validation where safe
doctorSource string // source of truth selection: auto, jsonl, db
perfMode bool
checkHealthMode bool
doctorCheckFlag string // run specific check (e.g., "pollution")
@@ -117,6 +119,8 @@ Examples:
bd doctor --fix --yes # Automatically fix issues (no confirmation)
bd doctor --fix -i # Confirm each fix individually
bd doctor --fix --fix-child-parent # Also fix child→parent deps (opt-in)
bd doctor --fix --force # Force repair even when database can't be opened
bd doctor --fix --source=jsonl # Rebuild database from JSONL (source of truth)
bd doctor --dry-run # Preview what --fix would do without making changes
bd doctor --perf # Performance diagnostics
bd doctor --output diagnostics.json # Export diagnostics to file
@@ -219,6 +223,8 @@ func init() {
doctorCmd.Flags().BoolVar(&doctorDryRun, "dry-run", false, "Preview fixes without making changes")
doctorCmd.Flags().BoolVar(&doctorFixChildParent, "fix-child-parent", false, "Remove child→parent dependencies (opt-in)")
doctorCmd.Flags().BoolVarP(&doctorVerbose, "verbose", "v", false, "Show detailed output during fixes (e.g., list each removed dependency)")
doctorCmd.Flags().BoolVar(&doctorForce, "force", false, "Force repair mode: attempt recovery even when database cannot be opened")
doctorCmd.Flags().StringVar(&doctorSource, "source", "auto", "Choose source of truth for recovery: auto (detect), jsonl (prefer JSONL), db (prefer database)")
}
func runDiagnostics(path string) doctorResult {