Hold a joining build for a person without superseding its delivery, join only on an engine that keeps own groups, and keep a held module's settings (hq ADR 0328 §8, ADR 0334; re-review of 96e15f86)

A delivery does not send a joining build held for its library or its engine: the module waits for a person,
the plan stays open saying so and judges its other modules, and sends it once nothing holds it. A held move is
no move for the gate. A module joins only on an engine at contract 7, and a held module is composed with the
settings it last ran with. The runner's verdicts are said for their modules and judged by whether a move
changes them. mesh-host pinned at #97 (7a41e120), its bundle now examples/foundation-first-node.lock.
This commit is contained in:
2026-10-13 00:55:54 +02:00
parent 1de9bbc448
commit 5b9908bed2
18 changed files with 618 additions and 55 deletions
+13 -1
View File
@@ -133,7 +133,13 @@ func (d *Directory) validate(where string, _ bool) []string {
if d.Path == "" {
problems = append(problems, where+": a directory needs a path")
}
return append(problems, checkMode(where, d.Mode)...)
// A directory's mode may carry the setgid bit (2xxx), so what is made beneath it keeps its group (novox/hq ADR
// 0334 §1); never setuid or sticky, which mean nothing the mesh asks of a directory it owns.
mode := d.Mode
if len(mode) == 4 && mode[0] == '2' {
mode = "0" + mode[1:]
}
return append(problems, checkMode(where, mode)...)
}
// File is a file with literal content. The host renders nothing.
@@ -1564,6 +1570,9 @@ type Identity struct {
UID int `json:"uid"`
GID int `json:"gid"`
Groups []int `json:"groups,omitempty"`
// Runner is the tool runner's account, put in the module's own group (novox/hq ADR 0334 §2) when its tools run
// on this machine. Absent for a module that declares no tools here, and from a controller older than ADR 0334.
Runner string `json:"runner,omitempty"`
}
// Group is one library's group, as the controller allocated it (ADR 0328 §3).
@@ -1707,6 +1716,9 @@ func checkIdentities(ids []Identity, groups []Group) []string {
"declaration makes", where, g))
}
}
if id.Runner != "" && !accountName.MatchString(id.Runner) {
problems = append(problems, fmt.Sprintf("%s: its runner %q is not an account's name", where, id.Runner))
}
names[id.Name], uids[id.UID] = true, id.Name
}
return problems