bd sync: 2025-11-09 14:53:59

This commit is contained in:
Steve Yegge
2025-11-09 14:53:59 -08:00
parent d482d9ea6e
commit 83472aca3d
9 changed files with 288 additions and 143 deletions

File diff suppressed because one or more lines are too long

View File

@@ -45,6 +45,11 @@ func TestExtractPrefix(t *testing.T) {
{"no-number", "no"}, // Has hyphen, so "no" is prefix {"no-number", "no"}, // Has hyphen, so "no" is prefix
{"nonumber", ""}, // No hyphen {"nonumber", ""}, // No hyphen
{"", ""}, {"", ""},
// Multi-part suffixes (bd-fasa regression tests)
{"vc-baseline-test", "vc"},
{"vc-92cl-gate-test", "vc"},
{"bd-multi-part-id", "bd"},
{"prefix-a-b-c-d", "prefix"},
} }
for _, tt := range tests { for _, tt := range tests {

View File

@@ -628,6 +628,7 @@ func attemptAutoMerge(conflictedPath string) error {
} }
// detectPrefixFromIssues extracts the common prefix from issue IDs // detectPrefixFromIssues extracts the common prefix from issue IDs
// Only considers the first hyphen, so "vc-baseline-test" -> "vc"
func detectPrefixFromIssues(issues []*types.Issue) string { func detectPrefixFromIssues(issues []*types.Issue) string {
if len(issues) == 0 { if len(issues) == 0 {
return "" return ""
@@ -636,10 +637,10 @@ func detectPrefixFromIssues(issues []*types.Issue) string {
// Count prefix occurrences // Count prefix occurrences
prefixCounts := make(map[string]int) prefixCounts := make(map[string]int)
for _, issue := range issues { for _, issue := range issues {
// Extract prefix from issue ID (e.g., "bd-123" -> "bd") // Extract prefix from issue ID using first hyphen only
parts := strings.SplitN(issue.ID, "-", 2) idx := strings.Index(issue.ID, "-")
if len(parts) == 2 { if idx > 0 {
prefixCounts[parts[0]]++ prefixCounts[issue.ID[:idx]]++
} }
} }

View File

@@ -0,0 +1,137 @@
package main
import (
"context"
"path/filepath"
"testing"
"github.com/steveyegge/beads/internal/types"
)
// TestImportMultiPartIDs tests that issue IDs with hyphens in the suffix
// (like "vc-baseline-test") are correctly recognized as having prefix "vc"
// and not treated as having different prefixes like "vc-baseline-"
func TestImportMultiPartIDs(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, ".beads", "beads.db")
// Create database with "vc" prefix
st := newTestStoreWithPrefix(t, dbPath, "vc")
ctx := context.Background()
// Create issues with multi-part IDs
issues := []*types.Issue{
{
ID: "vc-baseline-test",
Title: "Baseline test issue",
Description: "Issue with hyphenated suffix",
Status: "open",
Priority: 1,
IssueType: "task",
},
{
ID: "vc-92cl-gate-test",
Title: "Gate test issue",
Description: "Another issue with hyphenated suffix",
Status: "open",
Priority: 1,
IssueType: "task",
},
{
ID: "vc-test",
Title: "Simple test issue",
Description: "Issue without hyphenated suffix",
Status: "open",
Priority: 1,
IssueType: "task",
},
}
// Import should succeed without prefix mismatch errors
opts := ImportOptions{
DryRun: false,
SkipUpdate: false,
Strict: false,
}
result, err := importIssuesCore(ctx, dbPath, st, issues, opts)
if err != nil {
t.Fatalf("Import failed: %v", err)
}
// Should not detect prefix mismatch
if result.PrefixMismatch {
t.Errorf("Import incorrectly detected prefix mismatch")
t.Logf("Mismatched prefixes: %v", result.MismatchPrefixes)
}
// All issues should be created
if result.Created != 3 {
t.Errorf("Expected 3 issues created, got %d", result.Created)
}
// Verify issues exist in database
for _, issue := range issues {
dbIssue, err := st.GetIssue(ctx, issue.ID)
if err != nil {
t.Errorf("Failed to get issue %s: %v", issue.ID, err)
continue
}
if dbIssue.Title != issue.Title {
t.Errorf("Issue %s title mismatch: got %q, want %q", issue.ID, dbIssue.Title, issue.Title)
}
}
}
// TestDetectPrefixFromIssues tests the detectPrefixFromIssues function
// with multi-part IDs
func TestDetectPrefixFromIssues(t *testing.T) {
tests := []struct {
name string
issues []*types.Issue
expected string
}{
{
name: "simple IDs",
issues: []*types.Issue{
{ID: "bd-1"},
{ID: "bd-2"},
{ID: "bd-3"},
},
expected: "bd",
},
{
name: "multi-part IDs",
issues: []*types.Issue{
{ID: "vc-baseline-test"},
{ID: "vc-92cl-gate-test"},
{ID: "vc-test"},
},
expected: "vc",
},
{
name: "mixed multi-part IDs",
issues: []*types.Issue{
{ID: "prefix-a-b-c"},
{ID: "prefix-x-y-z"},
{ID: "prefix-simple"},
},
expected: "prefix",
},
{
name: "empty list",
issues: []*types.Issue{},
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := detectPrefixFromIssues(tt.issues)
if result != tt.expected {
t.Errorf("detectPrefixFromIssues() = %q, want %q", result, tt.expected)
}
})
}
}

View File

@@ -241,12 +241,13 @@ Examples:
} }
// extractPrefix extracts the prefix from an issue ID (e.g., "bd-123" -> "bd") // extractPrefix extracts the prefix from an issue ID (e.g., "bd-123" -> "bd")
// Only considers the first hyphen, so "vc-baseline-test" -> "vc"
func extractPrefix(issueID string) string { func extractPrefix(issueID string) string {
parts := strings.Split(issueID, "-") idx := strings.Index(issueID, "-")
if len(parts) > 0 { if idx <= 0 {
return parts[0] return ""
} }
return "" return issueID[:idx]
} }
// VersionChange represents agent-relevant changes for a specific version // VersionChange represents agent-relevant changes for a specific version

View File

@@ -179,7 +179,7 @@ func detectPrefix(_ string, memStore *memory.MemoryStorage) (string, error) {
// extractIssuePrefix extracts the prefix from an issue ID like "bd-123" -> "bd" // extractIssuePrefix extracts the prefix from an issue ID like "bd-123" -> "bd"
func extractIssuePrefix(issueID string) string { func extractIssuePrefix(issueID string) string {
idx := strings.LastIndex(issueID, "-") idx := strings.Index(issueID, "-")
if idx <= 0 { if idx <= 0 {
return "" return ""
} }

View File

@@ -18,7 +18,7 @@ func TestExtractIssuePrefix(t *testing.T) {
{"standard ID", "bd-123", "bd"}, {"standard ID", "bd-123", "bd"},
{"custom prefix", "myproject-456", "myproject"}, {"custom prefix", "myproject-456", "myproject"},
{"hash ID", "bd-abc123def", "bd"}, {"hash ID", "bd-abc123def", "bd"},
{"hyphenated prefix", "alpha-beta-1", "alpha-beta"}, {"multi-part suffix", "alpha-beta-1", "alpha"}, // Only first hyphen (bd-fasa)
{"no hyphen", "nohyphen", ""}, {"no hyphen", "nohyphen", ""},
{"empty", "", ""}, {"empty", "", ""},
} }

View File

@@ -322,9 +322,9 @@ func TestExtractIssuePrefix(t *testing.T) {
expected: "bd", expected: "bd",
}, },
{ {
name: "hyphenated prefix", name: "multi-part suffix",
issueID: "alpha-beta-1", issueID: "alpha-beta-1",
expected: "alpha-beta", expected: "alpha", // Only first hyphen (bd-fasa)
}, },
} }

View File

@@ -6,8 +6,9 @@ import (
) )
// ExtractIssuePrefix extracts the prefix from an issue ID like "bd-123" -> "bd" // ExtractIssuePrefix extracts the prefix from an issue ID like "bd-123" -> "bd"
// Only considers the first hyphen, so "vc-baseline-test" -> "vc"
func ExtractIssuePrefix(issueID string) string { func ExtractIssuePrefix(issueID string) string {
idx := strings.LastIndex(issueID, "-") idx := strings.Index(issueID, "-")
if idx <= 0 { if idx <= 0 {
return "" return ""
} }