Name what a held release holds, and where it is released
The release-held words said "release them, or leave them held" without the modules, the machines or the mesh MCP server, so the operator could neither tell what waited nor where to act (ADR 0258). The controller's restart needs missed the same suffix. A test now holds every need that opens with a verb only the mesh MCP server performs to name it, so a new kind cannot miss it.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -222,3 +223,69 @@ func TestDataLossOffersNoSilence(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// **Updates held after a failed release** (2026-10-08): the popup read "Needs you: release them, or leave
|
||||
// them held." — naming neither what waits nor where it is released. It names the modules and machines, and
|
||||
// the mesh MCP server.
|
||||
func TestUpdatesHeldNameWhatWaitsAndWhereItIsReleased(t *testing.T) {
|
||||
saved := backlogNow
|
||||
t.Cleanup(func() { backlogNow = saved })
|
||||
backlogNow.held = "release-1791457717307061152 failed (failed its gate on g14); what waits is released again by a person"
|
||||
backlogNow.waiting = map[string][]inventory.CarriedMove{
|
||||
"shanks": {{Module: "openrazer"}},
|
||||
"g14": {{Module: "openrazer"}, {Module: "sensors"}},
|
||||
}
|
||||
got := backlogObservation()
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("raised %d", len(got))
|
||||
}
|
||||
plainExample(t, got[0], "Updates of openrazer and sensors wait for your release",
|
||||
"Needs you: release them from the mesh MCP server; this notification cannot do it. Updates of openrazer and "+
|
||||
"sensors on g14 and shanks wait for a person to release them, because the last release failed its check. "+
|
||||
"They are not delivered until then, and stay held if you leave them.")
|
||||
}
|
||||
|
||||
// mcpVerb is a need that opens with a verb only the mesh MCP server performs (ADR 0258 §1): release, stop,
|
||||
// start, restart, and a restore.
|
||||
var mcpVerb = regexp.MustCompile(`^(release|stop|start|restart|restore)\b`)
|
||||
|
||||
// **A need no notification can answer says where it is answered** (ADR 0258 §1): every wording whose need
|
||||
// opens with a verb the mesh MCP server performs, and offers no action, ends with FromMeshMCPServer — the
|
||||
// kinds worded here for every subject shape, and those worded where they are raised. A new kind that misses
|
||||
// it fails here; release-held did (2026-10-08).
|
||||
func TestANeedNoNotificationAnswersNamesTheMeshMCPServer(t *testing.T) {
|
||||
check := func(what string, w words) {
|
||||
t.Helper()
|
||||
if w.Needs == "" || len(w.Actions) > 0 || !mcpVerb.MatchString(w.Needs) {
|
||||
return
|
||||
}
|
||||
if !strings.HasSuffix(w.Needs, FromMeshMCPServer) {
|
||||
t.Errorf("%s needs %q without %q", what, w.Needs, FromMeshMCPServer)
|
||||
}
|
||||
}
|
||||
subjects := []conditions.Observation{
|
||||
{Scope: conditions.ScopeMachine, ID: "ace", Machine: "ace"},
|
||||
{Scope: conditions.ScopeMachine, ID: "ace.immich.library", Machine: "ace"},
|
||||
{Scope: conditions.ScopeModule, ID: "openrazer.g14", Machine: "g14"},
|
||||
{Scope: conditions.ScopeDelivery, ID: "novox/hq@055550802096"},
|
||||
{Scope: conditions.ScopeCore, ID: "controller.anchor", Machine: "anchor"},
|
||||
{Scope: conditions.ScopeMesh, ID: "release", Also: []string{"g14"}},
|
||||
}
|
||||
for kind, fn := range plainWordings {
|
||||
for _, s := range subjects {
|
||||
for _, sev := range []conditions.Severity{conditions.Warning, conditions.Urgent} {
|
||||
s.Kind, s.Severity, s.Resolver = kind, sev, conditions.ResolverOperator
|
||||
check(kind+" about "+s.ID, fn(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
check("a walk waiting", words{Needs: waitingNeeds(conditions.Urgent)})
|
||||
check("a module's failed service", words{Needs: moduleNeeds("g14",
|
||||
[]inventory.ResourceHealth{{Kind: link.KindUnit, Target: "openrazer-daemon.service"}})})
|
||||
for _, state := range []string{"held", "ready", "failing"} {
|
||||
_, _, _, needs, actions := stalledWords(stalledLine{ID: "novox/hq@055550802096", State: state, For: "36h"},
|
||||
conditions.Observation{Resolver: conditions.ResolverOperator})
|
||||
check("a delivery "+state, words{Needs: needs, Actions: actions})
|
||||
}
|
||||
check("updates held", releaseHeldWords([]string{"openrazer"}, []string{"g14"}))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user