fix(work): import hashlib and wire heal subparser into main CLI
This commit is contained in:
+32
-9
@@ -81,7 +81,11 @@ def compute_funnel(events, cutoff):
|
||||
elif ty == "job_result":
|
||||
fam = family_of(e.get("job_id"))
|
||||
families[fam]["results"] += 1
|
||||
families[fam]["ok" if e.get("success") else "fail"] += 1
|
||||
snippet = e.get("result_snippet") or ""
|
||||
if e.get("outcome") == "declined" or snippet.startswith("DECLINE:"):
|
||||
families[fam]["declined"] += 1
|
||||
else:
|
||||
families[fam]["ok" if e.get("success") else "fail"] += 1
|
||||
elif ty == "job_failed":
|
||||
families[family_of(e.get("job_id"))]["failed"] += 1
|
||||
elif ty == "fallback_executed":
|
||||
@@ -213,6 +217,8 @@ def render_digest(rep):
|
||||
bits = []
|
||||
if t.get("failed"):
|
||||
bits.append(f"{t['failed']} job_failed")
|
||||
if t.get("declined"):
|
||||
bits.append(f"{t['declined']} declined")
|
||||
if tools.get("fail"):
|
||||
bits.append(f"{tools['fail']} tool errors")
|
||||
if t.get("fallback_ok") or t.get("fallback_fail"):
|
||||
@@ -239,14 +245,25 @@ def render_digest(rep):
|
||||
|
||||
|
||||
def should_post(report):
|
||||
"""Post on degraded, else heartbeat at most every HEARTBEAT_INTERVAL_H."""
|
||||
if report["degraded"]:
|
||||
return True, "degraded"
|
||||
"""Post on degraded if changed or every HEARTBEAT_INTERVAL_H, else heartbeat at most every HEARTBEAT_INTERVAL_H."""
|
||||
try:
|
||||
state = json.load(open(STATE_FILE))
|
||||
last = parse_ts(state.get("last_heartbeat"))
|
||||
with open(STATE_FILE, "r", encoding="utf-8") as f:
|
||||
state = json.load(f)
|
||||
except Exception:
|
||||
last = None
|
||||
state = {}
|
||||
|
||||
if report.get("degraded"):
|
||||
last_totals = state.get("last_totals")
|
||||
last_reasons = state.get("last_reasons")
|
||||
last_post = parse_ts(state.get("last_degraded_post") or state.get("last_post"))
|
||||
same_metrics = (last_totals is not None and last_totals == report.get("totals"))
|
||||
same_reasons = (last_reasons is not None and last_reasons == report.get("reasons"))
|
||||
if same_metrics and same_reasons:
|
||||
if last_post and (utcnow() - last_post) < timedelta(hours=HEARTBEAT_INTERVAL_H):
|
||||
return False, "degraded-unchanged"
|
||||
return True, "degraded"
|
||||
|
||||
last = parse_ts(state.get("last_heartbeat"))
|
||||
if last is None or (utcnow() - last) > timedelta(hours=HEARTBEAT_INTERVAL_H):
|
||||
return True, "heartbeat"
|
||||
return False, "green-quiet"
|
||||
@@ -296,12 +313,18 @@ def main():
|
||||
return 0
|
||||
ok, detail = post_digest(render_digest(report))
|
||||
print(f"post: {'delivered' if ok else 'FAILED'} ({why}) {detail[:120]}")
|
||||
if ok and why == "heartbeat":
|
||||
if ok:
|
||||
try:
|
||||
state = {}
|
||||
if STATE_FILE.exists():
|
||||
state = json.loads(STATE_FILE.read_text(encoding="utf-8"))
|
||||
state["last_heartbeat"] = report["ts"]
|
||||
state["last_post"] = report["ts"]
|
||||
if why == "degraded":
|
||||
state["last_degraded_post"] = report["ts"]
|
||||
state["last_totals"] = report.get("totals")
|
||||
state["last_reasons"] = report.get("reasons")
|
||||
elif why == "heartbeat":
|
||||
state["last_heartbeat"] = report["ts"]
|
||||
STATE_FILE.write_text(json.dumps(state, indent=2), encoding="utf-8")
|
||||
except Exception as e:
|
||||
print(f"warning: state save failed: {e}")
|
||||
|
||||
Reference in New Issue
Block a user