feat(brain): cross-wing tunnels — bidirectional wikilinks between notes in different Wings #16

Closed
opened 2026-05-18 18:03:55 +00:00 by mathias · 1 comment
Owner

Context

Successor to the Tunnels portion of #2. The embedding-retrieval portion of #2 is superseded by #8 (Qdrant + nomic-embed-text), which is the project's chosen vector backend per DECISIONS.md 2026-04-08.

This issue scopes only to cross-Wing concept linking — making the same concept discoverable from any Wing it appears in, without rebuilding retrieval.

Depends on #1 (Hall taxonomy) — Wing/Hall path layout must exist for tunnels to be meaningful.

Problem

The same concept can appear in multiple Wings (e.g. pex-copper in both bathroom-plumbing and koala-plumbing, or val-vol-r2 in both jepa-fx and hyperguild). Current retrieval treats Wings as isolated silos. There is no way to ask "what do I know about X across all Wings?" and get a navigable cross-Wing path.

Design

A Tunnel is a bidirectional wikilink between two notes in different Wings that share a concept. Two creation paths:

Automatic — when brain_write writes a note with wing=A, run a lightweight concept-match pass over the new note's content against an in-memory index of existing Wing names and note titles. On exact match in Wing B, append a ## See also section to the new note with a wikilink to the matched note in Wing B, and append a reciprocal link to that note.

Manual — new MCP tool brain_tunnel taking source (wing/hall/slug) and target (wing/hall/slug) writes the bidirectional link explicitly.

Tunnels are plain Obsidian wikilinks ([[wing-b/hall/slug]]) — no special syntax, no database. Obsidian's graph view shows cross-wing edges naturally.

Fuzzy auto-matches (not exact) land in brain/raw/tunnel-candidates-<date>.md for human review, never written automatically.

Implementation

ingestion/internal/brain/tunnel.go

package brain

type TunnelCandidate struct {
    SourcePath  string
    TargetPath  string
    MatchedTerm string
}

// DetectTunnels scans brainDir/wiki/ for notes whose titles overlap with
// terms extracted from content. Returns candidates without writing.
func DetectTunnels(brainDir, content string) ([]TunnelCandidate, error)

// WriteTunnel appends a wikilink to both source and target notes.
// Idempotent — does not duplicate if link already present.
func WriteTunnel(brainDir, sourcePath, targetPath string) error

New MCP tool: brain_tunnel

{
  "name": "brain_tunnel",
  "description": "Create an explicit bidirectional link between two notes in different wings.",
  "parameters": {
    "source": "wing/hall/slug of the source note",
    "target": "wing/hall/slug of the target note"
  }
}

Auto-tunnel hook in brain_write

After every brain_write with wing+hall, run DetectTunnels over the new content. Exact title matches → write bidirectional link. Fuzzy candidates → append to brain/raw/tunnel-candidates-<YYYY-MM-DD>.md.

Acceptance criteria

  • brain_tunnel source=jepa-fx/decisions/val-vol-r2 target=hyperguild/decisions/routing-floor writes wikilinks in both files and is idempotent on second call
  • Auto-tunnel after brain_write creates a link when an exact Wing/note title match is found in content
  • Fuzzy candidates land in brain/raw/tunnel-candidates-<date>.md, not written automatically
  • Obsidian graph view shows cross-wing edges for tunnel links
  • task check passes

Out of scope

  • Embedding-based retrieval — moved to #8
  • Automatic creation for fuzzy matches — human review required
  • Obsidian plugin development

Dependencies

  • #1 (Hall taxonomy) must merge first
