fix(halacha): authoritative rate-limit detection + early-morning catch-up window (supervisor) #251
Reference in New Issue
Block a user
Delete Branch "worktree-halacha-supervisor-ratelimit"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
הבעיה (ליל 13→14.6)
הדריינר רץ בחלון הלילה אך חילץ 0 הלכות. שורש-הכשל, שתי שכבות:
claude_session.py,total_cost_usd:0= מנוי). כל מקטע קיבלapi_error_status:429 — "You've hit your session limit · resets 2:30am (UTC)". ה-reset (2:30 UTC = 5:30 IDT) נחת מעט אחרי סגירת החלון (05:00 IDT) → כל החלון היה חסום.scan_rate_limitקורא רק 120 שורות-זנב מלוג-השגיאות; ה-429 (שורה 8,273/9,170) נקבר תחת ~900 שורות teardown שה-restart-storm של המתזמר עצמו ייצר →rate_limited:falseכל הלילה →restart-hungכל 15 דק'. בנוסף"hold"לא עצר את הדריינר → המשך הלמת-429 ובזבוז המכסה שמחכים לה.התיקון
Fix A — זיהוי rate-limit עמיד (durable):
quota_exhausted()חדש: מקור-האמת הוא endpoint-המכסה (subscription_usage, אותו util שה-UI של Claude Code מציג) — אינו תלוי בעומק-זנב-הלוג, ולכן 429 שגלל החוצה כבר לא "נעלם".scan_rate_limitנשאר רק כ-fallback כש-endpoint לא-זמין.hold-stopped) כדי לא להלום 429; מצית-מחדש כשהמכסה חוזרת (exit מיידי כש-endpoint <100%, או probeclaude -pאם ה-endpoint למטה).Fix B — חלון catch-up בוקר
[05:00–07:00 IDT):not in_cooldown+ תור≠ריק) — איפוס-5-שעות מאוחר נוחת לרוב מעט אחרי 05:00, וכך המכסה המשוחררת לא מתבזבזת עד הלילה הבא. הקצה המורחב (win=(23,7)) מועבר לדריינר כך ש-window-self-guard שלו מקבל את סבבי-ה-catch-up.בדיקות
win.py_compileנקי;statusרץ מקצה-לקצה מול ה-endpoint החי (מכסה 5% כרגע).Invariants
🤖 Generated with Claude Code
הדריינר רץ בלילה 13→14.6 אך חילץ 0 הלכות: מכסת-המנוי של claude.ai אזלה (`api_error_status:429 "You've hit your session limit · resets 2:30am UTC"`, `total_cost_usd:0`), וה-reset (5:30 IDT) נחת מעט אחרי סגירת החלון (05:00 IDT). המתזמר סיווג זאת שגוי כ-"hung" ועשה restart-storm כל 15 דק' — כי `scan_rate_limit` קורא רק 120 שורות-זנב, וה-429 (שורה 8273/9170) נקבר תחת ~900 שורות teardown שה-churn שלו עצמו ייצר. בנוסף "hold" לא עצר את הדריינר → המשך הלמת-429 ובזבוז המכסה. Fix A — זיהוי rate-limit עמיד: • `quota_exhausted()` חדש: מקור-האמת הוא endpoint-המכסה (`subscription_usage`, אותו util שה-UI מציג) — durable, לא תלוי בעומק-זנב-הלוג. log-scrape רק כ-fallback. • בזמן מוגבל עוצר דריינר online (`hold-stopped`) כדי לא להלום 429; מצית-מחדש כשהמכסה חוזרת (exit מיידי כש-endpoint <100%, או probe `claude -p` אם endpoint למטה). Fix B — חלון catch-up בוקר [05:00–07:00 IDT): • נפתח רק לניקוי backlog שנותר כשהמכסה חזרה (מגודר: לא-מוגבל + תור≠ריק) כדי שהמכסה המשוחררת לא תתבזבז עד הלילה הבא. הקצה המורחב מועבר לדריינר (window self-guard). נתונים בטוחים — תיקים נשארו 'processing' for retry, שום הלכה לא אבדה. 13 unit-tests עוברים (parse endpoint, gating של catch-band, win extension); `status` חי OK. Invariants: מקיים G1 (תיקון-במקור: זיהוי ממקור-מכסה סמכותי, לא מתסמין-לוג), G2 (אותו endpoint+מנגנון-חלון קיימים — בלי מסלול מקביל), INV-G3/X16 (לא נוגע ב-checkpointing הדטרמיניסטי). G12 לא רלוונטי (host-side pm2, בלי Paperclip). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>