Severity: MED — O(E) per equality / hash on Graph.
Where:
lib/dag/graph.rb:425-433 (== and hash both call edges)
lib/dag/graph.rb:239-242 (edges returns @cached_edges for frozen but still returns the full Set)
What:
== does @nodes == other.nodes && edges == other.edges. For frozen graphs edges returns the cached Set, but ==/hash still walks/materializes Edge objects. For unfrozen graphs compute_edges allocates a fresh Set of fresh Edges per call.
Definition's Data.define derived ==/hash recurses into Graph and Registry — anywhere a Definition is keyed in a Hash or compared, this fires.
Suggested fix:
Short-circuit == with node_count != other.node_count || edge_count != other.edge_count then compare @adjacency Sets directly (Set values are frozen on freeze). hash can be [@nodes, @adjacency, @edge_metadata].hash for frozen graphs.
Filed from a multi-agent review (DRY / efficiency / bug). Behavior change is out of scope; suggested fixes are refactors only.
Severity: MED — O(E) per equality / hash on Graph.
Where:
lib/dag/graph.rb:425-433(==andhashboth calledges)lib/dag/graph.rb:239-242(edgesreturns@cached_edgesfor frozen but still returns the full Set)What:
==does@nodes == other.nodes && edges == other.edges. For frozen graphsedgesreturns the cached Set, but==/hashstill walks/materializes Edge objects. For unfrozen graphscompute_edgesallocates a fresh Set of fresh Edges per call.Definition's
Data.definederived==/hashrecurses into Graph and Registry — anywhere a Definition is keyed in a Hash or compared, this fires.Suggested fix:
Short-circuit
==withnode_count != other.node_count || edge_count != other.edge_countthen compare@adjacencySets directly (Set values are frozen on freeze).hashcan be[@nodes, @adjacency, @edge_metadata].hashfor frozen graphs.Filed from a multi-agent review (DRY / efficiency / bug). Behavior change is out of scope; suggested fixes are refactors only.