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)