## Context Successor to the Tunnels portion of #2. The embedding-retrieval portion of #2 is superseded by #8 (Qdrant + nomic-embed-text), which is the project's chosen vector backend per DECISIONS.md 2026-04-08. This issue scopes only to **cross-Wing concept linking** — making the same concept discoverable from any Wing it appears in, without rebuilding retrieval. Depends on #1 (Hall taxonomy) — Wing/Hall path layout must exist for tunnels to be meaningful. ## Problem The same concept can appear in multiple Wings (e.g. `pex-copper` in both `bathroom-plumbing` and `koala-plumbing`, or `val-vol-r2` in both `jepa-fx` and `hyperguild`). Current retrieval treats Wings as isolated silos. There is no way to ask "what do I know about X across all Wings?" and get a navigable cross-Wing path. ## Design A Tunnel is a bidirectional wikilink between two notes in different Wings that share a concept. Two creation paths: **Automatic** — when `brain_write` writes a note with `wing=A`, run a lightweight concept-match pass over the new note's content against an in-memory index of existing Wing names and note titles. On exact match in Wing B, append a `## See also` section to the new note with a wikilink to the matched note in Wing B, and append a reciprocal link to that note. **Manual** — new MCP tool `brain_tunnel` taking `source` (wing/hall/slug) and `target` (wing/hall/slug) writes the bidirectional link explicitly. Tunnels are plain Obsidian wikilinks (`[[wing-b/hall/slug]]`) — no special syntax, no database. Obsidian's graph view shows cross-wing edges naturally. Fuzzy auto-matches (not exact) land in `brain/raw/tunnel-candidates-<date>.md` for human review, never written automatically. ## Implementation ### `ingestion/internal/brain/tunnel.go` ```go package brain type TunnelCandidate struct { SourcePath string TargetPath string MatchedTerm string } // DetectTunnels scans brainDir/wiki/ for notes whose titles overlap with // terms extracted from content. Returns candidates without writing. func DetectTunnels(brainDir, content string) ([]TunnelCandidate, error) // WriteTunnel appends a wikilink to both source and target notes. // Idempotent — does not duplicate if link already present. func WriteTunnel(brainDir, sourcePath, targetPath string) error ``` ### New MCP tool: `brain_tunnel` ```json { "name": "brain_tunnel", "description": "Create an explicit bidirectional link between two notes in different wings.", "parameters": { "source": "wing/hall/slug of the source note", "target": "wing/hall/slug of the target note" } } ``` ### Auto-tunnel hook in `brain_write` After every `brain_write` with `wing`+`hall`, run `DetectTunnels` over the new content. Exact title matches → write bidirectional link. Fuzzy candidates → append to `brain/raw/tunnel-candidates-<YYYY-MM-DD>.md`. ## Acceptance criteria - `brain_tunnel source=jepa-fx/decisions/val-vol-r2 target=hyperguild/decisions/routing-floor` writes wikilinks in both files and is idempotent on second call - Auto-tunnel after `brain_write` creates a link when an exact Wing/note title match is found in content - Fuzzy candidates land in `brain/raw/tunnel-candidates-<date>.md`, not written automatically - Obsidian graph view shows cross-wing edges for tunnel links - `task check` passes ## Out of scope - Embedding-based retrieval — moved to #8 - Automatic creation for fuzzy matches — human review required - Obsidian plugin development ## Dependencies - #1 (Hall taxonomy) must merge first
Author
Owner

Shipped in ddd07ae. TDD all the way: each helper landed RED → GREEN with regression tests.

Acceptance criteria

  • brain_tunnel source=jepa-fx/decisions/val-vol target=hyperguild/decisions/routing writes wikilinks in both files; re-call is a no-op (idempotency test asserts link count = 1 after 3 calls)
  • Auto-tunnel after brain_write creates a link when an exact wing/note-title match is found in content (TestBrainWriteAutoTunnelsOnExactMatch)
  • Same-wing matches are NOT auto-linked (TestBrainWriteAutoTunnelSkipsSameWing)
  • Fuzzy candidates land in brain/raw/tunnel-candidates-<date>.md, not written automatically (TestAutoTunnel_FuzzyGoesToCandidatesFile)
  • Obsidian graph view sees [[wing/hall/slug]] wikilinks under ## See also
  • task check green

Surface added

// ingestion/internal/brain
type TunnelCandidate struct{ TargetPath, MatchedTerm string; Exact bool }
func WriteTunnel(brainDir, source, target string) error
func DetectTunnels(brainDir, content string) ([]TunnelCandidate, error)
func AutoTunnel(brainDir, source, content string) error

MCP additions:

  • brain_tunnel(source, target) — manual cross-wing link
  • brain_write with wing+hall now runs AutoTunnel after the write

Slug-fallback titles humanise (widget-flagswidget flags) so titleless notes still participate in matching.

Live deploy

CI/CD picks up ddd07ae automatically. Live brain MCP will expose the new tool after the routing-pod-style ingestion image rebuild lands in infra.

Closing.

Shipped in `ddd07ae`. TDD all the way: each helper landed RED → GREEN with regression tests. ### Acceptance criteria - [x] `brain_tunnel source=jepa-fx/decisions/val-vol target=hyperguild/decisions/routing` writes wikilinks in both files; re-call is a no-op (idempotency test asserts link count = 1 after 3 calls) - [x] Auto-tunnel after `brain_write` creates a link when an exact wing/note-title match is found in content (`TestBrainWriteAutoTunnelsOnExactMatch`) - [x] Same-wing matches are NOT auto-linked (`TestBrainWriteAutoTunnelSkipsSameWing`) - [x] Fuzzy candidates land in `brain/raw/tunnel-candidates-<date>.md`, not written automatically (`TestAutoTunnel_FuzzyGoesToCandidatesFile`) - [x] Obsidian graph view sees `[[wing/hall/slug]]` wikilinks under `## See also` - [x] `task check` green ### Surface added ```go // ingestion/internal/brain type TunnelCandidate struct{ TargetPath, MatchedTerm string; Exact bool } func WriteTunnel(brainDir, source, target string) error func DetectTunnels(brainDir, content string) ([]TunnelCandidate, error) func AutoTunnel(brainDir, source, content string) error ``` MCP additions: - `brain_tunnel(source, target)` — manual cross-wing link - `brain_write` with wing+hall now runs `AutoTunnel` after the write Slug-fallback titles humanise (`widget-flags` → `widget flags`) so titleless notes still participate in matching. ### Live deploy CI/CD picks up `ddd07ae` automatically. Live brain MCP will expose the new tool after the routing-pod-style ingestion image rebuild lands in infra. Closing.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: mathias/hyperguild#16