F3: mesh-build-go and mesh-build-node follow the drops other holders write, and a module with no watch says so (ADR 0664 §4, issue 1748 review 1)

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-09 23:42:13 +01:00
parent 1948da9cb4
commit 6a29e41175
6 changed files with 34 additions and 0 deletions
@@ -90,6 +90,7 @@ func (p *Puller) Drop(id, by, why string) error {
// the pull then refuses a dropped ask but cannot end a running one.
func (p *Puller) watchDrops() {
if p.WatchPauses == nil {
p.logf("no watch of the drops in the pauses of %s is set: a dropped ask that runs here is not ended, only refused when it is pulled", p.Node)
return
}
err := p.WatchPauses(sdkstep.DroppedPrefix+"**", func(ch stdio.StateChange) error {
@@ -132,6 +132,12 @@ func TestADroppedAskIsRefusedAtPullAndARunningOneIsEnded(t *testing.T) {
fresh, _ := json.Marshal(nodebuildgo.Drop{V: int64(sdkstep.Version), Why: "fresh", At: ptr(time.Now())})
kv.put(sdkstep.DropKey("old"), json.RawMessage(old))
kv.put(sdkstep.DropKey("fresh"), json.RawMessage(fresh))
// Review 1: a module that sets no watch of the drops says so (a dropped ask running here is then not ended).
var said []string
(&Puller{Node: "shanks", Logf: func(f string, a ...any) { said = append(said, fmt.Sprintf(f, a...)) }}).watchDrops()
if len(said) != 1 || !strings.Contains(said[0], "no watch of the drops") {
t.Errorf("a puller with no watch said %q", said)
}
p := &Puller{Load: kv.load, ForgetPause: kv.forget, PauseKeys: kv.keys}
p.sweepDropped()
if keys, _ := kv.keys(); len(keys) != 1 || keys[0] != sdkstep.DropKey("fresh") {
@@ -45,6 +45,7 @@ func main() {
Pauses: stdio.State(sdkstep.PausesState).Put,
ReadPauses: readOf(stdio.State(sdkstep.PausesState)),
ForgetPause: stdio.State(sdkstep.PausesState).Delete,
WatchPauses: stdio.State(sdkstep.PausesState).Watch,
ReadToolchainsState: readOf(stdio.State(seat.StateToolchains)),
Ctx: ctx,
Say: logf,
@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"errors"
"os"
"strings"
"sync"
"testing"
@@ -198,3 +199,15 @@ func TestParseGoVersion(t *testing.T) {
t.Errorf("accepted a text that is not `go version`'s")
}
}
// ADR 0664 §4 (issue 1748, review 1): the module follows the drops other holders write in the shared pauses state, or a drop
// served by another node would never end a step running here. main wires the watch.
func TestMainWiresTheWatchOfTheDrops(t *testing.T) {
raw, err := os.ReadFile("main.go")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(raw), "WatchPauses:") {
t.Error("main.go does not set Module.WatchPauses: a drop written by another holder would not end a running step")
}
}
@@ -51,6 +51,7 @@ func main() {
Pauses: stdio.State(sdkstep.PausesState).Put,
ReadPauses: readOf(stdio.State(sdkstep.PausesState)),
ForgetPause: stdio.State(sdkstep.PausesState).Delete,
WatchPauses: stdio.State(sdkstep.PausesState).Watch,
ReadToolchainsState: readOf(stdio.State(seat.StateToolchains)),
Work: WorkDir,
Run: dforgekey.ExecRunnerClean(0),
@@ -228,3 +228,15 @@ func TestParseNodeVersion(t *testing.T) {
t.Errorf("accepted a text that is not `node --version`'s")
}
}
// ADR 0664 §4 (issue 1748, review 1): the module follows the drops other holders write in the shared pauses state, or a drop
// served by another node would never end a step running here. main wires the watch.
func TestMainWiresTheWatchOfTheDrops(t *testing.T) {
raw, err := os.ReadFile("main.go")
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(raw), "WatchPauses:") {
t.Error("main.go does not set Module.WatchPauses: a drop written by another holder would not end a running step")
}
}