Say what each verb replaces, so the agent is pointed at it instead of a shell command (hq ADR 0245)
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
mesh/delivery superseded: a newer delivery to the same trunk took over its walk

A seat's verb names the shell commands it is the mesh's way to do, and a module says it for its own
tools in its manifest; the tools verb and module list --json carry both, for the mesh MCP server's
search, the agent's instructions and the guard on its shell.
This commit is contained in:
jochen
2026-10-07 20:44:47 +02:00
parent 65610f2ea2
commit 4469cab7f4
15 changed files with 269 additions and 41 deletions
+4 -1
View File
@@ -182,6 +182,9 @@ func moduleCommand(ctx context.Context, args []string) error {
Requires []string `json:"requires,omitempty"`
Claims []string `json:"claims,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
// Replaces is what each of its own tools replaces (novox/hq ADR 0245), for the console's
// search and the agent's instructions.
Replaces map[string][]string `json:"replaces,omitempty"`
}
out := make([]listed, 0, len(entries))
for _, e := range entries {
@@ -189,7 +192,7 @@ func moduleCommand(ctx context.Context, args []string) error {
l := listed{Module: m.Module, Version: m.Version, Built: e.Source.BuiltFrom, Head: e.Source.Head,
Current: e.Provided || e.Source.Repository == "" || e.Source.Current(), Provided: e.Provided,
On: append([]string{}, e.On...), Provides: m.Offers(), Requires: m.Requires,
Capabilities: m.Capabilities, Tools: declaresTools(m)}
Capabilities: m.Capabilities, Tools: declaresTools(m), Replaces: m.Replaces}
for _, c := range m.Claims {
l.Claims = append(l.Claims, c.At()+"/"+c.Name)
}
+2 -2
View File
@@ -128,7 +128,7 @@ func planFor(ctx context.Context, open *stores, nodeName string) (catalogue.Reso
}
// A recorded module is composed at the build this machine runs, on any send but a person's push
// (novox/hq issue 295, ADR 0242).
// (novox/hq issue 295, ADR 0245).
if _, err := keepRecorded(ctx, open, nodeName, shelf); err != nil {
return catalogue.Resolution{}, nil, err
}
@@ -439,7 +439,7 @@ func declarationWith(ctx context.Context, open *stores, node string,
names = append(names, m.Module)
}
out.Builds = carriedBuilds(names, composed.LeftOut, current, before)
// A recorded module kept at the build the machine runs is recorded as carrying that one (ADR 0242).
// A recorded module kept at the build the machine runs is recorded as carrying that one (ADR 0245).
kept, err := recordedKept(ctx, open, node)
if err != nil {
return sendable{}, err
+1 -1
View File
@@ -1082,7 +1082,7 @@ func sendToEach(ctx context.Context, open *stores, names []string) ([]string, er
return nil, err
}
// **A recorded build moves only by a person's push** (novox/hq issue 295, ADR 0242): this send — a
// **A recorded build moves only by a person's push** (novox/hq issue 295, ADR 0245): this send — a
// plan's, a release plan's, a rollback's, a healer's, a rotation's — composes every recorded module at
// the build its machine runs. The bus step is a person's word for the bus alone.
if ctx, err = sendKeeps(ctx, inv); err != nil {
+2 -2
View File
@@ -9,7 +9,7 @@ import (
"github.com/novox/mesh-controller/internal/inventory"
)
// A recorded build reaches a machine only by a person's push (novox/hq issue 295, ADR 0242).
// A recorded build reaches a machine only by a person's push (novox/hq issue 295, ADR 0245).
//
// **A send carries the machine's whole declaration** (ADR 0221), composed from the build the mesh holds
// of every module on it. A module whose upgrade policy records — postgres, mongodb, keycloak, the
@@ -121,7 +121,7 @@ func keepRecorded(ctx context.Context, open *stores, node string, shelf map[stri
if !found {
return nil, fmt.Errorf("%s records rather than rolls out, and %s runs its build %s, which the build "+
"records no longer hold: this send cannot keep it and does not move it — `push %s` sends the new "+
"one on a person's word (novox/hq ADR 0242)", m, node, short(kept[m]), node)
"one on a person's word (novox/hq ADR 0245)", m, node, short(kept[m]), node)
}
shelf[m] = ran
}
+2 -2
View File
@@ -11,7 +11,7 @@ import (
"github.com/novox/mesh-controller/internal/link"
)
// A recorded build reaches a machine only by a person's push (novox/hq issue 295, ADR 0242).
// A recorded build reaches a machine only by a person's push (novox/hq issue 295, ADR 0245).
// aContainerBuild is a build outcome of a module of containers, each named by id with the image and the
// health it is given; a policy when one is said.
@@ -189,7 +189,7 @@ func TestARecordedBuildIsCarriedOnlyByAPersonsPush(t *testing.T) {
}
}
// The send says what it recreates (ADR 0242): mail's health checks adopted recreate both its
// The send says what it recreates (ADR 0245): mail's health checks adopted recreate both its
// containers, with no new image, every one at once.
func TestASendSaysWhatItRecreates(t *testing.T) {
image := "registry.invalid:5000/mailu/smtp@sha256:" + strings.Repeat("c", 64)
+1 -1
View File
@@ -671,7 +671,7 @@ func backlogCommand(ctx context.Context, sub string, args []string) error {
}
// sayRecreations says, on each move a send carries, what it does to the module's containers (novox/hq
// ADR 0242): the build the machine ran against the one it is sent, container by container. Left unsaid
// ADR 0245): the build the machine ran against the one it is sent, container by container. Left unsaid
// for a move whose earlier build is not in the records.
func sayRecreations(ctx context.Context, open *stores, moves []inventory.CarriedMove) {
if len(moves) == 0 {
+2 -2
View File
@@ -904,7 +904,7 @@ func firstSend(ctx context.Context, open *stores, p *inventory.Plan, node string
strings.Join(sent, ", "))
fmt.Printf("%s: tier %d built; sent %d module(s) to %s first in one send (%s), the rest once its gate passes\n",
p.ID, p.Tier, len(modules), strings.Join(sent, ", "), strings.Join(modules, ", "))
// What the send recreates, said with it (novox/hq ADR 0242).
// What the send recreates, said with it (novox/hq ADR 0245).
if said := recreationsSaid(carried); said != "" {
p.Note += "; " + said
fmt.Printf("%s: %s\n", p.ID, said)
@@ -1287,7 +1287,7 @@ func plansCommand(ctx context.Context, args []string) error {
case s != nil && s.Gate != nil:
fmt.Printf(" %-22s %s\n", "", gateLine(s.Gate))
}
// What the send to its first machine did to its containers (ADR 0242).
// What the send to its first machine did to its containers (ADR 0245).
if s != nil && s.Gate != nil {
for _, c := range s.Gate.Carried {
if c.Recreates != "" {
+8 -2
View File
@@ -970,9 +970,15 @@ func seatTools() map[string]any {
}
var tools []map[string]any
for _, v := range s.Serves {
tools = append(tools, map[string]any{
tool := map[string]any{
"name": v.Name, "description": v.Description, "input": v.Input, "output": v.Output,
})
}
// What the verb is the mesh's way to do (novox/hq ADR 0245): read by the console's search and
// the agent's instructions.
if len(v.Replaces) > 0 {
tool["replaces"] = v.Replaces
}
tools = append(tools, tool)
}
seats = append(seats, map[string]any{"seat": s.Name, "scope": s.Scope, "tools": tools})
}
+17
View File
@@ -144,6 +144,23 @@ func TestToolsAnswersTheSeatsRecords(t *testing.T) {
if !found {
t.Fatal("the mesh-controller seat is not in the listing")
}
// And what a verb replaces travels with it (novox/hq ADR 0245): the console and the agent's
// instructions read it from here.
var journal []string
for _, s := range seats {
if s["seat"] != catalogue.ServiceManagerSeat {
continue
}
tools, _ := s["tools"].([]map[string]any)
for _, tool := range tools {
if tool["name"] == "journal" {
journal, _ = tool["replaces"].([]string)
}
}
}
if len(journal) == 0 || journal[0] != "journalctl" {
t.Fatalf("the service manager's journal does not say it replaces journalctl: %v", journal)
}
}
// A JSON verb's answer is parsed from what the command wrote to standard output alone; a warning it