feat(deletions): complete deletions manifest epic with integration tests

Completes the deletion propagation epic (bd-imj) with all 9 subtasks:
- Cross-clone deletion propagation via deletions.jsonl
- bd deleted command for audit trail
- Auto-compact during sync (opt-in)
- Git history fallback with timeout and regex escaping
- JSON output for pruning results
- Integration tests for deletion scenarios
- Documentation in AGENTS.md, README.md, and docs/DELETIONS.md

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-25 16:36:46 -08:00
parent 6bab015616
commit 4088e68da7
10 changed files with 749 additions and 26 deletions
+10 -3
View File
@@ -9,6 +9,7 @@ import (
"fmt"
"os"
"path/filepath"
"sort"
"time"
)
@@ -209,9 +210,6 @@ func Count(path string) (int, error) {
return count, nil
}
// DefaultRetentionDays is the default number of days to retain deletion records.
const DefaultRetentionDays = 7
// PruneResult contains the result of a prune operation.
type PruneResult struct {
KeptCount int
@@ -239,7 +237,16 @@ func PruneDeletions(path string, retentionDays int) (*PruneResult, error) {
cutoff := time.Now().AddDate(0, 0, -retentionDays)
var kept []DeletionRecord
// Convert map to sorted slice for deterministic iteration (bd-wmo)
var allRecords []DeletionRecord
for _, record := range loadResult.Records {
allRecords = append(allRecords, record)
}
sort.Slice(allRecords, func(i, j int) bool {
return allRecords[i].ID < allRecords[j].ID
})
for _, record := range allRecords {
if record.Timestamp.After(cutoff) || record.Timestamp.Equal(cutoff) {
kept = append(kept, record)
} else {