R4e (mesh-controller): deliver from the builder's keys, ask build-agent only for its own modules, follow the forge's protection for checks, announce the controller's states #441

Merged
mesh-tools merged 39 commits from feat/r4e into main 2026-10-22 22:12:59 +00:00
4 changed files with 119 additions and 0 deletions
Showing only changes of commit b71425867f - Show all commits
+2
View File
@@ -73,6 +73,8 @@ func run() error {
}
switch args[0] {
case "register":
return registerCommand(ctx, args[1:])
case "build", "rebuild":
// **The hand build goes with the asks** (novox/hq ADR 0501 §5): refused by name, naming the replacement.
return removedByName(args[0])
+76
View File
@@ -0,0 +1,76 @@
package main
import (
"context"
"errors"
"fmt"
"strings"
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
"github.com/novox/mesh-controller/internal/builderkeys"
)
// The terminal's exception becomes a registration, not a build (novox/hq ADR 0501 §4; issue 1851, A11b).
//
// `register <module> <owner>/<repository> <path>`, at the controller's terminal alone: the operator names the module,
// its repository and its path, and the controller registers it from the builder's done key of the fingerprint the
// indexer's trunk value of that module names — the registration a build asked at the terminal used to make for a
// repository the catalogue does not build (ADR 0432 §3, ADR 0420 §7). No verb reaches it (terminalOnlyCommands), and it
// asks no build: a key not done is waited for, and said.
func registerCommand(ctx context.Context, args []string) error {
if len(args) != 3 {
return errors.New("register <module> <owner>/<repository> <path> (the path \".\" for a module at the repository's root)")
}
if !startedAtTheTerminal() {
return terminalRefusal("register is run at the controller's terminal only, never through a verb: it registers " +
"a module from a repository the catalogue does not build, and whoever may call a verb includes agents " +
"(novox/hq ADR 0266, ADR 0501 §4). Nothing was done")
}
module, repository, path := args[0], strings.Trim(args[1], "/"), strings.Trim(args[2], "/")
if path == sdkdelivery.RootPath {
path = ""
}
open, err := openStores(ctx)
if err != nil {
return err
}
defer open.Close()
keys, closeKeys, err := openBuilderKeys()
if err != nil {
return err
}
defer closeKeys()
trunk, err := keys.ModuleAt(ctx, module, "")
if errors.Is(err, builderkeys.ErrNoValue) {
return fmt.Errorf("the indexer holds no trunk value of %s: a module is registered from the builder's key of a "+
"merged trunk, and %s has none yet. Nothing was registered", module, module)
}
if err != nil {
return err
}
if err := sameModuleSource(module, repository, path, trunk); err != nil {
return err
}
manifest, kept, err := takeInKey(ctx, open.inventory, keys, keyTake{Module: module, Trunk: trunk,
Ask: registrationAsk{Terminal: true}})
if errors.Is(err, errKeyWaits) {
return fmt.Errorf("%s is not registered yet, and nothing was asked: %v. Run register again once its key is done",
module, err)
}
if err != nil {
return err
}
fmt.Printf("%s %s registered from the builder's key %s (%s at %.8s), as asked at the controller's terminal; "+
"assigned nowhere\n", manifest.Module, manifest.Version, short(kept.SourceFingerprint), repository, kept.Commit)
return nil
}
// sameModuleSource refuses a registration whose repository or path is not the one the indexer's trunk value names: the
// operator names exactly what is registered (ADR 0501 §4).
func sameModuleSource(module, repository, path string, trunk sdkdelivery.Module) error {
if !strings.EqualFold(trunk.Repository, repository) || keyPath(trunk) != path {
return fmt.Errorf("the indexer holds %s in %s at %q, not in %s at %q: the registration names exactly what it "+
"registers. Nothing was registered", module, trunk.Repository, orRoot(keyPath(trunk)), repository, orRoot(path))
}
return nil
}
+38
View File
@@ -0,0 +1,38 @@
package main
import (
"context"
"errors"
"strings"
"testing"
sdkdelivery "git.novox.be/novox/mesh-sdk/go/delivery"
)
// A11b (ADR 0501 §4): the terminal's registration names exactly the module's repository and path the indexer holds,
// and no verb reaches it.
func TestTheTerminalsRegistrationNamesItsSourceExactlyAndNoVerbReachesIt(t *testing.T) {
trunk := sdkdelivery.Module{Repository: "someone/app", Path: sdkdelivery.RootPath, Commit: "c0ffee"}
if err := sameModuleSource("app", "someone/app", "", trunk); err != nil {
t.Fatalf("the source the indexer holds is refused: %v", err)
}
if err := sameModuleSource("app", "someone/other", "", trunk); err == nil || !strings.Contains(err.Error(), "someone/app") {
t.Fatalf("another repository is taken: %v", err)
}
if err := sameModuleSource("app", "someone/app", "modules/app", trunk); err == nil {
t.Fatal("another path is taken")
}
// The generic verb only reads; and register is the terminal's only, whatever reads.
if _, err := argvFor("command", map[string]any{"command": "register app someone/app ."}); err == nil {
t.Fatal("a verb reaches the terminal's registration")
}
var refused *heldAtTheTerminal
if err := terminalOnly([]string{"register", "app", "someone/app", "."}); !errors.As(err, &refused) {
t.Fatalf("register is not the terminal's only: %v", err)
}
t.Setenv(verbVar, "command")
var held *heldAtTheTerminal
if err := registerCommand(context.Background(), []string{"app", "someone/app", "."}); !errors.As(err, &held) {
t.Fatalf("register through a verb: %v", err)
}
}
+3
View File
@@ -1784,6 +1784,9 @@ var terminalOnlyCommands = map[string]string{
"broker": "the bus's accounts",
"api": "the controller's API keys",
"licence": "the licences' secrets",
// The terminal's exception of ADR 0501 §4: a module from a repository the catalogue does not build, registered
// from its done key.
"register": "a module's registration from a repository the catalogue does not build",
}
// givenAtTheDesk is exactly the line the `give` and `secret-ask` verbs compose, and nothing beside it: `secret ask