When Circular Dependencies Are the Point
In Kumiko, there’s no foundation piece. The frame constrains the grid, the grid constrains the infill, and the infill pushes back against the frame. Try to model this as a dependency graph and run make: infinite loop. Try npm install: circular dependency error.
Most build systems treat cycles as pathological. Tarjan’s algorithm, published in 1972, gave us an efficient way to find strongly connected components—clusters of nodes that all reach each other. A cycle. A problem to be eliminated.
But watch a Kumiko craftsperson assemble a panel and you see the cycle is the structure. Nothing can be installed first because nothing is primary. The pieces slide into place together, their geometric tolerances allowing just enough play for assembly before the final tap locks everything into mutual compression.
defmodule Cycle do
def detect(graph, node, visited \\ MapSet.new(), stack \\ MapSet.new()) do
cond do
node in stack -> {:cycle, node}
node in visited -> :ok
true ->
stack = MapSet.put(stack, node)
Enum.reduce_while(Map.get(graph, node, []), :ok, fn neighbor, _ ->
case detect(graph, neighbor, visited, stack) do
:ok -> {:cont, :ok}
cycle -> {:halt, cycle}
end
end)
end
end
end
# Kumiko constraint loop: frame → grid → infill → frame
graph = %{frame: [:grid], grid: [:infill], infill: [:frame]}
Cycle.detect(graph, :frame) |> IO.inspect()
# => {:cycle, :frame}
The Bash version walks the same idea with arrays—crude, but it runs:
#!/bin/bash
declare -A deps=([frame]=grid [grid]=infill [infill]=frame)
visited=() node="frame"
while [[ -n "$node" ]]; do
for v in "${visited[@]}"; do
[[ "$v" == "$node" ]] && echo "cycle at: $node" && exit 0
done
visited+=("$node")
node="${deps[$node]}"
done
echo "no cycle"
Output: cycle at: frame
The algorithm does exactly what it should: it finds the cycle and reports it. What it can’t express is that for Kumiko, the cycle isn’t a flaw in the dependency graph—it’s load-bearing. The frame doesn’t support the grid; they support each other. Tarjan’s algorithm detects structural integrity and flags it as an error.
Some systems are meant to be acyclic. Some aren’t. The algorithm can’t tell you which kind you’re building.