The witness judges the tool runner's process by its id, <its module>.runtime, which the composition gives only the module whose manifest says it is the tool runner, never by a name (ADR 0601 §4; review round 1, finding 4)

This commit is contained in:
2026-11-03 14:02:01 +01:00
parent 2239fb253d
commit b47668421f
3 changed files with 16 additions and 14 deletions
+1 -1
View File
@@ -536,7 +536,7 @@ func witnessOf(r *declaration.Process) string {
if r.Witness != "" {
return r.Witness
}
return witness.Default(r.Name)
return witness.Default(r.ID, r.Name)
}
// digestWritten is the bundle digest a process's record says was placed: the first half of what it
+8 -9
View File
@@ -9,6 +9,7 @@ package witness
import (
"time"
"github.com/novox/mesh-host/internal/compose/resolution"
"github.com/novox/mesh-host/internal/link"
)
@@ -33,15 +34,13 @@ const (
GiveUp = 30 * time.Minute
)
// NodeToolsService is the runtime's name on the services protocol, and its instance is this
// machine's node name: what `$SRV.PING.<service>.<node>` asks, so only this machine's runtime can
// answer (mesh-tools node-tools internal/runtime, announce.Service{Name: module, ID: node}).
const NodeToolsService = "node-tools"
// Default is the witness a process is judged by when it names none: the core process the mesh composes on every
// machine, by the name it composes it under, and nothing else.
func Default(process string) string {
if process == NodeToolsService {
// Default is the witness a process is judged by when it names none: the tool runner's process, the core process the mesh
// composes on every machine for the module whose manifest says it is the tool runner (catalogue.IsToolRunner), with the id
// `<its module>.runtime` and named as its module (resolution's runtime process), and nothing else. It is pinged by that
// name on this machine's instance, `$SRV.PING.<name>.<node>`, so only this machine's runtime answers; never known here by
// a module's name (ADR 0601 §4).
func Default(id, process string) string {
if process != "" && id == process+"."+resolution.RuntimeProcessID() {
return ByPing
}
return ByNone
+7 -4
View File
@@ -13,7 +13,7 @@ import (
// The runtime is asked by its own name and this machine's: only this machine's runtime can answer.
func TestTheRuntimeIsAskedByItsNameAndThisMachines(t *testing.T) {
if got := link.PingSubject(NodeToolsService, "anchor"); got != "$SRV.PING.node-tools.anchor" {
if got := link.PingSubject("runner-x", "anchor"); got != "$SRV.PING.runner-x.anchor" {
t.Fatalf("the host asks %s; the runtime answers $SRV.PING.node-tools.<node>", got)
}
}
@@ -21,9 +21,12 @@ func TestTheRuntimeIsAskedByItsNameAndThisMachines(t *testing.T) {
// What is judged by default is the mesh's core process on every machine, by the name it is composed under, and
// nothing else: the controller ended (issue 1126), so its name is judged as any other.
func TestOnlyTheCoreProcessIsJudgedByDefault(t *testing.T) {
for name, want := range map[string]string{"mesh-controller": ByNone, "node-tools": ByPing, "greeter": ByNone} {
if got := Default(name); got != want {
t.Errorf("%s is judged by %q, want %q", name, got, want)
// The tool runner's process by its id and name, whatever its module is called (ADR 0601 §4); a process named so by
// another id, or another process, is not.
for id, want := range map[string]string{"runner-x.runtime": ByPing, "node-tools.runtime": ByNone, "runner-x.server": ByNone,
"mesh-controller.runtime": ByNone} {
if got := Default(id, "runner-x"); got != want {
t.Errorf("%s is judged by %q, want %q", id, got, want)
}
}
if NodeToolsWithin != 60*time.Second || PingWithin != 5*time.Second {