Files
agentic-resources/tests/test_digest_errors.py
tegwick 1b6081cd88 session-memory: denoise error fingerprints (WP-0006 follow-up)
Tighten _is_failed: exclude successful hub JSON responses (top-level no-error
payloads) and file-read snapshots (numbered cat -n source lines) that were
polluting error_snippets. JSON verdict classifies error vs success payloads
directly. Cuts distinct fingerprints 444 -> 269 (~40%) over the real corpus with
the top errors unchanged. Assessment caveat updated. 5 new tests; suite 102/102.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 13:39:08 +02:00

102 lines
3.9 KiB
Python

"""Error-body mining into the digest (WP-0006 T01)."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from session_memory.core.digest import ( # noqa: E402
_error_fingerprint,
_error_snippets,
build_digest,
)
from session_memory.core.schema import SCHEMA_VERSION, Session, SessionEvent # noqa: E402
def _ev(seq, kind, **kw):
return SessionEvent(session_uid="claude:s", seq=seq, kind=kind, **kw)
def test_fingerprint_normalizes_paths_numbers_ids():
a = _error_fingerprint("ModuleNotFoundError: No module named 'foo' at /home/x/a.py:42")
b = _error_fingerprint("ModuleNotFoundError: No module named 'foo' at /srv/y/b.py:9991")
assert a == b # paths + line numbers stripped -> same fingerprint
assert "<path>" in a and "<n>" in a
def test_fingerprint_uuid_and_addr():
fp = _error_fingerprint("connection 0xDEADBEEF to 1972d1d9-fc35-4912-8126-1fe64cc51425 failed")
assert "<addr>" in fp and "<uuid>" in fp
def test_snippets_dedup_and_count():
blobs = {"b1": "Traceback...\nValueError: bad thing at /p/x.py:10",
"b2": "Traceback...\nValueError: bad thing at /q/y.py:99",
"b3": "KeyError: 'id'"}
events = [
_ev(0, "error", payload_ref="b1"),
_ev(1, "error", payload_ref="b2"), # same fingerprint as b1
_ev(2, "error", payload_ref="b3"),
]
snips = _error_snippets(events, blobs)
assert len(snips) == 2
top = snips[0]
assert top["count"] == 2 # the ValueError collapsed
assert "ValueError" in top["sample"]
def test_failed_tool_result_mined():
blobs = {"b1": "npm ERR! something failed with non-zero exit"}
events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")]
snips = _error_snippets(events, blobs)
assert len(snips) == 1
assert snips[0]["tool"] == "Bash"
def test_clean_tool_result_not_mined():
blobs = {"b1": "6 passed in 0.4s"}
events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")]
assert _error_snippets(events, blobs) == []
def test_success_json_not_mined():
# a hub MCP success payload mentioning 'error' deep inside is NOT a failure
blobs = {"b1": '{"result": "{\\"domain\\": \\"custodian\\", \\"note\\": \\"no errors\\"}"}'}
events = [_ev(0, "tool_result", tool="mcp__state-hub__get_domain_summary", payload_ref="b1")]
assert _error_snippets(events, blobs) == []
def test_error_json_still_mined():
blobs = {"b1": '{"detail": "Invalid request parameters"}'}
events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")]
snips = _error_snippets(events, blobs)
assert len(snips) == 1
def test_plain_mcp_error_still_mined():
blobs = {"b1": "MCP error -32602: Invalid request parameters"}
events = [_ev(0, "tool_result", tool="Bash", payload_ref="b1")]
assert len(_error_snippets(events, blobs)) == 1
def test_file_read_snapshot_not_mined():
# a Read result of source code containing 'raise ...Error' is not a runtime error
blobs = {"b1": "227\t def f():\n228\t x = 1\n229\t raise InfospaceError()\n"}
events = [_ev(0, "tool_result", tool="Read", payload_ref="b1")]
assert _error_snippets(events, blobs) == []
def test_build_digest_includes_error_snippets_and_v2():
s = Session(session_uid="claude:s", flavor="claude", native_session_id="s", repo="r")
events = [_ev(0, "user_msg"), _ev(1, "error", payload_ref="b1"), _ev(2, "assistant_msg")]
d = build_digest(s, events, {"b1": "RuntimeError: kaboom at /a/b.py:3"})
assert d["schema_version"] == SCHEMA_VERSION == 2
assert d["error_snippets"][0]["count"] == 1
assert "RuntimeError" in d["error_snippets"][0]["sample"]
def test_no_errors_empty_list():
s = Session(session_uid="claude:s", flavor="claude", native_session_id="s", repo="r")
d = build_digest(s, [_ev(0, "user_msg"), _ev(1, "assistant_msg")])
assert d["error_snippets"] == []