ADR-070: Task Dependency Graph
Status
Accepted - January 13, 2026
Context
Atomic tasks in the CODITECT Task Orchestrator have inter-task relationships that must be:
- Stored persistently
- Queried efficiently (blocking detection)
- Visualized for debugging
- Updated as tasks complete
Dependency Types
| Type | Semantics | Enforcement |
|---|---|---|
blocks | Source must complete before target starts | Hard - prevents execution |
informs | Source provides context for target | Soft - warns but allows |
relates | Tasks are related but independent | None - informational only |
Decision
Primary Storage: PostgreSQL
Use PostgreSQL for dependency storage with the task_dependencies table:
CREATE TABLE task_dependencies (
id UUID PRIMARY KEY,
source_task_id VARCHAR(20) REFERENCES atomic_tasks(task_id),
target_task_id VARCHAR(20) REFERENCES atomic_tasks(task_id),
dependency_type VARCHAR(20) CHECK (dependency_type IN ('blocks', 'informs', 'relates')),
created_at TIMESTAMP DEFAULT NOW(),
UNIQUE(source_task_id, target_task_id, dependency_type)
);
-- Indexes for traversal
CREATE INDEX idx_deps_source ON task_dependencies(source_task_id);
CREATE INDEX idx_deps_target ON task_dependencies(target_task_id);
CREATE INDEX idx_deps_type ON task_dependencies(dependency_type);
Ready Task Detection
Tasks are ready when all blocks dependencies are completed:
-- Materialized view for ready tasks
CREATE MATERIALIZED VIEW ready_tasks AS
SELECT t.* FROM atomic_tasks t
WHERE t.status IN ('pending', 'ready')
AND t.assigned_agent IS NULL
AND NOT EXISTS (
SELECT 1 FROM task_dependencies d
JOIN atomic_tasks dep ON d.source_task_id = dep.task_id
WHERE d.target_task_id = t.task_id
AND d.dependency_type = 'blocks'
AND dep.status != 'completed'
);
-- Refresh when tasks complete
CREATE OR REPLACE FUNCTION refresh_ready_tasks()
RETURNS TRIGGER AS $$
BEGIN
REFRESH MATERIALIZED VIEW CONCURRENTLY ready_tasks;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
Optional: Neo4j for Visualization
For complex dependency analysis and visualization, optionally sync to Neo4j:
// Create task nodes
CREATE (t:Task {
task_id: 'A.9.1.1',
title: 'Implement API',
status: 'pending'
})
// Create dependency edges
MATCH (source:Task {task_id: 'A.9.1.1'})
MATCH (target:Task {task_id: 'A.9.1.2'})
CREATE (source)-[:BLOCKS]->(target)
// Find critical path
MATCH path = (start:Task)-[:BLOCKS*]->(end:Task)
WHERE NOT EXISTS((start)<-[:BLOCKS]-())
AND NOT EXISTS((end)-[:BLOCKS]->())
RETURN path
ORDER BY length(path) DESC
LIMIT 1
Consequences
Positive
- Efficient blocking detection via SQL indexes
- Transactional consistency with task updates
- No additional infrastructure (PostgreSQL only)
- Optional graph visualization with Neo4j
Negative
- Recursive queries for deep traversal are less elegant in SQL
- Full graph analysis requires Neo4j (optional)
References
Local:
External (coditect-core):
- ADR-068: Large Project Plan Token Economics
Decision Date: January 13, 2026 Implementation Priority: P1 (Part of core orchestrator)