Give the build verb the command's other two shapes, so a changed base or a source that moved can be rebuilt through the console
mesh/delivery delivered
mesh/merge-gate pass: builds build-agent, mesh-controller, route-proxy → ace, g14, novox, shanks; no bus step; every machine composes with the change as it…
mesh/repo-check pass: its merge-check.sh passed

The verb took only a repository; build --on <module> and build --behind were reachable only through the generic command verb. Each shape is one per call, and a second beside it is refused as passed over.
This commit is contained in:
jochen
2026-10-08 01:37:30 +02:00
parent 5ddc59cd32
commit 6faf701656
5 changed files with 119 additions and 10 deletions
+13 -2
View File
@@ -694,8 +694,19 @@ func (a *verbArguments) commandLine() ([]string, error) {
}
return []string{"module", "issue", str("module"), "--node", str("node")}, nil
case "build":
if err := need("repository"); err != nil {
return nil, err
// The command's three shapes (`build --on`, `build --behind`, `build <repository>`), one per
// call: each branch reads only its own argument, so another given beside it is refused as
// passed over rather than dropped. Asked and not waited for, the same as a single build.
if b := str("on"); b != "" {
return []string{"build", "--on", b, "--wait", "0"}, nil
}
if on("behind") {
return []string{"build", "--behind", "--wait", "0"}, nil
}
if a.given["repository"] == "" {
// No shape named: the command says its usage, which names all three — the answer the
// caller needs, and the same as `rotate` given neither of its shapes.
return []string{"build"}, nil
}
// Not waited for: a tool call cannot hold a connection for the minutes a build takes; the
// daemon takes the outcome in when it comes and the id follows the build (issue 176). A
@@ -263,8 +263,6 @@ var accountedFlags = map[string]map[string]string{
"wait": "set by the verb to 0: the id follows the build (issue 176)",
"self": "set by the verb from the repository's form: a path on the forge, or a URL",
"dry-run": "withheld: a dry run answers only when the build ends, which a call cannot wait for; `command` reaches it",
"behind": "withheld: the named verb builds one named repository; `command` reaches the rest",
"on": "withheld: the named verb builds one named repository; `command` reaches the rest",
},
"builds": {"n": "=limit"},
"plans": {"n": "=limit", "what-if": "=repository"},
+46
View File
@@ -36,6 +36,52 @@ func TestTheBuildToolTellsAForgePathFromAURL(t *testing.T) {
}
}
// The build tool has the command's three shapes: a repository, a base whose dependents are rebuilt
// (`--on`), or everything behind its source (`--behind`) — each asked, not waited for, one per call.
// Given neither, the command line is what it was: nothing new reaches it.
func TestTheBuildToolTakesOnAndBehind(t *testing.T) {
cases := []struct {
args map[string]any
want string
}{
{map[string]any{"on": "mesh-tools"}, "build --on mesh-tools --wait 0"},
{map[string]any{"behind": "true"}, "build --behind --wait 0"},
{map[string]any{"behind": true}, "build --behind --wait 0"},
{map[string]any{"repository": "novox/x"}, "build novox/x --wait 0 --self"},
{map[string]any{"repository": "novox/x", "behind": "false"}, "build novox/x --wait 0 --self"},
{map[string]any{"repository": "novox/x", "path": "modules/x", "ref": "main"},
"build novox/x --wait 0 --self --path modules/x --ref main"},
}
for _, c := range cases {
argv, err := argvFor("build", c.args)
if err != nil {
t.Errorf("%v: %v", c.args, err)
continue
}
if got := strings.Join(argv, " "); got != c.want {
t.Errorf("%v became %q, not %q", c.args, got, c.want)
}
}
// Naming no shape runs the bare command, which answers its usage naming all three.
if argv, err := argvFor("build", map[string]any{}); err != nil || strings.Join(argv, " ") != "build" {
t.Errorf("a build naming nothing became %v, %v", argv, err)
}
// One shape per call: a second beside it is refused, not dropped, and so is half of one.
for _, refused := range []map[string]any{
{"path": "modules/x"},
{"on": "mesh-tools", "repository": "novox/x"},
{"on": "mesh-tools", "behind": "true"},
{"behind": "true", "repository": "novox/x"},
{"behind": "true", "path": "modules/x"},
{"on": "mesh-tools", "ref": "main"},
{"behind": "yes"},
} {
if argv, err := argvFor("build", refused); err == nil {
t.Errorf("%v was accepted as %v", refused, argv)
}
}
}
// `rotate` is one verb with two shapes (ADR 0114, issue 180): a pair credential by provision, or a
// module's own secret by machine, module and name.
func TestRotateTakesAProvisionOrAnOwnSecret(t *testing.T) {