packages feed

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.