memory+llm: three failures the prod logs gave up
All three came out of a survey of the last days of journalctl on prod,
and each one silently destroyed something the user had already paid for.

memory_facts: the no-embedding INSERT bound $13 while its column list
had 12 entries, so every fact written while the embedding backend was
down died on PostgresSyntaxError and the extraction was lost — 47
embed failures in three days, most of them landing in this branch.

Embedding input now gets clipped instead of 400'd away. A 400 takes the
whole embedding with it and recall drops to ILIKE over everything; a
reaction-session prompt (a full event envelope inlined) did that 41
times. settings.embedding_max_chars (6000, 0 disables) caps the input
at the model's window, so recall still works on the head of the text.

Message strips NUL bytes at the model boundary. PostgreSQL text cannot
hold one, and a NUL arriving in a tool result (reading a binary file)
made the whole session_messages INSERT fail with
CharacterNotInRepertoireError — the turn died and the user lost it.
Three times on 2026-10-07. As a Message validator it covers every
writer downstream: session store, kv store, memory extraction.

Each new test was checked to fail with its fix reverted.
1 parent 25843cc commit 5e51f8742824163f7618cbbd34c07cf5be5ea7ad
@Eugene Sukhodolskiy Eugene Sukhodolskiy authored 5 hours ago
Showing 6 changed files
View
navi/config.py
View
navi/llm/base.py
View
navi/memory/_embeddings.py
View
navi/memory/_facts.py
View
tests/unit/llm/test_message.py 0 → 100644
View
tests/unit/memory/test_store.py