generated from coulomb/repo-seed
session-memory: map signals to catalog recommendations via covers (WP-0010 follow-up)
Closes the gap where recurring_error suggestions showed generic 'Investigate' instead of the curated recommendation. Added a covers[] field to SolutionPattern (lowercase substrings a pattern's recommendation also applies to) + Catalog.find_for (exact key first, then covers match against signal key+locus). Retro now resolves recommendations through find_for. Tagged the read-before-edit pattern with covers=['file has not been read','modified since read','file_not_read'] (v1.0.1). Live: file-not-read suggestions across all repos now inherit 'Read the file before Edit/Write'. 6 new tests; suite 158/158. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
{"covers": [], "created_at": "2026-06-07T13:26:25Z", "distribution_ready": true, "id": "sp-problem-file_not_read-edit", "name": "Read before you Edit", "polarity": "problem", "problem": "Agents call Edit/Write on a file they have not read in the current session, or after it changed under them. The edit tools reject this ('File has not been read yet' / 'File has been modified since read'), and the retry burns a turn. Top recurring error in the corpus (12/27 sessions, 8 repos).", "provenance": {"detected_at": null, "evidence": {"frequency": 32, "origin": "AGENTIC-WP-0006 error mining / ASSESSMENT-infra-friction.md", "polarity": "problem", "repos": 8, "sessions": 12}, "promoted_at": null, "source_key": "problem:file_not_read:edit"}, "rendering_hints": {"claude": {"target": "CLAUDE.md"}, "codex": {"target": "AGENTS.md"}, "grok": {"target": ".grok/instructions.md"}}, "resolutions": [{"detail": "Never blind-write a file you haven't read this session.", "steps": ["Read the target file", "Then Edit/Write"], "summary": "Read the file (or the region you'll touch) before Edit/Write"}, {"detail": "A stale read means the file changed under you; refresh, don't loop.", "steps": ["Re-Read the file", "Re-apply the Edit"], "summary": "On 'modified since read', re-Read then re-Edit"}], "schema_version": 1, "scope": {"domains": [], "flavors": [], "repos": []}, "status": "superseded", "updated_at": "2026-06-07T13:26:25Z", "version": "1.0.0"}
|
||||||
@@ -1,4 +1,9 @@
|
|||||||
{
|
{
|
||||||
|
"covers": [
|
||||||
|
"file has not been read",
|
||||||
|
"modified since read",
|
||||||
|
"file_not_read"
|
||||||
|
],
|
||||||
"created_at": "2026-06-07T13:26:25Z",
|
"created_at": "2026-06-07T13:26:25Z",
|
||||||
"distribution_ready": true,
|
"distribution_ready": true,
|
||||||
"id": "sp-problem-file_not_read-edit",
|
"id": "sp-problem-file_not_read-edit",
|
||||||
@@ -53,6 +58,6 @@
|
|||||||
"repos": []
|
"repos": []
|
||||||
},
|
},
|
||||||
"status": "approved",
|
"status": "approved",
|
||||||
"updated_at": "2026-06-07T13:26:25Z",
|
"updated_at": "2026-06-07T19:06:45Z",
|
||||||
"version": "1.0.0"
|
"version": "1.0.1"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from .schema import SolutionPattern
|
|||||||
# Content fields that define a pattern's substance. Version, timestamps, status,
|
# Content fields that define a pattern's substance. Version, timestamps, status,
|
||||||
# and distribution_ready are metadata — changes to them never bump the version.
|
# and distribution_ready are metadata — changes to them never bump the version.
|
||||||
_CONTENT_KEYS = ("name", "polarity", "problem", "resolutions", "scope",
|
_CONTENT_KEYS = ("name", "polarity", "problem", "resolutions", "scope",
|
||||||
"provenance", "rendering_hints")
|
"provenance", "rendering_hints", "covers")
|
||||||
|
|
||||||
ADDED = "added"
|
ADDED = "added"
|
||||||
UNCHANGED = "unchanged"
|
UNCHANGED = "unchanged"
|
||||||
@@ -86,6 +86,22 @@ class Catalog:
|
|||||||
with open(path, encoding="utf-8") as fh:
|
with open(path, encoding="utf-8") as fh:
|
||||||
return [json.loads(line) for line in fh if line.strip()]
|
return [json.loads(line) for line in fh if line.strip()]
|
||||||
|
|
||||||
|
def find_for(self, signal_key: str, locus: str = "") -> Optional[SolutionPattern]:
|
||||||
|
"""Best catalog pattern for a detect signal: exact id first, then ``covers``.
|
||||||
|
|
||||||
|
Lets a signal that doesn't share a pattern's exact key (e.g. a
|
||||||
|
``recurring_error`` fingerprint) inherit the curated recommendation when a
|
||||||
|
pattern declares it covers that text.
|
||||||
|
"""
|
||||||
|
exact = self.load(SolutionPattern.make_id(signal_key))
|
||||||
|
if exact is not None:
|
||||||
|
return exact
|
||||||
|
hay = f"{signal_key} {locus}".lower()
|
||||||
|
for p in self.list(): # sorted by id -> deterministic
|
||||||
|
if any(c.lower() in hay for c in p.covers):
|
||||||
|
return p
|
||||||
|
return None
|
||||||
|
|
||||||
# --- the single write path ---------------------------------------------
|
# --- the single write path ---------------------------------------------
|
||||||
|
|
||||||
def upsert(self, pattern: SolutionPattern) -> str:
|
def upsert(self, pattern: SolutionPattern) -> str:
|
||||||
|
|||||||
@@ -81,6 +81,11 @@ class SolutionPattern:
|
|||||||
# per-flavor rendering hints, kept OUT of the agnostic core (OQ4):
|
# per-flavor rendering hints, kept OUT of the agnostic core (OQ4):
|
||||||
# {"claude": {...}, "codex": {...}, "grok": {...}}
|
# {"claude": {...}, "codex": {...}, "grok": {...}}
|
||||||
rendering_hints: dict[str, dict[str, Any]] = field(default_factory=dict)
|
rendering_hints: dict[str, dict[str, Any]] = field(default_factory=dict)
|
||||||
|
# other signal keys/loci this pattern's recommendation also applies to —
|
||||||
|
# lowercase substrings matched against a candidate signal's key+locus, so a
|
||||||
|
# detect signal that doesn't share this pattern's exact key (e.g. a
|
||||||
|
# recurring_error fingerprint) can still inherit the curated resolution.
|
||||||
|
covers: list[str] = field(default_factory=list)
|
||||||
status: str = "provisional"
|
status: str = "provisional"
|
||||||
distribution_ready: bool = False
|
distribution_ready: bool = False
|
||||||
created_at: Optional[str] = None
|
created_at: Optional[str] = None
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ from dataclasses import asdict, dataclass
|
|||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
from ..curate.schema import SolutionPattern
|
|
||||||
from ..detect.cluster import cluster
|
from ..detect.cluster import cluster
|
||||||
from ..detect.quality import QualityConfig, filter_real
|
from ..detect.quality import QualityConfig, filter_real
|
||||||
from ..detect.signals import extract_signals
|
from ..detect.signals import extract_signals
|
||||||
@@ -48,10 +47,10 @@ class Suggestion:
|
|||||||
pattern_key: str
|
pattern_key: str
|
||||||
|
|
||||||
|
|
||||||
def _recommendation(pattern_key: str, catalog) -> Optional[str]:
|
def _recommendation(pattern_key: str, locus: str, catalog) -> Optional[str]:
|
||||||
if catalog is None:
|
if catalog is None:
|
||||||
return None
|
return None
|
||||||
sp = catalog.load(SolutionPattern.make_id(pattern_key))
|
sp = catalog.find_for(pattern_key, locus)
|
||||||
if sp and sp.resolutions:
|
if sp and sp.resolutions:
|
||||||
return sp.resolutions[0].summary
|
return sp.resolutions[0].summary
|
||||||
return None
|
return None
|
||||||
@@ -75,7 +74,7 @@ def weekly_retro(digests: list[dict], catalog=None, *, since: Optional[str] = No
|
|||||||
for p in patterns:
|
for p in patterns:
|
||||||
if p.polarity != "problem":
|
if p.polarity != "problem":
|
||||||
continue # improvements come from problems
|
continue # improvements come from problems
|
||||||
rec = (_recommendation(p.key, catalog)
|
rec = (_recommendation(p.key, p.locus, catalog)
|
||||||
or f"Investigate {p.signal_type.replace('_', ' ')} on {p.locus}")
|
or f"Investigate {p.signal_type.replace('_', ' ')} on {p.locus}")
|
||||||
priority = "high" if (p.cross_flavor or p.score >= _HIGH_SCORE) else "medium"
|
priority = "high" if (p.cross_flavor or p.score >= _HIGH_SCORE) else "medium"
|
||||||
for repo in (p.repos or ["(unknown)"]):
|
for repo in (p.repos or ["(unknown)"]):
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"generated_at": "2026-06-07T17:14:00Z",
|
"generated_at": "2026-06-07T19:07:51Z",
|
||||||
"measure": {
|
"measure": {
|
||||||
"error_rate": 0.957,
|
"error_rate": 0.957,
|
||||||
"infra_overhead_share_median": 0.167,
|
"infra_overhead_share_median": 0.167,
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "activity-core",
|
"repo": "activity-core",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -147,7 +147,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "citation-evidence",
|
"repo": "citation-evidence",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -157,7 +157,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "infospace-bench",
|
"repo": "infospace-bench",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -167,7 +167,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "issue-facade",
|
"repo": "issue-facade",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -177,7 +177,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "railiance-apps",
|
"repo": "railiance-apps",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -187,7 +187,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "state-hub",
|
"repo": "state-hub",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -197,7 +197,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "the-custodian",
|
"repo": "the-custodian",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -207,7 +207,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||||
"priority": "high",
|
"priority": "high",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "vergabe-teilnahme",
|
"repo": "vergabe-teilnahme",
|
||||||
"score": 290.0,
|
"score": 290.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -217,7 +217,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "artifact-store",
|
"repo": "artifact-store",
|
||||||
"score": 78.0,
|
"score": 78.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -227,7 +227,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "issue-facade",
|
"repo": "issue-facade",
|
||||||
"score": 78.0,
|
"score": 78.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -237,7 +237,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "railiance-apps",
|
"repo": "railiance-apps",
|
||||||
"score": 78.0,
|
"score": 78.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -247,7 +247,7 @@
|
|||||||
"cross_flavor": false,
|
"cross_flavor": false,
|
||||||
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"pattern_key": "problem:recurring_error:<tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
||||||
"priority": "medium",
|
"priority": "medium",
|
||||||
"recommendation": "Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>",
|
"recommendation": "Read the file (or the region you'll touch) before Edit/Write",
|
||||||
"repo": "state-hub",
|
"repo": "state-hub",
|
||||||
"score": 78.0,
|
"score": 78.0,
|
||||||
"signal_type": "recurring_error",
|
"signal_type": "recurring_error",
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
],
|
],
|
||||||
"window": {
|
"window": {
|
||||||
"days": 30,
|
"days": 30,
|
||||||
"since": "2026-05-08T17:14:00Z",
|
"since": "2026-05-08T19:07:51Z",
|
||||||
"until": "2026-06-07T17:14:00Z"
|
"until": "2026-06-07T19:07:51Z"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
# Weekly Coding Retro (2026-05-08 → 2026-06-07)
|
# Weekly Coding Retro (2026-05-08 → 2026-06-07)
|
||||||
_23 real sessions · generated 2026-06-07T17:14:00Z_
|
_23 real sessions · generated 2026-06-07T19:07:51Z_
|
||||||
|
|
||||||
## Top improvement suggestions (cross-flavor first, ≤3 per repo)
|
## Top improvement suggestions (cross-flavor first, ≤3 per repo)
|
||||||
- **net-kingdom** (high, score=54.0) [CROSS-FLAVOR]: cross-flavor problem: recurring error — Investigate recurring error on make: *** [makefile:<n>: fix-consistency] error <n>
|
- **net-kingdom** (high, score=54.0) [CROSS-FLAVOR]: cross-flavor problem: recurring error — Investigate recurring error on make: *** [makefile:<n>: fix-consistency] error <n>
|
||||||
@@ -14,18 +14,18 @@ _23 real sessions · generated 2026-06-07T17:14:00Z_
|
|||||||
- **flex-auth** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
- **flex-auth** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
||||||
- **infospace-bench** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
- **infospace-bench** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
||||||
- **ops-bridge** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
- **ops-bridge** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
||||||
- **activity-core** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **activity-core** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **citation-evidence** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **citation-evidence** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **infospace-bench** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **infospace-bench** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **issue-facade** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **issue-facade** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **railiance-apps** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **railiance-apps** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **state-hub** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **state-hub** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **the-custodian** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **the-custodian** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **vergabe-teilnahme** (high, score=290.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>
|
- **vergabe-teilnahme** (high, score=290.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **artifact-store** (medium, score=78.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>
|
- **artifact-store** (medium, score=78.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **issue-facade** (medium, score=78.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>
|
- **issue-facade** (medium, score=78.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **railiance-apps** (medium, score=78.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>
|
- **railiance-apps** (medium, score=78.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **state-hub** (medium, score=78.0): problem: recurring error — Investigate recurring error on <tool_use_error>file has been modified since read, either by the user or by a linter. read it again before attempting to write it.<<path>>
|
- **state-hub** (medium, score=78.0): problem: recurring error — Read the file (or the region you'll touch) before Edit/Write
|
||||||
- **artifact-store** (medium, score=50.55): problem: budget overrun — Read narrowly — target the region you need, not whole large files
|
- **artifact-store** (medium, score=50.55): problem: budget overrun — Read narrowly — target the region you need, not whole large files
|
||||||
- **vergabe-teilnahme** (medium, score=12.0): problem: recurring error — Investigate recurring error on {
|
- **vergabe-teilnahme** (medium, score=12.0): problem: recurring error — Investigate recurring error on {
|
||||||
- **ops-bridge** (medium, score=10.0): problem: recurring error — Investigate recurring error on found <n> errors (<n> fixed, <n> remaining).
|
- **ops-bridge** (medium, score=10.0): problem: recurring error — Investigate recurring error on found <n> errors (<n> fixed, <n> remaining).
|
||||||
|
|||||||
62
tests/test_catalog_covers.py
Normal file
62
tests/test_catalog_covers.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
"""find_for / covers tests (AGENTIC-WP-0010 follow-up)."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
|
||||||
|
from session_memory.curate.catalog import Catalog # noqa: E402
|
||||||
|
from session_memory.curate.schema import ( # noqa: E402
|
||||||
|
Provenance,
|
||||||
|
Resolution,
|
||||||
|
SolutionPattern,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _pattern(pid, src, covers=None, name="P"):
|
||||||
|
return SolutionPattern(
|
||||||
|
id=pid, name=name, version="1.0.0", polarity="problem", problem="p",
|
||||||
|
resolutions=[Resolution(summary="do x")],
|
||||||
|
provenance=Provenance(source_key=src), covers=covers or [])
|
||||||
|
|
||||||
|
|
||||||
|
def test_covers_round_trips(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path))
|
||||||
|
cat.upsert(_pattern("sp-a", "problem:file_not_read:edit",
|
||||||
|
covers=["file has not been read"]))
|
||||||
|
assert cat.load("sp-a").covers == ["file has not been read"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_find_for_exact_key(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path))
|
||||||
|
cat.upsert(_pattern(SolutionPattern.make_id("problem:retry_storm:retries"),
|
||||||
|
"problem:retry_storm:retries"))
|
||||||
|
got = cat.find_for("problem:retry_storm:retries")
|
||||||
|
assert got is not None and got.id == "sp-problem-retry_storm-retries"
|
||||||
|
|
||||||
|
|
||||||
|
def test_find_for_covers_match(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path))
|
||||||
|
cat.upsert(_pattern("sp-rbe", "problem:file_not_read:edit",
|
||||||
|
covers=["file has not been read", "modified since read"]))
|
||||||
|
# a recurring_error signal with a different key but matching fingerprint locus
|
||||||
|
got = cat.find_for(
|
||||||
|
"problem:recurring_error:<tool_use_error>file has not been read yet...",
|
||||||
|
locus="<tool_use_error>file has not been read yet. read it first...")
|
||||||
|
assert got is not None and got.id == "sp-rbe"
|
||||||
|
|
||||||
|
|
||||||
|
def test_find_for_no_match_returns_none(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path))
|
||||||
|
cat.upsert(_pattern("sp-rbe", "problem:file_not_read:edit",
|
||||||
|
covers=["file has not been read"]))
|
||||||
|
assert cat.find_for("problem:recurring_error:some unrelated error") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_covers_change_versions(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path))
|
||||||
|
cat.upsert(_pattern("sp-a", "problem:x:y"))
|
||||||
|
p = cat.load("sp-a")
|
||||||
|
p.covers = ["new coverage"]
|
||||||
|
assert cat.upsert(p) == "versioned" # covers is substantive content
|
||||||
|
assert cat.load("sp-a").version == "1.0.1"
|
||||||
@@ -55,6 +55,26 @@ def test_recommendation_from_catalog(tmp_path):
|
|||||||
assert r["suggestions"][0]["recommendation"] == "Stop and diagnose before retrying"
|
assert r["suggestions"][0]["recommendation"] == "Stop and diagnose before retrying"
|
||||||
|
|
||||||
|
|
||||||
|
def test_recurring_error_inherits_recommendation_via_covers(tmp_path):
|
||||||
|
cat = Catalog(str(tmp_path / "catalog"))
|
||||||
|
cat.upsert(SolutionPattern(
|
||||||
|
id="sp-rbe", name="Read before edit", version="1.0.0", polarity="problem",
|
||||||
|
problem="edit before read",
|
||||||
|
resolutions=[Resolution(summary="Read the file first before Edit/Write")],
|
||||||
|
covers=["file has not been read"]))
|
||||||
|
digs = []
|
||||||
|
for i in range(2):
|
||||||
|
d = _digest(f"claude:{i}", "r1", "2026-06-0{}T10:00:00Z".format(i + 1))
|
||||||
|
d["error_snippets"] = [{
|
||||||
|
"fingerprint": "<tool_use_error>file has not been read yet. read it first...",
|
||||||
|
"sample": "File has not been read yet", "count": 2, "tool": "Edit"}]
|
||||||
|
digs.append(d)
|
||||||
|
r = weekly_retro(digs, catalog=cat, since="2026-05-30T00:00:00Z", until="2026-06-08T00:00:00Z")
|
||||||
|
rec_err = [s for s in r["suggestions"] if s["signal_type"] == "recurring_error"]
|
||||||
|
assert rec_err, "expected a recurring_error suggestion"
|
||||||
|
assert rec_err[0]["recommendation"] == "Read the file first before Edit/Write"
|
||||||
|
|
||||||
|
|
||||||
def test_caps_three_per_repo():
|
def test_caps_three_per_repo():
|
||||||
# five distinct problem signals in one repo -> capped at 3
|
# five distinct problem signals in one repo -> capped at 3
|
||||||
digs = []
|
digs = []
|
||||||
|
|||||||
Reference in New Issue
Block a user