package main // What a Claude Code session on the mesh runs or obeys, and where each came from (novox/hq issue 340): for the // operator to review what is registered today, read-only. import ( "encoding/json" "sort" "strings" ) // ByIs says what a registration's `by` is: the node whose tool runner served the call, never the caller, whom the // runner is not told. const ByIs = "the node whose tool runner served the registering call; who called it was never recorded, because the runner is not told" // Registrations is every registration on the mesh that reaches a session, read from the state, and what the // operator set at the controller's terminal for this node: the tool servers (`servers`), the agent's // configuration (`config`) with whether each is still rendered and, when not, why; and the `managed_settings` // and `mcp_servers` this node's layers compose to. func Registrations(p Paths, servers ServerState, config ConfigState) (map[string]any, error) { serverKeys, err := servers.Keys() if err != nil { return nil, err } sort.Strings(serverKeys) registered := map[string]any{} for _, key := range serverKeys { raw, found, err := servers.Get(key) if err != nil || !found { continue } var entry map[string]any if json.Unmarshal(raw, &entry) != nil { continue } row := map[string]any{"entry": entry, "by": "not recorded: a tool server's registration kept only its entry"} if _, name, ok := strings.Cut(key, "."); ok { if problem := EntryProblem(name, entry); problem != "" { row["rendered"] = "no: " + problem } else { row["rendered"] = "yes, on the nodes its key names" } } registered[key] = row } configKeys, err := config.Keys() if err != nil { return nil, err } sort.Strings(configKeys) items := map[string]any{} for _, key := range configKeys { raw, found, err := config.Get(key) if err != nil || !found { continue } var it Item if json.Unmarshal(raw, &it) != nil { continue } _, node, _, _, ok := ParseKey(key) if !ok { continue } row := map[string]any{"kind": it.Kind, "scope": it.Scope, "by": it.By, "at": it.At} files := make([]string, 0, len(it.Files)) for f := range it.Files { files = append(files, f) } sort.Strings(files) if len(files) > 0 { row["files"] = files } switch it.Kind { case KindHook: row["event"], row["matcher"], row["command"] = it.Event, it.Matcher, it.Command case KindSettings: row["settings"] = it.Settings } switch problem := it.Problem(); { case it.Key(node) != key: row["rendered"] = "no: its key does not say what the item is" case problem != "": row["rendered"] = "no: " + problem default: row["rendered"] = "yes, as " + it.Scope + " " + it.Kind } items[key] = row } var settings Settings _ = readJSON(p.Settings, &settings) text, leftOut := terminalText(p) shared := []string{} for _, it := range text { shared = append(shared, it.Kind+" "+it.Name) } return map[string]any{ "set at the controller's terminal, as this node composes it": map[string]any{ "managed_settings": settings.ManagedSettings, "mcp_servers": settings.MCPServers, "shared text": shared, "shared text left out": leftOut}, "tool servers registered through this module": registered, "configuration registered through this module": items, "by": ByIs, }, nil }