Send the bus's user list alone before a walk's gate, so a new grant is on the bus when the first machine is judged (issue 490)
mesh/merge-gate pass: builds build-agent, mesh-controller → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request

A walk left the machine holding the bus out of a gated send whenever a
build waited there for that same gate, so a module's new health tool was
refused by the bus on its first machine and the gate could not pass. The
grants step sends that machine its declaration with every build kept,
before the first machine's send; plans and the change plan say it.
This commit is contained in:
2026-10-11 19:01:41 +02:00
parent 9edc5c758d
commit e1367d185a
8 changed files with 335 additions and 17 deletions
+39 -3
View File
@@ -61,20 +61,52 @@ func keptExcept(ctx context.Context) (map[string]bool, bool) {
return skip, on
}
type keepEveryBuildKey struct{}
// keepingEveryBuild is a context whose sends compose every module — recorded or rolling out — at the build
// its machine was last sent (novox/hq issue 490): the grants step, which sends the machine holding the bus
// its new user list and nothing of any module's new code. That code waits there for a gate like any other
// move; the list must not, or the first machine's gate is judged against a bus that refuses what the change
// newly grants.
func keepingEveryBuild(ctx context.Context) context.Context {
return context.WithValue(ctx, keepEveryBuildKey{}, true)
}
// everyBuildKept is whether this context keeps every module at the build its machine runs.
func everyBuildKept(ctx context.Context) bool {
on, _ := ctx.Value(keepEveryBuildKey{}).(bool)
return on
}
// recordedKept is, for a send under keepingRecorded, every recorded module the machine was last sent a
// build of that the mesh's build is not identical to: module → the commit it keeps. Nil when the context
// keeps nothing, or when what the machine was last sent is not known (it is then held whole elsewhere —
// ADR 0221).
//
// Under keepingEveryBuild, every module the machine was sent is kept, whatever its policy (novox/hq issue
// 490), and a machine whose last send is not known refuses the send: there is nothing to keep it at, and
// composing the mesh's builds would be the very move the grants step must not make.
func recordedKept(ctx context.Context, open *stores, node string) (map[string]string, error) {
every := everyBuildKept(ctx)
skip, on := keptExcept(ctx)
if !on {
if !on && !every {
return nil, nil
}
if every {
skip = nil
}
inv := open.inventory
sent, known, err := inv.SentBuilds(ctx, node)
if err != nil || !known {
if err != nil {
return nil, err
}
if !known {
if every {
return nil, fmt.Errorf("what %s was last sent is not known, so the bus's user list cannot be sent "+
"there alone with every build kept (novox/hq issue 490)", node)
}
return nil, nil
}
current, err := inv.CurrentBuilds(ctx)
if err != nil {
return nil, err
@@ -83,7 +115,7 @@ func recordedKept(ctx context.Context, open *stores, node string) (map[string]st
out := map[string]string{}
for m, was := range sent {
now, held := current[m]
if !held || now.RollOut || skip[m] || was == "" || sameCommit(was, now.Commit) {
if !held || (now.RollOut && !every) || skip[m] || was == "" || sameCommit(was, now.Commit) {
continue
}
if f == nil {
@@ -118,6 +150,10 @@ func keepRecorded(ctx context.Context, open *stores, node string, shelf map[stri
if err != nil {
return nil, err
}
if !found && everyBuildKept(ctx) {
return nil, fmt.Errorf("%s runs %s's build %s, which the build records no longer hold: the bus's user "+
"list cannot be sent there alone with it kept (novox/hq issue 490)", node, m, short(kept[m]))
}
if !found {
return nil, fmt.Errorf("%s records rather than rolls out, and %s runs its build %s, which the build "+
"records no longer hold: this send cannot keep it and does not move it — `push %s` sends the new "+