Advise a recheck for an unanswered check on a ready or rejected delivery (issue 451)
mesh/merge-gate pass: builds mesh-controller → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.66s)
mesh/delivery-group group fix/451-a-recheck-is-the-step rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-controller → novox; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check fail: its merge-check.sh failed: --- FAIL: TestTheInstallersFirstUserListIsWhatTheControllerWouldCompose (0.66s)
mesh/delivery-group group fix/451-a-recheck-is-the-step rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
That check answered once, so the build queue is the wrong place to look. mesh-delivery names the delivery's state as data (held); the controller reads it and never the words. A line without it keeps the advice of 438.
This commit is contained in:
@@ -47,10 +47,14 @@ type stalledLine struct {
|
|||||||
// issue 443): a time inside a finished sentence cannot be said again in another zone. A mesh-delivery from before
|
// issue 443): a time inside a finished sentence cannot be said again in another zone. A mesh-delivery from before
|
||||||
// says none, and Waiting is said as it reads.
|
// says none, and Waiting is said as it reads.
|
||||||
Checks []waitingCheck `json:"checks,omitempty"`
|
Checks []waitingCheck `json:"checks,omitempty"`
|
||||||
For string `json:"for"`
|
// Held is the state of the delivery of a line of the state `unanswered`, ready or rejected, when one is held
|
||||||
Bound string `json:"bound"`
|
// (novox/hq issues 441, 451): its check answered once, so a recheck is the step. Empty for a head with no
|
||||||
H2 string `json:"h2"`
|
// delivery, and from a mesh-delivery that does not say it yet; the words are never read for it.
|
||||||
Says string `json:"says"`
|
Held string `json:"held,omitempty"`
|
||||||
|
For string `json:"for"`
|
||||||
|
Bound string `json:"bound"`
|
||||||
|
H2 string `json:"h2"`
|
||||||
|
Says string `json:"says"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitingCheck is one merge check a stalled line waits on, as mesh-delivery gives it: its context, its state —
|
// waitingCheck is one merge check a stalled line waits on, as mesh-delivery gives it: its context, its state —
|
||||||
|
|||||||
@@ -885,6 +885,11 @@ func stalledWords(l stalledLine, o conditions.Observation) (headline, explanatio
|
|||||||
// within its bound (novox/hq issue 438): mesh-delivery holds no delivery of it, so there is nothing to stop, release
|
// within its bound (novox/hq issue 438): mesh-delivery holds no delivery of it, so there is nothing to stop, release
|
||||||
// or close, and no action is offered. The operator's acts are a new commit, which asks the check again, or a look
|
// or close, and no action is offered. The operator's acts are a new commit, which asks the check again, or a look
|
||||||
// at the build queue, where a check that never ran may still wait.
|
// at the build queue, where a check that never ran may still wait.
|
||||||
|
//
|
||||||
|
// A head whose delivery is held in ready or rejected is said the same way (novox/hq issue 441), and mesh-delivery
|
||||||
|
// names that state as data (Held). Its check answered once, so the build queue is the wrong place to look: the step
|
||||||
|
// is a recheck of the delivery, named by its id, which asks the check again (novox/hq issue 451). It is advised, not
|
||||||
|
// offered as an action, since the controller is not granted the recheck.
|
||||||
func unansweredWords(l stalledLine, o conditions.Observation) (headline, explanation, resolved, needs string,
|
func unansweredWords(l stalledLine, o conditions.Observation) (headline, explanation, resolved, needs string,
|
||||||
actions []conditions.Action) {
|
actions []conditions.Action) {
|
||||||
repository, _, _ := strings.Cut(l.ID, "@")
|
repository, _, _ := strings.Cut(l.ID, "@")
|
||||||
@@ -899,13 +904,23 @@ func unansweredWords(l stalledLine, o conditions.Observation) (headline, explana
|
|||||||
if d, err := time.ParseDuration(l.Bound); err == nil {
|
if d, err := time.ParseDuration(l.Bound); err == nil {
|
||||||
limit = ", past its limit of " + humanDuration(d)
|
limit = ", past its limit of " + humanDuration(d)
|
||||||
}
|
}
|
||||||
which := "Its merge check"
|
check := "merge check"
|
||||||
if said := uncheckedWaitWords(l); len(said) > 0 {
|
if said := uncheckedWaitWords(l); len(said) > 0 {
|
||||||
which = "Its merge check (" + strings.Join(said, ", ") + ")"
|
check = "merge check (" + strings.Join(said, ", ") + ")"
|
||||||
}
|
}
|
||||||
if o.Resolver == conditions.ResolverOperator {
|
which := "Its " + check
|
||||||
needs = "push a new commit to its branch, which asks the check again, or see whether its check still waits " +
|
switch l.Held {
|
||||||
"in the build queue: mesh-controller.queue."
|
case "ready", "rejected":
|
||||||
|
which = "Its delivery is " + l.Held + ", but on the forge its " + check
|
||||||
|
if o.Resolver == conditions.ResolverOperator {
|
||||||
|
needs = "recheck its delivery, which asks its check again — mesh-delivery.recheck with id " + l.ID +
|
||||||
|
" and why — or push a new commit to its branch, " + FromMeshMCPServer
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
if o.Resolver == conditions.ResolverOperator {
|
||||||
|
needs = "push a new commit to its branch, which asks the check again, or see whether its check still " +
|
||||||
|
"waits in the build queue: mesh-controller.queue."
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%s's merge check has not answered %s", pull, long),
|
return fmt.Sprintf("%s's merge check has not answered %s", pull, long),
|
||||||
fmt.Sprintf("%s is open on a branch that requires the merge check. %s was asked and has not answered %s%s. "+
|
fmt.Sprintf("%s is open on a branch that requires the merge check. %s was asked and has not answered %s%s. "+
|
||||||
|
|||||||
@@ -452,3 +452,49 @@ func TestAnUnansweredMergeChecksTimeIsSaidInLocalTime(t *testing.T) {
|
|||||||
t.Fatalf("a pending check without its time reads %q", o.Explanation)
|
t.Fatalf("a pending check without its time reads %q", o.Explanation)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// **An unanswered merge check on a ready or rejected delivery is told to recheck it** (novox/hq issue 451):
|
||||||
|
// mesh-delivery says such a line too (issue 441) and names the delivery's state as data, `held`. That check already
|
||||||
|
// answered once, so the build queue is the wrong place to look; a recheck of the delivery, named by its id, asks the
|
||||||
|
// check again. A line with no delivery held keeps the advice of issue 438, and so does a line from a mesh-delivery
|
||||||
|
// that does not say `held` yet: the controller never guesses the state from the words.
|
||||||
|
func TestAnUnansweredMergeCheckOnAReadyOrRejectedDeliveryOffersARecheck(t *testing.T) {
|
||||||
|
line := func(held string) stalledLine {
|
||||||
|
var l stalledLine
|
||||||
|
raw := `{"id":"novox/mesh-controller@c11222026a3b","number":212,"state":"unanswered",` +
|
||||||
|
`"waiting":["mesh/merge-gate pending since 2026-10-11T00:11:39Z"],"for":"1h1m0s","bound":"1h0m0s",` +
|
||||||
|
`"h2":"none: its delivery is ` + held + `, a state the operator moves — the operator's",` +
|
||||||
|
`"says":"novox/mesh-controller#212 is open on main, which requires the merge check, and its delivery is ` +
|
||||||
|
held + ` here, but on the forge its check has not answered: mesh/merge-gate pending since ` +
|
||||||
|
`2026-10-11T00:11:39Z. The pull request waits on that status and nothing asks it again. A recheck of the ` +
|
||||||
|
`delivery asks its check again, as does a new commit on its branch"`
|
||||||
|
if held != "" {
|
||||||
|
raw += `,"held":"` + held + `"`
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal([]byte(raw+`}`), &l); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
for _, held := range []string{"ready", "rejected"} {
|
||||||
|
o := stalledObservations([]stalledLine{line(held)})[0]
|
||||||
|
t.Logf("%s: %s\nneeds: %s", held, o.Explanation, o.Needs)
|
||||||
|
if !strings.Contains(o.Needs, "mesh-delivery.recheck") || !strings.Contains(o.Needs, "novox/mesh-controller@c11222026a3b") {
|
||||||
|
t.Fatalf("a %s delivery's unanswered check is not offered its recheck: %q", held, o.Needs)
|
||||||
|
}
|
||||||
|
if strings.Contains(o.Needs, "build queue") || strings.Contains(o.Needs, "mesh-controller.queue") {
|
||||||
|
t.Fatalf("a %s delivery's unanswered check, which answered once, is sent to the build queue: %q", held, o.Needs)
|
||||||
|
}
|
||||||
|
if !strings.Contains(o.Explanation, "Its delivery is "+held) {
|
||||||
|
t.Fatalf("a %s delivery's explanation does not say its state: %q", held, o.Explanation)
|
||||||
|
}
|
||||||
|
if len(o.Actions) != 0 {
|
||||||
|
t.Fatalf("it offers an action the controller is not granted: %+v", o.Actions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// No delivery held, or a mesh-delivery that does not say: the advice of issue 438, and no recheck.
|
||||||
|
o := stalledObservations([]stalledLine{line("")})[0]
|
||||||
|
if !strings.Contains(o.Needs, "in the build queue: mesh-controller.queue") || strings.Contains(o.Needs, "recheck") {
|
||||||
|
t.Fatalf("a line with no delivery held says to %q", o.Needs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user