packages feed

keiro-dsl-0.6.0.0: CHANGELOG.md

# Changelog

All notable changes to `keiro-dsl` are recorded here. The format follows
[Keep a Changelog](https://keepachangelog.com/), and the package follows the
[Haskell Package Versioning Policy](https://pvp.haskell.org/).

## [Unreleased]

## 0.6.0.0 — 2026-07-31

### Breaking Changes

- `DiagnosticCode` gains `AggregateTypeUnknown`,
  `AggregateTypeUnsupportedAtUse`, `AggregateRegisterInitialInvalid`,
  `AggregateGuardTypeMismatch`, `AggregateGuardCapabilityUnsupported`,
  `WorkspaceLanguageVersionMismatch`, and `SourceLanguageDeclarationChanged`.
  The additions are append-only, but exhaustive matches must be extended.
- `DiagnosticCode` also gains the nominal-check codes
  `NominalMissingIngredient`, `NominalInvalidHaskellSource`,
  `NominalInvalidQualifiedName`, `NominalInvalidIdentity`,
  `NominalInvalidIdPrefix`, `NominalUnsupportedRepresentation`,
  `NominalEmptyEnumRepresentation`, `NominalMissingInitialValue`, and
  `NominalNameCollision`, plus the nominal diff codes
  `NominalBindingChanged`, `NominalFixturesChanged`,
  `NominalCanonicalTypeChanged`, `NominalInitialChanged`,
  `NominalRepresentationChanged`, and `NominalIdDecoderTightened`.
- `DiagnosticCode` gains `AggregateExpressionRootUnknown`,
  `AggregateExpressionRootAmbiguous`, `AggregateExpressionPathInvalid`,
  `AggregateExpressionPathUnsupported`, `AggregateExpressionLiteralNeedsType`,
  `AggregateExpressionLiteralInvalid`,
  `AggregateExpressionOperandTypeMismatch`,
  `AggregateExpressionOperatorUnsupported`,
  `AggregateExpressionBooleanRequired`,
  `AggregateExpressionGuardBoolRequired`,
  `AggregateExpressionWriteTargetUnknown`,
  `AggregateExpressionWriteTypeMismatch`,
  `AggregateTransitionOwnershipConflict`, and
  `CollectionExpressionUnsupported`. The additions are append-only, but
  exhaustive matches must be extended.
- The public expression AST gains located arithmetic, scalar literals, explicit
  roots/paths, and transition implementation ownership. Exhaustive matches over
  `Expr`, `TypeExpr`, or transition implementation must be extended.
- Aggregate command/event fields now use the located `AggregateField` type and
  aggregate register types use `TypeExpr` instead of a raw `Name`. The library
  requires the exact-Integer/total-Natural `keiki >=0.6 && <0.7` and
  `keiki-codec-json >=0.6 && <0.7` releases.

### New Features

- Adds language version 2 syntax for binding direct aggregate IDs, enums, and
  nominal scalar wrappers to consumer-owned Haskell types. Version 1 and
  legacy-unversioned sources reject these declarations at source-language
  dispatch. The `pretty` command is an explicit alias for canonical parsing and
  rendering.
- Adds the public total `Keiro.Codec.Nominal` binding/fixture API, a checked
  nominal registry, generated prefix-safe `KindID` codecs, closed private enum
  representations, built-in scalar codecs, create-once binding skeletons, and
  context-level nominal scalar projections. Consumer-owned registers retain
  the existing consumer-JSON snapshot cache boundary.
- Nominal consumer provenance is fingerprinted and diff-visible, persisted in
  additive `nominal-mapping` scaffold/workspace rows, and included in generated
  package requirements. Bound-ID adoption at an existing event use reports a
  named decoder-tightening historical-read advisory and targeted replay audit.
- Adds a compiled nominal conformance ring with binding laws, pinned wire bytes,
  malformed/wrong-prefix ID and unknown-enum rejection, snapshot and canonical
  identity checks, projection agreement, forward/replay parity, mutation gates,
  and a compile-fail partial-inverse fixture.
- Adds authoritative version-2 scalar aggregate expressions. Guards and writes
  accept typed `reg.`/`cmd.` roots, required structural scalar paths, all scalar
  literal families, exact `Integer` `+`/`-`/`*`, and total `Natural`
  `+`/monus/`*`. Machine-`Int` arithmetic, coercion, division/remainder, Time
  arithmetic, predicate-valued Bool writes, and collection expressions fail
  before scaffolding.
- Adds generated per-aggregate `Expressions` and `Transducer` modules. Every
  version-2 transition is exclusively generated-owned or explicitly
  `implementation hole`; Hole transitions retain a generated structural
  envelope, require a per-transition `FoldVersion`, and expose conservative
  ownership/predicate-verification reports where opaque terms remain
  unverified. Version-1 generated output remains frozen.
- Adds a compiled scalar-expression conformance service with a 360-case oracle,
  concrete/symbolic/replay/snapshot agreement, all scalar literal families,
  Natural `2 - 5 = 0`, required structural projection identity, and eight
  mutation sentinels for arithmetic, authority, envelope, fold-version, and
  verification drift.

- Adds an explicit source-language contract. A first-significant-clause
  `language keiro-dsl 1` preamble selects the frozen released v1 parser before
  body parsing; unsupported future versions fail at that boundary. Unversioned
  input remains readable as `legacy-unversioned` with effective version 1 and
  is never silently rewritten by parse/pretty.
- Adds `Keiro.Dsl.LanguageVersion`, provenance-preserving parsing/rendering,
  `keiro-dsl inspect FILE --format=json`, and canonical per-member workspace
  inspection. Workspace composition compares effective versions before graph
  merge while keeping the merged `Spec` semantic.
- Single-file and workspace scaffold records now carry additive source-language
  rows, report provenance drift, and interpret missing rows from older records
  as legacy. Source-aware diff reports declaration-only changes with an
  all-compatible vector and no-semantic-action remedy; generated bytes, fold
  fingerprints, and replay impact remain unchanged.
- Every `new <kind>` skeleton and ordinary checked-in `.keiro` fixture now
  declares version 1. Dedicated legacy, future, malformed, zero, duplicate, and
  misplaced fixtures preserve compatibility and rejection coverage. Automated
  upgrades remain deferred to IR-5.
- Adds `Keiro.Dsl.AggregateType`, the single resolution and capability policy
  used by aggregate validation, Haskell lowering, imports, packages, JSON and
  snapshot samples, fold fingerprints, diffs, replay impact, and scaffold
  refusals.
- Direct aggregate `Time` and `Natural` fields and registers now check,
  scaffold, compile, encode, snapshot, and replay. `Time`/`UTCTime` normalize
  to `Time` and lower to exact `UTCTime` constructors; Natural accepts only
  non-negative integral initials. Equality and ordering are checked against
  Keiki's released symbolic capabilities. Version 1 retains the original
  no-arithmetic grammar; version 2 adds only the exact/total arithmetic listed
  above.
- Direct aggregate `Json` and container shapes now parse far enough to receive
  a located remediation toward `mapped structural`, and malformed scalar
  initials, mismatched comparisons, and unsupported ordering fail during
  `check`. Generated imports and Cabal dependencies remain type-directed and
  minimal.

## 0.5.0.0 — 2026-07-31

### Breaking Changes

- `DiagnosticCode` gains nine constructors:
  `WorkspaceMemberUnreadable`, `WorkspaceMemberParseFailed`,
  `WorkspaceContextMismatch`, `WorkspaceAuthorityConflict`,
  `WorkspaceDuplicateDeclaration`, `WorkspaceDuplicateNodeName`,
  `WorkspacePathCollision`, `OwnershipMoved`, and `WorkspaceAuthorityChanged`.
  Additions are append-only, but exhaustive matches over the type must be
  extended.
- `Keiro.Dsl.ScaffoldRun.Refusal` gains `GoldenRootDivergence`, raised only by
  the workspace path when a golden payload fixture sits beside a member that
  the one workspace golden root lacks.
- `Keiro.Dsl.ScaffoldRun.WriteDisposition` gains `Unchanged`, produced only by
  the workspace write path when a Generated module's bytes already match.
- `Keiro.Dsl.DiffReport.Remedy` gains `RemedyRescaffoldWorkspace`.

No behaviour of the single-file path changed: it keeps its context-keyed
record and manifest names, its report bytes, and its `Overwritten`
disposition, and no existing generated bytes moved.

### New Features

- Adds **service workspaces**: a `.keiro-workspace` manifest names a service and
  lists its member `.keiro` files, and `keiro-dsl check <manifest>` validates
  them as one service contract. Shared ids, enums, rules, and mapped structural
  types resolve once across all members, so an aggregate in one file may use a
  declaration or feed a read model owned by another. A single `.keiro` file is
  unchanged and behaves as a one-member workspace.

  Membership is a set: member paths are normalized and canonically sorted, so
  listing order changes neither the parsed manifest nor any output. Composition
  refuses, before producing a graph, when members declare different contexts,
  when a member's `module`/`layout` clause contradicts the manifest authority,
  when a shared declaration or node is owned by two members (identical
  duplicates never silently merge), or when two members claim generated module
  paths that collide under case folding. One diagnostic can cite several files:
  the primary location keeps the established
  `<file>:<line>: error[<Code>]: <message>` shape and each further location
  follows as an indented `note:` line.

  `check --emit`, `--explain-bindings`, `--coverage-report`, and
  `--fail-on-opaque` all work against the merged whole-service graph, and
  `keiro-dsl parse <manifest>` round-trips the manifest canonically.
  Dispatch is by file extension, so every single-file branch is unchanged.

  New `Keiro.Dsl.Workspace` module; `Keiro.Dsl.Validate.nodeIdentity` is now
  exported. Recorded as ADR-14.

- **Whole-workspace scaffolding.** `keiro-dsl scaffold <manifest> --out DIR`
  emits the complete generated module set for every member in one invocation,
  with unchanged flags. The set is emitted once from the merged spec, so the
  structural projection facade and the replay-audit assembly are produced
  exactly once from the complete graph, and every existing refusal gate runs
  over the whole set. Both preflights — golden fixtures stranded beside a
  member, and Generated paths lacking the `@generated` banner — are evaluated
  across the whole workspace before the output directory is created, so a
  failure in any member leaves the tree, the record, and the build manifest
  byte-for-byte untouched.

  History is workspace-keyed (`workspace.<service>`, a name a context can never
  collide with), and each record row carries its producing member, so an
  aggregate moved between member files is reported as an ownership move with
  zero stale churn and zero content change. A Generated module whose bytes
  already match is reported `(unchanged)` rather than rewritten, making
  idempotence observable. Ownership is attributed structurally through the new
  `scaffoldStructuralOwners` and `bindingSkeletonOwners` seams in
  `Keiro.Dsl.Scaffold`, never by parsing the human-readable `origin` string.

  New `Keiro.Dsl.WorkspaceRecord` and `Keiro.Dsl.WorkspaceScaffold` modules;
  `Keiro.Dsl.ScaffoldRun` now exports the gates and helpers the workspace path
  reuses (`pureRefusals`, `missingGeneratedBanners`, `staleAgainst`,
  `constraintPlan`, `mappingDrift`, `newBindingObligations`,
  `obligationKindLabel`, `renderMappingIdentity`) so the two paths cannot
  diverge on what counts as a refusal.

- **Adoption of pre-workspace output.** The first whole-workspace scaffold into
  a directory that already holds per-context output imports what is
  attributable, reports everything, and claims nothing silently. A file is
  claimed only with evidence — `record` when a legacy per-context record for
  this workspace's effective context lists it, or `banner` when it sits at a
  planned Generated path carrying the `@generated` banner while no surviving
  record lists it. Hole paths are never claimed; everything else is listed as
  unclaimed and left untouched, and a bannerless file at a planned Generated
  path still refuses the run. Nothing is deleted or renamed: the superseded
  legacy record gains one appended `superseded-by:` line that its own v1 parser
  ignores. The migration report is persisted once as
  `keiro-dsl-migration-report.workspace.<service>.txt`. New
  `Keiro.Dsl.WorkspaceAdoption` module; recorded as ADR-15.

- **Whole-workspace diffing.** `keiro-dsl diff <manifest> --since <rev>`
  composes the historical workspace from the manifest and member blobs at that
  revision through the same loader and diffs it against the working tree as one
  service. Membership deltas are covered, and when the manifest itself does not
  exist at the old revision an adoption baseline is composed from the current
  membership and flagged in the report.

  Findings from the merged graph are annotated with the member file and line
  that own the changed declaration and each use site, so a shared declaration's
  blast radius is visible across files. The report carries workspace metadata
  (service identity, manifest, `since`, old and new membership, adoption-baseline
  flag), and replay-impact and coverage output are merged across members. Owner-map
  and manifest-authority changes are reported as the non-blocking consumer-build
  advisories `OwnershipMoved` and `WorkspaceAuthorityChanged`, classified
  independently of wire evolution so they never mask a real wire finding.

  The `keiro-dsl/diff-report/1` JSON schema is preserved: single-file reports
  keep their original bytes, and workspace inputs add a top-level `workspace`
  object plus optional per-finding `declaration` and `useSites` keys, which
  version-1 readers ignore. New `Keiro.Dsl.WorkspaceDiff` module; new
  `Keiro.Dsl.DiffReport` exports `OwnedSite`, `WorkspaceChange`,
  `WorkspaceMeta`, `WorkspaceDiffReport`, and `workspaceDiffReport`. ADR-4 is
  amended with the composed-workspace diff boundary.

## 0.4.0.1 — 2026-07-28

### Other Changes

- Adds PVP upper bounds to every dependency that previously carried a lower
  bound only, so `cabal check` reports no packaging warnings. No API or
  behaviour change from 0.4.0.0, which was tagged but never published.


## 0.4.0.0 — 2026-07-28

### New Features

- Structural scaffolding now creates hand-owned binding/fixture/initial
  skeletons grouped by their declared owner modules, persists granular hole
  obligations for non-overwriting re-scaffold reports, derives `Generic` for
  private shape types, and provides `check --explain-bindings` for deterministic
  package/module/signature/use-site reports.

- Adds `Keiro.Dsl.CodecCompare`, a historical-codec comparison engine that
  classifies RFC 8785 canonical-JSON parity between a declared codec and a
  historical one, reports structured migration differences and declared-versus-
  observed branch coverage gaps, and writes stable reports atomically.
  `scaffold --codec-comparison MAPPED-NAME --comparison-out FILE` emits a
  non-production comparison module and runner for one structural mapped type.

- Adds `Keiro.Dsl.Coverage` and reporting-only `--coverage-report FILE` on both
  `check` and `diff`, recording structural and opaque mapped-root coverage as
  JSON. `check --fail-on-opaque` fails when a private persisted root still
  contains an opaque boundary; `diff --fail-on-opaque-increase` fails when a
  change adds a named opaque boundary. Without the flags, coverage is purely
  informational.

- Adds checked `mapped structural` and `mapped opaque` declarations with a
  resolved, total type-expression graph. Validation rejects recursive,
  ambiguous, non-injective, or incomplete mappings; recursive diff findings
  carry six-surface compatibility vectors and complete command/event/register
  use-site paths.

- Structural consumer-type scaffolding now emits private
  `Generated.<Context>.Structural.Shape.*` wire representations, generates
  structural codecs from declared keys/defaults/tags, delegates opaque values
  only at their declared JSON boundary, imports consumer types into aggregate
  domains, and exposes eligible total scalar getters through a narrow Keiki
  0.4 `StructuralProjections` witness facade.
- Scaffold preflight now reports consumer packages/modules, refuses consumer
  modules inside the generated namespace before writing, includes consumer
  requirements in manifests, persists canonical structural/opaque mapping
  identities as forward-compatible JSON rows, and reports mapping drift on
  subsequent runs. Mapped register wire/binding/initial identities now
  participate in the aggregate fold fingerprint.
- Generated harnesses exercise both structural binding laws, declared codec
  policy and current payload goldens, enum/union/optional fixture coverage,
  canonical projection witness agreement, and forward-versus-replay equality
  over every mapped and scalar register. The committed structural conformance
  suite includes opaque-boundary checks and three falsifying mutations.
- `Keiro.Dsl.ReplayImpact` and `diff --replay-impact-out FILE`. Diffs now
  print whether stored-data replay is unchanged and can emit a stable JSON
  affected set for targeted auditing. New aggregates, events, transitions,
  and syntactically proven guard loosenings are replay-neutral; changed
  decode/fold surfaces identify conservative event types and whether
  snapshot-bearing streams must be included.
- Scaffolding now emits one context-wide
  `Generated.<Context>.ReplayAudit` module. Its typed target list includes
  every aggregate, validates discovered stream names against the generated
  category, and documents the replay-neutral/targeted/full deployment tiers.
- `diff` now emits non-breaking, coded advisories when a router resolve or
  dispatch surface, process handle surface, or unversioned process-timer
  payload changes. The advisories explain the subscription drain and
  dead-letter procedure needed to avoid mixed old/new deterministic fan-out
  during a deployment.

- First-class replay-only transitions for guard evolution (plan 143). A
  `replay-only` prefix on a transition line marks it as serving inversion
  only: the parser accepts it, the pretty-printer round-trips it, and the
  scaffolder lowers it to `B.replayOnly` (keiki's `ReplayOnly` edge mode) in
  the transducer skeleton. New validator rules: `ReplayOnlyEmitsNothing`
  (error — a replay-only transition with no emit can invert nothing) and
  `ReplayOnlyCommandStillLive` (warning — no live sibling for the (source,
  command) pair; the fuller procedure is event retirement). A deprecated
  event may keep being emitted by a replay-only transition — replay-only
  transitions are not the write path.
- `diff` computes the guard-tightening remedy (`AggGuardTightened`
  advisory): on any live-transition guard change without a replay-only twin,
  it prints a paste-ready `replay-only` twin whose guard is the removed
  region `old ∧ ¬new`, negation eliminated inside the guard grammar by the
  new total `Keiro.Dsl.Grammar.complementExpr` (De Morgan, comparison
  flipping, `x == false` for bare boolean atoms). The twin carries the old
  transition's writes/emits/goto and re-parses as-is; it is printed, never
  auto-applied.
- `Keiro.Dsl.PrettyPrint.renderTransition` renders one transition in
  concrete `.keiro` syntax (used by the advisory).
- Requires `keiki >=0.4 && <0.5`, including `EdgeMode` and the typed structural
  projection contracts.

## 0.3.0.0 — 2026-07-14

No user-facing changes. `keiro-dsl` is released at 0.3.0.0 to stay in lockstep
with the rest of the Keiro package set.

## 0.2.0.0 — 2026-07-13

### Breaking Changes

- The process `saga` clause is now `saga <Aggregate> category "<camelCase>"`,
  replacing `saga <Aggregate> stream="<prefix>-" <> correlationId`. Categories are
  validated (`SagaCategoryIllegal`) against the same rules as
  `Keiro.Stream.category`: non-empty, not `$all`, no `-` (Kiroku's category/id
  boundary), no whitespace or control characters, and no `:` (reserved for the
  `wf:<name>` workflow stream family). Generated process and router fills now
  build saga streams with `entityStream` and a typed category constant instead of
  splicing a string prefix.
- `process` nodes must now declare node-level `rejected => <policy>` and
  `poison => <policy>` clauses (`halt`, `deadLetter`, or `skip`), and every timer
  `fire` disposition must carry an `on-ambiguous` arm alongside `on-ok`,
  `on-reject`, `on-error`, and `not-mine`. Mapping `on-ambiguous => Fired` is
  rejected (`AmbiguousMarkedBenign`): `CommandAmbiguous` means several aggregate
  edges matched, and is never a benign success.
- Identifier lexemes are restricted to ASCII, and the validator now enforces
  per-category Haskell hygiene: constructor-position names must be
  constructor-safe (`IdentNotConstructorSafe`), no identifier may be a Haskell
  keyword (`IdentHaskellKeyword`), and generated vertex constructors may not
  collide (`VertexCtorCollision`). Specs that previously scaffolded to
  uncompilable Haskell are now rejected at `check`.
- All numeric literal sites parse through `Integer` and fail with a positioned
  diagnostic when the value exceeds `maxBound :: Int`, instead of silently
  wrapping. `maxBound` itself still parses exactly.
- Duplicate singleton blocks and duplicate `goto` clauses are reported at their
  second occurrence rather than being silently accepted with a last-one-wins
  reading; a missing `goto` is now anchored to its transition.
- `Text` registers require a quoted initial, and a register whose type is an enum
  or the aggregate's vertex type must start at a declared constructor or state.
  The scaffolder refuses the run rather than emitting a broken initial.
- Validation is substantially stricter, so specs that checked under 0.1.0.0 may
  now be rejected: duplicate node, command, event, enum-constructor, enum-wire and
  id-prefix names; dangling aggregate, projection, timer, workflow, signal and
  operation references; non-total or unresolved `rule` domains; duplicate workflow
  replay labels; incomplete or shadowed disposition tables; duplicate and dangling
  status-map keys; emit/intake topic-affinity mismatch; workqueue
  `queueRef`/table/DLQ divergence; unresolved dispatch dedup queues and fields;
  invalid process timer ceilings; and write targets that are not registers.
- `keiro-dsl scaffold` now plans the entire module set before writing any byte,
  and refuses the run on module-path collisions, self-firewall breaches, or
  unlowerable spec content. It also refuses to overwrite a file on a `Generated`
  path that lacks the `@generated` banner; pass the new
  `--force-generated-overwrite` flag to override.
- `keiro-dsl diff` gained a `WARNING:` tier, and its change lines now read
  `<node> <facet> <subject>: <detail>` rather than always saying `event`. Only
  `BREAKING:` changes exit non-zero.

### New Features

- Added a first-class `readmodel` node declaring `table`, `schema`, a typed
  `columns` block, `version`, captured `shape`, `consistency`, `scope`, `feed`
  (`inline` or `subscription`), and `subscription`. The validator owns a closed
  column-type vocabulary (`text`, `int`, `bigint`, `bool`, `timestamptz`, `jsonb`,
  `numeric`), detects shape-hash drift against the declared columns, rejects
  `Strong` + `inline` (no subscription worker advances the cursor a strong read
  waits on), rejects `scope` without `Strong`, and rejects an `inline` model that
  no aggregate projection feeds. `scaffold` emits `ReadModel` and `ReadModelTable`
  generated modules plus `ReadModelHoles`. Aggregate `projection` consistency is
  now optional and must agree with the read-model node when one exists; a
  projection with no `readmodel` node warns that registration, schema identity,
  and rebuild helpers are unavailable.
- Query operations and PGMQ dispatch source/dedup read-model references now
  actually resolve against declared nodes — these were explicitly deferred no-ops
  in 0.1.0.0. Query `consistency` must be `Strong`, `Eventual`, or `PositionWait`,
  and a dispatch dedup field must be a column of the named model.
- Added the `router` node for stateless content-based routing: `input`, `key`,
  `resolve stable via read-model <name> row { ... }` (or `via hole`), `target`,
  `projections`, `dispatch-each`, a runtime-owned `dispatch-id` strategy keyed on
  target stream name and occurrence, and node-level `rejected`/`poison` policies.
  Validation resolves the target aggregate, its command and command fields, the
  key field, and the read-model reference, and confines dispatch bindings to the
  `input.` and `resolved.` scopes plus quoted literals. `scaffold` emits
  `Router`/`RouterHarness` generated modules with `RouterHoles`/`RouterValue`
  stubs, and `keiro-dsl new router` prints a starter.
- Added node-level `rejected` and `poison` worker policies on `process` and
  `router` nodes, reconciled against the per-dispatch `on-failed` arms: a dispatch
  declaring `DeadLetter` under a node that does not is a `PolicyContradiction`,
  divergent `on-failed` actions within one node are rejected because the runtime
  applies a single `RejectedCommandPolicy` to the whole failure group, and an
  unacknowledged `rejected => deadLetter` warns.
- Added aggregate `snapshot every <n>` / `snapshot on-terminal` with a captured
  `state-codec version=<n> shape-hash="..."` fixture, lowered into the generated
  aggregate modules and pinned against the live runtime. Intervals below 1, and
  empty or zero-version codec fixtures, are rejected.
- Added workqueue `ordering` (`unordered`, `fifo-throughput`, `fifo-round-robin`),
  `group key from <field> via <fn>`, and provisioning (standard, `unlogged`,
  partitioned). The validator rejects FIFO without a group key, a group key
  without FIFO, and an empty partition spec, and flags unlogged durability; the
  generated `QueuePolicy` is pinned to the live pgmq configuration types.
- Added intake `persist = full-envelope | dedupe-only`, lowered to the live
  `InboxPersistence` value and wired through `runInboxTransactionWith`.
- Added a durable-workflow evolution surface: guarded `patch <id> { ... }` blocks
  and a terminal `continueAsNew <SeedType>`, lowered into the workflow run
  options. Duplicate and malformed patch ids, and a non-terminal `continueAsNew`,
  are rejected.
- Added a `partial` token on `status-map`, so an intentionally incomplete
  event-to-status mapping is expressible instead of failing the totality rule.
- Publisher backoff accepts optional `max=` and `multiplier=`, and exponential
  backoff now lowers to `ExponentialBackoff ExponentialBackoffOptions{..}` with
  faithful duration units.
- Rebuilt `diff` on an exhaustive node-family registry: every `Node` constructor
  either has a differ or carries a recorded out-of-scope rationale, so a new node
  kind can no longer be silently treated as safe. Classification now covers
  aggregate decode evolution through command indirection (field type, removal,
  wire spelling, enum add/drop, schema-version regression, old-anchored upcaster
  continuity, undeprecation), contract topic/schema-version/discriminator/field/
  event changes, workqueue payload, identity, ordering and provisioning changes,
  process input and timer-window changes, intake decode posture, dedupe identity
  and persistence, emit mapping and ordering, publisher policy, workflow shape,
  body, patch and continue-as-new seed changes, read-model
  version/shape/feed/consistency evolution, projection and id-prefix re-keying,
  router stable-name changes, and dispatch retargeting.
- `scaffold` now writes a versioned per-context record
  (`keiro-dsl-scaffold-record.<context>.txt`) and reports still-present modules
  made stale by node, layout, or `--module-root` changes. It never deletes
  generated or hand-owned files.
- Diagnostics carry row-level source locations, so validator-owned rows
  (dispositions, status maps, timers, dispatches) are anchored precisely instead
  of at the enclosing node.

### Bug Fixes

- String literals now decode and re-render the closed DSL escape set, so topics,
  emit maps, and quoted field bindings survive a parse/pretty-print round trip
  instead of being corrupted.
- The scaffolder escapes payload literal splices, closing a template-injection
  path where a quoted spec literal could break out of the generated Haskell
  string. Its status-map and harness lowering is now total.
- Generated `Stream` and stream-category imports are emitted in the
  repository-standard post-qualified (`ImportQualifiedPost`) form.
- The self-firewall is derived from Keiki's exported surface as a single
  token-aware scan, rather than a hand-maintained substring list that both missed
  operators and produced false positives.

### Other Changes

- Added conformance suites that round-trip every node family, compile every
  `keiro-dsl new <kind>` starter, cold-start the new read-model, router, snapshot,
  queue-ordering, and workflow-rotation surfaces, and exercise the generated
  read-model and router modules against the live runtime.
- Test fixtures and conformance corpora resolve from either the package root or
  the repository root.

## 0.1.0.0 — 2026-07-05

Initial Hackage release.

### Breaking Changes

- Renamed the typed-spec file extension from `.kdsl` to `.keiro` before the first
  public release.

### New Features

- Added grammar, parser, pretty-printer, validator, diff engine, scaffold
  generator, harness emitter, and CLI for typed `.keiro` service specs.
- Added aggregate, process manager, durable timer, integration contract, inbox,
  publisher, PGMQ workqueue, workflow, and operation nodes.
- Added configurable module placement, build-wiring manifests, self-firewall
  checks, post-scaffold reports, per-iteration ergonomics, and `new <kind>`
  starter skeletons.
- Generated validated event streams compatible with Keiro's command boundaries.

### Bug Fixes

- Tolerated formatter comma style in scaffold conformance tests.