Summary
mcp_server/hooks/auto_recall.py queries memories.heat, but the actual schema column is heat_base. As a result, every UserPromptSubmit hook fire fails with:
[cortex-auto-recall] FTS query failed: column "heat" does not exist
LINE 2: SELECT id, content, heat, domain, agent_context,...
^
The hook then exits 0 with no injection — silent failure on every user prompt. Per-prompt memory injection is broken.
Reproducer
On a fresh Cortex install (3.14.5 + 3.15.0):
echo '{"prompt": "any text"}' | python -m mcp_server.hooks.auto_recall
# stderr: [cortex-auto-recall] FTS query failed: column "heat" does not exist
# stdout: (empty)
Affected lines
mcp_server/hooks/auto_recall.py, lines 144-170:
SELECT id, content, heat, domain, agent_context, is_protected, # heat → heat_base
ts_rank_cd(content_tsv, q) AS rank
FROM memories,
plainto_tsquery('english', %s) q
WHERE content_tsv @@ q
AND heat >= %s # heat → heat_base
AND NOT is_benchmark
ORDER BY is_protected DESC, rank DESC, heat DESC # heat → heat_base
LIMIT %s
The heat_base column comes from pg_schema.py / migrations; the heat field returned by recall_memories() is a derived/effective heat (computed via effective_heat() PL/pgSQL function), not a stored column.
Fix
Three options:
-
Use heat_base directly (simplest, what I patched locally):
SELECT id, content, heat_base, domain, agent_context, is_protected, ...
AND heat_base >= %s
ORDER BY is_protected DESC, rank DESC, heat_base DESC
Caveat: doesn't apply lazy decay. The hook's purpose is fast filter, so raw heat_base is acceptable here.
-
Use the effective_heat() PL/pgSQL function to mirror the production recall path:
SELECT id, content, effective_heat(heat_base, heat_base_set_at, plasticity, no_decay) AS heat, ...
-
Use the recall_memories() server-side procedure instead of inline SQL, matching the main retrieval path. Heaviest of the three but stays consistent with production.
I'd lean toward #2 — keeps the lazy-decay semantics that the docstring at line 28 advertises ("heat_base filter") while preserving the time-decayed scoring intent.
Environment
- Cortex 3.14.5 (also confirmed in 3.15.0 via inspection)
- Python 3.12 / Postgres 16 + pgvector
- Discovered while wiring auto_recall as a UserPromptSubmit hook in
~/.claude/settings.json
Summary
mcp_server/hooks/auto_recall.pyqueriesmemories.heat, but the actual schema column isheat_base. As a result, every UserPromptSubmit hook fire fails with:The hook then exits 0 with no injection — silent failure on every user prompt. Per-prompt memory injection is broken.
Reproducer
On a fresh Cortex install (3.14.5 + 3.15.0):
Affected lines
mcp_server/hooks/auto_recall.py, lines 144-170:The
heat_basecolumn comes frompg_schema.py/ migrations; theheatfield returned byrecall_memories()is a derived/effective heat (computed viaeffective_heat()PL/pgSQL function), not a stored column.Fix
Three options:
Use
heat_basedirectly (simplest, what I patched locally):Caveat: doesn't apply lazy decay. The hook's purpose is fast filter, so raw
heat_baseis acceptable here.Use the
effective_heat()PL/pgSQL function to mirror the production recall path:Use the
recall_memories()server-side procedure instead of inline SQL, matching the main retrieval path. Heaviest of the three but stays consistent with production.I'd lean toward #2 — keeps the lazy-decay semantics that the docstring at line 28 advertises ("heat_base filter") while preserving the time-decayed scoring intent.
Environment
~/.claude/settings.json