feat(approvals): קטגוריית "חילוץ-הלכות תקוע" במרכז-האישורים (#133)
All checks were successful
G12 Leak-Guard / leak-guard (pull_request) Successful in 10s

ה-wakeup המיידי ל-CEO לחילוץ-הלכות הוא best-effort; כשהוא נכשל
(למשל churn-פריסה), הבקשה נשענת רק על הדריינר-הלילי והפריט נשאר
'pending' בלי שאיש רואה — עד ~16-18 שעות. מוסיף קטגוריה חמישית
ל-api_chair_pending שמציפה case_law עם halacha_extraction_status=
'pending' שכבר עבר את חלון-הדריינר (requested_at < now()-6h), כך
שכשל-שקט הופך לפריט-יו"ר גלוי במרכז-האישורים ("שלא יישכח").

שאילתת-מקור ישירה (תואם הדפוס), href ל-/precedents (שם כפתור
request-halachot מעיר CEO = retry). אפס שינוי-frontend — דף-האישורים
מרנדר categories גנרית (ApprovalCard לפי label/severity/count/href).
gate-free (לוגיקה/נתון בלבד). הסף 6h תכוונן.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 10:49:19 +00:00
parent 51f9d9d309
commit 5913654ae2

View File

@@ -5737,6 +5737,27 @@ async def api_chair_pending():
"href": f"/cases/{r['case_number']}"} for r in qa_rows[:5]],
})
# 5) חילוץ-הלכות תקוע — כשל-שקט (#133): ה-wakeup המיידי ל-CEO הוא
# best-effort; אם הוא נכשל, הבקשה נשענת רק על הדריינר-הלילי (23:0005:00),
# והפריט נשאר 'pending' בלי שאיש רואה. מציפים רק כאלה שכבר עברו את חלון
# הדריינר (>6 שעות) — כך בקשת-בוקר שתנוקה הלילה לא מקפיצה אזעקת-שווא.
STUCK = "halacha_extraction_status='pending' AND " \
"halacha_extraction_requested_at < now() - interval '6 hours'"
se_count = await conn.fetchval(f"SELECT count(*) FROM case_law WHERE {STUCK}")
se_oldest = await conn.fetchval(
f"SELECT min(halacha_extraction_requested_at) FROM case_law WHERE {STUCK}")
se_sample = await conn.fetch(
f"SELECT coalesce(case_number,'') AS cn, coalesce(case_name,'') AS name "
f"FROM case_law WHERE {STUCK} ORDER BY halacha_extraction_requested_at ASC LIMIT 5")
categories.append({
"key": "stuck_halacha_extraction", "label": "חילוץ-הלכות תקוע",
"description": "החלטות סופיות שחילוץ-ההלכות שלהן לא נכנס לתור — להרצה מחדש מ-ספריית הפסיקה.",
"count": se_count, "severity": "high" if se_count else "ok",
"href": "/precedents",
"oldest_at": se_oldest.isoformat() if se_oldest else None,
"sample": [{"text": r["cn"], "source": r["name"]} for r in se_sample],
})
total_pending = sum(c["count"] for c in categories)
return {
"total_pending": total_pending,