Merge pull request 'Issue 362: two merges seconds apart left their delivery waiting for a person' (#263) from issues/362-two-merges-seconds-apart-left-their-delivery-waiting-for-a-person into main
This commit was merged in pull request #263.
This commit is contained in:
+8
@@ -62,3 +62,11 @@ first, the earlier one second; one plan is left open, at the later commit, build
|
||||
done. `TestTheBranchOrderIsTheMerges` holds the order and the lookup, to the nanosecond. The first and the
|
||||
last fail on the commit before the fix, and every condition of the change was mutated and caught.
|
||||
A replay in mesh-lab's register is still owed before this core issue resolves (ADR 0237).
|
||||
|
||||
## A sequel (2026-10-10)
|
||||
|
||||
Planning an earlier merge at the later merge's commit left that earlier merge's delivery without a walk:
|
||||
mesh-delivery matches a walk to the delivery merged as its commit, so two catalogue merges 18 seconds apart
|
||||
left the earlier one held for a person and the walk carrying it waiting for a word nothing gave, for 58
|
||||
minutes, with no condition raised. See
|
||||
[issue 362](../362-two-merges-seconds-apart-left-their-delivery-waiting-for-a-person/00-report.md).
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
---
|
||||
status: located
|
||||
opened: 2026-10-10
|
||||
located-in: [mesh-catalog modules/mesh-delivery (holder.go deliveryWalking), mesh-controller cmd/mesh-controller (upgrades.go SourceMoved, release_plan.go laterOnTheBranch)]
|
||||
fixed-by:
|
||||
amended-design:
|
||||
---
|
||||
|
||||
# 362. Two catalogue merges seconds apart left their delivery waiting for a person
|
||||
|
||||
## Symptom
|
||||
|
||||
Shortly after midnight on 2026-10-10 (local time) two pull requests of the catalogue were merged
|
||||
18 seconds apart: the claude-code change at 00:20:52, then the dunst change at 00:21:09. Both had passed
|
||||
their checks.
|
||||
|
||||
`mesh-delivery.deliveries` then showed every delivery of the dunst pull request `superseded`, and the
|
||||
claude-code delivery `held`, waiting for "a person: merged, and the controller opened no walk for it within
|
||||
10m0s — nothing it holds follows that branch, or the merge was not heard: a person decides". The
|
||||
controller's `plans` said of the walk for the catalogue's head, which carried both merges: "published; its
|
||||
walk waits for mesh-delivery's word — `plans go … --why …` starts it by hand".
|
||||
|
||||
Both reasons were wrong. A walk had been opened, and it contained the held merge. Nothing gave it the word.
|
||||
The operator's change sat undelivered for 58 minutes, until the operator's hand-act `mesh-controller.plans
|
||||
go` started the walk. **No condition was raised to anyone in that time**: the hold is bounded at 24 hours
|
||||
before `mesh-delivery.stalled` lists it, and the walk's own wait raised nothing either.
|
||||
|
||||
## Why it is an issue
|
||||
|
||||
A merge that passed its checks must reach its nodes without a person, and a delivery that cannot must say
|
||||
so loudly and truly. Here both failed: two ordinary merges close together, which happens whenever the
|
||||
operator merges a batch, stopped the mesh's delivery silently, and the reason it gave pointed away from the
|
||||
cause.
|
||||
|
||||
## Where it lives
|
||||
|
||||
The diagnosis ([01-diagnosis.md](01-diagnosis.md)) traces it to two parts that each behave as written and
|
||||
disagree on what a walk's commit means:
|
||||
|
||||
- The controller plans a merge it hears after a later merge of the same branch at the later merge's commit,
|
||||
and supersedes the earlier walk (the fix for
|
||||
[issue 349](../349-a-merge-acted-on-late-superseded-the-merge-after-it-and-built-the-older-commit/00-report.md)).
|
||||
So the claude-code merge got a walk at the dunst merge's commit.
|
||||
- mesh-delivery finds the delivery a walk walks by its walk id or by the commit it was merged as. The new
|
||||
walk matched the dunst delivery, which its first walk's supersession had already ended, so no live
|
||||
delivery held the walk and nothing gave it its turn. The claude-code delivery, merged as a different
|
||||
commit, never got a walk and was held after ten minutes.
|
||||
|
||||
## Open
|
||||
|
||||
- How mesh-delivery learns which merges a walk carries: the controller's walk record naming every merge it
|
||||
answers (its commit and the commits planned into it), or mesh-delivery reading containment itself, as it
|
||||
already does to decide *covered*.
|
||||
- A superseded walk whose successor matches only final deliveries: whether the successor should be adopted
|
||||
as a delivery of its own, so that something always holds a waiting walk.
|
||||
- A walk waiting for mesh-delivery's word past a bound, and a delivery held because no walk was opened,
|
||||
raise a condition to the operator instead of waiting silently (failure must be loud).
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
# 362. Diagnosis
|
||||
|
||||
## 2026-10-10 — the trail
|
||||
|
||||
Times are local (UTC+2). Commits are the catalogue's.
|
||||
|
||||
**What the forge did.** The claude-code pull request was merged as 553b7191 at 00:20:52, and the dunst pull
|
||||
request as 48bda475 at 00:21:09. 48bda475 is the branch's head and contains 553b7191.
|
||||
|
||||
**What the controller did** (its journal, 00:21:14 to 00:21:15):
|
||||
|
||||
1. It heard the dunst merge first and opened a walk at 48bda475 for dunst, waiting for mesh-delivery's word.
|
||||
2. It then heard the claude-code merge, and printed "553b7191 was merged before 48bda475 (…, building): what
|
||||
it moved is built from 48bda475, which contains it". This is the rule that answered
|
||||
[issue 349](../349-a-merge-acted-on-late-superseded-the-merge-after-it-and-built-the-older-commit/00-report.md):
|
||||
`SourceMoved` replaces the merge's commit and merge time with the newest planned merge's, when that merge
|
||||
is later on the branch (`laterOnTheBranch`).
|
||||
3. The second walk, also at 48bda475 and with the same merge time, superseded the first by when each was made
|
||||
(`supersededBy`, `earlierOnTheBranch`), and carried claude-code and dunst. It waited for mesh-delivery's
|
||||
word.
|
||||
|
||||
So every walk the controller opened was at 48bda475. None was at 553b7191.
|
||||
|
||||
**What mesh-delivery did** (`mesh-delivery.show` for both deliveries):
|
||||
|
||||
1. The dunst delivery (merged as 48bda475) took the first walk, matched by its commit, and became
|
||||
`published` at 00:21:14.
|
||||
2. The first walk's record came back superseded, and the table's `superseded` transition ended the dunst
|
||||
delivery at 00:21:15 ("a newer delivery to the same trunk took over its walk").
|
||||
3. The second walk was matched to a delivery by `deliveryWalking`, which looks first for a delivery holding
|
||||
that walk id and then for any delivery whose `merged_as` is the walk's commit, final or not. That is the
|
||||
dunst delivery again, already final. The walk was linked there, so it was never kept as unmatched and
|
||||
never adopted as a delivery of its own. A turn (`turn`) is given only to a `published` or `delivering`
|
||||
delivery, so nothing ever gave the second walk its word.
|
||||
4. The claude-code delivery, merged as 553b7191, matched no walk. Ten minutes after its merge
|
||||
(`unwalkedPast`) it was held for a person at 00:30:58, with a reason that names two causes, neither of
|
||||
which happened.
|
||||
|
||||
After the operator's `plans go`, the second walk's machine steps (claude-code and dunst, each on a node) were recorded on the superseded dunst delivery, which confirms that mesh-delivery reads that walk as
|
||||
the dunst delivery's.
|
||||
|
||||
**Why nothing was raised.** A delivery held for a person is bounded at 24 hours before
|
||||
`mesh-delivery.stalled` lists it (`Bounds` in its table). The walk waiting for mesh-delivery's word is
|
||||
counted against the controller's plan wait bound of 30 minutes for `status`, but no condition reached the
|
||||
operator in the 58 minutes it waited. I did not trace why the controller's side stayed quiet; that belongs
|
||||
to the fix.
|
||||
|
||||
## Ruled out
|
||||
|
||||
- **The merge was not heard.** The controller's journal shows it was heard and planned within 23 seconds.
|
||||
- **Nothing follows that branch.** claude-code follows the catalogue's trunk; the second walk built it.
|
||||
- **A failed check.** Both heads passed the merge gate and the repository's check.
|
||||
|
||||
## Located
|
||||
|
||||
Two parts, each correct by its own rule, disagree on what a walk's commit means. The controller (since issue
|
||||
349) plans an earlier merge at a later commit that contains it; mesh-delivery assumes a walk's commit is the
|
||||
merge commit of exactly one delivery. `located-in` names both. The fix is to make the walk say which merges
|
||||
it answers, or to make mesh-delivery match by containment, and to let no waiting walk be held by nothing.
|
||||
Reference in New Issue
Block a user