Skip to content

auto_recall hook queries non-existent 'heat' column instead of 'heat_base' #20

@PSGSupport

Description

@PSGSupport

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:

  1. 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.

  2. 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, ...
  3. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions