Skip to main content

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:

  1. Stored persistently
  2. Queried efficiently (blocking detection)
  3. Visualized for debugging
  4. Updated as tasks complete

Dependency Types

TypeSemanticsEnforcement
blocksSource must complete before target startsHard - prevents execution
informsSource provides context for targetSoft - warns but allows
relatesTasks are related but independentNone - 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)