Skip to content

[bug] Holographic memory: retrieval_count never incremented — search() in retrieval.py bypasses store.search_facts() #17899

@albertoMartinsen

Description

@albertoMartinsen

Bug Description

The retrieval_count field in the facts table is never incremented during normal operation, making it useless as a usage metric.

Root Cause

There are two search paths, and the wrong one is used everywhere:

  1. store.search_facts() (plugins/memory/holographic/store.py:187) — correctly increments retrieval_count via UPDATE facts SET retrieval_count = retrieval_count + 1 WHERE fact_id IN (...) after returning results. But nobody calls this method.

  2. retrieval.search() (plugins/memory/holographic/retrieval.py:48) — the hybrid search used by both prefetch() (context injection) and fact_store(action='search') (user tool). Goes through _fts_candidates() directly against SQLite and never increments retrieval_count.

Call Chain

prefetch() [__init__.py:209]
  → retriever.search() [retrieval.py:48]
    → _fts_candidates() [retrieval.py:481]
      → conn.execute(sql, params)  ← direct SQL, no UPDATE

fact_store(action='search') [__init__.py:273]
  → retriever.search()  ← same path, same bug

Impact

  • fact-store-dreaming skill relies on retrieval_count to identify orphan/stale facts, but it's always 0 for every fact
  • Trust score adjustments based on "was this fact retrieved?" are impossible without user confirmation
  • Any future feature depending on usage metrics gets zero signal from this field

Proposed Fix

Add the retrieval_count UPDATE to retrieval.search() after results are collected, so both prefetch and manual search paths are covered:

# Increment retrieval_count for all matched facts.
# This covers both prefetch injections and manual fact_store(action='search').
if results:
    ids = [r["fact_id"] for r in results]
    placeholders = ",".join(["?"] * len(ids))
    try:
        self.store._conn.execute(
            f"UPDATE facts SET retrieval_count = retrieval_count + 1 WHERE fact_id IN ({placeholders})",
            ids,
        )
        self.store._conn.commit()
    except Exception:
        pass  # Non-critical — don't fail search over a counter update

The search_facts() method in store.py could then be retired or kept as a simple wrapper, since its main differentiator (retrieval_count increment) would now exist in both paths.

Related

Environment

  • Hermes Agent version: latest main (648b8991)
  • Plugin: plugins/memory/holographic/
  • Affected file: retrieval.py (method search())

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/pluginsPlugin system and bundled pluginstool/memoryMemory tool and memory providerstype/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions