v0.3.2 — OS-level vault lock + edge-case fixes
Locking rewrite (headline)
The vault lock now uses OS-level byte-range locks (msvcrt.locking on Windows, fcntl.flock on POSIX) instead of the previous create-file + 60-second-stale-reclaim scheme. Two real problems disappear:
- Crash availability: if an agent process crashed while holding the lock, every writer to that vault failed with
VaultLockTimeoutfor up to 60 seconds. OS locks are released by the kernel at process death — the next writer acquires immediately. Covered by a dedicated test that kills a lock-holding subprocess and asserts instant re-acquisition. - Stale-reclaim race: two waiters could both judge the lock stale, both unlink it, and both acquire — silently breaking mutual exclusion. There is no stale concept to race over anymore.
The lock file (.vault.lock) now persists on disk; removing it while a holder might exist would break exclusion, so it is never unlinked. It is never indexed or treated as a lesson.
Other fixes
agentbrain lint --scope tag:xno longer reports falseDANGLINGfindings for supersede targets that exist outside the requested tag scope.case_ids containing glob metacharacters ([,?,*) no longer cause lesson-id collisions that could silently overwrite an existing lesson (exact-prefix regex scan replaces the raw glob).- Suggestion files (
memory_suggest) now use real YAML frontmatter — titles containing colons or newlines used to corrupt the header — and are written atomically. atomic_writetemp-file names include the thread id, so same-process concurrent writers can never share a temp file.
Verification
- 65 tests (6 new/updated: killed-holder release, leftover-lock-file non-blocking, timeout-while-held, YAML-injection roundtrip, tag-scope DANGLING, glob-metachar ids)
- Cross-process smoke: 4 independent processes ingesting into the same case concurrently — 20/20 lessons landed, no loss, no lock timeouts