All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Entries for unreleased work are not written here directly. Each issue drops a
fragment in changelog.d/; the fragments are assembled
into a version section at release. See that README for the format and for when a
change warrants an entry at all.
[0.1.3] 2026-08-27
Docs release: README and guide refresh onto the family's canonical example domains. No library code changes.
Changed
- The README now walks a full worked run in the card-processing domain - load, step, execute effects, persist - and continues it across a restart, with a new module map; the examples are executed by a test so they cannot drift from the real API.
- Example domains follow the family rule: card processing and the signup wizard with A/B testing only.
- Agent tooling: gate attestation points at
mix quality.verify(shipped by ex_quality 0.14) instead of a retired local task.
[0.1.2] 2026-08-24
Docs release: the hexdocs/README overhaul from PR #20. No library code changes.
Changed
- Hexdocs no longer publishes the ADRs: the ADR extras and their
groups_for_extrasentry are removed, so the published docs are the README, this changelog, and the restart-demo guide. ex_docis pinned to~> 0.40, andCHANGELOG.mdis listed inskip_undefined_reference_warnings_on;mix docsnow completes with zero warnings.- The README gains the standard badge row (CI, hex.pm version/downloads, hexdocs, license) and a documentation index line linking the published restart-demo guide.
[0.1.1] 2026-08-24
Patch release: the key-generator compile-race fix from PR #18.
Fixed
- Custom key-generator validation in
use StatifierPersistence.Ectono longer fails spuriously when the generator module is still being compiled by the host's parallel compiler; validation now waits for in-flight compilation (Code.ensure_compiled/1) instead of checkingCode.ensure_loaded?/1.
[0.1.0] 2026-08-22
First release: the persistence-first execution loop for the statifier statechart engine - load a persisted position, step it, execute the effects, persist - packaged as a storage-adapter behaviour with an identity guard, an in-memory reference adapter, a run lifecycle, and an Ecto/Postgres layer, all covered by one conformance suite downstream adapters inherit.
Added
StatifierPersistence.Storage.Adapter, the storage contract, including run records:insert_run/2,fetch_run/2, andupdate_run/2callbacks withrun_record/run_statustypes and the:run_exists/:run_not_founderror arms;StatifierPersistence.Storage.InMemoryis the reference implementation.- Guarded run access on the facade:
StatifierPersistence.Storage.insert_run/5,update_run/5,fetch_run/2, andload_run_position/3(identity-guarded, with the:run_position_missingarm for a run persisted without a position). - Run-record conformance tests in
StatifierPersistence.Testing.StorageConformance, so downstream adapters inherit the same contract checks. - The run lifecycle as a library:
StatifierPersistence.Runs.create/4andstep/5drive the load -> re-stamp -> step -> execute -> persist loop over durable run records, handing effects to a host-suppliedStatifierPersistence.Executor(behaviour or arity-2 fun) and returning the host-facingStatifierPersistence.Runstruct; events to a terminal run come back as{:discarded, run}. - Failure semantics on the loop: executor failures on actionable effects
re-enter the chart as
error.communicationevents (single wave per step, observational failures discarded); effect execution is at-least-once, with a failed persist re-driving the same event and re-emitting the same effects under identical deterministic keys; budget exhaustion persists a:failedrun (position untouched) and returns{:error, {:budget_exhausted, payload}}. StatifierPersistence.Runs.fail/4, the host-driven abandonment: marks an active run:failedwith a reason, leaves the stored position untouched, and discards on a terminal run - backed by the status-only writerStatifierPersistence.Storage.update_run_status/4.- Pluggable per-run serialization: the
StatifierPersistence.Serializationbehaviour (with_run/3), selected per lifecycle call withserialization: {module, config}onRuns.create/4,step/5, andfail/4. The default strategy,StatifierPersistence.Serialization.AdapterLock, delegates to the optional adapter callbackStatifierPersistence.Storage.Adapter.lock_run/3(implemented byInMemory, conformance-tested when exported) and refuses with{:error, {:serialization, :not_supported}}when the adapter does not export it. use StatifierPersistence.Ecto: compile-time configuration on the host's module (repo:,key:,table_prefix:,tables:,prefix:) that definesChart,Position, andRunschema modules and exposes the resolved config via__statifier_persistence__/1. Requires the optionalecto_sqldependency.StatifierPersistence.Ecto.KeyGenerator: the behaviour a surrogate-key scheme implements, with:uxid(default),:uuid(UUIDv7),:bigserial, and{module, opts}resolved throughresolve/1.StatifierPersistence.Ecto.Migrations: the versioned migrations helper (up/1,down/1, takingfor: HostModuleor the same literal optionsusetakes) that creates thecharts/positions/runstables from the same resolved config the schemas use.StatifierPersistence.Storage.Ecto: the Postgres storage adapter over the schemas a host generates withuse StatifierPersistence.Ecto(Storage.new(Storage.Ecto, persistence: MyApp.Persistence)). Passes the same conformance suite as the in-memory reference adapter; engine identities stored verbatim;:run_existsenforced atomically by the unique index.Storage.Ecto.isolate/1: withsandbox: true, wraps each test in its ownEcto.Adapters.SQL.Sandboxcheckout - the hook host test suites (and this package's conformance suite) isolate through.Storage.Ecto.lock_run/3: per-run mutual exclusion as a transaction-scopedpg_advisory_xact_lockplus aSELECT ... FOR UPDATErow lock (ADR-0004 as amended), consumed bySerialization.AdapterLock.uxidis a required dependency (the default key scheme works out of the box);ecto_sqlis optional and the package compiles without it.