fix: resolve P2 sync noise and cleanup issues

- bd-6pni: Auto-filter tombstoned issues with mismatched prefixes during
  import instead of failing. Tombstones from contributor PRs with different
  test prefixes are pollution and safe to ignore.

- bd-ffr9: Stop recreating deletions.jsonl after tombstone migration.
  Added IsTombstoneMigrationComplete() check to all code paths that write
  to the legacy deletions manifest.

- bd-admx: Fix perpetual "JSONL file hash mismatch" warning. Now clears
  both export_hashes AND jsonl_file_hash when mismatch detected, so the
  warning doesn't repeat.

🤖 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 00:55:43 -08:00
parent 88ccce884c
commit 2c86404d65
9 changed files with 341 additions and 30 deletions

View File

@@ -181,6 +181,17 @@ func DefaultPath(beadsDir string) string {
return filepath.Join(beadsDir, "deletions.jsonl")
}
// IsTombstoneMigrationComplete checks if the tombstone migration has been completed.
// After running `bd migrate-tombstones`, the deletions.jsonl file is archived to
// deletions.jsonl.migrated. This function checks for that marker file.
// When migration is complete, new deletion records should NOT be written to
// deletions.jsonl (bd-ffr9).
func IsTombstoneMigrationComplete(beadsDir string) bool {
migratedPath := filepath.Join(beadsDir, "deletions.jsonl.migrated")
_, err := os.Stat(migratedPath)
return err == nil
}
// Count returns the number of lines in the deletions manifest.
// This is a fast operation that doesn't parse JSON, just counts lines.
// Returns 0 if the file doesn't exist or is empty.

View File

@@ -290,6 +290,38 @@ func TestDefaultPath(t *testing.T) {
}
}
func TestIsTombstoneMigrationComplete(t *testing.T) {
t.Run("no migrated file", func(t *testing.T) {
tmpDir := t.TempDir()
if IsTombstoneMigrationComplete(tmpDir) {
t.Error("expected false when no .migrated file exists")
}
})
t.Run("migrated file exists", func(t *testing.T) {
tmpDir := t.TempDir()
migratedPath := filepath.Join(tmpDir, "deletions.jsonl.migrated")
if err := os.WriteFile(migratedPath, []byte("{}"), 0644); err != nil {
t.Fatalf("failed to create migrated file: %v", err)
}
if !IsTombstoneMigrationComplete(tmpDir) {
t.Error("expected true when .migrated file exists")
}
})
t.Run("deletions.jsonl exists without migrated", func(t *testing.T) {
tmpDir := t.TempDir()
deletionsPath := filepath.Join(tmpDir, "deletions.jsonl")
if err := os.WriteFile(deletionsPath, []byte("{}"), 0644); err != nil {
t.Fatalf("failed to create deletions file: %v", err)
}
// Should return false because the .migrated marker doesn't exist
if IsTombstoneMigrationComplete(tmpDir) {
t.Error("expected false when only deletions.jsonl exists (not migrated)")
}
})
}
func TestLoadDeletions_EmptyLines(t *testing.T) {
tmpDir := t.TempDir()
path := filepath.Join(tmpDir, "deletions.jsonl")