15 launchd Jobs and One Quota Circuit Breaker: Deciding What to Re-run Once the Circuit Closes A developer built quota-catchup.py, a companion tool to the claude-quota-guard.py quota circuit breaker that decides which of 15 launchd jobs to re-run after a quota-exhaustion circuit closes. The script identifies re-run candidates by checking whether a plist is guarded, whether today's latest marker is a SKIPPED entry, and whether a StartCalendarInterval slot has already passed, avoiding duplicate runs and unnecessary catch-up for StartInterval jobs. A circuit breaker that halts every job the moment your quota runs dry is only half the story. The harder question shows up afterward: once the circuit closes again, what do you do with everything it skipped? In my previous post I wrote about fixing the Wiki secret-scan sync https://zenn.dev/bokuwalily/articles/wiki-improvements-secret-scan-sync . This time I'm switching gears and following up on claude-quota-guard.py , the quota circuit breaker I built. That script's story ended at "detect quota exhaustion, stop all jobs." In real operation, there's a whole second problem waiting past that point—and along the way it involved a 1200-second timeout that turned out to need 2700, and a load average that climbed past 40 when I got it wrong. run job in claude-quota-guard.py returns exit 0 immediately when a job starts while the circuit is open, leaving nothing but a marker in the log. print "CLAUDE QUOTA JOB SKIPPED " f"job={label} reason={status 'reason' } remaining={status 'remaining seconds' }s ts={now }", file=sys.stderr, return 0 This guard sits in front of 15 of the plists under ~/Library/LaunchAgents/ .plist . As the comment in the code puts it: 🔴 2026-08-21: circuit が開くと全ジョブが一律で止まるため、消費の大半を占める 返信/エンゲージ系がクォータを使い切った巻き添えで「投稿」まで停止していた。 The problem is that after the circuit closes and open until has passed, launchd does nothing until that job's next StartCalendarInterval slot comes around . If the 9:00 AM job was skipped for quota reasons and the quota recovers at 10:00, but the next slot is 9:00 AM tomorrow, you've lost an entire day's execution. Deciding "what to re-run, and how much, after recovery" is the job of quota-catchup.py . quota-catchup.py walks every plist in find candidates and only treats a job as a re-run candidate if it passes three conditions ANDed together. | Condition | Function | Purpose | |---|---|---| | Is this plist guarded? | is guarded | Don't mix in unrelated jobs | | Is today's latest marker SKIPPED? | latest marker is today skip | Exclude jobs that already ran, and stale skips | | Has a slot already passed? | calendar slot passed | Exclude StartInterval jobs and jobs with only future slots | php def find candidates ... - list Candidate : candidates: list Candidate = for plist path in sorted launch agents.glob " .plist" : plist = load plist plist path if not plist or not is guarded plist : continue ... if label in already kicked or not latest marker is today skip output paths, label, today : continue if calendar slot passed plist, now : candidates.append Candidate label, plist path return candidates Re-run targets are limited to jobs that are "launched via claude-quota-guard.py ." The check is nothing more than a string match on ProgramArguments . php def is guarded plist: dict - bool: arguments = plist.get "ProgramArguments" return isinstance arguments, list and any "claude-quota-guard" in str value for value in arguments If you picked up plists that don't go through the guard, you'd end up kicking ordinary cron-style jobs that have nothing to do with the quota. This is the star of the show. A single job can have multiple StartCalendarInterval slots. A real example is com.shun.daily-brief.plist .