A group of the controller, the node-engine and the lab, each ready, was refused as a cycle: built by put the controller's member first, engine before controller the node-engine's. ADR 0249 ranks the rules (declared, then built by and version skew, then engine before controller) and supersedes ADR 0239's sentence that made a declared order against an inferred one a cycle; design 47 says the precedence. Resolved by mesh-controller #134 and mesh-catalog #119, replay R309 (mesh-lab #64).
4.1 KiB
status, opened, located-in, fixed-by, replay, amended-design
| status | opened | located-in | fixed-by | replay | amended-design | ||||
|---|---|---|---|---|---|---|---|---|---|
| resolved | 2026-10-08 |
|
novox/mesh-controller PR | R309 | 03-DESIGN/01-to-be/47-delivery-from-commit-to-delivered.md |
309. Two order rules ordered one pair both ways
Symptom
On 2026-10-08 the delivery group feat/a-machine-joins-through-the-tunnel held three pull requests: the
controller's mesh-controller #132, the node-engine's mesh-host #52 and the lab's mesh-lab #61. Each was
ready on its own. The delivery's show said of the group:
"state": "rejected";"why": "its order contradicts itself: novox/mesh-controller@…, novox/mesh-host@…".
The group's pairs held two orderings of the same two members:
- the controller's member before the node-engine's,
why: "built by"; - the node-engine's member before the controller's,
why: "engine before controller".
Nothing would ever deliver the group. The person merged it by hand, the controller first, since that member only added a verb the new node-engine calls.
Why it is a design issue
ADR 0239
decision 4 states four order rules and says what to do when a declared order contradicts an inferred
one. It does not say what to do when two inferred rules contradict each other. The planner treated that
as a cycle, so a group that moved the controller's repository and the node-engine together could never be
delivered. The refusal named two members and no rule, so the person could not tell what had clashed or
what to write. A declared after: line could not help, because a declared order against an inferred one
was a cycle too.
Cause
The controller's pull request moved the build agent, which lives in the controller's repository. Every source-built module is built by the build agent, including the node-engine. Built by therefore put the controller's member first. Engine before controller put the node-engine's member first. Both were added as pairs, and the planner's sort found no member with nothing before it.
Fix
ADR 0249 gives the rules a precedence: declared, then built by and version skew, then engine before controller.
- The controller (novox/mesh-controller PR #134).
orderOfcollects each rule's claim, and for each two members the claims of the highest rank decide. The pair carries the rules it won over. Claims of one rank in opposite directions are a contradiction. Both pairs are kept, the members are the cycle, and the contradiction names both rules, the member each puts first and theafter:line that settles it. - mesh-delivery (novox/mesh-catalog PR #119). It keeps the rules each pair won over and the
contradictions, lists them in
groupsand the plan note, and refuses a group in the contradiction's words. A cycle through three members or more names the pairs in it. An older controller's answer reads as before.
Replayed, the group is ordered: the controller's member, then the node-engine's, then the lab's. The pair reads "built by, over engine before controller". That is the order the person chose by hand.
How it is checked
The check: the replay R309 in mesh-lab's register (novox/mesh-lab PR #64), TestReplay309 in the
controller. It replays this group's members, reach and graph using only what orderOf had before the
fix. Proved with the register's cmd/prove: it fails on the commit before the fix (the group refused as
a cycle) and passes on it. The controller's TestAnOrderRuleYieldsToAStrongerOne checks the precedence
and the refusal's words. mesh-delivery's TestAGroupSaysWhichOrderRuleWonOrWhyNoneDid checks that they
reach groups and the group's state.
Not checked: that a new inferred rule is given a rank. One added without a rank sorts below every other rule, and nothing refuses that.