package agenttask import ( "regexp" "sort" "strings" ) type dependencyStatus string const ( dependencyReady dependencyStatus = "ready" dependencyWaiting dependencyStatus = "waiting" dependencyMissing dependencyStatus = "missing" dependencyAmbiguous dependencyStatus = "ambiguous" dependencyBlocked dependencyStatus = "blocked" ) type dependencyResult struct { Status dependencyStatus Ref string } var dependencySeparator = regexp.MustCompile(`[^a-z0-9]+`) func normalizeDependencyRef(value string) string { value = strings.ToLower(strings.TrimSpace(value)) value = strings.Trim(value, "`'\"") value = dependencySeparator.ReplaceAllString(value, "-") return strings.Trim(value, "-") } // evaluateDependencies intentionally considers only ExplicitPredecessors. // Directory ordinals and declared/unknown/overlapping write sets never create // a dependency. func evaluateDependencies( unit WorkUnit, workflow ProjectWorkflowSnapshot, works map[WorkUnitID]WorkRecord, ) dependencyResult { if len(unit.ExplicitPredecessors) == 0 { return dependencyResult{Status: dependencyReady} } for _, predecessor := range unit.ExplicitPredecessors { ref := normalizeDependencyRef(predecessor.Ref) matches := make([]WorkUnit, 0, 1) for _, candidate := range workflow.Units { if candidate.ID == unit.ID { continue } if normalizeDependencyRef(string(candidate.ID)) == ref { matches = append(matches, candidate) continue } for _, alias := range candidate.Aliases { if normalizeDependencyRef(alias) == ref { matches = append(matches, candidate) break } } } sort.Slice(matches, func(left, right int) bool { return matches[left].ID < matches[right].ID }) switch len(matches) { case 0: return dependencyResult{Status: dependencyMissing, Ref: predecessor.Ref} case 1: default: return dependencyResult{Status: dependencyAmbiguous, Ref: predecessor.Ref} } match := matches[0] record, tracked := works[match.ID] if match.Completed || tracked && record.State == WorkStateCompleted { continue } if tracked && (record.State == WorkStateBlocked || record.State == WorkStateTerminalDeferred || record.State == WorkStateStopped) { return dependencyResult{Status: dependencyBlocked, Ref: predecessor.Ref} } return dependencyResult{Status: dependencyWaiting, Ref: predecessor.Ref} } return dependencyResult{Status: dependencyReady} }