refactor: remove all deletions.jsonl code (bd-fom)

Complete removal of the legacy deletions.jsonl manifest system.
Tombstones are now the sole deletion mechanism.

Removed:
- internal/deletions/ - entire package
- cmd/bd/deleted.go - deleted command
- cmd/bd/doctor/fix/deletions.go - HydrateDeletionsManifest
- Tests for all removed functionality

Cleaned:
- cmd/bd/sync.go - removed sanitize, auto-compact
- cmd/bd/delete.go - removed dual-writes
- cmd/bd/doctor.go - removed checkDeletionsManifest
- internal/importer/importer.go - removed deletions checks
- internal/syncbranch/worktree.go - removed deletions merge
- cmd/bd/integrity.go - updated validation (warn-only on decrease)

Files removed: 12
Lines removed: ~7500

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-16 14:20:32 -08:00
parent e0528de590
commit 9f76cfda01
32 changed files with 298 additions and 7534 deletions

View File

@@ -1,14 +1,10 @@
package fix
import (
"bufio"
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"github.com/steveyegge/beads/internal/deletions"
)
// DBJSONLSync fixes database-JSONL sync issues by running bd sync --import-only
@@ -31,13 +27,10 @@ func DBJSONLSync(path string) error {
}
hasJSONL := false
actualJSONLPath := ""
if _, err := os.Stat(jsonlPath); err == nil {
hasJSONL = true
actualJSONLPath = jsonlPath
} else if _, err := os.Stat(beadsJSONLPath); err == nil {
hasJSONL = true
actualJSONLPath = beadsJSONLPath
}
if !hasDB || !hasJSONL {
@@ -61,107 +54,5 @@ func DBJSONLSync(path string) error {
return fmt.Errorf("failed to sync database with JSONL: %w", err)
}
// bd-8v5o: Clean up deletions manifest for hydrated issues
// After sync, remove any issues from deletions.jsonl that exist in JSONL
// This prevents perpetual "Skipping bd-xxx (in deletions manifest)" warnings
if err := cleanupDeletionsManifest(beadsDir, actualJSONLPath); err != nil {
// Non-fatal - just log warning
fmt.Printf(" Warning: failed to clean up deletions manifest: %v\n", err)
}
return nil
}
// cleanupDeletionsManifest removes issues from deletions.jsonl that exist in JSONL.
// This is needed because when issues are hydrated from git history (e.g., via bd init
// or bd sync --import-only), they may still be in the deletions manifest from a
// previous deletion. This causes perpetual skip warnings during sync.
func cleanupDeletionsManifest(beadsDir, jsonlPath string) error {
deletionsPath := deletions.DefaultPath(beadsDir)
// Check if deletions manifest exists
if _, err := os.Stat(deletionsPath); os.IsNotExist(err) {
return nil // No deletions manifest, nothing to clean up
}
// Load deletions manifest
loadResult, err := deletions.LoadDeletions(deletionsPath)
if err != nil {
return fmt.Errorf("failed to load deletions manifest: %w", err)
}
if len(loadResult.Records) == 0 {
return nil // No deletions, nothing to clean up
}
// Get IDs from JSONL (excluding tombstones)
jsonlIDs, err := getNonTombstoneJSONLIDs(jsonlPath)
if err != nil {
return fmt.Errorf("failed to read JSONL: %w", err)
}
// Find IDs that are in both deletions manifest and JSONL
var idsToRemove []string
for id := range loadResult.Records {
if jsonlIDs[id] {
idsToRemove = append(idsToRemove, id)
}
}
if len(idsToRemove) == 0 {
return nil // No conflicting entries
}
// Remove conflicting entries from deletions manifest
result, err := deletions.RemoveDeletions(deletionsPath, idsToRemove)
if err != nil {
return fmt.Errorf("failed to remove deletions: %w", err)
}
if result.RemovedCount > 0 {
fmt.Printf(" Removed %d issue(s) from deletions manifest (now hydrated in JSONL)\n", result.RemovedCount)
}
return nil
}
// getNonTombstoneJSONLIDs reads the JSONL file and returns a set of IDs
// that are not tombstones (status != "tombstone").
func getNonTombstoneJSONLIDs(jsonlPath string) (map[string]bool, error) {
ids := make(map[string]bool)
file, err := os.Open(jsonlPath) // #nosec G304 - path validated by caller
if err != nil {
if os.IsNotExist(err) {
return ids, nil
}
return nil, err
}
defer func() {
_ = file.Close()
}()
scanner := bufio.NewScanner(file)
scanner.Buffer(make([]byte, 0, 64*1024), 10*1024*1024)
for scanner.Scan() {
line := scanner.Bytes()
if len(line) == 0 {
continue
}
var issue struct {
ID string `json:"id"`
Status string `json:"status"`
}
if err := json.Unmarshal(line, &issue); err != nil {
continue
}
// Only include non-tombstone issues
if issue.ID != "" && issue.Status != "tombstone" {
ids[issue.ID] = true
}
}
return ids, scanner.Err()
}