Commit Graph

18 Commits

Author SHA1 Message Date
ruby
442ad0f0e5 fix(sqlite): add retry logic to transaction entry points (GH#1272)
Update withTx to use BEGIN IMMEDIATE with exponential backoff retry
on SQLITE_BUSY errors. This prevents "database is locked" failures
during concurrent operations (daemon + CLI, multi-agent workflows).

Changes:
- withTx now uses beginImmediateWithRetry (same pattern as RunInTransaction)
- Add dbExecutor interface for helper functions that work with both
  *sql.Tx and *sql.Conn
- Update all withTx callers to use *sql.Conn
- Refactor DeleteIssue to use withTx (fixes the specific error in auto-import)
- Update markIssuesDirtyTx to accept dbExecutor interface

Affected paths:
- MarkIssuesDirty, ClearDirtyIssuesByID (dirty.go)
- AddDependency, RemoveDependency (dependencies.go)
- executeLabelOperation (labels.go)
- AddComment (events.go)
- ApplyCompaction (compact.go)
- DeleteIssue (queries.go)

Note: Some direct BeginTx calls in queries.go (CloseIssue, UpdateIssue,
ReopenIssue, DeleteIssues) still use the old pattern and could be
refactored in a follow-up.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 13:43:30 -08:00
beads/crew/dave
ec1a32b9a8 fix(storage): add reconnectMu RLock protection to prevent race condition (#1054)
Add missing reconnectMu.RLock() protection to storage methods that were
vulnerable to the same race condition fixed in GH#607. The FreshnessChecker
can trigger reconnect() which closes s.db while queries are in flight,
causing "database is closed" errors during daemon export operations.

Protected methods:
- labels.go: GetLabelsForIssues (GetLabels intentionally unprotected - called from GetIssue which holds lock)
- comments.go: GetIssueComments, GetCommentsForIssues
- dependencies.go: GetDependencyCounts, GetDependencyRecords, GetAllDependencyRecords, GetDependencyTree, loadDependencyGraph
- config.go: SetConfig, GetConfig, GetAllConfig, DeleteConfig, SetMetadata, GetMetadata
- dirty.go: MarkIssueDirty, GetDirtyIssues, GetDirtyIssueHash, GetDirtyIssueCount
- events.go: GetEvents, GetStatistics, GetMoleculeProgress
- hash.go: All hash methods
- hash_ids.go: GetNextChildID, ensureChildCounterUpdated (getNextChildNumber unprotected - called internally)

Internal helpers called from already-locked contexts intentionally omit
RLock to avoid deadlock (Go's RWMutex doesn't support recursive locking).

Fixes: bd-vx7fp

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

Executed-By: beads/crew/dave
Rig: beads
Role: crew
2026-01-12 18:29:42 -08:00
beads/crew/fang
0a96b10bba feat: add bd mol progress command for efficient molecule monitoring (bd-8xnf)
Adds a new `bd mol progress` command that shows molecule progress using
indexed queries instead of loading all steps into memory. This makes it
suitable for mega-molecules with millions of steps.

Features:
- Efficient SQL-based counting via idx_dependencies_depends_on_type index
- Progress display: completed / total (percentage)
- Current step identification
- Rate calculation from closure timestamps
- ETA estimation
- JSON output support

New storage interface method: GetMoleculeProgress(ctx, moleculeID)

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-31 12:35:41 -08:00
Steve Yegge
7d1ee6d3e9 feat: Add 'hooked' status for GUPP work assignment (bd-s00m)
Separates semantics of 'pinned' (identity records) from work-on-hook:
- 'pinned' = domain table / identity record (agents, roles) - non-blocking
- 'hooked' = work on agent's hook (GUPP-driven) - blocks dependents

Changes:
- Add StatusHooked constant to types.go
- Update all blocking queries to include 'hooked' status
- Add cyan styling for 'hooked' in UI output
- Create migration 032 to convert pinned work items to hooked

Generated with Claude Code

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-28 22:37:04 -08:00
Steve Yegge
5d2daf2e1e fix(pin): add 'pinned' field to allowed update fields (gt-zr0a)
The 'bd pin' command was failing with "invalid field for update: pinned"
because the pinned field was missing from allowedUpdateFields.

Fixes:
- Add 'pinned' to allowedUpdateFields in queries.go
- Update importer to include pinned field in updates during import
- Add equalBool comparator for IssueDataChanged to detect pinned changes
- Fix stats query to count pinned=1 instead of status='pinned'
- Fix pinIndicator in list.go to check issue.Pinned instead of status

This unblocks gt mail --pinned functionality.

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 19:57:16 -08:00
Steve Yegge
e778b3f648 feat(status): add deferred status for icebox issues (bd-4jr)
Add 'deferred' as a valid issue status for issues that are deliberately
put on ice - not blocked by dependencies, just postponed for later.

Changes:
- Add StatusDeferred constant and update IsValid() validation
- Add DeferredIssues to Statistics struct with counting in both SQLite
  and memory storage
- Add 'bd defer' command to set status to deferred
- Add 'bd undefer' command to restore status to open
- Update help text across list, search, count, dep, stale, and config
- Update MCP server models and tools to accept deferred status
- Add deferred to blocker status checks (schema, cache, ready, compact)
- Add StatusDeferred to public API exports (beads.go, internal/beads)
- Add snowflake styling for deferred in dep tree and graph views

Semantics:
- deferred vs blocked: deferred is a choice, blocked is forced
- deferred vs closed: deferred will be revisited, closed is done
- Deferred issues excluded from 'bd ready' (already works since
  default filter only includes open/in_progress)
- Deferred issues still block dependents (they are not done!)
- Deferred issues visible in 'bd list' and 'bd stale'

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-20 14:24:48 -08:00
Steve Yegge
f4f51da007 feat(types): add StatusPinned for persistent beads (bd-6v2)
Add pinned status for beads that should stay open indefinitely:
- Add StatusPinned constant and update IsValid()
- Add PinnedIssues count to Statistics struct
- Protect pinned issues from bd close (requires --force)
- Show pinned count in bd stats output

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-18 21:55:54 -08:00
Steve Yegge
5c49c25e9e feat(tombstone): add P2 code review improvements (bd-saa, bd-1bu, bd-nyt)
- Add partial index on deleted_at for efficient TTL queries
- Exclude tombstones from SearchIssues by default (new IncludeTombstones filter)
- Report tombstone count separately in GetStatistics
- Display tombstone count in bd stats output

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-05 15:48:46 -08:00
Steve Yegge
345766badc Fix FK constraint failures in AddComment and ApplyCompaction (bd-5arw)
Amp-Thread-ID: https://ampcode.com/threads/T-4358e6e4-28ea-4ed7-ba3f-3da39072e169
Co-authored-by: Amp <amp@ampcode.com>
2025-11-20 18:55:10 -05:00
Steve Yegge
7d6d64d2c1 Refactor: Replace manual transaction handling with withTx() helper
Fixes bd-1b0a

Changes:
- Added withTx() helper in util.go for cleaner transaction handling
- Kept ExecInTransaction() as deprecated wrapper for backward compatibility
- Refactored all manual BEGIN/COMMIT/ROLLBACK blocks to use withTx():
  - events.go: AddComment
  - dirty.go: MarkIssuesDirty, ClearDirtyIssuesByID
  - labels.go: executeLabelOperation
  - dependencies.go: AddDependency, RemoveDependency
  - compact.go: ApplyCompaction

All tests pass.

Amp-Thread-ID: https://ampcode.com/threads/T-dfacc972-f6c8-4bb3-8997-faa079b5d070
Co-authored-by: Amp <amp@ampcode.com>
2025-11-02 12:55:47 -08:00
Steve Yegge
648ecfafe7 Address gosec security warnings (bd-102)
- Enable gosec linter in .golangci.yml
- Tighten file permissions: 0755→0750 for directories, 0644→0600 for configs
- Git hooks remain 0700 (executable, user-only access)
- Add #nosec comments for safe cases with justifications:
  - G204: Safe subprocess launches (git show, bd daemon)
  - G304: File inclusions with controlled paths
  - G201: SQL formatting with controlled column names
  - G115: Integer conversions with controlled values

All gosec warnings resolved (20→0). All tests passing.

Amp-Thread-ID: https://ampcode.com/threads/T-d7166b9e-cbbe-4c7b-9e48-3df36b20f0d0
Co-authored-by: Amp <amp@ampcode.com>
2025-10-26 22:48:19 -07:00
Steve Yegge
b855c444d4 Enable errcheck linter and fix all production code warnings
- Enabled errcheck linter (previously disabled)
- Set tests: false in .golangci.yml to focus on production code
- Fixed 27 errcheck warnings using Go best practices:
  * Database resources: defer func() { _ = rows.Close() }()
  * Transaction rollbacks: defer func() { _ = tx.Rollback() }()
  * Best-effort closers: _ = store.Close(), _ = client.Close()
  * File writes: proper error checking on Close()
  * Interactive input: handle EOF gracefully
  * File ops: ignore ENOENT on os.Remove()
- All tests pass
- Closes bd-58

Amp-Thread-ID: https://ampcode.com/threads/T-57c9afd3-9adf-40c2-8be7-3e493d200361
Co-authored-by: Amp <amp@ampcode.com>
2025-10-25 18:44:38 -07:00
Steve Yegge
14c744861c Add epic closure management commands (fixes #62)
- Add 'bd epic status' to show epic completion with child progress
- Add 'bd epic close-eligible' to bulk-close completed epics
- Add GetEpicsEligibleForClosure() storage method
- Update 'bd stats' to show count of epics ready to close
- Add EpicStatus type for tracking epic/child relationships
- Support --eligible-only, --dry-run, and --json flags
- Fix golangci-lint config version requirement

Addresses GitHub issue #62 - epics now have visibility and
management tools for closure when all children are complete.

Amp-Thread-ID: https://ampcode.com/threads/T-e8ac3f48-f0cf-4858-8e8f-aace2481c30d
Co-authored-by: Amp <amp@ampcode.com>
2025-10-17 13:50:20 -07:00
Joshua Shanks
cf4f11cff7 Fix error handling consistency in auto-import and fallback paths (#47)
* 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
2025-10-15 23:34:33 -07:00
Joshua Shanks
3f42cab0d1 fix: Handle NULL values in GetStatistics for empty databases (#37)
When GetStatistics is called on an empty database, SQL SUM() returns NULL
which causes a scan error when converting to int. Wrap SUM expressions with
COALESCE to return 0 instead of NULL.

Add TestGetStatistics to verify both empty and populated database cases.

Fixes issue where `bd stats` and MCP stats tool crash on fresh databases.

Signed-off-by: Joshua Shanks <jjshanks@gmail.com>
2025-10-15 00:30:36 -07:00
Steve Yegge
bafb2801c5 Implement incremental JSONL export with dirty issue tracking
Optimize auto-flush by tracking which issues have changed instead of
exporting the entire database on every flush. For large projects with
1000+ issues, this provides significant performance improvements.

Changes:
- Add dirty_issues table to schema with issue_id and marked_at columns
- Implement dirty tracking functions in new dirty.go file:
  * MarkIssueDirty() - Mark single issue as needing export
  * MarkIssuesDirty() - Batch mark multiple issues efficiently
  * GetDirtyIssues() - Query which issues need export
  * ClearDirtyIssues() - Clear tracking after successful export
  * GetDirtyIssueCount() - Monitor dirty issue count
- Update all CRUD operations to mark affected issues as dirty:
  * CreateIssue, UpdateIssue, DeleteIssue
  * AddDependency, RemoveDependency (marks both issues)
  * AddLabel, RemoveLabel, AddEvent
- Modify export to support incremental mode:
  * Add --incremental flag to export only dirty issues
  * Used by auto-flush for performance
  * Full export still available without flag
- Add Storage interface methods for dirty tracking

Performance impact: With incremental export, large databases only write
changed issues instead of regenerating entire JSONL file on every
auto-flush.

Closes bd-39

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-14 00:17:23 -07:00
Steve Yegge
15afb5ad17 Implement JSONL export/import and shift to text-first architecture
This is a fundamental architectural shift from binary SQLite to JSONL as
the source of truth for git workflows.

## New Features

- `bd export --format=jsonl` - Export issues to JSON Lines format
- `bd import` - Import issues from JSONL (create new, update existing)
- `--skip-existing` flag for import to only create new issues

## Architecture Change

**Before:** Binary SQLite database committed to git
**After:** JSONL text files as source of truth, SQLite as ephemeral cache

Benefits:
- Git-friendly text format with clean diffs
- AI-resolvable merge conflicts (append-only is 95% conflict-free)
- Human-readable issue tracking in git
- No binary merge conflicts

## Documentation

- Updated README with JSONL-first workflow and git hooks
- Added TEXT_FORMATS.md analyzing JSONL vs CSV vs binary
- Updated GIT_WORKFLOW.md with historical context
- .gitignore now excludes *.db, includes .beads/*.jsonl

## Implementation Details

- Export sorts issues by ID for consistent diffs
- Import handles both creates and updates atomically
- Proper handling of pointer fields (EstimatedMinutes)
- All tests passing

## Breaking Changes

- Database files (*.db) should now be gitignored
- Use export/import workflow for git collaboration
- Git hooks recommended for automation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-12 01:17:50 -07:00
Steve Yegge
704515125d Initial commit: Beads issue tracker with security fixes
Core features:
- Dependency-aware issue tracking with SQLite backend
- Ready work detection (issues with no open blockers)
- Dependency tree visualization
- Cycle detection and prevention
- Full audit trail
- CLI with colored output

Security and correctness fixes applied:
- Fixed SQL injection vulnerability in UpdateIssue (whitelisted fields)
- Fixed race condition in ID generation (added mutex)
- Fixed cycle detection to return full paths (not just issue IDs)
- Added cycle prevention in AddDependency (validates before commit)
- Added comprehensive input validation (priority, status, types, etc.)
- Fixed N+1 query in GetBlockedIssues (using GROUP_CONCAT)
- Improved query building in GetReadyWork (proper string joining)
- Fixed P0 priority filter bug (using Changed() instead of value check)

All critical and major issues from code review have been addressed.

🤖 Generated with Claude Code
2025-10-11 20:07:36 -07:00