Skip to content

v2.14.0 — Brain scoping, honest degradation, and consolidation that finishes

Choose a tag to compare

@acidkill acidkill released this 28 Jul 16:17
· 152 commits to main since this release
2695870

Correctness release. The theme is silent failure: three of these bugs made the system look like it was working while it quietly was not — an empty-looking brain that held 10k neurons, recall that skipped reranking without saying so, and writes that landed but reported a timeout.

⚠️ Upgrade note — check your brain scopes

Rows are scoped by brain name (brain_id = "default"), but several call sites passed the brain record's UUID primary key. Reasoning mining was one such path, so its output landed in a scope recall never reads.

Before this release, a fetch by record id still returned those rows. #63 now brain-scopes every by-id fetch, so they become unreadable by any path until remapped.

-- Check first: a healthy install shows one scope per brain name, no UUIDs
SELECT brain_id, count() FROM neuron GROUP BY brain_id;

-- Remap if a UUID scope shows up
UPDATE neuron  SET brain_id = "default" WHERE brain_id = "<uuid>";
UPDATE fiber   SET brain_id = "default" WHERE brain_id = "<uuid>";
UPDATE synapse SET brain_id = "default" WHERE brain_id = "<uuid>";

Security

  • Cross-brain read via record id (#63) — every fetch-by-record-id did a bare select with no WHERE brain_id, so a caller in brain A could read a record owned by brain B just by knowing its id. All six getters now scope to the brain, fail-closed, while staying pinned by id so the dashboard-perf property holds.
  • Id sanitisers consolidated (#63) — two paths bypassed the single _to_surreal_id choke point.

Fixed

  • Brain-owned rows scoped by UUID instead of name (#97, #98) — reads reported an empty brain (grade F, EMPTY_BRAIN) on a brain holding 10k+ neurons; writes were worse, with reasoning mining binding its whole job to a UUID scope. POST /brain now pins brain_id to the name, closing the source rather than the symptoms.
  • Rerank degradation was silent (#97) — an unreachable reranker made recall fall back to raw spreading-activation order with only a log line. Recall now retries once and reports it: rerank_degraded (MCP), rerank_degraded_warning (CLI). The reason string is sanitised so a remote endpoint cannot leak a token through it.
  • Consolidation aborted on large brains (#97) — per-strategy timeout had regressed to 120s (restored to 600s, total budget 3600s); _lifecycle pulled 10k neurons with embeddings in one response, which SurrealDB dropped mid-transfer ([Errno 104] Connection reset by peer); _query retried its reconnect once in the same instant, hitting the same reset. Verified live: a full smem consolidate --dry-run on an 11k-neuron brain now finishes in ~47s.
  • Writes reported a timeout after succeeding (#79, #99) — inline embedding ran unbounded in the write path, pushing smem_remember past the 30s tool-call cap MCP hosts impose. Now bounded at 10s (SURREAL_MEMORY_INLINE_EMBED_TIMEOUT; <= 0 restores the old behaviour).
  • Dangling synapses survived neuron deletion (#89) — the cascade filtered on brain_id, but some write paths create synapses with a NULL one.
  • SURREAL_MEMORY_BRAIN ignored when --brain omitted (#90, #98).
  • En-dash was not a clause boundary (#65).

Added

  • Write-gate shadow/enforce modes (#93) — off | shadow | enforce with a per-intent auto_capture_mode, so the gate can be enforced on passive auto-captures while interactive writes stay in shadow. Defaults to off with a legacy fallback, so behaviour is unchanged until opted into.
  • Machine-noise denylist (#94) — empirically derived from real false-accepts (180× Session activity:).
  • BGE-M3 embedding provider (#91) — 1024D, L2-normalised, with a zero-vector guard that raises rather than returning a fabricated [0.0] * dim that would silently poison top-k KNN.
  • Reranker Bearer auth (#92) — an empty key sends no header, so llamastash and llama.cpp are unaffected.

Changed

  • Lint is pinned (#96) — an unpinned ruff made Lint a moving target; 0.16.0 turned main and every open PR red on the same day without a line of code changing.

Full changelog: v2.13.0...v2.14.0