hasCycle
Returns true if the graph contains at least one cycle.
Iterative DFS over an explicit frame stack of outgoing-edge iterators - safe on graphs deep enough to blow a recursive call stack. Self-loops are skipped at the edge level so a project(":self") declaration does not register as a cycle.