Ask a failed delivery's Retry on the phone, tell a refused answer, and pass a seat to its only claimant (ADR 0449, ADR 0451) #402

Merged
mesh-tools merged 8 commits from feat/no-terminal into main 2026-10-19 22:44:08 +00:00
4 changed files with 108 additions and 5 deletions
Showing only changes of commit f1775281e2 - Show all commits
+11 -2
View File
@@ -2,6 +2,7 @@ package main
import (
"context"
"errors"
"fmt"
"regexp"
"sort"
@@ -30,6 +31,9 @@ type failedPlanFacts struct {
since time.Time
// refusal is why `plans retry` would not run it, empty when it would.
refusal string
// passed is set when a newer plan of its repository and branch is done and the retry is refused for that
// (novox/hq ADR 0451): the condition asks nothing.
passed bool
}
// machines are the machines the failed modules run on, sorted, each once.
@@ -135,11 +139,14 @@ func planFailedWords(p failedPlanFacts) (headline, explanation, needs string, ac
} else {
explanation = fmt.Sprintf("The delivery of %s stopped before it finished.", what)
}
if p.refusal == "" {
switch {
case p.passed:
explanation += " A newer delivery has passed it, so a retry would send older code. Nothing is needed from you."
case p.refusal == "":
explanation += " Nothing is tried again until you say so."
needs = "try the failed builds again " + FromMeshMCPServer
actions = []conditions.Action{retryAction(p.id)}
} else {
default:
explanation += " " + conditions.Capital(plainRefusal(p.refusal)) + "."
needs = "look at why it failed; a retry would not run now."
}
@@ -213,6 +220,8 @@ func failedFactsOf(p inventory.Plan, plans []inventory.Plan, onPath map[string]s
}
if err := retryRefusal(p, plans, onPath); err != nil {
f.refusal = err.Error()
var passed passedBy
f.passed = errors.As(err, &passed)
}
return f
}
+61
View File
@@ -190,3 +190,64 @@ func TestNoClockRetriesAFailedPlan(t *testing.T) {
t.Fatalf("a plan failed beyond the window is still raised: %+v", got)
}
}
// A later plan of the same repository and branch, in a state, made after the failed one.
func laterPlan(id, branch, state string, after inventory.Plan) inventory.Plan {
q := aFailedPlan(id, func(p *inventory.Plan) {
p.State, p.Branch, p.Created = state, branch, after.Created.Add(time.Hour)
})
return q
}
// 1328/AC1: a later done plan of the same repository and branch makes `plans retry` refuse, naming the later plan.
func TestARetryIsRefusedWhenALaterPlanOfTheBranchIsDone(t *testing.T) {
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", time.Date(2026, 10, 19, 12, 0, 0, 0, time.UTC) })
later := laterPlan("plan-2", "main", inventory.PlanDone, failed)
err := retryRefusal(failed, []inventory.Plan{failed, later}, map[string]string{})
if err == nil || !strings.Contains(err.Error(), "plan-2") || !strings.Contains(err.Error(), "passed") {
t.Fatalf("the retry was not refused, naming the later plan: %v", err)
}
}
// 1328/AC2: with the later plan still open the refusal is today's; a later done plan of another branch refuses nothing.
func TestARetryIsRefusedAsBeforeWhileTheLaterPlanIsOpenAndNotForAnotherBranch(t *testing.T) {
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", time.Date(2026, 10, 19, 12, 0, 0, 0, time.UTC) })
open := laterPlan("plan-2", "main", inventory.PlanBuilding, failed)
err := retryRefusal(failed, []inventory.Plan{failed, open}, map[string]string{})
if err == nil || !strings.Contains(err.Error(), "supersedes it") {
t.Fatalf("with the later plan open the refusal is not today's: %v", err)
}
other := laterPlan("plan-3", "feature/x", inventory.PlanDone, failed)
if err := retryRefusal(failed, []inventory.Plan{failed, other}, map[string]string{}); err != nil {
t.Fatalf("a later done plan of another branch refused the retry: %v", err)
}
earlier := laterPlan("plan-4", "main", inventory.PlanDone, failed)
earlier.Created = failed.Created.Add(-time.Hour)
if err := retryRefusal(failed, []inventory.Plan{failed, earlier}, map[string]string{}); err != nil {
t.Fatalf("an earlier done plan refused the retry: %v", err)
}
}
// 1328/AC3: such a plan's plan-failed condition has no action and says it was passed by a newer delivery.
func TestAPassedFailedPlanHasNoActionAndSaysItWasPassed(t *testing.T) {
now := time.Date(2026, 10, 19, 18, 30, 0, 0, time.UTC)
failed := aFailedPlan("plan-1", func(p *inventory.Plan) { p.Branch, p.Created = "main", now.Add(-5*time.Hour) })
later := laterPlan("plan-2", "main", inventory.PlanDone, failed)
f := calm(now)
f.failed = []failedPlanFacts{failedFactsFor(failed, later)}
got := watchPlansFailed(f)
if len(got) != 1 || len(got[0].Actions) != 0 {
t.Fatalf("%+v", got)
}
o := got[0]
if !strings.Contains(o.Explanation, "newer delivery") || !strings.Contains(o.Explanation, "passed") {
t.Errorf("the words do not say it was passed by a newer delivery: %q", o.Explanation)
}
if strings.Contains(o.Explanation, "details say why") {
t.Errorf("the generic fallback was said: %q", o.Explanation)
}
words := conditions.Words{Headline: o.Headline, Explanation: o.Explanation, Resolved: o.Resolved, Needs: o.Needs}
if why, ok := conditions.PlainWords(words, "g14", "shanks"); !ok {
t.Errorf("not plain: %s\n%+v", why, words)
}
}
+30
View File
@@ -154,6 +154,31 @@ func newerOpenPlan(p inventory.Plan, plans []inventory.Plan) (inventory.Plan, bo
return inventory.Plan{}, false
}
// newerDonePlan is a done plan of the same repository and branch made after this one: the newer commit it delivered
// runs now (novox/hq ADR 0451).
func newerDonePlan(p inventory.Plan, plans []inventory.Plan) (inventory.Plan, bool) {
var found inventory.Plan
var ok bool
for _, q := range plans {
if q.ID == p.ID || q.State != inventory.PlanDone || !strings.EqualFold(q.Repository, p.Repository) ||
(p.Branch != "" && q.Branch != "" && p.Branch != q.Branch) || !q.Created.After(p.Created) {
continue
}
if !ok || q.Created.After(found.Created) {
found, ok = q, true
}
}
return found, ok
}
// passedBy is the refusal of a retry of a plan that a newer done plan of its repository and branch has passed.
type passedBy struct{ plan, by, repository, commit string }
func (e passedBy) Error() string {
return fmt.Sprintf("%s was passed by %s, a newer plan of %s (at %s) that is done: retrying it would build and "+
"send the older commit over the newer one that runs", e.plan, e.by, e.repository, short(e.commit))
}
// retryRefusal is why a plan cannot be retried, or nothing.
func retryRefusal(p inventory.Plan, plans []inventory.Plan, onPath map[string]string) error {
switch {
@@ -165,6 +190,11 @@ func retryRefusal(p inventory.Plan, plans []inventory.Plan, onPath map[string]st
return fmt.Errorf("%s is still %s; nothing in it failed to retry — `rebuild <module>` asks one module again", p.ID, p.State)
}
// **A newer delivery of the branch has passed it** (novox/hq ADR 0451): retrying would build and send the older
// commit over the newer one that runs.
if q, found := newerDonePlan(p, plans); found {
return passedBy{plan: p.ID, by: q.ID, repository: q.Repository, commit: q.Commit}
}
if len(failedIn(p)) > 0 {
if q, found := newerOpenPlan(p, plans); found {
return fmt.Errorf("%s supersedes it: a newer merge of %s (%s at %s) is open, and retrying %s would build "+
+6 -3
View File
@@ -79,11 +79,14 @@ func TestRetryRefusesWhatItCannotResume(t *testing.T) {
if err := retryRefusal(stopped, nil, ownPathOf(nil)); err == nil || !strings.Contains(err.Error(), "nothing in tier 0") {
t.Errorf("a plan with nothing failed to build was retried: %v", err)
}
// A failed or done plan does not supersede it.
if err := retryRefusal(failed, []inventory.Plan{plan("plan-4", inventory.PlanFailed, at.Add(time.Hour)),
plan("plan-5", inventory.PlanDone, at.Add(time.Hour))}, ownPathOf(nil)); err != nil {
// A later failed plan does not supersede it; a later done one passes it (novox/hq ADR 0451).
if err := retryRefusal(failed, []inventory.Plan{plan("plan-4", inventory.PlanFailed, at.Add(time.Hour))}, ownPathOf(nil)); err != nil {
t.Errorf("refused for a plan that does not supersede it: %v", err)
}
if err := retryRefusal(failed, []inventory.Plan{plan("plan-5", inventory.PlanDone, at.Add(time.Hour))}, ownPathOf(nil)); err == nil ||
!strings.Contains(err.Error(), "plan-5") {
t.Errorf("a plan a later done plan passed was retried: %v", err)
}
// Another branch's open walk, or an older one, does not supersede it, and is one walk open: one at a time
// (novox/hq ADR 0276).
other := plan("plan-3", inventory.PlanBuilding, at.Add(time.Hour))