Merge remote-tracking branch 'origin/main' into fix/1656-xterm-config

This commit is contained in:
2026-10-21 19:43:02 +02:00
4 changed files with 127 additions and 1 deletions
+47
View File
@@ -78,3 +78,50 @@ func TestAPlansLineCountsFromItsTierNotFromItsLastSave(t *testing.T) {
// novox/hq issue 296: a plan whose tier stands still is not written again by every advance — its revision,
// its last save and its `plan-moved` stay where its last change left them.
// novox/hq issue 920: a building plan whose current tier is all built waits on its note (a send refused at the
// gate), and its line, `status` and the stalled condition say that, not "building for ... LATE".
func TestAPlanLineSaysTheRefusalOnceItsTierIsBuilt(t *testing.T) {
now := time.Date(2026, 10, 21, 19, 0, 0, 0, time.UTC)
built := now.Add(-time.Hour)
refusal := "tier 1: a build waits there for its gate: novox (mesh-delivery-indexer d68d7ef3→a5aa69a4) — tried again"
mk := func(second string) inventory.Plan {
return inventory.Plan{ID: "plan-r", Repository: "novox/mesh-sdk", Commit: "b2f5ebb2", State: inventory.PlanBuilding,
Tier: 1, Created: now.Add(-2 * time.Hour), Updated: now, TierEntered: now.Add(-time.Hour), Note: refusal,
Tiers: [][]string{{"a"}, {"b", "c"}},
Modules: map[string]*inventory.PlanModule{"a": {State: "built", BuiltAt: &built},
"b": {State: "built", BuiltAt: &built}, "c": {State: second}}}
}
// 920/AC1
p := mk("built")
line := planLine(p, now)
if !strings.Contains(line, "a build waits there for its gate: novox") || strings.Contains(line, "building for") {
t.Errorf("920/AC1: a plan with its tier built and a refusal reads %q", line)
}
st := planStatuses([]inventory.Plan{p}, now, pauseView{}, nil)
if len(st) != 1 || !st[0].Late {
t.Errorf("920/AC1: status: %+v", st)
}
// 920/AC2
if line := planLine(mk("asked"), now); !strings.Contains(line, "building for 1h0m0s") {
t.Errorf("920/AC2: a plan with a tier module not built reads %q", line)
}
}
// 920/AC3: a tier move writes the previous tier's summary into the note; with the new tier all built, the line still
// says "building for", for that note is no refusal.
func TestAPlanLineDoesNotHoldOnATiersSummary(t *testing.T) {
now := time.Date(2026, 10, 21, 19, 0, 0, 0, time.UTC)
built := now.Add(-time.Hour)
for _, note := range []string{"failed or refused its send (apply-failing says it): novox", "tier 0 built; sent mesh-sdk to novox first, in one send"} {
p := inventory.Plan{ID: "plan-s", Repository: "novox/mesh-sdk", Commit: "b2f5ebb2", State: inventory.PlanBuilding,
Tier: 1, Created: now.Add(-2 * time.Hour), Updated: now, TierEntered: now.Add(-time.Hour), Note: note,
Tiers: [][]string{{"a"}, {"b"}},
Modules: map[string]*inventory.PlanModule{"a": {State: "built", BuiltAt: &built}, "b": {State: "built", BuiltAt: &built}}}
if line := planLine(p, now); !strings.Contains(line, "building for 1h0m0s") || strings.Contains(line, "held") {
t.Errorf("920/AC3: a plan with the note %q reads %q", note, line)
}
}
}
+39 -1
View File
@@ -851,6 +851,23 @@ func startedBeside(ctx context.Context, inv *inventory.Inventory, p inventory.Pl
return behind, why, nil
}
// refusalNotePrefix and refusalNoteSuffix frame the note of a step the walk was refused: "tier N: <refusal> — tried
// again". Only a note so framed is a hold (novox/hq issue 920); a tier's summary left in the note is not.
const (
refusalNotePrefix = "tier "
refusalNoteSuffix = " — tried again"
)
// refusalNote is the note of a plan whose step at tier was refused with err.
func refusalNote(tier int, err error) string {
return refusalNotePrefix + fmt.Sprint(tier) + ": " + err.Error() + refusalNoteSuffix
}
// refused says a plan's note is a refusal the walk wrote.
func refused(p inventory.Plan) bool {
return strings.HasPrefix(p.Note, refusalNotePrefix) && strings.HasSuffix(p.Note, refusalNoteSuffix)
}
// deferredNote begins the note of a walk deferred behind another.
const deferredNote = "let go; starts once "
@@ -863,6 +880,20 @@ func deferred(p inventory.Plan) bool {
p.Delivery.Awaits != "" && p.Delivery.Go != nil && strings.HasPrefix(p.Note, deferredNote)
}
// currentTierBuilt says every module of a plan's current tier is built (or deleted), so no build is what it waits on.
func currentTierBuilt(p inventory.Plan) bool {
if p.Tier < 0 || p.Tier >= len(p.Tiers) || len(p.Tiers[p.Tier]) == 0 {
return false
}
for _, m := range p.Tiers[p.Tier] {
s := p.Modules[m]
if s == nil || (s.State != "built" && s.State != planDeleted) {
return false
}
}
return true
}
// askedAny says a plan asked any module.
func askedAny(p inventory.Plan) bool {
for _, s := range p.Modules {
@@ -927,7 +958,7 @@ func advanceHeld(ctx context.Context, open *stores) (failed bool) {
// Kept in the plan, so `plans` says why it has not moved rather than the log alone;
// the state is left as it was and the step is tried again by the next look. Said and
// kept when it is new: the same refusal on every tick is one fact, not one per tick.
p.Note = "tier " + fmt.Sprint(p.Tier) + ": " + err.Error() + " — tried again"
p.Note = refusalNote(p.Tier, err)
// A refusal for the bus's planned step is a person's to end: S17 says it from its first moment.
if refusedForTheBus(err) {
busRefusedFirst.mark(p.ID, time.Now())
@@ -2025,6 +2056,13 @@ func planLineSaid(p inventory.Plan, now time.Time, pause pauseView, bound time.D
if p.State == inventory.PlanRolling {
what = p.Note
}
if p.State == inventory.PlanBuilding && refused(p) && currentTierBuilt(p) {
// Every build of the tier is in, so the plan waits on something else, and its note says what: a send
// refused ("a build waits there for its gate: <machine> (...)") is kept there. "building" with LATE
// hid that reason from `plans`, `status` and the stalled condition (novox/hq issue 920).
what = p.Note
return fmt.Sprintf("%s · %s, held: %s, for %s%s", name, where, what, since, late)
}
return fmt.Sprintf("%s · %s, %s for %s%s", name, where, what, since, late)
}
+15
View File
@@ -1773,9 +1773,24 @@ func whatFailed(s string) string {
}
}
}
// The controller's module check refuses with a count line and one line per problem, then prints its
// warning counts: the refusal is what failed, not the count printed last (novox/hq issue 1310).
for i, line := range lines {
if line = strings.TrimSpace(line); problemsLine.MatchString(line) {
if i+1 < len(lines) {
if first := strings.TrimSpace(lines[i+1]); first != "" {
return line + ": " + first
}
}
return line
}
}
return lastLine(s)
}
// problemsLine is the module check's refusal: "mesh-controller: 3 problem(s) in 139 manifest(s)".
var problemsLine = regexp.MustCompile(`^\S+: [1-9]\d* problem\(s\) in \d+ manifest\(s\)$`)
func lastLine(s string) string {
lines := strings.Split(strings.TrimSpace(s), "\n")
return strings.TrimSpace(lines[len(lines)-1])
+26
View File
@@ -199,3 +199,29 @@ func TestOnlyATrysSelectionReachesTheScript(t *testing.T) {
t.Fatalf("a try's selection: %v", env)
}
}
// **Issue 1310**: the controller's module check prints its warning counts after its refusal; a failed check
// is said by the refusal, never by the count printed last.
func TestAFailedModuleCheckIsSaidByItsRefusalNotItsWarningCount(t *testing.T) {
log := "mesh-controller: 3 problem(s) in 139 manifest(s)\n" +
"mesh-files claims node-files and says it serves upload, download, which that seat's protocol does not promise\n" +
"minio claims the seat \"object-store\", which no module declares and the mesh does not define\n" +
"only-office claims the seat \"document-converter\", which no module declares and the mesh does not define\n" +
"adwaita: ok, 4 tool(s)\nlong-running resources without health: 59\n" +
"files asking for a setting without saying whether it is trusted: 36\n"
// 1310/AC1
want := "mesh-controller: 3 problem(s) in 139 manifest(s): mesh-files claims node-files and says it serves upload, " +
"download, which that seat's protocol does not promise"
if got := whatFailed(log); got != want {
t.Errorf("1310/AC1: the captured log is said as %q, not %q", got, want)
}
// 1310/AC2
if got, want := whatFailed("ok \tx/a\t1s\n--- FAIL: TestX (0.01s)\n x_test.go:12: got 1\nFAIL\nlong-running resources without health: 59\n"),
"--- FAIL: TestX (0.01s)"; got != want {
t.Errorf("1310/AC2: a failing test is said as %q, not %q", got, want)
}
// 1310/AC3
if got, want := whatFailed("starting\nsomething odd happened\n"), "something odd happened"; got != want {
t.Errorf("1310/AC3: a log with no failure line is said as %q, not %q", got, want)
}
}