BoB v0.98.0 — the memory-hardening release
Added
- Single-writer memory write fence (OS-level, fail-closed). Every memory writer must hold a family-scoped lock (one per vector-store service + collection family, spanning all vector dims). Memory enabled with no armed fence refuses to boot; a second writer degrades to read-only with honest health (HTTP 423
memory_write_locked) instead of corrupting the store; permission problems report as permission problems. - Experimental zero-Docker vector store (opt-in). The
zvecembedded store runs as a supervised child process owned by core (crash → clean error, next call restarts it). Opt in viaconfig/memory_stores.toml+pip install zvec==0.5.1; Docker Qdrant remains the default. - BobLKS: a local corpus instance —
ingest()/retrieve()over your own documents through the same fence, fingerprint, and provider seams as agent memory. - Asymmetric embedder seam — typed
embed_query/embed_docwith per-slot instruction templates (part of the embed fingerprint), batching, and a first-write dimension probe (writes fail closed on mismatch, reads fail open). - Retrieval eval harness with an honest ceiling (
bobclaw-core/evals/retrieval/): author-blind paraphrase set, Wilson CIs, paired outputs. Measured recall@10: qwen3-embedding-4b 65%, 0.6b 40%. Earlier 95%/88% figures were lexical-leakage-inflated and are relabeled as an embedding smoke. - Recall backfill past dangling vectors — bounded overfetch/paging to reach
top_kvalid facts instead of silently truncating. - CI + release-surface baseline runner —
.github/workflows/tests.yml(Windows + Linux from the pinned locks) and./run_baseline_tests.ps1, which exits non-zero if ANY suite fails. UPGRADE.md— the supported tag-to-tag upgrade runbook.
Changed
- Default embedder:
granite-embedding-311m→qwen3-embedding-4b(768 → 2560 dims, last-token pooling), decided on the paraphrase eval above. granite remains the documented CPU-light alternative (not yet measured on the new eval set — an open follow-up). Existing memory stores must re-index — seeUPGRADE.md. - Compose project naming is pinned and fixed container names are removed, so two installs coexist on one host (distinct
COMPOSE_PROJECT_NAME+ the newBOBCLAW_*_PORTdatastore-port overrides). Existing installs: seeUPGRADE.mdbefore your nextcompose up. - L1 fact dedup identity excludes volatile turn metadata (extractor v1→v2), so a repeated fact no longer multiplies.
Fixed
- Qdrant container healthcheck was always failing (image ships no
wget) — now probes/healthzover bash/dev/tcp. MEMORY_SLOTS_FILEwas documented but ignored by bootstrap — a second install could not point its embedder/extractor slots away from the shipped defaults. Now honored (with a regression test).start-local.ps1probed fixed ports for its idempotence check and could mistake a coexisting install's gateway for its own — now probes the ports the install is configured for.- Memory integration smokes isolate to throwaway collections with a fail-closed write guard.
Known limitations (honest)
- zvec approximate search trails exact search on recall (21/40 vs 26/40 through the full production path); a rescoring tier is planned for v0.99 — Qdrant stays the recommended default.
- The document parser applies soft limits only (hard cap filed for v0.99).
pip-auditreports UNAVAILABLE (never silently "clean") when not installed.
Full notes in CHANGELOG.md. Release gate: clean-clone, shipped-install-path baseline — core 2846 passed / 0 failed / 6 skipped, gateway / pipeline / KMM / pip-check green — plus a live production-shaped stand-up (login, chat, fan-out, memory write + recall, restart survival) on a second isolated install.