Files
beads/docs/DELETIONS.md
Steve Yegge d80d82d693 docs: update deletion docs for tombstones, add changelog entries
- Rewrote DELETIONS.md to document inline tombstones (replacing legacy
  deletions.jsonl approach)
- Added tombstone feature entries to CHANGELOG.md under Unreleased
- Fixed duplicate 0.29.0 header in CHANGELOG.md
- Ran bd migrate-tombstones on beads repo (dogfooding)
- Closed bd-vw8 epic (all 12 dependencies complete)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 07:28:38 -08:00

208 lines
5.7 KiB
Markdown

# 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.
**Beads uses inline tombstones** - deleted issues are converted to a special `tombstone` status and remain in `issues.jsonl`. This provides:
- Full audit trail (who, when, why)
- Atomic sync with issue data (no separate manifest to merge)
- TTL-based expiration (default 30 days)
- Proper 3-way merge conflict resolution
## How Tombstones Work
When you delete an issue:
1. The issue's status changes to `tombstone`
2. Deletion metadata is recorded (`deleted_at`, `deleted_by`, `delete_reason`)
3. The original issue type is preserved in `original_type`
4. All dependencies are removed (tombstones don't block anything)
5. The tombstone syncs via git like any other issue
### Tombstone Fields
| Field | Type | Description |
|-------|------|-------------|
| `status` | string | Always `"tombstone"` |
| `deleted_at` | ISO 8601 | When the issue was deleted |
| `deleted_by` | string | Actor who performed the deletion |
| `delete_reason` | string | Optional context (e.g., "duplicate", "cleanup") |
| `original_type` | string | Issue type before deletion (task, bug, etc.) |
### Example Tombstone in JSONL
```json
{"id":"bd-42","status":"tombstone","title":"Original title","deleted_at":"2025-01-15T10:00:00Z","deleted_by":"stevey","delete_reason":"duplicate of bd-xyz","original_type":"task"}
```
## Commands
### Deleting Issues
```bash
bd delete bd-42 # Delete single issue (preview mode)
bd delete bd-42 --force # Actually delete
bd delete bd-42 bd-43 bd-44 -f # Delete multiple issues
bd delete bd-42 --cascade -f # Delete with all dependents
bd delete --from-file ids.txt -f # Delete from file (one ID per line)
bd delete bd-42 --dry-run # Preview what would be deleted
```
### Viewing Deleted Issues
```bash
bd list --status=tombstone # List all tombstones
bd show bd-42 # View tombstone details (if you know the ID)
```
## TTL and Expiration
Tombstones expire after a configurable TTL (default: 30 days). This prevents unbounded growth while ensuring deletions propagate to all clones.
### How Expiration Works
1. Tombstones older than TTL + 1 hour grace period are eligible for pruning
2. `bd compact` removes expired tombstones from `issues.jsonl`
3. Git history fallback handles edge cases where pruned tombstones are needed
### Configuration
```yaml
# .beads/config.yaml
tombstone:
ttl_days: 30 # Default: 30 days
```
Or via CLI:
```bash
bd config set tombstone.ttl_days 60
```
### Manual Pruning
```bash
bd compact # Prune expired tombstones (and other compaction)
```
## Conflict Resolution
When the same issue is modified in one clone and deleted in another:
1. Both changes sync via git
2. 3-way merge detects the conflict
3. Resolution rules:
- If tombstone is expired → live issue wins (resurrection)
- If tombstone is fresh → tombstone wins (deletion propagates)
- `updated_at` timestamps break ties
This ensures deletions propagate reliably while handling clock skew and delayed syncs.
## Migration from Legacy Format
Prior to v0.30, beads used a separate `deletions.jsonl` manifest. To migrate:
```bash
bd migrate-tombstones # Convert deletions.jsonl to inline tombstones
bd migrate-tombstones --dry-run # Preview changes first
```
The migration:
1. Reads existing deletions from `deletions.jsonl`
2. Creates tombstone entries in `issues.jsonl`
3. Archives the old file as `deletions.jsonl.migrated`
After migration, run `bd sync` to propagate tombstones to other clones.
## Troubleshooting
### Deleted Issue Reappearing
If a deleted issue reappears after sync:
```bash
# Check if it's a tombstone
bd list --status=tombstone | grep bd-xxx
# Check tombstone details
bd show bd-xxx
# Force re-import from JSONL
bd import --force
```
If the issue keeps reappearing, the tombstone may have expired. Re-delete it:
```bash
bd delete bd-xxx --force
bd sync
```
### Tombstones Not Syncing
Ensure tombstones are being exported:
```bash
# Check if tombstone is in JSONL
grep '"id":"bd-xxx"' .beads/issues.jsonl
# Force export
bd export --force
bd sync
```
### Too Many Tombstones
If you have many old tombstones:
```bash
# Check tombstone count
bd list --status=tombstone | wc -l
# Prune expired tombstones
bd compact
```
## Design Rationale
### Why Inline Tombstones?
The previous `deletions.jsonl` manifest had issues:
- **Wild poisoning**: Stale clone's manifest could delete issues incorrectly
- **Merge inconsistency**: Separate file meant separate merge logic
- **Two sources of truth**: Issue data and deletion data could diverge
Inline tombstones solve these by:
- Single source of truth (`issues.jsonl`)
- Same merge semantics as regular issues
- Atomic with issue data
- Full audit trail preserved
### Why TTL-Based Expiration?
- Bounds storage growth (tombstones eventually pruned)
- Git history fallback handles edge cases
- 30-day default handles typical sync scenarios
- Configurable for teams with longer sync cycles
### Why 1-Hour Grace Period?
Clock skew between machines can cause issues:
- Machine A deletes issue at 10:00 (its clock)
- Machine B's clock is 30 minutes ahead
- Without grace period, B might see tombstone as expired immediately
The 1-hour grace period ensures tombstones propagate even with minor clock drift.
## Related
- [CONFIG.md](CONFIG.md) - Configuration options
- [DAEMON.md](DAEMON.md) - Daemon auto-sync behavior
- [TROUBLESHOOTING.md](TROUBLESHOOTING.md) - General troubleshooting