From d3b0cbb8b89a31f937e4fd048bea2db089b63dd7 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 16 Dec 2025 16:47:43 -0800 Subject: [PATCH] chore: bump version to 0.30.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Update version in version.go, default.nix, hook templates - Add CHANGELOG.md entry for v0.30.2 - Add v0.30.1 and v0.30.2 to versionChanges in info.go - Remove stale internal/deletions import from integration test (fixes CI failure from bd-fom refactor) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- CHANGELOG.md | 35 ++++ cmd/bd/info.go | 27 +++ cmd/bd/templates/hooks/post-checkout | 2 +- cmd/bd/templates/hooks/post-merge | 2 +- cmd/bd/templates/hooks/pre-commit | 2 +- cmd/bd/templates/hooks/pre-push | 2 +- cmd/bd/version.go | 2 +- default.nix | 2 +- .../importer/importer_integration_test.go | 197 +----------------- 9 files changed, 69 insertions(+), 202 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f23c0f8..cb6de109 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [0.30.2] - 2025-12-16 + +### Added + +- **`bd setup droid`** (GH#598) - Factory.ai (Droid) IDE support + - Configure beads for use with Factory.ai's Droid + - Contributed by @jordanhubbard + +- **Messaging schema fields** (bd-kwro.1) - Foundation for inter-agent messaging + - New `message` issue type for agent-to-agent communication + - New fields: `sender`, `ephemeral`, `replies_to`, `relates_to`, `duplicate_of`, `superseded_by` + - New dependency types: `replies-to`, `relates-to`, `duplicates`, `supersedes` + - Schema migration 019 (automatic on first use) + +### Fixed + +- **Windows build errors** (GH#585) - Fixed gosec lint warnings + - Contributed by @deblasis + +- **Issue ID prefix extraction** - Word-like suffixes (e.g., `my-project-audit`) now parse correctly + - Previously could incorrectly split on word boundaries + +### Removed + +- **Legacy deletions.jsonl code** (bd-fom) - Fully migrated to inline tombstones + - Removed `deletions.jsonl` from git tracking + - All deletion tracking now via inline tombstones in `issues.jsonl` + +### Documentation + +- Windows installation command in upgrade instructions (GH#589) + - Contributed by @alexx-ftw + +- Aligned `bd prime` guidance with skill's hybrid TodoWrite approach + ## [0.30.1] - 2025-12-16 ### Added diff --git a/cmd/bd/info.go b/cmd/bd/info.go index 224bdc9b..4eb91f4b 100644 --- a/cmd/bd/info.go +++ b/cmd/bd/info.go @@ -287,6 +287,33 @@ type VersionChange struct { // versionChanges contains agent-actionable changes for recent versions var versionChanges = []VersionChange{ + { + Version: "0.30.2", + Date: "2025-12-16", + Changes: []string{ + "bd setup droid (GH#598) - Factory.ai (Droid) IDE support", + "Messaging schema fields (bd-kwro.1) - New 'message' issue type, sender/ephemeral/replies_to/relates_to/duplicate_of/superseded_by fields", + "New dependency types: replies-to, relates-to, duplicates, supersedes", + "Windows build fixes (GH#585) - gosec lint errors resolved", + "Issue ID prefix extraction fix - Word-like suffixes now parse correctly", + "Legacy deletions.jsonl code removed (bd-fom) - Fully migrated to inline tombstones", + }, + }, + { + Version: "0.30.1", + Date: "2025-12-16", + Changes: []string{ + "bd reset command (GH#505) - Complete beads removal from a repository", + "bd update --type flag (GH#522) - Change issue type after creation", + "bd q silent mode (GH#540) - Quick-capture without output for scripting", + "bd show displays dependent issue status (GH#583) - Shows status for blocked-by/blocking issues", + "claude.local.md support - Local-only documentation, gitignored by default", + "Auto-disable daemon in git worktrees (GH#567) - Prevents database conflicts", + "Inline tombstones for soft-delete (bd-vw8) - Deleted issues become tombstones in issues.jsonl", + "bd migrate-tombstones command (bd-8f9) - Converts legacy deletions.jsonl to inline tombstones", + "Enhanced Git Worktree Support (bd-737) - Shared .beads database across worktrees", + }, + }, { Version: "0.30.0", Date: "2025-12-15", diff --git a/cmd/bd/templates/hooks/post-checkout b/cmd/bd/templates/hooks/post-checkout index 00d7af7a..9e743c51 100755 --- a/cmd/bd/templates/hooks/post-checkout +++ b/cmd/bd/templates/hooks/post-checkout @@ -1,5 +1,5 @@ #!/bin/sh -# bd-hooks-version: 0.30.1 +# bd-hooks-version: 0.30.2 # # bd (beads) post-checkout hook # diff --git a/cmd/bd/templates/hooks/post-merge b/cmd/bd/templates/hooks/post-merge index fd7e7a71..30a49d26 100755 --- a/cmd/bd/templates/hooks/post-merge +++ b/cmd/bd/templates/hooks/post-merge @@ -1,5 +1,5 @@ #!/bin/sh -# bd-hooks-version: 0.30.1 +# bd-hooks-version: 0.30.2 # # bd (beads) post-merge hook # diff --git a/cmd/bd/templates/hooks/pre-commit b/cmd/bd/templates/hooks/pre-commit index d636905a..7e9fa27f 100755 --- a/cmd/bd/templates/hooks/pre-commit +++ b/cmd/bd/templates/hooks/pre-commit @@ -1,5 +1,5 @@ #!/bin/sh -# bd-hooks-version: 0.30.1 +# bd-hooks-version: 0.30.2 # # bd (beads) pre-commit hook # diff --git a/cmd/bd/templates/hooks/pre-push b/cmd/bd/templates/hooks/pre-push index ed3731d6..6821cfbc 100755 --- a/cmd/bd/templates/hooks/pre-push +++ b/cmd/bd/templates/hooks/pre-push @@ -1,5 +1,5 @@ #!/bin/sh -# bd-hooks-version: 0.30.1 +# bd-hooks-version: 0.30.2 # # bd (beads) pre-push hook # diff --git a/cmd/bd/version.go b/cmd/bd/version.go index 8f64a466..55abceef 100644 --- a/cmd/bd/version.go +++ b/cmd/bd/version.go @@ -14,7 +14,7 @@ import ( var ( // Version is the current version of bd (overridden by ldflags at build time) - Version = "0.30.1" + Version = "0.30.2" // Build can be set via ldflags at compile time Build = "dev" // Commit and branch the git revision the binary was built from (optional ldflag) diff --git a/default.nix b/default.nix index 2ee89ebf..6704a908 100644 --- a/default.nix +++ b/default.nix @@ -1,7 +1,7 @@ { pkgs, self }: pkgs.buildGoModule { pname = "beads"; - version = "0.24.4"; + version = "0.30.2"; src = self; diff --git a/internal/importer/importer_integration_test.go b/internal/importer/importer_integration_test.go index 5fcdfd90..bc6f30ce 100644 --- a/internal/importer/importer_integration_test.go +++ b/internal/importer/importer_integration_test.go @@ -11,7 +11,6 @@ import ( "testing" "time" - "github.com/steveyegge/beads/internal/deletions" "github.com/steveyegge/beads/internal/storage/sqlite" "github.com/steveyegge/beads/internal/types" ) @@ -95,125 +94,6 @@ func TestConcurrentExternalRefUpdates(t *testing.T) { } } -// TestCrossCloneDeletionPropagation tests that deletions propagate across clones -// via the deletions manifest. Simulates: -// 1. Clone A and Clone B both have issue bd-test-123 -// 2. Clone A deletes bd-test-123 (recorded in deletions.jsonl) -// 3. Clone B pulls and imports - issue should be purged from Clone B's DB -func TestCrossCloneDeletionPropagation(t *testing.T) { - ctx := context.Background() - - // Create temp directory structure for "Clone B" (the clone that receives the deletion) - tmpDir := t.TempDir() - beadsDir := filepath.Join(tmpDir, ".beads") - if err := os.MkdirAll(beadsDir, 0755); err != nil { - t.Fatalf("Failed to create .beads dir: %v", err) - } - - // Create database in .beads/ (required for purgeDeletedIssues to find deletions.jsonl) - dbPath := filepath.Join(beadsDir, "beads.db") - store, err := sqlite.New(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to create store: %v", err) - } - defer store.Close() - - if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil { - t.Fatalf("Failed to set prefix: %v", err) - } - - // Create an issue in Clone B's database (simulating it was synced before) - issueToDelete := &types.Issue{ - ID: "bd-test-123", - Title: "Issue that will be deleted in Clone A", - Status: types.StatusOpen, - Priority: 1, - IssueType: types.TypeTask, - } - if err := store.CreateIssue(ctx, issueToDelete, "test"); err != nil { - t.Fatalf("Failed to create issue: %v", err) - } - - // Also create another issue that should NOT be deleted - issueToKeep := &types.Issue{ - ID: "bd-test-456", - Title: "Issue that stays", - Status: types.StatusOpen, - Priority: 1, - IssueType: types.TypeTask, - } - if err := store.CreateIssue(ctx, issueToKeep, "test"); err != nil { - t.Fatalf("Failed to create kept issue: %v", err) - } - - // Verify both issues exist - issues, err := store.SearchIssues(ctx, "", types.IssueFilter{}) - if err != nil { - t.Fatalf("Failed to search issues: %v", err) - } - if len(issues) != 2 { - t.Fatalf("Expected 2 issues before import, got %d", len(issues)) - } - - // Simulate Clone A deleting bd-test-123 by writing to deletions manifest - deletionsPath := filepath.Join(beadsDir, "deletions.jsonl") - record := deletions.DeletionRecord{ - ID: "bd-test-123", - Timestamp: time.Now().UTC(), - Actor: "clone-a-user", - Reason: "test deletion", - } - if err := deletions.AppendDeletion(deletionsPath, record); err != nil { - t.Fatalf("Failed to write deletion record: %v", err) - } - - // Create JSONL with only the kept issue (simulating git pull from remote) - // The deleted issue is NOT in the JSONL (it was removed in Clone A) - jsonlIssues := []*types.Issue{issueToKeep} - - // Import with Options that uses the database path (triggers purgeDeletedIssues) - result, err := ImportIssues(ctx, dbPath, store, jsonlIssues, Options{}) - if err != nil { - t.Fatalf("Import failed: %v", err) - } - - // Verify the purge happened - if result.Purged != 1 { - t.Errorf("Expected 1 purged issue, got %d", result.Purged) - } - if len(result.PurgedIDs) != 1 || result.PurgedIDs[0] != "bd-test-123" { - t.Errorf("Expected purged ID bd-test-123, got %v", result.PurgedIDs) - } - - // Verify database state - finalIssues, err := store.SearchIssues(ctx, "", types.IssueFilter{}) - if err != nil { - t.Fatalf("Failed to search final issues: %v", err) - } - - if len(finalIssues) != 1 { - t.Errorf("Expected 1 issue after import, got %d", len(finalIssues)) - } - - // The kept issue should still exist - keptIssue, err := store.GetIssue(ctx, "bd-test-456") - if err != nil { - t.Fatalf("Failed to get kept issue: %v", err) - } - if keptIssue == nil { - t.Error("Expected bd-test-456 to still exist") - } - - // The deleted issue should be gone - deletedIssue, err := store.GetIssue(ctx, "bd-test-123") - if err != nil { - t.Fatalf("Failed to query deleted issue: %v", err) - } - if deletedIssue != nil { - t.Error("Expected bd-test-123 to be purged") - } -} - // TestLocalUnpushedIssueNotDeleted verifies that local issues that were never // in git are NOT deleted during import (they are local work, not deletions) func TestLocalUnpushedIssueNotDeleted(t *testing.T) { @@ -261,9 +141,6 @@ func TestLocalUnpushedIssueNotDeleted(t *testing.T) { t.Fatalf("Failed to create remote issue: %v", err) } - // Empty deletions manifest (no deletions) - // Don't create the file - LoadDeletions handles missing file gracefully - // JSONL only contains the remote issue (local issue was never exported) jsonlIssues := []*types.Issue{remoteIssue} @@ -273,7 +150,7 @@ func TestLocalUnpushedIssueNotDeleted(t *testing.T) { t.Fatalf("Import failed: %v", err) } - // No purges should happen (not in deletions manifest, not in git history) + // No purges should happen (local issues not in JSONL are preserved) if result.Purged != 0 { t.Errorf("Expected 0 purged issues, got %d (purged: %v)", result.Purged, result.PurgedIDs) } @@ -295,75 +172,3 @@ func TestLocalUnpushedIssueNotDeleted(t *testing.T) { } } -// TestDeletionWithReason verifies that deletion reason is properly recorded -func TestDeletionWithReason(t *testing.T) { - ctx := context.Background() - - tmpDir := t.TempDir() - beadsDir := filepath.Join(tmpDir, ".beads") - if err := os.MkdirAll(beadsDir, 0755); err != nil { - t.Fatalf("Failed to create .beads dir: %v", err) - } - - dbPath := filepath.Join(beadsDir, "beads.db") - store, err := sqlite.New(ctx, dbPath) - if err != nil { - t.Fatalf("Failed to create store: %v", err) - } - defer store.Close() - - if err := store.SetConfig(ctx, "issue_prefix", "bd"); err != nil { - t.Fatalf("Failed to set prefix: %v", err) - } - - // Create issue - issue := &types.Issue{ - ID: "bd-dup-001", - Title: "Duplicate issue", - Status: types.StatusOpen, - Priority: 1, - IssueType: types.TypeTask, - } - if err := store.CreateIssue(ctx, issue, "test"); err != nil { - t.Fatalf("Failed to create issue: %v", err) - } - - // Record deletion with reason "duplicate of bd-orig-001" - deletionsPath := filepath.Join(beadsDir, "deletions.jsonl") - record := deletions.DeletionRecord{ - ID: "bd-dup-001", - Timestamp: time.Now().UTC(), - Actor: "dedup-bot", - Reason: "duplicate of bd-orig-001", - } - if err := deletions.AppendDeletion(deletionsPath, record); err != nil { - t.Fatalf("Failed to write deletion: %v", err) - } - - // Verify record was written with reason - loadResult, err := deletions.LoadDeletions(deletionsPath) - if err != nil { - t.Fatalf("Failed to load deletions: %v", err) - } - - if loaded, ok := loadResult.Records["bd-dup-001"]; !ok { - t.Error("Deletion record not found") - } else { - if loaded.Reason != "duplicate of bd-orig-001" { - t.Errorf("Expected reason 'duplicate of bd-orig-001', got '%s'", loaded.Reason) - } - if loaded.Actor != "dedup-bot" { - t.Errorf("Expected actor 'dedup-bot', got '%s'", loaded.Actor) - } - } - - // Import empty JSONL (issue was deleted) - result, err := ImportIssues(ctx, dbPath, store, []*types.Issue{}, Options{}) - if err != nil { - t.Fatalf("Import failed: %v", err) - } - - if result.Purged != 1 { - t.Errorf("Expected 1 purged, got %d", result.Purged) - } -}