A source from an earlier round does not satisfy an edge
Inside a loop, a completed node is not necessarily a current one. Without this, a consumer on round N happily reads the value its source produced on round N-1.
The rule
- A completed source does not satisfy a trigger, error or data edge when the source and the consumer share a loop and the source's round on that loop is behind the consumer's.
- Behindness is decided by intersecting the two round stamps' loops and comparing lexicographically, outermost loop first: the first shared loop on which the two differ settles it, and nothing nested inside it can overturn that.
- Where the two share no loop the source is never behind: a source outside every loop the consumer is in will never fire again, its value is the current one forever, and gating it would hang the consumer on a round that cannot arrive.
- The verdict differs by edge class: a gated trigger or error edge leaves the consumer waiting, to be skipped when the run ends, while a gated data port must be terminated.
What it means
Inside a loop, a completed source is not automatically a current one. A consumer on round two that reads a source's value from round one is reading last round's answer as if it were this round's, and the rule exists to stop exactly that: a completed source stops satisfying a trigger, error or data edge once the source and the consumer share a loop and the source is behind the consumer on it.
Behindness is not "behind on any shared loop" — it is decided by intersecting the two round stamps and comparing them lexicographically, outermost loop first. The first shared loop where the two differ settles the question, and nothing nested inside that loop can overturn the verdict, even if the inner numbers alone would look like the source is lagging.
Where the two share no loop at all, the source is never behind. A source outside every loop its consumer is in will never fire again on its own terms, so its value is the current one forever — gating it on a round that cannot arrive would hang the consumer permanently.
The consequence differs by edge class. A gated trigger or error edge simply leaves the consumer waiting, to be swept up and skipped once the run ends. A gated data port cannot be left waiting the same way; it has to be terminated (SG-20).
Example
{"source": {"loop_a": 0}, "consumer": {"loop_a": 1}}{"source": {}, "consumer": {"loop_a": 3}}{"source": {"outer": 2, "inner": 0}, "consumer": {"outer": 1, "inner": 5}}{"source": {"outer": 1, "inner": 0}, "consumer": {"outer": 1, "inner": 2}}The outermost shared loop always decides first: ahead on the outer loop outweighs behind on the inner one, and only once the outer rounds match does the inner comparison get a say.