Skip to content

TS client: injectable currentTimestampProvider for reducer modifiedAt (#186)#189

Open
joshmouch wants to merge 1 commit into
microsoft:mainfrom
joshmouch:ts-reducer-now-seam
Open

TS client: injectable currentTimestampProvider for reducer modifiedAt (#186)#189
joshmouch wants to merge 1 commit into
microsoft:mainfrom
joshmouch:ts-reducer-now-seam

Conversation

@joshmouch
Copy link
Copy Markdown
Contributor

Summary

Brings the TypeScript client to parity with Go/Kotlin/Rust by adding an injectable now-seam for the summary.modifiedAt timestamps the session reducer stamps. Closes the TS half of #186.

The problem (#186)

The docs state reducers are pure and "the same reducer code runs on both server and client, which is what makes write-ahead possible" — but types/channels-session/reducer.ts stamps summary.modifiedAt = Date.now() at 10 sites, so an identical (state, action) pair yields different output depending on when it runs. Under write-ahead reconciliation the client replays the same action and stamps a different modifiedAt than the server, until the server echo overwrites it.

The Go, Kotlin, and Rust clients already deliberately stamp from a clock but expose an injectable seam for deterministic replay/tests (Go SetNowProvider, Kotlin currentTimestampProvider, Rust MOCK_NOW_MS). The TS client was the only one with no seam, forcing its own fixture test to monkeypatch the process-global Date.now (racy under concurrency, not per-call controllable).

The change (non-breaking)

  • types/channels-session/reducer.ts: add currentTimestampProvider: () => number (default Date.now) + setCurrentTimestampProvider(...), mirroring Kotlin's currentTimestampProvider. Replace the 10 inline Date.now() sites with currentTimestampProvider().
  • Re-exported through types/reducers.ts and types/index.ts.
  • types/reducers.test.ts: inject the seam (setCurrentTimestampProvider(() => 9999)) in beforeEach instead of monkeypatching the global Date.now — matching the other clients' fixture runners.

Default behavior is unchanged (Date.now), so this is purely additive for existing consumers.

Verification

  • tsc --noEmit -p types/tsconfig.json — clean.
  • tsx --test types/reducers.test.ts169 pass / 0 fail (incl. the 163 shared reducer fixtures, now driven through the seam).

Note on intent

Filed issue-first as #186, which asked whether you'd prefer (a) docs clarification, (b) TS now-seam parity, or (c) a wire-breaking timestamp change. This PR implements (b) — the non-breaking option that matches the existing cross-client precedent — but happy to adjust toward (a)/(c) or a different seam name/shape per your preference.

…edAt (microsoft#186)

The session reducer stamps summary.modifiedAt from the ambient wall clock
(Date.now), which makes an identical (state, action) pair produce different
output depending on when it runs — at odds with the documented 'pure reducers /
same code on server and client' model. The Go, Kotlin, and Rust clients already
expose an injectable now-seam for exactly this (Go SetNowProvider, Kotlin
currentTimestampProvider, Rust MOCK_NOW_MS); the TypeScript client was the only
one forcing consumers to monkeypatch the process-global Date.now.

This adds a non-breaking, default-Date.now currentTimestampProvider +
setCurrentTimestampProvider to the session reducer (mirroring Kotlin's name),
re-exported through reducers.ts + index.ts, and replaces the 10 inline Date.now()
sites with currentTimestampProvider(). The TS fixture test now injects the seam
instead of monkeypatching the global Date.now (racy under concurrency), matching
the other clients' fixture runners.

typecheck clean; all 169 reducer tests pass (incl. the 163 shared fixtures).

Refs: microsoft#186
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@joshmouch
Copy link
Copy Markdown
Contributor Author

@microsoft-github-policy-service agree

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