fix(molecule): use Dependencies from bd show instead of empty DependsOn (#901)
* fix(molecule): use Dependencies from bd show instead of empty DependsOn Bug: Molecule step dependency checking was broken because bd list doesn't populate the DependsOn field (it's always empty). Only bd show returns dependency info in the Dependencies field. This caused all open steps to appear "ready" regardless of actual dependencies - the polecat would start blocked steps prematurely. Fix: Call ShowMultiple() after List() to fetch full issue details including Dependencies, then check Dependencies instead of DependsOn. Affected functions: - findNextReadyStep() in molecule_step.go - getMoleculeProgressInfo() in molecule_status.go - runMoleculeCurrent() in molecule_status.go Tests: - Added TestFindNextReadyStepWithBdListBehavior to verify fix - Added TestOldBuggyBehavior to demonstrate the bug - Updated existing tests to use fixed algorithm Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix(molecule): use Dependencies from bd show instead of empty DependsOn Bug: Molecule step dependency checking was broken because bd list doesn't populate the DependsOn field (it's always empty). Only bd show returns dependency info in the Dependencies field. This caused all open steps to appear "ready" regardless of actual dependencies - the polecat would start blocked steps prematurely. Fix: Call ShowMultiple() after List() to fetch full issue details including Dependencies, then check Dependencies instead of DependsOn. Also filter to only check "blocks" type dependencies - ignore "parent-child" relationships which are just structural, not blocking. Affected functions: - findNextReadyStep() in molecule_step.go - getMoleculeProgressInfo() in molecule_status.go - runMoleculeCurrent() in molecule_status.go Tests: - Added TestFindNextReadyStepWithBdListBehavior to verify fix - Added TestOldBuggyBehavior to demonstrate the bug - Updated existing tests to use fixed algorithm Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: julianknutsen <julianknutsen@users.noreply.github> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -184,11 +184,25 @@ func runMoleculeProgress(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
}
|
||||
|
||||
// Build set of closed issue IDs for dependency checking
|
||||
// Build set of closed issue IDs and collect open step IDs for dependency checking
|
||||
closedIDs := make(map[string]bool)
|
||||
var openStepIDs []string
|
||||
for _, child := range children {
|
||||
if child.Status == "closed" {
|
||||
closedIDs[child.ID] = true
|
||||
} else if child.Status == "open" {
|
||||
openStepIDs = append(openStepIDs, child.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch full details for open steps to get dependency info.
|
||||
// bd list doesn't return dependencies, but bd show does.
|
||||
var openStepsMap map[string]*beads.Issue
|
||||
if len(openStepIDs) > 0 {
|
||||
openStepsMap, err = b.ShowMultiple(openStepIDs)
|
||||
if err != nil {
|
||||
// Non-fatal: continue without dependency info (all open steps will be "ready")
|
||||
openStepsMap = make(map[string]*beads.Issue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,16 +216,30 @@ func runMoleculeProgress(cmd *cobra.Command, args []string) error {
|
||||
case "in_progress":
|
||||
progress.InProgress++
|
||||
case "open":
|
||||
// Check if all dependencies are closed
|
||||
// Get full step info with dependencies
|
||||
step := openStepsMap[child.ID]
|
||||
|
||||
// Check if all dependencies are closed using Dependencies field
|
||||
// (from bd show), not DependsOn (which is empty from bd list).
|
||||
// Only "blocks" type dependencies block progress - ignore "parent-child".
|
||||
allDepsClosed := true
|
||||
for _, depID := range child.DependsOn {
|
||||
if !closedIDs[depID] {
|
||||
hasBlockingDeps := false
|
||||
var deps []beads.IssueDep
|
||||
if step != nil {
|
||||
deps = step.Dependencies
|
||||
}
|
||||
for _, dep := range deps {
|
||||
if dep.DependencyType != "blocks" {
|
||||
continue // Skip parent-child and other non-blocking relationships
|
||||
}
|
||||
hasBlockingDeps = true
|
||||
if !closedIDs[dep.ID] {
|
||||
allDepsClosed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(child.DependsOn) == 0 || allDepsClosed {
|
||||
if !hasBlockingDeps || allDepsClosed {
|
||||
progress.ReadySteps = append(progress.ReadySteps, child.ID)
|
||||
} else {
|
||||
progress.BlockedSteps = append(progress.BlockedSteps, child.ID)
|
||||
@@ -509,11 +537,25 @@ func getMoleculeProgressInfo(b *beads.Beads, moleculeRootID string) (*MoleculePr
|
||||
}
|
||||
}
|
||||
|
||||
// Build set of closed issue IDs for dependency checking
|
||||
// Build set of closed issue IDs and collect open step IDs for dependency checking
|
||||
closedIDs := make(map[string]bool)
|
||||
var openStepIDs []string
|
||||
for _, child := range children {
|
||||
if child.Status == "closed" {
|
||||
closedIDs[child.ID] = true
|
||||
} else if child.Status == "open" {
|
||||
openStepIDs = append(openStepIDs, child.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch full details for open steps to get dependency info.
|
||||
// bd list doesn't return dependencies, but bd show does.
|
||||
var openStepsMap map[string]*beads.Issue
|
||||
if len(openStepIDs) > 0 {
|
||||
openStepsMap, err = b.ShowMultiple(openStepIDs)
|
||||
if err != nil {
|
||||
// Non-fatal: continue without dependency info (all open steps will be "ready")
|
||||
openStepsMap = make(map[string]*beads.Issue)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -527,16 +569,30 @@ func getMoleculeProgressInfo(b *beads.Beads, moleculeRootID string) (*MoleculePr
|
||||
case "in_progress":
|
||||
progress.InProgress++
|
||||
case "open":
|
||||
// Check if all dependencies are closed
|
||||
// Get full step info with dependencies
|
||||
step := openStepsMap[child.ID]
|
||||
|
||||
// Check if all dependencies are closed using Dependencies field
|
||||
// (from bd show), not DependsOn (which is empty from bd list).
|
||||
// Only "blocks" type dependencies block progress - ignore "parent-child".
|
||||
allDepsClosed := true
|
||||
for _, depID := range child.DependsOn {
|
||||
if !closedIDs[depID] {
|
||||
hasBlockingDeps := false
|
||||
var deps []beads.IssueDep
|
||||
if step != nil {
|
||||
deps = step.Dependencies
|
||||
}
|
||||
for _, dep := range deps {
|
||||
if dep.DependencyType != "blocks" {
|
||||
continue // Skip parent-child and other non-blocking relationships
|
||||
}
|
||||
hasBlockingDeps = true
|
||||
if !closedIDs[dep.ID] {
|
||||
allDepsClosed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if len(child.DependsOn) == 0 || allDepsClosed {
|
||||
if !hasBlockingDeps || allDepsClosed {
|
||||
progress.ReadySteps = append(progress.ReadySteps, child.ID)
|
||||
} else {
|
||||
progress.BlockedSteps = append(progress.BlockedSteps, child.ID)
|
||||
@@ -774,10 +830,10 @@ func runMoleculeCurrent(cmd *cobra.Command, args []string) error {
|
||||
|
||||
info.StepsTotal = len(children)
|
||||
|
||||
// Build set of closed issue IDs for dependency checking
|
||||
// Build set of closed issue IDs and collect open step IDs for dependency checking
|
||||
closedIDs := make(map[string]bool)
|
||||
var inProgressSteps []*beads.Issue
|
||||
var readySteps []*beads.Issue
|
||||
var openStepIDs []string
|
||||
|
||||
for _, child := range children {
|
||||
switch child.Status {
|
||||
@@ -786,23 +842,47 @@ func runMoleculeCurrent(cmd *cobra.Command, args []string) error {
|
||||
closedIDs[child.ID] = true
|
||||
case "in_progress":
|
||||
inProgressSteps = append(inProgressSteps, child)
|
||||
case "open":
|
||||
openStepIDs = append(openStepIDs, child.ID)
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch full details for open steps to get dependency info.
|
||||
// bd list doesn't return dependencies, but bd show does.
|
||||
var openStepsMap map[string]*beads.Issue
|
||||
if len(openStepIDs) > 0 {
|
||||
openStepsMap, _ = b.ShowMultiple(openStepIDs)
|
||||
if openStepsMap == nil {
|
||||
openStepsMap = make(map[string]*beads.Issue)
|
||||
}
|
||||
}
|
||||
|
||||
// Find ready steps (open with all deps closed)
|
||||
for _, child := range children {
|
||||
if child.Status == "open" {
|
||||
allDepsClosed := true
|
||||
for _, depID := range child.DependsOn {
|
||||
if !closedIDs[depID] {
|
||||
allDepsClosed = false
|
||||
break
|
||||
}
|
||||
var readySteps []*beads.Issue
|
||||
for _, stepID := range openStepIDs {
|
||||
step := openStepsMap[stepID]
|
||||
if step == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
// Check dependencies using Dependencies field (from bd show),
|
||||
// not DependsOn (which is empty from bd list).
|
||||
// Only "blocks" type dependencies block progress - ignore "parent-child".
|
||||
allDepsClosed := true
|
||||
hasBlockingDeps := false
|
||||
for _, dep := range step.Dependencies {
|
||||
if dep.DependencyType != "blocks" {
|
||||
continue // Skip parent-child and other non-blocking relationships
|
||||
}
|
||||
if len(child.DependsOn) == 0 || allDepsClosed {
|
||||
readySteps = append(readySteps, child)
|
||||
hasBlockingDeps = true
|
||||
if !closedIDs[dep.ID] {
|
||||
allDepsClosed = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if !hasBlockingDeps || allDepsClosed {
|
||||
readySteps = append(readySteps, step)
|
||||
}
|
||||
}
|
||||
|
||||
// Determine current step and status
|
||||
|
||||
Reference in New Issue
Block a user