Runner (A-328): never hold the runner's own pipeline for its new version
A take-in that changes the runner held its own pipeline with the others, so its new version could not be wanted before the 30-minute bound and its change was judged by the old runner. The runner's own pipeline now runs on the running runner, as a build module's running version builds its successor; its pass lets the new runner be wanted, whose running releases the held ones. The bound stays as the failure bound, with its finding. Builds: design 47 G §29, §65, §270; A-328 Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
@@ -56,9 +56,20 @@ func TestPipelinesAreHeldForTheNewRunnerAndReleasedWhenItRuns(t *testing.T) {
|
||||
if p.Held == nil || p.Held.Fingerprint != "src2:new" || len(p.Steps) != 0 || len(asks.asks) != 0 {
|
||||
t.Fatalf("held = %+v, steps %d, asks %d", p.Held, len(p.Steps), len(asks.asks))
|
||||
}
|
||||
if strings.Join(said, "|") != "wait-held.app.main.c1|wait-held.runner.main.c2" || len(bounds) != 2 || bounds[0] != "src2:new" {
|
||||
// The runner's own pipeline is never held: it runs on the running runner, so its pass lets the new runner be wanted (A-328).
|
||||
if p, _, _ := store.Get("main.4.runner"); p.Held != nil || p.Outcome != seat.PipelineOutcomeRunning {
|
||||
t.Errorf("the runner's own pipeline is held %+v, want it running on the running runner", p.Held)
|
||||
}
|
||||
if strings.Join(said, "|") != "wait-held.app.main.c1|pipeline-started.runner.main.c2" || len(bounds) != 1 || bounds[0] != "src2:new" {
|
||||
t.Errorf("said %v, bounds %v", said, bounds)
|
||||
}
|
||||
// A later take-in of the runner's own module while one is held is not held either (A-328).
|
||||
if n, err := s.TakenIn(catalog.TakeInTaken{Key: "m.3", Branch: "main", Source: 6, Entries: []catalog.TakeInEntry{{Module: "runner", Commit: "c4", Fingerprint: "src2:new"}}}, view); err != nil || n != 1 {
|
||||
t.Fatalf("a take-in of the runner alone = %d, %v", n, err)
|
||||
}
|
||||
if p, _, _ := store.Get("main.6.runner"); p.Held != nil {
|
||||
t.Errorf("the runner's own later pipeline is held %+v", p.Held)
|
||||
}
|
||||
// A later take-in not naming the runner is held for the same version.
|
||||
if n, err := s.TakenIn(catalog.TakeInTaken{Key: "m.2", Branch: "main", Source: 5, Entries: []catalog.TakeInEntry{{Module: "app", Commit: "c3", Fingerprint: "src2:bb"}}}, view); err != nil || n != 1 {
|
||||
t.Fatalf("second take-in = %d, %v", n, err)
|
||||
@@ -72,7 +83,7 @@ func TestPipelinesAreHeldForTheNewRunnerAndReleasedWhenItRuns(t *testing.T) {
|
||||
}
|
||||
said = nil
|
||||
n, err = s.Release(store.memStore, "src2:new", "ran", view)
|
||||
if err != nil || n != 3 {
|
||||
if err != nil || n != 2 {
|
||||
t.Fatalf("release = %d, %v", n, err)
|
||||
}
|
||||
p, _, _ = store.Get("main.4.app")
|
||||
@@ -114,7 +125,7 @@ func TestReleaseVerbStartsTheHeldPipelinesByHand(t *testing.T) {
|
||||
}
|
||||
said = nil
|
||||
a, err := v.Release("operator", seat.ReleaseInput{Why: "the new runner will not come"})
|
||||
if err != nil || a.Released != 2 {
|
||||
if err != nil || a.Released != 1 { // the runner's own pipeline was never held (A-328)
|
||||
t.Fatalf("release = %+v, %v", a, err)
|
||||
}
|
||||
if p, _, _ := store.Get("main.4.app"); p.Held != nil || len(p.Steps) == 0 || len(asks.asks) == 0 {
|
||||
@@ -193,7 +204,7 @@ func TestHeldWaitCountsFromTheBuildEndOfTheAwaitedRunner(t *testing.T) {
|
||||
if _, err := s.TakenIn(taken, view); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(waits) != 2 || waits[0].ClockFrom != nil || len(armed) != 0 {
|
||||
if len(waits) != 1 || waits[0].ClockFrom != nil || len(armed) != 0 { // the runner's own pipeline is never held (A-328)
|
||||
t.Fatalf("waits %+v, armed %v", waits, armed)
|
||||
}
|
||||
// A failed end starts no clock; a done end does.
|
||||
@@ -204,10 +215,10 @@ func TestHeldWaitCountsFromTheBuildEndOfTheAwaitedRunner(t *testing.T) {
|
||||
t.Fatalf("heard %q %q %v", m, fp, ok)
|
||||
}
|
||||
waits = nil
|
||||
if n, err := s.Clocked(store.memStore, "runner", "src2:new"); err != nil || n != 2 {
|
||||
if n, err := s.Clocked(store.memStore, "runner", "src2:new"); err != nil || n != 1 {
|
||||
t.Fatalf("clocked = %d, %v", n, err)
|
||||
}
|
||||
if len(waits) != 2 || waits[0].ClockFrom == nil || !waits[0].ClockFrom.Equal(end) || len(armed) != 1 || armed[0] != "src2:new@2026-11-11T12:00:00Z" {
|
||||
if len(waits) != 1 || waits[0].ClockFrom == nil || !waits[0].ClockFrom.Equal(end) || len(armed) != 1 || armed[0] != "src2:new@2026-11-11T12:00:00Z" {
|
||||
t.Errorf("waits %+v, armed %v", waits, armed)
|
||||
}
|
||||
// A pipeline held after the end is known says the clock at once.
|
||||
|
||||
@@ -122,7 +122,12 @@ func (s *Starter) Start(b Begin) (bool, error) {
|
||||
if err := s.Fills.refuseStart(key); err != nil {
|
||||
return false, err
|
||||
}
|
||||
if b.Await == "" && s.Awaiting != nil {
|
||||
// The runner's own pipeline is never held: it runs on the running runner, as a build module's running version builds its
|
||||
// successor (§270, A-328), so its pass lets the new runner be wanted (§29).
|
||||
if b.Module != "" && b.Module == s.Self {
|
||||
b.Await = ""
|
||||
}
|
||||
if b.Await == "" && s.Awaiting != nil && b.Module != s.Self {
|
||||
if fp, ok := s.Awaiting(); ok {
|
||||
b.Await = fp
|
||||
}
|
||||
@@ -403,7 +408,8 @@ func (s *Starter) TakenIn(ev catalog.TakeInTaken, view func(branch string) (Half
|
||||
half, _ := view(ev.Branch)
|
||||
started := 0
|
||||
var first error
|
||||
// A take-in giving the runner's own module a new fingerprint holds every pipeline it starts for that runner (§65, A-195).
|
||||
// A take-in giving the runner's own module a new fingerprint holds every other pipeline it starts for that runner (§65,
|
||||
// A-195, A-328); the runner's own runs now (Start).
|
||||
await := ""
|
||||
for _, e := range ev.Entries {
|
||||
if s.changesSelf(e.Module, e.Fingerprint) {
|
||||
|
||||
Reference in New Issue
Block a user