generated from coulomb/repo-seed
session-memory: weekly retro entrypoint + hub publish (AGENTIC-WP-0010)
The analysis half of the weekly coding retrospection. retro/build.py: windowed detect+measure -> top-3 improvement suggestions per repo (cross-flavor first, recommendations pulled from the Pattern Catalog) + fleet snapshot. retro/publish.py: publishes the report to the hub as the coding_retro read model (event_type= coding_retro progress event) + local JSON/md, graceful degrade. retro entrypoint with --window-days/--publish/--json. Live verify over real sessions surfaced per-repo suggestions with catalog recommendations. 13 new tests; suite 152/152. Consumed by activity-core ACTIVITY-WP-0008 (Weekly Coding Retrospection, Sat 19:00). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -42,6 +42,9 @@ session_memory/
|
||||
measure/metrics.py # fleet metrics + persisted baseline snapshots
|
||||
measure/effect.py # before/after per-pattern effectiveness
|
||||
measure/__main__.py # python -m session_memory.measure
|
||||
retro/build.py # windowed top-3-per-repo suggestions
|
||||
retro/publish.py # hub coding_retro read model + local report
|
||||
retro/__main__.py # python -m session_memory.retro
|
||||
config.toml # store paths, retention caps, sources, repo->domain map, curate gate
|
||||
```
|
||||
|
||||
@@ -163,6 +166,24 @@ python -m session_memory.measure --no-save --json
|
||||
retired. Recorded pre-fix baseline (2026-06-07): 27 sessions, infra-overhead
|
||||
median 11.7 %, error rate 0.96, schema-thrash 8 sessions.
|
||||
|
||||
## Weekly retro (the input to the scheduled retrospection)
|
||||
|
||||
A windowed roll-up: detect + measure over the last N days → the **top-3
|
||||
improvement suggestions per repo** (cross-flavor first; recommendations pulled
|
||||
from the Pattern Catalog) → published to the hub as the `coding_retro` read model.
|
||||
|
||||
```bash
|
||||
python -m session_memory.retro # last 7 days, local report
|
||||
python -m session_memory.retro --window-days 30 --json
|
||||
python -m session_memory.retro --publish # also post coding_retro to the hub
|
||||
```
|
||||
|
||||
Writes `retro/last_retro.{json,md}` and (with `--publish`) posts an
|
||||
`event_type=coding_retro` progress event. This is consumed by activity-core's
|
||||
**Weekly Coding Retrospection** schedule (ACTIVITY-WP-0008, Saturday 19:00 Berlin),
|
||||
which emits one improvement task per relevant repo. Hub publish degrades
|
||||
gracefully when the hub is unreachable.
|
||||
|
||||
## Retention knobs (`[retention]` in config.toml)
|
||||
|
||||
| Key | Meaning |
|
||||
|
||||
@@ -43,6 +43,14 @@ min_prompt_len = 25 # first prompt shorter than this is treated as trivial
|
||||
[measure]
|
||||
baselines = "session_memory/measure/baselines.jsonl" # timestamped metric snapshots (committed)
|
||||
|
||||
# Weekly retro (AGENTIC-WP-0010): windowed top-3-per-repo report, published to the
|
||||
# hub as the coding_retro read model that activity-core's weekly schedule consumes.
|
||||
[retro]
|
||||
window_days = 7
|
||||
report_json = "session_memory/retro/last_retro.json" # latest report (committed)
|
||||
report_md = "session_memory/retro/last_retro.md" # human-readable mirror
|
||||
hub_url = "http://127.0.0.1:8000" # for --publish (best-effort)
|
||||
|
||||
# Distribute phase (AGENTIC-WP-0007): where per-flavor proposals + the active
|
||||
# registry are written. Proposals are HITL — reviewed, never auto-applied.
|
||||
[distribute]
|
||||
|
||||
9
session_memory/retro/__init__.py
Normal file
9
session_memory/retro/__init__.py
Normal file
@@ -0,0 +1,9 @@
|
||||
"""Weekly retro (AGENTIC-WP-0010) — the analysis half of the coding retrospection.
|
||||
|
||||
build.py windowed detect + measure -> ranked top-3 suggestions per repo (T01)
|
||||
publish.py publish the retro to the hub read model + local report (T02)
|
||||
__main__.py python -m session_memory.retro (T03)
|
||||
|
||||
Consumed by activity-core's weekly-coding-retro schedule (ACTIVITY-WP-0008) via
|
||||
the ``event_type=coding_retro`` read model.
|
||||
"""
|
||||
68
session_memory/retro/__main__.py
Normal file
68
session_memory/retro/__main__.py
Normal file
@@ -0,0 +1,68 @@
|
||||
"""Weekly retro entrypoint (AGENTIC-WP-0010 T03).
|
||||
|
||||
python -m session_memory.retro [--window-days 7] [--since D] [--until D]
|
||||
[--publish] [--json]
|
||||
|
||||
Builds the windowed top-3-per-repo retro over the captured sessions, writes a local
|
||||
JSON + markdown report, and (with ``--publish``) posts it to the hub as the
|
||||
``coding_retro`` read model that activity-core's weekly schedule consumes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
|
||||
from ..core.store import Store
|
||||
from ..curate.catalog import Catalog
|
||||
from ..ingest import _expand, load_config
|
||||
from .build import weekly_retro
|
||||
from .publish import publish_to_hub, render_markdown, write_local
|
||||
|
||||
|
||||
def run_retro(config: dict, *, window_days=None, since=None, until=None):
|
||||
s = config.get("store", {})
|
||||
store = Store(_expand(s["db_path"]), _expand(s["blob_dir"]))
|
||||
digests = store.list_digests()
|
||||
store.close()
|
||||
cur = config.get("curate", {})
|
||||
catalog = Catalog(_expand(cur.get("catalog_dir", "session_memory/catalog")))
|
||||
rcfg = config.get("retro", {})
|
||||
return weekly_retro(digests, catalog, since=since, until=until,
|
||||
window_days=window_days or rcfg.get("window_days", 7))
|
||||
|
||||
|
||||
def main(argv=None) -> int:
|
||||
here = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
ap = argparse.ArgumentParser(description="Build (and optionally publish) the weekly coding retro.")
|
||||
ap.add_argument("--config", default=os.path.join(here, "config.toml"))
|
||||
ap.add_argument("--window-days", type=int, default=None)
|
||||
ap.add_argument("--since", default=None)
|
||||
ap.add_argument("--until", default=None)
|
||||
ap.add_argument("--publish", action="store_true", help="post to the hub coding_retro read model")
|
||||
ap.add_argument("--json", action="store_true")
|
||||
args = ap.parse_args(argv)
|
||||
|
||||
config = load_config(args.config)
|
||||
report = run_retro(config, window_days=args.window_days, since=args.since, until=args.until)
|
||||
|
||||
rcfg = config.get("retro", {})
|
||||
write_local(report, _expand(rcfg.get("report_json", "session_memory/retro/last_retro.json")),
|
||||
_expand(rcfg.get("report_md", "session_memory/retro/last_retro.md")))
|
||||
|
||||
published = None
|
||||
if args.publish:
|
||||
published = publish_to_hub(report, base_url=rcfg.get("hub_url", "http://127.0.0.1:8000"))
|
||||
|
||||
if args.json:
|
||||
print(json.dumps({"report": report, "published": published}, indent=2))
|
||||
else:
|
||||
print(render_markdown(report))
|
||||
if args.publish:
|
||||
print(f"\npublished to hub: {published}")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
100
session_memory/retro/build.py
Normal file
100
session_memory/retro/build.py
Normal file
@@ -0,0 +1,100 @@
|
||||
"""Windowed weekly retro report (AGENTIC-WP-0010 T01).
|
||||
|
||||
Runs the existing detect pipeline over a date window, ranks the recurring problem
|
||||
patterns into **per-repo improvement suggestions** (top 3, cross-flavor first),
|
||||
attaches a recommendation from the Pattern Catalog where one exists, and bundles a
|
||||
fleet measure snapshot for context. Pure function over digests — the entrypoint
|
||||
(T03) handles store/publish.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import collections
|
||||
from dataclasses import asdict, dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Optional
|
||||
|
||||
from ..curate.schema import SolutionPattern
|
||||
from ..detect.cluster import cluster
|
||||
from ..detect.quality import QualityConfig, filter_real
|
||||
from ..detect.signals import extract_signals
|
||||
from ..measure.metrics import aggregate
|
||||
|
||||
# score at/above which a suggestion is "high" priority even when single-flavor
|
||||
_HIGH_SCORE = 100.0
|
||||
|
||||
|
||||
def _parse(ts: str) -> datetime:
|
||||
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
|
||||
|
||||
|
||||
def _iso(dt: datetime) -> str:
|
||||
return dt.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
||||
|
||||
|
||||
def _now() -> datetime:
|
||||
return datetime.now(timezone.utc)
|
||||
|
||||
|
||||
@dataclass
|
||||
class Suggestion:
|
||||
repo: str
|
||||
title: str
|
||||
recommendation: str
|
||||
priority: str # high | medium
|
||||
score: float
|
||||
signal_type: str
|
||||
cross_flavor: bool
|
||||
pattern_key: str
|
||||
|
||||
|
||||
def _recommendation(pattern_key: str, catalog) -> Optional[str]:
|
||||
if catalog is None:
|
||||
return None
|
||||
sp = catalog.load(SolutionPattern.make_id(pattern_key))
|
||||
if sp and sp.resolutions:
|
||||
return sp.resolutions[0].summary
|
||||
return None
|
||||
|
||||
|
||||
def weekly_retro(digests: list[dict], catalog=None, *, since: Optional[str] = None,
|
||||
until: Optional[str] = None, window_days: int = 7,
|
||||
max_per_repo: int = 3, min_frequency: int = 2,
|
||||
quality: Optional[QualityConfig] = None) -> dict:
|
||||
"""Build the ranked weekly retro report over a date window."""
|
||||
until_dt = _parse(until) if until else _now()
|
||||
since_dt = _parse(since) if since else until_dt - timedelta(days=window_days)
|
||||
|
||||
windowed = [d for d in digests
|
||||
if d.get("started_at") and since_dt <= _parse(d["started_at"]) < until_dt]
|
||||
real = filter_real(windowed, quality or QualityConfig())
|
||||
|
||||
patterns = cluster(extract_signals(real), min_frequency=min_frequency)
|
||||
|
||||
by_repo: dict[str, list[Suggestion]] = collections.defaultdict(list)
|
||||
for p in patterns:
|
||||
if p.polarity != "problem":
|
||||
continue # improvements come from problems
|
||||
rec = (_recommendation(p.key, catalog)
|
||||
or f"Investigate {p.signal_type.replace('_', ' ')} on {p.locus}")
|
||||
priority = "high" if (p.cross_flavor or p.score >= _HIGH_SCORE) else "medium"
|
||||
for repo in (p.repos or ["(unknown)"]):
|
||||
by_repo[repo].append(Suggestion(
|
||||
repo=repo, title=p.title, recommendation=rec, priority=priority,
|
||||
score=p.score, signal_type=p.signal_type, cross_flavor=p.cross_flavor,
|
||||
pattern_key=p.key))
|
||||
|
||||
suggestions: list[Suggestion] = []
|
||||
for repo in sorted(by_repo):
|
||||
items = sorted(by_repo[repo], key=lambda s: -s.score)
|
||||
suggestions.extend(items[:max_per_repo])
|
||||
# cross-flavor first, then by score (global ordering for the report)
|
||||
suggestions.sort(key=lambda s: (not s.cross_flavor, -s.score))
|
||||
|
||||
return {
|
||||
"window": {"since": _iso(since_dt), "until": _iso(until_dt), "days": window_days},
|
||||
"generated_at": _iso(_now()),
|
||||
"n_sessions": len(real),
|
||||
"suggestions": [asdict(s) for s in suggestions],
|
||||
"measure": aggregate(real),
|
||||
}
|
||||
322
session_memory/retro/last_retro.json
Normal file
322
session_memory/retro/last_retro.json
Normal file
@@ -0,0 +1,322 @@
|
||||
{
|
||||
"generated_at": "2026-06-07T17:14:00Z",
|
||||
"measure": {
|
||||
"error_rate": 0.957,
|
||||
"infra_overhead_share_median": 0.167,
|
||||
"infra_overhead_share_p90": 0.23,
|
||||
"n_sessions": 23,
|
||||
"recurring_error_occurrences": 463,
|
||||
"schema_thrash_sessions": 7,
|
||||
"success_rate": 1.0,
|
||||
"tokens_p50": 250725,
|
||||
"tokens_p90": 901422
|
||||
},
|
||||
"n_sessions": 23,
|
||||
"suggestions": [
|
||||
{
|
||||
"cross_flavor": true,
|
||||
"pattern_key": "problem:recurring_error:make: *** [makefile:<n>: fix-consistency] error <n>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on make: *** [makefile:<n>: fix-consistency] error <n>",
|
||||
"repo": "net-kingdom",
|
||||
"score": 54.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "cross-flavor problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "activity-core",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "artifact-store",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "citation-evidence",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "infospace-bench",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "railiance-apps",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:tool_thrash:tool:Bash",
|
||||
"priority": "high",
|
||||
"recommendation": "Batch related shell work into one script, not many small Bash calls",
|
||||
"repo": "state-hub",
|
||||
"score": 13128.0,
|
||||
"signal_type": "tool_thrash",
|
||||
"title": "problem: tool thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:schema_thrash:schema_load",
|
||||
"priority": "high",
|
||||
"recommendation": "Load the tool schemas you'll need once, up front",
|
||||
"repo": "activity-core",
|
||||
"score": 441.0,
|
||||
"signal_type": "schema_thrash",
|
||||
"title": "problem: schema thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:schema_thrash:schema_load",
|
||||
"priority": "high",
|
||||
"recommendation": "Load the tool schemas you'll need once, up front",
|
||||
"repo": "citation-evidence",
|
||||
"score": 441.0,
|
||||
"signal_type": "schema_thrash",
|
||||
"title": "problem: schema thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:schema_thrash:schema_load",
|
||||
"priority": "high",
|
||||
"recommendation": "Load the tool schemas you'll need once, up front",
|
||||
"repo": "flex-auth",
|
||||
"score": 441.0,
|
||||
"signal_type": "schema_thrash",
|
||||
"title": "problem: schema thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:schema_thrash:schema_load",
|
||||
"priority": "high",
|
||||
"recommendation": "Load the tool schemas you'll need once, up front",
|
||||
"repo": "infospace-bench",
|
||||
"score": 441.0,
|
||||
"signal_type": "schema_thrash",
|
||||
"title": "problem: schema thrash"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:schema_thrash:schema_load",
|
||||
"priority": "high",
|
||||
"recommendation": "Load the tool schemas you'll need once, up front",
|
||||
"repo": "ops-bridge",
|
||||
"score": 441.0,
|
||||
"signal_type": "schema_thrash",
|
||||
"title": "problem: schema thrash"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "activity-core",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "citation-evidence",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "infospace-bench",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "issue-facade",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "railiance-apps",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "state-hub",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "the-custodian",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"priority": "high",
|
||||
"recommendation": "Investigate recurring error on <tool_use_error>file has not been read yet. read it first before writing to it.<<path>>",
|
||||
"repo": "vergabe-teilnahme",
|
||||
"score": 290.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"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>>",
|
||||
"repo": "artifact-store",
|
||||
"score": 78.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"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>>",
|
||||
"repo": "issue-facade",
|
||||
"score": 78.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"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>>",
|
||||
"repo": "railiance-apps",
|
||||
"score": 78.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"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>>",
|
||||
"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>>",
|
||||
"repo": "state-hub",
|
||||
"score": 78.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:budget_overrun:tokens",
|
||||
"priority": "medium",
|
||||
"recommendation": "Read narrowly \u2014 target the region you need, not whole large files",
|
||||
"repo": "artifact-store",
|
||||
"score": 50.55,
|
||||
"signal_type": "budget_overrun",
|
||||
"title": "problem: budget overrun"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:recurring_error:{",
|
||||
"priority": "medium",
|
||||
"recommendation": "Investigate recurring error on {",
|
||||
"repo": "vergabe-teilnahme",
|
||||
"score": 12.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:recurring_error:found <n> errors (<n> fixed, <n> remaining).",
|
||||
"priority": "medium",
|
||||
"recommendation": "Investigate recurring error on found <n> errors (<n> fixed, <n> remaining).",
|
||||
"repo": "ops-bridge",
|
||||
"score": 10.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:recurring_error:(note: edit also tried swapping \\uxxxx escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. re-read the file a",
|
||||
"priority": "medium",
|
||||
"recommendation": "Investigate recurring error on (note: edit also tried swapping \\uxxxx escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. re-read the file a",
|
||||
"repo": "net-kingdom",
|
||||
"score": 6.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:recurring_error:found <n> error (<n> fixed, <n> remaining).",
|
||||
"priority": "medium",
|
||||
"recommendation": "Investigate recurring error on found <n> error (<n> fixed, <n> remaining).",
|
||||
"repo": "ops-bridge",
|
||||
"score": 6.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
},
|
||||
{
|
||||
"cross_flavor": false,
|
||||
"pattern_key": "problem:recurring_error:<n> failed, <n> passed in <n>.00s",
|
||||
"priority": "medium",
|
||||
"recommendation": "Investigate recurring error on <n> failed, <n> passed in <n>.00s",
|
||||
"repo": "agentic-resources",
|
||||
"score": 4.0,
|
||||
"signal_type": "recurring_error",
|
||||
"title": "problem: recurring error"
|
||||
}
|
||||
],
|
||||
"window": {
|
||||
"days": 30,
|
||||
"since": "2026-05-08T17:14:00Z",
|
||||
"until": "2026-06-07T17:14:00Z"
|
||||
}
|
||||
}
|
||||
39
session_memory/retro/last_retro.md
Normal file
39
session_memory/retro/last_retro.md
Normal file
@@ -0,0 +1,39 @@
|
||||
# Weekly Coding Retro (2026-05-08 → 2026-06-07)
|
||||
_23 real sessions · generated 2026-06-07T17:14:00Z_
|
||||
|
||||
## 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>
|
||||
- **activity-core** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **artifact-store** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **citation-evidence** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **infospace-bench** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **railiance-apps** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **state-hub** (high, score=13128.0): problem: tool thrash — Batch related shell work into one script, not many small Bash calls
|
||||
- **activity-core** (high, score=441.0): problem: schema thrash — Load the tool schemas you'll need once, up front
|
||||
- **citation-evidence** (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
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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>>
|
||||
- **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 {
|
||||
- **ops-bridge** (medium, score=10.0): problem: recurring error — Investigate recurring error on found <n> errors (<n> fixed, <n> remaining).
|
||||
- **net-kingdom** (medium, score=6.0): problem: recurring error — Investigate recurring error on (note: edit also tried swapping \uxxxx escapes and their characters; neither form matched, so the mismatch is likely elsewhere in old_string. re-read the file a
|
||||
- **ops-bridge** (medium, score=6.0): problem: recurring error — Investigate recurring error on found <n> error (<n> fixed, <n> remaining).
|
||||
- **agentic-resources** (medium, score=4.0): problem: recurring error — Investigate recurring error on <n> failed, <n> passed in <n>.00s
|
||||
|
||||
## Fleet snapshot
|
||||
- infra-overhead median: 0.167
|
||||
- error rate: 0.957 · schema-thrash: 7
|
||||
- success rate: 1.0 · tokens p50: 250725
|
||||
78
session_memory/retro/publish.py
Normal file
78
session_memory/retro/publish.py
Normal file
@@ -0,0 +1,78 @@
|
||||
"""Publish the weekly retro (AGENTIC-WP-0010 T02).
|
||||
|
||||
The retro is published to the State Hub as a **read model** — a progress event of
|
||||
``event_type=coding_retro`` whose ``detail`` carries the structured report. This is
|
||||
exactly how ``daily-triage-report`` surfaces, and it is what activity-core's
|
||||
``coding_retro`` resolver (ACTIVITY-WP-0008) reads. A local JSON + markdown report
|
||||
is always written; the hub publish is best-effort and **degrades gracefully** when
|
||||
the hub is unreachable.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
from typing import Callable, Optional
|
||||
|
||||
DEFAULT_HUB = "http://127.0.0.1:8000"
|
||||
|
||||
|
||||
def render_markdown(report: dict) -> str:
|
||||
w = report.get("window", {})
|
||||
lines = [
|
||||
f"# Weekly Coding Retro ({w.get('since', '')[:10]} → {w.get('until', '')[:10]})",
|
||||
f"_{report.get('n_sessions', 0)} real sessions · generated {report.get('generated_at', '')}_",
|
||||
"",
|
||||
"## Top improvement suggestions (cross-flavor first, ≤3 per repo)",
|
||||
]
|
||||
if not report.get("suggestions"):
|
||||
lines.append("- (no recurring problems above threshold this week)")
|
||||
for s in report.get("suggestions", []):
|
||||
flag = " [CROSS-FLAVOR]" if s.get("cross_flavor") else ""
|
||||
lines.append(f"- **{s['repo']}** ({s['priority']}, score={s['score']}){flag}: "
|
||||
f"{s['title']} — {s['recommendation']}")
|
||||
m = report.get("measure", {})
|
||||
lines += ["", "## Fleet snapshot",
|
||||
f"- infra-overhead median: {m.get('infra_overhead_share_median')}",
|
||||
f"- error rate: {m.get('error_rate')} · schema-thrash: {m.get('schema_thrash_sessions')}",
|
||||
f"- success rate: {m.get('success_rate')} · tokens p50: {m.get('tokens_p50')}"]
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def write_local(report: dict, json_path: str, md_path: Optional[str] = None) -> None:
|
||||
os.makedirs(os.path.dirname(json_path) or ".", exist_ok=True)
|
||||
with open(json_path, "w", encoding="utf-8") as fh:
|
||||
json.dump(report, fh, indent=2, sort_keys=True)
|
||||
fh.write("\n")
|
||||
if md_path:
|
||||
with open(md_path, "w", encoding="utf-8") as fh:
|
||||
fh.write(render_markdown(report))
|
||||
fh.write("\n")
|
||||
|
||||
|
||||
def _http_post(url: str, payload: dict) -> None:
|
||||
req = urllib.request.Request(url, data=json.dumps(payload).encode(),
|
||||
headers={"Content-Type": "application/json"}, method="POST")
|
||||
with urllib.request.urlopen(req, timeout=10) as r:
|
||||
r.read()
|
||||
|
||||
|
||||
def publish_to_hub(report: dict, *, base_url: str = DEFAULT_HUB,
|
||||
poster: Optional[Callable[[str, dict], None]] = None) -> bool:
|
||||
"""POST the retro as an event_type=coding_retro progress event. Best-effort."""
|
||||
poster = poster or _http_post
|
||||
n = report.get("n_sessions", 0)
|
||||
k = len(report.get("suggestions", []))
|
||||
payload = {
|
||||
"event_type": "coding_retro",
|
||||
"author": "helix-forge",
|
||||
"summary": f"Weekly coding retro: {k} ranked suggestions across "
|
||||
f"{report.get('window', {}).get('days', 7)} days ({n} sessions).",
|
||||
"detail": report,
|
||||
}
|
||||
try:
|
||||
poster(f"{base_url.rstrip('/')}/progress/", payload)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
Reference in New Issue
Block a user