Keep the core over an older record instead of refusing the send (issue 201, review 90)
mesh/merge-gate out of date: judged against trunk f8daec01d3f0, the trunk is now ba685b17f5e1; judged again on a recheck or when it is merged
mesh/repo-check out of date: judged against trunk f8daec01d3f0, the trunk is now ba685b17f5e1; judged again on a recheck or when it is merged
mesh/delivery superseded: a newer head of the same pull request

Refusing the whole send wedged a machine whose gate put back the
controller and whose put-back send was refused: every later automatic
send there was refused, retried each tick, and held up every other walk.

A send now composes the controller and the node-engine at the build the
machine was last sent when the mesh's record is older, and carries the
rest; nothing is refused or retried, and a plan whose own build is the
older one ends with its gate superseded. A sent build that failed its
gate is never kept, so the next send completes a put-back. The condition
names the two ways out and clears on any send that carries the record,
a person's push included.
This commit is contained in:
jochen
2026-10-12 18:51:17 +02:00
parent 6bb1c9b768
commit d170491256
6 changed files with 267 additions and 200 deletions
+11 -35
View File
@@ -251,47 +251,26 @@ func ungatedIn(ctx context.Context, open *stores, names []string, addedHolder st
if err != nil {
return nil, err
}
var kept, refused, older []string
var kept, refused []string
for _, n := range names {
// Every move, gated or not: a move to a build that passed a gate elsewhere is no gate's concern here, and
// may still be a core module's downgrade (novox/hq issue 201).
every, err := machineMoves(ctx, open, f, n, true)
moves, err := machineMoves(ctx, open, f, n, false)
if err != nil {
return nil, err
}
// The grants step composes every module at the build its machine was last sent (novox/hq issue 490):
// a move it keeps is not made. Read, not assumed, so a move it could not keep is still refused here.
var keeps map[string]string
if everyBuildKept(ctx, n) {
if keeps, err = recordedKept(ctx, open, n); err != nil {
return nil, err
}
// The grants step composes every module at the build its machine was last sent (novox/hq issue 490),
// and every send keeps the controller and the node-engine over an older record (issue 201): a move it
// keeps is not made. Read, not assumed, so a move it could not keep is still refused here.
keeping := ctx
if _, on := keptExcept(ctx); !on {
keeping = keepingRecorded(ctx) // as the send composes it (sendKeeps), which is set after this check
}
var made []inventory.CarriedMove
for _, mv := range every {
if was, kept := keeps[mv.Module]; kept && sameCommit(was, mv.From) {
continue
}
made = append(made, mv)
}
// **No send but a gate's own put-back moves the controller or the node-engine to an older build than
// the one its machine runs** (novox/hq issue 201).
downgrades, err := refuseCoreDowngrades(ctx, open.inventory, scope, n, made)
keeps, err := recordedKept(keeping, open, n)
if err != nil {
return nil, err
}
if len(downgrades) > 0 {
if n == addedHolder {
fmt.Printf("%s holds the bus and its user list changed, and it is not sent now: %s\n", n,
strings.Join(downgrades, "; "))
continue
}
older = append(older, downgrades...)
continue
}
var waiting []string
for _, mv := range made {
if f.gated(mv.Module, mv.To) {
for _, mv := range moves {
if was, kept := keeps[mv.Module]; kept && sameCommit(was, mv.From) {
continue
}
if !scope.judged[n] && !scope.modules[mv.Module] {
@@ -308,9 +287,6 @@ func ungatedIn(ctx context.Context, open *stores, names []string, addedHolder st
refused = append(refused, fmt.Sprintf("%s (%s)", n, strings.Join(waiting, ", ")))
}
}
if len(older) > 0 {
return nil, fmt.Errorf("%w: %s", errCoreDowngrade, strings.Join(older, "; "))
}
if len(refused) > 0 {
return nil, fmt.Errorf("%w: %s — a walk sends them, one machine at a time, each judged (novox/hq ADR "+
"0236); `upgrade backlog` lists them", errUngated, strings.Join(refused, "; "))