Skip to content

[BUG] smem drift reports "no drift" and session summaries vanish — callers of methods removed in #141 swallow the AttributeError #151

Description

@RobertSigmundsson

Description

Found while migrating our production install onto 3.0.3 — so this comes with the caveat that
#141 was clearly the right call and we are not asking you to undo any of it. This is just the
tail it left behind.

Six storage methods that #141 removed with the SQLite backend still have live callers in
src/. Every one of those call sites is wrapped in try: ... except Exception: and annotated
# type: ignore[attr-defined], so on the only backend that ships today — SurrealDB — the
AttributeError is swallowed and the caller reports success.

Two user-visible consequences:

  1. smem drift / smem_drift(action="detect") always answers
    "No semantic drift detected. Tag usage is consistent."
    That answer is indistinguishable
    from a real, successful analysis. The feature cannot produce any other result on SurrealDB,
    because both of its inputs raise before they are read.
  2. Session summaries are never persisted. ReflexPipeline calls save_session_summary on
    every session.needs_persist(); it raises, is caught, and logged at debug.

The methods with no definition anywhere in src/:
record_tag_cooccurrence, get_tag_cooccurrence, get_tag_fiber_counts, save_drift_cluster,
get_drift_clusters, resolve_drift_cluster, save_session_summary.

To Reproduce

  1. Install on a SurrealDB backend (the default since v2.0.0) with some tagged memories.
  2. Run smem drift detect, or call the MCP tool smem_drift(action="detect").
  3. Observe status: "clean" with the message above — regardless of the tag data present.
  4. Confirm the cause without a database:
git grep -c "def get_tag_cooccurrence" v3.0.3 -- src/     # 0 definitions
git grep -n  "get_tag_cooccurrence"    v3.0.3 -- src/     # live callers
git grep -n  "save_session_summary"    v3.0.3 -- src/     # 1 caller, 0 definitions
storage = await get_shared_storage()          # SurrealDBStorage
hasattr(storage, "get_tag_cooccurrence")      # False

Expected Behavior

A capability that cannot run should say so. Either the drift family is restored behind the
NeuralStorage interface — exactly the pattern #139 used for pinning and #145 for watch
state — or the command and the MCP tool are removed. A third option, which is the current
state, is the worst of the three: an answer that looks like an analysis.

Actual Behavior

run_drift_detection degrades to empty inputs through three separate swallows, and the handler
turns the empty result into a success message.

# src/surreal_memory/engine/drift_detection.py:415-443
try:
    cooccurrences = await storage.get_tag_cooccurrence(  # type: ignore[attr-defined]
        min_count=MIN_COOCCURRENCE_COUNT,
    )
except Exception:
    cooccurrences = []                       # (1) AttributeError -> "no data"

try:
    tag_fiber_counts = await storage.get_tag_fiber_counts()  # type: ignore[attr-defined]
except Exception:
    tag_fiber_counts = {}                    # (2) AttributeError -> "no data"

reports = detect_clusters(cooccurrences, tag_fiber_counts)   # [] from empty inputs

for report in reports:                       # never entered
    try:
        await storage.save_drift_cluster(...)  # type: ignore[attr-defined]
    except Exception:
        pass                                 # (3)
# src/surreal_memory/mcp/drift_handler.py:63-69
if not clusters and not temporal:
    return {
        "status": "clean",
        "message": "No semantic drift detected. Tag usage is consistent.",
    }
# src/surreal_memory/engine/retrieval.py:831-845
if session.needs_persist():
    try:
        summary = session.to_summary_dict()
        await self._storage.save_session_summary(  # type: ignore[attr-defined]
            ...
        )
    except Exception:
        logger.debug(...)                    # summaries silently never written

Two of the nine call sites are honest about it — engine/uncertainty_report.py:112,228 probe
with getattr(storage, "get_drift_clusters", None), and the docstring on line 227 already says
"SQLite-only; [] on backends without get_drift_clusters". So part of the codebase knows the
methods are gone; the drift command does not.

Error Message

There is none, and that is the bug. Every failure is caught. With logging turned up, the only
trace is the logger.debug line in retrieval.py.

Environment

  • OS: Linux (x86-64)
  • Python version: 3.12.13
  • Surreal-Memory version: 3.0.3 (ac20df41, origin/main at the time of writing)
  • Installation method: source, pip install -e ".[dev,server,surrealdb]"
  • Backend: SurrealDB 3.2.0

Additional Context

The call sites are annotated # type: ignore[attr-defined], which is why mypy src/ --ignore-missing-imports stays green over methods that no longer exist — the annotation was
correct while the SQLite backend existed and became a silencer when it went away. If the
family is restored on the interface, those type: ignore comments should go with it; ruff's
RUF100 will then point at any that are left.

Related: #141 (backend removal), #139 (the pinning family moved onto the interface),
#145 (the watch family moved onto the interface).

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