All notable changes to this project are documented in this file.

The format follows Keep a Changelog, and this project adheres to Semantic Versioning.

0.2.0 — 2026-08-20

Added

  • TimeWarp.Model.commit/2 may return {:ok, new_model_state} in addition to :ok. Fossil collection threads the returned state back into the logical process, which gives a model the only point at which it can release state that a commit has made unreachable. handle_event/3 never observes GVT and so cannot determine what is safe to drop; a model that accumulates per-event state and always returns :ok grows for the length of the run. Events are committed in ascending virtual-time order and the state is threaded through that fold, so commit order is now observable in the resulting state.

    Pruning is bounded by GVT lag rather than immediate: a rollback restores a state snapshot from history, and snapshots at or above GVT were taken before the prune, so they still reference the released terms until the same fossil-collection pass drops them.

    Returning :ok remains valid, so existing models are unaffected.

  • :lp_call_timeout — timeout in milliseconds for the coordinator's own calls into logical processes, default 5_000. These execute inside the coordinator process and a logical process may be remote, so exceeding the timeout fails the run rather than an individual call.

  • :quiescence_timeout_ms — ceiling on the freeze-confirm quiescence poll, default 1_000. The workable value scales with link latency, so a distributed run may require more than a local one.

  • TimeWarp.stop_run/1 cancels a run, tearing down its logical processes and returning the engine to idle. The function existed on the coordinator and the public documentation directed callers into that internal module; it is now part of the public surface.

    It waits without a timeout. Teardown reaches into every placement node's supervisor, which can exceed the default five-second call timeout on a multi-node run; a caller that timed out would exit while the teardown continued and completed, reporting a crash from a call that had in fact worked.

Changed

Fixed

  • start_run/1 against an active run matched no clause and raised FunctionClauseError inside the coordinator. This terminated the coordinator and every caller awaiting the active run, so the run already in progress was lost and the failure surfaced as a crash in the second.

  • Three coordinator calls into logical processes — the per-process dump behind report/1, the GVT query, and the freeze-confirm poll — used the default five-second call timeout while executing inside the coordinator process. A remote or slow logical process therefore terminated the coordinator instead of failing a single call. These now honour :lp_call_timeout.

  • The test suite aborted before ExUnit started when distribution was unavailable, reporting zero tests run. Tests requiring distribution are tagged :distributed and excluded when Node.start/2 fails.

Documentation

  • :placement, :gvt_mode and :net_delay are documented in the TimeWarp moduledoc. All three were read by the coordinator and absent from the option list. :net_delay is marked test-only.

  • The one-run-per-BEAM constraint is stated explicitly. The coordinator is a global singleton, and start_run/1 tears down every logical process on the placement nodes without scoping the teardown to a single run, so concurrent runs destroy one another.

  • :time_window documents that it is the only bound on retained state. Fossil collection reclaims only below GVT, so a workload holding GVT near the floor grows retained state until the VM exhausts memory, and :none places no limit on how far a logical process runs ahead.

  • The TimeWarp moduledoc example referenced a model that does not exist and would not compile, and set time_window: :none — the value documented immediately below it as unbounded. It now uses TimeWarp.Examples.PHOLD with a binding window.

  • TimeWarp.Coordinator's moduledoc described the superseded stop-the-world computation as current and Mattern's algorithm as future work, though :gvt_mode defaults to :mattern. It now leads with Mattern's algorithm as the computation in use, and presents the stop-the-world variant as the test oracle and termination confirmation it actually is.

  • References to an unpublished specification (§N, Mitigation N, Phase-N) are resolved to their meaning throughout the published modules.

0.1.0 — 2026-08-04

Initial release. Jefferson's Time Warp with Mattern's distributed Global Virtual Time, mapped onto BEAM primitives: speculative execution with automatic rollback, anti-message annihilation, fossil collection on GVT advance, an optional deadlock-free time window, lazy cancellation, a purity divergence detector, and static multi-node LP placement.