Determinism guarantees

Worlds makes tests replayable: the same seed, configuration and ordered request sequence produce byte-identical world state. This applies to the shared world engine across connectors. It does not make an agent's model output or external services deterministic.

On this page

The guarantee

For the same runtime version and inputs, a replay produces:

  • The same dump: canonical JSON containing resource objects, events, request history, counters, marks, idempotency records, webhook delivery records and the logical clock.
  • The same event history: IDs, order, timestamps and payloads.
  • The same diff before-images: each updated or deleted object's state at the chosen mark.

The guarantee holds across processes, machines and reruns. A dump compares the world's recorded state; a mark and diff shows what a test changed. Diffs exclude internal events, which have their own query endpoint.

Replay a test

  1. Create a fresh world from the same seed and use the same configuration.
  2. Apply the same failure conditions, if any.
  3. Send requests in the same order, including clock advances and marks.
  4. Finish webhook delivery, if the test uses it, then compare the dumps or assertions.

Keep the runtime version and seed bytes with the test. Await requests when their order matters: a world serializes writes, but the arrival order of concurrent requests is an input to the replay.

Inspect Admin endpoint
Canonical recorded dump (private history omitted) GET /admin/worlds/:id/dump
Changes since a mark GET /admin/worlds/:id/diff?from=<mark>
Event history GET /admin/worlds/:id/events

The dashboard workflow guide covers saved-input reruns and inspecting test evidence.

Reset and restart

worlds world reset <world_id> restores the seed, clock and counters. It clears mutations, history, marks, active scenarios and both connector replay caches. Request numbering and API-key use counts restart too. Existing keys, admin tokens and webhook endpoint configuration survive.

Reset waits up to five seconds for in-flight twin requests. Requests arriving during reset wait for the new world. An older request that outlives that wait receives a reset-related 500 when it resumes; it is neither executed nor logged in the reset world. An old webhook send cannot write its result into the reset world.

Restarting the server preserves persisted world state, including replay caches. Resetting a world clears that history; exporting a seed also leaves request caches out.

How

Mechanism Replay behavior
Persisted IDs and counters IDs derive from seed data and world counters. Stripe uses deterministic prefixed IDs; native Zendesk exposes numeric IDs.
Logical time Timestamps come from the seed's epoch and explicit clock advances or automatic ticks.
Transactional handlers Resource changes and their events commit or roll back together.
One writer per world A world serializes handlers. Each world has its own database, lock and counters.
Scenario counters Matching failures occur at the same requests on each replay.
Before-images and membership presence Updates and deletes retain prior object state for diffs. Confluence label reactivation also records closed absence intervals. These private histories are excluded from dumps and seed export, survive restart, and clear transactionally on reset.

Separate worlds can interleave on one server process. This is state isolation; synchronous database work still shares one JavaScript thread.

Logical clock

The clock uses integer Unix seconds. In manual mode, only an admin advance moves it. In automatic mode, a fixed tick runs after route and preflight checks, before replay-cache lookup and handler execution.

Authentication failures, injected responses, unknown routes and preflight parameter refusals do not tick. A handler's own validation failure does tick. A replay-cache hit also ticks. These distinctions are part of the request contract.

Clock advancement and due work share one transaction. If due work fails, both roll back. If the tick succeeds and the subsequent handler refuses a request, the tick remains committed.

Connector behavior during an advance:

  • Stripe: elapsed subscription renewals run at their period boundaries in stable order. One advance processes at most 1,000 periods per subscription; renewals_pending identifies subscriptions still due. Further advances or automatic ticks continue the backlog.
  • Native Zendesk: optional closure runs after Stripe renewals in the same transaction, ordered by due time and numeric ticket ID. See logical closure and follow-ups for its explicit simulation settings and limits.

Request replay caches

Connector Stored response
Stripe and legacy desk Exact status and body from keyed POST endpoint outcomes, including execution errors and 500s. Parameter-validation and injected failures store nothing.
Native Zendesk (current source) Successful keyed ticket creations only, independently addressed by literal creation path and key, for two logical hours from each insertion. The original response snapshot is retained; only its known ticket URL is rendered for the current validated origin.

In the beta.4 candidate and current source, native cache dumps include path and sort by path, then key. The frozen published beta.1, beta.2 and 0.9.0-beta.3 packages predate path scoping: their native cache is keyed only by the idempotency key, and dumps omit path and sort by key. Those packages do not provide independent creation caches for /api/v2/tickets and /api/v2/tickets.json. The global world-format change requires recreating incompatible worlds; see storage and upgrades.

Restart retains each entry and its original expiry; reset clears all entries, while seed export/fork carries settled state without retry history. Native request equality, expiry and response wording are local contracts pending live API qualification. See native retry behavior.

Boundaries (deliberate)

Inputs and credentials

World IDs, API keys and admin tokens are random harness credentials, excluded from dumps. They do not become connector object IDs.

Request values remain your inputs. A wall-clock timestamp, random value, different request order or changing external response can produce a different result. The guarantee compares equivalent inputs on the same runtime version.

Webhook delivery

The current webhook dispatcher delivers Stripe events; native Zendesk writes do not emit them. Eligible deliveries run in event-creation order unless a scenario changes it. Outcomes depend on your listener's responses. Retry backoff uses logical time: 60s × 2^(attempt−1).

Automatic delivery runs in the background, outside the world lock. For repeatable delivery assertions, set auto_deliver: false and call POST /admin/worlds/:id/webhooks/flush at fixed points. Alternatively, stop sending requests and flush until a batch is empty before marking or comparing state. Retries scheduled for a future logical time remain pending until the clock advances.

Delivery is at least once across a server restart. If the process stops during a send, that pending delivery can be sent again. Deduplicate listener effects by event ID.

Transport timing

Injected latency and webhook HTTP round trips use wall-clock time. They affect arrival timing, not the logical timestamps stored in objects.

By default, the Stripe-Signature timestamp uses delivery time so ordinary SDK verification works. It is excluded from world state. Use signature_time: "logical" when comparing headers across replays, and configure the verifier's tolerance for the fixture's age. The event's own timestamp always uses logical time.

Checking it yourself

From a source checkout:

pnpm test                         # includes replay and reset coverage
pnpm check:determinism-sources    # guards against ambient time and randomness
pnpm bench                        # isolation acceptance; run on a quiet machine

Recorded verification and measurements live in the build log.