Files
agentic-resources/tests/test_detect_recurring_error.py
tegwick e022c0f9d6 session-memory: recurring-error signal + clustering (WP-0006 T02)
detect/signals.py sig_recurring_error emits one signal per distinct error
fingerprint per session (magnitude = in-session occurrences), so the same error
recurring across sessions/repos/flavors clusters into a candidate root-cause
problem pattern via the existing clusterer — cross-flavor flagged automatically.
3 new tests; suite 98/98 green.

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

60 lines
2.1 KiB
Python

"""Recurring-error signal + clustering (WP-0006 T02)."""
import os
import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from session_memory.detect.cluster import cluster # noqa: E402
from session_memory.detect.signals import ( # noqa: E402
extract_signals,
sig_recurring_error,
)
def _digest(uid, repo, flavor="claude", snippets=None):
return {
"session_uid": uid, "flavor": flavor, "repo": repo, "outcome": "success",
"cost": {"input_tokens": 1, "output_tokens": 1},
"markers": {"errors": 0, "retries": 0, "test_runs": 0},
"tool_histogram": {}, "error_snippets": snippets or [],
}
_FP = "modulenotfounderror: no module named 'foo' at <path>:<n>"
def test_signal_per_distinct_fingerprint():
d = _digest("claude:a", "r1", snippets=[
{"fingerprint": _FP, "sample": "ModuleNotFoundError ...", "count": 3, "tool": "Bash"},
{"fingerprint": "keyerror: <str>", "sample": "KeyError", "count": 1, "tool": None},
])
sigs = sig_recurring_error(d, {})
assert len(sigs) == 2
top = [s for s in sigs if s.locus == _FP][0]
assert top.type == "recurring_error"
assert top.magnitude == 3.0
assert top.detail["sample"].startswith("ModuleNotFound")
def test_clusters_across_sessions_and_flavors():
# same fingerprint in a claude and a grok session -> cross-flavor candidate
digs = [
_digest("claude:a", "r1", "claude",
[{"fingerprint": _FP, "sample": "ModuleNotFoundError", "count": 2, "tool": "Bash"}]),
_digest("grok:b", "r2", "grok",
[{"fingerprint": _FP, "sample": "ModuleNotFoundError", "count": 1, "tool": None}]),
]
signals = extract_signals(digs)
pats = cluster([s for s in signals if s.type == "recurring_error"], min_frequency=2)
assert len(pats) == 1
p = pats[0]
assert p.signal_type == "recurring_error"
assert p.cross_flavor is True
assert sorted(p.flavors) == ["claude", "grok"]
assert p.frequency == 2
def test_no_snippets_no_signal():
assert sig_recurring_error(_digest("claude:a", "r1"), {}) == []