checker_ask_again: an unread backlog and a recheck ask refuse too (issue 1373, review)
mesh/delivery merged: source.main moved to novox/mesh-delivery@7221a1e5cd7f
mesh/check checking 2ab74991b856 on 7221a1e5cd7f

Principle 3: unread is never empty. build_usage's notes refuse; a waiting or held recheck ask refuses like a check ask.

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
This commit is contained in:
2026-11-13 00:48:36 +01:00
parent b6e61fe63d
commit 2ab74991b8
4 changed files with 23 additions and 2 deletions
@@ -7,6 +7,7 @@ import (
"encoding/json"
"fmt"
"os"
"strings"
"sync/atomic"
"time"
@@ -515,10 +516,14 @@ func backlogOf(ask func(string, any) (json.RawMessage, error)) func() ([]check.B
}
var a struct {
Backlog []check.BacklogRow `json:"backlog"`
Notes []string `json:"notes"`
}
if err := json.Unmarshal(answer, &a); err != nil {
return nil, fmt.Errorf("%s answered what does not decode: %w", buildUsageTool, err)
}
if len(a.Notes) > 0 { // the backlog was not read: unread is never empty
return nil, fmt.Errorf("%s did not read the backlog: %s", buildUsageTool, strings.Join(a.Notes, "; "))
}
return a.Backlog, nil
}
}
@@ -165,6 +165,20 @@ func TestTheToolsServedAreTheFiveReadTools(t *testing.T) {
}
})
}
// 1373: the build seats' backlog is read through build_usage; one it says it did not read is never taken for empty.
for _, c := range []struct {
name, answer string
rows int
failed bool
}{
{name: "a backlog read", answer: `{"backlog":[{"language":"go","kind":"check","waiting":2}]}`, rows: 1},
{name: "a backlog with a note says it was not read", answer: `{"backlog":[],"notes":["the seats' streams cannot be read here"]}`, failed: true},
} {
rows, err := backlogOf(func(string, any) (json.RawMessage, error) { return json.RawMessage(c.answer), nil })()
if (err != nil) != c.failed || len(rows) != c.rows || (c.failed && !strings.Contains(err.Error(), "cannot be read here")) {
t.Errorf("1373 backlog, %s: %v, %v", c.name, rows, err)
}
}
// With nothing to read from a tool says so and changes nothing.
for _, tl := range Tools(check.Reader{}) {
if tl.Name == check.StatusReadTool {
@@ -255,7 +255,7 @@ type BacklogRow struct {
Error string `json:"error,omitempty"`
}
// noCheckAskWaits is nil when no check ask waits or is held on any build seat's stream and every row was read; else it
// noCheckAskWaits is nil when no check or recheck ask waits or is held on any build seat's stream and every row was read; else it
// names the stream that holds asks. It cannot tell whose ask one is, so any check ask refuses: asking again could run it twice.
func (r Reader) noCheckAskWaits() error {
rows, err := r.Backlog()
@@ -263,7 +263,7 @@ func (r Reader) noCheckAskWaits() error {
return fmt.Errorf("the build seats' streams cannot be read (%v), so whether an ask of the check waits in one cannot be told; asking again could run it twice", err)
}
for _, row := range rows {
if row.Kind != "check" {
if row.Kind != "check" && row.Kind != "recheck" { // both kinds are the checker's asks (judge.go)
continue
}
if row.Error != "" {
@@ -340,6 +340,8 @@ func TestACheckWhoseAskJudgedNothingIsAskedAgainOrRead(t *testing.T) {
bad string
}{{"a check ask waits", []BacklogRow{{Language: "go", Kind: "check", Waiting: 1}}, "holds asks"},
{"a check ask is held", []BacklogRow{{Language: "go", Kind: "check", Held: 1}}, "holds asks"},
{"a recheck ask waits", []BacklogRow{{Language: "go", Kind: "recheck", Waiting: 1}}, "holds asks"},
{"a recheck ask is held", []BacklogRow{{Language: "go", Kind: "recheck", Held: 1}}, "holds asks"},
{"a stream cannot be read", []BacklogRow{{Language: "go", Kind: "check", Error: "no worker"}}, "cannot be read"},
{"only a delivery ask waits", []BacklogRow{{Language: "go", Kind: "delivery", Waiting: 3}}, ""}} {
noAsk.Backlog = func() ([]BacklogRow, error) { return c.rows, nil }