packages feed

keiro-dsl-0.4.0.1: 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.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.