Every brain write accepted [[targets]] that resolve to nothing — skill names, repo names, agent-local memory-file slugs — and each one became a permanent [high] broken-link finding. The brain-audit digest grew 19 -> 63 high findings between 2026-08-26 and 2026-09-16; 53 of the 63 were exactly this, and none are fixable by brain-gardener's BrokenLinkFixer, which only repoints edit-distance-1 renames. There is no target to repoint to. StripUnresolvedLinks resolves each link against knowledge/ + wiki/ using brain-gardener's own resolver semantics (bare slug or wing/hall/slug path form, code spans excluded) and unwraps the misses to plain text, reporting them as Ref.Unlinked / the MCP `unlinked` field. Unwrap rather than reject: a refused write on claudewatcher's unattended ingest path is a lost learning, and the author's words are worth keeping even when the link is not. An empty corpus is a no-op, so a misconfigured brainDir cannot strip every link in a note. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
94 lines
2.8 KiB
Go
94 lines
2.8 KiB
Go
package brain_test
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/mathiasbq/hyperguild/ingestion/internal/brain"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// seedCorpus writes the two entries every linkcheck case resolves against.
|
|
func seedCorpus(t *testing.T, dir string) {
|
|
t.Helper()
|
|
seedNote(t, dir, "wiki/homelab/facts/real-entry.md",
|
|
"---\nwing: homelab\nhall: facts\ntitle: 'Real entry'\n---\n\nbody.\n")
|
|
seedNote(t, dir, "knowledge/legacy-entry.md",
|
|
"---\ntitle: 'Legacy entry'\n---\n\nbody.\n")
|
|
}
|
|
|
|
func TestStripUnresolvedLinks(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
body string
|
|
want string
|
|
stripped []string
|
|
}{
|
|
{
|
|
name: "bare slug that resolves is kept",
|
|
body: "See [[real-entry]] for the rule.",
|
|
want: "See [[real-entry]] for the rule.",
|
|
},
|
|
{
|
|
name: "path-style target that resolves is kept",
|
|
body: "See [[homelab/facts/real-entry]].",
|
|
want: "See [[homelab/facts/real-entry]].",
|
|
},
|
|
{
|
|
name: "knowledge/ entry resolves by bare slug",
|
|
body: "See [[legacy-entry]].",
|
|
want: "See [[legacy-entry]].",
|
|
},
|
|
{
|
|
name: "unresolvable target is unwrapped to plain text",
|
|
body: "Related: [[secret-hygiene]].",
|
|
want: "Related: secret-hygiene.",
|
|
stripped: []string{"secret-hygiene"},
|
|
},
|
|
{
|
|
name: "unresolvable aliased target keeps its display text",
|
|
body: "Related: [[feedback_check_host_not_assume|check the host]].",
|
|
want: "Related: check the host.",
|
|
stripped: []string{"feedback_check_host_not_assume"},
|
|
},
|
|
{
|
|
name: "link inside a fenced code block is left alone",
|
|
body: "Syntax:\n\n```\n[[wing/hall/slug]]\n```\n",
|
|
want: "Syntax:\n\n```\n[[wing/hall/slug]]\n```\n",
|
|
},
|
|
{
|
|
name: "link inside an inline code span is left alone",
|
|
body: "Write `[[wing/hall/slug]]` to link.",
|
|
want: "Write `[[wing/hall/slug]]` to link.",
|
|
},
|
|
{
|
|
name: "every unresolvable target is reported once, sorted",
|
|
body: "[[zeta-gap]] and [[alpha-gap]] and [[zeta-gap]] again.",
|
|
want: "zeta-gap and alpha-gap and zeta-gap again.",
|
|
stripped: []string{"alpha-gap", "zeta-gap"},
|
|
},
|
|
}
|
|
|
|
for _, tc := range tests {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dir := t.TempDir()
|
|
seedCorpus(t, dir)
|
|
|
|
got, stripped, err := brain.StripUnresolvedLinks(dir, tc.body)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, tc.want, got)
|
|
assert.Equal(t, tc.stripped, stripped)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestStripUnresolvedLinks_MissingBrainDirIsNotAGate(t *testing.T) {
|
|
// A brain dir with no knowledge//wiki/ tree yet must not turn every link
|
|
// into an unresolved one — an empty corpus is a bootstrap state, not
|
|
// evidence that the author invented the target.
|
|
got, stripped, err := brain.StripUnresolvedLinks(t.TempDir(), "See [[real-entry]].")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "See [[real-entry]].", got)
|
|
assert.Nil(t, stripped)
|
|
}
|