docs: add ZFC resurrection bug investigation notes

🤖 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 00:04:58 -08:00
parent 8370a43a59
commit 3d6dd278bf
2 changed files with 156 additions and 122 deletions

File diff suppressed because one or more lines are too long

82
ZFC_BUG_INVESTIGATION.md Normal file
View File

@@ -0,0 +1,82 @@
# ZFC Resurrection Bug Investigation
## Problem
When `bd sync` runs with a stale DB (more issues than JSONL), it should trust JSONL as source of truth. Instead, it re-exports the stale DB back to JSONL, "resurrecting" deleted issues.
## Reproduction
1. Clean JSONL has 74 issues
2. Stale DB has 122 issues (from previous bad sync)
3. Run `bd sync`
4. Expected: JSONL stays at 74
5. Actual: JSONL becomes 122 (polluted)
## Root Cause Analysis
### The Sync Flow
1. **Step 1**: ZFC check - if DB > JSONL by >50%, import first and set `skipExport=true`
2. **Step 1b**: If `!skipExport`, export DB to JSONL
3. **Step 2**: Commit changes
4. **Step 3**: Pull from remote
5. **Step 4**: Import pulled JSONL
6. **Step 4.5**: Check if re-export needed (`skipReexport` variable)
7. **Step 5**: Push
### The Bug
The post-pull re-export check (Step 4.5) has its own `skipReexport` variable that was NOT inheriting from the initial `skipExport` flag.
**Original code (line 306):**
```go
skipReexport := false // BUG: doesn't inherit skipExport
```
**Fixed code:**
```go
skipReexport := skipExport // Carry forward initial ZFC detection
```
### Why Fix Didn't Work
Even with the fix, the bug persists because:
1. **Multiple pollution paths**: The initial export (Step 1b) can pollute JSONL before pull
2. **Import doesn't delete**: When 74-line JSONL is imported to 122-issue DB, the 48 extra issues remain
3. **Post-import check fails**: After import, DB still has 122, JSONL has 74 (from pull), but `dbNeedsExport()` sees differences and triggers re-export
### Key Insight
The import operation only **creates/updates** - it never **deletes** issues that exist in DB but not in JSONL. So even after importing a 74-issue JSONL, the DB still has 122 issues.
## Proposed Solutions
### Option 1: Use `--delete-missing` flag during ZFC import
When ZFC is detected, import with deletion of missing issues:
```go
if err := importFromJSONL(ctx, jsonlPath, renameOnImport, true /* deleteMissing */); err != nil {
```
### Option 2: Skip ALL exports after ZFC detection
Make `skipExport` a more powerful flag that blocks all export paths, including post-pull re-export.
### Option 3: Add explicit ZFC mode
Track ZFC state throughout the entire sync operation and prevent any export when in ZFC mode.
## Files Involved
- `cmd/bd/sync.go:126-178` - Initial ZFC check and export
- `cmd/bd/sync.go:303-357` - Post-pull re-export logic
- `cmd/bd/integrity.go:289-301` - `validatePostImport()` function
- `cmd/bd/import.go` - Import logic (needs `--delete-missing` support)
## Current State
- Remote (origin/main) has 74-line JSONL at commit `c6f9f7e`
- Local DB has 122 issues (stale)
- Fix attempt at line 306 (`skipReexport := skipExport`) is in place but insufficient
- Need to also handle the import-without-delete issue
## Next Steps
1. Clean up: `git reset --hard c6f9f7e` (or earlier clean commit)
2. Delete local DB: `rm -f .beads/beads.db`
3. Import with delete-missing to sync DB with JSONL
4. Implement proper fix that either:
- Uses delete-missing during ZFC import, OR
- Completely blocks re-export when ZFC is detected
## Version
This should be fixed in v0.24.6 (v0.24.5 had the resurrection bug).