Skip to content

The Drift Log · Model independence and repeatable builds

Injecting Deterministic Time Providers into Async Libraries

28 September 2026 · 3 min read · 600 words · established

Two identical crystal prisms casting exactly the same refraction

Inject a deterministic TimeSource to replace system clocks and make async tests reliably repeatable.

Flaky tests caused by the system clock

Your test suite fails intermittently. One run passes, the next times out. The stack trace points to a setTimeout that fired early, or a promise that resolved in a different order. The culprit is the system clock. It advances while the test runs, and network packets arrive in nondeterministic order. The clock is a source of variance that leaks into every async path. When the same code runs in CI, the timing differences become more pronounced. The result is a flaky test suite that erodes confidence.

The hidden state of time in async libraries

Most async libraries call Date.now() or process.hrtime() directly. The call is hidden behind a utility function, but the function still reads the host clock. The value is used to compute deadlines, back‑off intervals, or to stamp logs. Because the clock is not under test control, the library cannot guarantee the same ordering across runs. The failure mode is “non‑deterministic time”. The fix is to replace the hidden call with a deterministic time provider that can be seeded.

A deterministic time provider in practice

Create a small interface that supplies the current timestamp and a monotonic tick. Implement two versions: one that forwards to the system clock for production, and one that returns a virtual clock for tests.

export interface TimeSource {
  now(): number          // milliseconds since epoch
  tick(): number         // monotonic counter
}

export class SystemTime implements TimeSource {
  now() { return Date.now() }
  tick() { return process.hrtime.bigint() as unknown as number }
}

export class VirtualTime implements TimeSource {
  private _now: number
  private _tick: number
  constructor(seed = 0) {
    this._now = seed
    this._tick = 0
  }
  now() { return this._now }
  tick() { return ++this._tick }
  advance(ms: number) { this._now += ms }
}

Pass an instance of TimeSource to every async component that needs timing. In production you inject new SystemTime(). In test code you inject new VirtualTime(1_600_000_000_000). Before each async operation you can advance the virtual clock manually, or let a test harness advance it automatically after each promise resolves. The same virtual clock eliminates ordering variance. The test becomes deterministic: the same sequence of now() values is observed on every run.

Making CI reproducible

When the virtual clock is the sole source of time, the build artefacts become repeatable. The same source code, the same seed, and the same dependency versions produce identical timestamps in generated files. This aligns with the broader goal of reproducible builds: the output does not depend on a mutable environment. The deterministic time source also simplifies the certification harness. The harness can execute an artifact, record a verdict, and know that any timing‑related failure is a real defect, not a flake.

The approach does not remove all sources of nondeterminism. Network ordering still depends on the underlying transport. However, by removing the clock, you eliminate the most common cause of flaky async tests. The remaining variance can be addressed with mock sockets or by fixing message ordering in the test harness.

How SHPBL helps

SHPBL supplies a reusable library that already abstracts time behind a TimeSource interface. The library is model‑independent: it contains only computed code, no runtime model calls. It is available through the typed TypeScript client at /sdk and can be exercised via the live certification harness at /harness. The same artefact can be evaluated for free at /evaluation before deciding to adopt the full set of capabilities. By adopting SHPBL’s time abstraction you gain a proven virtual clock implementation that integrates with the broader catalog of repeatable‑build primitives.

Monday’s concrete step

Add a TimeSource parameter to the top‑level async function that your tests exercise. Replace every direct Date.now() call inside that function with timeSource.now(). Write a small test that injects new VirtualTime(0) and asserts that a timeout fires after exactly three virtual milliseconds. Run the test locally and in CI. If it passes both places, you have removed the most visible source of flakiness and moved a step closer to reproducible builds.

Keep reading

Next in the log

The Strategic Master Library · written and reviewed under the house's own epistemic rules: nothing claimed that we cannot show.