◉ANTIGRAVITY LABJP
Articles/Agents & Manager
◈ Agents & Manager/2026-07-12Advanced

Clock Design for Agents: Separating Monotonic and Wall Time to Protect Long Runs

In scheduled and overnight agent runs, computing timeouts, retry backoff, and rate-limit windows from the wall clock breaks in quiet, hard-to-debug ways. This piece walks through a small Clock abstraction that splits monotonic from wall time, with a decision table and testable code.

agents146long-runningmonotonic clocktimeout designscheduled executionreliability12

✦ Premium Article

One morning I opened the log of an agent I had left running overnight, and my hands stopped. A job that started around 2 a.m. had aborted itself with "timed out after 4 hours and 14 minutes." The actual work should have taken about ten minutes. After some digging, it finally clicked: the machine had slept partway through, and on wake the code treated the difference in wall-clock time as "elapsed time." The work was healthy. Only the way I read the clock was wrong.

As an indie developer building apps on my own, I hand a batch of maintenance chores to a scheduled overnight run, and this kind of clock-induced misbehavior is hard to avoid. The longer you entrust to an agent, the more that an unglamorous decision — how you measure time — quietly determines whether you can sleep while it works. This piece lays out a small design that deliberately separates a monotonic clock from a wall clock, with working code and a decision table.

Why elapsed time on the wall clock breaks

Let's start with the pattern most code adopts without thinking about it.

const start = Date.now();
await doWork();
const elapsedMs = Date.now() - start;
if (elapsedMs > 30_000) {
  throw new Error("work exceeded 30 seconds");
}

It looks perfectly reasonable, and for short work it rarely causes trouble. But Date.now() returns wall-clock time — a human-facing "what time is it right now." The wall clock moves for reasons that have nothing to do with your program.

Here are the situations where that difference drifts away from the real duration.

EventEffect on the wall clockWhat happens to elapsed-time math
NTP time correctionJumps forward or back by hundreds of ms to secondsElapsed goes negative, or spikes
VM or laptop suspend/resumeLeaps forward by the time it was pausedHealthy work is misjudged as a timeout
Daylight saving transitionShifts by one hourOvernight jobs land off by an hour
Manual or sync time changeJumps arbitrarilyRetry intervals and rate-limit windows break

The nastiest case is when time moves backward. If NTP pulls the wall clock into the past, Date.now() - start becomes negative, and elapsedMs > 30_000 never becomes true. The timeout never fires, and the agent waits forever. Conversely, right after resuming from suspend the difference balloons, and a job that hasn't finished anything gets cut off. Either way, the log only ever says "timeout" or "hung," and you burn hours before reaching the real cause.

The monotonic clock as an alternative

At the heart of the problem is reusing a clock meant for "displaying the current time" to "measure how long something took." For durations, the right tool is a monotonic clock.

A monotonic clock is guaranteed never to run backward. It advances only forward, whether NTP corrects the system time or DST flips. In Node.js that is process.hrtime.bigint(); in browsers and Deno it is performance.now(). What it returns is not a "when" but a relative "how far from some origin." You can't read it as a human timestamp, but take the difference of two readings and you get an accurate duration.

const start = process.hrtime.bigint();
await doWork();
const elapsedMs = Number(process.hrtime.bigint() - start) / 1_000_000;
if (elapsedMs > 30_000) {
  throw new Error("work exceeded 30 seconds");
}

The visible change is tiny, but the meaning shifts a great deal. This elapsedMs reflects the real time that actually passed, no matter how the machine slept or how NTP thrashed. The catch is that a monotonic value is relative to an origin, so it can't be used across processes or written to a log as a human-readable time. That is exactly where a division of responsibility is needed.

✦

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
✦Why timeouts, backoff, and rate-limit windows silently break when measured on the wall clock during VM suspend or NTP correction
✦A small Clock abstraction that routes durations to a monotonic clock and timestamps to the wall clock
✦A decision table for which clock to use per purpose, plus how to test timing logic deterministically
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.

or
Unlock all articles with Membership →
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.

  • ✦Copy-paste ready implementation code
  • ✦New advanced guides published daily
  • ✦$5/mo or $10 for lifetime access
View Membership →

Related Articles

◈ Agents & Manager2026-04-24
SRE for Antigravity Agents — Taming Probabilistic Systems with SLOs and Error Budgets
AI agents are probabilistic by nature, so running them in production without SRE thinking is risky. This guide applies SLIs, SLOs, and error budgets to Antigravity agents with working code, and shows why the textbook 14.4x burn-rate alert never fires on a loose SLO.
◈ Agents & Manager2026-10-16
Before Handing an Agent a .zip: Dropping .git Cut 29 MB to 15 MB, and 28 Secret Warnings Turned Out to Be Noise
Antigravity app v2.23.0 lets you attach a .zip. I packed my own repository to count what .git weighs and how many secret-scan warnings are false alarms, and I leave the full Python script that excludes, scans, and writes a manifest.
◈ Agents & Manager2026-10-13
Turning Gut-Feeling Judgments into Rules an Agent Can Actually Follow
A small, repeatable way to turn the judgment calls that live only in your head into written rules an Antigravity agent can apply, with a quick check against past cases.