Writers that created or rewrote brain files by path followed symlinks:
- promote/reject: os.MkdirAll + os.WriteFile of the destination, then
os.Remove of the raw source. Now PutRegular + brainfs.Remove; the source
is kept when the write is refused.
- StampProvenanceFile (brain_edit's re-stamp): provenance.ApplyFile read
and rewrote by path. Now ReadRegular + WriteRegular; ApplyFile, now
unused, is removed.
- BuildWingIndex (_index.md) and wiki.RebuildIndex (wiki/index.md): now
PutRegular.
- wiki.AppendLog (log.md) and AutoTunnel's fuzzy-candidate log
(raw/tunnel-candidates-*.md): now AppendRegular; tunnel's fileExists,
now unused, is removed.
- New brainfs.Remove: clean path, real parent, removes a final symlink
itself, never its target.
brain_edit's own atomic rename was already refused behind a symlinked
directory by #187's read check; a guard test pins it.
Pipeline writes, BackfillRefs, the watcher's raw/ markers and the session
archive in cmd/server follow in a separate PR (#158).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DetectTunnels (inside every brain_write/brain_update via AutoTunnel) walks with brainfs.WalkRegular and reads titles with ReadRegular: a FIFO under wiki/ no longer hangs the write, and a symlinked note's title no longer becomes a tunnel candidate. A wiki/ that is not a real directory yields no candidates; other walk errors still abort.
Fixes#174. Refs #157 (last reader slice). Adversarial review: BLOCKED, PASS.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
brain_tunnel accepts only clean wiki/.../*.md paths and reads/rewrites both notes through brainfs; new brainfs.WriteRegular never creates, never writes through a final symlink (Lstat + O_NOFOLLOW|O_NONBLOCK + os.SameFile on the handle, since os.Root follows in-root final symlinks even with O_NOFOLLOW). brain_update resolves like brain_edit (knowledge/ and wiki/ notes only) and reads/rewrites through brainfs. session_log requires a plain-name session id and opens with O_NOFOLLOW|O_NONBLOCK.
Fixes#169. Adversarial review: BLOCKED, BLOCKED, PASS (round 3 by the estate session's reviewer). Follow-ups: #173, #174.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
AutoTunnel matched note titles case-insensitively, so a one-word title linked
every entry that happened to use that word. Measured on the live corpus: 185
entries carry an auto-written See-also link to wiki/telos/decisions/wrong.md
(title "WRONG") and 136 to .../status.md — the two most-linked nodes in the
whole graph, and both of them artifacts of the English language rather than
references. telos's projects/problems/strategies/goals/beliefs entries account
for another ~60 between them.
That pollution is not cosmetic: inbound-link count is what the duplicate
Fixer picks survivors by, what orphan detection reads, and what brain_graph
shows.
One-word titles now need the title's own casing in the prose ("WRONG", "BERT",
"NLP" — an entity name in a sentence still links). Multi-word titles keep the
case-insensitive match: "risk tier middleware" is a real mention however it is
capitalised, and a three-word collision is not an accident.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Adds the `brain_tunnel` MCP tool and auto-tunnel behaviour for
`brain_write`, so concepts that appear in multiple wings become
navigable from any of them.
New surface in package brain:
- WriteTunnel(brainDir, src, tgt) — appends a `## See also` bidirectional
wikilink between two notes in different wings. Idempotent (link not
duplicated on re-call) and reuses an existing See also section.
- DetectTunnels(brainDir, content) — walks brain/wiki/, returns
TunnelCandidates for notes whose title appears in content. Tags
whole-word case-insensitive hits as Exact=true and substring-only hits
as Exact=false.
- AutoTunnel(brainDir, src, content) — wraps DetectTunnels: writes
cross-wing exact matches, stages fuzzy matches into
brain/raw/tunnel-candidates-<YYYY-MM-DD>.md for human review.
MCP wiring:
- `brain_tunnel` tool: explicit manual link (source, target).
- `brain_write` with wing+hall now triggers AutoTunnel on the new
content. Failures are logged and never abort the primary write.
readTitleAndCreated also humanises the slug fallback (hyphens → spaces)
so titleless notes participate in content matching.
Closes hyperguild#16.
Tests: idempotency, same-wing rejection, missing-note rejection,
See-also reuse, exact/fuzzy detection, slug fallback, MCP tool happy
path, auto-tunnel hook (cross-wing exact → linked; same-wing → skipped;
fuzzy → candidates file).