From 1fc10b0323c8ffd3776b054b0e6fd690183ee997 Mon Sep 17 00:00:00 2001 From: jochen Date: Mon, 5 Oct 2026 14:33:53 +0200 Subject: [PATCH] claude-code: say what a registration wrote here, whichever took it first On g14 the node's own watch took a home registration before the tool did, and the answer said 'already so' though the file had just been placed. Compare the view before and after, and render whenever the item applies here, as the MCP server registration already does. --- modules/claude-code/cmd/claude-code/config.go | 17 ++++++++++++----- .../claude-code/cmd/claude-code/config_test.go | 13 +++++++++++++ 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/modules/claude-code/cmd/claude-code/config.go b/modules/claude-code/cmd/claude-code/config.go index 13e1333..31a99d9 100644 --- a/modules/claude-code/cmd/claude-code/config.go +++ b/modules/claude-code/cmd/claude-code/config.go @@ -800,8 +800,10 @@ func Register(p Paths, it Item, nodes []string, unregister bool, state ConfigSta } } } + // Compared before and after rather than read from Take: this node's own watch may take the same change + // first, and then Take here finds nothing new although this call made it. + before, _ := json.Marshal(v.Items()) var keys []string - changed := false for _, n := range nodes { key := it.Key(n) keys = append(keys, key) @@ -819,7 +821,12 @@ func Register(p Paths, it Item, nodes []string, unregister bool, state ConfigSta op = "delete" } item := it - changed = v.Take(key, op, &item) || changed + v.Take(key, op, &item) + } + after, _ := json.Marshal(v.Items()) + here := string(before) != string(after) + for _, k := range keys { + here = here || v.Applies(k) } verb := map[bool]string{false: "registered", true: "unregistered"}[unregister] answer := map[string]any{verb: it.Kind + " " + it.Name, "keys": keys} @@ -835,15 +842,15 @@ func Register(p Paths, it Item, nodes []string, unregister bool, state ConfigSta answer["offered as"] = "/" + Plugin + ":" + it.Name } } - if changed { + if here { + // Rendered whenever it applies here, so the answer says what this node wrote, whichever of the + // watch and this call took the change. rendered, err := RenderNow(p, write) answer["rendered here"] = rendered if err != nil { answer["not written here"] = err.Error() // kept on the bus all the same; the next render tries again } answer["sessions"] = "a new session takes it; a running one at /reload-plugins" - } else if v.Applies(keys[0]) || len(keys) > 1 { - answer["here"] = "already so" } else { answer["here"] = "not this node: each node it is for takes it from the bus" } diff --git a/modules/claude-code/cmd/claude-code/config_test.go b/modules/claude-code/cmd/claude-code/config_test.go index 69036ed..0a1edf8 100644 --- a/modules/claude-code/cmd/claude-code/config_test.go +++ b/modules/claude-code/cmd/claude-code/config_test.go @@ -455,3 +455,16 @@ func TestOneFileThatCannotBeWrittenDoesNotStopTheOthers(t *testing.T) { t.Fatalf("the other files were not written: %v", w) } } + +// The answer says what this node wrote even when its own watch took the change first. +func TestTheAnswerSaysWhatWasWrittenWhenTheWatchWasFirst(t *testing.T) { + p, w := node(t, "laptop") + state, view := memConfig{}, NewConfigView(p) + it := Item{Kind: KindCommand, Name: "c", Scope: ScopeHome, Files: one("c", "x")} + first := it + view.Take(it.Key("laptop"), "put", &first) // the watch, first + answer, err := Register(p, it, nil, false, state, view, writer(w)) + if err != nil || answer["rendered here"] == nil || answer["here"] != nil { + t.Fatalf("%v %v", answer, err) + } +}