Refs #157 (slice 5a). risk/high: control code, adversarial review required.
Problem
BuildWingIndex and BuildAllWingIndexes run on every brain_write. They listed wiki/<wing>/<hall>/ with os.ReadDir and opened notes by path:
Symlinked note: its target's title landed in _index.md (110k leaks in the race).
Symlinked wing: it was followed, and _index.md was written into the link's target.
FIFO: it hung the build.
Change
New brainfs.ReadDirDirs: lists real subdirectories only, and every component of the directory must be a real directory. Wings and halls are listed with it and ReadDirRegular, and titles are read with ReadRegular (readTitleAndCreatedIn). A symlinked wing is an error; a symlinked hall is skipped.
linkcheck entryIndex: walks with WalkRegular, so a symlinked .md name no longer makes a [[link]] resolve.
Not in this PR:readTitleAndCreated (path-based) stays for tunnel.DetectTunnels until #170 lands. That will be slice 5b, which includes the FIFO hang on every wiki write.
Tests
All have outside and in-brain targets and positive controls.
TestBuildWingIndexRefusesSymlinkedNotesAndSurvivesSwapRaces: red before the fix (110k leaks).
TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall: red before the fix.
TestStripUnresolvedLinksDoesNotCountASymlinkedNoteAsAnEntry: red against the old linkcheck.go.
Refs #157 (slice 5a). risk/high: control code, adversarial review required.
## Problem
`BuildWingIndex` and `BuildAllWingIndexes` run on every brain_write. They listed `wiki/<wing>/<hall>/` with `os.ReadDir` and opened notes by path:
- **Symlinked note:** its target's title landed in `_index.md` (110k leaks in the race).
- **Symlinked wing:** it was followed, and `_index.md` was written into the link's target.
- **FIFO:** it hung the build.
## Change
- **New `brainfs.ReadDirDirs`:** lists real subdirectories only, and every component of the directory must be a real directory. Wings and halls are listed with it and `ReadDirRegular`, and titles are read with `ReadRegular` (`readTitleAndCreatedIn`). A symlinked wing is an error; a symlinked hall is skipped.
- **linkcheck `entryIndex`:** walks with `WalkRegular`, so a symlinked `.md` name no longer makes a `[[link]]` resolve.
- **Not in this PR:** `readTitleAndCreated` (path-based) stays for `tunnel.DetectTunnels` until #170 lands. That will be slice 5b, which includes the FIFO hang on every wiki write.
## Tests
All have outside **and in-brain** targets and positive controls.
- `TestBuildWingIndexRefusesSymlinkedNotesAndSurvivesSwapRaces`: red before the fix (110k leaks).
- `TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall`: red before the fix.
- `TestStripUnresolvedLinksDoesNotCountASymlinkedNoteAsAnEntry`: red against the old linkcheck.go.
- `TestReadDirDirsListsOnlyRealSubdirsAndRefusesSymlinkedComponents`
## Flips
- **Red:** SameFile off; ReadDirDirs without `realDir`; ReadDirDirs listing symlinks.
- **Not used as evidence:** reading titles via ReadRegularMax is equivalent, so it isn't a real flip.
## Not covered
- **`_index.md` write:** still uses `os.WriteFile` (write path, #158).
- **Intermediate in-brain dir swap:** accepted residual, #167.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---
_Created via git-mcp on behalf of @mathias_
BuildWingIndex/BuildAllWingIndexes listed wiki/<wing>/<hall>/ with
os.ReadDir and opened each note by path: a symlinked note put the
target's title into _index.md (110k leaks in the swap race), a symlinked
wing was followed and its _index.md written into the target, and a FIFO
hung the build (it runs on every brain_write).
- New brainfs.ReadDirDirs: real subdirectories only, every component of
the dir a real directory. Wings and halls are listed with it and
ReadDirRegular; titles are read with ReadRegular
(readTitleAndCreatedIn). A symlinked wing is an error; a symlinked hall
is skipped.
- linkcheck entryIndex walks with WalkRegular: a symlinked .md name no
longer makes a [[link]] resolve.
- readTitleAndCreated (path-based) stays for tunnel.DetectTunnels until
PR #170 lands; that is slice 5b.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1. internal/brain/linkcheck.go (entryIndex walkErr): a write-path regression. Every brain_write and brain_update now fails and the learning is lost when one of the linked trees is not a real directory. StripUnresolvedLinks runs on every write, through brainstore.Write/Update, api.WriteNote and api.UpdateNote, and its error fails the write. On this PR, entryIndex turns WalkRegular's walk-root ErrNotRegular into a returned error. So if the top-level knowledge/ or wiki/ is a symlink (in-brain target or dangling) or a regular file, every write is refused before anything touches disk.
I ran the same probe against origin/main and this head, through brainstore.Store.Write:
state of knowledge/
main
PR
real dir
note written
note written
symlink to an in-brain dir
note written
write error, no note
dangling symlink
note written
write error, no note
regular file
note written
write error, no note
On main, filepath.WalkDir Lstats its root and skips a non-directory, so the tree contributed nothing and the write landed. StripUnresolvedLinks's own doc says a refused write on the unattended claudewatcher path is a lost learning. The brain is a Syncthing folder, so one peer-planted entry switches off all writes, with nothing in the suite to notice.
Fix: match main. A linked-tree root that is not a real directory contributes no entries; warn about it, don't fail. Add a test that a brain_write lands with a symlinked knowledge/, with an in-brain target. The per-entry error case (an unreadable subdirectory) already fails the write on main, so it is not a regression; whether it should is a separate question.
2. internal/brain/index_root_test.go, TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall: the hall half is vacuous, and the name claims more than the assertions show (rule 9). readIndex discards the os.ReadFile error, and the test discards BuildWingIndex's error. So "not contains" passes when no _index.md was written at all. Flip: let ReadDirDirs include symlinks. Then one symlinked hall makes BuildWingIndex error for the whole wing (no index written), and one symlinked wing makes BuildAllWingIndexes abort for every wing. All brain tests stay green under that flip. Only the brainfs unit test goes red. The PR's code handles this correctly today (it skips the hall), but nothing at the caller level would notice that behaviour being removed. Main also skipped symlinked halls, because DirEntry.IsDir() is false for a symlink, so this assertion is not a regression test either.
Fix: add a real hall next to the symlinked one. Assert that BuildWingIndex returns nil, that the index exists, and that it lists the real hall's note. Add a BuildAllWingIndexes case with a symlinked wing next to a real one: no error, and the real wing is indexed.
Flip results (each patch restored afterwards)
index.go reverted to main: red (sentinel in the index via both the outside link and the in-brain link; symlinked wing not refused).
linkcheck.go reverted to main: red.
ReadRegular without its Lstat type check: red. Without SameFile: red (the in-brain swap race).
ReadDirRegular listing symlinks: red in brain and brainfs.
ReadDirDirs listing symlinks, or without realDir: red in brainfs only (finding 2).
BuildWingIndex swallowing the collectWingEntries error: red.
WalkRegular yielding symlinks: red (StripUnresolvedLinks test).
entryIndex dropping walkErr: green. That is the direction of finding 1's fix, so nothing pins this error path either way.
Dropping the opened-handle IsRegular check: green, because SameFile subsumes it when the Lstat info is passed. Redundant, not a gap.
Non-blocking
BuildWingIndex ignores Lstat errors other than not-exist. They do resurface via realDir in collectWingEntries, so this is cosmetic.
The test comment says a FIFO "hung every brain_write". That is still true after this PR: brainstore.Write with a held-open FIFO under wiki/ still hangs (verified, 3s timeout, note already on disk), now through AutoTunnel→DetectTunnels→path-based readTitleAndCreated. BuildWingIndex itself no longer hangs (verified). This belongs to the remaining slice 5 scope (tunnel); make sure it is tracked as 5b.
On a symlinked wing, BuildWingIndex now errors, where main followed it. Every write-path caller treats an index error as best-effort (brainstore, api.Write, promote, tools_notes ReindexError), so no write is lost. brain_index/POST /index with that wing now returns an error, which is correct.
The note write itself still follows a symlinked wing (writeHallNote). That is #158's scope.
Gates
CI run 9311 (pull_request, 6e3c51c): Lint/Test/Vet success. CI=1 task check exits 0 at the worktree head. go test -race -count=5 ./internal/brain ./internal/brainfs passes. Green, and green covers neither blocking finding.
**Gatekeeper review, PR #175 (#157 slice 5a, head 6e3c51c): BLOCKED.** Verdict only; Mathias approves merges.
### Blocking
**1. `internal/brain/linkcheck.go` (entryIndex walkErr): a write-path regression. Every brain_write and brain_update now fails and the learning is lost when one of the linked trees is not a real directory.**
`StripUnresolvedLinks` runs on every write, through `brainstore.Write`/`Update`, `api.WriteNote` and `api.UpdateNote`, and its error fails the write. On this PR, `entryIndex` turns `WalkRegular`'s walk-root `ErrNotRegular` into a returned error. So if the top-level `knowledge/` or `wiki/` is a symlink (in-brain target or dangling) or a regular file, every write is refused before anything touches disk.
I ran the same probe against `origin/main` and this head, through `brainstore.Store.Write`:
| state of `knowledge/` | main | PR |
|---|---|---|
| real dir | note written | note written |
| symlink to an in-brain dir | note written | **write error, no note** |
| dangling symlink | note written | **write error, no note** |
| regular file | note written | **write error, no note** |
On main, `filepath.WalkDir` Lstats its root and skips a non-directory, so the tree contributed nothing and the write landed. `StripUnresolvedLinks`'s own doc says a refused write on the unattended claudewatcher path is a lost learning. The brain is a Syncthing folder, so one peer-planted entry switches off all writes, with nothing in the suite to notice.
Fix: match main. A linked-tree root that is not a real directory contributes no entries; warn about it, don't fail. Add a test that a brain_write lands with a symlinked `knowledge/`, with an in-brain target. The per-entry error case (an unreadable subdirectory) already fails the write on main, so it is not a regression; whether it should is a separate question.
**2. `internal/brain/index_root_test.go`, `TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall`: the hall half is vacuous, and the name claims more than the assertions show (rule 9).**
`readIndex` discards the `os.ReadFile` error, and the test discards `BuildWingIndex`'s error. So "not contains" passes when no `_index.md` was written at all. Flip: let `ReadDirDirs` include symlinks. Then one symlinked hall makes `BuildWingIndex` error for the whole wing (no index written), and one symlinked wing makes `BuildAllWingIndexes` abort for every wing. **All brain tests stay green** under that flip. Only the brainfs unit test goes red. The PR's code handles this correctly today (it skips the hall), but nothing at the caller level would notice that behaviour being removed. Main also skipped symlinked halls, because `DirEntry.IsDir()` is false for a symlink, so this assertion is not a regression test either.
Fix: add a real hall next to the symlinked one. Assert that `BuildWingIndex` returns nil, that the index exists, and that it lists the real hall's note. Add a `BuildAllWingIndexes` case with a symlinked wing next to a real one: no error, and the real wing is indexed.
### Flip results (each patch restored afterwards)
- `index.go` reverted to main: red (sentinel in the index via both the outside link and the **in-brain** link; symlinked wing not refused).
- `linkcheck.go` reverted to main: red.
- `ReadRegular` without its Lstat type check: red. Without SameFile: red (the in-brain swap race).
- `ReadDirRegular` listing symlinks: red in brain and brainfs.
- `ReadDirDirs` listing symlinks, or without `realDir`: red **in brainfs only** (finding 2).
- `BuildWingIndex` swallowing the `collectWingEntries` error: red.
- `WalkRegular` yielding symlinks: red (StripUnresolvedLinks test).
- `entryIndex` dropping walkErr: green. That is the direction of finding 1's fix, so nothing pins this error path either way.
- Dropping the opened-handle IsRegular check: green, because SameFile subsumes it when the Lstat info is passed. Redundant, not a gap.
### Non-blocking
- `BuildWingIndex` ignores Lstat errors other than not-exist. They do resurface via `realDir` in `collectWingEntries`, so this is cosmetic.
- The test comment says a FIFO "hung every brain_write". That is still true after this PR: `brainstore.Write` with a held-open FIFO under `wiki/` still hangs (verified, 3s timeout, note already on disk), now through `AutoTunnel`→`DetectTunnels`→path-based `readTitleAndCreated`. `BuildWingIndex` itself no longer hangs (verified). This belongs to the remaining slice 5 scope (tunnel); make sure it is tracked as 5b.
- On a symlinked wing, `BuildWingIndex` now errors, where main followed it. Every write-path caller treats an index error as best-effort (brainstore, `api.Write`, promote, tools_notes `ReindexError`), so no write is lost. `brain_index`/`POST /index` with that wing now returns an error, which is correct.
- The note write itself still follows a symlinked wing (`writeHallNote`). That is #158's scope.
### Gates
CI run 9311 (pull_request, 6e3c51c): Lint/Test/Vet success. `CI=1 task check` exits 0 at the worktree head. `go test -race -count=5 ./internal/brain ./internal/brainfs` passes. **Green, and green covers neither blocking finding.**
Review round 1 on PR #175:
- entryIndex returned WalkRegular's walk-root ErrNotRegular as an error,
and StripUnresolvedLinks runs before every brain_write: a symlinked,
dangling or regular-file knowledge/ (or wiki/) made every write fail
and lose the learning. main's filepath.WalkDir skipped such a root. It
is now skipped with a warning; other walk errors still abort, as on
main. New table test over the three shapes.
- The symlinked-hall assertion passed with no index written at all. It
now indexes a real hall next to the symlinked one and asserts no
error, the index exists and lists the real note; a new test does the
same for BuildAllWingIndexes with a symlinked wing next to a real one.
- The test comment no longer says this PR fixed the FIFO hang on every
brain_write: that path is tunnel.DetectTunnels (slice 5b).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
entryIndex write-path regression: closed. I re-ran the round-1 probe through brainstore.Store.Write on 8fdf728. With knowledge/ as a symlink to an in-brain dir, a dangling symlink, or a regular file, StripUnresolvedLinks returns nil, the note lands, [[real]] still resolves and the invented link is unwrapped. That matches main. I also ran the same three states for wiki/ (with a real knowledge/ entry): StripUnresolvedLinks returns nil in all three. The dangling and file cases then fail in the note write's own mkdir, which this PR does not touch.
Vacuous hall test: closed. The renamed TestBuildWingIndexSkipsASymlinkedHallAndRefusesASymlinkedWing now requires no error, an existing index, and the real hall's note listed. With ReadDirDirs listing symlinks, it and the new TestBuildAllWingIndexesSkipsASymlinkedWing both go red. The names match the assertions.
The FIFO comment now says "hung the index build", which is accurate for BuildWingIndex on main.
Flips (each restored afterwards)
Red: skip disabled (all 3 subtests); ReadDirDirs listing symlinks (brain and brainfs); ReadDirDirs without realDir (brainfs); BuildWingIndex swallowing the collectWingEntries error; ReadRegular without SameFile (the in-brain swap race); WalkRegular yielding symlinks.
Green (survivors, read one by one):
Skip limited to rel == "knowledge": green. The skip test builds only knowledge/ cases, so the wiki/ half of the doc comment is unasserted. The code is generic (rel == dir), so this is not blocking. A wiki/ subtest (with a real knowledge/ entry as the positive control) would pin it.
Skip any walk error: green. This is the deliberately unpinned non-root abort. I agree with not pinning it. That abort is behaviour main already has (an unreadable subdirectory under knowledge//wiki/ refuses every brain_write), so it is not this PR's regression. It is a lost-learning path, though, and should be a tracker issue rather than a closing remark.
Skip ErrNotRegular at any rel: green. This is equivalent, because WalkRegular produces ErrNotRegular only from its root check. Not a gap.
BuildAllWingIndexes continuing past a failing wing: green. It aborts on the first error, as main did, and nothing pins that either way. Not a regression.
Correction to my round-1 report
I said flip.sh drops failure names on this host. That was wrong. Invoked correctly, it prints them (re-checked on the skip-disabled flip). My empty-name rc=1 runs came from my own quoting of the package list, which made go test fail with "setup failed". Your earlier flip results stand.
Gates
CI run 9324 (8fdf728): Lint/Test/Vet success. CI=1 task check exits 0, with 0 FAIL lines and a clean tree. go test -race -count=5 ./internal/brain ./internal/brainfs passes.
Not checked
Live pod or a real brain dir; MCP brain_write end to end over HTTP (brainstore.Write called directly); hard links and mid-walk directory swaps (#167); the _index.md write through a symlink (#158); slice 5b (the FIFO hang through DetectTunnels, still present).
**Gatekeeper review, round 2, PR #175 (#157 slice 5a, head 8fdf728): PASS.** Verdict only; Mathias approves merges (risk/high).
### Round-1 blockers, re-verified by execution
1. **entryIndex write-path regression: closed.** I re-ran the round-1 probe through `brainstore.Store.Write` on 8fdf728. With `knowledge/` as a symlink to an in-brain dir, a dangling symlink, or a regular file, `StripUnresolvedLinks` returns nil, the note lands, `[[real]]` still resolves and the invented link is unwrapped. That matches main. I also ran the same three states for `wiki/` (with a real `knowledge/` entry): `StripUnresolvedLinks` returns nil in all three. The dangling and file cases then fail in the note write's own `mkdir`, which this PR does not touch.
2. **Vacuous hall test: closed.** The renamed `TestBuildWingIndexSkipsASymlinkedHallAndRefusesASymlinkedWing` now requires no error, an existing index, and the real hall's note listed. With `ReadDirDirs` listing symlinks, it and the new `TestBuildAllWingIndexesSkipsASymlinkedWing` both go red. The names match the assertions.
3. The FIFO comment now says "hung the index build", which is accurate for `BuildWingIndex` on main.
### Flips (each restored afterwards)
Red: skip disabled (all 3 subtests); `ReadDirDirs` listing symlinks (brain and brainfs); `ReadDirDirs` without `realDir` (brainfs); `BuildWingIndex` swallowing the `collectWingEntries` error; `ReadRegular` without SameFile (the in-brain swap race); `WalkRegular` yielding symlinks.
Green (survivors, read one by one):
- **Skip limited to `rel == "knowledge"`: green.** The skip test builds only `knowledge/` cases, so the `wiki/` half of the doc comment is unasserted. The code is generic (`rel == dir`), so this is not blocking. A `wiki/` subtest (with a real `knowledge/` entry as the positive control) would pin it.
- **Skip any walk error: green.** This is the deliberately unpinned non-root abort. I agree with not pinning it. That abort is behaviour main already has (an unreadable subdirectory under `knowledge/`/`wiki/` refuses every brain_write), so it is not this PR's regression. It is a lost-learning path, though, and should be a tracker issue rather than a closing remark.
- **Skip `ErrNotRegular` at any rel: green.** This is equivalent, because `WalkRegular` produces `ErrNotRegular` only from its root check. Not a gap.
- **`BuildAllWingIndexes` continuing past a failing wing: green.** It aborts on the first error, as main did, and nothing pins that either way. Not a regression.
### Correction to my round-1 report
I said `flip.sh` drops failure names on this host. That was wrong. Invoked correctly, it prints them (re-checked on the skip-disabled flip). My empty-name rc=1 runs came from my own quoting of the package list, which made `go test` fail with "setup failed". Your earlier flip results stand.
### Gates
CI run 9324 (8fdf728): Lint/Test/Vet success. `CI=1 task check` exits 0, with 0 FAIL lines and a clean tree. `go test -race -count=5 ./internal/brain ./internal/brainfs` passes.
### Not checked
Live pod or a real brain dir; MCP brain_write end to end over HTTP (`brainstore.Write` called directly); hard links and mid-walk directory swaps (#167); the `_index.md` write through a symlink (#158); slice 5b (the FIFO hang through `DetectTunnels`, still present).
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Refs #157 (slice 5a). risk/high: control code, adversarial review required.
Problem
BuildWingIndexandBuildAllWingIndexesrun on every brain_write. They listedwiki/<wing>/<hall>/withos.ReadDirand opened notes by path:_index.md(110k leaks in the race)._index.mdwas written into the link's target.Change
brainfs.ReadDirDirs: lists real subdirectories only, and every component of the directory must be a real directory. Wings and halls are listed with it andReadDirRegular, and titles are read withReadRegular(readTitleAndCreatedIn). A symlinked wing is an error; a symlinked hall is skipped.entryIndex: walks withWalkRegular, so a symlinked.mdname no longer makes a[[link]]resolve.readTitleAndCreated(path-based) stays fortunnel.DetectTunnelsuntil #170 lands. That will be slice 5b, which includes the FIFO hang on every wiki write.Tests
All have outside and in-brain targets and positive controls.
TestBuildWingIndexRefusesSymlinkedNotesAndSurvivesSwapRaces: red before the fix (110k leaks).TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall: red before the fix.TestStripUnresolvedLinksDoesNotCountASymlinkedNoteAsAnEntry: red against the old linkcheck.go.TestReadDirDirsListsOnlyRealSubdirsAndRefusesSymlinkedComponentsFlips
realDir; ReadDirDirs listing symlinks.Not covered
_index.mdwrite: still usesos.WriteFile(write path, #158).🤖 Generated with Claude Code
Created via git-mcp on behalf of @mathias
Gatekeeper review, PR #175 (#157 slice 5a, head
6e3c51c): BLOCKED. Verdict only; Mathias approves merges.Blocking
1.
internal/brain/linkcheck.go(entryIndex walkErr): a write-path regression. Every brain_write and brain_update now fails and the learning is lost when one of the linked trees is not a real directory.StripUnresolvedLinksruns on every write, throughbrainstore.Write/Update,api.WriteNoteandapi.UpdateNote, and its error fails the write. On this PR,entryIndexturnsWalkRegular's walk-rootErrNotRegularinto a returned error. So if the top-levelknowledge/orwiki/is a symlink (in-brain target or dangling) or a regular file, every write is refused before anything touches disk.I ran the same probe against
origin/mainand this head, throughbrainstore.Store.Write:knowledge/On main,
filepath.WalkDirLstats its root and skips a non-directory, so the tree contributed nothing and the write landed.StripUnresolvedLinks's own doc says a refused write on the unattended claudewatcher path is a lost learning. The brain is a Syncthing folder, so one peer-planted entry switches off all writes, with nothing in the suite to notice.Fix: match main. A linked-tree root that is not a real directory contributes no entries; warn about it, don't fail. Add a test that a brain_write lands with a symlinked
knowledge/, with an in-brain target. The per-entry error case (an unreadable subdirectory) already fails the write on main, so it is not a regression; whether it should is a separate question.2.
internal/brain/index_root_test.go,TestBuildWingIndexDoesNotFollowASymlinkedWingOrHall: the hall half is vacuous, and the name claims more than the assertions show (rule 9).readIndexdiscards theos.ReadFileerror, and the test discardsBuildWingIndex's error. So "not contains" passes when no_index.mdwas written at all. Flip: letReadDirDirsinclude symlinks. Then one symlinked hall makesBuildWingIndexerror for the whole wing (no index written), and one symlinked wing makesBuildAllWingIndexesabort for every wing. All brain tests stay green under that flip. Only the brainfs unit test goes red. The PR's code handles this correctly today (it skips the hall), but nothing at the caller level would notice that behaviour being removed. Main also skipped symlinked halls, becauseDirEntry.IsDir()is false for a symlink, so this assertion is not a regression test either.Fix: add a real hall next to the symlinked one. Assert that
BuildWingIndexreturns nil, that the index exists, and that it lists the real hall's note. Add aBuildAllWingIndexescase with a symlinked wing next to a real one: no error, and the real wing is indexed.Flip results (each patch restored afterwards)
index.goreverted to main: red (sentinel in the index via both the outside link and the in-brain link; symlinked wing not refused).linkcheck.goreverted to main: red.ReadRegularwithout its Lstat type check: red. Without SameFile: red (the in-brain swap race).ReadDirRegularlisting symlinks: red in brain and brainfs.ReadDirDirslisting symlinks, or withoutrealDir: red in brainfs only (finding 2).BuildWingIndexswallowing thecollectWingEntrieserror: red.WalkRegularyielding symlinks: red (StripUnresolvedLinks test).entryIndexdropping walkErr: green. That is the direction of finding 1's fix, so nothing pins this error path either way.Non-blocking
BuildWingIndexignores Lstat errors other than not-exist. They do resurface viarealDirincollectWingEntries, so this is cosmetic.brainstore.Writewith a held-open FIFO underwiki/still hangs (verified, 3s timeout, note already on disk), now throughAutoTunnel→DetectTunnels→path-basedreadTitleAndCreated.BuildWingIndexitself no longer hangs (verified). This belongs to the remaining slice 5 scope (tunnel); make sure it is tracked as 5b.BuildWingIndexnow errors, where main followed it. Every write-path caller treats an index error as best-effort (brainstore,api.Write, promote, tools_notesReindexError), so no write is lost.brain_index/POST /indexwith that wing now returns an error, which is correct.writeHallNote). That is #158's scope.Gates
CI run 9311 (pull_request,
6e3c51c): Lint/Test/Vet success.CI=1 task checkexits 0 at the worktree head.go test -race -count=5 ./internal/brain ./internal/brainfspasses. Green, and green covers neither blocking finding.Gatekeeper review, round 2, PR #175 (#157 slice 5a, head
8fdf728): PASS. Verdict only; Mathias approves merges (risk/high).Round-1 blockers, re-verified by execution
brainstore.Store.Writeon8fdf728. Withknowledge/as a symlink to an in-brain dir, a dangling symlink, or a regular file,StripUnresolvedLinksreturns nil, the note lands,[[real]]still resolves and the invented link is unwrapped. That matches main. I also ran the same three states forwiki/(with a realknowledge/entry):StripUnresolvedLinksreturns nil in all three. The dangling and file cases then fail in the note write's ownmkdir, which this PR does not touch.TestBuildWingIndexSkipsASymlinkedHallAndRefusesASymlinkedWingnow requires no error, an existing index, and the real hall's note listed. WithReadDirDirslisting symlinks, it and the newTestBuildAllWingIndexesSkipsASymlinkedWingboth go red. The names match the assertions.BuildWingIndexon main.Flips (each restored afterwards)
Red: skip disabled (all 3 subtests);
ReadDirDirslisting symlinks (brain and brainfs);ReadDirDirswithoutrealDir(brainfs);BuildWingIndexswallowing thecollectWingEntrieserror;ReadRegularwithout SameFile (the in-brain swap race);WalkRegularyielding symlinks.Green (survivors, read one by one):
rel == "knowledge": green. The skip test builds onlyknowledge/cases, so thewiki/half of the doc comment is unasserted. The code is generic (rel == dir), so this is not blocking. Awiki/subtest (with a realknowledge/entry as the positive control) would pin it.knowledge//wiki/refuses every brain_write), so it is not this PR's regression. It is a lost-learning path, though, and should be a tracker issue rather than a closing remark.ErrNotRegularat any rel: green. This is equivalent, becauseWalkRegularproducesErrNotRegularonly from its root check. Not a gap.BuildAllWingIndexescontinuing past a failing wing: green. It aborts on the first error, as main did, and nothing pins that either way. Not a regression.Correction to my round-1 report
I said
flip.shdrops failure names on this host. That was wrong. Invoked correctly, it prints them (re-checked on the skip-disabled flip). My empty-name rc=1 runs came from my own quoting of the package list, which madego testfail with "setup failed". Your earlier flip results stand.Gates
CI run 9324 (
8fdf728): Lint/Test/Vet success.CI=1 task checkexits 0, with 0 FAIL lines and a clean tree.go test -race -count=5 ./internal/brain ./internal/brainfspasses.Not checked
Live pod or a real brain dir; MCP brain_write end to end over HTTP (
brainstore.Writecalled directly); hard links and mid-walk directory swaps (#167); the_index.mdwrite through a symlink (#158); slice 5b (the FIFO hang throughDetectTunnels, still present).