Answer the verbs release 6 removed by name, and drop the merge window and push settings from the manifest (release 6, issue 1945 B10)

This commit is contained in:
2026-10-24 16:43:40 +02:00
parent 788fb681ec
commit 4f95be7e08
4 changed files with 77 additions and 84 deletions
+5
View File
@@ -360,6 +360,9 @@ func connectionName(command, verb string) string {
// removedByName refuses a command the controller no longer has, naming what replaced it (novox/hq ADR 0501 §4, §5;
// ADR 0429 §18; ADR 0503 §3; issue 1851, A11, and issue 1318).
func removedByName(command string) error {
if why, removed := removedInRelease6(command); removed {
return &removedCommand{msg: fmt.Sprintf("`%s` no longer exists: %s", command, why)}
}
replacement := map[string]string{
"build": "the controller asks no build (ADR 0501 §5): the builder builds every fingerprint the indexer writes, a " +
"module is registered from its done key, and a branch is tried with `try`; a module from a repository the " +
@@ -382,6 +385,8 @@ func removedByName(command string) error {
"resume, or its capacity",
"forget-checks": "build-agent makes no check build from R4e (ADR 0503 §3), so there is nothing to let go of",
"delivery-plan": "a delivery's plan is read with `delivery-order` and the checker's verdict (ADR 0503 §3)",
"durations": "the controller measures no sends, tiers or applies since release 6 (ADR 0539 §17): the haves and wants " +
"on the bus carry what a delivery took",
"delivery-check": "the checker judges every head and every delivery group (ADR 0503 §3, ADR 0527): the controller asks " +
"nothing and says no verdict of a pull request",
}[command]
+11 -47
View File
@@ -58,6 +58,9 @@ func argvFor(verb string, args map[string]any) ([]string, error) {
if removedVerbs[verb] {
return nil, removedByName(verb)
}
if why, removed := removedInRelease6(verb); removed {
return nil, &removedCommand{msg: fmt.Sprintf("`%s` no longer exists: %s", verb, why)}
}
a, err := readArguments(verb, args)
if err != nil {
return nil, err
@@ -93,13 +96,15 @@ type verbArguments struct {
}
// removedVerbs are the controller seat's verbs R4e removed, kept in its table with no input so a caller is answered
// by name (ADR 0501 §5, ADR 0503 §3, ADR 0450 §8).
// by name (ADR 0501 §5, ADR 0503 §3, ADR 0450 §8). The verbs release 6 removed are catalogue.RemovedControllerVerbs.
var removedVerbs = map[string]bool{"build": true, "rebuild": true, "forget-checks": true, "delivery-plan": true,
"delivery-check": true, "queue": true, "cancel": true, "clear": true, "kill": true, "replay": true, "pause": true, "resume": true,
// Release 6 (ADR 0539 §5): a delivery is each machine following its wants, so the verbs that planned, started, stopped,
// retried or closed one are gone.
"plans": true, "plan": true, "push": true, "deliver": true, "delivery-stop": true, "delivery-backups-again": true,
"delivery-walks": true, "delivery-order": true}
"delivery-check": true, "queue": true, "cancel": true, "clear": true, "kill": true, "replay": true, "pause": true, "resume": true}
// removedInRelease6 says a verb is one release 6 removed (ADR 0539, ADR 0429 §8): refused by name, with its replacement.
func removedInRelease6(verb string) (string, bool) {
why, removed := catalogue.RemovedControllerVerbs[verb]
return why, removed
}
// controllerVerb is this binary's own definition of a verb: what it runs is what it declares, so the
// arguments are checked against the table compiled beside argvFor, not a row a newer or older build
@@ -1187,47 +1192,6 @@ var inProcess = map[string]bool{"tools": true, "calls": true, "doctor": true,
// issue 1937).
"held": true}
// pushFileVar names the file the controller's push settings are written to (module.json, `push-wait`).
const pushFileVar = "MESH_PUSH_FILE"
// pushWait is how long a push to a named machine waits for its answer (novox/hq issue 629): the call's own
// `wait` when given, else the controller's setting `push-wait`, else nothing. No length is written here: the
// setting's default is the module's, and a setting that cannot be read waits for nothing, as a push did
// before, and is said.
func pushWait(given string) (time.Duration, error) {
if strings.TrimSpace(given) != "" {
d, ok := durationSetting(given)
if !ok {
return 0, fmt.Errorf("push: wait %q is no duration: say it as \"90s\", \"2m\", or \"0\" to wait for nothing", given)
}
return d, nil
}
path := os.Getenv(pushFileVar)
if path == "" {
return 0, nil
}
raw, err := os.ReadFile(path)
if err != nil {
fmt.Fprintf(os.Stderr, "the push settings cannot be read (%v); a push waits for nothing\n", err)
return 0, nil
}
var values map[string]any
if err := json.Unmarshal(raw, &values); err != nil {
fmt.Fprintf(os.Stderr, "the push settings are not JSON (%v); a push waits for nothing\n", err)
return 0, nil
}
v, has := values["push-wait"]
if !has || v == "" {
return 0, nil
}
d, ok := durationSetting(v)
if !ok {
fmt.Fprintf(os.Stderr, "the setting push-wait=%v is no duration; a push waits for nothing\n", v)
return 0, nil
}
return d, nil
}
// answersFirst is a command line whose caller is answered before it runs: a push, by its verb or
// through `command`. A push sends the machine holding the bus first when its user list changed, the
// broker reloads, and a reload forgets every answer the bus was about to permit — so an answer
+60
View File
@@ -3,8 +3,13 @@ package main
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"testing"
"git.novox.be/novox/mesh-sdk/go/catalogue"
)
// `builds` given a build's id no longer reads a log from the bus: the build seat is retired (ADR 0525 clause 7).
@@ -226,3 +231,58 @@ func TestAQuotedValueRoundTrips(t *testing.T) {
}
}
}
// **A verb release 6 removed is refused by name, with what replaces it** (ADR 0539, ADR 0429 §8, issue 1945 B10): every
// verb of catalogue.RemovedControllerVerbs is answered "no longer exists" naming itself and its replacement, whatever
// arguments it is given, and the tool-call path and the terminal's command line give the same words.
func TestTheVerbsReleaseSixRemovedAreRefusedByName(t *testing.T) {
if len(catalogue.RemovedControllerVerbs) == 0 {
t.Fatal("the SDK lists no removed controller verb")
}
for verb, why := range catalogue.RemovedControllerVerbs {
argv, err := argvFor(verb, map[string]any{"node": "anchor", "why": "w"})
if err == nil || len(argv) != 0 {
t.Errorf("%s was composed into %v", verb, argv)
continue
}
for _, want := range []string{"`" + verb + "`", "no longer exists", why} {
if !strings.Contains(err.Error(), want) {
t.Errorf("%s: the refusal %q lacks %q", verb, err, want)
}
}
if cmd := removedByName(verb); cmd == nil || cmd.Error() != err.Error() {
t.Errorf("%s: the terminal says %v, the tool says %v", verb, cmd, err)
}
}
}
// The controller's size, printed against the 111,386 non-test lines its repository held before release 6 (acceptance test 20, ADR
// 0539 §17): raw lines of every non-test Go file of the module, which the pull request reports.
func TestTheControllersNonTestLinesArePrinted(t *testing.T) {
const before = 111386
total := 0
err := filepath.WalkDir("../..", func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() && (d.Name() == ".git" || d.Name() == "testdata") {
return filepath.SkipDir
}
if d.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") {
return nil
}
raw, err := os.ReadFile(path)
if err != nil {
return err
}
total += strings.Count(string(raw), "\n")
return nil
})
if err != nil {
t.Fatal(err)
}
t.Logf("non-test Go lines: %d against %d before release 6 (%+d)", total, before, total-before)
if total >= before {
t.Errorf("the controller holds %d non-test lines, no fewer than the %d it held before release 6", total, before)
}
}
+1 -37
View File
@@ -25,23 +25,6 @@
},
"secrets-owner": "mesh-controller",
"prepares": true,
"settings": {
"merge-window": {
"kind": "preference",
"default": "90s",
"why": "merges are collected while they keep coming, and a batch is planned once no merge came for this long (novox/hq ADR 0276)"
},
"merge-window-at-most": {
"kind": "preference",
"default": "10m",
"why": "a busy period never holds a batch longer than this after its first merge (novox/hq ADR 0276)"
},
"push-wait": {
"kind": "preference",
"default": "2m",
"why": "how long a push to a named machine waits for that machine's answer, so its call says how it went; a call's own wait overrides it, and 0 waits for nothing (novox/hq issue 629)"
}
},
"tools": [
"tools",
"calls",
@@ -124,23 +107,6 @@
"place": "mesh",
"owner": "mesh-controller"
},
{
"id": "merge-window",
"type": "file",
"path": "${dir:mesh-state}/merge-window.json",
"mode": "0600",
"owner": "mesh-controller",
"content": "{\n \"merge-window\": \"${setting:merge-window}\",\n \"merge-window-at-most\": \"${setting:merge-window-at-most}\"\n}\n"
},
{
"id": "push",
"trusted": false,
"type": "file",
"path": "${dir:mesh-state}/push.json",
"mode": "0600",
"owner": "mesh-controller",
"content": "{\n \"push-wait\": \"${setting:push-wait}\"\n}\n"
},
{
"id": "controller",
"type": "process",
@@ -164,9 +130,7 @@
"MESH_BROKER_MANAGEMENT_PORT": "${seat:mesh-broker:15672}",
"MESH_BROKER_ADDRESS_PORT": "${seat:mesh-broker:5671}",
"MESH_BUS_NATS_FILE": "${dir:mesh-state}/bus",
"MESH_CONTROLLER_VERSION": "${version}",
"MESH_MERGE_WINDOW_FILE": "${dir:mesh-state}/merge-window.json",
"MESH_PUSH_FILE": "${dir:mesh-state}/push.json"
"MESH_CONTROLLER_VERSION": "${version}"
},
"replaces": [
"server"