"""Cycle detection for governance DAG using DFS.""" from __future__ import annotations WHITE, GRAY, BLACK = 0, 1, 2 def detect_cycles(adj: dict[str, list[str]]) -> list[list[str]]: """Detect all cycles in a directed graph. Args: adj: Adjacency list mapping node → list of neighbors (dependencies). Returns: List of cycles, each cycle is a list of node IDs forming the loop. """ color: dict[str, int] = {node: WHITE for node in adj} parent: dict[str, str | None] = {node: None for node in adj} cycles: list[list[str]] = [] def dfs(u: str) -> None: color[u] = GRAY for v in adj.get(u, []): if v not in color: # Node referenced but not in graph — skip continue if color[v] == GRAY: # Back edge → cycle found cycle = [v, u] node = u while node != v: node = parent[node] if node is None: break cycle.append(node) cycle.reverse() cycles.append(cycle) elif color[v] == WHITE: parent[v] = u dfs(v) color[u] = BLACK for node in adj: if color[node] == WHITE: dfs(node) return cycles