feat: Handle FOREIGN KEY constraint violations gracefully during import (bd-koab)

When importing JSONL after merges that include deletions, FK constraint
violations can occur if an issue references a deleted issue. Previously,
import would fail completely. Now it continues and reports skipped dependencies.

Changes:
- Add SkippedDependencies field to Result/ImportResult structs
- Update importDependencies() to detect FK violations using IsForeignKeyConstraintError()
- Log warnings for each skipped dependency with issue IDs and type
- Continue importing remaining dependencies instead of failing
- Display summary of all skipped dependencies at end of import

Example output:
  Warning: Skipping dependency due to missing reference: bd-b → bd-a (blocks)

  ⚠️  Warning: Skipped 2 dependencies due to missing references:
    - bd-b → bd-a (blocks)
    - bd-c → bd-a (parent-child)

  This can happen after merges that delete issues referenced by other issues.
  The import continued successfully - you may want to review the skipped dependencies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-11-23 23:32:34 -08:00
parent e2e04c011a
commit d45cff5085
4 changed files with 72 additions and 47 deletions

View File

@@ -169,16 +169,17 @@ type ImportOptions struct {
// ImportResult contains statistics about the import operation
type ImportResult struct {
Created int // New issues created
Updated int // Existing issues updated
Unchanged int // Existing issues that matched exactly (idempotent)
Skipped int // Issues skipped (duplicates, errors)
Collisions int // Collisions detected
IDMapping map[string]string // Mapping of remapped IDs (old -> new)
CollisionIDs []string // IDs that collided
PrefixMismatch bool // Prefix mismatch detected
ExpectedPrefix string // Database configured prefix
MismatchPrefixes map[string]int // Map of mismatched prefixes to count
Created int // New issues created
Updated int // Existing issues updated
Unchanged int // Existing issues that matched exactly (idempotent)
Skipped int // Issues skipped (duplicates, errors)
Collisions int // Collisions detected
IDMapping map[string]string // Mapping of remapped IDs (old -> new)
CollisionIDs []string // IDs that collided
PrefixMismatch bool // Prefix mismatch detected
ExpectedPrefix string // Database configured prefix
MismatchPrefixes map[string]int // Map of mismatched prefixes to count
SkippedDependencies []string // Dependencies skipped due to FK constraint violations
}
// importIssuesCore handles the core import logic used by both manual and auto-import.
@@ -228,16 +229,17 @@ func importIssuesCore(ctx context.Context, dbPath string, store storage.Storage,
// Convert importer.Result to ImportResult
return &ImportResult{
Created: result.Created,
Updated: result.Updated,
Unchanged: result.Unchanged,
Skipped: result.Skipped,
Collisions: result.Collisions,
IDMapping: result.IDMapping,
CollisionIDs: result.CollisionIDs,
PrefixMismatch: result.PrefixMismatch,
ExpectedPrefix: result.ExpectedPrefix,
MismatchPrefixes: result.MismatchPrefixes,
Created: result.Created,
Updated: result.Updated,
Unchanged: result.Unchanged,
Skipped: result.Skipped,
Collisions: result.Collisions,
IDMapping: result.IDMapping,
CollisionIDs: result.CollisionIDs,
PrefixMismatch: result.PrefixMismatch,
ExpectedPrefix: result.ExpectedPrefix,
MismatchPrefixes: result.MismatchPrefixes,
SkippedDependencies: result.SkippedDependencies,
}, nil
}