hermes - ✅(Solved) Fix [bug] Holographic memory: retrieval_count never incremented — search() in retrieval.py bypasses store.search_facts() [1 pull requests, 1 comments, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#17899Fetched 2026-05-01 05:55:17
View on GitHub
Comments
1
Participants
1
Timeline
7
Reactions
0
Participants
Timeline (top)
labeled ×4commented ×1cross-referenced ×1renamed ×1

Error Message

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

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.

Fix Action

Fixed

PR fix notes

PR #17910: fix(memory): increment retrieval_count in retrieval.search() (#17899)

Description (problem / solution / changelog)

Summary

retrieval.search() — the code path used by both prefetch() and fact_store(action='search') — never incremented the retrieval_count column in the facts table. The column stayed at 0 for every fact, breaking downstream consumers like fact-store-dreaming that rely on usage metrics to identify orphan/stale facts.

Root Cause

Two search paths exist:

  1. store.search_facts() (store.py) — correctly increments retrieval_count, but nothing calls it
  2. retrieval.search() (retrieval.py) — the actual path used everywhere, goes through _fts_candidates() with direct SQL and never increments the counter

Changes

  • plugins/memory/holographic/retrieval.py: Added UPDATE facts SET retrieval_count = retrieval_count + 1 after search() collects results, wrapped in try/except so counter failures never block searches
  • tests/plugins/memory/test_holographic_retrieval_count.py: New test verifying retrieval_count increments on each search() call

Testing

pytest tests/plugins/memory/test_holographic_retrieval_count.py -v  # 1 passed

Closes #17899

Changed files

  • plugins/memory/holographic/retrieval.py (modified, +17/-0)
  • tests/plugins/memory/test_holographic_retrieval_count.py (added, +87/-0)

Code Example

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

---

# 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
RAW_BUFFERClick to expand / collapse

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

  • #17350 — two other holographic memory bugs (FTS OR fallback broken, silent numpy degradation) found and fixed in the same plugin. This is a separate issue in the same plugin area.

Environment

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

extent analysis

TL;DR

The most likely fix is to add an UPDATE statement to increment the retrieval_count field in the retrieval.search() method.

Guidance

  • Verify that the store.search_facts() method is not being used anywhere in the codebase before modifying the retrieval.search() method.
  • Add the proposed UPDATE statement to the retrieval.search() method to increment the retrieval_count field for matched facts.
  • Test the changes to ensure that the retrieval_count field is being incremented correctly for both prefetch and manual search paths.
  • Consider retiring or modifying the search_facts() method in store.py since its main functionality will be duplicated in the retrieval.search() method.

Example

The proposed fix provides a code snippet that can be used to increment the retrieval_count field:

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

Notes

The fix assumes that the retrieval.search() method is the correct place to increment the retrieval_count field. If there are other search paths that are not accounted for, additional modifications may be necessary.

Recommendation

Apply the proposed workaround by adding the UPDATE statement to the retrieval.search() method, as it directly addresses the issue and provides a clear solution.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING