claude-code: read where it runs from the controller's JSON answer
mesh/merge-gate pass: builds claude-code → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered

nodesRunningMe looked for printed lines in what is a JSON list, so every register tool's "also on"
hint and every nodes: "all" named no machine.
This commit is contained in:
jochen
2026-10-07 20:28:06 +02:00
parent 24a3fac2ba
commit f72a435487
11 changed files with 61 additions and 28 deletions
+28 -11
View File
@@ -163,20 +163,37 @@ func nodesRunningMe() ([]string, error) {
if err != nil {
return nil, err
}
var answer struct {
Output string `json:"output"`
return NodesRunning(raw, "claude-code"), nil
}
// NodesRunning reads where a module runs from the controller's `modules` answer. The verb runs `module list
// --json`, so the answer is a JSON list of {module, on}: the lines of the printed list it was once read as
// never came, and every "also on" hint and every `nodes: "all"` named no machine. The printed form, `<module>
// … on a, b`, is still read when that is what came.
func NodesRunning(raw json.RawMessage, module string) []string {
var listed []struct {
Module string `json:"module"`
On []string `json:"on"`
}
text := string(raw)
if json.Unmarshal(raw, &answer) == nil && answer.Output != "" {
text = answer.Output
if json.Unmarshal(verbAnswer(raw), &listed) == nil {
for _, m := range listed {
if m.Module == module {
return m.On
}
}
return nil
}
text := outputOf(raw)
if text == "" {
text = string(raw)
}
for _, line := range strings.Split(text, "\n") {
if !strings.HasPrefix(line, "claude-code ") {
if !strings.HasPrefix(line, module+" ") {
continue
}
_, on, ok := strings.Cut(line, " on ")
if !ok || strings.TrimSpace(on) == "nothing" {
return nil, nil
return nil
}
var out []string
for _, n := range strings.Split(on, ",") {
@@ -184,9 +201,9 @@ func nodesRunningMe() ([]string, error) {
out = append(out, n)
}
}
return out, nil
return out
}
return nil, nil
return nil
}
func fingerprintOfFile(path string) any {
@@ -264,7 +281,7 @@ func tools(p Paths, servers ServerState, view *ServerView, config ConfigState, c
return answer, err
}},
{Name: "claude_code_guard",
Description: "The guard on the agent's shell on this machine (novox/hq ADR 0244): what it refuses — ssh to a mesh machine, writing /etc/hosts or /etc/resolv.conf, HOSTALIASES — the machines and the replaced commands it judges with, the \"instead of\" table rendered into the agent's instructions, and its record of what it refused and what the operator's override let through, newest last.",
Description: "The guard on the agent's shell on this machine (novox/hq ADR 0245): what it refuses — ssh to a mesh machine, writing /etc/hosts or /etc/resolv.conf, HOSTALIASES — the machines and the replaced commands it judges with, the \"instead of\" table rendered into the agent's instructions, and its record of what it refused and what the operator's override let through, newest last.",
Input: map[string]any{"last": map[string]any{"type": "integer", "description": "how many lines of the record (default 50)"}},
Run: func(a map[string]any) (any, error) {
last := 50
@@ -370,7 +387,7 @@ func persist(what string, attempt func() error, done func(refusals int)) {
}
func main() {
// The guard on the agent's shell (novox/hq ADR 0244): this binary, run by the agent's hook.
// The guard on the agent's shell (novox/hq ADR 0245): this binary, run by the agent's hook.
if len(os.Args) == 3 && os.Args[1] == "guard" {
os.Exit(runGuard(os.Args[2], os.Stdin, os.Stderr))
}