From 3d924f88f333afd558c269fbb1123af02b390de0 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Tue, 23 Dec 2025 16:20:04 -0800 Subject: [PATCH] fix: handle multi-hyphen prefixes correctly (GH#422) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The prefix mismatch detection was using ExtractIssuePrefix() which tries to guess the prefix by analyzing suffix patterns. This failed for multi-hyphen prefixes like "asianops-audit-" when issue IDs had word-like suffixes (e.g., "asianops-audit-test") - the heuristic would fall back to the first hyphen and report "asianops-" as the prefix. Fixed by checking directly if the issue ID starts with the configured prefix using strings.HasPrefix(). This is more reliable than guessing since we know the expected prefix from the database config. Added test case TestImportMultiHyphenPrefix to prevent regression. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- cmd/bd/import_multipart_id_test.go | 86 ++++++++++++++++++++++++++++++ internal/importer/importer.go | 9 +++- 2 files changed, 93 insertions(+), 2 deletions(-) diff --git a/cmd/bd/import_multipart_id_test.go b/cmd/bd/import_multipart_id_test.go index edf0584a..0a1c51a4 100644 --- a/cmd/bd/import_multipart_id_test.go +++ b/cmd/bd/import_multipart_id_test.go @@ -84,6 +84,92 @@ func TestImportMultiPartIDs(t *testing.T) { } } +// TestImportMultiHyphenPrefix tests GH#422: importing with multi-hyphen prefixes +// like "asianops-audit-" should not cause false positive prefix mismatch errors. +func TestImportMultiHyphenPrefix(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, ".beads", "beads.db") + + // Create database with multi-hyphen prefix "asianops-audit" + st := newTestStoreWithPrefix(t, dbPath, "asianops-audit") + + ctx := context.Background() + + // Create issues with hash-like suffixes that could be mistaken for words + // The key is that "test", "task", "demo" look like English words (4+ chars, no digits) + // which previously caused ExtractIssuePrefix to fall back to first hyphen + issues := []*types.Issue{ + { + ID: "asianops-audit-sa0", + Title: "Issue with short hash suffix", + Description: "Short hash suffix should work", + Status: "open", + Priority: 1, + IssueType: "task", + }, + { + ID: "asianops-audit-test", + Title: "Issue with word-like suffix", + Description: "Word-like suffix 'test' was causing false positive", + Status: "open", + Priority: 1, + IssueType: "task", + }, + { + ID: "asianops-audit-task", + Title: "Another word-like suffix", + Description: "Word-like suffix 'task' was also problematic", + Status: "open", + Priority: 1, + IssueType: "task", + }, + { + ID: "asianops-audit-demo", + Title: "Demo issue", + Description: "Word-like suffix 'demo'", + 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) + } + + // GH#422: Should NOT detect prefix mismatch + if result.PrefixMismatch { + t.Errorf("Import incorrectly detected prefix mismatch for multi-hyphen prefix") + t.Logf("Expected prefix: asianops-audit") + t.Logf("Mismatched prefixes detected: %v", result.MismatchPrefixes) + } + + // All issues should be created + if result.Created != 4 { + t.Errorf("Expected 4 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) { diff --git a/internal/importer/importer.go b/internal/importer/importer.go index a19410f2..6adb527a 100644 --- a/internal/importer/importer.go +++ b/internal/importer/importer.go @@ -231,8 +231,13 @@ func handlePrefixMismatch(ctx context.Context, sqliteStore *sqlite.SQLiteStorage var tombstonesToRemove []string for _, issue := range issues { - prefix := utils.ExtractIssuePrefix(issue.ID) - if !allowedPrefixes[prefix] { + // GH#422: Check if issue ID starts with configured prefix directly + // rather than extracting/guessing. This handles multi-hyphen prefixes + // like "asianops-audit-" correctly. + prefixMatches := strings.HasPrefix(issue.ID, configuredPrefix+"-") + if !prefixMatches { + // Extract prefix for error reporting (best effort) + prefix := utils.ExtractIssuePrefix(issue.ID) if issue.IsTombstone() { tombstoneMismatchPrefixes[prefix]++ tombstonesToRemove = append(tombstonesToRemove, issue.ID)