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>
5.5 KiB
Deletion Tracking
This document describes how bd tracks and propagates deletions across repository clones.
Overview
When issues are deleted in one clone, those deletions need to propagate to other clones. Without this mechanism, deleted issues would "resurrect" when another clone's database is imported.
The deletions manifest (.beads/deletions.jsonl) is an append-only log that records every deletion. This file is committed to git and synced across all clones.
File Format
The deletions manifest is a JSON Lines file where each line is a deletion record:
{"id":"bd-abc","ts":"2025-01-15T10:00:00Z","by":"stevey","reason":"duplicate of bd-xyz"}
{"id":"bd-def","ts":"2025-01-15T10:05:00Z","by":"claude","reason":"cleanup"}
Fields
| Field | Type | Required | Description |
|---|---|---|---|
id |
string | Yes | Issue ID that was deleted |
ts |
string | Yes | ISO 8601 UTC timestamp |
by |
string | Yes | Actor who performed the deletion |
reason |
string | No | Optional context (e.g., "duplicate", "cleanup") |
Commands
Deleting Issues
bd delete bd-42 # Delete single issue
bd delete bd-42 bd-43 bd-44 # Delete multiple issues
bd cleanup -f # Delete all closed issues
All deletions are automatically recorded to the manifest.
Viewing Deletions
bd deleted # Recent deletions (last 7 days)
bd deleted --since=30d # Deletions in last 30 days
bd deleted --all # All tracked deletions
bd deleted bd-xxx # Lookup specific issue
bd deleted --json # Machine-readable output
Propagation Mechanism
Export (Local Delete)
bd deleteremoves issue from SQLite- Deletion record appended to
deletions.jsonl bd synccommits and pushes the manifest
Import (Remote Delete)
bd syncpulls updated manifest- Import checks each DB issue against manifest
- If issue ID is in manifest, it's deleted from local DB
- If issue ID is NOT in manifest and NOT in JSONL:
- Check git history (see fallback below)
- If found in history → deleted upstream, remove locally
- If not found → local unpushed work, keep it
Git History Fallback
The manifest is pruned periodically to prevent unbounded growth. When a deletion record is pruned but the issue still exists in some clone's DB:
- Import detects: "DB issue not in JSONL, not in manifest"
- Falls back to git history search
- Uses
git log -Sto check if issue ID was ever in JSONL - If found in history → it was deleted, remove from DB
- Backfill: Re-append the deletion to manifest (self-healing)
This fallback ensures deletions propagate even after manifest pruning.
Configuration
Retention Period
By default, deletion records are kept for 7 days. Configure via:
bd config set deletions.retention_days 30
Or in .beads/config.yaml:
deletions:
retention_days: 30
Auto-Compact Threshold
Auto-compaction during bd sync is opt-in:
bd config set deletions.auto_compact_threshold 100
When the manifest exceeds this threshold, old records are pruned during sync. Set to 0 to disable (default).
Manual Pruning
bd compact --retention 7 # Prune records older than 7 days
bd compact --retention 0 # Prune all records (use git fallback)
Size Estimates
- Each record: ~80 bytes
- 7-day retention with 100 deletions/day: ~56KB
- Git compressed: ~10KB
The manifest stays small even with heavy deletion activity.
Conflict Resolution
When multiple clones delete issues simultaneously:
- Both append their deletion records
- Git merges (append-only = no conflicts)
- Result: duplicate entries for same ID (different timestamps)
LoadDeletionsdeduplicates by ID (keeps any entry)- Result: deletion propagates correctly
Duplicate records are harmless and cleaned up during pruning.
Troubleshooting
Deleted Issue Reappearing
If a deleted issue reappears after sync:
# Check if in manifest
bd deleted bd-xxx
# Force re-import
bd import --force
# If still appearing, check git history
git log -S '"id":"bd-xxx"' -- .beads/beads.jsonl
Manifest Not Being Committed
Ensure deletions.jsonl is tracked:
git add .beads/deletions.jsonl
And NOT in .gitignore.
Large Manifest
If the manifest is growing too large:
# Check size
wc -l .beads/deletions.jsonl
# Manual prune
bd compact --retention 7
# Enable auto-compact
bd config set deletions.auto_compact_threshold 100
Design Rationale
Why JSONL?
- Append-only: natural for deletion logs
- Human-readable: easy to audit
- Git-friendly: line-based diffs
- No merge conflicts: append = trivial merge
Why Not Delete from JSONL?
Removing lines from beads.jsonl would work but:
- Loses audit trail (who deleted what when)
- Harder to merge (line deletions can conflict)
- Can't distinguish "deleted" from "never existed"
Why Time-Based Pruning?
- Bounds manifest size
- Git history fallback handles edge cases
- 7-day default handles most sync scenarios
- Configurable for teams with longer sync cycles
Why Git Fallback?
- Handles pruned records gracefully
- Self-healing via backfill
- Works with shallow clones (partial fallback)
- No data loss from aggressive pruning
Related
- CONFIG.md - Configuration options
- DAEMON.md - Daemon auto-sync behavior
- TROUBLESHOOTING.md - General troubleshooting