- C1(critical): Replace tsc build with tsup for dual ESM + CJS output - W2(warning): Change workflowCost to accept TaskGraph instead of TaskGraphInner - S1(suggestion): Add test for bottlenecks empty-graph early return - S2(suggestion): Document dangling-reference detection is unreachable via public API
55 lines
2.5 KiB
Markdown
55 lines
2.5 KiB
Markdown
---
|
|
id: analysis/critical-path
|
|
name: Implement criticalPath and weightedCriticalPath functions
|
|
status: completed
|
|
depends_on:
|
|
- graph/construction
|
|
- graph/queries
|
|
scope: moderate
|
|
risk: medium
|
|
impact: component
|
|
level: implementation
|
|
---
|
|
|
|
## Description
|
|
|
|
Implement `criticalPath` and `weightedCriticalPath` as standalone functions. `criticalPath` finds the longest path from sources to sinks using default edge weighting. `weightedCriticalPath` accepts a custom weight function for per-node weighting.
|
|
|
|
`criticalPath` can be implemented via topological order + dynamic programming (longest path in DAG). For unweighted, each edge has weight 1; for weighted, each node contributes a weight.
|
|
|
|
## Acceptance Criteria
|
|
|
|
- [ ] `criticalPath(graph: TaskGraph): string[]` — returns the longest path as an ordered array of task IDs
|
|
- [ ] `weightedCriticalPath(graph: TaskGraph, weightFn: (taskId: string, attrs: TaskGraphNodeAttributes) => number): string[]` — returns the path with the highest cumulative weight
|
|
- [ ] Both functions throw `CircularDependencyError` if graph is cyclic
|
|
- [ ] When multiple paths tie, returns any one of them (deterministic order preferred)
|
|
- [ ] Empty graph returns `[]`; single-node graph returns `[nodeId]`
|
|
- [ ] Unit tests: linear chain (the chain itself is critical path), diamond graph (tests path selection), weighted variant with diverse scope values
|
|
|
|
## References
|
|
|
|
- docs/architecture/api-surface.md — criticalPath, weightedCriticalPath signatures
|
|
- docs/architecture/graph-model.md — edge direction (prerequisite→dependent determines source→sink)
|
|
|
|
## Notes
|
|
|
|
Implementation uses topological order + dynamic programming (longest path in DAG).
|
|
Both functions delegate to a shared `computeLongestPath` helper that:
|
|
1. Gets topological order (throws CircularDependencyError via `graph.topologicalOrder()`)
|
|
2. Initializes source nodes with their weight
|
|
3. Relaxes edges in topological order (DP: dist[v] = max(dist[u] + weight(v)))
|
|
4. Backtracks from the node with maximum distance to reconstruct the path
|
|
|
|
`criticalPath` uses `weightFn = () => 1` (unweighted).
|
|
`weightedCriticalPath` accepts a custom weight function on `(taskId, attrs)`.
|
|
|
|
## Summary
|
|
|
|
Implemented `criticalPath` and `weightedCriticalPath` as standalone functions using topological-order DP.
|
|
- Modified: `src/analysis/critical-path.ts` (full implementation, 161 lines)
|
|
- Modified: `test/analysis.test.ts` (20 tests covering all acceptance criteria)
|
|
- Tests: 20, all passing (462 total passing)
|
|
|
|
## Summary
|
|
|
|
> To be filled on completion |