# Changelog
## 0.2.0
First public release on Hex.
`0.2.0` is a clean rewrite. The pre-release `0.1.0` SDK was never published;
the API is intentionally not backwards-compatible with that version.
### Programming model
Workflows read top-to-bottom as sequential code. Concurrency is explicit and
structured — the only ways to introduce it are `API.receive/2` (a message
loop with signal/update handlers) and `API.parallel/1` (concurrent fan-out).
Every spawned handler must complete before the receive returns.
### Features
- `Temporalex.Workflow` — workflow modules with `run/1` and `handle_query/3`.
- `Temporalex.Activity` — `defactivity` macro for both regular and
`local: true` activities. Local activities are durable across worker
crashes (recorded in workflow history).
- `Temporalex.Workflow.API`:
- Sequential primitives: `execute_activity/3`, `execute_local_activity/3`,
`sleep/1`, `wait_for_signal/1`, `publish_state/1`, `patched?/1`,
`cancelled?/0`.
- Structured concurrency: `receive/2` with signal/update handlers and an
optional `:timeout`; `parallel/1` for fan-out.
- Async-only: `update_state/1` for atomic mutations from inside an
`{:async, fn, _}` handler.
- `Temporalex.Worker` — supervisor for one task queue. Drop into an OTP
supervision tree.
- `Temporalex.Client` — start, signal, query, cancel workflows from
outside workflow code.
- `Temporalex.Testing` — step-by-step test driver. The same call protocol
as the production executor, so workflow code is unchanged between
tests and real runs.
- ETF as the default payload encoding (preserves full Elixir type fidelity).
### Robustness
This release shipped after several rounds of bug-hunting. Notable fixes
worth knowing about:
- **Updates respond end-to-end.** Update handler return values are now
emitted as `UpdateResponse` commands (Accepted → Completed / Rejected).
Without this, the Temporal Update API caller would hang until update
timeout.
- **Multi-signal-in-one-activation.** When Temporal delivers several
signals in a single activation, all handlers run in dispatch order and
every state mutation lands.
- **Cancelled activities and child workflows replay correctly.** Replay
log builds entries for `:cancelled` resolutions; runtime apply path
handles them. Local-activity backoffs are filtered from the replay log.
- **Receive timer / handler stop race.** Timer fires while a sync handler
is in flight; first stop wins, executor doesn't crash on a cleared
`receive_from`.
- **Heartbeat details encode as a Payload.** Details now round-trip
through Temporal history with metadata intact, instead of being sent
as raw ETF bytes.
- **Bounded buffers.** `signal_buffer` and `pending_handler_queue` are
capped (defaults: 10_000 each, configurable per executor). Floods drop
oldest with a warning; updates beyond the queue cap surface a
`:rejected` response instead of hanging.
- **Worker shutdown awaits drain.** `terminate/2` calls the async
`shutdown_worker` NIF and waits up to 30s (configurable) for Core to
finish in-flight activations before returning.
- **Server crash takes the worker tree with it.** Worker supervisor uses
`:one_for_all` so a Server crash doesn't leave executors orphaned with
stale `WorkerResource` references.
- **Query and validator handlers are crash-protected.** A throwing or
exiting query/validator no longer kills the executor — surfaces as
`{:error, _}` to the caller.
- **Native command encoding propagates errors.** Malformed commands and
malformed payloads inside command lists now surface as `{:error, _}`
from `encode_workflow_completion` instead of silently disappearing.
### Status
Suitable for evaluation and small-scale production. 303 unit tests pass.
The API surface in `Temporalex.Workflow.API` is stable for `0.2.x`;
lower-level modules (`Worker.Server`, `Worker.Executor`, `Native`) are
public for testing hooks but not part of the API contract — those will
likely change.
### Known limitations
- **Update results aren't readable via the `temporal` CLI.** Workflow
payloads default to `binary/etf` encoding (full Elixir term fidelity).
The CLI's update-execute can't render binary/etf responses — it
errors with "payload encoding is not supported" when displaying. The
update itself executes correctly; only the CLI display is affected.
Two ways to drive updates without the CLI today: from another worker,
or from external code that reads ETF directly. A
`Temporalex.Client.update_workflow` (matching `start_workflow`,
`signal_workflow`) is the planned 0.2.1 fix.
### Wire-protocol invariant
Each workflow activation must produce exactly one
`complete_workflow_activation` call. The unit test harness
`Temporalex.Test.ExecutorHelpers.assert_one_flush_per_activation`
validates this without needing a Temporal server. Tests under
`test/temporalex/worker_executor_test.exs` exercise the harness for
sync update handlers and update handlers that park on activities.