PR #170 round-2 review: both guards stayed green when removed. Adds an
ErrInvalidID sentinel, a plain-prefix-then-'..' escape case asserted on
the sentinel, and a no-reader FIFO case with a timeout. Flip-verified:
allowing '/' in the id tail and dropping O_NONBLOCK each turn the suite red.
Refs #169
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review round 1 on PR #170 found two more handlers with the #169 class of
bug (verified by execution):
- brain_update resolved its caller path with resolveWithin, a lexical
containment check: any brain file could be superseded
(classification.yaml, raw/, sessions/), and a final symlink was
written through. UpdateNote now resolves like brain_edit
(notes.Resolve, ScopeNotes: knowledge/ and wiki/ .md notes), and reads
and rewrites through brainfs (ReadRegular, WriteRegular).
- session_log joined the caller's session_id onto the sessions dir and
opened it O_CREATE|O_APPEND: an id with path segments created or
appended a .jsonl file anywhere the pod could write. The id must now
be a plain name (^[A-Za-z0-9_][A-Za-z0-9._-]{0,127}$, which every live
session file matches), and the open adds O_NOFOLLOW|O_NONBLOCK. Read
checks the id too.
- Tunnel tests: the .txt case now names an existing file (it failed on
"missing" before), the symlink test adds an in-brain target, and the
FIFO comment no longer claims the handle check is the only guard.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>