AI Skill Report Card

Resolving Dependency Graphs

A-87·Sep 5, 2026·Source: Web
15 / 15
Python
from graphlib import TopologicalSorter, CycleError graph = { "deploy": {"test", "build"}, "test": {"build"}, "build": {"lint"}, "lint": set(), } ts = TopologicalSorter(graph) print(list(ts.static_order())) # ['lint', 'build', 'test', 'deploy']

graph[node] = {dependencies} — keys depend on their values (values must be resolved first).

Recommendation
Add an example showing recovery/handling of CycleError in a real workflow (e.g., reporting which nodes form the cycle to the user) rather than just showing the raised exception
14 / 15

Progress:

  • Build the dependency dict: {node: set_of_prerequisites}
  • Choose API: static_order() for a simple full sort, or the manual protocol for streaming/parallel execution
  • Handle CycleError if the graph might contain cycles
  • (If parallel) drive the sorter manually with prepare()/get_ready()/done()

Simple case — full ordering:

Python
ts = TopologicalSorter(graph) order = list(ts.static_order())

Parallel/streaming case — process ready nodes as they unblock:

Python
ts = TopologicalSorter(graph) ts.prepare() # raises CycleError here if cyclic while ts.is_active(): ready = ts.get_ready() # tuple of nodes with no unresolved deps for node in ready: do_work(node) ts.done(node) # mark complete, unblocks dependents

get_ready() returns all currently unblocked nodes — dispatch them concurrently (thread pool, async tasks, subprocess batch) for real parallelism.

Adding edges incrementally:

Python
ts = TopologicalSorter() ts.add(node, *predecessors) # can call repeatedly to build up graph ts.add("build", "lint") ts.add("test", "build")
Recommendation
Consider showing the async/thread-pool dispatch pattern concretely for the parallel case, since it's mentioned but not demonstrated in code
17 / 20

Example 1: Detecting a cycle Input:

Python
graph = {"a": {"b"}, "b": {"c"}, "c": {"a"}} ts = TopologicalSorter(graph) ts.static_order()

Output:

graphlib.CycleError: ('nodes are in a cycle', ['a', 'b', 'c', 'a'])

Example 2: Parallel build batches Input:

Python
graph = {"link": {"a.o", "b.o"}, "a.o": {"a.c"}, "b.o": {"b.c"}, "a.c": set(), "b.c": set()} ts = TopologicalSorter(graph) ts.prepare() batches = [] while ts.is_active(): ready = ts.get_ready() batches.append(sorted(ready)) for n in ready: ts.done(n)

Output:

batches == [['a.c', 'b.c'], ['a.o', 'b.o'], ['link']]

Example 3: Independent nodes (no dependencies) Input:

Python
graph = {"x": set(), "y": set()} list(TopologicalSorter(graph).static_order())

Output: ['x', 'y'] (order among unrelated nodes is arbitrary but valid)

Recommendation
A brief note on performance characteristics or node count limits would round out completeness for large-scale build systems
  • Call prepare() explicitly when using the manual protocol — it finalizes the graph and does the upfront cycle check.
  • Never mutate the graph (via add()) after calling prepare(); it raises ValueError.
  • Each node returned by get_ready() must eventually get a matching done() call, or is_active() stays true forever.
  • Nodes must be hashable; they don't need to be pre-declared as dict keys — referencing them as a dependency is enough.
  • Use static_order() when you just need a single sequential list; it's a thin convenience wrapper around the manual protocol.
  • Don't confuse edge direction: graph[x] = {y} means x depends on y (y runs first), not the reverse.
  • Don't call get_ready() again without calling done() on previously returned nodes — already-returned nodes won't reappear, but forgetting done() stalls the sort.
  • Don't assume get_ready() order is deterministic/meaningful — treat it as an unordered ready-set.
  • Don't reuse a TopologicalSorter instance after exhausting it — create a new one per sort.
0
Grade A-AI Skill Framework
Scorecard
Criteria Breakdown
Quick Start
15/15
Workflow
14/15
Examples
17/20
Completeness
18/20
Format
15/15
Conciseness
14/15