keiro-0.2.0.0: CHANGELOG.md
# Changelog
All notable changes to the `keiro` library are recorded here. The format follows
[Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
the [Haskell Package Versioning Policy](https://pvp.haskell.org/).
## [Unreleased]
_No unreleased changes._
## 0.2.0.0 — 2026-07-13
### Breaking Changes
- **Keiro's framework tables moved out of the `kiroku` schema into a dedicated
`keiro` PostgreSQL schema.** Every runtime query is now fully qualified
(`keiro.keiro_snapshots`, `keiro.keiro_timers`, `keiro.keiro_outbox`, …) and no
longer depends on `search_path`. Existing databases must run the
`keiro-migrations` bootstrap that creates the schema and relocates the tables;
application SQL reading bare `keiro_*` relations must be re-qualified.
- `runCommandWithSql`, `runCommandWithSqlEvents`, `runCommandWithProjections`,
and the process-manager/router runners now require `KirokuStoreResource` so
transactional appends can apply Kiroku's configured `enrichEvent` hook. Acquire
the store with `withKirokuStore` and interpret `Store` with `runStoreResource`;
plain `runCommand` is unchanged.
- Read-model queries no longer auto-register missing registry rows. Applications
must call `registerReadModel` at projection startup; unknown models now return
`ReadModelUnregistered` without mutating the registry.
- `ReadModel` now requires a `strongScope :: StrongScope` field. Use `EntireLog`
for all-stream subscriptions or `CategoryHead category` for a category
subscription, so that unrelated traffic cannot hold `Strong` reads behind.
- `ReadModel` now also requires a `schema :: Text` field naming the PostgreSQL
schema its data table lives in. Keiro does not rewrite `query`; qualify the
application's SQL with `qualifiedTableName` or `Keiro.Connection.qualifyTable`.
The field is Haskell-level wiring only and is deliberately not persisted in the
`keiro.keiro_read_models` registry.
- `AsyncProjection` now requires `readModelName`, naming the registry row that
fences writes during a rebuild.
- `applyAsyncProjection` now returns `AsyncApplyOutcome` (`AsyncApplied`,
`AsyncDuplicate`, or `AsyncFenced`) and live workers must not checkpoint a
fenced event. Rebuild replayers use `applyAsyncProjectionUnfenced` between the
new atomic `startRebuild` and guarded `finishRebuild` helpers.
- Router deterministic command ids are now derived from the resolved target
stream name and same-stream occurrence rather than the target's list position
(`deterministicRouterCommandId`). A transition point-probe recognizes legacy
positional ids for stable resolver output and may be removed in a later
release. If both the deployment version and resolver output change between
attempts, a target command may be dispatched at most one extra time across that
one-time upgrade window.
- `runWorkflow`, `runWorkflowWith`, and the child-workflow runtime now require
`Error StoreError` in their effect rows, so post-commit workflow snapshot
failures can be caught without leaving the typed error channel.
- `HydrationReplayFailed` now carries a typed `HydrationReplayReason` alongside
the failing stream version. The reasons distinguish no inverting edge, ambiguous
inversion, queue mismatch, and a truncated multi-event chain. `CommandError`
also gains `CommandAmbiguous` (matched edge indices) and `HydrationGapDetected`
(expected and observed stream versions).
- A command matching multiple transitions is now reported as `CommandAmbiguous`
instead of `CommandRejected`. Process managers and routers halt on this
aggregate-definition bug, while generated timer dispositions route it through
their on-error arm rather than benign on-reject handling.
- `PMCommandResult.PMCommandFailed` now carries the target `StreamName` alongside
its `CommandError`, so worker policy can identify the failing target.
- `WorkerOptions` gains a `rejectedCommandPolicy :: RejectedCommandPolicy` field,
and `ShardedWorkerOptions` gains `handlerRetryDelay :: RetryDelay` and
`retryPolicy :: RetryPolicy`. Record construction must supply them; the
`defaultWorkerOptions` and `defaultShardedWorkerOptions` defaults are unchanged
in behavior except as noted below.
- The `hydration_replay_failed` telemetry class is replaced by four
reason-specific `error.type` values, and `command_ambiguous` is new. Dashboards
keyed on the old hydration class must be updated.
- Stream validation is stricter through the re-exported `keiro-core` contracts.
Keiki 0.2's head-recoverability, inversion-ambiguity, unguarded-input-read, and
state-changing-silent-edge checks now reject a stream at `mkEventStream`, and
`mkEventStreamWith` can no longer disable the head-recoverability or
state-changing-epsilon checks. Snapshot-enabled streams whose codec cannot
encode their initial state and registers are rejected at startup. `keiro` now
requires `keiki >=0.2`, `keiki-codec-json >=0.2`, and `kiroku-store >=0.3`.
### New Features
- New `Keiro.DeadLetter`, `Keiro.DeadLetter.Schema`, and `Keiro.DeadLetter.Replay`
modules. Process-manager and router workers can now park a rejected dispatch
instead of halting: `RejectedCommandPolicy` selects `RejectedHalt` (the
default), `RejectedDeadLetter` (persist a `DispatchDeadLetter` in
`keiro.keiro_dead_letters` and acknowledge), or `RejectedSkip` (acknowledge and
count). `recordDispatchDeadLetter` is idempotent under source-event redelivery,
and `listDispatchDeadLetters` reads one dispatcher's witnesses newest-first.
`replaySubscriptionDeadLetters` re-runs a caller-supplied handler over the rows
Kiroku parked in `kiroku.dead_letters`, reporting `ReplayedFresh`,
`ReplayedDuplicate`, `ReplayFailed`, or `ReplaySourceMissing` without deleting
or mutating the Kiroku-owned rows.
- New `Keiro.Connection` module for application read-model and projection tables:
`qualifyTable`, `quoteIdentifier`, `withProjectionSchema`,
`keiroConnectionSettings`, and the opt-in `ensureProjectionSchema`. The store
connection's `schema` stays `kiroku` because it drives the `LISTEN`/`NOTIFY`
channel; a projection schema is reached by qualification and/or
`extraSearchPath`. `ReadModel.qualifiedTableName` builds the model's
`"schema"."table"` reference.
- `Keiro.Subscription.Shard.Worker` gains an acknowledgement-aware surface:
`runShardedSubscriptionGroupAck` with `ShardAck`, `ShardDelivery`, and
`ShardEventHandler`, plus per-event retry and dead-letter dispositions.
`runShardedSubscriptionGroup` remains as the `RecordedEvent -> IO ()`
compatibility wrapper.
- Added `keiro.dispatch.deadlettered` and `keiro.subscription.deadlettered`
counters. `Keiro.Telemetry.kirokuEventBridge` installs on Kiroku's
`eventHandler` to observe `KirokuEventSubscriptionDeadLettered`, the terminal
retry-exhaustion signal, and delegates every event to the application's handler.
- Added `keiro.snapshot.encode.failures`, `keiro.snapshot.decode.failures`,
`keiro.snapshot.read.hits`, `keiro.snapshot.read.misses`, and
`keiro.snapshot.apply.divergence`. Snapshot lookup APIs (`lookupSnapshotSeed`,
`SnapshotLookup`, `SnapshotMissReason`, `encodeSnapshotStrict`,
`writeSnapshotEncoded`) now retain miss and decode reasons, while compatibility
wrappers preserve the previous `Maybe` surface.
- `commandErrorClass` exposes a stable error-class string for a `CommandError`.
`isRejectionClass`, `decideForFailures`, `DispatchFailure`, and
`confirmBenignDuplicate` are exported from `Keiro.ProcessManager` and
`Keiro.Router` so that custom workers can reuse the runtime's acknowledgement
classification. `ReadModel.categoryHeadPosition` reads the latest global
position originating in a Kiroku category.
### Bug Fixes
- Command hydration now detects stream-version gaps caused by Kiroku per-stream
truncation, and returns `HydrationGapDetected` unless a snapshot covers the
hidden prefix.
- Transactional command runners now apply Kiroku's configured `enrichEvent` hook
before event preparation, so persisted events and the `runCommandWithSqlEvents`
callback observe the same enriched metadata as plain `runCommand`.
- Router and process-manager duplicate-event rejections are now confirmed against
the intended target stream before being treated as benign. Unconfirmed
cross-stream or id-less collisions surface as command failures, causing workers
to halt instead of silently dropping a dispatch.
- Sharded subscription readers now acknowledge each event only after its handler
returns, using Kiroku's acknowledgement bridge. A shed or rebalanced bucket's
checkpoint can no longer cover an unprocessed event, and a synchronous handler
exception is retried in place under `retryPolicy` before Kiroku dead-letters the
event; asynchronous exceptions write no acknowledgement, and the event is
redelivered by the next owner.
- No-op commands now report `CommandResult.globalPosition = Nothing` instead of
exposing Kiroku's per-stream-read sentinel `GlobalPosition 0`. Appended commands
continue to report the real store-assigned position.
### Other Changes
- `RunCommandOptions.verifyReplayOnAppend` defaults on. Both command append paths
replay each just-committed batch from the pre-command state, count an
unreplayable batch through `keiro.snapshot.apply.divergence`, and attach a
bounded typed reason to `keiro.replay.divergence` without turning an
already-committed command into a reported failure.
- Aggregate snapshot encoding is forced before the store write. An `ErrorCall`
from a partial state encoder or an uninitialized register is swallowed after the
event append and counted, instead of escaping a successful command.
- Workflow snapshot writes after steps, completion, and continue-as-new rotation
are advisory: store failures are swallowed and counted after the journal append
commits.
- Corrected snapshot documentation: version non-regression applies within one
codec version and shape hash, while an incompatible codec can replace a newer
row to permit rollback. Upgrade notes cover the full-replay miss caused by
Keiki EP-78's stable shape hash.
- Documented previously implicit runtime contracts: the inbox deduplication window
closes when `garbageCollectCompleted` removes a completed row; outbox
`created_at` is transaction-start time, so `PerKeyHeadOfLine` and
`PerSourceStream` ordering is best-effort unless the caller serializes same-key
enqueues; the default timer worker has no attempt ceiling and requeues claims
left `Firing` for five minutes; and process-manager `correlate` joins across
streams must be order-insensitive.
- The shared PostgreSQL test fixture now provisions templates through the native
Kiroku/Keiro migration plan. Codd transition and remediation tests are retained
behind the manual `legacy-codd-tools` flag.
- Kiroku 0.3, Keiki 0.2, and pg-migrate 1.0 now resolve from Hackage; their
obsolete Git package overrides and the local Cabal overlay are no longer needed.
## 0.1.0.0 — 2026-07-05
The initial Hackage release of `keiro`, an event-sourcing framework and workflow
engine that composes the kiroku event store, the keiki aggregate core, and the
shibuya worker substrate.
### Breaking Changes
- Command-boundary APIs require `ValidatedEventStream` instead of bare
`EventStream`: `runCommand`, `runCommandWithSql`, `runCommandWithSqlEvents`,
`runCommandWithProjections`, `Router.targetEventStream`, and
`ProcessManager.eventStream` / `targetEventStream`. Build stream definitions
with `mkEventStream` or `mkEventStreamOrThrow` before wiring them into runners.
This is source-level only; persisted events, snapshots, stream names, and wire
formats are unchanged.
### New Features
- Command-side write APIs with optimistic concurrency, idempotent event ids,
transactional SQL hooks, inline projections, command metadata, and OpenTelemetry
spans.
- Typed event codecs, schema-version metadata, ordered upcasters, advisory
snapshots, and replay-safe event-stream validation via `Keiro.EventStream.Validate`.
- Read models with consistency modes, rebuild lifecycle support, async projection
deduplication, and strong consistency checks.
- Process managers, routers, database-backed timers, shard workers, and worker
options for retry, halt, duplicate, and poison-message handling.
- Durable integration outbox and idempotent inbox support, including Kafka
adapters, trace propagation, retry accounting, dead-lettering, and maintenance
helpers.
- Durable workflow primitives: journaled named steps, sleep, await/signal,
child workflows, continue-as-new, patching, push wake signals, resume workers,
instance leasing, garbage collection, snapshots, and workflow telemetry.
- Metrics surfaces for command dispatch, projections, timers, outbox/inbox
workers, and workflow execution.
### Bug Fixes
- Hardened command retry, snapshot boundary handling, workflow crash windows,
process-manager/router ack finalization, timer requeueing, shard reader
recovery, async projection deduplication, and inbox/outbox recovery paths.
- Fixed duplicate-event classification, outbox publish grouping, no-op late
failure marks, and idempotent runtime release paths.
### Other Changes
- Re-exported the shared contracts from `keiro-core`, including codecs, streams,
event streams, validation, integration events, and snapshot policies.
- Added user guides, migration notes, Haddock coverage, API references, and
guide-backed examples for command-side usage, event evolution, snapshots,
read models, process managers, routers, integration events, and durable
workflows.