Skip to content

feat: default timelines to a shared ambient clock, overridable per timeline#79

Merged
johngeorgewright merged 1 commit into
masterfrom
feat/ambient-clock
Jun 5, 2026
Merged

feat: default timelines to a shared ambient clock, overridable per timeline#79
johngeorgewright merged 1 commit into
masterfrom
feat/ambient-clock

Conversation

@johngeorgewright

Copy link
Copy Markdown
Collaborator

Why

Threading an explicit { clock } through every Timeline.create proved to be the wrong default: in practice a source, the code under test, and an expectation always need the same clock, and forgetting to share it produced a class of confusing bugs (timer races, deadlocks). The fake-timers clock is already a process-global singleton anyway, so injection was fighting reality.

What

  • Ambient default clock. getDefaultClock() returns a shared clock; Timeline uses it unless given an explicit { clock } (the override is unchanged). setDefaultClock(clock) swaps it and returns a restore handle to pair with test teardown, so the ambient clock doesn't leak between tests.
  • Per-frame timer onPass. A passed Tn now advances one frame at a time (like dash) instead of jumping its whole duration in one synchronous step. This lets two timelines sharing a clock interleave in lock-step — fixing the "source races ahead of the expectation's barrier" failure on direct source → expectation timing.

Tests

tsc (src + test projects) clean, build clean, bun test13 pass, including new coverage: timelines share the ambient clock by default, an explicit clock overrides it, and setDefaultClock swaps/restores. Existing streamBridge end-to-end proof still passes.

Note

Behavioural change: the default clock is now shared rather than fresh-per-Timeline. The { clock } override restores per-timeline isolation where needed. Tests that assert absolute clock.now should reset the ambient clock in a beforeEach (setDefaultClock(new Clock())), as this PR's own tests now do.

🤖 Generated with Claude Code

…meline

In practice a source, the code under test, and an expectation always want
the same clock, so make sharing the default. Timelines now use an ambient
clock (`getDefaultClock`) unless given an explicit `{ clock }`. Swap the
ambient clock with `setDefaultClock` (e.g. a fake-timers-backed clock in a
test); it returns a restore handle to pair with teardown so the ambient
clock doesn't leak between tests.

Also advance a passed timer one frame at a time (like `dash`), so timelines
sharing a clock interleave in lock-step instead of the source jumping a
timer's whole duration synchronously and racing ahead of an expectation's
barrier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@johngeorgewright johngeorgewright merged commit 308688e into master Jun 5, 2026
2 checks passed
@johngeorgewright johngeorgewright deleted the feat/ambient-clock branch June 5, 2026 12:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant