chore: bump version to 0.30.2
- 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 <noreply@anthropic.com>
This commit is contained in:
35
CHANGELOG.md
35
CHANGELOG.md
@@ -7,6 +7,41 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [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
|
## [0.30.1] - 2025-12-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@@ -287,6 +287,33 @@ type VersionChange struct {
|
|||||||
|
|
||||||
// versionChanges contains agent-actionable changes for recent versions
|
// versionChanges contains agent-actionable changes for recent versions
|
||||||
var versionChanges = []VersionChange{
|
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",
|
Version: "0.30.0",
|
||||||
Date: "2025-12-15",
|
Date: "2025-12-15",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# bd-hooks-version: 0.30.1
|
# bd-hooks-version: 0.30.2
|
||||||
#
|
#
|
||||||
# bd (beads) post-checkout hook
|
# bd (beads) post-checkout hook
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# bd-hooks-version: 0.30.1
|
# bd-hooks-version: 0.30.2
|
||||||
#
|
#
|
||||||
# bd (beads) post-merge hook
|
# bd (beads) post-merge hook
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# bd-hooks-version: 0.30.1
|
# bd-hooks-version: 0.30.2
|
||||||
#
|
#
|
||||||
# bd (beads) pre-commit hook
|
# bd (beads) pre-commit hook
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# bd-hooks-version: 0.30.1
|
# bd-hooks-version: 0.30.2
|
||||||
#
|
#
|
||||||
# bd (beads) pre-push hook
|
# bd (beads) pre-push hook
|
||||||
#
|
#
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import (
|
|||||||
|
|
||||||
var (
|
var (
|
||||||
// Version is the current version of bd (overridden by ldflags at build time)
|
// 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 can be set via ldflags at compile time
|
||||||
Build = "dev"
|
Build = "dev"
|
||||||
// Commit and branch the git revision the binary was built from (optional ldflag)
|
// Commit and branch the git revision the binary was built from (optional ldflag)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{ pkgs, self }:
|
{ pkgs, self }:
|
||||||
pkgs.buildGoModule {
|
pkgs.buildGoModule {
|
||||||
pname = "beads";
|
pname = "beads";
|
||||||
version = "0.24.4";
|
version = "0.30.2";
|
||||||
|
|
||||||
src = self;
|
src = self;
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/steveyegge/beads/internal/deletions"
|
|
||||||
"github.com/steveyegge/beads/internal/storage/sqlite"
|
"github.com/steveyegge/beads/internal/storage/sqlite"
|
||||||
"github.com/steveyegge/beads/internal/types"
|
"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
|
// TestLocalUnpushedIssueNotDeleted verifies that local issues that were never
|
||||||
// in git are NOT deleted during import (they are local work, not deletions)
|
// in git are NOT deleted during import (they are local work, not deletions)
|
||||||
func TestLocalUnpushedIssueNotDeleted(t *testing.T) {
|
func TestLocalUnpushedIssueNotDeleted(t *testing.T) {
|
||||||
@@ -261,9 +141,6 @@ func TestLocalUnpushedIssueNotDeleted(t *testing.T) {
|
|||||||
t.Fatalf("Failed to create remote issue: %v", err)
|
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)
|
// JSONL only contains the remote issue (local issue was never exported)
|
||||||
jsonlIssues := []*types.Issue{remoteIssue}
|
jsonlIssues := []*types.Issue{remoteIssue}
|
||||||
|
|
||||||
@@ -273,7 +150,7 @@ func TestLocalUnpushedIssueNotDeleted(t *testing.T) {
|
|||||||
t.Fatalf("Import failed: %v", err)
|
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 {
|
if result.Purged != 0 {
|
||||||
t.Errorf("Expected 0 purged issues, got %d (purged: %v)", result.Purged, result.PurgedIDs)
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user