Brain-quality sweep, batch 1: brain_edit/brain_update correctness + close-session docs (#124)
CI / Lint / Test / Vet (push) Successful in 32s
CI / Mirror to GitHub (push) Successful in 4s

Co-authored-by: Mathias <mthbqv@gmail.com>
This commit was merged in pull request #124.
This commit is contained in:
2026-09-22 21:27:11 +00:00
committed by mathias
parent cc86a74dc5
commit 5a5d938e8e
5 changed files with 176 additions and 2 deletions
+8
View File
@@ -3,6 +3,7 @@ package mcp
import (
"context"
"encoding/json"
"errors"
"fmt"
"log/slog"
"strings"
@@ -180,6 +181,13 @@ func (s *Server) editWithScope(args json.RawMessage, scope notes.Scope) (notes.R
}
res, err := notes.Edit(s.brainDir, a.Path, a.OldStr, a.NewStr, scope)
if err != nil {
if errors.Is(err, notes.ErrDegenerate) && a.OldStr == "" {
// hyperguild#115: "old_str is empty" alone doesn't say whether the
// caller sent it empty or a hop between the caller and here lost
// it. Naming the raw argument byte count makes that distinguishable
// without a second investigation.
return notes.Result{}, nil, fmt.Errorf("%w (raw arguments were %d bytes)", err, len(args))
}
return notes.Result{}, nil, err
}
@@ -70,6 +70,29 @@ func TestBrainReadAndEdit_RoundTripThroughJSONRPC(t *testing.T) {
"only the matched bytes may change")
}
func TestBrainEdit_EmptyOldStrErrorNamesRawArgumentBytes(t *testing.T) {
// hyperguild#115: a session reported "old_str is empty" with an old_str
// that, on their end, was never empty. The server-side path was
// unreachable to reproduce (verified separately: a fresh brain_edit call
// with real content round-trips cleanly), which points at something
// between a specific client and this call losing the field. The fix this
// issue actually asked for isn't a behavior change — old_str=="" must
// still refuse — it's that the refusal should say how many raw argument
// bytes it received, so "client sent empty" is distinguishable from
// "server received empty after some hop lost it" without a second
// session's worth of guessing.
dir := newBrainRepo(t)
srv := mcp.NewServer(dir, nil, nil, nil)
resp := call(t, srv, "brain_edit", map[string]any{
"path": "wiki/homelab/facts/n.md", "old_str": "", "new_str": "BETA",
})
errMsg, ok := resp["error"].(map[string]any)["message"].(string)
require.True(t, ok, "%v", resp)
assert.Contains(t, errMsg, "old_str is empty")
assert.Contains(t, errMsg, "raw arguments were", "%v", errMsg)
}
func TestBrainEdit_RefusesGovernanceFilesFromTheModelFacingTool(t *testing.T) {
dir := newBrainRepo(t)
srv := mcp.NewServer(dir, nil, nil, nil)