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.6.0] 2026-09-02
Added
- An async invocation's
caller_contextis stored on its Oban job row and handed back when the invocation is answered, so a completion days later still links to the trace that started it. StatifierOban.Invoke.Deliverygains optionaldeliver/4anddeliver_failure/4, which receive thatcaller_contextfor a process-less host building the answer event itself; implementations defining only the three-argument doors are called exactly as before.
Changed
- This package now requires
{:statifier, "~> 2.5"}, the first release carrying%Statifier.Effect.Invoke{}.caller_contextandStatifier.Invoke.Answer.done/4/failed/4. Upgrade statifier to 2.5.0 or later alongside this release.
[0.5.0] 2026-09-01
Added
StatifierOban.Telemetryemits eleven[:statifier_oban, ...]events across the scheduling and delivery seams, withevents/0returning the full list for:telemetry.attach_many/4(ADR-0006,docs/telemetry.md).
[0.4.0] 2026-09-01
Added
- An invoke handler may define
run/2instead ofrun/1and receive the run's scope alongside the invoke effect, so work keyed to the workflow instance can be written against the Oban handler base. StatifierOban.Timer.Delivery.fired_event/2builds the external event a fired timer feeds back, so a host delivery implementation restores the caller's trace context instead of assembling the event by hand and dropping it.
[0.3.2] 2026-08-31
Fixed
- A base handler's cancel no longer cancels an invoke job that is already
executing, so an invocation whose own completion exits its invoking state is
no longer killed mid-delivery by that state's
<cancel>.
[0.3.1] 2026-08-31
Changed
- An undecodable invoke job now reports
error.communication.invoke.<invoke_id>through the delivery seam before cancelling, whenever its row still names a scope and an invoke id, so a chart parked onerror.communicationno longer hangs on a corrupt row.
Fixed
StatifierOban.Timer.cancel/3no longer cancels a timer job that is already executing, so a fired timer whose delivery exits the state that armed it is no longer killed mid-step by its ownonexit<cancel>.
[0.3.0] 2026-08-27
Added
- An invoke handler whose retries are exhausted now feeds
error.communication.invoke.<invoke_id>back into the chart, carrying%{"reason" => class, "attempts" => n, "detail" => text}and delivered behind the same run-liveness check a completion goes through, so a chart parking failed work for operator recovery leaves the invoking state instead of hanging in it. Previously a permanent failure was visible only on the discarded job row. The failure classes are"run_failed"(the last attempt returned{:error, reason}) and"run_crashed"(it raised or exited); see ADR-0005. StatifierOban.Configaccepts an optional:opaque_codec, a module implementing the newStatifierOban.OpaqueTerm.Codecbehaviour, that transforms the bytes of a job's host-opaque args (a timer'sdataandcaller_context, an invoke'sparamsandcontent) before they are stored. The default (nil) is unchanged: today's plain Base64 encoding. For most hosts, passing entity ids instead of values and re-fetching at execution time remains the recommended shape - see the README's "Sensitive values in job args" section.
Changed
- Breaking for hosts implementing
StatifierOban.Invoke.Deliverythemselves: the behaviour gains a requireddeliver_failure/3callback, and a delivery module that does not export it no longer resolves - its jobs retry with{:error, {:invalid_delivery, name}}. Adddeliver_failure/3alongside yourdeliver/3, running the same liveness check and reporting the failure to the run. Hosts using the defaultStatifierOban.Invoke.Delivery.Sessionneed no change. - This package now requires
{:statifier, "~> 2.2"}, the first release carryingStatifier.Session.failed_invocation/3(st-ADR-0068). - The README now opens with a worked canonical-domain example (a card authorization's settlement window as a durable timer, plus an async invoke), and the project docs were refreshed to match the current surface.
[0.2.1] 2026-08-24
Documentation-only release: brings the package docs to the shared hexdocs standard.
Changed
- ADRs are no longer published to hexdocs; they remain in the repo.
- README gains the standard badge row (CI, hex version, downloads, docs, license).
Fixed
- README install snippet now points at
~> 0.2to match the released package. - Two
mix docsreference warnings resolved;mix docscompletes with zero warnings.
[0.2.0] 2026-08-24
Fixed
- A re-entered state's authored invoke id schedules a fresh Oban job
instead of being deduped against a previous entry's job: invoke jobs
are now unique on
{scope, invoke_id, macrostep}rather than{scope, invoke_id}. Crash replays still dedup; cancellation still matches every job under{scope, invoke_id}.
[0.1.1] 2026-08-23
Changed
- Relaxed oban dependency to 2.19
[0.1.0] 2026-08-22
First release: durable timers and async invoke execution for the
statifier statechart engine, on the
host's own Oban instance. This package is one implementation of the
host-facing pattern statifier specifies (its docs/durable-timers.md
recipe and ADR-0054/0055/0059/0051), not the definition of it.
Added
StatifierOban.Configcarries the host-supplied Oban instance name plus the required:timers_queueand:invoke_queue(no defaults - a missing queue is a typed error, never a silent fallback), and the:delivery/:invoke_deliveryseams. The package never starts or names an Oban instance of its own (ADR-0002).StatifierOban.Timer.schedule/3consumes a%Statifier.Effect.SendDelayed{}into one Oban job on the host's instance, unique on the{scope, ordinal}dedup key across every job state, with the fire time computed at insert from the relativedelay_ms. Onlynil-target sends are schedulable (st-ADR-0055); a duplicate insert is a conflict no-op.StatifierOban.Timer.cancel/3consumes a%Statifier.Effect.Cancel{}into cancellation of every timer job stored under the{scope, send_id}cancellation key - several jobs may match, per spec 6.3. A cancel matching nothing returns{:ok, 0}, a no-op rather than an error. A cancel racing execution resolves to whichever transition commits first: a job already in a terminal state keeps its outcome and is not counted.- Fired timer jobs deliver:
StatifierOban.Timer.Workerfeeds the stored event back through aStatifierOban.Timer.Deliverymodule, behind the run-liveness check st-ADR-0054 decision 4 requires - a run that is not live discards the event (spec 6.2), recorded on the cancelled job as{:discarded, reason}. The default (StatifierOban.Timer.Delivery.Session, configurable viaStatifierOban.Config's:deliveryoption) checks a liveStatifier.Sessionin two steps - registry lookup, thenstatus/1- so a halted-but-alive session discards rather than queueing. - An Oban-backed invoke handler base:
use StatifierOban.Invoke.Handlerimplements statifier'sStatifier.Invoke.Handlerbehaviour (st-ADR-0051) with pure planning callbacks whoseperform/2inserts oneStatifierOban.Invoke.Workerjob - unique on{scope, invoke_id}over every state, so an at-least-once replay conflicts instead of duplicating - into the host's Oban instance. Theuse-ing module suppliesconfig/0andrun/1; the worker runsrun/1inside the job and delivers{:ok, donedata}back into the run asdone.invoke.<invoke_id>(the session runs<finalize>off the arriving event). Exiting the invoking state cancels the stored job through the same handler. StatifierOban.Invoke.Delivery, the seam a completed invoke'sdone.invokegoes back through - the same run-liveness shape asStatifierOban.Timer.Delivery: a completed invoke against a dead or halted run is discarded the way a fired timer is, recorded on the cancelled job as{:discarded, reason}. The default (StatifierOban.Invoke.Delivery.Session) delivers throughStatifier.Session.done_invocation/3behind the two-step liveness check.