diff --git a/.beads/beads.jsonl b/.beads/beads.jsonl index f2a571b7..aae1cdd8 100644 --- a/.beads/beads.jsonl +++ b/.beads/beads.jsonl @@ -134,7 +134,7 @@ {"id":"bd-a5a8bec0","content_hash":"fb509cc456ea9de6000129ad7780e417d2f06361e577738709a9ccf1c3b1f3b1","title":"bd find-duplicates - AI-powered duplicate detection","description":"Find semantically duplicate issues.\n\nApproaches:\n1. Mechanical: Exact title/description matching\n2. Embeddings: Cosine similarity (cheap, scalable)\n3. AI: LLM-based semantic comparison (expensive, accurate)\n\nUses embeddings by default for \u003e100 issues.\n\nFiles: cmd/bd/find_duplicates.go (new)","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T19:42:29.853269-07:00","updated_at":"2025-10-30T17:12:58.193143-07:00","closed_at":"2025-10-29T16:43:30.367455-07:00"} {"id":"bd-a9699011","content_hash":"87d969cf57e247ebfac4f052a9ecbd1254bc55070b87b5ffb78a2b6ee2afddb6","title":"GH#146: No color showing in terminal for some users","description":"User reports color not working in macOS (Taho 26.0.1) with iTerm 3.6.4 and Terminal.app, despite color working elsewhere in terminal. Python rich and printf escape codes work.\n\nNeed to investigate:\n- Is NO_COLOR env var set?\n- Terminal type detection?\n- fatih/color library configuration\n- Does bd list show colors? bd ready? bd init?\n- What's the output of: echo $TERM, echo $NO_COLOR","status":"open","priority":2,"issue_type":"bug","created_at":"2025-10-24T22:26:36.22163-07:00","updated_at":"2025-10-30T17:12:58.2142-07:00","external_ref":"github:146"} {"id":"bd-aec5439f","content_hash":"9ad0242285e9ef9b326468b9be34f533f27cbbaa0c698607cca0cd6228016d2c","title":"Update LINTING.md with current baseline","description":"After cleanup, document the remaining acceptable baseline in LINTING.md so we can track regression.","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-27T18:53:10.38679-07:00","updated_at":"2025-10-30T17:12:58.194901-07:00"} -{"id":"bd-b245","content_hash":"098a736934abbfd9ce0bf20a31c24848c4415b03b289cbdc53ba3f475ae3fb24","title":"Add migration registry and simplify New()","description":"Create migrations.go with Migration type and registry. Change New() to: openDB -\u003e initSchema -\u003e RunMigrations(db). This removes 8+ separate migrate functions from New().","status":"open","priority":1,"issue_type":"task","created_at":"2025-11-01T11:41:14.862623-07:00","updated_at":"2025-11-01T11:41:14.862623-07:00"} +{"id":"bd-b245","content_hash":"e160e195ded0b66cadae424da7558d201a1eb506a7742875dbee42984037c8a6","title":"Add migration registry and simplify New()","description":"Create migrations.go with Migration type and registry. Change New() to: openDB -\u003e initSchema -\u003e RunMigrations(db). This removes 8+ separate migrate functions from New().","status":"closed","priority":1,"issue_type":"task","created_at":"2025-11-01T11:41:14.862623-07:00","updated_at":"2025-11-02T12:40:45.528212-08:00","closed_at":"2025-11-02T12:40:45.528212-08:00"} {"id":"bd-b47c034e","content_hash":"133dfd651d402bb95928091138c77a57b2f3f349587962c744209a534fb800a6","title":"Address gosec security warnings (102 issues)","description":"Security linter warnings: file permissions (0755 should be 0750), G304 file inclusion via variable, G204 subprocess launches. Many are false positives but should be reviewed.","design":"Review each gosec warning. Add exclusions for legitimate cases to .golangci.yml. Fix real security issues (overly permissive file modes).","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-25T13:47:10.719134-07:00","updated_at":"2025-10-30T17:12:58.216521-07:00"} {"id":"bd-b501fcc1","content_hash":"323c3b4f2e53d707ce73e75a357bbb4e320327bea00d0b010c3dd09d1e6555cf","title":"Unit tests for Debouncer","description":"Test debouncer batches multiple triggers into single action. Test timer reset on subsequent triggers. Test cancel during wait. Test thread safety.","status":"closed","priority":1,"issue_type":"task","created_at":"2025-10-29T19:42:29.86146-07:00","updated_at":"2025-10-31T17:54:06.880513-07:00","closed_at":"2025-10-31T17:54:06.880513-07:00"} {"id":"bd-b55e2ac2","content_hash":"44122b61b1dcd06407ecf36f57577ea72c5df6dc8cc2a8c1b173b37d16a10267","title":"Fix autoimport tests for content-hash collision scoring","description":"## Overview\nThree autoimport tests are failing after bd-cbed9619.4 because they expect behavior based on the old reference-counting collision resolution, but the system now uses deterministic content-hash scoring.\n\n## Failing Tests\n1. `TestAutoImportMultipleCollisionsRemapped` - expects local versions preserved\n2. `TestAutoImportAllCollisionsRemapped` - expects local versions preserved \n3. `TestAutoImportCollisionRemapMultipleFields` - expects specific collision resolution behavior\n\n## Root Cause\nThese tests were written when ScoreCollisions used reference counting to determine which version to keep. Now it uses content-hash comparison (introduced in commit 2e87329), which produces different but deterministic results.\n\n## Example\nOld behavior: Issue with more references would be kept\nNew behavior: Issue with lexicographically lower content hash is kept\n\n## Solution\nUpdate each test to:\n1. Verify the new content-hash based behavior is correct\n2. Check that the remapped issue (not necessarily local/remote) has the expected content\n3. Ensure dependencies are preserved on the correct remapped issue\n\n## Acceptance Criteria\n- All three autoimport tests pass\n- Tests verify content-hash determinism (same collision always resolves the same way)\n- Tests check dependency preservation on remapped issues\n- Test documentation explains content-hash scoring expectations\n\n## Files to Modify\n- `cmd/bd/autoimport_collision_test.go`\n\n## Testing\nRun: `go test ./cmd/bd -run \"TestAutoImport.*Collision\" -v`","status":"open","priority":2,"issue_type":"task","created_at":"2025-10-28T19:17:28.358028-07:00","updated_at":"2025-10-30T17:12:58.179059-07:00"} diff --git a/internal/storage/sqlite/migrations.go b/internal/storage/sqlite/migrations.go index 7ef746e7..367d1edb 100644 --- a/internal/storage/sqlite/migrations.go +++ b/internal/storage/sqlite/migrations.go @@ -8,6 +8,37 @@ import ( "github.com/steveyegge/beads/internal/types" ) +// Migration represents a single database migration +type Migration struct { + Name string + Func func(*sql.DB) error +} + +// migrations is the ordered list of all migrations to run +// Migrations are run in order during database initialization +var migrations = []Migration{ + {"dirty_issues_table", migrateDirtyIssuesTable}, + {"external_ref_column", migrateExternalRefColumn}, + {"composite_indexes", migrateCompositeIndexes}, + {"closed_at_constraint", migrateClosedAtConstraint}, + {"compaction_columns", migrateCompactionColumns}, + {"snapshots_table", migrateSnapshotsTable}, + {"compaction_config", migrateCompactionConfig}, + {"compacted_at_commit_column", migrateCompactedAtCommitColumn}, + {"export_hashes_table", migrateExportHashesTable}, + {"content_hash_column", migrateContentHashColumn}, +} + +// RunMigrations executes all registered migrations in order +func RunMigrations(db *sql.DB) error { + for _, migration := range migrations { + if err := migration.Func(db); err != nil { + return fmt.Errorf("migration %s failed: %w", migration.Name, err) + } + } + return nil +} + func migrateDirtyIssuesTable(db *sql.DB) error { // Check if dirty_issues table exists var tableName string diff --git a/internal/storage/sqlite/sqlite.go b/internal/storage/sqlite/sqlite.go index f36ab3c7..17bac9f6 100644 --- a/internal/storage/sqlite/sqlite.go +++ b/internal/storage/sqlite/sqlite.go @@ -71,54 +71,9 @@ func New(path string) (*SQLiteStorage, error) { return nil, fmt.Errorf("failed to initialize schema: %w", err) } - // Migrate existing databases to add dirty_issues table if missing - if err := migrateDirtyIssuesTable(db); err != nil { - return nil, fmt.Errorf("failed to migrate dirty_issues table: %w", err) - } - - // Migrate existing databases to add external_ref column if missing - if err := migrateExternalRefColumn(db); err != nil { - return nil, fmt.Errorf("failed to migrate external_ref column: %w", err) - } - - // Migrate existing databases to add composite index on dependencies - if err := migrateCompositeIndexes(db); err != nil { - return nil, fmt.Errorf("failed to migrate composite indexes: %w", err) - } - - // Migrate existing databases to add status/closed_at CHECK constraint - if err := migrateClosedAtConstraint(db); err != nil { - return nil, fmt.Errorf("failed to migrate closed_at constraint: %w", err) - } - - // Migrate existing databases to add compaction columns - if err := migrateCompactionColumns(db); err != nil { - return nil, fmt.Errorf("failed to migrate compaction columns: %w", err) - } - - // Migrate existing databases to add issue_snapshots table - if err := migrateSnapshotsTable(db); err != nil { - return nil, fmt.Errorf("failed to migrate snapshots table: %w", err) - } - - // Migrate existing databases to add compaction config defaults - if err := migrateCompactionConfig(db); err != nil { - return nil, fmt.Errorf("failed to migrate compaction config: %w", err) - } - - // Migrate existing databases to add compacted_at_commit column - if err := migrateCompactedAtCommitColumn(db); err != nil { - return nil, fmt.Errorf("failed to migrate compacted_at_commit column: %w", err) - } - - // Migrate existing databases to add export_hashes table (bd-164) - if err := migrateExportHashesTable(db); err != nil { - return nil, fmt.Errorf("failed to migrate export_hashes table: %w", err) - } - - // Migrate existing databases to add content_hash column (bd-95) - if err := migrateContentHashColumn(db); err != nil { - return nil, fmt.Errorf("failed to migrate content_hash column: %w", err) + // Run all migrations + if err := RunMigrations(db); err != nil { + return nil, err } // Convert to absolute path for consistency @@ -133,11 +88,9 @@ func New(path string) (*SQLiteStorage, error) { }, nil } -// migrateDirtyIssuesTable checks if the dirty_issues table exists and creates it if missing. -// This ensures existing databases created before the incremental export feature get migrated automatically. // REMOVED (bd-8e05): getNextIDForPrefix and AllocateNextID - sequential ID generation // no longer needed with hash-based IDs -// Migration functions moved to migrations.go (bd-fc2d) +// Migration functions moved to migrations.go (bd-fc2d, bd-b245) // getNextChildNumber atomically generates the next child number for a parent ID // Uses the child_counters table for atomic, cross-process child ID generation