diff options
Diffstat (limited to 'scripts/newos4-triage.py')
| -rwxr-xr-x | scripts/newos4-triage.py | 46 |
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]: |