PR #160 review:
- The planter chooses the paths, and one alert listed them all, while the
queue retried oldest-first forever: a message ntfy refuses (too large)
blocked every later alert (2000 links: 0 alerts delivered), and an
outage queued one message per removal, unbounded. Alerts now name at
most 10 paths and count the rest; the queue holds at most 5, further
alerts become one suppression summary; a message failing 5 times is
dropped so it cannot block the rest.
- removeSymlinks' Lstat re-check had no test: a direct test passes a
regular file, a path through an in-brain dir link, a ../ escape and a
missing path, and asserts all fail with nothing deleted. A Watch test
covers dir and in-brain targets. root.Remove (not RemoveAll) is
commented: it refuses a dir swapped in after the Lstat.
- Every queued alert is delivered after an outage (truncating the queue
after the first send is now red).
- hyperguild#162: race tests did not wait for their swapper goroutines
before t.TempDir cleanup ("directory not empty" in CI run 8912). Each
swapper is now stopped and awaited in a t.Cleanup.
Fixes#162
Refs #157
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds brain_reject (RejectNote + MCP tool) and fixes the watcher to skip raw/rejected/, preventing declined notes from being silently re-ingested and republished.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-authored-by: Mathias <mthbqv@gmail.com>
Root cause was neither of the two mechanisms the issue named
(CanonicalizeLinks over-canonicalizing, or a schema/prompt bias) —
CanonicalizeLinks correctly resolved [[Mission]] because
wiki/concepts/mission.md genuinely exists; the extraction prompt has
no "mission" bias either. The real bug: watcher.processDir walks
brain/raw/ and feeds every .md/.txt/.pdf file to pipeline.Run as
source material to extract, with no exclusion for
tunnel-candidates-*.md — AutoTunnel's own fuzzy-match human-review
queue (logFuzzyCandidates writes it into that same raw/ directory).
The watcher's next poll re-ingested the raw candidate log itself, and
the LLM naturally surfaced "mission" as a wikilink because the log
literally lists `(term: "mission")` entries in its content — a
generic-word title match DetectTunnels correctly flagged as fuzzy
(not auto-tunneled) but which still leaked downstream once treated as
real source material.
api.ListPending already excludes tunnel-candidates-* when listing
raw/ for promotion; processDir gets the same exclusion.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Roq1ajWKR5f1hG5Df9wC6A
- New extract package: Text() dispatcher for .md/.txt passthrough and
PDF extraction via pdftotext subprocess
- wiki.Entry gains Aliases []string, loaded from YAML frontmatter
- Fuzzy entity resolution in pipeline: normalizes titles (lowercase,
strip articles, collapse hyphens) and matches proposed pages against
existing inventory slugs and aliases to prevent proliferation
- Watcher and API handler now use extract.Text() instead of os.ReadFile
- Dockerfile: apk add poppler-utils in Alpine runtime stage
Files dropped into brain/raw/ are now copied to processed/ or failed/ rather
than moved. A .processed or .failed marker is written next to the original so
the watcher skips it on subsequent polls without deleting it. This keeps
Syncthing-synced Obsidian vaults intact after ingestion.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Polls brain/raw/ on a configurable ticker, derives human-readable source
names from filenames, runs the pipeline, and moves files to
processed/YYYY-MM-DD/ on success or failed/ on error with a log.md entry.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>