Navan tried to create a cycle, and the validator said no.
He had what he considered a good reason. The pipeline he was building had a natural feedback loop: generate code, run tests, analyze failures, feed the analysis back to the code generator. In his head, this was a circle. Generate, test, analyze, generate, test, analyze. A loop. A cycle. A thing that goes around.
He drew it in DOT. Node A to B, B to C, C to A. A cycle. He fed it to the validator.
ERROR: Pipeline contains cycle: A -> B -> C -> A. Attractor pipelines must be directed acyclic graphs.
"Why?" Navan asked the room. It was not a rhetorical question. He genuinely wanted to know.
Justin answered without looking up from his screen. "Because cycles don't terminate."
"Mine does. I have a condition on the C-to-A edge: satisfaction < 0.9. When the satisfaction crosses 0.9, the edge isn't taken. The cycle breaks."
"And if satisfaction never crosses 0.9?"
"Then..."
"Then the pipeline runs forever. Or until we run out of tokens. Or until someone kills it. There's no structural guarantee of termination. The guarantee is conditional, which means it depends on runtime behavior, which means it's a hope, not a promise."
Navan opened his mouth to argue. Jay cut in.
"He's right, Navan. I've been down this road. In distributed systems, you never trust runtime conditions for termination. You need structural guarantees. A DAG terminates because it has no cycles. That's a mathematical fact. No conditions required."
"But I need iteration," Navan protested. "The pipeline has to loop."
"Then model the loop as a sequence," Justin said. "Unroll it. Instead of A-B-C-A, make it A-B-C-A'-B'-C'. Each iteration is a distinct set of nodes. You can have five iterations by having five copies of the pattern. The pipeline is still a DAG. It still terminates. And you can see exactly how many iterations are possible by counting the layers."
"That's... verbose."
"It's explicit. And explicit is better than clever when your pipeline is running unattended at 3 AM with a thousand dollars of tokens on the line."
Navan stared at the validator error. He stared at his DOT file. He stared at the ceiling.
"Fine," he said.
"Fine," Justin echoed.
Navan unrolled the loop. Five iterations. Fifteen nodes instead of three. The DOT file was longer. It was also, he had to admit, completely unambiguous. Each iteration was visible. The maximum depth was explicit. The termination was structural.
He ran it. It converged on iteration three. Nodes four and five were never reached. They sat in the graph like unused rooms in a house built for a larger family—unnecessary, but reassuring.
"I still think cycles are more natural," Navan said.
"Cycles are more natural," Justin agreed. "And DAGs are more reliable. Pick one."
Navan had already picked. He just hadn't liked it yet.
"Unused rooms in a house built for a larger family." That metaphor for unrolled loop iterations is going to live in my head permanently. Verbose but safe. Explicit but reassuring.