- Fix dry-run to not mutate state (no export/clear dirty flags)
- Use os.Executable() for import to avoid path hijacking
- Add preflight checks for merge/rebase in progress
- Add upstream tracking validation with helpful hints
- Use CommandContext for all git operations (enable cancellation)
- Add chmod(0644) to exportToJSONL for consistency with export.go
All critical issues from Oracle review addressed.
- Changed from substring matching to standalone line detection
- Only flags actual Git conflict markers on their own lines
- Prevents false alarms from conflict markers in issue descriptions
- Fixes bd-313
Amp-Thread-ID: https://ampcode.com/threads/T-2acdebf1-e4ce-4534-8538-4e7c4fb84232
Co-authored-by: Amp <amp@ampcode.com>
- Removed restore command from bd show output
- Updated compact help text (removed snapshot claim)
- Fixed COMPACTION.md (removed 'batch restore' from roadmap)
- All compaction UI now correctly states permanent decay
- Add EventCompacted event type constant
- Add compaction fields to Issue struct (CompactionLevel, CompactedAt, OriginalSize)
- Update ApplyCompaction to record compaction events with JSON metadata
- Update bd show to display compaction status with emoji indicators
- Update GetIssue query to load compaction fields
- All tests passing
Amp-Thread-ID: https://ampcode.com/threads/T-3f7946c6-8f5e-4a81-9527-1217041c7b39
Co-authored-by: Amp <amp@ampcode.com>
Snapshots defeated the entire purpose of compaction - if we're keeping
the original content, we're not actually saving any space. Compaction
is about graceful memory decay for agentic databases, not reversible
compression.
Removed:
- CreateSnapshot/GetSnapshots/RestoreFromSnapshot from storage
- --restore flag and functionality from bd compact command
- All snapshot-related tests
- Snapshot struct and related code
The database is ephemeral and meant to decay over time. Compaction
actually reduces database size now.
Closes bd-260 (won't fix - conceptually wrong)
Closes bd-261 (already done in bd-259)
- Add --label/-l flag to filter issues by labels (AND logic)
- Add --title flag to filter issues by title substring
- Add TitleSearch field to IssueFilter type
- Implement label and title filtering in SearchIssues
- Perfect for worktree-specific issue management
Examples:
bd list --label worktree,feature-x
bd list --title "authentication"
bd list --label worktree --title "bug"
Breaks down large functions into smaller, focused helpers to pass gocyclo linter:
Auto-import refactoring:
- Extract parseJSONLIssues() to handle JSONL parsing
- Extract handleCollisions() to detect and report conflicts
- Extract importIssueData() to coordinate issue/dep/label imports
- Extract updateExistingIssue() and createNewIssue() for clarity
- Extract importDependencies() and importLabels() for modularity
Flush refactoring:
- Extract recordFlushFailure() and recordFlushSuccess() for state management
- Extract readExistingJSONL() to isolate file reading logic
- Extract fetchDirtyIssuesFromDB() to separate DB access
- Extract writeIssuesToJSONL() to handle atomic writes
Command improvements:
- Extract executeLabelCommand() to eliminate duplication in label.go
- Extract addLabelsToIssue() helper for label management
- Replace deprecated strings.Title with manual capitalization
Configuration:
- Add gocyclo exception for test files in .golangci.yml
All tests passing, no functionality changes.
* Fix error handling consistency in auto-import and fallback paths
- Add error checking/warnings for auto-import CRUD operations (UpdateIssue, CreateIssue, AddDependency)
- Add error checking/warnings for auto-import label operations (AddLabel, RemoveLabel)
- Add warning when import hash storage fails (prevents unnecessary re-imports)
- Add proper error handling for UserHomeDir with fallback to current directory
These changes make auto-import error handling consistent with manual operations
and prevent silent failures that could confuse users or cause data inconsistencies.
* Remove invalid version property from golangci-lint config
* Fix linter errors: errcheck, unused, goconst, and misspell
- Fix unchecked error returns in ROLLBACK statements
- Fix unchecked type assertion for status field
- Extract LIMIT SQL constant to reduce duplication
- Fix spelling: cancelled -> canceled
- Remove unused ensureCounterInitialized function
- Remove unused parameter in parallel test goroutine
The skipUpdate flag should only apply to existing DB issues, not to
duplicates within the import batch. Batch duplicates always use
last-one-wins semantics.
- Replace CreateIssue loop with single CreateIssues batch call
- Add in-memory de-duplication for duplicate IDs (last one wins)
- 5-15x faster for bulk imports
- All tests pass
- Test add/remove labels
- Test multiple labels and duplicates
- Test label persistence across updates
- Test event creation for label operations
- Test labels with different issue types
- All 10 test cases passing
Enhance Graphviz dot output with status-based fill colors:
- open: white background (default)
- in_progress: light yellow background
- blocked: light coral background
- closed: light gray background with dimmed text
Node labels show: ID, type, priority, title, and status.
Priority is visible in the label (e.g., [bug P0]) but not color-coded
to keep the visualization clean and focused on status.
Add flexible --format flag to 'bd list' command supporting:
- Built-in presets: 'digraph' (basic 'from to' format) and 'dot' (Graphviz)
- Custom Go templates for dependency output
- Template variables: IssueID, DependsOnID, Type, Issue, Dependency
This enables graph analysis with tools like golang.org/x/tools/cmd/digraph while allowing users to customize output format for their specific needs.
Examples:
bd list --format=digraph | digraph nodes
bd list --format=dot | dot -Tsvg -o deps.svg
bd list --format='{{.IssueID}} -> {{.DependsOnID}} [{{.Type}}]'
- Add closed_at field to Issue type with JSON marshaling
- Implement closed_at timestamp in SQLite storage layer
- Update import/export to handle closed_at field
- Add comprehensive tests for closed_at functionality
- Maintain backward compatibility with existing databases
Amp-Thread-ID: https://ampcode.com/threads/T-f3a7799b-f91e-4432-a690-aae0aed364b3
Co-authored-by: Amp <amp@ampcode.com>
Code review and fixes:
- Increased scanner buffer to 2MB for large JSON lines
- Added line numbers and snippets to parse error messages
- Made non-SQLite fallback conservative (skip import to prevent data loss)
- Improved collision warnings (concise, show first 10 IDs)
- Removed unused autoImportWithoutCollisionDetection function
Status/closed_at invariant enforcement:
- Auto-import now enforces invariant on all creates/updates
- Fixed CreateIssue to respect closed_at field (was ignoring it)
- Closed issues without closed_at get timestamp set automatically
Integration tests:
- TestAutoImportWithCollision: verifies local changes preserved
- TestAutoImportNoCollision: happy path with new issues
- TestAutoImportClosedAtInvariant: enforces invariant
Closes bd-226, bd-230, bd-231
Findings:
- 86/93 closed issues (92%) are missing closed_at timestamps
- All inconsistencies are historical (old issues bd-1 through bd-93)
- No cases of non-closed issues with timestamps
Recommendation: Set closed_at = updated_at for affected issues
Next: Apply cleanup SQL and add constraint
Fixed UNIQUE constraint errors when creating multiple issues in parallel.
Root cause: The previous two-step approach used INSERT OR IGNORE to
pre-initialize counters, followed by an UPSERT to increment. Multiple
concurrent transactions could all execute the INSERT OR IGNORE with the
same initial value, causing them to generate duplicate IDs.
Solution: Replaced with a single atomic UPSERT that:
1. Initializes counter from MAX(existing IDs) if needed
2. Updates counter to MAX(current, max existing) + 1 on conflict
3. Returns the final incremented value
This ensures counters are correctly initialized from existing issues
(fixing lazy init tests) while preventing race conditions through the
BEGIN IMMEDIATE transaction serialization.
Tested with 10 parallel processes - all succeeded with unique IDs.
Also added comprehensive profiling test suite for import performance
investigation (bd-199).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
CRITICAL FIX: Auto-import was silently overwriting local changes without any
collision detection or warning. This caused data loss in multi-developer workflows.
Changes:
- Auto-import now uses sqlite.DetectCollisions() before importing
- Colliding issues are skipped (preserves local changes)
- Warning printed with list of skipped issues and resolution instructions
- Added autoImportWithoutCollisionDetection() fallback for non-SQLite backends
- All tests pass
Impact:
- Local changes are now preserved during git pull
- Users are informed when collisions occur
- Can manually resolve with 'bd import --resolve-collisions'
- No more silent data corruption
Also:
- Removed critical warning banner from README
- Created bd-229 for data recovery investigation
- Closed bd-228 as fixed
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Prevent user confusion when running outdated bd binaries by detecting
version mismatches between the binary and database.
Features:
- Store bd version in metadata table on init
- Check version on every command (PersistentPreRun)
- Warn if binary is outdated with rebuild instructions
- Auto-upgrade database if binary is newer
- Silent operation when versions match
Fixes confusion from bd-182 (auto-export not working with old binary)
Implements bd-197
Files changed:
- cmd/bd/init.go: Store version on init
- cmd/bd/main.go: checkVersionMismatch() + integration
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Ensures exactly one hyphen between prefix and issue number regardless of
whether user provides trailing hyphen.
Before:
bd init --prefix wy- → Issues: wy--1, wy--2 (double hyphen)
bd init --prefix wy → Issues: wy-1, wy-2 (single hyphen)
After:
bd init --prefix wy- → Issues: wy-1, wy-2 (single hyphen)
bd init --prefix wy → Issues: wy-1, wy-2 (single hyphen)
The hyphen is added automatically during ID generation in CreateIssue(),
so the stored prefix should never include trailing hyphens.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Implement `bd create -f file.md` to parse markdown files and create
multiple issues in one command. This enables drafting features in
markdown and converting them to tracked issues.
Features:
- Parse markdown H2 headers (##) as issue titles
- Support all issue fields via H3 sections (### Priority, ### Type, etc.)
- Handle multiple issues per file
- Comprehensive validation and error handling
- Full test coverage with 5 test cases
Closes bd-91 (GH-9)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed bug where PersistentPostRun was clearing isDirty flag before
calling flushToJSONL(), causing the flush to abort immediately.
The fix ensures flushToJSONL() handles the isDirty flag itself,
allowing the JSONL export to complete successfully.
Also added Arch Linux AUR installation instructions to README.
Changes:
- cmd/bd/main.go: Fixed PersistentPostRun flush logic
- README.md: Added Arch Linux (AUR) installation section
- .beads/bd.jsonl: Auto-exported issue bd-169 (init -q flag bug)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Allow BD_ACTOR environment variable to set the default actor name,
providing a cleaner alternative to the --actor flag for automated
workflows.
Priority order for actor determination:
1. --actor flag (highest)
2. BD_ACTOR environment variable
3. USER environment variable
4. "unknown" (fallback)
Updated --actor flag help text to reflect the new environment variable.
Release notes:
- --deps flag for one-command issue creation (#18)
- External reference tracking for linking to external trackers
- Critical bug fixes (dep tree, auto-import, parallel creation)
- Windows build support and Go extension examples
- Community PRs merged (#8, #10, #12, #14, #15, #17)
See CHANGELOG.md for full details.
Handle edge cases in dependency spec parsing:
- Skip empty dependency specs (e.g., from trailing commas)
- Trim whitespace around type and ID (e.g., 'discovered-from: bd-20')
This makes the flag more forgiving of user input errors.
Implements GH-18: Allow creating issues with dependencies in a single command.
Changes:
- Add --deps flag to bd create command
- Support format: 'type:id' or just 'id' (defaults to 'blocks')
- Multiple dependencies supported via comma-separated values
- Example: bd create "Fix bug" --deps discovered-from:bd-20,blocks:bd-15
- Updated README.md and CLAUDE.md with examples
This improves the UX for AI agents by reducing two commands (create + dep add)
to a single command, making discovered-from workflows much smoother.
Fixes#18🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
The auto-import mechanism previously relied on file modification time
comparison between JSONL and DB. This broke in git workflows because
git doesn't preserve original mtimes - pulled files get fresh timestamps.
Changes:
- Added metadata table for internal state storage (separate from config)
- Replaced mtime comparison with SHA256 hash comparison in autoImportIfNewer()
- Store JSONL hash in metadata after both import and export operations
- Added crypto/sha256 and encoding/hex imports
Benefits:
- Git-proof: Works regardless of file timestamps after git pull
- Universal: Works with git, Dropbox, rsync, manual edits
- Efficient: SHA256 is fast (~20ms for 1MB files)
- Accurate: Only imports when content actually changed
- No user action required: Fully automatic and invisible
Testing:
- All existing tests pass
- Manual testing confirms hash-based import triggers on content changes
- Linter warnings are baseline only (documented in LINTING.md)
This fixes issues where parallel agents in git workflows couldn't
find their assigned issues after git pull because auto-import
silently failed due to stale mtimes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Add nullable external_ref TEXT field to link bd issues with external
systems like GitHub Issues, Jira, etc. Includes automatic schema
migration for backward compatibility.
Changes:
- Added external_ref column to issues table with feature-based migration
- Updated Issue struct with ExternalRef *string field
- Added --external-ref flag to bd create and bd update commands
- Updated all SQL queries across the codebase to include external_ref:
- GetIssue, CreateIssue, UpdateIssue, SearchIssues
- GetDependencies, GetDependents, GetDependencyTree
- GetReadyWork, GetBlockedIssues, GetIssuesByLabel
- Added external_ref handling in import/export logic
- Follows existing patterns for nullable fields (sql.NullString)
This enables tracking relationships between bd issues and external
systems without requiring changes to existing databases or JSONL files.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
This commit addresses all critical follow-up issues identified in the
code review of PR #8 (atomic counter implementation).
## bd-64: Fix SyncAllCounters performance bottleneck (P0)
- Replace SyncAllCounters() on every CreateIssue with lazy initialization
- Add ensureCounterInitialized() that only scans prefix-specific issues on first use
- Performance improvement: O(n) full table scan → O(1) for subsequent creates
- Add comprehensive tests in lazy_init_test.go
## bd-65: Add migration for issue_counters table (P1)
- Add migrateIssueCountersTable() similar to migrateDirtyIssuesTable()
- Checks if table is empty and syncs from existing issues on first open
- Handles both fresh databases and migrations from old databases
- Add comprehensive tests in migration_test.go (3 scenarios)
## bd-66: Make import counter sync failure fatal (P1)
- Change SyncAllCounters() failure from warning to fatal error in import
- Prevents ID collisions when counter sync fails
- Data integrity > convenience
## bd-67: Update test comments (P2)
- Update TestMultiProcessIDGeneration comments to reflect fix is in place
- Change "With the bug, we expect errors" → "After the fix, all should succeed"
All tests pass. Atomic counter implementation is now production-ready.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
When importing issues with explicit high IDs (e.g., bd-100), the
issue_counters table wasn't being updated. This caused the next
auto-generated issue to collide with existing IDs (bd-4 instead of bd-101).
Changes:
- Add SyncAllCounters() to scan all issues and update counters atomically
- Add SyncCounterForPrefix() for granular counter synchronization
- Call SyncAllCounters() in import command after creating issues
- Add comprehensive tests for counter sync functionality
- Update TestImportCounterSyncAfterHighID to verify fix
The fix uses a single efficient SQL query to prevent ID collisions
with subsequently auto-generated issues.