Don't Build Your Own Peak: Time-Spreading Background Agent Schedules
Antigravity 2.0's desktop auto-schedules tasks in the background. Convenient, but cluster them at round hours and you build your own peak, and the late-night jobs fail together. Here is a design that spreads times and bans overlap, with real results.
Once Antigravity 2.0's desktop began running multiple agents in parallel and auto-scheduling tasks in the background, I moved one repetitive job after another to background execution. Across my indie apps and my Dolice blogs, generation, verification, and integrity checks run on a fixed rhythm every day. Not having to sit and wait for them is plainly a relief.
But after running it a while, I noticed that on some days the late-night jobs failed together at the same hour. The cause was not external; it was me. Every time I added a new job, I unconsciously chose a round time like 8 p.m. or 7:30. The result was that processing clustered at the same instant, jobs fought over a limited allowance, and the later ones were rejected right at startup.
This article lays out a design for time-spreading background schedules so you don't build your own peak, together with the process of reorganizing my own schedule.
Background execution hands you the "when"
Back when I ran things by hand, the moment I launched was the run time. They spread out naturally. Move to background execution and you now decide and hand over that "when." Choose round times carelessly here and several jobs stand up at the same instant.
It's more accurate to see it this way: in exchange for the convenience, the responsibility for designing run times has come to you. Even when you think you've delegated to the background, the time clustering is something you planted.
What a self-inflicted peak really is
People gravitate to round numbers when choosing a time: 8 p.m., 9:30, noon. Add several jobs at separate moments and, because the chosen times are the same round numbers, they end up concentrated at one point.
Times we gravitate to
Why they cluster
What happens
Round hours (like 20:00)
Easy to remember and specify
Multiple jobs launch at once and fight over allowance
Noon, midnight
Chosen as natural boundaries
Overlaps with other scheduled processing too
A fixed pre-bedtime hour
Set to match your daily rhythm
The evening peak thickens
In my case, processing had bunched around 8 p.m. and 7:30. Light on their own, but run at once they scramble for the allowance and the later ones get rejected. The peak did not arrive from outside; it was built up from a habit in how I picked times.
✦
Thank you for reading this far.
Continue Reading
What follows includes implementation code, benchmarks, and practical content we hope you'll find useful. This site runs without ads — server and development costs are supported entirely by members like you. If it's been helpful, we'd be truly grateful for your support.
WHAT YOU'LL LEARN
✦How jobs pile onto round hours and build a self-inflicted peak, and how to spot it
✦A ~30-line shell that spreads offsets deterministically from the job name, plus three criteria for time spreading
✦An overlap guard that skips a window if the previous run is still active, and the results of rounding the total run count down
Secure payment via Stripe · Cancel anytime
✦
Unlock This Article
Get full access to the rest of this article. Buy once, read anytime. This site is ad-free — your support goes directly toward keeping it running.
Spreading design — three criteria for shifting times
Spreading blindly makes the schedule impossible to track. I decide times by three criteria.
Avoid round times; shift deliberately at the minute level
Don't place heavy jobs in the same time band as each other
Avoid the peaks of existing scheduled work (cache jobs, other automation)
Following just these three cut simultaneous launches sharply. What helped most was moving a few of the heavy jobs that had bunched at the same evening hour to different times. I kept the count the same and shifted only the times. If they don't run at once, even a limited allowance sees no scramble.
Spread in code — deterministic offset from the job name
Pick times by hand, one by one, and you drift back to round numbers. I compute the offset deterministically from the job name and spread mechanically. The same job name always lands at the same time, so it stays easy to track.
#!/usr/bin/env bash# Compute a deterministic minute offset from the job name to avoid round-hour clusteringset -euo pipefailjob_name="$1" # e.g. "blog-verify"base_hour="$2" # the rough hour you want (e.g. 20)# make a deterministic 0-59 minute offset from a hash of the job namehash="$(printf '%s' "$job_name" | cksum | cut -d' ' -f1)"minute=$(( hash % 60 ))# also avoid round minutes (0/15/30/45) by always shiftingcase "$minute" in 0|15|30|45) minute=$(( minute + 7 )) ;;esacminute=$(( minute % 60 ))printf 'schedule %s at %02d:%02d\n' "$job_name" "$base_hour" "$minute"# register this time in the background schedule
Specify the same rough hour and, if the job names differ, the minutes spread. Because a round minute always gets nudged by a few, several jobs never coincidentally line up on the zero mark. The aim is to cancel the by-hand habit with a mechanism.
Ban overlap — skip the window if the previous run is still active
Even with spread times, a long-running job overlaps the previous run with the next. The scramble for allowance revives the instant they overlap, so I add a guard: if the previous window is still running, skip the next.
#!/usr/bin/env bash# simple lock to prevent double-launch of the same jobset -euo pipefaillock="/tmp/agent-${1:?job name required}.lock"if [ -e "$lock" ] && kill -0 "$(cat "$lock")" 2>/dev/null; then echo "previous run still active: skip this window" >&2 exit 0 # a skip, not a failurefiecho $$ > "$lock"trap 'rm -f "$lock"' EXIT# run the actual work here
This guard erased the double-launch in the window after a late-night job ran long. The crux is exiting the skip with exit 0 as a normal completion, not an error. Overlap is not an anomaly but a happy-path branch to be avoided.
What I learned by actually spreading
Reorganizing my own schedule, a few things clicked.
Without cutting the count, spreading times alone eases the peak greatly. Just unclustering the same-hour concentration of heavy jobs nearly erased the "fail together" days.
Rounding the total run count down at the source helps too. Cutting the daily run count by a few and spreading the same-hour evening concentration made scrambles over a limited allowance far less likely.
Deterministic offsets reconcile trackability and spread. The same job always lands at the same time, so I could keep in my head which runs when while avoiding only the concentration.
In numbers: spreading times, adding the overlap guard, and rounding the total run count down by about ten percent ended the days when jobs got rejected together at night. All I added was spread; I did not reduce the processing itself.
Your next step
If you've lined up background jobs on round hours, first lay your schedule out in time order and look at it. You'll probably find spots where processing bunches at the same time. Shift those by the minute while keeping the count, add one guard that skips when the previous window is still running, and the late-night peak comes apart by your own hand.
I hope it helps the design of anyone else running several scheduled processes in the background.
Share
Thank You for Reading
Antigravity Lab is ad-free, supported entirely by members like you. We publish practical guides daily with implementation code, benchmarks, and production-ready patterns. If you've found it useful, we'd love to have you on board.