aboutsummaryrefslogtreecommitdiff
path: root/scripts/newos4-triage.py
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/newos4-triage.py')
-rwxr-xr-xscripts/newos4-triage.py46
1 files changed, 46 insertions, 0 deletions
diff --git a/scripts/newos4-triage.py b/scripts/newos4-triage.py
index f639359..ef05dc1 100755
--- a/scripts/newos4-triage.py
+++ b/scripts/newos4-triage.py
@@ -61,6 +61,19 @@ def age_hours(s):
return None if d is None else round((datetime.now(timezone.utc) - d).total_seconds() / 3600, 1)
+def _ledger_module():
+ """Load scripts/newos4-ledger.py by path (the filename has a hyphen, so no plain import)."""
+ try:
+ import importlib.util
+ path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'newos4-ledger.py')
+ spec = importlib.util.spec_from_file_location('newos4_ledger', path)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
+ return mod
+ except Exception:
+ return None
+
+
def check_runner():
r = subprocess.run([sys.executable, 'scripts/newos4-maintain.py'], cwd=ROOT,
capture_output=True, text=True, timeout=300)
@@ -104,6 +117,22 @@ def build(apply_fix=False):
for e in errors:
buckets[classify(e)].append(e)
+ # A signature's lifetime count is not a priority: the four biggest entries in this file are one
+ # 8-day-old Ollama outage. Rank by recency + normalised clusters, and let the ledger say whether a
+ # shipped fix actually held.
+ ledger = _ledger_module()
+ groups = ledger.rows_by_norm(errors) if ledger else {}
+ fixes = ledger.verify(errors) if ledger else []
+ fix_summary = ledger.summarise(fixes) if ledger else {'regressed': [], 'watching': [], 'closed': []}
+ active = [e for e in errors if (age_hours(e.get('last_seen')) or 1e9) < 24]
+ stale = [e for e in errors if (age_hours(e.get('last_seen')) or 0) > 168]
+ clusters = sorted(
+ ({'norm': g['norm'], 'count': g['count'], 'signatures': g['signatures'],
+ 'last_hours': age_hours(g['last_seen']), 'frame': g['frame'],
+ 'message': safe((g['messages'] or [''])[0], 120)} for g in groups.values()),
+ key=lambda x: (x['last_hours'] if x['last_hours'] is not None else 1e9, -x['count'])
+ )[:8]
+
digest = {
'generated_at': datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%SZ'),
'counts': {
@@ -114,7 +143,15 @@ def build(apply_fix=False):
'self_referential_records': len(buckets['self-referential']),
'reports_total': len(reports),
'reports_new': len(new_reports),
+ 'active_records': len(active),
+ 'stale_records': len(stale),
+ 'clusters': len(groups),
+ 'fixes_regressed': len(fix_summary['regressed']),
+ 'fixes_watching': len(fix_summary['watching']),
+ 'fixes_closed': len(fix_summary['closed']),
},
+ 'clusters': clusters,
+ 'fixes': fix_summary,
'top_real': sorted(
({'message': safe(e.get('message'), 200), 'type': e.get('type'),
'count': int(e.get('count') or 1), 'age_hours': age_hours(e.get('last_seen')),
@@ -148,6 +185,15 @@ def build(apply_fix=False):
print(f"newOS4 triage {digest['generated_at']}: records={c['records']} "
f"occurrences={c['occurrences']} real={c['real_records']} noise={c['noise_records']} "
f"self={c['self_referential_records']} reports={c['reports_total']} new={c['reports_new']}")
+ print(f" ACTIVE| {c['active_records']} records seen in last 24h | stale>7d {c['stale_records']} "
+ f"| clusters {c['clusters']} of {c['records']} signatures")
+ for cl in digest['clusters'][:5]:
+ fr = f" @ {cl['frame']}" if cl.get('frame') else ''
+ print(f" CLUSTER| {cl['count']}x ({cl['signatures']} sig) {safe(cl['message'], 95)}{fr}")
+ for fx in digest['fixes']['regressed']:
+ print(f" FIX| REGRESSED {fx['commit']} {fx['count']}x {safe(fx['message'], 80)} ({fx['detail']})")
+ for fx in digest['fixes']['watching']:
+ print(f" FIX| watching {fx['commit']} {safe(fx['message'], 80)} ({fx['detail']})")
for f in digest['checks']['findings']:
print('check:', f)
for e in digest['top_real'][:5]: