Issue 349: a merge acted on late superseded the merge after it, and built the older commit
mesh/merge-gate pass: the change touches no module of the mesh's graph
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

Plans of a branch superseded each other by when they were made; the
catch-up's late plan of an older merge took over a newer one, once a
security fix to the forge's module. Recorded with the fix in review.
This commit is contained in:
2026-10-09 14:12:42 +02:00
committed by jochen
parent 582be67f05
commit 3acc1ec219
2 changed files with 58 additions and 0 deletions
@@ -173,3 +173,6 @@ registry, is the operator's decision.
| live | after delivery: `dnsmasq_health` on both resolvers healthy; a restart by the watch said as `restarted` on the bus |
A replay in mesh-lab's register is still owed before this core issue resolves (ADR 0237).
The controller's own test of the resolver's manifest (`TestTheResolverForwardsToFixedUpstreamsAndNeverReadsResolvConf`)
now asks for `bind-interfaces` and refuses `bind-dynamic`, so the two pull requests land together.
@@ -0,0 +1,55 @@
---
status: located
opened: 2026-10-09
located-in: [mesh-controller cmd/mesh-controller (release_plan.go supersededBy, upgrades.go), mesh-controller internal/inventory (release_plan)]
fixed-by:
amended-design:
---
# 349. A merge acted on late superseded the merge after it, and built the older commit
## Symptom
On 2026-10-09 the plans of the catalogue superseded each other out of the branch's order, twice
(`mesh-controller.plans`):
- the plan at 1833045c was "superseded at tier 0 by" the plan at a783ac94, a merge made before it;
- the plan at a082615b — the merge of a security fix to the forge's module — was "superseded at tier 0 by"
the plan at 8ff8197a, the merge before it, with "gitea planned there again". The forge's module was then
to be built from the commit without the fix. A third merge (9e4267bf) came a minute later and superseded
that plan in turn, so the fix was built from 9e4267bf. Had nothing come after, it would not have been.
## Why it is an issue
A newer plan of a branch supersedes the older open ones and builds what they had not
([ADR 0218](../../02-DECISIONS/0218-a-plan-sends-grants-before-code-rolls-out-one-machine-first-and-a-newer-merge-takes-over-an-older-plan.md),
issue 254). "Newer" was read from when each plan was made, never from the commits. The catch-up acts on a
merge the bus did not hand over (issue 266) after the merges that came after it, so its plan was made last
and won. The mesh built an older commit than the branch held, and said nothing wrong: each plan said
"superseded", as designed.
## Diagnosis
The order of merges into one branch is the forge's: each merge is made on the branch as the one before
left it, so a later merge's commit contains every earlier one's, and the forge's merge time
(`merged_at`, which every announcement carries) is the branch's order. A plan kept none of it.
`supersededBy` compared `Created`.
## Fix
In mesh-controller, in the same pull request as issue 348 (PR 179):
- A plan keeps when its merge was made (`merged_at`, migration 0086; null for a release and for a plan kept
before it).
- `supersededBy` orders two plans of a branch by their merge times where both are known, and by when they
were made only where one is not.
- A merge older than an open plan of its branch is planned at that plan's commit, which contains it, and
that plan is superseded by it as by any newer plan. What both merges moved is built once, from the newest
commit of the branch.
## How it is checked
`TestTwoMergesActedOnInReverseOrderBuildTheNewerCommit` replays the day's case: the later merge acted on
first, the earlier one second; one plan is left open, at the later commit, building what both moved.
`TestTheBranchOrderIsTheMerges` holds the order and the lookup. Both fail on the commit before the fix.
A replay in mesh-lab's register is still owed before this core issue resolves (ADR 0237).