Files
automaker/apps/ui/src/components/views/graph-view/utils/dependency-validation.ts
jbotwina 8d80c73faa feat: Add task dependencies and spawn sub-task functionality
- Add edge dragging to create dependencies in graph view
- Add spawn sub-task action available in graph view and kanban board
- Implement ancestor context selection when spawning tasks
- Add dependency validation (circular, self, duplicate prevention)
- Include ancestor context in spawned task descriptions

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

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2025-12-23 19:30:30 -05:00

52 lines
1.8 KiB
TypeScript

import { Feature } from '@/store/app-store';
/**
* Checks if adding a dependency from sourceId to targetId would create a circular dependency.
* Uses DFS to detect if targetId can reach sourceId through existing dependencies.
*
* @param features - All features in the system
* @param sourceId - The feature that would become a dependency (the prerequisite)
* @param targetId - The feature that would depend on sourceId
* @returns true if adding this dependency would create a cycle
*/
export function wouldCreateCircularDependency(
features: Feature[],
sourceId: string,
targetId: string
): boolean {
const featureMap = new Map(features.map((f) => [f.id, f]));
const visited = new Set<string>();
function canReach(currentId: string, targetId: string): boolean {
if (currentId === targetId) return true;
if (visited.has(currentId)) return false;
visited.add(currentId);
const feature = featureMap.get(currentId);
if (!feature?.dependencies) return false;
for (const depId of feature.dependencies) {
if (canReach(depId, targetId)) return true;
}
return false;
}
// Check if source can reach target through existing dependencies
// If so, adding target -> source would create a cycle
return canReach(sourceId, targetId);
}
/**
* Checks if a dependency already exists between two features.
*
* @param features - All features in the system
* @param sourceId - The potential dependency (prerequisite)
* @param targetId - The feature that might depend on sourceId
* @returns true if targetId already depends on sourceId
*/
export function dependencyExists(features: Feature[], sourceId: string, targetId: string): boolean {
const targetFeature = features.find((f) => f.id === targetId);
if (!targetFeature?.dependencies) return false;
return targetFeature.dependencies.includes(sourceId);
}