diff --git a/checks/words/buildagent-allowed b/checks/words/buildagent-allowed new file mode 100644 index 00000000..916dafcf --- /dev/null +++ b/checks/words/buildagent-allowed @@ -0,0 +1,3 @@ +# The modules of the catalogue whose texts still name the build agent or the toolchain images, and why (checks/words, +# buildagent_test.go). One module directory a line, with its reason after a #. It only shrinks. +modules/mesh-build-go # build.go:45 says "the build agent's recipe" in a step's description: the asks lane's module (issue 1887), not E6's to edit diff --git a/checks/words/buildagent_test.go b/checks/words/buildagent_test.go new file mode 100644 index 00000000..febc6e29 --- /dev/null +++ b/checks/words/buildagent_test.go @@ -0,0 +1,57 @@ +package main + +import ( + "os" + "strings" + "testing" +) + +// The build agent and the toolchain images are gone (novox/hq ADR 0524 §5, issue 1295 and E6 of milestone R4g): no text a module +// can show names them. Where the word stands for a reason that is another lane's or still waits for its change, buildagent-allowed +// names the module's directory with that reason and the date it is dated from; the list only shrinks: a directory on it that no +// longer names the word is refused, so the line is taken out. +var gone = []string{"build-agent", "build agent", "toolchain image"} + +func allowedModules(t *testing.T) map[string]string { + t.Helper() + raw, err := os.ReadFile("buildagent-allowed") + if err != nil { + t.Fatal(err) + } + out := map[string]string{} + for _, line := range strings.Split(string(raw), "\n") { + entry, why, _ := strings.Cut(line, "#") + if entry = strings.TrimSpace(entry); entry != "" && !strings.HasPrefix(line, "#") { + if strings.TrimSpace(why) == "" { + t.Fatalf("%q is allowed without a reason", entry) + } + out[entry] = strings.TrimSpace(why) + } + } + return out +} + +func TestNoTextOfTheCatalogueNamesTheBuildAgentOutsideTheModulesAllowed(t *testing.T) { + texts, _, err := collect("../..") + if err != nil { + t.Fatal(err) + } + allowed := allowedModules(t) + seen := map[string]bool{} + for _, f := range check(gone, texts) { + module := f + if rest, ok := strings.CutPrefix(f, "modules/"); ok { + module = "modules/" + strings.SplitN(rest, "/", 2)[0] + } + if _, ok := allowed[module]; ok { + seen[module] = true + continue + } + t.Errorf("%s (the build agent and the toolchain images are gone: say what stands in their place)", f) + } + for module, why := range allowed { + if !seen[module] { + t.Errorf("buildagent-allowed names %s (%s), which names the word no more: take the line out, so it cannot come back", module, why) + } + } +} diff --git a/checks/words/main.go b/checks/words/main.go index b9bfe776..9db6b1e6 100644 --- a/checks/words/main.go +++ b/checks/words/main.go @@ -58,41 +58,7 @@ func run(root string) int { fmt.Fprintln(os.Stderr, "words: retired-words lists no word — the check would pass on anything") return 2 } - var texts []text - files := 0 - err = filepath.WalkDir(filepath.Join(root, "modules"), func(path string, d fs.DirEntry, err error) error { - if err != nil { - return err - } - if d.IsDir() { - switch d.Name() { - case "node_modules", "dist", "vendor", "test", "tests", "testdata", ".git": - return filepath.SkipDir - } - return nil - } - rel, _ := filepath.Rel(root, path) - name := d.Name() - var found []text - switch { - case strings.HasSuffix(name, "_test.go"), strings.Contains(name, ".test."), strings.Contains(name, ".spec."): - return nil - case strings.HasSuffix(name, ".go"): - found, err = goStrings(path, rel) - case strings.HasSuffix(name, ".ts"), strings.HasSuffix(name, ".js"), strings.HasSuffix(name, ".mjs"): - found, err = scriptStrings(path, rel) - case name == "module.json": - found, err = jsonStrings(path, rel) - default: - return nil - } - if err != nil { - return fmt.Errorf("%s: %w", rel, err) - } - files++ - texts = append(texts, found...) - return nil - }) + texts, files, err := collect(root) if err != nil { fmt.Fprintln(os.Stderr, "words:", err) return 2 @@ -128,6 +94,47 @@ func run(root string) int { return 0 } +// collect reads every text a module can show under root: the strings of its own code that is not a test, and every string of +// its manifests. +func collect(root string) ([]text, int, error) { + var texts []text + files := 0 + err := filepath.WalkDir(filepath.Join(root, "modules"), func(path string, d fs.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() { + switch d.Name() { + case "node_modules", "dist", "vendor", "test", "tests", "testdata", ".git": + return filepath.SkipDir + } + return nil + } + rel, _ := filepath.Rel(root, path) + name := d.Name() + var found []text + switch { + case strings.HasSuffix(name, "_test.go"), strings.Contains(name, ".test."), strings.Contains(name, ".spec."): + return nil + case strings.HasSuffix(name, ".go"): + found, err = goStrings(path, rel) + case strings.HasSuffix(name, ".ts"), strings.HasSuffix(name, ".js"), strings.HasSuffix(name, ".mjs"): + found, err = scriptStrings(path, rel) + case name == "module.json": + found, err = jsonStrings(path, rel) + default: + return nil + } + if err != nil { + return fmt.Errorf("%s: %w", rel, err) + } + files++ + texts = append(texts, found...) + return nil + }) + return texts, files, err +} + // readList reads retired-words: one word or phrase a line; blank lines and lines starting with # are not words. func readList(path string) ([]string, error) { f, err := os.Open(path) diff --git a/modules/claude-code/cmd/claude-code/proposals.go b/modules/claude-code/cmd/claude-code/proposals.go index dbde6538..9f878b66 100644 --- a/modules/claude-code/cmd/claude-code/proposals.go +++ b/modules/claude-code/cmd/claude-code/proposals.go @@ -951,7 +951,7 @@ func HeardEnd(p Paths, store ProposalStore, w asks.Warrant, now time.Time) (stri return "", nil } // Said to a proposer waiting on it only once the discard is kept: what it reads next is already there (the - // flake of the router's refusal test under a loaded build agent, 2026-10-12). + // flake of the router's refusal test under a loaded build node, 2026-10-12). tell := func() { endedMu.Lock() ended[w.Ask] = w diff --git a/modules/dev-tools/README.md b/modules/dev-tools/README.md index 64eb8d9a..3bf50c27 100644 --- a/modules/dev-tools/README.md +++ b/modules/dev-tools/README.md @@ -19,8 +19,8 @@ Assign it to every node, with the `go` and `nodejs` modules: it requires their p | git and jq | `git`, `jq` | package | All are in the distribution's official repositories, so the node-engine's package manager installs them on any -node. The versions are the distribution's: the toolchain images that run `mesh/repo-check` stay the judge of a -pull request, and a local run is the developer's early answer, not the verdict. +node. The versions are the distribution's: the build modules' toolchains, which the checker runs a pull request's tests with, stay the +judge of a pull request, and a local run is the developer's early answer, not the verdict. Nothing else is placed: no file, no service, no environment. A module's own dependencies come from its lock -file (`go.mod`, `package-lock.json`), as in the toolchain images. +file (`go.mod`, `package-lock.json`), as in the build modules. diff --git a/modules/gitea/README.md b/modules/gitea/README.md index 6e204169..4db08d83 100644 --- a/modules/gitea/README.md +++ b/modules/gitea/README.md @@ -161,7 +161,7 @@ listed repository: - one in the organisation is only put right: main's rule narrowed, `mesh/check` required in place of `mesh/merge-gate` and `mesh/repo-check`, and every other status already there kept. A main that requires `mesh/repo-check` alone (hq's did, until issue 1296) is given `mesh/check` in its place too, since nothing writes - `mesh/repo-check` once build-agent is gone (ADR 0527 §2); `mesh/check` is never dropped. + `mesh/repo-check` once the merge check's second layer is gone (ADR 0527 §2); `mesh/check` is never dropped. Every other repository of the organisation whose main requires `mesh/merge-gate` or `mesh/repo-check` has the old statuses swapped for `mesh/check`, and only its statuses (issue 1301): the switch reaches every repository the merge gate judged, with diff --git a/modules/gitea/cmd/repositories/repositories.go b/modules/gitea/cmd/repositories/repositories.go index d1c8b000..a76ad993 100644 --- a/modules/gitea/cmd/repositories/repositories.go +++ b/modules/gitea/cmd/repositories/repositories.go @@ -39,8 +39,8 @@ const ( // wanted is what a main rule requiring have should require: have with GateStatus and OwnCheckStatus replaced by // CheckStatus, every other status kept in its order (ADR 0447 §1). A rule requiring OwnCheckStatus alone, as hq's -// did, is swapped too: the checker judges hq like any repository and nothing writes OwnCheckStatus once build-agent -// is gone (ADR 0527 §2, issue 1296). This reads the rule, not a list of names (lead decision L11 of R4e). +// did, is swapped too: the checker judges hq like any repository and nothing writes OwnCheckStatus once the merge +// check's second layer is gone (ADR 0527 §2, issue 1296). This reads the rule, not a list of names (lead decision L11 of R4e). func wanted(have []string) []string { out := []string{} for _, s := range have { diff --git a/modules/gitea/provisioner/teams.ts b/modules/gitea/provisioner/teams.ts index 3183af79..61100be8 100644 --- a/modules/gitea/provisioner/teams.ts +++ b/modules/gitea/provisioner/teams.ts @@ -2,7 +2,7 @@ // half of the provisioner around it — pure of the forge's address, so it is tested with a stand-in. // // **Two teams, by what the consumer said it needs** (novox/hq issue 624). A consumer that publishes — -// the build agent, whose builds publish the SDK and every TypeScript module's packages — joins `packages`: +// the build modules, whose builds publish the SDK and every TypeScript module's packages — joins `packages`: // code read on the org's repositories and write on its packages. A consumer that contributes // `{"access": "read"}` joins `readers`: code read and package read, and no write. The lab is the first: it // clones branches that are not yet reviewed and runs their code beside its credential, and code that is diff --git a/modules/lab/tools/runs.ts b/modules/lab/tools/runs.ts index c65bfb41..a253c069 100644 --- a/modules/lab/tools/runs.ts +++ b/modules/lab/tools/runs.ts @@ -166,7 +166,7 @@ export async function npmRegistry(forge: string, get: typeof fetch = fetch, publ * The forge credential a run's clones may present, as a git credential-store line — * scheme://user:password@host[:port] — for the forge's own origin, or undefined when the lab holds none. * - * **The mesh's credential for the forge, the way the build agent holds it** (novox/hq issue 624): the lab + * **The mesh's credential for the forge, the way the build modules hold it** (novox/hq issue 624): the lab * requires the package registry, the forge's provisioner binds it an account (`as`) and seals that * account's password, and the forge that answers npm is the forge that hosts the repositories. Private * repositories (mesh-tools-go) are cloned with it; public ones clone as before. Written to a file git reads diff --git a/modules/mesh-issues/README.md b/modules/mesh-issues/README.md index 581a29b3..803adfc8 100644 --- a/modules/mesh-issues/README.md +++ b/modules/mesh-issues/README.md @@ -34,7 +34,7 @@ what it refuses. folders until the import, and hq ADR 0284): a status is one of `open`, `diagnosing`, `located`, `fixing`, `resolved`, `wontfix`; `diagnosing` and `fixing` need `by`; `located` needs `located-in`; `resolved` is reached only from `fixing` and needs `located-in` and `fixed-by`; a core issue (located in the controller, the node-engine, the tool runner, -the SDK, or the catalogue's bus, forge or build agent) resolves with `replay` or `replay-none`; `wontfix` +the SDK, the delivery parts, or the catalogue's bus, forge or build modules) resolves with `replay` or `replay-none`; `wontfix` needs a `reason`. A refused change changes nothing and says which rule refused it. **Who works on it** (hq ADR 0284). A move to `diagnosing` or `fixing` names in `by` the issue's **worker**: who diff --git a/modules/mesh-issues/cmd/mesh-issues/check.go b/modules/mesh-issues/cmd/mesh-issues/check.go index 50ec850e..b29b45a9 100644 --- a/modules/mesh-issues/cmd/mesh-issues/check.go +++ b/modules/mesh-issues/cmd/mesh-issues/check.go @@ -175,7 +175,7 @@ func (o Owners) locatedIn(e string) (string, error) { // core is an owner whose issues are core issues (hq cycle.py, to-be 45): the controller, the node-engine, // the tool runner and the console, the SDK, the delivery parts, and the catalogue's bus, forge and build modules -// (issue 1041; the build agent is gone, ADR 0524 §5). +// (issue 1041; ADR 0524 §5). var core = regexp.MustCompile(`\b(mesh-controller|mesh-host|mesh-tools|mesh-sdk|node-tools|mesh-delivery|` + `mesh-build-(go|node|image)|mesh-catalog\s+modules/(nats|gitea))\b`) diff --git a/modules/mesh-issues/cmd/mesh-issues/tracker.go b/modules/mesh-issues/cmd/mesh-issues/tracker.go index ec5adb20..327506f7 100644 --- a/modules/mesh-issues/cmd/mesh-issues/tracker.go +++ b/modules/mesh-issues/cmd/mesh-issues/tracker.go @@ -547,7 +547,7 @@ func cycleRules(is *Issue, from, to string) error { } if IsCore(is.LocatedIn) && is.Replay == "" && is.ReplayNone == "" { return fmt.Errorf("issue %d is a core issue (it is located in the controller, the node-engine, the tool "+ - "runner, the SDK, or the catalogue's bus, forge or build agent): it resolves with replay, the replay's id "+ + "runner, the SDK, the delivery parts, or the catalogue's bus, forge or build modules): it resolves with replay, the replay's id "+ "in mesh-lab's replays register, or replay-none, why no replay is possible (ADR 0237)", is.Number) } case StatusWontfix: