From aa7c9e8924b34d2fbb9e6ad6dbe0efca43dcc05e Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 23:19:03 +0100 Subject: [PATCH] A removal of a replacement names both fingerprints; the two-texts refusal names the places and the way out Claude-Session: https://claude.ai/code/session_01NcbgfFSnn24M3GcqttoU7i --- .../claude-code/cmd/claude-code/proposals.go | 27 ++++++++++++++----- .../claude-code/replace_registered_test.go | 16 +++++++---- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/modules/claude-code/cmd/claude-code/proposals.go b/modules/claude-code/cmd/claude-code/proposals.go index c6869619d..e3a32a129 100644 --- a/modules/claude-code/cmd/claude-code/proposals.go +++ b/modules/claude-code/cmd/claude-code/proposals.go @@ -215,6 +215,11 @@ func (p Proposal) glanceAsk() oc.Ask { } fmt.Fprintf(&d, "Proposed through the claude-code module on %s at %s UTC. %s has the fingerprint %s.", p.From, p.At.UTC().Format("15:04 on 2 Jan"), removes, print) + if p.Over != "" && p.Digest != p.Over { + // A replacement is removed with the registered text it stands over: both fingerprints, the replacement's + // being the text sessions read. + fmt.Fprintf(&d, " The replacement sessions read, which it also removes, has the fingerprint %s.", p.Fingerprint()) + } } else { if whole { d.WriteString("The text:\n" + shown + "\n") @@ -1352,25 +1357,33 @@ func registeredSection(p Paths, scope string, nodes []string, name string) (stri _ = readJSON(p.config(), &items) c := ConfigOf(Taken(items)) c.Terminal, _ = terminalText(p) - layers := [][]Item{c.Mesh, c.Terminal} + type place struct { + items []Item + says string + } + places := []place{{c.Mesh, "registered for the mesh (unregister it with claude_code_instructions_unregister, scope mesh, name " + name + ")"}, + {c.Terminal, "set at the controller's terminal (its instructions setting)"}} if scope == ScopeNode { - layers = [][]Item{c.Node, c.Terminal} + places = []place{{c.Node, "registered for this node (unregister it with claude_code_instructions_unregister, scope node, name " + name + ")"}, + {c.Terminal, "set at the controller's terminal (its instructions setting)"}} if !contains(nodes, p.Node) { - layers = [][]Item{c.Terminal} + places = places[1:] } } digest := "" - for _, layer := range layers { - for _, it := range layer { + var holding []string + for _, pl := range places { + for _, it := range pl.items { if it.Kind != KindInstructions || it.Name != name { continue } d := textDigest(it.Files[name+".md"]) if digest != "" && d != digest { - return "", fmt.Errorf("the section %q is registered with different texts (for the mesh and at the controller's terminal); "+ - "remove one of them first", name) + return "", fmt.Errorf("the section %q is registered with different texts: %s and %s; an approval is bound to one text, "+ + "so unregister one of them first", name, strings.Join(holding, ", "), pl.says) } digest = d + holding = append(holding, pl.says) } } return digest, nil diff --git a/modules/claude-code/cmd/claude-code/replace_registered_test.go b/modules/claude-code/cmd/claude-code/replace_registered_test.go index e19299c13..f6dc4260f 100644 --- a/modules/claude-code/cmd/claude-code/replace_registered_test.go +++ b/modules/claude-code/cmd/claude-code/replace_registered_test.go @@ -190,7 +190,9 @@ func TestRegisteredSectionEdgeCases(t *testing.T) { writeFile(t, p.Settings, string(raw)) store, r := &memProposals{m: map[string]json.RawMessage{}}, newRouter() in := ProposeInput{Scope: ScopeMesh, Name: name, Text: "A third text.\n"} - if _, err := Propose(p, in, store, r, t0); err == nil || !strings.Contains(err.Error(), "different texts") { + if _, err := Propose(p, in, store, r, t0); err == nil || !strings.Contains(err.Error(), "different texts") || + !strings.Contains(err.Error(), "claude_code_instructions_unregister, scope mesh, name "+name) || + !strings.Contains(err.Error(), "set at the controller's terminal") { t.Fatalf("a name registered with two texts was proposed over: %v", err) } if _, err := Propose(p, ProposeInput{Scope: ScopeMesh, Name: name, Remove: true}, store, r, t0); err == nil { @@ -223,11 +225,11 @@ func TestRegisteredSectionEdgeCases(t *testing.T) { if got := renderedInstructions(t, p); strings.Contains(got, "The old words.") || strings.Contains(got, "The new words.") { t.Errorf("a text is left:\n%s", got) } - // The ask names the registered text's fingerprint, not the replacement's. + // The ask names both fingerprints: the registered text's, and the replacement's that sessions read. d := r.published[rid].Details if !strings.Contains(d, "The registered text it removes has the fingerprint "+fingerprintOf(textDigest(registeredOld))) || - strings.Contains(d, fingerprintOf(textDigest(registeredNew))) { - t.Errorf("the removal names the wrong fingerprint: %q", d) + !strings.Contains(d, "has the fingerprint "+fingerprintOf(textDigest(registeredNew))) { + t.Errorf("the removal does not name both fingerprints: %q", d) } }) t.Run("TestAChangedRegisteredTextOutlivesItsApproval", func(t *testing.T) { @@ -248,9 +250,13 @@ func TestRegisteredSectionEdgeCases(t *testing.T) { raw, _ := json.Marshal(map[string]Item{"mesh.instructions." + name: {Kind: KindInstructions, Name: name, Scope: ScopeMesh, Files: one(name, "A later registered text.\n"), By: "g14"}}) writeFile(t, p.config(), string(raw)) - if got := renderedInstructions(t, p); !strings.Contains(got, "A later registered text.") { + got := renderedInstructions(t, p) + if !strings.Contains(got, "A later registered text.") { t.Errorf("the changed registered text was swallowed by the old approval:\n%s", got) } + if !strings.Contains(got, "The new words.") || !strings.Contains(got, "## Instructions the operator approved") { + t.Errorf("the approved replacement does not render among the approved sections:\n%s", got) + } }) t.Run("TestARemovalOfARemovedSectionIsRefused", func(t *testing.T) { p, _ := node(t, "g14")