Labs / Lab 07

Build a dependency-aware task graph

Separate work definition from execution so a runner can tell what is ready now, what is blocked, and what can happen next.

What you'll build

A tiny DAG runner with durable task state.

This lab gives you a small task graph stored in JSON and a Python script that reads it, checks dependencies, and tells you which tasks are ready. In plain English, it answers the boring but essential orchestration question: what can run right now without breaking the order of the work?

Under the hood, that is DAG execution in miniature. The graph says which tasks depend on which other tasks, and the runner only advances work when every upstream dependency is already done. That same pattern shows up in build systems, data pipelines, CI jobs, and multi-step agent workflows.

Run it

cd ai_ecosystem_labs
python3 07-task-graph/task_graph.py ready
python3 07-task-graph/task_graph.py done add-hook
Starting here? Quick setup
git clone https://github.com/BanditF/ai_ecosystem_labs
cd ai_ecosystem_labs
python3 07-task-graph/task_graph.py ready

Requires Python 3.8+. No extra packages needed.

Time guide. Setup: ~2 min. Working through it: 20–35 min, especially if you pause to inspect the saved task state.

Why this piece exists

Coordination gets easier once the graph is explicit.

If you keep task order in your head, or bury it inside one long script, the system cannot help you much. It cannot tell you what is blocked, cannot resume cleanly after a restart, and cannot hand work to another process without a lot of custom logic. A task graph fixes that by making dependencies part of the data.

A real-world analog is a pipeline orchestrator like Airflow or Dagster, or even a CI workflow that refuses to run deployment before tests pass. This lab is a much smaller version of the same idea: write the dependency graph down, then let the runner decide what is ready instead of guessing.

The code

task_graph.py

Walk through it

Four things worth noticing.

ready_tasks() is dependency filtering, not full ordering

The key function loops through every pending task and only returns it if all of its dependencies already have status done. A full orchestrator would also validate the graph for cycles and produce a complete topological order. This toy runner asks only: which tasks are ready to run right now?

The task graph lives in data, not hard-coded branches

tasks.json defines the work, the dependency edges, and the current status. The Python file just loads, checks, and updates that graph. That separation matters because a coordinator can change the plan by editing data instead of rewriting the runner itself.

Status behaves like a tiny state machine

Even in this stripped-down version, each task moves through recognizable states: pending until dependencies clear, then done once the runner marks it complete. In a fuller orchestrator you would usually insert running between those two states, checkpoint the graph, and resume later from the saved file.

DAG rules are what prevent deadlock

The graph only works cleanly if dependencies stay acyclic. If task B depends on task A, task A cannot also depend on task B. Once you introduce a cycle, nothing can become ready, because every task in the loop is waiting on some other task in the same loop to finish first.

Expected output

What a successful run looks like.

Checking which work is ready right now:

{
  "ready": [
    {
      "id": "add-hook",
      "title": "Add pre-tool hook",
      "status": "pending",
      "deps": [
        "write-wrapper"
      ]
    }
  ]
}

Marking that task done so the next downstream task becomes ready:

{
  "ok": true,
  "task": {
    "id": "add-hook",
    "title": "Add pre-tool hook",
    "status": "done",
    "deps": [
      "write-wrapper"
    ]
  }
}

After that update, re-running the ready check would surface agent-loop. That is the whole orchestration pattern in miniature: finish upstream work, then unlock the next node in the graph.

Try this

Three things to try before moving on.

  1. Add another task to tasks.json. Create a new task that depends on an existing one, then run the ready command again. It should stay hidden until its dependency is marked done.
  2. Manually edit a task status. Flip one of the upstream tasks to done in tasks.json, save the file, and re-run the script. Watch which downstream task becomes ready. That is dependency propagation, just without a scheduler wrapped around it yet.
  3. Create a cycle on purpose. Make task A depend on task B and task B depend on task A, then run the ready check. Notice how nothing new becomes runnable. That is why orchestration systems insist on DAGs instead of arbitrary graphs.

Concepts behind this

Agents matter here because multi-step agents often need this exact shape: represent work as nodes, check dependencies, and only advance when prerequisites are complete.

The next lab, Lab 08, builds on the same idea by adding a queue and worker coordination around tasks that are ready.