Implement cycle detection and prevention improvements
- Add diagnostic warnings when cycles detected after dep add (bd-309) - Add semantic validation for parent-child dependency direction (bd-308) - Document cycle handling behavior in code, README, and DESIGN (bd-310) Changes: - cmd/bd/dep.go: Add DetectCycles() call and warning after dep add - internal/storage/sqlite/dependencies.go: Add parent-child direction validation and comprehensive cycle prevention comments - internal/storage/sqlite/dependencies_test.go: Add TestParentChildValidation - README.md: Add dependency types and cycle prevention section with examples - DESIGN.md: Add detailed cycle prevention design rationale and trade-offs
This commit is contained in:
@@ -31,26 +31,51 @@ var depAddCmd = &cobra.Command{
|
||||
|
||||
ctx := context.Background()
|
||||
if err := store.AddDependency(ctx, dep, actor); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Schedule auto-flush
|
||||
markDirtyAndScheduleFlush()
|
||||
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"status": "added",
|
||||
"issue_id": args[0],
|
||||
"depends_on_id": args[1],
|
||||
"type": depType,
|
||||
})
|
||||
return
|
||||
// Check for cycles after adding dependency
|
||||
cycles, err := store.DetectCycles(ctx)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Warning: Failed to check for cycles: %v\n", err)
|
||||
} else if len(cycles) > 0 {
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
fmt.Fprintf(os.Stderr, "\n%s Warning: Dependency cycle detected!\n", yellow("⚠"))
|
||||
fmt.Fprintf(os.Stderr, "This can hide issues from the ready work list and cause confusion.\n\n")
|
||||
fmt.Fprintf(os.Stderr, "Cycle path:\n")
|
||||
for _, cycle := range cycles {
|
||||
for j, issue := range cycle {
|
||||
if j == 0 {
|
||||
fmt.Fprintf(os.Stderr, " %s", issue.ID)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, " → %s", issue.ID)
|
||||
}
|
||||
}
|
||||
if len(cycle) > 0 {
|
||||
fmt.Fprintf(os.Stderr, " → %s", cycle[0].ID)
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\n")
|
||||
}
|
||||
fmt.Fprintf(os.Stderr, "\nRun 'bd dep cycles' for detailed analysis.\n\n")
|
||||
}
|
||||
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("%s Added dependency: %s depends on %s (%s)\n",
|
||||
green("✓"), args[0], args[1], depType)
|
||||
if jsonOutput {
|
||||
outputJSON(map[string]interface{}{
|
||||
"status": "added",
|
||||
"issue_id": args[0],
|
||||
"depends_on_id": args[1],
|
||||
"type": depType,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
green := color.New(color.FgGreen).SprintFunc()
|
||||
fmt.Printf("%s Added dependency: %s depends on %s (%s)\n",
|
||||
green("✓"), args[0], args[1], depType)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user