Task Dependencies¶
Dependencies allow you to define the order in which tasks must be completed. A task with dependencies won't be selected for execution until all its dependencies are closed.
How Dependencies Work¶
When cub selects the next task to run, it:
- Finds all tasks with
status == "open" - Filters to tasks where all dependencies are
closed - Sorts remaining tasks by priority (P0 first)
- Selects the first one
flowchart LR
A[Task A<br/>open] --> B[Task B<br/>open]
B --> C[Task C<br/>open]
style A fill:#4CAF50,color:white
style B fill:#FFC107
style C fill:#FFC107 In this diagram:
- Task A is ready (no dependencies)
- Task B depends on A (blocked until A closes)
- Task C depends on B (blocked until B closes)
Blocked vs Ready Tasks¶
Ready tasks have:
- Status of
open - All dependencies closed (or no dependencies)
Blocked tasks have:
- Status of
open - One or more dependencies that are not
closed
Check which tasks are ready:
Setting Up Dependencies¶
Beads Backend¶
Beads uses a blocks field (inverse of dependsOn):
# Task B depends on Task A
# Read as: "B is blocked by A"
bd dep add task-B task-A --type blocks
# View a task's dependencies
bd show task-B --json | jq '.blocks'
Remove a dependency:
JSON Backend¶
Use the dependsOn array:
{
"tasks": [
{
"id": "myproj-001",
"title": "Design database schema",
"status": "open",
"dependsOn": []
},
{
"id": "myproj-002",
"title": "Implement user model",
"status": "open",
"dependsOn": ["myproj-001"]
},
{
"id": "myproj-003",
"title": "Add user API endpoints",
"status": "open",
"dependsOn": ["myproj-002"]
}
]
}
Dependency Patterns¶
Sequential Chain¶
Tasks must execute in strict order:
flowchart LR
A[Design] --> B[Implement] --> C[Test] --> D[Deploy] # Beads
bd dep add task-B task-A --type blocks
bd dep add task-C task-B --type blocks
bd dep add task-D task-C --type blocks
Fan-Out (Parallel)¶
Multiple tasks can run after a common prerequisite:
flowchart LR
A[Design API] --> B[Backend]
A --> C[Frontend]
A --> D[Mobile] All three implementation tasks become ready once "Design API" closes. Cub runs them one at a time by priority, but they don't block each other.
{
"tasks": [
{"id": "api-001", "title": "Design API", "dependsOn": []},
{"id": "api-002", "title": "Backend implementation", "dependsOn": ["api-001"]},
{"id": "api-003", "title": "Frontend implementation", "dependsOn": ["api-001"]},
{"id": "api-004", "title": "Mobile implementation", "dependsOn": ["api-001"]}
]
}
Fan-In (Merge)¶
A task that requires multiple prerequisites:
flowchart LR
A[Backend] --> D[Integration Tests]
B[Frontend] --> D
C[Mobile] --> D "Integration Tests" only becomes ready when all three implementations are closed.
Diamond Pattern¶
Combining fan-out and fan-in:
flowchart LR
A[Design] --> B[Backend]
A --> C[Frontend]
B --> D[Integration]
C --> D {
"tasks": [
{"id": "t-001", "title": "Design", "dependsOn": []},
{"id": "t-002", "title": "Backend", "dependsOn": ["t-001"]},
{"id": "t-003", "title": "Frontend", "dependsOn": ["t-001"]},
{"id": "t-004", "title": "Integration", "dependsOn": ["t-002", "t-003"]}
]
}
Priority vs Dependencies¶
Dependencies always take precedence over priority.
Even if Task A is P4 (lowest) and Task B is P0 (critical), if B depends on A, then A runs first.
{
"tasks": [
{"id": "t-001", "title": "Setup infrastructure", "priority": "P4", "dependsOn": []},
{"id": "t-002", "title": "Critical feature", "priority": "P0", "dependsOn": ["t-001"]}
]
}
Execution order: t-001 then t-002 (despite P0 priority).
Use Priority for Independent Tasks
Priority determines order among tasks that are both ready. Use it to prioritize unrelated work, not to override dependencies.
Example Workflow: Feature Development¶
A typical feature implementation:
flowchart TD
A[spec] --> B[design]
B --> C[implement-model]
B --> D[implement-api]
C --> E[implement-ui]
D --> E
E --> F[write-tests]
F --> G[documentation] # Create tasks
bd create "Write feature spec" --type task -p 1
bd create "Design architecture" --type task -p 2
bd create "Implement data model" --type task -p 2
bd create "Implement API endpoints" --type task -p 2
bd create "Implement UI components" --type task -p 2
bd create "Write integration tests" --type task -p 3
bd create "Update documentation" --type task -p 4
# Set up dependencies
bd dep add cub-002 cub-001 --type blocks
bd dep add cub-003 cub-002 --type blocks
bd dep add cub-004 cub-002 --type blocks
bd dep add cub-005 cub-003 --type blocks
bd dep add cub-005 cub-004 --type blocks
bd dep add cub-006 cub-005 --type blocks
bd dep add cub-007 cub-006 --type blocks
Avoiding Circular Dependencies¶
Circular dependencies create a deadlock where no task can run:
flowchart LR
A --> B --> C --> A Cub doesn't explicitly detect cycles, but you'll notice when cub run --ready returns no tasks despite open work.
To fix:
-
Identify the cycle:
-
Remove one dependency to break the cycle:
Gates and Checkpoints¶
Use gate type tasks for manual approval points:
{
"id": "t-gate-001",
"title": "Security Review Checkpoint",
"type": "gate",
"status": "open",
"dependsOn": ["t-003", "t-004"]
}
Downstream tasks depend on the gate:
The gate blocks production deployment until manually closed:
Debugging Dependencies¶
See What's Blocking¶
# Beads
bd show task-id --json | jq '.blocks'
# JSON
jq '.tasks[] | select(.id == "task-id") | .dependsOn' prd.json
Find All Blocked Tasks¶
# Tasks that have unfulfilled dependencies
cub run --ready # Shows only ready tasks
# Compare with all open
bd list --status open # Shows all open tasks
Visualize Dependencies¶
For complex graphs, export and visualize:
# Generate DOT format
bd list --json | jq -r '
.[] |
. as $task |
(.blocks // [])[] |
"\($task.id) -> \(.)"
' | sort -u > deps.dot
# Wrap with digraph header
echo "digraph deps {" > graph.dot
cat deps.dot >> graph.dot
echo "}" >> graph.dot
# Render with graphviz
dot -Tpng graph.dot -o dependencies.png
Best Practices¶
-
Keep chains short: Long dependency chains delay execution. Parallelize where possible.
-
Use epics for grouping, dependencies for ordering: Don't use dependencies just to group related tasks.
-
Document non-obvious dependencies: If the dependency isn't clear from task titles, add a note explaining why.
-
Validate early: Run
cub run --readybefore starting to catch missing dependencies. -
Avoid micro-dependencies: Don't create dependencies between tiny tasks that could be combined.
Next Steps¶
-
See how cub selects and executes tasks.
-
Deep dive into the selection algorithm.