diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,113 @@
 
 ## [Unreleased]
 
+## 0.9.0.0 — 2026-08-02
+
+### Breaking Changes
+
+- Requires `keiki >=0.8 && <0.9`, replacing the previous `>=0.7 && <0.8` bound,
+  in lockstep with `keiro-core` and `keiro`.
+- Aggregate fold fingerprints widen from 16-hex-digit FNV-1a-64 to
+  32-lowercase-hex-digit FNV-1a-128. This intentionally invalidates snapshots
+  produced with the old fold discriminator; re-scaffold generated transducers
+  before deploying `0.9.0.0`. Read-model shape, mapped-wire, and behavior-key
+  64-bit identities are unchanged.
+- Aggregate fold, diff, replay-impact, and workspace-diff service APIs now
+  return `Either FoldSurfaceError`; scaffold planning refuses the same error
+  before module generation. Removes the misleading legacy/version-1
+  `Spec`-only wrappers `aggregateFoldFingerprint`, `aggregateFoldSurface`,
+  `diffSpecs`, `replayImpact`, `nominalEqualityContract`,
+  `nominalEqualityIdentity`, and `nominalEqualityIdentities`. Retain and pass a
+  `CheckedService`, or explicitly use `legacyCheckedService` at a deliberate
+  compatibility boundary.
+- `DiagnosticCode` gains append-only constructors for contract TypeID
+  admission and for closed policy, numeric, duplicate, identity, external-name,
+  intake-coupling, topic-alias, and wire-clause validation. Exhaustive matches
+  over `DiagnosticCode` must be extended. `RolloutConstraint` also gains
+  `RolloutProducerFirst`.
+- Removes the unreachable exported grammar types `Derivation`,
+  `DerivStrategy`, `Disposition`, `DispAction`, `EnvelopeBinding`, and
+  `EnvelopeLayer`. No parser, renderer, validator, or generator constructed
+  them; use the live node-specific derivation, disposition, and intake binding
+  types instead.
+- Workqueue `jobOutcomeFor` functions now accept a generated queue-named closed
+  outcome sum instead of `Text`; inbox generation exposes closed outcome and
+  detailed disposition sums carrying retry delays, dead-letter reasons, and
+  runtime failure detail. Replace string literals and bare acknowledgement
+  matches with the generated constructors.
+- Structural projection witnesses are renamed from hex-mangled identifiers to
+  deterministic owner-and-JSON-pointer names, with a stable suffix only for
+  normalized-name collisions. Re-scaffold and update hand-owned imports and
+  uses; witness values and projection semantics are unchanged.
+- Aggregate stream categories are fixed to their generated event-stream
+  definition phantom. `PMCommand` and router consumers must use the new
+  `<aggregate>CommandCategory`, fixed to the command phantom; generated process
+  categories are fixed to the saga event-stream definition.
+- `workflowFacts` changes from packed `[(String, String)]` values to the
+  generated `WorkflowFacts` record with separate body, await-label, and
+  patch-identifier lists. Honest zero-constructor command/event declarations
+  are now empty datatypes rather than a synthetic `()` constructor.
+
+### New Features
+
+- Runtime semantics now uses private monotone capability profiles rather than
+  consumer-maintained identifier-string lists. Aggregate fold identity and
+  replay transition comparison use a total frozen canonical encoder independent
+  of presentation pretty-printing, and replay pairing of guard-disambiguated
+  sibling transitions is declaration-order invariant. Fold identity applies the
+  standard FNV-1a-128 octet fold to that frozen UTF-8 surface.
+- Adds `language keiro-dsl 4`, selecting syntax profile 2 and
+  `keiro-dsl/runtime-semantics/3`. Relative to language 3, aggregate ID
+  admission, fold fingerprints, and replay classification remain unchanged;
+  versions 1 through 3 retain their released semantics.
+- Designates language 4 as the sole stable authoring contract. `inspect` now
+  reports language 4 as `stable` and versions 1 through 3 as
+  `compatibility-only`; every `new <kind>` skeleton starts at language 4.
+  Historical and unversioned sources remain accepted with their released
+  semantics and are never silently upgraded.
+- Moves the primary conformance baseline to language 4: 226 stable fixtures and
+  30 stable-primary compiled suites are enforced against 14 named source
+  exceptions, two compatibility suites, and one version-independent suite.
+  `keiro-dsl-conformance-contract` now owns the typed TypeID contract proof;
+  the former permissive target is explicitly
+  `keiro-dsl-conformance-contract-v1-compat`, and the redundant
+  `keiro-dsl-conformance-contract-typeid` target is removed.
+- Closes accepted-but-unenforced service surfaces. Values that cannot lower to
+  working generated code are rejected under every language version; language 4
+  additionally enforces numeric floors, duplicate/shadowing rules, stable
+  runtime identity uniqueness, Kafka and PostgreSQL naming, intake envelope
+  and schema coupling, contract topic aliases, and the aggregate wire
+  convention. Emit source/key/discriminant names remain explicitly
+  descriptive-only.
+- Language-4 public contract fields declared as `typeid "inc"` now scaffold as
+  `KindID "inc"`. Generated encoders keep canonical JSON strings, while
+  field-path-aware decoders reject malformed text, the wrong prefix,
+  non-canonical spelling, and non-v7 UUIDs through Keiro's frozen admission
+  policy. Their manifests add `keiro-core` and `mmzk-typeid`; single-file and
+  workspace records persist stable per-field ID-domain identities.
+- Adds service-aware `scaffoldContractForService`,
+  `manifestDependenciesForService`, and `renderManifestForService` entry points.
+  Existing `Spec`-only functions remain legacy/version-1 wrappers, and CLI and
+  workspace semantic routes use the checked service.
+- Diffing an unchanged contract TypeID field from language 3 to language 4 now
+  emits `ContractTypeIdDomainChanged`: public consumers and consumer builds are
+  breaking, with producer-first and drain-required rollout. Producers must emit
+  the frozen TypeID-v7 domain, legacy-invalid in-flight messages must drain or
+  be remediated, and consumers must then be re-scaffolded, recompiled, and run
+  through contract conformance.
+- Generated contract topic constants are exported. Generated contract,
+  aggregate, mapped-enum, nominal, and union decoders now report the rejected
+  value, complete expected set, and Aeson field path without changing accepted
+  wire bytes.
+- Generated files carry the package version, effective language version, and a
+  stable node origin in their provenance banner. Overwrite, stale, and
+  workspace-adoption checks accept both this frozen stamped shape and the
+  historical banner, but reject unrelated marker comments.
+- Regenerated committed fixtures pass the repository Fourmolu configuration;
+  event decode arms render one field per line, contract topics no longer need
+  private-binding warning suppression, and generated workflow facts expose
+  typed list structure.
+
 ## 0.8.0.0 — 2026-08-01
 
 ### Breaking Changes
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -17,6 +17,7 @@
 import Keiro.Dsl.Diff (Change (..), CompatibilitySurface, diffSources, gateWith, gatedBreaking)
 import Keiro.Dsl.DiffReport (diffReport, parseSurfaceName, renderExplainBlock, renderFinding)
 import Keiro.Dsl.ExplainBindings (bindingObligationsForService, renderBindingObligations)
+import Keiro.Dsl.FoldFingerprint (renderFoldSurfaceError)
 import Keiro.Dsl.Goldens (emitGoldenPayloads, loadGoldenPayloads)
 import Keiro.Dsl.Grammar (Placement (..), Spec (..))
 import Keiro.Dsl.LanguageVersion (ParsedSource (..), SourceLanguage, declaredLanguageVersionMaybe, effectiveLanguageVersion, sourceFormText)
@@ -319,19 +320,20 @@
                   newService = checkedSource newSource
                   oldSpec = checkedSpec oldService
                   newSpec = checkedSpec newService
-              written <- maybe (pure []) (\root -> emitGoldenPayloads root oldSpec newSpec) emitGoldensRoot
-              mapM_ (putStrLn . ("golden: wrote synthesized weak stand-in " <>)) written
-              let changes = diffSources oldSource newSource
-                  impact = replayImpactServices oldService newService
-                  effectiveGate = gateWith gatedSurfaces
-              mapM_ (TIO.putStrLn . renderFinding) changes
-              when explain $
-                mapM_ (TIO.putStrLn . renderExplainBlock) (filter shouldExplain changes)
-              TIO.putStrLn (renderReplayImpact impact)
-              mapM_ (`Aeson.encodeFile` impact) replayImpactOut
-              mapM_ (\path -> Aeson.encodeFile path (diffReport effectiveGate changes)) reportOut
-              coverageOk <- runDiffCoverage fp (T.pack ref) oldSpec newSpec coverageOptions
-              if any (gatedBreaking effectiveGate) changes || not coverageOk then exitFailure else pure ()
+              case (,) <$> diffSources oldSource newSource <*> replayImpactServices oldService newService of
+                Left surfaceError -> TIO.hPutStrLn stderr (renderFoldSurfaceError surfaceError) >> exitFailure
+                Right (changes, impact) -> do
+                  written <- maybe (pure []) (\root -> emitGoldenPayloads root oldSpec newSpec) emitGoldensRoot
+                  mapM_ (putStrLn . ("golden: wrote synthesized weak stand-in " <>)) written
+                  let effectiveGate = gateWith gatedSurfaces
+                  mapM_ (TIO.putStrLn . renderFinding) changes
+                  when explain $
+                    mapM_ (TIO.putStrLn . renderExplainBlock) (filter shouldExplain changes)
+                  TIO.putStrLn (renderReplayImpact impact)
+                  mapM_ (`Aeson.encodeFile` impact) replayImpactOut
+                  mapM_ (\path -> Aeson.encodeFile path (diffReport effectiveGate changes)) reportOut
+                  coverageOk <- runDiffCoverage fp (T.pack ref) oldSpec newSpec coverageOptions
+                  if any (gatedBreaking effectiveGate) changes || not coverageOk then exitFailure else pure ()
 
 -- | @parse@ on a workspace manifest: read it, parse it, and print it back in
 -- canonical form (clauses in order, members codepoint-sorted).
@@ -589,29 +591,30 @@
                           oldSpec = checkedSpec oldService
                           newSpec = checkedSpec newService
                           goldenRoot = fmap (workspaceGoldenRoot fp) emitGoldensRoot
-                      written <- maybe (pure []) (\root -> emitGoldenPayloads root oldSpec newSpec) goldenRoot
-                      mapM_ (putStrLn . ("golden: wrote synthesized weak stand-in " <>)) written
-                      let workspaceChanges = diffWorkspaces oldWorkspace newWorkspace
-                          changes = map wcChange workspaceChanges
-                          impact = replayImpactServices oldService newService
-                          effectiveGate = gateWith gatedSurfaces
-                          reportMeta =
-                            WorkspaceMeta
-                              { wmIdentity = wsService newWorkspace,
-                                wmManifest = fp,
-                                wmSince = T.pack ref,
-                                wmMembersOld = map wmPath (wsMembers oldWorkspace),
-                                wmMembersNew = map wmPath (wsMembers newWorkspace),
-                                wmAdoptionBaseline = adoptionBaseline
-                              }
-                      mapM_ (TIO.putStrLn . renderWorkspaceFinding) workspaceChanges
-                      when explain $
-                        mapM_ (TIO.putStrLn . renderExplainBlock) (filter shouldExplain changes)
-                      TIO.putStrLn (renderReplayImpact impact)
-                      mapM_ (`Aeson.encodeFile` impact) replayImpactOut
-                      mapM_ (\path -> Aeson.encodeFile path (workspaceDiffReport reportMeta effectiveGate workspaceChanges)) reportOut
-                      coverageOk <- runDiffCoverage fp (T.pack ref) oldSpec newSpec coverageOptions
-                      if any (gatedBreaking effectiveGate) changes || not coverageOk then exitFailure else pure ()
+                      case (,) <$> diffWorkspaces oldWorkspace newWorkspace <*> replayImpactServices oldService newService of
+                        Left surfaceError -> TIO.hPutStrLn stderr (renderFoldSurfaceError surfaceError) >> exitFailure
+                        Right (workspaceChanges, impact) -> do
+                          written <- maybe (pure []) (\root -> emitGoldenPayloads root oldSpec newSpec) goldenRoot
+                          mapM_ (putStrLn . ("golden: wrote synthesized weak stand-in " <>)) written
+                          let changes = map wcChange workspaceChanges
+                              effectiveGate = gateWith gatedSurfaces
+                              reportMeta =
+                                WorkspaceMeta
+                                  { wmIdentity = wsService newWorkspace,
+                                    wmManifest = fp,
+                                    wmSince = T.pack ref,
+                                    wmMembersOld = map wmPath (wsMembers oldWorkspace),
+                                    wmMembersNew = map wmPath (wsMembers newWorkspace),
+                                    wmAdoptionBaseline = adoptionBaseline
+                                  }
+                          mapM_ (TIO.putStrLn . renderWorkspaceFinding) workspaceChanges
+                          when explain $
+                            mapM_ (TIO.putStrLn . renderExplainBlock) (filter shouldExplain changes)
+                          TIO.putStrLn (renderReplayImpact impact)
+                          mapM_ (`Aeson.encodeFile` impact) replayImpactOut
+                          mapM_ (\path -> Aeson.encodeFile path (workspaceDiffReport reportMeta effectiveGate workspaceChanges)) reportOut
+                          coverageOk <- runDiffCoverage fp (T.pack ref) oldSpec newSpec coverageOptions
+                          if any (gatedBreaking effectiveGate) changes || not coverageOk then exitFailure else pure ()
 
 -- | A @git show@ backed source rooted at a workspace manifest directory.
 gitContentSource :: FilePath -> String -> FilePath -> ContentSource
diff --git a/keiro-dsl.cabal b/keiro-dsl.cabal
--- a/keiro-dsl.cabal
+++ b/keiro-dsl.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            keiro-dsl
-version:         0.8.0.0
+version:         0.9.0.0
 synopsis:        Typed specification toolchain for keiro services
 description:
   keiro-dsl is the toolchain over a typed `.keiro` specification of a keiro
@@ -26,14 +26,17 @@
     DuplicateRecordFields
     ImportQualifiedPost
     LambdaCase
+    OverloadedLabels
     OverloadedStrings
 
 library
   import:          warnings, shared
   hs-source-dirs:  src
+  autogen-modules: Paths_keiro_dsl
   exposed-modules:
     Keiro.Dsl.AggregateType
     Keiro.Dsl.BehaviorCoverage
+    Keiro.Dsl.CanonicalEncoding
     Keiro.Dsl.CodecCompare
     Keiro.Dsl.Coverage
     Keiro.Dsl.Diff
@@ -85,6 +88,7 @@
     Keiro.Dsl.Parser.Queue
     Keiro.Dsl.Parser.ReadModel
     Keiro.Dsl.Parser.Workflow
+    Paths_keiro_dsl
 
   build-depends:
     , aeson               >=2.2.1    && <2.3
@@ -93,8 +97,8 @@
     , containers          >=0.6      && <0.8
     , directory           >=1.3      && <1.4
     , filepath            >=1.4      && <1.6
-    , keiki               >=0.7      && <0.8
-    , keiro-core          ^>=0.8.0.0
+    , keiki               >=0.8      && <0.9
+    , keiro-core          ^>=0.9.0.0
     , megaparsec          >=9.6      && <9.9
     , mmzk-typeid         >=0.7      && <0.8
     , parser-combinators  >=1.3      && <1.4
@@ -117,29 +121,33 @@
     , text                  >=2.1  && <2.2
 
 test-suite keiro-dsl-test
-  import:         warnings, shared
-  type:           exitcode-stdio-1.0
-  hs-source-dirs: test
-  main-is:        Main.hs
+  import:          warnings, shared
+  type:            exitcode-stdio-1.0
+  hs-source-dirs:  test
+  main-is:         Main.hs
+  autogen-modules: Paths_keiro_dsl
   other-modules:
+    Keiro.Dsl.ConformanceBaseline
     Keiro.Dsl.FrontendCompatibility
     Keiro.Dsl.FrontendProfiles
     Keiro.Dsl.FrontendPublicApiProbe
     Keiro.Dsl.FrontendSurface
+    Paths_keiro_dsl
 
   build-depends:
-    , aeson       >=2.2  && <2.3
-    , base        >=4.21 && <5
-    , containers  >=0.6  && <0.8
-    , directory   >=1.3  && <1.4
-    , filepath    >=1.4  && <1.6
-    , hspec       >=2.11
-    , keiki       >=0.7  && <0.8
+    , aeson        >=2.2  && <2.3
+    , base         >=4.21 && <5
+    , containers   >=0.6  && <0.8
+    , directory    >=1.3  && <1.4
+    , filepath     >=1.4  && <1.6
+    , hspec        >=2.11
+    , keiki        >=0.8  && <0.9
     , keiro-core
     , keiro-dsl
-    , process     >=1.6  && <1.7
-    , QuickCheck  >=2.14
-    , text        >=2.1  && <2.2
+    , mmzk-typeid  >=0.7  && <0.8
+    , process      >=1.6  && <1.7
+    , QuickCheck   >=2.14
+    , text         >=2.1  && <2.2
 
 -- Conformance: proves the scaffolded Generated modules plus a hand-filled
 -- Holes.hs compile against keiki/keiro and that the filled transducer passes
@@ -153,20 +161,23 @@
   main-is:        Main.hs
   other-modules:
     Generated.HospitalCapacity.Nominals
+    Generated.HospitalCapacity.Nominals.Internal
     Generated.HospitalCapacity.ReplayAudit
     Generated.HospitalCapacity.Reservation.Codec
     Generated.HospitalCapacity.Reservation.Domain
     Generated.HospitalCapacity.Reservation.EventStream
     Generated.HospitalCapacity.Reservation.Harness
     Generated.HospitalCapacity.Reservation.Projection
+    Generated.HospitalCapacity.Reservation.Transducer
     HospitalCapacity.Reservation.Holes
 
   build-depends:
-    , aeson  >=2.2  && <2.3
-    , base   >=4.21 && <5
-    , keiki  >=0.7  && <0.8
+    , aeson       >=2.2  && <2.3
+    , base        >=4.21 && <5
+    , bytestring  >=0.12 && <0.13
+    , keiki       >=0.8  && <0.9
     , keiro
-    , text   >=2.1  && <2.2
+    , text        >=2.1  && <2.2
 
 -- Plan 157: compiled direct Time/Natural aggregate fields and registers,
 -- symbolic equality/ordering, codecs, snapshots, forward/replay equality,
@@ -184,11 +195,12 @@
     Generated.AggregateScalars.ScalarLedger.EventStream
     Generated.AggregateScalars.ScalarLedger.Harness
     Generated.AggregateScalars.ScalarLedger.Projection
+    Generated.AggregateScalars.ScalarLedger.Transducer
 
   build-depends:
     , aeson  >=2.2  && <2.3
     , base   >=4.21 && <5
-    , keiki  >=0.7  && <0.8
+    , keiki  >=0.8  && <0.9
     , keiro
     , text   >=2.1  && <2.2
     , time   >=1.12 && <1.15
@@ -205,12 +217,12 @@
     AggregateScalarExpressions.ScalarAccount.BehaviorHoles
     AggregateScalarExpressions.ScalarAccount.Holes
     Generated.AggregateScalarExpressions.Nominals
+    Generated.AggregateScalarExpressions.Nominals.Internal
     Generated.AggregateScalarExpressions.ReplayAudit
     Generated.AggregateScalarExpressions.ScalarAccount.BehaviorContract
     Generated.AggregateScalarExpressions.ScalarAccount.Codec
     Generated.AggregateScalarExpressions.ScalarAccount.Domain
     Generated.AggregateScalarExpressions.ScalarAccount.EventStream
-    Generated.AggregateScalarExpressions.ScalarAccount.Expressions
     Generated.AggregateScalarExpressions.ScalarAccount.Harness
     Generated.AggregateScalarExpressions.ScalarAccount.Projection
     Generated.AggregateScalarExpressions.ScalarAccount.Transducer
@@ -223,7 +235,7 @@
     , aeson       >=2.2  && <2.3
     , base        >=4.21 && <5
     , containers  >=0.6  && <0.8
-    , keiki       >=0.7  && <0.8
+    , keiki       >=0.8  && <0.9
     , keiro
     , keiro-dsl
     , text        >=2.1  && <2.2
@@ -243,10 +255,11 @@
     Generated.BehaviorComplete.Journey.Codec
     Generated.BehaviorComplete.Journey.Domain
     Generated.BehaviorComplete.Journey.EventStream
-    Generated.BehaviorComplete.Journey.Expressions
     Generated.BehaviorComplete.Journey.Harness
     Generated.BehaviorComplete.Journey.Projection
     Generated.BehaviorComplete.Journey.Transducer
+    Generated.BehaviorComplete.Nominals
+    Generated.BehaviorComplete.Nominals.Internal
     Generated.BehaviorComplete.ReplayAudit
     Generated.BehaviorComplete.Structural.Shape.StartPayload
     Generated.BehaviorComplete.StructuralProjections
@@ -256,7 +269,7 @@
     , base        >=4.21 && <5
     , bytestring  >=0.12 && <0.13
     , containers  >=0.6  && <0.8
-    , keiki       >=0.7  && <0.8
+    , keiki       >=0.8  && <0.9
     , keiro
     , text        >=2.1  && <2.2
     , time        >=1.12 && <1.15
@@ -274,7 +287,6 @@
     Generated.NominalScalars.NominalLedger.Codec
     Generated.NominalScalars.NominalLedger.Domain
     Generated.NominalScalars.NominalLedger.EventStream
-    Generated.NominalScalars.NominalLedger.Expressions
     Generated.NominalScalars.NominalLedger.Harness
     Generated.NominalScalars.NominalLedger.Projection
     Generated.NominalScalars.NominalLedger.Transducer
@@ -288,7 +300,7 @@
     , aeson        >=2.2  && <2.3
     , base         >=4.21 && <5
     , containers   >=0.6  && <0.8
-    , keiki        >=0.7  && <0.8
+    , keiki        >=0.8  && <0.9
     , keiro
     , mmzk-typeid  >=0.7  && <0.8
     , text         >=2.1  && <2.2
@@ -305,11 +317,11 @@
   main-is:        Main.hs
   other-modules:
     Generated.WorkspaceNominalProof.Nominals
+    Generated.WorkspaceNominalProof.Nominals.Internal
     Generated.WorkspaceNominalProof.Project.BehaviorContract
     Generated.WorkspaceNominalProof.Project.Codec
     Generated.WorkspaceNominalProof.Project.Domain
     Generated.WorkspaceNominalProof.Project.EventStream
-    Generated.WorkspaceNominalProof.Project.Expressions
     Generated.WorkspaceNominalProof.Project.Harness
     Generated.WorkspaceNominalProof.Project.Projection
     Generated.WorkspaceNominalProof.Project.Transducer
@@ -317,7 +329,6 @@
     Generated.WorkspaceNominalProof.ProjectArtifact.Codec
     Generated.WorkspaceNominalProof.ProjectArtifact.Domain
     Generated.WorkspaceNominalProof.ProjectArtifact.EventStream
-    Generated.WorkspaceNominalProof.ProjectArtifact.Expressions
     Generated.WorkspaceNominalProof.ProjectArtifact.Harness
     Generated.WorkspaceNominalProof.ProjectArtifact.Projection
     Generated.WorkspaceNominalProof.ProjectArtifact.Transducer
@@ -329,7 +340,7 @@
     , aeson       >=2.2  && <2.3
     , base        >=4.21 && <5
     , containers  >=0.6  && <0.8
-    , keiki       >=0.7  && <0.8
+    , keiki       >=0.8  && <0.9
     , keiro
     , text        >=2.1  && <2.2
 
@@ -349,6 +360,7 @@
     Generated.StructuralConformance.ArtifactCatalog.EventStream
     Generated.StructuralConformance.ArtifactCatalog.Harness
     Generated.StructuralConformance.ArtifactCatalog.Projection
+    Generated.StructuralConformance.ArtifactCatalog.Transducer
     Generated.StructuralConformance.ReplayAudit
     Generated.StructuralConformance.Structural.Shape.ArtifactInfo
     Generated.StructuralConformance.Structural.Shape.ArtifactKind
@@ -363,7 +375,7 @@
     , bytestring  >=0.12 && <0.13
     , containers  >=0.6  && <0.8
     , deepseq     >=1.5  && <1.6
-    , keiki       >=0.7  && <0.8
+    , keiki       >=0.8  && <0.9
     , keiro
     , text        >=2.1  && <2.2
     , time        >=1.12 && <1.15
@@ -397,7 +409,7 @@
     , deepseq     >=1.5   && <1.6
     , directory   >=1.3   && <1.4
     , filepath    >=1.4   && <1.6
-    , keiki       >=0.7   && <0.8
+    , keiki       >=0.8   && <0.9
     , keiro
     , keiro-dsl
     , text        >=2.1   && <2.2
@@ -417,13 +429,14 @@
     Generated.ReplayDivergence.Note.EventStream
     Generated.ReplayDivergence.Note.Harness
     Generated.ReplayDivergence.Note.Projection
+    Generated.ReplayDivergence.Note.Transducer
     Generated.ReplayDivergence.ReplayAudit
     ReplayDivergence.Note.Holes
 
   build-depends:
     , aeson  >=2.2  && <2.3
     , base   >=4.21 && <5
-    , keiki  >=0.7  && <0.8
+    , keiki  >=0.8  && <0.9
     , keiro
     , text   >=2.1  && <2.2
 
@@ -447,7 +460,7 @@
     , base         >=4.21 && <5
     , containers   >=0.6  && <0.8
     , deepseq      >=1.5  && <1.6
-    , keiki        >=0.7  && <0.8
+    , keiki        >=0.8  && <0.9
     , keiro
     , tasty-bench  >=0.5  && <0.6
     , text         >=2.1  && <2.2
@@ -462,16 +475,19 @@
   hs-source-dirs: test/conformance-snapshot
   main-is:        Main.hs
   other-modules:
+    Generated.HospitalCapacity.Nominals
+    Generated.HospitalCapacity.Nominals.Internal
     Generated.HospitalCapacity.ReplayAudit
     Generated.HospitalCapacity.Reservation.Codec
     Generated.HospitalCapacity.Reservation.Domain
     Generated.HospitalCapacity.Reservation.EventStream
+    Generated.HospitalCapacity.Reservation.Transducer
     HospitalCapacity.Reservation.Holes
 
   build-depends:
     , aeson  >=2.2  && <2.3
     , base   >=4.21 && <5
-    , keiki  >=0.7  && <0.8
+    , keiki  >=0.8  && <0.9
     , keiro
     , text   >=2.1  && <2.2
 
@@ -485,12 +501,16 @@
   main-is:        Main.hs
   other-modules:
     SkelAggregate.Generated.MyService.Nominals
+    SkelAggregate.Generated.MyService.Nominals.Internal
     SkelAggregate.Generated.MyService.ReplayAudit
+    SkelAggregate.Generated.MyService.Thing.BehaviorContract
     SkelAggregate.Generated.MyService.Thing.Codec
     SkelAggregate.Generated.MyService.Thing.Domain
     SkelAggregate.Generated.MyService.Thing.EventStream
     SkelAggregate.Generated.MyService.Thing.Harness
     SkelAggregate.Generated.MyService.Thing.Projection
+    SkelAggregate.Generated.MyService.Thing.Transducer
+    SkelAggregate.MyService.Thing.BehaviorHoles
     SkelAggregate.MyService.Thing.Holes
     SkelContract.Generated.MyService.MyContract.Contract
     SkelEmit.Generated.MyService.MyContract.Contract
@@ -502,15 +522,18 @@
     SkelProcess.Generated.MyService.Hospital.EventStream
     SkelProcess.Generated.MyService.Hospital.Harness
     SkelProcess.Generated.MyService.Hospital.Projection
+    SkelProcess.Generated.MyService.Hospital.Transducer
     SkelProcess.Generated.MyService.HospitalSurge.Process
     SkelProcess.Generated.MyService.HospitalSurge.ProcessHarness
     SkelProcess.Generated.MyService.Nominals
+    SkelProcess.Generated.MyService.Nominals.Internal
     SkelProcess.Generated.MyService.ReplayAudit
     SkelProcess.Generated.MyService.Surge.Codec
     SkelProcess.Generated.MyService.Surge.Domain
     SkelProcess.Generated.MyService.Surge.EventStream
     SkelProcess.Generated.MyService.Surge.Harness
     SkelProcess.Generated.MyService.Surge.Projection
+    SkelProcess.Generated.MyService.Surge.Transducer
     SkelProcess.MyService.Hospital.Holes
     SkelProcess.MyService.HospitalSurge.ProcessHoles
     SkelProcess.MyService.Surge.Holes
@@ -530,6 +553,7 @@
     SkelRouter.Generated.MyService.Page.EventStream
     SkelRouter.Generated.MyService.Page.Harness
     SkelRouter.Generated.MyService.Page.Projection
+    SkelRouter.Generated.MyService.Page.Transducer
     SkelRouter.Generated.MyService.PagingRouter.Router
     SkelRouter.Generated.MyService.PagingRouter.RouterHarness
     SkelRouter.Generated.MyService.ReplayAudit
@@ -544,11 +568,12 @@
     , containers
     , effectful-core
     , hasql-transaction
-    , keiki              >=0.7  && <0.8
+    , keiki              >=0.8  && <0.9
     , keiro
     , keiro-core
     , keiro-pgmq
     , kiroku-store
+    , mmzk-typeid        >=0.7  && <0.8
     , shibuya-core
     , text               >=2.1  && <2.2
     , time
@@ -564,22 +589,26 @@
   main-is:        Main.hs
   other-modules:
     Billing.Subscription.Holes
+    Generated.Billing.Nominals
+    Generated.Billing.Nominals.Internal
     Generated.Billing.ReplayAudit
     Generated.Billing.Subscription.Codec
     Generated.Billing.Subscription.Domain
     Generated.Billing.Subscription.EventStream
     Generated.Billing.Subscription.Harness
     Generated.Billing.Subscription.Projection
+    Generated.Billing.Subscription.Transducer
 
   build-depends:
     , aeson  >=2.2  && <2.3
     , base   >=4.21 && <5
-    , keiki  >=0.7  && <0.8
+    , keiki  >=0.8  && <0.9
     , keiro
     , text   >=2.1  && <2.2
 
--- Contract codec conformance (EP-4): the scaffolded self-contained contract
--- payload ADT + codec, compiled + round-tripped per event type.
+-- Stable language-4 contract conformance: prefix-indexed KindID fields,
+-- canonical text JSON, field-path-aware rejection, and the complete generated
+-- dependency set.
 test-suite keiro-dsl-conformance-contract
   import:         warnings, shared
   type:           exitcode-stdio-1.0
@@ -587,10 +616,26 @@
   main-is:        Main.hs
   other-modules:  Generated.HospitalCapacity.Emergency.Contract
   build-depends:
-    , aeson  >=2.2  && <2.3
-    , base   >=4.21 && <5
-    , text   >=2.1  && <2.2
+    , aeson        >=2.2  && <2.3
+    , base         >=4.21 && <5
+    , keiro-core
+    , mmzk-typeid  >=0.7  && <0.8
+    , text         >=2.1  && <2.2
 
+-- Language-1 compatibility proof: the historical permissive Text contract DTO
+-- still accepts pre-TypeID-v7 samples and retains its released JSON image.
+test-suite keiro-dsl-conformance-contract-v1-compat
+  import:         warnings, shared
+  type:           exitcode-stdio-1.0
+  hs-source-dirs: test/conformance-contract-v1-compat
+  main-is:        Main.hs
+  other-modules:  Generated.HospitalCapacity.Emergency.Contract
+  build-depends:
+    , aeson       >=2.2  && <2.3
+    , base        >=4.21 && <5
+    , bytestring  >=0.12 && <0.13
+    , text        >=2.1  && <2.2
+
 -- EP-4 intake runtime conformance: the scaffolded Inbox disposition + dedupe
 -- policy compiled against the LIVE Keiro.Inbox.Types (InboxResult / dedupe).
 test-suite keiro-dsl-conformance-intake-runtime
@@ -602,6 +647,7 @@
   build-depends:
     , base   >=4.21 && <5
     , keiro
+    , text   >=2.1  && <2.2
 
 -- EP-4 M5 full-service conformance: a complete integration service — scaffolded
 -- inbox dedupe + a filled inbox transaction runner and outbox IntegrationProducer
@@ -621,6 +667,7 @@
     , hasql-transaction
     , keiro
     , kiroku-store       >=0.3  && <0.4
+    , text               >=2.1  && <2.2
 
 -- EP-4 publisher runtime conformance: the scaffolded Publisher config compiled
 -- against the LIVE Keiro.Outbox.Types (OrderingPolicy / BackoffSchedule).
@@ -667,6 +714,7 @@
   build-depends:
     , aeson        >=2.2  && <2.3
     , base         >=4.21 && <5
+    , bytestring   >=0.12 && <0.13
     , keiro-core
     , keiro-dsl
     , keiro-pgmq
@@ -760,11 +808,15 @@
     Generated.SurgeDemo.Hospital.Domain
     Generated.SurgeDemo.Hospital.EventStream
     Generated.SurgeDemo.Hospital.Projection
+    Generated.SurgeDemo.Hospital.Transducer
+    Generated.SurgeDemo.Nominals
+    Generated.SurgeDemo.Nominals.Internal
     Generated.SurgeDemo.ReplayAudit
     Generated.SurgeDemo.Surge.Codec
     Generated.SurgeDemo.Surge.Domain
     Generated.SurgeDemo.Surge.EventStream
     Generated.SurgeDemo.Surge.Projection
+    Generated.SurgeDemo.Surge.Transducer
     Generated.SurgeDemo.SurgeFlow.Process
     SurgeDemo.Hospital.Holes
     SurgeDemo.Surge.Holes
@@ -773,7 +825,7 @@
   build-depends:
     , aeson         >=2.2  && <2.3
     , base          >=4.21 && <5
-    , keiki         >=0.7  && <0.8
+    , keiki         >=0.8  && <0.9
     , keiro
     , shibuya-core
     , text          >=2.1  && <2.2
@@ -808,10 +860,20 @@
   type:           exitcode-stdio-1.0
   hs-source-dirs: test/conformance-process-runtime
   main-is:        Main.hs
-  other-modules:  Generated.HospitalCapacity.HospitalSurge.Process
+  other-modules:
+    Generated.HospitalCapacity.HospitalSurge.Process
+    Generated.HospitalCapacity.Nominals
+    Generated.HospitalCapacity.Nominals.Internal
+    Generated.HospitalCapacity.Surge.Codec
+    Generated.HospitalCapacity.Surge.Domain
+    Generated.HospitalCapacity.Surge.EventStream
+    Generated.HospitalCapacity.Surge.Transducer
+    HospitalCapacity.Surge.Holes
+
   build-depends:
     , aeson         >=2.2  && <2.3
     , base          >=4.21 && <5
+    , keiki         >=0.8  && <0.9
     , keiro
     , keiro-dsl
     , shibuya-core
@@ -858,6 +920,7 @@
     Generated.IncidentPaging.Page.Codec
     Generated.IncidentPaging.Page.Domain
     Generated.IncidentPaging.Page.EventStream
+    Generated.IncidentPaging.Page.Transducer
     Generated.IncidentPaging.PagingRouter.Router
     Generated.IncidentPaging.PagingRouter.RouterHarness
     Generated.IncidentPaging.ReplayAudit
@@ -868,7 +931,7 @@
     , aeson           >=2.2  && <2.3
     , base            >=4.21 && <5
     , effectful-core
-    , keiki           >=0.7  && <0.8
+    , keiki           >=0.8  && <0.9
     , keiro
     , shibuya-core
     , text            >=2.1  && <2.2
@@ -888,6 +951,7 @@
     Generated.TransferRouting.Hospital.EventStream
     Generated.TransferRouting.Hospital.Harness
     Generated.TransferRouting.Hospital.Projection
+    Generated.TransferRouting.Hospital.Transducer
     Generated.TransferRouting.Hospital_load.ReadModel
     Generated.TransferRouting.Hospital_load.ReadModelHarness
     Generated.TransferRouting.Hospital_load.ReadModelTable
@@ -904,7 +968,7 @@
     , base               >=4.21 && <5
     , effectful-core
     , hasql-transaction
-    , keiki              >=0.7  && <0.8
+    , keiki              >=0.8  && <0.9
     , keiro
     , kiroku-store
     , shibuya-core
@@ -936,19 +1000,22 @@
   hs-source-dirs: test/conformance-v2
   main-is:        Main.hs
   other-modules:
+    Generated.HospitalCapacity.Nominals
+    Generated.HospitalCapacity.Nominals.Internal
     Generated.HospitalCapacity.ReplayAudit
     Generated.HospitalCapacity.Reservation.Codec
     Generated.HospitalCapacity.Reservation.Domain
     Generated.HospitalCapacity.Reservation.EventStream
     Generated.HospitalCapacity.Reservation.Harness
     Generated.HospitalCapacity.Reservation.Projection
+    Generated.HospitalCapacity.Reservation.Transducer
     HospitalCapacity.Reservation.Holes
 
   build-depends:
     , aeson      >=2.2  && <2.3
     , base       >=4.21 && <5
     , directory  >=1.3  && <1.4
-    , keiki      >=0.7  && <0.8
+    , keiki      >=0.8  && <0.9
     , keiro
     , text       >=2.1  && <2.2
 
@@ -969,6 +1036,6 @@
   build-depends:
     , aeson       >=2.2  && <2.3
     , base        >=4.21 && <5
-    , keiki       >=0.7  && <0.8
+    , keiki       >=0.8  && <0.9
     , keiro-core
     , text        >=2.1  && <2.2
diff --git a/src/Keiro/Dsl/AggregateType.hs b/src/Keiro/Dsl/AggregateType.hs
--- a/src/Keiro/Dsl/AggregateType.hs
+++ b/src/Keiro/Dsl/AggregateType.hs
@@ -154,9 +154,8 @@
     AggregateMapped {} -> Unsupported
     AggregateNominal nominal -> case resolvedNominalRepresentation nominal of
       ScalarRepresentation {} -> SolverVisible
-      _ -> case nominalEqualityContract nominal of
-        Just _ -> SolverVisible
-        Nothing -> OpaqueOnly
+      IdRepresentation {} -> SolverVisible
+      EnumRepresentation {} -> SolverVisible
     _ -> solverVisibility resolved
   OrderingGuardUse -> case resolved of
     AggregateInt -> SolverVisible
diff --git a/src/Keiro/Dsl/CanonicalEncoding.hs b/src/Keiro/Dsl/CanonicalEncoding.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Dsl/CanonicalEncoding.hs
@@ -0,0 +1,168 @@
+-- | Frozen canonical encoding for persisted aggregate-fold identity.
+--
+-- The bytes produced here feed snapshot compatibility decisions. They may
+-- change only as part of an explicit, ADR-recorded identity migration with
+-- updated golden fixtures. Keep this implementation independent from the
+-- presentation-oriented pretty printer so readability changes cannot move
+-- persisted identity accidentally.
+module Keiro.Dsl.CanonicalEncoding
+  ( canonicalExpr,
+    canonicalTransition,
+    foldFingerprint128,
+  )
+where
+
+import Data.Bits (shiftR, xor, (.&.), (.|.))
+import Data.Text (Text)
+import Data.Text qualified as T
+import Keiro.Dsl.Grammar
+import Numeric (showHex)
+import Prettyprinter
+import Prettyprinter.Render.Text (renderStrict)
+
+-- | Canonical concrete encoding of one expression.
+canonicalExpr :: Expr -> Text
+canonicalExpr =
+  renderStrict
+    . layoutPretty LayoutOptions {layoutPageWidth = Unbounded}
+    . docExpr 0
+
+-- | Canonical concrete encoding of one transition.
+canonicalTransition :: Transition -> Text
+canonicalTransition =
+  renderStrict
+    . layoutPretty LayoutOptions {layoutPageWidth = Unbounded}
+    . docTransition
+
+-- | A fixed-width FNV-1a-128 digest over the frozen fold surface's UTF-8
+-- bytes. The constants and octet fold are the standard values from RFC 9923;
+-- multiplication is reduced modulo 2^128 explicitly so the result does not
+-- depend on a machine integer width.
+foldFingerprint128 :: Text -> Text
+foldFingerprint128 input =
+  T.justifyRight 32 '0' (T.pack (showHex digest ""))
+  where
+    digest = foldl' step fnvOffsetBasis128 (concatMap utf8Bytes (T.unpack input))
+    step hash octet = ((hash `xor` octet) * fnvPrime128) `mod` fnvModulus128
+
+fnvOffsetBasis128 :: Integer
+fnvOffsetBasis128 = 0x6c62272e07bb014262b821756295c58d
+
+fnvPrime128 :: Integer
+fnvPrime128 = 0x0000000001000000000000000000013b
+
+fnvModulus128 :: Integer
+fnvModulus128 = 2 ^ (128 :: Int)
+
+-- | Encode one Unicode scalar value as UTF-8 octets without coupling the
+-- persisted identity implementation to a text or bytestring encoder version.
+utf8Bytes :: Char -> [Integer]
+utf8Bytes character
+  | codePoint <= 0x7f = [byte codePoint]
+  | codePoint <= 0x7ff =
+      [ byte (0xc0 .|. (codePoint `shiftR` 6)),
+        continuation codePoint
+      ]
+  | codePoint <= 0xffff =
+      [ byte (0xe0 .|. (codePoint `shiftR` 12)),
+        continuation (codePoint `shiftR` 6),
+        continuation codePoint
+      ]
+  | otherwise =
+      [ byte (0xf0 .|. (codePoint `shiftR` 18)),
+        continuation (codePoint `shiftR` 12),
+        continuation (codePoint `shiftR` 6),
+        continuation codePoint
+      ]
+  where
+    codePoint = fromEnum character
+    byte = toInteger
+    continuation value = byte (0x80 .|. (value .&. 0x3f))
+
+docTransition :: Transition -> Doc ann
+docTransition transition =
+  vsep $
+    [modePrefix <> pretty (tSource transition) <+> "--" <+> pretty (tCommand transition) <+> "-->"]
+      ++ map (indent 2) clauses
+  where
+    modePrefix = case tMode transition of
+      TmLive -> mempty
+      TmReplayOnly -> "replay-only "
+    clauses =
+      ["implementation hole" | tImplementation transition == HoleImplementation]
+        ++ maybe [] (\guardExpression -> ["guard" <+> docExpr 0 guardExpression]) (tGuard transition)
+        ++ map (\(registerName, expression) -> "write" <+> pretty registerName <+> ":=" <+> docExpr 0 expression) (tWrites transition)
+        ++ map (\eventName -> "emit" <+> pretty eventName) (tEmits transition)
+        ++ ["goto" <+> pretty (tGoto transition)]
+
+-- | @showsPrec@-style frozen expression encoding. Precedence levels are
+-- @||@ = 1, @&&@ = 2, comparisons = 3, addition/subtraction = 4,
+-- multiplication = 5, and atoms = 6.
+docExpr :: Int -> Expr -> Doc ann
+docExpr context expression = parensIf (precedence expression < context) (body expression)
+  where
+    body (EOr left right) = docExpr 1 left <+> "||" <+> docExpr 2 right
+    body (EAnd left right) = docExpr 2 left <+> "&&" <+> docExpr 3 right
+    body (ECmp operator left right) = docExpr 4 left <+> docCmp operator <+> docExpr 4 right
+    body (EAdd _ left right) = docExpr 4 left <+> "+" <+> docExpr 5 right
+    body (ESubtract _ left right) = docExpr 4 left <+> "-" <+> docExpr 5 right
+    body (EMultiply _ left right) = docExpr 5 left <+> "*" <+> docExpr 6 right
+    body (EPath _ root path) = docPath root path
+    body (ELiteral _ literal) = docLiteral literal
+    body (EAtom atom) = docAtom atom
+
+precedence :: Expr -> Int
+precedence EOr {} = 1
+precedence EAnd {} = 2
+precedence ECmp {} = 3
+precedence EAdd {} = 4
+precedence ESubtract {} = 4
+precedence EMultiply {} = 5
+precedence EPath {} = 6
+precedence ELiteral {} = 6
+precedence EAtom {} = 6
+
+docPath :: ExprRoot -> [Name] -> Doc ann
+docPath UnqualifiedRoot [] = mempty
+docPath UnqualifiedRoot (first : rest) = pretty first <> hcat (map (("." <>) . pretty) rest)
+docPath root path = docRoot root <> hcat (map (("." <>) . pretty) path)
+
+docRoot :: ExprRoot -> Doc ann
+docRoot UnqualifiedRoot = mempty
+docRoot RegisterRoot = "reg"
+docRoot CommandRoot = "cmd"
+
+docLiteral :: ScalarLiteral -> Doc ann
+docLiteral (LiteralText value) = dquoted value
+docLiteral (LiteralIntegral value) = pretty value
+docLiteral (LiteralBool True) = "true"
+docLiteral (LiteralBool False) = "false"
+docLiteral (LiteralQualified typeName constructorName) = pretty typeName <> "." <> pretty constructorName
+docLiteral (LiteralId typeName value) = pretty typeName <> "(" <> dquoted value <> ")"
+
+dquoted :: Text -> Doc ann
+dquoted value = "\"" <> pretty (T.concatMap escapeCharacter value) <> "\""
+  where
+    escapeCharacter '"' = "\\\""
+    escapeCharacter '\\' = "\\\\"
+    escapeCharacter '\n' = "\\n"
+    escapeCharacter '\t' = "\\t"
+    escapeCharacter '\r' = "\\r"
+    escapeCharacter character = T.singleton character
+
+docCmp :: CmpOp -> Doc ann
+docCmp OpEq = "=="
+docCmp OpNeq = "!="
+docCmp OpLt = "<"
+docCmp OpLe = "<="
+docCmp OpGt = ">"
+docCmp OpGe = ">="
+
+docAtom :: Atom -> Doc ann
+docAtom (AName name) = pretty name
+docAtom (ABool True) = "true"
+docAtom (ABool False) = "false"
+
+parensIf :: Bool -> Doc ann -> Doc ann
+parensIf True document = "(" <> document <> ")"
+parensIf False document = document
diff --git a/src/Keiro/Dsl/Diff.hs b/src/Keiro/Dsl/Diff.hs
--- a/src/Keiro/Dsl/Diff.hs
+++ b/src/Keiro/Dsl/Diff.hs
@@ -1,4 +1,4 @@
--- | The spec evolution differ. 'diffSpecs' compares an /old/ and a /new/ 'Spec'
+-- | The service evolution differ compares an /old/ and a /new/ checked graph
 -- and classifies changes over the persisted decode and identity surfaces.
 --
 -- Changes are __ADDITIVE__ when they preserve stored data, __WARNING__ when they
@@ -41,7 +41,6 @@
     diffSources,
     sourceLanguageChange,
     diffServices,
-    diffSpecs,
     DiffEnv (..),
     NodeFamily (..),
     familyOf,
@@ -55,6 +54,7 @@
 where
 
 import Data.Char (toUpper)
+import Data.Foldable (traverse_)
 import Data.List (find, (\\))
 import Data.Maybe (isJust, isNothing, mapMaybe)
 import Data.Set (Set)
@@ -62,9 +62,9 @@
 import Data.Text (Text)
 import Data.Text qualified as T
 import Keiro.Dsl.AggregateType (typeExprCanonicalName)
-import Keiro.Dsl.FoldFingerprint (aggregateFoldSurface, aggregateFoldSurfaceForService)
+import Keiro.Dsl.FoldFingerprint (FoldSurfaceError, aggregateFoldSurfaceForService)
 import Keiro.Dsl.Grammar
-import Keiro.Dsl.IdDomain (IdDomainContract (..), idDomainContractFor)
+import Keiro.Dsl.IdDomain (IdDomainContract (..), contractIdDomainContractFor, idDomainContractFor)
 import Keiro.Dsl.LanguageVersion (ParsedSource (..), SourceLanguage, declaredLanguageVersionMaybe, languageVersionText, sourceFormText)
 import Keiro.Dsl.MappedDiff (MappedFinding (..), diffMapped, renderMappedSubject)
 import Keiro.Dsl.PrettyPrint
@@ -75,7 +75,7 @@
     renderTransition,
   )
 import Keiro.Dsl.ReadModelShape (registryNameFor, subscriptionNameFor)
-import Keiro.Dsl.SemanticContract (CheckedService (..), checkedSource, effectiveLanguageContract, effectiveRuntimeSemantics, legacyCheckedService)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, checkedSource, effectiveLanguageContract, effectiveRuntimeSemantics, legacyCheckedService)
 import Keiro.Dsl.TypeGraph (UsePath (..), UseSite (..))
 import Keiro.Dsl.Validate (DiagnosticCode (..))
 
@@ -110,6 +110,7 @@
   | RolloutWorkersFirst
   | RolloutDrainRequired
   | RolloutProducerLast
+  | RolloutProducerFirst
   deriving stock (Eq, Ord, Show)
 
 -- | The explicit, compile-forcing compatibility result for one finding.
@@ -279,6 +280,7 @@
   | code == NominalRepresentationChanged = mappedWireBreakingVector context
   | code == NominalIdDecoderTightened =
       (advisoryVector PrivateHistoryRead Set.empty) {cvConsumerBuild = VAdvisory}
+  | code == ContractTypeIdDomainChanged = contractTypeIdDomainVector
   | code == IdDomainContractChanged = idDomainContractVector
   | code == MappedDeclAdded = compatibleVector
   | code `elem` privateDecodeCodes = privateDecodeBreakingVector
@@ -377,6 +379,18 @@
       cvRollout = Set.singleton RolloutProducerLast
     }
 
+contractTypeIdDomainVector :: CompatibilityVector
+contractTypeIdDomainVector =
+  CompatibilityVector
+    { cvPrivateHistoryRead = VNotApplicable,
+      cvOldBinaryReadNewEvents = VNotApplicable,
+      cvSnapshotHydration = VNotApplicable,
+      cvPublicConsumer = VBreaking,
+      cvPersistedIdentity = VNotApplicable,
+      cvConsumerBuild = VBreaking,
+      cvRollout = Set.fromList [RolloutDrainRequired, RolloutProducerFirst]
+    }
+
 mappedWireBreakingCodes :: [DiagnosticCode]
 mappedWireBreakingCodes =
   [ MappedFieldAddedNoDefault,
@@ -633,13 +647,15 @@
     (FamOperation, OutOfDiffScope "operations own no persisted decode or identity surface; their references and workflow signal/await pairing are single-spec validation concerns")
   ]
 
--- | Compare two graphs under their effective semantic contracts. Current
--- released contracts share runtime semantics, so the selected policy delegates
--- to the graph differ; successor semantics extend this boundary rather than
--- teaching CLI callers to recover versions from provenance.
-diffServices :: CheckedService -> CheckedService -> [Change]
-diffServices oldService newService =
-  diffCheckedSpecs oldSpec newSpec <> idDomainContractChanges <> semanticContractFoldChanges
+-- | Compare two graphs under their effective semantic contracts. The ordinary
+-- graph differ runs first; service-aware admission and fold findings then expose
+-- semantic-profile changes that leave the normalized graph itself unchanged.
+diffServices :: CheckedService -> CheckedService -> Either FoldSurfaceError [Change]
+diffServices oldService newService = do
+  traverse_ (aggregateFoldSurfaceForService oldService . snd) oldAggregates
+  traverse_ (aggregateFoldSurfaceForService newService . snd) newAggregates
+  semanticContractFoldChanges <- fmap concat (traverse semanticContractFoldChange oldAggregates)
+  pure (diffCheckedSpecs oldSpec newSpec <> idDomainContractChanges <> contractTypeIdDomainChanges <> semanticContractFoldChanges)
   where
     oldSpec = checkedSpec oldService
     newSpec = checkedSpec newService
@@ -663,30 +679,78 @@
         let newContract = idDomainContractFor (checkedLanguageContract newService) (idPrefix newDeclaration),
         oldContract /= newContract
       ]
-    semanticContractFoldChanges =
-      [ advisory
-          name
-          "semantic-contract"
-          name
-          AggFoldSurfaceChanged
-          "effective runtime semantics changed the aggregate fold surface even though the normalized graph is unchanged; re-scaffold, redeploy, and audit replay under the candidate contract"
-      | (name, oldAggregate) <- oldAggregates,
-        Just newAggregate <- [lookup name newAggregates],
-        aggregateFoldSurface oldSpec oldAggregate == aggregateFoldSurface newSpec newAggregate,
-        aggregateFoldSurfaceForService oldService oldAggregate /= aggregateFoldSurfaceForService newService newAggregate
+    contractTypeIdDomainChanges =
+      [ breaking
+          (ctrName newContract)
+          "contract-typeid-domain"
+          (ceName newEvent <> "." <> cfName newField)
+          ContractTypeIdDomainChanged
+          ( renderContractIdDomainChange
+              (cfType newField)
+              oldContract
+              newContract'
+          )
+      | newContract <- [contract | NContract contract <- specNodes newSpec],
+        Just oldContractNode <- [find ((== ctrName newContract) . ctrName) [contract | NContract contract <- specNodes oldSpec]],
+        newEvent <- ctrEvents newContract,
+        Just oldEvent <- [find ((== ceName newEvent) . ceName) (ctrEvents oldContractNode)],
+        newField <- ceFields newEvent,
+        Just oldField <- [find ((== cfName newField) . cfName) (ceFields oldEvent)],
+        cfType oldField == cfType newField,
+        let oldContract = contractFieldIdDomain (checkedLanguageContract oldService) oldField,
+        let newContract' = contractFieldIdDomain (checkedLanguageContract newService) newField,
+        oldContract /= newContract'
       ]
+    semanticContractFoldChange (name, oldAggregate) = case lookup name newAggregates of
+      Nothing -> pure []
+      Just newAggregate -> do
+        oldLegacySurface <- aggregateFoldSurfaceForService (legacyCheckedService oldSpec) oldAggregate
+        newLegacySurface <- aggregateFoldSurfaceForService (legacyCheckedService newSpec) newAggregate
+        oldSurface <- aggregateFoldSurfaceForService oldService oldAggregate
+        newSurface <- aggregateFoldSurfaceForService newService newAggregate
+        pure
+          [ advisory
+              name
+              "semantic-contract"
+              name
+              AggFoldSurfaceChanged
+              "effective runtime semantics changed the aggregate fold surface even though the normalized graph is unchanged; re-scaffold, redeploy, and audit replay under the candidate contract"
+          | oldLegacySurface == newLegacySurface,
+            oldSurface /= newSurface
+          ]
 
 renderIdDomainContract :: Maybe IdDomainContract -> Text
 renderIdDomainContract Nothing = "legacy-unchecked"
 renderIdDomainContract (Just contract) =
   idDomainVersion contract <> "(prefix=" <> idDomainPrefix contract <> ",json=" <> idDomainJsonRepresentation contract <> ")"
 
--- | Compatibility wrapper for graph-only callers, explicitly using
--- legacy/version-1 semantics on both sides.
-diffSpecs :: Spec -> Spec -> [Change]
-diffSpecs old new =
-  diffServices (legacyCheckedService old) (legacyCheckedService new)
+contractFieldIdDomain :: EffectiveLanguageContract -> ContractField -> Maybe IdDomainContract
+contractFieldIdDomain languageContract field = case cfType field of
+  CTypeId prefix -> contractIdDomainContractFor languageContract prefix
+  _ -> Nothing
 
+renderContractIdDomainChange :: ContractType -> Maybe IdDomainContract -> Maybe IdDomainContract -> Text
+renderContractIdDomainChange fieldType oldContract newContract =
+  "contract TypeID admission changed "
+    <> renderIdDomainContract oldContract
+    <> " -> "
+    <> renderIdDomainContract newContract
+    <> representationChange
+  where
+    prefix = case fieldType of
+      CTypeId value -> value
+      _ -> ""
+    representationChange = case (oldContract, newContract) of
+      (Nothing, Just _) ->
+        "; generated Haskell changes from Text to KindID \""
+          <> prefix
+          <> "\" while valid JSON stays canonical text; newly generated consumers reject malformed, wrong-prefix, non-canonical, and non-v7 values"
+      (Just _, Nothing) ->
+        "; generated Haskell changes from KindID \""
+          <> prefix
+          <> "\" to Text and the generated decoder no longer enforces the frozen TypeID-v7 domain"
+      _ -> "; generated contract admission changed while the source field type remained unchanged"
+
 diffCheckedSpecs :: Spec -> Spec -> [Change]
 diffCheckedSpecs old new =
   sharedDeclarationDiff env
@@ -694,15 +758,18 @@
   where
     env = DiffEnv old new
 
--- | Compare provenance first, then delegate semantic graphs to 'diffSpecs'.
-diffSources :: ParsedSource -> ParsedSource -> [Change]
-diffSources old new =
-  sourceLanguageChange
-    (specContext (parsedSpec new))
-    "declaration"
-    (parsedSourceLanguage old)
-    (parsedSourceLanguage new)
-    <> diffServices (checkedSource old) (checkedSource new)
+-- | Compare provenance first, then delegate semantic graphs to 'diffServices'.
+diffSources :: ParsedSource -> ParsedSource -> Either FoldSurfaceError [Change]
+diffSources old new = do
+  semanticChanges <- diffServices (checkedSource old) (checkedSource new)
+  pure
+    ( sourceLanguageChange
+        (specContext (parsedSpec new))
+        "declaration"
+        (parsedSourceLanguage old)
+        (parsedSourceLanguage new)
+        <> semanticChanges
+    )
 
 -- | One all-compatible source-provenance finding, reusable per workspace member.
 sourceLanguageChange :: Name -> Text -> SourceLanguage -> SourceLanguage -> [Change]
@@ -1017,7 +1084,7 @@
 -- fingerprint and invalidates old snapshots, so this remains advisory.
 transitionSurfaceDiff :: Spec -> Spec -> Aggregate -> Aggregate -> [Change]
 transitionSurfaceDiff oldSpec newSpec oldAgg newAgg
-  | aggregateFoldSurface oldSpec oldAgg == aggregateFoldSurface newSpec newAgg = []
+  | aggregateFoldSurfaceForService (legacyCheckedService oldSpec) oldAgg == aggregateFoldSurfaceForService (legacyCheckedService newSpec) newAgg = []
   | otherwise =
       [ advisory
           (aggName newAgg)
@@ -2197,6 +2264,7 @@
     publicCodes =
       [ ContractEventRemoved,
         ContractFieldChanged,
+        ContractTypeIdDomainChanged,
         ContractDiscriminatorChanged,
         ContractTopicChanged,
         ContractSchemaVersionDecreased,
diff --git a/src/Keiro/Dsl/DiffReport.hs b/src/Keiro/Dsl/DiffReport.hs
--- a/src/Keiro/Dsl/DiffReport.hs
+++ b/src/Keiro/Dsl/DiffReport.hs
@@ -50,6 +50,10 @@
   | RemedyRunConformance
   | RemedyNoSemanticAction
   | RemedyDoNotDeploy Text
+  | RemedyEmitContractTypeIdDomain
+  | RemedyDrainLegacyInvalidContractMessages
+  | RemedyRescaffoldContractConsumers
+  | RemedyRunContractConformance
   deriving stock (Eq, Show)
 
 data DiffReport = DiffReport
@@ -184,6 +188,12 @@
   | code == AggFoldSurfaceChanged = RemedyStateCodecBump :| [RemedyRunConformance]
   | code == IdDomainContractChanged =
       RemedyDeploymentOrder RolloutProducerLast :| [RemedyStateCodecBump, RemedyRecompileConsumers, RemedyRunConformance]
+  | code == ContractTypeIdDomainChanged =
+      RemedyEmitContractTypeIdDomain
+        :| [ RemedyDrainLegacyInvalidContractMessages,
+             RemedyRescaffoldContractConsumers,
+             RemedyRunContractConformance
+           ]
   | code `elem` mappedWireCodes = mappedWireRemedy
   | code `elem` [MappedFieldAddedWithDefault, MappedArmAdded, MappedEnumValueAdded] = mappedAdditionRemedy
   | code `elem` [MappedHaskellSourceChanged, MappedRecordConstructorChanged] =
@@ -304,6 +314,10 @@
   RemedyRunConformance -> "run the generated conformance and historical fixture suites"
   RemedyNoSemanticAction -> "no semantic action is required; only source-language provenance changed"
   RemedyDoNotDeploy detail -> detail
+  RemedyEmitContractTypeIdDomain -> "make all producers emit the frozen TypeID-v7 domain"
+  RemedyDrainLegacyInvalidContractMessages -> "drain or remediate legacy-invalid in-flight messages"
+  RemedyRescaffoldContractConsumers -> "re-scaffold and recompile every affected consumer against the generated interface"
+  RemedyRunContractConformance -> "run contract conformance"
 
 renderFinding :: Change -> Text
 renderFinding change =
@@ -389,6 +403,7 @@
   RolloutWorkersFirst -> "workers-first"
   RolloutDrainRequired -> "drain-required"
   RolloutProducerLast -> "producer-last"
+  RolloutProducerFirst -> "producer-first"
 
 labelName :: Label -> Text
 labelName label = case label of
diff --git a/src/Keiro/Dsl/FoldFingerprint.hs b/src/Keiro/Dsl/FoldFingerprint.hs
--- a/src/Keiro/Dsl/FoldFingerprint.hs
+++ b/src/Keiro/Dsl/FoldFingerprint.hs
@@ -3,10 +3,10 @@
 -- projections, snapshot policy, and source locations: those inputs do not change
 -- how an existing event log becomes aggregate state.
 module Keiro.Dsl.FoldFingerprint
-  ( aggregateFoldFingerprintForService,
+  ( FoldSurfaceError (..),
+    renderFoldSurfaceError,
+    aggregateFoldFingerprintForService,
     aggregateFoldSurfaceForService,
-    aggregateFoldFingerprint,
-    aggregateFoldSurface,
   )
 where
 
@@ -18,48 +18,74 @@
 import Data.Text (Text)
 import Data.Text qualified as T
 import Keiro.Dsl.AggregateType
+import Keiro.Dsl.CanonicalEncoding (canonicalExpr, foldFingerprint128)
 import Keiro.Dsl.EventOutput
 import Keiro.Dsl.Expression
 import Keiro.Dsl.Grammar
+import Keiro.Dsl.LanguageVersion (RuntimeCapability (NominalEqualityV2), runtimeProfileHasCapability)
 import Keiro.Dsl.NominalType
-import Keiro.Dsl.PrettyPrint (renderExpr)
-import Keiro.Dsl.ReadModelShape (fnv1a64)
-import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, legacyCheckedService, runtimeSemanticsFingerprintSegment)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveRuntimeProfile, runtimeSemanticsFingerprintSegments)
 import Keiro.Dsl.TypeGraph
 
--- | The sixteen-hex-digit identity of an aggregate's replay fold under the
--- service's effective runtime semantics.
-aggregateFoldFingerprintForService :: CheckedService -> Aggregate -> Text
-aggregateFoldFingerprintForService service = fnv1a64 . aggregateFoldSurfaceForService service
+-- | A checked service can retain language provenance before semantic
+-- validation succeeds. Fold identity therefore reports every resolution
+-- failure instead of silently omitting or inventing canonical segments.
+data FoldSurfaceError
+  = FoldTypeGraphResolutionFailed !Text
+  | FoldNominalResolutionFailed !Text
+  | FoldRegisterTypeResolutionFailed !Name !Text
+  | FoldRegisterInitialResolutionFailed !Name !Text
+  | FoldGuardResolutionFailed !Name !Name !Text
+  | FoldEventOutputResolutionFailed !Name !Name !Name !Text
+  deriving stock (Eq, Show)
 
--- | The sixteen-hex-digit identity of an aggregate's replay fold.
--- This compatibility wrapper selects legacy/version-1 runtime semantics.
-aggregateFoldFingerprint :: Spec -> Aggregate -> Text
-aggregateFoldFingerprint spec = aggregateFoldFingerprintForService (legacyCheckedService spec)
+renderFoldSurfaceError :: FoldSurfaceError -> Text
+renderFoldSurfaceError = \case
+  FoldTypeGraphResolutionFailed detail -> "aggregate fold type-graph resolution failed: " <> detail
+  FoldNominalResolutionFailed detail -> "aggregate fold nominal resolution failed: " <> detail
+  FoldRegisterTypeResolutionFailed registerName detail ->
+    "aggregate fold register '" <> registerName <> "' type resolution failed: " <> detail
+  FoldRegisterInitialResolutionFailed registerName detail ->
+    "aggregate fold register '" <> registerName <> "' initial resolution failed: " <> detail
+  FoldGuardResolutionFailed aggregateName commandName detail ->
+    "aggregate fold guard resolution failed for '" <> aggregateName <> "." <> commandName <> "': " <> detail
+  FoldEventOutputResolutionFailed aggregateName commandName eventName detail ->
+    "aggregate fold output resolution failed for '"
+      <> aggregateName
+      <> "."
+      <> commandName
+      <> "' emitting '"
+      <> eventName
+      <> "': "
+      <> detail
 
--- | Canonical pre-hash text for an aggregate's replay fold.
---
--- Rules are declarations on 'Spec', not children of 'Aggregate', so the complete
--- spec is required. Only rules reached from transition guards and writes are
--- included, transitively, in declaration order.
-aggregateFoldSurface :: Spec -> Aggregate -> Text
-aggregateFoldSurface spec = aggregateFoldSurfaceForService (legacyCheckedService spec)
+-- | The thirty-two-hex-digit identity of an aggregate's replay fold under the
+-- service's effective runtime semantics.
+aggregateFoldFingerprintForService :: CheckedService -> Aggregate -> Either FoldSurfaceError Text
+aggregateFoldFingerprintForService service = fmap foldFingerprint128 . aggregateFoldSurfaceForService service
 
 -- | Canonical pre-hash text under a checked semantic contract. A runtime
 -- discriminator is included only for a contract that can change fold behavior;
 -- source declaration provenance and grammar-only versions never enter it.
-aggregateFoldSurfaceForService :: CheckedService -> Aggregate -> Text
-aggregateFoldSurfaceForService service aggregate =
-  T.intercalate
-    "\n"
-    ( maybe [] pure (runtimeSemanticsFingerprintSegment (checkedLanguageContract service))
-        ++ map stateSegment (aggStates aggregate)
-        ++ map (registerSegment symbols) (aggRegs aggregate)
-        ++ mappedRegisterSegments
-        ++ nominalSegments
-        ++ nominalEqualitySegments
-        ++ map (transitionSegment spec aggregate) (aggTransitions aggregate)
-        ++ map ruleSegment referencedRules
+aggregateFoldSurfaceForService :: CheckedService -> Aggregate -> Either FoldSurfaceError Text
+aggregateFoldSurfaceForService service aggregate = do
+  graph <- mapLeft (FoldTypeGraphResolutionFailed . showText) (resolveTypeGraph spec)
+  nominalRegistry <- mapLeft (FoldNominalResolutionFailed . showText) (resolveNominalTypes spec)
+  registerSegments <- traverse (registerSegment symbols) (aggRegs aggregate)
+  equalityUses <- nominalEqualityUses service aggregate
+  transitionSegments <- traverse (transitionSegment spec aggregate) (aggTransitions aggregate)
+  pure
+    ( T.intercalate
+        "\n"
+        ( runtimeSemanticsFingerprintSegments (checkedLanguageContract service)
+            ++ map stateSegment (aggStates aggregate)
+            ++ registerSegments
+            ++ mappedRegisterSegments graph
+            ++ nominalSegments nominalRegistry
+            ++ ["nominal-equality-use:" <> identity | identity <- Set.toAscList equalityUses]
+            ++ transitionSegments
+            ++ map ruleSegment referencedRules
+        )
     )
   where
     spec = checkedSpec service
@@ -69,42 +95,47 @@
       | rule <- specRules spec,
         ruleName rule `Set.member` referencedRuleNames spec aggregate
       ]
-    mappedRegisterSegments = case resolveTypeGraph spec of
-      Left _ -> []
-      Right graph ->
-        [ mappedRegisterSegment graph declaration
-        | register <- aggRegs aggregate,
-          TRef typeName <- [regType register],
-          Just declaration <- [Map.lookup (MappedKey typeName) (tgDeclarations graph)]
-        ]
-    nominalSegments = case resolveNominalTypes spec of
-      Left _ -> []
-      Right registry ->
-        [ nominalUseSegment useSite nominal binding
-        | (useSite, typeName) <- nominalUseNames aggregate,
-          Just nominal <- [lookupNominalType typeName registry],
-          ConsumerNominal binding <- [resolvedNominalOwnership nominal]
-        ]
-    nominalEqualitySegments =
-      [ "nominal-equality-use:" <> identity
-      | identity <- Set.toAscList (nominalEqualityUses service aggregate)
+    mappedRegisterSegments graph =
+      [ mappedRegisterSegment graph declaration
+      | register <- aggRegs aggregate,
+        TRef typeName <- [regType register],
+        Just declaration <- [Map.lookup (MappedKey typeName) (tgDeclarations graph)]
       ]
+    nominalSegments registry =
+      [ nominalUseSegment useSite nominal binding
+      | (useSite, typeName) <- nominalUseNames aggregate,
+        Just nominal <- [lookupNominalType typeName registry],
+        ConsumerNominal binding <- [resolvedNominalOwnership nominal]
+      ]
 
 -- | Equality representation belongs in the fold identity only when a guard
 -- actually compares that declaration. This keeps unrelated binding metadata out
 -- of replay compatibility while ensuring a witness/domain change cannot silently
 -- retain the old fold fingerprint.
-nominalEqualityUses :: CheckedService -> Aggregate -> Set Text
+nominalEqualityUses :: CheckedService -> Aggregate -> Either FoldSurfaceError (Set Text)
 nominalEqualityUses service aggregate =
-  Set.fromList
-    [ identity
-    | transition <- aggTransitions aggregate,
-      guardSyntax <- maybeToList (tGuard transition),
-      Right guardExpression <- [resolveGuardExpr (expressionEnvironment spec aggregate transition) guardSyntax],
-      identity <- equalityIdentities (checkedLanguageContract service) guardExpression
-    ]
+  if runtimeProfileHasCapability (effectiveRuntimeProfile (checkedLanguageContract service)) NominalEqualityV2
+    then fmap (Set.fromList . concat) (traverse transitionIdentities (aggTransitions aggregate))
+    else
+      Right
+        ( Set.fromList
+            [ identity
+            | transition <- aggTransitions aggregate,
+              guardSyntax <- maybeToList (tGuard transition),
+              Right guardExpression <- [resolveGuardExpr (expressionEnvironment spec aggregate transition) guardSyntax],
+              identity <- equalityIdentities (checkedLanguageContract service) guardExpression
+            ]
+        )
   where
     spec = checkedSpec service
+    transitionIdentities transition = case tGuard transition of
+      Nothing -> Right []
+      Just guardSyntax -> do
+        guardExpression <-
+          mapLeft
+            (FoldGuardResolutionFailed (aggName aggregate) (tCommand transition) . showText)
+            (resolveGuardExpr (expressionEnvironment spec aggregate transition) guardSyntax)
+        pure (equalityIdentities (checkedLanguageContract service) guardExpression)
 
 equalityIdentities :: EffectiveLanguageContract -> TypedScalarExpr -> [Text]
 equalityIdentities languageContract expression =
@@ -198,59 +229,56 @@
     <> "|terminal="
     <> if stTerminal state then "true" else "false"
 
-registerSegment :: AggregateSymbols -> RegDecl -> Text
-registerSegment symbols register =
-  "reg:"
-    <> regName register
-    <> ":"
-    <> typeExprCanonicalName (regType register)
-    <> "="
-    <> canonicalInitial
-  where
-    canonicalInitial = case resolveAggregateType symbols (regLoc register) RegisterUse (regType register) of
-      Left _ -> renderInitial (regInitial register)
-      Right resolvedType -> case resolveRegisterInitial symbols (regLoc register) resolvedType (regInitial register) of
-        Left _ -> renderInitial (regInitial register)
-        Right resolvedInitial -> registerInitialCanonicalName resolvedInitial
-
-renderInitial :: RegInitial -> Text
-renderInitial (RegInitBare value) = value
-renderInitial (RegInitText value) = "\"" <> escapeText value <> "\""
-
-escapeText :: Text -> Text
-escapeText = T.concatMap $ \case
-  '"' -> "\\\""
-  '\\' -> "\\\\"
-  '\n' -> "\\n"
-  '\t' -> "\\t"
-  '\r' -> "\\r"
-  character -> T.singleton character
+registerSegment :: AggregateSymbols -> RegDecl -> Either FoldSurfaceError Text
+registerSegment symbols register = do
+  resolvedType <-
+    mapLeft
+      (FoldRegisterTypeResolutionFailed (regName register) . showText)
+      (resolveAggregateType symbols (regLoc register) RegisterUse (regType register))
+  resolvedInitial <-
+    mapLeft
+      (FoldRegisterInitialResolutionFailed (regName register) . showText)
+      (resolveRegisterInitial symbols (regLoc register) resolvedType (regInitial register))
+  pure
+    ( "reg:"
+        <> regName register
+        <> ":"
+        <> typeExprCanonicalName (regType register)
+        <> "="
+        <> registerInitialCanonicalName resolvedInitial
+    )
 
-transitionSegment :: Spec -> Aggregate -> Transition -> Text
-transitionSegment spec aggregate transition =
-  T.intercalate
-    "|"
-    ( [ "transition:" <> renderMode (tMode transition),
-        tSource transition,
-        tCommand transition
-      ]
-        ++ implementationSegment
-        ++ [ "guard=" <> maybe "" renderExpr (tGuard transition),
-             "writes=" <> T.intercalate ";" (map renderWrite (tWrites transition)),
-             "emits=" <> T.intercalate "," (tEmits transition)
-           ]
-        ++ outputOwnershipSegment
-        ++ ["goto=" <> tGoto transition]
+transitionSegment :: Spec -> Aggregate -> Transition -> Either FoldSurfaceError Text
+transitionSegment spec aggregate transition = do
+  outputOwnershipSegment <- case tImplementation transition of
+    LegacyHoleImplementation -> Right []
+    GeneratedImplementation -> fmap (pure . ("outputs=" <>) . T.intercalate ",") outputSegments
+    HoleImplementation -> fmap (pure . ("outputs=" <>) . T.intercalate ",") outputSegments
+  pure
+    ( T.intercalate
+        "|"
+        ( [ "transition:" <> renderMode (tMode transition),
+            tSource transition,
+            tCommand transition
+          ]
+            ++ implementationSegment
+            ++ [ "guard=" <> maybe "" canonicalExpr (tGuard transition),
+                 "writes=" <> T.intercalate ";" (map renderWrite (tWrites transition)),
+                 "emits=" <> T.intercalate "," (tEmits transition)
+               ]
+            ++ outputOwnershipSegment
+            ++ ["goto=" <> tGoto transition]
+        )
     )
   where
-    renderWrite (registerName, expression) = registerName <> ":=" <> renderExpr expression
-    outputSegment emitIndex eventName = case eventOutputMapping spec aggregate transition emitIndex eventName of
-      Right mapping -> eventName <> "=" <> eventOutputCanonical mapping
-      Left problem -> eventName <> "=invalid:" <> T.pack (show problem)
-    outputOwnershipSegment = case tImplementation transition of
-      LegacyHoleImplementation -> []
-      GeneratedImplementation -> ["outputs=" <> T.intercalate "," [outputSegment emitIndex eventName | (emitIndex, eventName) <- zip [1 ..] (tEmits transition)]]
-      HoleImplementation -> ["outputs=" <> T.intercalate "," [outputSegment emitIndex eventName | (emitIndex, eventName) <- zip [1 ..] (tEmits transition)]]
+    renderWrite (registerName, expression) = registerName <> ":=" <> canonicalExpr expression
+    outputSegments = traverse (uncurry outputSegment) (zip [1 ..] (tEmits transition))
+    outputSegment emitIndex eventName = do
+      mapping <-
+        mapLeft
+          (FoldEventOutputResolutionFailed (aggName aggregate) (tCommand transition) eventName . showText)
+          (eventOutputMapping spec aggregate transition emitIndex eventName)
+      pure (eventName <> "=" <> eventOutputCanonical mapping)
     implementationSegment = case tImplementation transition of
       LegacyHoleImplementation -> []
       GeneratedImplementation -> ["implementation=generated"]
@@ -270,7 +298,7 @@
       "cases=" <> T.intercalate ";" (map renderCase (ruleCases rule))
     ]
   where
-    renderCase (constructorName, expression) = constructorName <> "=>" <> renderExpr expression
+    renderCase (constructorName, expression) = constructorName <> "=>" <> canonicalExpr expression
 
 referencedRuleNames :: Spec -> Aggregate -> Set Name
 referencedRuleNames spec aggregate = close directNames
@@ -310,3 +338,9 @@
 maybeToList :: Maybe a -> [a]
 maybeToList Nothing = []
 maybeToList (Just value) = [value]
+
+mapLeft :: (errorValue -> otherError) -> Either errorValue value -> Either otherError value
+mapLeft convert = either (Left . convert) Right
+
+showText :: (Show value) => value -> Text
+showText = T.pack . show
diff --git a/src/Keiro/Dsl/Frontend/Internal.hs b/src/Keiro/Dsl/Frontend/Internal.hs
--- a/src/Keiro/Dsl/Frontend/Internal.hs
+++ b/src/Keiro/Dsl/Frontend/Internal.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
 {-# LANGUAGE NoFieldSelectors #-}
 
 -- | Shared implementation behind the public frontend and parser compatibility
diff --git a/src/Keiro/Dsl/Grammar.hs b/src/Keiro/Dsl/Grammar.hs
--- a/src/Keiro/Dsl/Grammar.hs
+++ b/src/Keiro/Dsl/Grammar.hs
@@ -30,14 +30,8 @@
     NominalScalarDecl (..),
     MappedDecl (..),
 
-    -- * The eight hole-kind types
-    Derivation (..),
-    DerivStrategy (..),
-    Disposition (..),
-    DispAction (..),
+    -- * Shared mapping type
     Mapping (..),
-    EnvelopeBinding (..),
-    EnvelopeLayer (..),
 
     -- * The Expr sublanguage
     Expr (..),
@@ -332,33 +326,6 @@
       }
   deriving stock (Eq, Show, Generic)
 
--- The eight hole-kind types. EP-1 only exercises hole-kinds 1–3 against the
--- aggregate vertical; the rest exist so EP-3…EP-6 reuse the same types.
-
--- | Hole-kind 1: a deterministic id/string derivation. Opaque strategies must
--- carry a captured @fixture@ (not a prose rule) so two agents re-derive them
--- identically.
-data Derivation = Derivation
-  { derivStrategy :: !DerivStrategy,
-    derivFixture :: !(Maybe Text)
-  }
-  deriving stock (Eq, Show, Generic)
-
-data DerivStrategy = UuidV5 | SuffixSplice
-  deriving stock (Eq, Show, Generic)
-
--- | Hole-kind 2: a failure→action table. Carries the two dangerous inversions a
--- @duplicate@/@rejected replay@ being treated as success and a
--- @previously-failed@ being dead-lettered rather than retried (enforced by
--- EP-4's validator rules).
-newtype Disposition = Disposition
-  { dispCases :: [(Name, DispAction)]
-  }
-  deriving stock (Eq, Show, Generic)
-
-data DispAction = AckOk | Retry !Int | DeadLetter !Text
-  deriving stock (Eq, Show, Generic)
-
 -- | Hole-kind 3: an explicit value→value table that is not an identity echo
 -- (e.g. an event name → projection status). @mapPartial@ records whether the
 -- spec author explicitly marked the table partial over its domain.
@@ -368,18 +335,6 @@
   }
   deriving stock (Eq, Show, Generic)
 
--- | Hole-kind 4: which layer carries each envelope field, and whether the two
--- are cross-checked. Defined here for reuse; exercised by EP-4.
-data EnvelopeBinding = EnvelopeBinding
-  { envField :: !Name,
-    envLayer :: !EnvelopeLayer,
-    envCrossChecked :: !Bool
-  }
-  deriving stock (Eq, Show, Generic)
-
-data EnvelopeLayer = KafkaHeader | JsonBody
-  deriving stock (Eq, Show, Generic)
-
 -- | The @Expr@ sublanguage used by @guard@ clauses and the right-hand side of
 -- @write@ clauses. An infix expression over 'Atom's; operators in precedence
 -- order are @||@ (lowest), @&&@, then the relational comparisons.
@@ -694,8 +649,7 @@
   deriving stock (Eq, Show, Generic)
 
 -- | The complete dispatch disposition table (every arm mandatory; the
--- @on-duplicate AckOk@ benign inversion is explicit). Named to avoid clashing
--- with the hole-kind 'Disposition'.
+-- @on-duplicate AckOk@ benign inversion is explicit).
 data DispatchDisposition = DispatchDisposition
   { onAppended :: !Disp,
     onDuplicate :: !Disp,
diff --git a/src/Keiro/Dsl/Harness.hs b/src/Keiro/Dsl/Harness.hs
--- a/src/Keiro/Dsl/Harness.hs
+++ b/src/Keiro/Dsl/Harness.hs
@@ -219,13 +219,12 @@
     [ generatedBanner,
       "module " <> genPrefix <> ".ProcessHarness (processHarnessValues) where",
       "",
-      "{- | (label, value): the spec's deterministic process/timer decisions,",
-      "lowered to plain values so a driver can assert them against a committed",
-      "expectation. The driver's expectation is hand-written (not generated), so a",
-      "spec change that alters a decision diverges from it and turns a specific",
-      "assertion red — the spec->behaviour pin. (Live-runtime behavioural",
-      "conformance of the filled ProcessManager is the M5 step.)",
-      "-}",
+      "-- | (label, value): the spec's deterministic process/timer decisions,",
+      "-- lowered to plain values so a driver can assert them against a committed",
+      "-- expectation. The driver's expectation is hand-written (not generated), so a",
+      "-- spec change that alters a decision diverges from it and turns a specific",
+      "-- assertion red — the spec->behaviour pin. (Live-runtime behavioural",
+      "-- conformance of the filled ProcessManager is the M5 step.)",
       "processHarnessValues :: [(String, String)]",
       "processHarnessValues =",
       "  [ (\"fireAtField\", " <> hs (faField (tmFireAt timer)) <> ")",
@@ -269,7 +268,7 @@
 -- pinning the spec's deterministic decisions: the stable name, the WorkflowId
 -- derivation, the ordered body (step/await/sleep/child by label), and the await
 -- labels (whose ids the signal operations must match). Exposes
--- @workflowFacts :: [(String, String)]@ so a driver asserts them against a
+-- a typed @WorkflowFacts@ record so a driver asserts it against a
 -- hand-written expectation — a spec change (e.g. renaming an await label) diverges
 -- and reddens a specific assertion. Workflows intentionally have no domain scaffold
 -- or hole stub: their behaviour-bearing body remains hand-written, while these facts
@@ -301,24 +300,35 @@
 emitWorkflowFacts genPrefix w =
   nl
     [ generatedBanner,
-      "module " <> genPrefix <> ".WorkflowFacts (workflowFacts) where",
+      "module " <> genPrefix <> ".WorkflowFacts (WorkflowFacts (..), workflowFacts) where",
       "",
-      "{- | (label, value): the workflow's deterministic decisions, pinned as pure",
-      "facts. A driver asserts them against a hand-written expectation, so a spec",
-      "change (e.g. renaming an await) reddens a specific assertion.",
-      "-}",
-      "workflowFacts :: [(String, String)]",
+      "-- | The workflow's deterministic decisions, pinned as typed pure facts.",
+      "-- A driver asserts them against a hand-written expectation, so a spec",
+      "-- change (e.g. renaming an await) reddens a specific assertion.",
+      "data WorkflowFacts = WorkflowFacts",
+      "  { workflowFactName :: !String",
+      "  , workflowFactIdVia :: !String",
+      "  , workflowFactIdField :: !String",
+      "  , workflowFactBody :: ![String]",
+      "  , workflowFactAwaitLabels :: ![String]",
+      "  , workflowFactPatchIds :: ![String]",
+      "  }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "workflowFacts :: WorkflowFacts",
       "workflowFacts =",
-      "  [ (\"name\", " <> hs (wfStable w) <> ")",
-      "  , (\"idVia\", " <> hs (wfIdVia w) <> ")",
-      "  , (\"idField\", " <> hs (maybe "input" id (wfIdField w)) <> ")",
-      "  , (\"body\", " <> hs (T.intercalate "," (map bodyTag (wfBody w))) <> ")",
-      "  , (\"awaits\", " <> hs (T.intercalate "," (workflowAwaitLabels (wfBody w))) <> ")",
-      "  , (\"patches\", " <> hs (T.intercalate "," (workflowPatchIds (wfBody w))) <> ")",
-      "  ]"
+      "  WorkflowFacts",
+      "    { workflowFactName = " <> hs (wfStable w),
+      "    , workflowFactIdVia = " <> hs (wfIdVia w),
+      "    , workflowFactIdField = " <> hs (maybe "input" id (wfIdField w)),
+      "    , workflowFactBody = " <> stringList (map bodyTag (wfBody w)),
+      "    , workflowFactAwaitLabels = " <> stringList (workflowAwaitLabels (wfBody w)),
+      "    , workflowFactPatchIds = " <> stringList (workflowPatchIds (wfBody w)),
+      "    }"
     ]
   where
     hs = tshow
+    stringList values = "[" <> T.intercalate ", " (map hs values) <> "]"
     bodyTag (WfStep l _ _) = "step:" <> l
     bodyTag (WfAwait l _ _) = "await:" <> l
     bodyTag (WfSleep l _ _) = "sleep:" <> l
@@ -410,10 +420,9 @@
       ++ aggregateHarnessImports a
       ++ goldenImports
       ++ [ "",
-           "{- | (label, passed). A driver runs these and exits non-zero on any False,",
-           "naming the failing assertion. Filling a hole wrongly turns a specific",
-           "entry False; the scaffold cannot.",
-           "-}",
+           "-- | (label, passed). A driver runs these and exits non-zero on any False,",
+           "-- naming the failing assertion. Filling a hole wrongly turns a specific",
+           "-- entry False; the scaffold cannot.",
            "harnessAssertions :: [(String, Bool)]",
            "harnessAssertions =",
            "  [ (\"validateTransducer is empty\", null (validateTransducer defaultValidationOptions " <> lowerFirst nm <> "Transducer))",
@@ -602,7 +611,7 @@
   ]
   where
     cmdSample = case [c | c <- aCommands a, rcName c == tCommand t] of
-      (c : _) -> "(" <> commandCtorExpr a c <> ")"
+      (c : _) -> "(" <> commandCtorExpr a t c <> ")"
       [] -> "(error \"no command\")"
 
 forwardReplayDecl :: Agg -> Transition -> [Text]
@@ -640,7 +649,7 @@
     forwardRegsName = if null (aRegs a) then "_forwardRegs" else "forwardRegs"
     replayRegsName = if null (aRegs a) then "_replayRegs" else "replayRegs"
     cmdSample = case [c | c <- aCommands a, rcName c == tCommand t] of
-      (c : _) -> "(" <> commandCtorExpr a c <> ")"
+      (c : _) -> "(" <> commandCtorExpr a t c <> ")"
       [] -> "(error \"no command\")"
 
 -- | @(<Ctor> (<Ctor>Data v1 v2 …))@ with positional sample field values.
@@ -651,19 +660,19 @@
     args = T.concat [" " <> sampleValue a fieldName ty | (fieldName, ty) <- rcFields rc]
 
 -- | A transition command sample prefers the initial value of a same-named,
--- same-typed register. This makes the generated acceptance/replay probes honest
--- for declaration-exact @cmd.field == reg.field@ guards while retaining the
--- field-distinct samples used by standalone event codec probes.
-commandCtorExpr :: Agg -> ResolvedCtor -> Text
-commandCtorExpr a rc =
+-- same-typed register only when the guard explicitly equates those two paths.
+-- Inequality guards retain the ordinary sample so an enum such as @Paid@ does
+-- not accidentally inherit a forbidden @Free@ register initial value.
+commandCtorExpr :: Agg -> Transition -> ResolvedCtor -> Text
+commandCtorExpr a transition rc =
   "(" <> rcName rc <> " (" <> rcName rc <> "Data" <> args <> "))"
   where
-    args = T.concat [" " <> commandSampleValue a fieldName ty | (fieldName, ty) <- rcFields rc]
+    args = T.concat [" " <> commandSampleValue a transition fieldName ty | (fieldName, ty) <- rcFields rc]
 
-commandSampleValue :: Agg -> Text -> ResolvedAggregateType -> Text
-commandSampleValue aggregate fieldName fieldType = case fieldType of
-  AggregateNominal nominal
-    | nominalEqualityUsedInGeneratedExpressions nominal aggregate -> case find matchesRegister (aRegs aggregate) of
+commandSampleValue :: Agg -> Transition -> Text -> ResolvedAggregateType -> Text
+commandSampleValue aggregate transition fieldName fieldType = case fieldType of
+  AggregateNominal _
+    | guardEquatesCommandAndRegister transition fieldName -> case find matchesRegister (aRegs aggregate) of
         Just register -> regInitialValueForHarness aggregate register
         Nothing -> fallback
   _ -> fallback
@@ -671,8 +680,22 @@
     fallback = sampleValue aggregate fieldName fieldType
     matchesRegister register = rrName register == fieldName && rrType register == fieldType
     regInitialValueForHarness owner register = case rrType register of
-      AggregateNominal nominal -> fromMaybe (renderRegisterInitial (rrInitial register)) (generatedIdSampleHaskell owner nominal)
+      AggregateNominal nominal -> case resolvedNominalOwnership nominal of
+        ConsumerNominal {} -> renderRegisterInitial (rrInitial register)
+        GeneratedNominal -> fromMaybe (renderRegisterInitial (rrInitial register)) (generatedIdSampleHaskell owner nominal)
       _ -> renderRegisterInitial (rrInitial register)
+
+guardEquatesCommandAndRegister :: Transition -> Text -> Bool
+guardEquatesCommandAndRegister transition fieldName = maybe False containsEquality (tGuard transition)
+  where
+    containsEquality = \case
+      EOr left right -> containsEquality left || containsEquality right
+      EAnd left right -> containsEquality left || containsEquality right
+      ECmp OpEq left right -> matchingPaths left right || matchingPaths right left
+      _ -> False
+    matchingPaths (EPath _ CommandRoot [commandField]) (EPath _ RegisterRoot [registerField]) =
+      commandField == fieldName && registerField == fieldName
+    matchingPaths _ _ = False
 
 sampleValue :: Agg -> Text -> ResolvedAggregateType -> Text
 sampleValue a fieldName ty = case ty of
diff --git a/src/Keiro/Dsl/IdDomain.hs b/src/Keiro/Dsl/IdDomain.hs
--- a/src/Keiro/Dsl/IdDomain.hs
+++ b/src/Keiro/Dsl/IdDomain.hs
@@ -5,6 +5,7 @@
     IdDomainFailure (..),
     enforcedIdDomainVersion,
     idDomainContractFor,
+    contractIdDomainContractFor,
     idDomainIdentity,
     idDomainIdentitiesForService,
     idDomainAcceptsText,
@@ -17,17 +18,26 @@
 import Data.Text (Text)
 import Data.Text qualified as T
 import Keiro.Codec.IdDomain
-import Keiro.Dsl.Grammar (IdDecl (..), Spec (..))
-import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveRuntimeSemantics)
+import Keiro.Dsl.Grammar (ContractEvent (..), ContractField (..), ContractNode (..), ContractType (..), IdDecl (..), Node (..), Spec (..))
+import Keiro.Dsl.LanguageVersion (RuntimeCapability (..), runtimeProfileHasCapability)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveRuntimeProfile)
 
 -- | Versions 1 and 2 intentionally return 'Nothing': their generated IDs
 -- admitted arbitrary text. Runtime-semantics generation 2 is the first
 -- enforcing contract.
 idDomainContractFor :: EffectiveLanguageContract -> Text -> Maybe IdDomainContract
 idDomainContractFor languageContract prefix
-  | effectiveRuntimeSemantics languageContract /= "keiro-dsl/runtime-semantics/2" = Nothing
-  | otherwise = Just (typeIdV7Domain prefix)
+  | runtimeProfileHasCapability (effectiveRuntimeProfile languageContract) GeneratedIdDomainTypeIdV7 =
+      Just (typeIdV7Domain prefix)
+  | otherwise = Nothing
 
+-- | Public contract DTOs adopt the frozen TypeID-v7 admission contract only
+-- in runtime semantics 3. Aggregate IDs retain the independent selector above.
+contractIdDomainContractFor :: EffectiveLanguageContract -> Text -> Maybe IdDomainContract
+contractIdDomainContractFor languageContract prefix
+  | runtimeProfileHasCapability (effectiveRuntimeProfile languageContract) ContractIdDomainTypeIdV7 = Just (typeIdV7Domain prefix)
+  | otherwise = Nothing
+
 -- | Durable identity for the runtime admission domain of one declaration.
 -- This is deliberately separate from nominal equality: IDs without equality
 -- expressions still have a construction and codec contract.
@@ -45,7 +55,20 @@
 
 idDomainIdentitiesForService :: CheckedService -> [Text]
 idDomainIdentitiesForService service =
-  [ idDomainIdentity (idName declaration) contract
-  | declaration <- specIds (checkedSpec service),
-    Just contract <- [idDomainContractFor (checkedLanguageContract service) (idPrefix declaration)]
-  ]
+  aggregateIdentities <> contractIdentities
+  where
+    spec = checkedSpec service
+    languageContract = checkedLanguageContract service
+    aggregateIdentities =
+      [ idDomainIdentity (idName declaration) contract
+      | declaration <- specIds spec,
+        Just contract <- [idDomainContractFor languageContract (idPrefix declaration)]
+      ]
+    contractIdentities =
+      [ idDomainIdentity ("contract:" <> ctrName contractNode <> "." <> ceName event <> "." <> cfName field) contract
+      | NContract contractNode <- specNodes spec,
+        event <- ctrEvents contractNode,
+        field <- ceFields event,
+        CTypeId prefix <- [cfType field],
+        Just contract <- [contractIdDomainContractFor languageContract prefix]
+      ]
diff --git a/src/Keiro/Dsl/LanguageVersion.hs b/src/Keiro/Dsl/LanguageVersion.hs
--- a/src/Keiro/Dsl/LanguageVersion.hs
+++ b/src/Keiro/Dsl/LanguageVersion.hs
@@ -1,3 +1,5 @@
+{-# OPTIONS_GHC -Werror=incomplete-patterns #-}
+
 -- | The released-language contract selected by a @.keiro@ source.
 --
 -- Source-language provenance deliberately wraps the semantic 'Spec' rather than
@@ -17,9 +19,20 @@
     SyntaxProfile,
     syntaxProfileIdentifier,
     syntaxProfileSupportsFeature,
+    RuntimeSemanticsProfile,
+    runtimeProfileIdentifier,
+    runtimeProfileHasCapability,
+    runtimeProfileFoldSegments,
+    RuntimeCapability (..),
+    capabilityFoldSegment,
+    LanguageSupport (..),
+    languageSupportText,
     LanguageDefinition (..),
+    definitionRuntimeSemantics,
     languageRegistry,
     supportedLanguageVersions,
+    currentStableLanguageVersion,
+    languageSupportForVersion,
     lookupLanguageDefinition,
     LanguageFeature (..),
     languageFeatureMinimumVersion,
@@ -113,6 +126,63 @@
 syntaxProfileSupportsFeature :: SyntaxProfile -> LanguageFeature -> Bool
 syntaxProfileSupportsFeature SyntaxProfile {profileFeatures} feature = Set.member feature profileFeatures
 
+-- | One independently selectable runtime behavior in a released language
+-- contract.  Constructors are append-only: registry profiles are monotone, and
+-- 'capabilityFoldSegment' must make an explicit fingerprint decision for every
+-- new constructor.
+data RuntimeCapability
+  = GeneratedIdDomainTypeIdV7
+  | NominalEqualityV2
+  | ContractIdDomainTypeIdV7
+  | StrictSpecSurfaceValidation
+  deriving stock (Eq, Ord, Show, Enum, Bounded)
+
+-- | An immutable, explicitly named set of runtime capabilities.  The
+-- constructor stays private so callers select only an authoritative registry
+-- profile rather than widening runtime behavior ad hoc.
+data RuntimeSemanticsProfile = RuntimeSemanticsProfile
+  { runtimeSemanticsIdentifier :: !Text,
+    runtimeSemanticsCapabilities :: !(Set RuntimeCapability)
+  }
+  deriving stock (Eq, Ord, Show, Generic)
+
+runtimeProfileIdentifier :: RuntimeSemanticsProfile -> Text
+runtimeProfileIdentifier RuntimeSemanticsProfile {runtimeSemanticsIdentifier} = runtimeSemanticsIdentifier
+
+runtimeProfileHasCapability :: RuntimeSemanticsProfile -> RuntimeCapability -> Bool
+runtimeProfileHasCapability RuntimeSemanticsProfile {runtimeSemanticsCapabilities} capability =
+  Set.member capability runtimeSemanticsCapabilities
+
+-- | Optional replay-fold identity contributed by one capability.  Duplicate
+-- tokens intentionally collapse at the profile boundary so the two coupled
+-- language-3 behaviors preserve their historical single segment.
+capabilityFoldSegment :: RuntimeCapability -> Maybe Text
+capabilityFoldSegment GeneratedIdDomainTypeIdV7 = Just "semantic-contract:keiro-dsl/runtime-semantics/2"
+capabilityFoldSegment NominalEqualityV2 = Just "semantic-contract:keiro-dsl/runtime-semantics/2"
+capabilityFoldSegment ContractIdDomainTypeIdV7 = Nothing
+capabilityFoldSegment StrictSpecSurfaceValidation = Nothing
+
+runtimeProfileFoldSegments :: RuntimeSemanticsProfile -> [Text]
+runtimeProfileFoldSegments RuntimeSemanticsProfile {runtimeSemanticsCapabilities} =
+  Set.toAscList
+    ( Set.fromList
+        [ segment
+        | capability <- Set.toAscList runtimeSemanticsCapabilities,
+          Just segment <- [capabilityFoldSegment capability]
+        ]
+    )
+
+-- | Whether a released language is recommended for new authoring or retained
+-- only so historical sources keep their released meaning.
+data LanguageSupport
+  = CompatibilityOnly
+  | Stable
+  deriving stock (Eq, Ord, Show, Enum, Bounded)
+
+languageSupportText :: LanguageSupport -> Text
+languageSupportText CompatibilityOnly = "compatibility-only"
+languageSupportText Stable = "stable"
+
 -- | One append-only released-language registry entry.
 data LanguageDefinition = LanguageDefinition
   { definitionVersion :: !LanguageVersion,
@@ -121,10 +191,16 @@
     -- dispatch uses 'definitionSyntaxProfile', never this historical tag.
     definitionBodyParser :: !LanguageBodyParser,
     definitionSyntaxProfile :: !SyntaxProfile,
-    definitionRuntimeSemantics :: !Text
+    definitionRuntimeSemanticsProfile :: !RuntimeSemanticsProfile,
+    definitionSupport :: !LanguageSupport
   }
   deriving stock (Eq, Show, Generic)
 
+-- | Stable compatibility projection used by serialized records and diagnostic
+-- text.  Runtime behavior must query 'definitionRuntimeSemanticsProfile'.
+definitionRuntimeSemantics :: LanguageDefinition -> Text
+definitionRuntimeSemantics = runtimeProfileIdentifier . definitionRuntimeSemanticsProfile
+
 version1 :: LanguageVersion
 version1 = LanguageVersion 1
 
@@ -134,12 +210,16 @@
 version3 :: LanguageVersion
 version3 = LanguageVersion 3
 
+version4 :: LanguageVersion
+version4 = LanguageVersion 4
+
 -- | The authoritative, append-only registry of released language contracts.
 languageRegistry :: NonEmpty LanguageDefinition
 languageRegistry =
-  LanguageDefinition version1 Nothing LanguageBodyParserV1 profileV1 "keiro-dsl/runtime-semantics/1"
-    :| [ LanguageDefinition version2 (Just version1) LanguageBodyParserV2 profileV2 "keiro-dsl/runtime-semantics/1",
-         LanguageDefinition version3 (Just version2) LanguageBodyParserV2 profileV2 "keiro-dsl/runtime-semantics/2"
+  LanguageDefinition version1 Nothing LanguageBodyParserV1 profileV1 runtimeProfileV1 CompatibilityOnly
+    :| [ LanguageDefinition version2 (Just version1) LanguageBodyParserV2 profileV2 runtimeProfileV1 CompatibilityOnly,
+         LanguageDefinition version3 (Just version2) LanguageBodyParserV2 profileV2 runtimeProfileV2 CompatibilityOnly,
+         LanguageDefinition version4 (Just version3) LanguageBodyParserV2 profileV2 runtimeProfileV3 Stable
        ]
 
 profileV1 :: SyntaxProfile
@@ -157,9 +237,43 @@
         ]
     )
 
+runtimeProfileV1 :: RuntimeSemanticsProfile
+runtimeProfileV1 =
+  RuntimeSemanticsProfile
+    "keiro-dsl/runtime-semantics/1"
+    Set.empty
+
+runtimeProfileV2 :: RuntimeSemanticsProfile
+runtimeProfileV2 =
+  RuntimeSemanticsProfile
+    "keiro-dsl/runtime-semantics/2"
+    (Set.fromList [GeneratedIdDomainTypeIdV7, NominalEqualityV2])
+
+runtimeProfileV3 :: RuntimeSemanticsProfile
+runtimeProfileV3 =
+  RuntimeSemanticsProfile
+    "keiro-dsl/runtime-semantics/3"
+    ( Set.fromList
+        [ GeneratedIdDomainTypeIdV7,
+          NominalEqualityV2,
+          ContractIdDomainTypeIdV7,
+          StrictSpecSurfaceValidation
+        ]
+    )
+
 -- | Supported versions, derived from 'languageRegistry'.
 supportedLanguageVersions :: NonEmpty LanguageVersion
 supportedLanguageVersions = definitionVersion <$> languageRegistry
+
+-- | The one registry entry recommended for newly-authored sources.
+currentStableLanguageVersion :: LanguageVersion
+currentStableLanguageVersion =
+  case [definitionVersion definition | definition <- NE.toList languageRegistry, definitionSupport definition == Stable] of
+    [version] -> version
+    _ -> error "keiro-dsl internal invariant: language registry must contain exactly one stable version"
+
+languageSupportForVersion :: LanguageVersion -> Maybe LanguageSupport
+languageSupportForVersion version = definitionSupport <$> lookupLanguageDefinition version
 
 lookupLanguageDefinition :: LanguageVersion -> Maybe LanguageDefinition
 lookupLanguageDefinition version =
diff --git a/src/Keiro/Dsl/Manifest.hs b/src/Keiro/Dsl/Manifest.hs
--- a/src/Keiro/Dsl/Manifest.hs
+++ b/src/Keiro/Dsl/Manifest.hs
@@ -28,7 +28,9 @@
 -- @base@ is always present.
 module Keiro.Dsl.Manifest
   ( renderManifest,
+    renderManifestForService,
     manifestDependencies,
+    manifestDependenciesForService,
     moduleNameOf,
   )
 where
@@ -39,15 +41,23 @@
 import Data.Text qualified as T
 import Keiro.Dsl.AggregateType
 import Keiro.Dsl.Grammar
+import Keiro.Dsl.IdDomain (contractIdDomainContractFor)
 import Keiro.Dsl.MappedConsumer (ConsumerPlan (..), consumerPlan)
 import Keiro.Dsl.NominalType
 import Keiro.Dsl.Scaffold (ScaffoldModule (..))
+import Keiro.Dsl.SemanticContract (CheckedService (..), legacyCheckedService)
 
 -- | Render a Cabal-pasteable manifest from the modules a scaffold run produced
 -- plus the node kinds present (which imply the dependency set). The first argument
 -- names the source spec (for the header comment).
 renderManifest :: Text -> [ScaffoldModule] -> Spec -> Text
-renderManifest specName mods spec =
+renderManifest specName mods = renderManifestForService specName mods . legacyCheckedService
+
+-- | Service-aware manifest renderer. Semantic CLI and workspace routes use
+-- this entry point so language-4 typed contract imports are represented in the
+-- consuming Cabal dependencies.
+renderManifestForService :: Text -> [ScaffoldModule] -> CheckedService -> Text
+renderManifestForService specName mods service =
   T.unlines $
     [ "-- keiro-dsl build manifest for " <> specName,
       "-- Paste the two blocks below into the consuming Cabal stanza.",
@@ -60,9 +70,10 @@
       ++ [ "",
            "build-depends:"
          ]
-      ++ map ("    , " <>) (manifestDependencies spec)
+      ++ map ("    , " <>) (manifestDependenciesForService service)
       ++ consumerBlocks
   where
+    spec = checkedSpec service
     plan = consumerPlan spec
     consumerBlocks
       | null (consumerMappings plan) = []
@@ -84,16 +95,21 @@
 -- | The sorted, deduplicated dependency set implied by the node kinds present
 -- in the spec. @base@ is always included.
 manifestDependencies :: Spec -> [Text]
-manifestDependencies spec =
-  sort (nub ("base" : consumerPackages (consumerPlan spec) <> concatMap (depsForNode spec) (specNodes spec)))
+manifestDependencies = manifestDependenciesForService . legacyCheckedService
 
+manifestDependenciesForService :: CheckedService -> [Text]
+manifestDependenciesForService service =
+  sort (nub ("base" : consumerPackages (consumerPlan spec) <> concatMap (depsForNode service spec) (specNodes spec)))
+  where
+    spec = checkedSpec service
+
 -- | The dependencies a single node kind implies (see the module header table).
-depsForNode :: Spec -> Node -> [Text]
-depsForNode spec n = case n of
+depsForNode :: CheckedService -> Spec -> Node -> [Text]
+depsForNode service spec n = case n of
   NAggregate aggregate -> ["aeson", "keiki", "keiro", "text"] <> aggregateDependencies spec aggregate
   NProcess {} -> ["aeson", "keiki", "keiro", "shibuya-core", "text", "time", "uuid"]
   NRouter {} -> ["effectful-core", "keiro", "shibuya-core", "text"]
-  NContract {} -> ["aeson", "text"]
+  NContract contract -> ["aeson", "text"] <> [dependency | hasTypedContractId contract, dependency <- ["keiro-core", "mmzk-typeid"]]
   NIntake {} -> integration
   NEmit {} -> integration
   NPublisher {} -> integration
@@ -104,6 +120,13 @@
   NOperation {} -> ["effectful-core", "keiro", "text"]
   where
     integration = ["effectful-core", "hasql-transaction", "keiro", "kiroku-store"]
+    hasTypedContractId contract =
+      or
+        [ contractIdDomainContractFor (checkedLanguageContract service) prefix /= Nothing
+        | event <- ctrEvents contract,
+          field <- ceFields event,
+          CTypeId prefix <- [cfType field]
+        ]
 
 aggregateDependencies :: Spec -> Aggregate -> [Text]
 aggregateDependencies spec aggregate =
diff --git a/src/Keiro/Dsl/NominalType.hs b/src/Keiro/Dsl/NominalType.hs
--- a/src/Keiro/Dsl/NominalType.hs
+++ b/src/Keiro/Dsl/NominalType.hs
@@ -19,11 +19,8 @@
     NominalTypeRegistry,
     nominalTypes,
     lookupNominalType,
-    nominalEqualityContract,
     nominalEqualityContractForService,
-    nominalEqualityIdentity,
     nominalEqualityIdentityForService,
-    nominalEqualityIdentities,
     nominalEqualityIdentitiesForService,
     NominalTypeError (..),
     resolveNominalTypes,
@@ -41,9 +38,9 @@
 import Data.TypeID qualified as TypeID
 import GHC.Generics (Generic)
 import Keiro.Dsl.Grammar
-import Keiro.Dsl.IdDomain (idDomainContractFor, idDomainVersion)
-import Keiro.Dsl.LanguageVersion (SourceLanguage (..))
-import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveLanguageContract)
+import Keiro.Dsl.IdDomain (enforcedIdDomainVersion)
+import Keiro.Dsl.LanguageVersion (RuntimeCapability (..), runtimeProfileHasCapability)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveRuntimeProfile)
 import Keiro.Dsl.TypeGraph
 
 data NominalScalarRepresentation
@@ -123,26 +120,22 @@
 lookupNominalType :: Name -> NominalTypeRegistry -> Maybe ResolvedNominalType
 lookupNominalType name = Map.lookup name . nominalTypes
 
--- | Resolve the declaration-scoped equality contract. Nominal scalar wrappers
--- keep their existing scalar projection behavior; this contract is the new
--- authority only for IDs and enums.
-nominalEqualityContract :: ResolvedNominalType -> Maybe CheckedNominalEquality
-nominalEqualityContract = nominalEqualityContractForService (effectiveLanguageContract LegacyUnversioned)
-
 nominalEqualityContractForService :: EffectiveLanguageContract -> ResolvedNominalType -> Maybe CheckedNominalEquality
 nominalEqualityContractForService languageContract nominal = case resolvedNominalRepresentation nominal of
   IdRepresentation prefix ->
     Just
       CheckedNominalEquality
         { equalityKeyRepresentation = NominalTextEqualityKey,
-          equalityDomain = case idDomainContractFor languageContract prefix of
-            Just contract -> EnforcedTypeIdV7TextDomain prefix (idDomainVersion contract)
-            Nothing -> case resolvedNominalOwnership nominal of
-              GeneratedNominal -> LegacyUnrestrictedTextDomain
-              ConsumerNominal {} -> TypeIdTextDomain prefix,
-          equalityContractVersion = case idDomainContractFor languageContract prefix of
-            Just _ -> "keiro-dsl/nominal-equality/2"
-            Nothing -> nominalEqualityContractVersion
+          equalityDomain =
+            if enforcesNominalEqualityV2
+              then EnforcedTypeIdV7TextDomain prefix enforcedIdDomainVersion
+              else case resolvedNominalOwnership nominal of
+                GeneratedNominal -> LegacyUnrestrictedTextDomain
+                ConsumerNominal {} -> TypeIdTextDomain prefix,
+          equalityContractVersion =
+            if enforcesNominalEqualityV2
+              then "keiro-dsl/nominal-equality/2"
+              else nominalEqualityContractVersion
         }
   EnumRepresentation constructors ->
     Just
@@ -152,13 +145,13 @@
           equalityContractVersion = nominalEqualityContractVersion
         }
   ScalarRepresentation {} -> Nothing
+  where
+    enforcesNominalEqualityV2 =
+      runtimeProfileHasCapability (effectiveRuntimeProfile languageContract) NominalEqualityV2
 
 -- | Stable, checked identity used by generated projection tags, fingerprints,
 -- scaffold history, and explain output. It includes the existing binding
 -- authority rather than introducing a second consumer equality function.
-nominalEqualityIdentity :: ResolvedNominalType -> Maybe Text
-nominalEqualityIdentity = nominalEqualityIdentityForService (effectiveLanguageContract LegacyUnversioned)
-
 nominalEqualityIdentityForService :: EffectiveLanguageContract -> ResolvedNominalType -> Maybe Text
 nominalEqualityIdentityForService languageContract nominal = do
   equality <- nominalEqualityContractForService languageContract nominal
@@ -186,9 +179,6 @@
           "binding=" <> unQualifiedValueName (consumerNominalBinding binding),
           "binding-version=" <> unBindingVersion (consumerNominalBindingVersion binding)
         ]
-
-nominalEqualityIdentities :: Spec -> [Text]
-nominalEqualityIdentities spec = nominalEqualityIdentitiesForService (CheckedService (effectiveLanguageContract LegacyUnversioned) spec)
 
 nominalEqualityIdentitiesForService :: CheckedService -> [Text]
 nominalEqualityIdentitiesForService service = case resolveNominalTypes spec of
diff --git a/src/Keiro/Dsl/Parser/Aggregate.hs b/src/Keiro/Dsl/Parser/Aggregate.hs
--- a/src/Keiro/Dsl/Parser/Aggregate.hs
+++ b/src/Keiro/Dsl/Parser/Aggregate.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Aggregate declarations, clauses, transitions, and expression ownership.
 module Keiro.Dsl.Parser.Aggregate
   ( pAggregate,
diff --git a/src/Keiro/Dsl/Parser/Coordination.hs b/src/Keiro/Dsl/Parser/Coordination.hs
--- a/src/Keiro/Dsl/Parser/Coordination.hs
+++ b/src/Keiro/Dsl/Parser/Coordination.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Process-manager, router, timer, dispatch, and correlation syntax.
 module Keiro.Dsl.Parser.Coordination
   ( pProcess,
diff --git a/src/Keiro/Dsl/Parser/Core.hs b/src/Keiro/Dsl/Parser/Core.hs
--- a/src/Keiro/Dsl/Parser/Core.hs
+++ b/src/Keiro/Dsl/Parser/Core.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
 -- EP-4 integration: structural keywords never used as identifiers, so a
 -- list like @accept A B C@ stops at the next block keyword.
 -- EP-5 pgmq structural keywords.
@@ -12,7 +11,6 @@
 --------------------------------------------------------------------------------
 -- Helpers
 --------------------------------------------------------------------------------
-{-# LANGUAGE ImportQualifiedPost #-}
 
 -- | Parser primitives shared by every internal grammar concern.
 module Keiro.Dsl.Parser.Core
diff --git a/src/Keiro/Dsl/Parser/Document.hs b/src/Keiro/Dsl/Parser/Document.hs
--- a/src/Keiro/Dsl/Parser/Document.hs
+++ b/src/Keiro/Dsl/Parser/Document.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Released-language dispatch and complete document composition.
 module Keiro.Dsl.Parser.Document
   ( parseSurfaceSource,
diff --git a/src/Keiro/Dsl/Parser/Expression.hs b/src/Keiro/Dsl/Parser/Expression.hs
--- a/src/Keiro/Dsl/Parser/Expression.hs
+++ b/src/Keiro/Dsl/Parser/Expression.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Legacy and typed scalar expression syntax.
 module Keiro.Dsl.Parser.Expression
   ( pExpr,
diff --git a/src/Keiro/Dsl/Parser/Mapped.hs b/src/Keiro/Dsl/Parser/Mapped.hs
--- a/src/Keiro/Dsl/Parser/Mapped.hs
+++ b/src/Keiro/Dsl/Parser/Mapped.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Consumer-owned mapped and nominal type syntax.
 module Keiro.Dsl.Parser.Mapped
   ( pMappedTopItem,
diff --git a/src/Keiro/Dsl/Parser/Preamble.hs b/src/Keiro/Dsl/Parser/Preamble.hs
--- a/src/Keiro/Dsl/Parser/Preamble.hs
+++ b/src/Keiro/Dsl/Parser/Preamble.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Released-language preamble recognition and source selection.
 module Keiro.Dsl.Parser.Preamble
   ( contextualDiagnostic,
diff --git a/src/Keiro/Dsl/Parser/Workflow.hs b/src/Keiro/Dsl/Parser/Workflow.hs
--- a/src/Keiro/Dsl/Parser/Workflow.hs
+++ b/src/Keiro/Dsl/Parser/Workflow.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | Durable workflow and operation syntax.
 module Keiro.Dsl.Parser.Workflow
   ( pWorkflow,
diff --git a/src/Keiro/Dsl/ReplayImpact.hs b/src/Keiro/Dsl/ReplayImpact.hs
--- a/src/Keiro/Dsl/ReplayImpact.hs
+++ b/src/Keiro/Dsl/ReplayImpact.hs
@@ -13,13 +13,13 @@
   ( AggregateImpact (..),
     ReplayImpact (..),
     replayImpactServices,
-    replayImpact,
     renderReplayImpact,
   )
 where
 
 import Data.Aeson (ToJSON (..), object, (.=))
-import Data.List (delete, find)
+import Data.Foldable (traverse_)
+import Data.List (delete, sortOn)
 import Data.List.NonEmpty qualified as NE
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
@@ -28,11 +28,11 @@
 import Data.Text (Text)
 import Data.Text qualified as Text
 import Keiro.Dsl.AggregateType
-import Keiro.Dsl.FoldFingerprint (aggregateFoldSurfaceForService)
+import Keiro.Dsl.CanonicalEncoding (canonicalExpr, canonicalTransition)
+import Keiro.Dsl.FoldFingerprint (FoldSurfaceError, aggregateFoldSurfaceForService)
 import Keiro.Dsl.Grammar
 import Keiro.Dsl.NominalType
-import Keiro.Dsl.PrettyPrint (renderTransition)
-import Keiro.Dsl.SemanticContract (CheckedService (..), legacyCheckedService)
+import Keiro.Dsl.SemanticContract (CheckedService (..))
 import Keiro.Dsl.TypeGraph (BindingVersion (..), CanonicalTypeId (..), MappedKey (..), QualifiedValueName (..), TypeGraph (..), resolveTypeGraph, wireFingerprint)
 
 -- | The smallest conservative audit input for one aggregate.
@@ -65,9 +65,15 @@
 
 -- | Compute replay impact for every aggregate that existed under the old
 -- effective semantic contract.
-replayImpactServices :: CheckedService -> CheckedService -> ReplayImpact
-replayImpactServices oldService newService =
-  case Map.filter hasImpact impacts of
+replayImpactServices :: CheckedService -> CheckedService -> Either FoldSurfaceError ReplayImpact
+replayImpactServices oldService newService = do
+  traverse_ (aggregateFoldSurfaceForService oldService . snd) oldAggregates
+  traverse_ (aggregateFoldSurfaceForService newService . snd) (Map.toList newAggregates)
+  resolvedImpacts <-
+    traverse
+      (\(name, oldAggregate) -> fmap ((,) name) (maybe (pure (removedAggregateImpact oldAggregate)) (matchedAggregateImpact oldService newService oldAggregate) (Map.lookup name newAggregates)))
+      oldAggregates
+  pure $ case Map.filter hasImpact (Map.fromList resolvedImpacts) of
     filtered
       | Map.null filtered -> ReplayNeutral
       | otherwise -> ReplayAffected filtered
@@ -76,18 +82,7 @@
     newSpec = checkedSpec newService
     oldAggregates = [(aggName aggregate, aggregate) | NAggregate aggregate <- specNodes oldSpec]
     newAggregates = Map.fromList [(aggName aggregate, aggregate) | NAggregate aggregate <- specNodes newSpec]
-    impacts =
-      Map.fromList
-        [ (name, maybe (removedAggregateImpact oldAggregate) (matchedAggregateImpact oldService newService oldAggregate) (Map.lookup name newAggregates))
-        | (name, oldAggregate) <- oldAggregates
-        ]
 
--- | Compatibility wrapper for graph-only callers. It explicitly compares both
--- sides under legacy/version-1 runtime semantics.
-replayImpact :: Spec -> Spec -> ReplayImpact
-replayImpact oldSpec newSpec =
-  replayImpactServices (legacyCheckedService oldSpec) (legacyCheckedService newSpec)
-
 hasImpact :: AggregateImpact -> Bool
 hasImpact impact =
   not (Set.null (eventTypes impact))
@@ -100,15 +95,22 @@
       includeSnapshotStreams = True
     }
 
-matchedAggregateImpact :: CheckedService -> CheckedService -> Aggregate -> Aggregate -> AggregateImpact
-matchedAggregateImpact oldService newService oldAggregate newAggregate =
-  AggregateImpact
-    { eventTypes =
-        decodeAffected
-          <> transitionAffected
-          <> if nonTransitionFoldChanged then oldEventTypes else Set.empty,
-      includeSnapshotStreams = transitionFoldChanged || nonTransitionFoldChanged || mappedRegisterChanged
-    }
+matchedAggregateImpact :: CheckedService -> CheckedService -> Aggregate -> Aggregate -> Either FoldSurfaceError AggregateImpact
+matchedAggregateImpact oldService newService oldAggregate newAggregate = do
+  oldSurface <- aggregateFoldSurfaceForService oldService oldAggregate
+  newNonTransitionSurface <-
+    aggregateFoldSurfaceForService
+      newService
+      newAggregate {aggTransitions = aggTransitions oldAggregate}
+  let nonTransitionFoldChanged = oldSurface /= newNonTransitionSurface
+  pure
+    AggregateImpact
+      { eventTypes =
+          decodeAffected
+            <> transitionAffected
+            <> if nonTransitionFoldChanged then oldEventTypes else Set.empty,
+        includeSnapshotStreams = transitionFoldChanged || nonTransitionFoldChanged || mappedRegisterChanged
+      }
   where
     oldSpec = checkedSpec oldService
     newSpec = checkedSpec newService
@@ -119,13 +121,6 @@
         /= mappedRegisterSurface newSpec newAggregate
     (transitionAffected, transitionFoldChanged) =
       changedTransitionEvents (aggTransitions oldAggregate) (aggTransitions newAggregate)
-    nonTransitionFoldChanged =
-      aggregateFoldSurfaceForService oldService oldAggregate
-        /= aggregateFoldSurfaceForService
-          newService
-          newAggregate
-            { aggTransitions = aggTransitions oldAggregate
-            }
 
 decodeSurfaceAffected :: Spec -> Spec -> Aggregate -> Aggregate -> Set Name
 decodeSurfaceAffected oldSpec newSpec oldAggregate newAggregate =
@@ -222,37 +217,77 @@
 
 changedTransitionEvents :: [Transition] -> [Transition] -> (Set Name, Bool)
 changedTransitionEvents oldTransitions newTransitions =
-  go oldTransitions newTransitions Set.empty False
+  foldl'
+    (\(affected, changed) key -> let (groupAffected, groupChanged) = compareGroup key in (affected <> groupAffected, changed || groupChanged))
+    (Set.empty, False)
+    (Set.toAscList allKeys)
   where
-    go [] _ affected changed = (affected, changed)
-    go (oldTransition : remainingOld) remainingNew affected changed =
-      case find (sameSurface oldTransition) remainingNew of
-        Just exact ->
-          go remainingOld (delete exact remainingNew) affected changed
-        Nothing ->
-          case find (sameIdentity oldTransition) remainingNew of
-            Just candidate
-              | guardOnlyLoosening oldTransition candidate ->
-                  go remainingOld (delete candidate remainingNew) affected changed
-              | otherwise ->
-                  go
-                    remainingOld
-                    (delete candidate remainingNew)
-                    (affected <> emittedBy oldTransition <> emittedBy candidate)
-                    True
-            Nothing ->
-              go
-                remainingOld
-                remainingNew
-                (affected <> emittedBy oldTransition)
-                True
+    oldGroups = transitionGroups oldTransitions
+    newGroups = transitionGroups newTransitions
+    allKeys = Map.keysSet oldGroups <> Map.keysSet newGroups
+    compareGroup key =
+      let (afterExactOld, afterExactNew) = cancelExact (Map.findWithDefault [] key oldGroups) (Map.findWithDefault [] key newGroups)
+          (remainingOld, remainingNew) = cancelLoosenings afterExactOld afterExactNew
+          sortedOld = sortOn transitionSortKey remainingOld
+          sortedNew = sortOn transitionSortKey remainingNew
+          (pairedOld, unpairedOld) = splitAt (length sortedNew) sortedOld
+          pairedNew = take (length pairedOld) sortedNew
+          pairedEvents = Set.unions [emittedBy old <> emittedBy new | (old, new) <- zip pairedOld pairedNew]
+          removedEvents = Set.unions (map emittedBy unpairedOld)
+          changed = not (null pairedOld) || not (null unpairedOld)
+       in (pairedEvents <> removedEvents, changed)
 
-    sameSurface left right = renderTransition left == renderTransition right
-    sameIdentity left right =
-      tMode left == tMode right
-        && tSource left == tSource right
-        && tCommand left == tCommand right
+    transitionGroups =
+      Map.fromListWith (<>)
+        . map (\transition -> (transitionIdentity transition, [transition]))
+    transitionIdentity transition =
+      ( modeKey (tMode transition),
+        tSource transition,
+        tCommand transition
+      )
+    modeKey TmLive = "live" :: Text
+    modeKey TmReplayOnly = "replay-only"
+    transitionSortKey transition =
+      (maybe "" canonicalExpr (tGuard transition), canonicalTransition transition)
     emittedBy = Set.fromList . tEmits
+
+-- | Remove byte-identical transitions as a multiset. Sorting makes duplicate
+-- cancellation independent of declaration order.
+cancelExact :: [Transition] -> [Transition] -> ([Transition], [Transition])
+cancelExact oldTransitions newTransitions = go sortedOld sortedNew [] []
+  where
+    sortedOld = sortOn canonicalTransition oldTransitions
+    sortedNew = sortOn canonicalTransition newTransitions
+    go [] remainingNew unmatchedOld unmatchedNew = (reverse unmatchedOld, reverse unmatchedNew <> remainingNew)
+    go remainingOld [] unmatchedOld unmatchedNew = (reverse unmatchedOld <> remainingOld, reverse unmatchedNew)
+    go old@(oldTransition : remainingOld) new@(newTransition : remainingNew) unmatchedOld unmatchedNew =
+      case compare (canonicalTransition oldTransition) (canonicalTransition newTransition) of
+        LT -> go remainingOld new (oldTransition : unmatchedOld) unmatchedNew
+        EQ -> go remainingOld remainingNew unmatchedOld unmatchedNew
+        GT -> go old remainingNew unmatchedOld (newTransition : unmatchedNew)
+
+-- | Deterministically cancel every provable guard-only loosening. At each step
+-- the lexicographically smallest canonical pair wins, so ambiguous siblings do
+-- not inherit declaration-order semantics.
+cancelLoosenings :: [Transition] -> [Transition] -> ([Transition], [Transition])
+cancelLoosenings oldTransitions newTransitions =
+  case sortOn looseningPairKey candidates of
+    [] -> (oldTransitions, newTransitions)
+    (oldTransition, newTransition) : _ ->
+      cancelLoosenings (delete oldTransition oldTransitions) (delete newTransition newTransitions)
+  where
+    candidates =
+      [ (oldTransition, newTransition)
+      | oldTransition <- oldTransitions,
+        newTransition <- newTransitions,
+        guardOnlyLoosening oldTransition newTransition
+      ]
+    looseningPairKey (oldTransition, newTransition) =
+      ( maybe "" canonicalExpr (tGuard oldTransition),
+        canonicalTransition oldTransition,
+        maybe "" canonicalExpr (tGuard newTransition),
+        canonicalTransition newTransition
+      )
 
 -- | A syntactically provable loosening preserves every old transition match.
 --
diff --git a/src/Keiro/Dsl/Scaffold.hs b/src/Keiro/Dsl/Scaffold.hs
--- a/src/Keiro/Dsl/Scaffold.hs
+++ b/src/Keiro/Dsl/Scaffold.hs
@@ -1,5479 +1,5915 @@
 -- | The scaffold engine. Given an 'Aggregate' (and a 'Context' naming the
--- service and output module-namespace root), it emits the __symbol-free
--- deterministic layer__ as @-- \@generated@ modules plus a single create-if-absent
--- @Holes.hs@ holding the typed holes a human or coding agent must fill.
---
--- The load-bearing invariant of this module is the __firewall__: no @Generated@
--- module ever contains a keiki symbolic operator (@./=@, @.==@, @.||@, @lit@,
--- @B.slot@, @B.requireGuard@). Those live only in the hand-owned @Holes.hs@. A
--- test ('Generated' text scan) enforces it.
---
--- Scope (EP-1, recorded in the plan's Decision Log): the scaffolder does /not/
--- emit the symbolic transducer body — that is the @buildTransducer@ hole in
--- @Holes.hs@, pinned by the harness. It also does not emit the read-model SQL
--- (the projection @apply@), which is a DB-coupled hole delegated to @codd@/the
--- agent; the @Generated@ Projection module emits only the deterministic
--- @InlineProjection@ wiring and the pure event→status mapping. The decode emitted
--- here is /strict/ (every field required); lenient\/optional decode is EP-4's
--- concern.
-module Keiro.Dsl.Scaffold
-  ( ScaffoldModule (..),
-    ModuleKind (..),
-    Context (..),
-    Placement (..),
-    defaultContext,
-    genPrefixFor,
-    holePrefixFor,
-    generatedNominalModule,
-    NominalUseSite (..),
-    NominalGenerationOwner (..),
-    planNominalGeneration,
-    planNominalGenerationForService,
-    generatedNominalsInTypes,
-    generatedNominalTypeImports,
-    generatedNominalTypeImportsForService,
-    generatedIdSampleHaskell,
-    scaffoldReplayAudit,
-    scaffoldStructural,
-    scaffoldStructuralForService,
-    scaffoldStructuralOwners,
-    scaffoldStructuralOwnersForService,
-    codecComparisonModule,
-    codecComparisonBanner,
-    bindingSkeletonModules,
-    bindingSkeletonOwners,
-    scaffoldAggregateForService,
-    scaffoldAggregate,
-    obsoleteGeneratedOutputHooks,
-    scaffoldProcess,
-    scaffoldRouter,
-    scaffoldContract,
-    scaffoldIntake,
-    scaffoldPublisher,
-    scaffoldWorkqueue,
-    scaffoldReadModel,
-    scaffoldRefusals,
-    windowSeconds,
-
-    -- * Firewall self-check (M3)
-    FirewallSurface (..),
-    firewallSurface,
-    firewallBreaches,
-
-    -- * Internal resolution, shared with "Keiro.Dsl.Harness"
-    Agg (..),
-    aggregateCheckedService,
-    ResolvedRegister (..),
-    ResolvedCtor (..),
-    StructuralProjection (..),
-    resolveAggForService,
-    resolveAgg,
-    nominalEqualityUsedInGeneratedExpressions,
-    projectionSpecs,
-    resolveProjectionModules,
-    nominalProjectionModule,
-    codecMappedDeclarations,
-    FieldCat (..),
-    fieldCat,
-    vertexCtor,
-    initialVertex,
-    firstEnumCtor,
-    lowerFirst,
-    pascal,
-    pascalFromKebab,
-    generatedBanner,
-  )
-where
-
-import Data.Char (isAlpha, isAlphaNum, isUpper, ord, toLower, toUpper)
-import Data.List (find, findIndex, groupBy, isSuffixOf, nub, sort, sortOn)
-import Data.List.NonEmpty (NonEmpty)
-import Data.List.NonEmpty qualified as NE
-import Data.Map.Strict qualified as Map
-import Data.Maybe (fromMaybe, isJust, mapMaybe)
-import Data.Set qualified as Set
-import Data.Text (Text)
-import Data.Text qualified as T
-import Keiro.Dsl.AggregateType
-import Keiro.Dsl.BehaviorCoverage qualified as Behavior
-import Keiro.Dsl.CodecCompare (BranchArm (..), BranchField (..), BranchSchema (..))
-import Keiro.Dsl.EventOutput
-import Keiro.Dsl.ExplainBindings (BindingObligation (..), BindingObligationKind (..), bindingObligations)
-import Keiro.Dsl.Expression
-import Keiro.Dsl.FoldFingerprint (aggregateFoldFingerprintForService)
-import Keiro.Dsl.Grammar
-import Keiro.Dsl.IdDomain (IdDomainContract, idDomainContractFor, idDomainPrefix, idDomainSampleText)
-import Keiro.Dsl.NominalType
-import Keiro.Dsl.PrettyPrint (renderExpr)
-import Keiro.Dsl.ReadModelShape (registryNameFor, subscriptionNameFor)
-import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, legacyCheckedService)
-import Keiro.Dsl.TypeGraph
-import Keiro.Dsl.Validate (sagaCategoryError)
-import Numeric (showHex)
-import Text.Read (readMaybe)
-
--- | One emitted module: its on-disk path (relative to the scaffold @--out@
--- directory), its full text, and whether it is overwritten every run
--- ('Generated') or written only when absent ('HoleStub').
-data ScaffoldModule = ScaffoldModule
-  { modulePath :: !FilePath,
-    moduleText :: !Text,
-    kind :: !ModuleKind,
-    origin :: !Text
-  }
-  deriving stock (Eq, Show)
-
-data ModuleKind
-  = -- | @-- \@generated@; overwritten on every scaffold.
-    Generated
-  | -- | Hand-owned; created only when absent, never overwritten.
-    HoleStub
-  deriving stock (Eq, Show)
-
--- | The threading context: the spec's @context@ name, the chosen output
--- module-namespace root, and the placement style. Extended additively (never
--- re-shaped) by later verticals.
-data Context = Context
-  { contextName :: !Text,
-    -- | @""@ means no namespace prefix (the historical default).
-    moduleRoot :: !Text,
-    -- | 'GeneratedPrefix' is the historical default.
-    placement :: !Placement
-  }
-  deriving stock (Eq, Show)
-
--- | One aggregate-level reason a generated nominal declaration must be visible.
--- The declaration itself is context-owned; these use sites determine the
--- aggregate modules that import it.
-data NominalUseSite = NominalUseSite
-  { nominalUseAggregate :: !Name,
-    nominalUseKind :: !AggregateUseSite
-  }
-  deriving stock (Eq, Ord, Show)
-
--- | The checked generation owner for one unbound ID or enum. Every owner in a
--- service points at the same context-level module, while retaining its source
--- location through 'ResolvedNominalType' and all aggregate use sites explicitly.
-data NominalGenerationOwner = NominalGenerationOwner
-  { nominalDeclaration :: !ResolvedNominalType,
-    nominalModule :: !Text,
-    nominalUseSites :: !(Set.Set NominalUseSite),
-    nominalEqualityUsed :: !Bool
-  }
-  deriving stock (Eq, Show)
-
--- | A context with today's default placement ('GeneratedPrefix', no root prefix)
--- for the given @context@ name. Callers that do not care about placement (the
--- @parse@ path, tests) build their context with this.
-defaultContext :: Text -> Context
-defaultContext name = Context {contextName = name, moduleRoot = "", placement = GeneratedPrefix}
-
--- | The generated-layer namespace for a node, honouring the root prefix and the
--- placement style. The 'Text' argument is the already-pascalised node name (e.g.
--- @Reservation@, @HospitalSurge@). For 'GeneratedPrefix' this is
--- @\<root\>.Generated.\<Ctx\>.\<Node\>@ (identical to the historical layout); for
--- 'CollocatedLeaf' it is @\<root\>.\<Ctx\>.\<Node\>.Generated@.
-genPrefixFor :: Context -> Text -> Text
-genPrefixFor ctx node = case placement ctx of
-  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> "." <> node
-  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> "." <> node <> ".Generated"
-
--- | The hand-owned (hole) namespace for a node: @\<root\>.\<Ctx\>.\<Node\>@ —
--- the same for both placement styles (holes always sit beside the domain).
-holePrefixFor :: Context -> Text -> Text
-holePrefixFor ctx node = rootPrefix ctx <> ctxPascalOf ctx <> "." <> node
-
--- | The one context-level Haskell owner for generated IDs and enums.
-generatedNominalModule :: Context -> Text
-generatedNominalModule ctx = case placement ctx of
-  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Nominals"
-  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.Nominals"
-
--- | The root namespace prefix, dot-terminated, or @""@ when no root is set.
-rootPrefix :: Context -> Text
-rootPrefix ctx = case moduleRoot ctx of r | T.null r -> ""; r -> r <> "."
-
--- | The context name in PascalCase, e.g. @hospital-capacity@ -> @HospitalCapacity@.
-ctxPascalOf :: Context -> Text
-ctxPascalOf = pascalFromKebab . contextName
-
---------------------------------------------------------------------------------
--- Firewall self-check (M3)
---------------------------------------------------------------------------------
-
--- | The canonical keiki surface forbidden in generated modules. Symbolic
--- operators are matched as maximal Haskell symbol tokens, identifiers as complete
--- tokens, qualifiers by their leading module alias, and imports structurally.
-data FirewallSurface = FirewallSurface
-  { forbiddenSymbolic :: ![Text],
-    forbiddenIdents :: ![Text],
-    forbiddenQualifiers :: ![Text],
-    forbiddenImports :: ![Text],
-    restrictedImports :: ![(Text, [Text])]
-  }
-  deriving stock (Eq, Show)
-
-firewallSurface :: FirewallSurface
-firewallSurface =
-  FirewallSurface
-    { forbiddenSymbolic = [".==", "./=", ".<", ".<=", ".>", ".>=", ".&&", ".||", ".+", ".-", ".*", "=:", "*:"],
-      forbiddenIdents = ["lit", "pnot", "tadd", "tsub", "tmul"],
-      forbiddenQualifiers = ["B"],
-      forbiddenImports = ["Keiki.Builder", "Keiki.Operators", "Keiki.Symbolic"],
-      -- Generated aggregate modules use the first two names; generated
-      -- harnesses validate, step, and replay filled holes register by register.
-      restrictedImports =
-        [ ( "Keiki.Core",
-            [ "RegFile",
-              "HsPred",
-              "FieldProjection",
-              "ExactFieldProjection",
-              "FieldWitness",
-              "fieldWitness",
-              "exactFieldWitness",
-              "fieldWitnessAgrees",
-              "applyEventsEither",
-              "defaultValidationOptions",
-              "step",
-              "validateTransducer",
-              "EdgeMode",
-              "EdgeRef",
-              "StepSuccess",
-              "StepFailure",
-              "ReplayEventSpan",
-              "ReplayAttribution",
-              "ReplaySuccess",
-              "applyEventsDetailedEither",
-              "stepDetailedEither",
-              "!"
-            ]
-          )
-        ]
-    }
-
--- | Scan generated modules for firewall breaches, returning every offending
--- @(module path, token, 1-based line number)@. Only modules whose 'kind' is
--- 'Generated' are scanned. Strings and comments are skipped, symbol runs use
--- maximal munch, and keiki imports are checked independently of token spelling.
-firewallBreaches :: [ScaffoldModule] -> [(FilePath, Text, Int)]
-firewallBreaches mods =
-  [ (modulePath m, breach, n)
-  | m <- mods,
-    kind m == Generated,
-    not (authoritativeScalarModule (modulePath m)),
-    (n, line) <- zip [1 ..] (T.lines (moduleText m)),
-    breach <- lineBreaches line
-  ]
-
--- Version-2 aggregate expression and transducer modules are the narrow,
--- intentional exception to the generated symbolic-operator firewall: they
--- are precisely the generated authority that constructs Keiki terms. Every
--- other generated module remains subject to the original firewall.
-authoritativeScalarModule :: FilePath -> Bool
-authoritativeScalarModule path =
-  any (`isSuffixOf` path) ["/Expressions.hs", "/Transducer.hs"]
-
-lineBreaches :: Text -> [Text]
-lineBreaches line = case importModule line of
-  Just _ -> importBreaches line
-  Nothing -> tokenBreaches (codeTokens line)
-  where
-    tokenBreaches = mapMaybe breachFor
-    breachFor (IdentToken ident)
-      | ident `elem` forbiddenIdents firewallSurface = Just ident
-    breachFor (QualifiedToken qualifier)
-      | qualifier `elem` forbiddenQualifiers firewallSurface = Just (qualifier <> ".*")
-    breachFor (SymbolToken symbol)
-      | symbol `elem` forbiddenSymbolic firewallSurface = Just symbol
-    breachFor _ = Nothing
-
-data CodeToken = IdentToken !Text | QualifiedToken !Text | SymbolToken !Text
-
-codeTokens :: Text -> [CodeToken]
-codeTokens = go . T.unpack
-  where
-    go [] = []
-    go ('-' : '-' : _) = []
-    go ('"' : rest) = go (dropString rest)
-    go ('\'' : rest) = go (dropChar rest)
-    go (c : rest)
-      | isIdentStart c =
-          let (identTail, afterIdent) = span isIdentContinue rest
-              ident = T.pack (c : identTail)
-           in case afterIdent of
-                '.' : next : more
-                  | isUpper c && isIdentStart next ->
-                      let (_member, afterMember) = span isIdentContinue more
-                       in QualifiedToken ident : go afterMember
-                _ -> IdentToken ident : go afterIdent
-      | isSymbolChar c =
-          let (symbolTail, afterSymbol) = span isSymbolChar rest
-           in SymbolToken (T.pack (c : symbolTail)) : go afterSymbol
-      | otherwise = go rest
-    isIdentStart c = isAlpha c || c == '_'
-    isIdentContinue c = isAlphaNum c || c == '_' || c == '\''
-    isSymbolChar c = c `elem` ("!#$%&*+./<=>?@\\^|-~:" :: String)
-    dropString [] = []
-    dropString ('\\' : _escaped : rest) = dropString rest
-    dropString ('"' : rest) = rest
-    dropString (_ : rest) = dropString rest
-    dropChar [] = []
-    dropChar ('\\' : _escaped : rest) = dropChar rest
-    dropChar ('\'' : rest) = rest
-    dropChar (_ : rest) = dropChar rest
-
-importBreaches :: Text -> [Text]
-importBreaches line = case importModule line of
-  Nothing -> []
-  Just imported
-    | imported `elem` forbiddenImports firewallSurface -> ["import:" <> imported]
-    | Just allowed <- lookup imported (restrictedImports firewallSurface),
-      not (hasAllowedExplicitImportList allowed line) ->
-        ["import:" <> imported]
-    | otherwise -> []
-
-importModule :: Text -> Maybe Text
-importModule line = case T.words (T.strip line) of
-  "import" : rest -> find (T.isPrefixOf "Keiki.") rest
-  _ -> Nothing
-
-hasAllowedExplicitImportList :: [Text] -> Text -> Bool
-hasAllowedExplicitImportList allowed line =
-  case (T.breakOn "(" line, T.breakOnEnd ")" line) of
-    ((_, open), (close, _))
-      | not (T.null open) && not (T.null close) ->
-          let inside = T.takeWhile (/= ')') (T.drop 1 open)
-              names = filter (not . T.null) (T.split (not . isAlphaNum) inside)
-           in all (`elem` allowed) names
-    _ -> False
-
---------------------------------------------------------------------------------
--- Derived naming
---------------------------------------------------------------------------------
-
--- | Resolved, denormalized view of an aggregate used by every emitter.
-data Agg = Agg
-  { aContext :: !Context,
-    aLanguageContract :: !EffectiveLanguageContract,
-    aSpec :: !Spec,
-    aAggregate :: !Aggregate,
-    aCtxPascal :: !Text,
-    aName :: !Text,
-    aLoc :: !Loc,
-    aVertexType :: !Text,
-    aIds :: ![IdDecl],
-    aEnums :: ![EnumDecl],
-    aRegs :: ![ResolvedRegister],
-    aStates :: ![StateDecl],
-    aCommands :: ![ResolvedCtor],
-    aEvents :: ![ResolvedCtor],
-    -- | Generated IDs and enums used by this aggregate, in stable name order.
-    aGeneratedNominals :: ![ResolvedNominalType],
-    aTransitions :: ![Transition],
-    aOutputMappings :: !(Map.Map (Int, Int) EventOutputMapping),
-    aWire :: !WireSpec,
-    aProjection :: !(Maybe ProjectionSpec),
-    aSnapshot :: !(Maybe SnapshotSpec),
-    aFoldFingerprint :: !Text,
-    aReadModels :: ![ReadModelNode],
-    aTypeGraph :: !(Maybe TypeGraph),
-    aSymbols :: !AggregateSymbols,
-    -- | e.g. @Generated.HospitalCapacity.Reservation@
-    aGenPrefix :: !Text,
-    -- | e.g. @HospitalCapacity.Reservation@
-    aHolePrefix :: !Text
-  }
-
-aggregateCheckedService :: Agg -> CheckedService
-aggregateCheckedService aggregate =
-  CheckedService
-    { checkedLanguageContract = aLanguageContract aggregate,
-      checkedSpec = aSpec aggregate
-    }
-
-data ResolvedRegister = ResolvedRegister
-  { rrName :: !Name,
-    rrType :: !ResolvedAggregateType,
-    rrInitial :: !ResolvedRegisterInitial,
-    rrLoc :: !Loc
-  }
-  deriving stock (Eq, Show)
-
--- | A command or event constructor with its fully-resolved field types.
-data ResolvedCtor = ResolvedCtor
-  { rcName :: !Text,
-    -- | (field name, canonical aggregate type)
-    rcFields :: ![(Text, ResolvedAggregateType)],
-    -- | EP-2: schema version (1 for commands and unversioned events).
-    rcVersion :: !Int,
-    -- | EP-2: the source version this event migrates from (the upcaster step).
-    rcUpcastFrom :: !(Maybe Int)
-  }
-
-defaultWire :: WireSpec
-defaultWire = WireSpec {wireKind = "ctorName", wireFields = "camelCase", wireSchemaVersion = 1}
-
-resolveAgg :: Context -> Spec -> Aggregate -> Agg
-resolveAgg ctx spec = resolveAggForService ctx (legacyCheckedService spec)
-
--- | Resolve one aggregate under the service's effective runtime semantics.
-resolveAggForService :: Context -> CheckedService -> Aggregate -> Agg
-resolveAggForService ctx service agg =
-  Agg
-    { aContext = ctx,
-      aLanguageContract = checkedLanguageContract service,
-      aSpec = spec,
-      aAggregate = agg,
-      aCtxPascal = ctxPascal,
-      aName = nm,
-      aLoc = aggLoc agg,
-      aVertexType = vertexType,
-      aIds = specIds spec,
-      aEnums = specEnums spec,
-      aRegs = map resolveRegister (aggRegs agg),
-      aStates = aggStates agg,
-      aCommands = map resolveCommand (aggCommands agg),
-      aEvents = map resolveEvent (aggEvents agg),
-      aGeneratedNominals = generatedNominalsInTypes aggregateResolvedTypes,
-      aTransitions = aggTransitions agg,
-      aOutputMappings =
-        Map.fromList
-          [ ( (transitionIndex, emitIndex),
-              orDieOutput (eventOutputMapping spec agg transition emitIndex eventName)
-            )
-          | (transitionIndex, transition) <- zip [1 ..] (aggTransitions agg),
-            (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
-          ],
-      aWire = fromMaybe defaultWire (aggWire agg),
-      aProjection = aggProjection agg,
-      aSnapshot = aggSnapshot agg,
-      aFoldFingerprint = aggregateFoldFingerprintForService service agg,
-      aReadModels = [readModel | NReadModel readModel <- specNodes spec],
-      aTypeGraph = either (const Nothing) Just (resolveTypeGraph spec),
-      aSymbols = symbols,
-      aGenPrefix = genPrefixFor ctx nm,
-      aHolePrefix = holePrefixFor ctx nm
-    }
-  where
-    spec = checkedSpec service
-    nm = aggName agg
-    symbols = aggregateSymbols spec
-    ctxPascal = pascalFromKebab (contextName ctx)
-    vertexType = nm <> "Vertex"
-    commandFieldTypes = [(cmdName c, cmdFields c) | c <- aggCommands agg]
-    resolveCommand c = (mkCtor CommandFieldUse (cmdName c) (cmdFields c)) {rcVersion = 1, rcUpcastFrom = Nothing}
-    resolveEvent e =
-      (mkCtor EventFieldUse (evName e) (eventFields e))
-        { rcVersion = evVersion e,
-          rcUpcastFrom = fst <$> evUpcastFrom e
-        }
-      where
-        eventFields ev = case evBody ev of
-          EventFields fs -> fs
-          EventFromCommand cn -> fromMaybe [] (lookup cn commandFieldTypes)
-    mkCtor useSite cn fs =
-      ResolvedCtor
-        { rcName = cn,
-          rcFields = map (\field -> (aggregateFieldName field, orDie (inferAggregateFieldType symbols agg useSite field))) fs,
-          rcVersion = 1,
-          rcUpcastFrom = Nothing
-        }
-    aggregateResolvedTypes =
-      map rrType (map resolveRegister (aggRegs agg))
-        <> map snd (concatMap rcFields (map resolveCommand (aggCommands agg)))
-        <> map snd (concatMap rcFields (map resolveEvent (aggEvents agg)))
-    resolveRegister register =
-      let resolvedType = orDie (resolveAggregateType symbols (regLoc register) RegisterUse (regType register))
-          resolvedInitial = orDie (resolveRegisterInitial symbols (regLoc register) resolvedType (regInitial register))
-       in ResolvedRegister
-            { rrName = regName register,
-              rrType = resolvedType,
-              rrInitial = resolvedInitial,
-              rrLoc = regLoc register
-            }
-    orDie = either (error . ("validated aggregate resolution failed: " <>) . show) id
-    orDieOutput = either (error . ("validated aggregate output resolution failed: " <>) . show) id
-
--- | Keep only generated nominal IDs/enums from a resolved aggregate type list.
--- The map both deduplicates and makes declaration/import order independent of
--- member and field order.
-generatedNominalsInTypes :: [ResolvedAggregateType] -> [ResolvedNominalType]
-generatedNominalsInTypes resolvedTypes =
-  Map.elems . Map.fromList $
-    [ (resolvedNominalName nominal, nominal)
-    | AggregateNominal nominal <- resolvedTypes,
-      GeneratedNominal <- [resolvedNominalOwnership nominal]
-    ]
-
--- | Plan declaration ownership and use closure without emitting text. Parsing
--- and validation already reject malformed declarations; retaining the checked
--- error here keeps this function total for direct library callers.
-planNominalGeneration :: Context -> Spec -> Either (NonEmpty NominalTypeError) [NominalGenerationOwner]
-planNominalGeneration ctx spec = planNominalGenerationForService ctx (legacyCheckedService spec)
-
-planNominalGenerationForService :: Context -> CheckedService -> Either (NonEmpty NominalTypeError) [NominalGenerationOwner]
-planNominalGenerationForService ctx service = do
-  registry <- resolveNominalTypes spec
-  let aggregates = [resolveAggForService ctx service aggregate | NAggregate aggregate <- specNodes spec]
-      generated =
-        [ nominal
-        | nominal <- Map.elems (nominalTypes registry),
-          GeneratedNominal <- [resolvedNominalOwnership nominal]
-        ]
-  pure
-    [ NominalGenerationOwner
-        { nominalDeclaration = nominal,
-          nominalModule = generatedNominalModule ctx,
-          nominalUseSites = Set.fromList (concatMap (usesFor nominal) aggregates),
-          nominalEqualityUsed = any (nominalEqualityUsedInGeneratedExpressions nominal) aggregates
-        }
-    | nominal <- generated
-    ]
-  where
-    spec = checkedSpec service
-    usesFor nominal aggregate =
-      [ NominalUseSite (aName aggregate) useKind
-      | useKind <- aggregateUseKinds nominal aggregate
-      ]
-
-nominalEqualityUsedInGeneratedExpressions :: ResolvedNominalType -> Agg -> Bool
-nominalEqualityUsedInGeneratedExpressions nominal aggregate =
-  any (anyTypedExpression comparesNominal) (resolvedGeneratedExpressions aggregate)
-  where
-    comparesNominal expression = case typedScalarNode expression of
-      TypedEqual left _ -> typedScalarType left == AggregateNominal nominal
-      TypedNotEqual left _ -> typedScalarType left == AggregateNominal nominal
-      _ -> False
-
-aggregateUseKinds :: ResolvedNominalType -> Agg -> [AggregateUseSite]
-aggregateUseKinds nominal aggregate =
-  nub $
-    [RegisterUse | nominal `elem` registerNominals]
-      <> [CommandFieldUse | nominal `elem` commandNominals]
-      <> [EventFieldUse | nominal `elem` eventNominals]
-      <> [CodecUse | nominal `elem` eventNominals]
-      <> [SnapshotUse | hasSnapshot aggregate && nominal `elem` registerNominals]
-      <> [HarnessSampleUse | nominal `elem` commandNominals || nominal `elem` eventNominals]
-      <> [HaskellLoweringUse | nominal `elem` aGeneratedNominals aggregate]
-  where
-    registerNominals = generatedNominalsInTypes (map rrType (aRegs aggregate))
-    commandNominals = generatedNominalsInTypes (map snd (concatMap rcFields (aCommands aggregate)))
-    eventNominals = generatedNominalsInTypes (map snd (concatMap rcFields (aEvents aggregate)))
-
---------------------------------------------------------------------------------
--- Entry point
---------------------------------------------------------------------------------
-
--- | Emit the context-level private structural stratum. Shape modules contain
--- only generated wire representations. The projection facade contains only
--- schema-derived Keiki field witnesses; neither layer owns consumer behavior.
-scaffoldStructural :: Context -> Spec -> [ScaffoldModule]
-scaffoldStructural ctx spec = scaffoldStructuralForService ctx (legacyCheckedService spec)
-
-scaffoldStructuralForService :: Context -> CheckedService -> [ScaffoldModule]
-scaffoldStructuralForService ctx service = map fst (scaffoldStructuralOwnersForService ctx service)
-
--- | 'scaffoldStructural' paired with the mapped declarations each module was
--- emitted for. A shape module names exactly one declaration; a binding skeleton
--- names every declaration whose obligations it carries (several declarations may
--- share one leaf binding module); the projection facade names __none__, because it
--- is emitted once for the whole context from the complete resolved graph.
---
--- This is the attribution seam whole-workspace scaffolding needs: a workspace
--- emits from one merged spec, and this list says which declaration — and therefore
--- which member file — produced each structural module, without parsing the
--- human-readable 'origin' string.
-scaffoldStructuralOwners :: Context -> Spec -> [(ScaffoldModule, [Name])]
-scaffoldStructuralOwners ctx spec = scaffoldStructuralOwnersForService ctx (legacyCheckedService spec)
-
-scaffoldStructuralOwnersForService :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
-scaffoldStructuralOwnersForService ctx service = case resolveTypeGraph (checkedSpec service) of
-  Left _ -> []
-  Right graph ->
-    [(shapeModule ctx graph entry, [sdName (fst entry)]) | entry <- structural]
-      <> projectionModules
-      <> generatedNominalOwners ctx service
-      <> nominalRepresentationOwners ctx spec
-      <> nominalProjectionOwners ctx service
-      <> bindingSkeletonOwners ctx spec graph
-    where
-      structural =
-        [ (declaration, shape)
-        | ResolvedStructural declaration shape <- Map.elems (tgDeclarations graph)
-        ]
-      projectionModules =
-        [ ( ScaffoldModule
-              { modulePath = T.unpack (T.replace "." "/" (structuralProjectionModule ctx) <> ".hs"),
-                moduleText = emitStructuralProjections ctx graph,
-                kind = Generated,
-                origin = "context " <> specContext spec <> " mapped structural facade"
-              },
-            []
-          )
-        | not (null (projectionSpecs graph))
-        ]
-      spec = checkedSpec service
-
--- | Plan one opt-in, non-production historical-codec comparison module.
---
--- The module is intentionally absent from 'scaffoldStructural' and therefore
--- from production manifests and scaffold records. It must be requested by name
--- and is compiled only by consumer-owned test/tool components.
-codecComparisonModule :: Context -> Spec -> Name -> Either Text ScaffoldModule
-codecComparisonModule ctx spec requestedName = do
-  graph <- either (Left . ("mapped type graph did not resolve: " <>) . T.pack . show) Right (resolveTypeGraph spec)
-  (declaration, shape) <- case Map.lookup (MappedKey requestedName) (tgDeclarations graph) of
-    Nothing -> Left ("codec comparison target is not a mapped declaration: " <> requestedName)
-    Just (ResolvedOpaque _) ->
-      Left
-        ( "codec comparison target "
-            <> requestedName
-            <> " is opaque; finite evidence must never upgrade an opaque declaration to a structural claim"
-        )
-    Just (ResolvedStructural declaration shape) -> Right (declaration, shape)
-  owner <- case sortOn aggName (comparisonOwners declaration) of
-    [] ->
-      Left
-        ( "codec comparison target "
-            <> requestedName
-            <> " is not reachable from a persisted private event payload"
-        )
-    aggregate : _ -> Right aggregate
-  let moduleName = structuralPrefix ctx <> ".CodecCompare." <> requestedName
-  pure
-    ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
-        moduleText = emitCodecComparison ctx moduleName graph declaration shape owner,
-        kind = Generated,
-        origin = "non-production codec comparison " <> requestedName
-      }
-  where
-    comparisonOwners declaration =
-      [ aggregate
-      | NAggregate aggregate <- specNodes spec,
-        let resolved = resolveAgg ctx spec aggregate,
-        any ((== sdName declaration) . mappedName) (codecMappedDeclarations resolved)
-      ]
-      where
-        mappedName (ResolvedStructural structural _) = sdName structural
-        mappedName (ResolvedOpaque opaque) = odName opaque
-
-codecComparisonBanner :: Text
-codecComparisonBanner =
-  "-- @generated by keiro-dsl codec comparison; non-production migration evidence; do not edit."
-
-emitCodecComparison :: Context -> Text -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Aggregate -> Text
-emitCodecComparison ctx moduleName graph declaration shape owner =
-  nl
-    [ "",
-      codecComparisonBanner,
-      "-- This module compares historical and generated codecs in consumer-owned tests only.",
-      "-- It is never a runtime fallback and never changes the generated codec's authority.",
-      "module " <> moduleName <> " (compareWithHistorical) where",
-      "",
-      "import Control.Monad (filterM)",
-      "import Data.Aeson (Value)",
-      "import Data.Aeson qualified as Aeson",
-      "import Data.List (sort)",
-      "import Data.List.NonEmpty qualified as NonEmpty",
-      "import Data.Text (Text)",
-      "import Data.Text qualified",
-      "import " <> codecModule <> " qualified as GeneratedCodec",
-      "import Keiro.Codec.Structural (FixtureCases (..))",
-      "import Keiro.Dsl.CodecCompare",
-      "import Keiro.Dsl.TypeGraph (BindingVersion (..), CanonicalTypeId (..), QualifiedValueName (..))",
-      "import System.Directory (doesFileExist, listDirectory)",
-      "import System.FilePath (takeExtension, (</>))",
-      "",
-      "import " <> fixtureModule <> " qualified as ConsumerFixtures",
-      "import " <> hsModule (sdHaskell declaration) <> " qualified as ConsumerDomain",
-      "",
-      "compareWithHistorical :: HistoricalCodec " <> domainType <> " -> FilePath -> IO CompareReport",
-      "compareWithHistorical historicalCodec goldenDirectory = do",
-      "  names <- sort . filter ((== \".json\") . takeExtension) <$> listDirectory goldenDirectory",
-      "  files <- filterM doesFileExist [goldenDirectory </> name | name <- names]",
-      "  loaded <- traverse (loadGolden historicalCodec) files",
-      "  let inputIssues = [issue | Left issue <- loaded]",
-      "      entries = [entry | Right entry <- loaded]",
-      "      typedCases = NonEmpty.toList (fixtureCases ConsumerFixtures." <> fixtureSymbol <> ")",
-      "      encodeObservations =",
-      "        [ EncodeObservation label (hcEncode historicalCodec value) (GeneratedCodec.encode" <> name <> "Mapped value)",
-      "        | (label, value) <- typedCases",
-      "        ]",
-      "      decodeObservations = [observation | (observation, _) <- entries]",
-      "      typedObserved =",
-      "        concat",
-      "          [ observedBranchesFor FromBinding branchSchema (GeneratedCodec.encode" <> name <> "Mapped value)",
-      "          | (_, value) <- typedCases",
-      "          ]",
-      "      historicalObserved =",
-      "        concat [observedBranchesFor HistoricalGolden branchSchema value | (_, values) <- entries, value <- values]",
-      "      declared = declaredBranchesFor FromBinding branchSchema <> declaredBranchesFor HistoricalGolden branchSchema",
-      "      provenance =",
-      "        CompareProvenance",
-      "          { cpHistoricalCodecIdentity = hcIdentity historicalCodec",
-      "          , cpHistoricalCodecVersion = hcVersion historicalCodec",
-      "          , cpCanonicalType = CanonicalTypeId " <> tshow (unCanonicalTypeId (sdCanonical declaration)),
-      "          , cpBindingSymbol = QualifiedValueName " <> tshow (unQualifiedValueName (sdBinding declaration)),
-      "          , cpBindingVersion = BindingVersion " <> tshow (unBindingVersion (sdBindingVersion declaration)),
-      "          , cpWireFingerprint = " <> tshow (wireFingerprint graph name),
-      "          }",
-      "  pure (compareReport provenance inputIssues (encodeObservations <> decodeObservations) declared (typedObserved <> historicalObserved))",
-      "",
-      "loadGolden :: HistoricalCodec " <> domainType <> " -> FilePath -> IO (Either CompareInputIssue (CompareObservation, [Value]))",
-      "loadGolden historicalCodec path = do",
-      "  decoded <- Aeson.eitherDecodeFileStrict path",
-      "  pure $ case decoded of",
-      "    Left reason -> Left (HistoricalGoldenUnreadable path (fromString reason))",
-      "    Right inputValue ->",
-      "      let historicalDecoded = hcDecode historicalCodec inputValue",
-      "          historicalOutcome = normalizeDecode historicalDecoded",
-      "          generatedOutcome = normalizeDecode (GeneratedCodec.decode" <> name <> "Mapped inputValue)",
-      "          observation = DecodeObservation path inputValue historicalOutcome generatedOutcome",
-      "          coveredValues = case historicalDecoded of",
-      "            Right value -> [inputValue, GeneratedCodec.encode" <> name <> "Mapped value]",
-      "            Left _ -> []",
-      "       in Right (observation, coveredValues)",
-      "",
-      "normalizeDecode :: Either Text " <> domainType <> " -> DecodeOutcome",
-      "normalizeDecode = either DecodeFailed (DecodedShape . GeneratedCodec.encode" <> name <> "Mapped)",
-      "",
-      "fromString :: String -> Text",
-      "fromString = Data.Text.pack",
-      "",
-      "branchSchema :: BranchSchema",
-      "branchSchema = " <> renderBranchSchema (branchSchemaFor graph (ResolvedStructural declaration shape))
-    ]
-  where
-    name = sdName declaration
-    domainType = "ConsumerDomain." <> hsType (sdHaskell declaration)
-    codecModule = genPrefixFor ctx (aggName owner) <> ".Codec"
-    fixtureModule = qualifiedModule (sdFixtures declaration)
-    fixtureSymbol = lastSegment (unQualifiedValueName (sdFixtures declaration))
-
-branchSchemaFor :: TypeGraph -> ResolvedMappedDecl -> BranchSchema
-branchSchemaFor graph =
-  foldMappedDecl
-    MappedDeclAlgebra
-      { onStructuralDecl = \_ shape ->
-          foldMappedShape
-            MappedShapeAlgebra
-              { onRecord = \_ _ fields ->
-                  BranchRecord
-                    [ BranchField
-                        (rwfKey field)
-                        (rwfPresence field == POptional)
-                        (branchExpr graph (rwfType field))
-                    | field <- fields
-                    ],
-                onEnum = const BranchScalar,
-                onUnion = \encoding arms ->
-                  BranchUnion
-                    (ueTagField encoding)
-                    (ueContentsField encoding)
-                    [BranchArm (rwaTag arm) (branchExpr graph <$> rwaPayload arm) | arm <- arms]
-              }
-            shape,
-        onOpaqueDecl = const BranchScalar
-      }
-
-branchExpr :: TypeGraph -> ResolvedTypeExpr -> BranchSchema
-branchExpr graph =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = BranchScalar,
-        onInt = BranchScalar,
-        onInteger = BranchScalar,
-        onBool = BranchScalar,
-        onNatural = BranchScalar,
-        onTime = BranchScalar,
-        onJson = BranchScalar,
-        onOptional = BranchOptional,
-        onList = BranchList,
-        onMap = BranchMap,
-        onRef = \key -> maybe BranchScalar (branchSchemaFor graph) (Map.lookup key (tgDeclarations graph))
-      }
-
-renderBranchSchema :: BranchSchema -> Text
-renderBranchSchema schema = case schema of
-  BranchScalar -> "BranchScalar"
-  BranchOptional nested -> "BranchOptional (" <> renderBranchSchema nested <> ")"
-  BranchList nested -> "BranchList (" <> renderBranchSchema nested <> ")"
-  BranchMap nested -> "BranchMap (" <> renderBranchSchema nested <> ")"
-  BranchRecord fields ->
-    "BranchRecord ["
-      <> T.intercalate
-        ", "
-        [ "BranchField "
-            <> tshow (bfWireKey field)
-            <> " "
-            <> (if bfPresenceOptional field then "True" else "False")
-            <> " ("
-            <> renderBranchSchema (bfSchema field)
-            <> ")"
-        | field <- fields
-        ]
-      <> "]"
-  BranchUnion tagField contentsField arms ->
-    "BranchUnion "
-      <> tshow tagField
-      <> " "
-      <> tshow contentsField
-      <> " ["
-      <> T.intercalate
-        ", "
-        [ "BranchArm "
-            <> tshow (baWireTag arm)
-            <> " "
-            <> maybe "Nothing" (\nested -> "(Just (" <> renderBranchSchema nested <> "))") (baPayloadSchema arm)
-        | arm <- arms
-        ]
-      <> "]"
-
--- | Emit one create-once consumer module per distinct qualified obligation
--- owner. Multiple mapped declarations may intentionally share a leaf binding
--- module, so grouping happens by module rather than by declaration.
-bindingSkeletonModules :: Context -> Spec -> TypeGraph -> [ScaffoldModule]
-bindingSkeletonModules ctx spec graph = map fst (bindingSkeletonOwners ctx spec graph)
-
--- | 'bindingSkeletonModules' paired with the mapped declarations whose
--- obligations each skeleton carries, in first-appearance order. A skeleton shared
--- by declarations from different member files therefore names all of them, which
--- is what lets whole-workspace scaffolding treat it as context-level rather than
--- attributing it to an arbitrary member.
-bindingSkeletonOwners :: Context -> Spec -> TypeGraph -> [(ScaffoldModule, [Name])]
-bindingSkeletonOwners ctx spec graph = case bindingObligations spec of
-  Left _ -> []
-  Right obligations ->
-    [ (emitBindingSkeleton ctx spec graph owner entries, nub (map obligationMappedName entries))
-    | (owner, entries) <- Map.toAscList (Map.fromListWith (<>) [(obligationModule obligation, [obligation]) | obligation <- obligations])
-    ]
-
-emitBindingSkeleton :: Context -> Spec -> TypeGraph -> Text -> [BindingObligation] -> ScaffoldModule
-emitBindingSkeleton ctx spec graph owner obligations =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" owner <> ".hs"),
-      moduleText =
-        nl $
-          [ "{-# LANGUAGE DataKinds #-}",
-            "{-# LANGUAGE LambdaCase #-}",
-            "",
-            "-- This is a HAND-OWNED consumer binding skeleton. keiro-dsl creates it once",
-            "-- and never overwrites it. Fill each HOLE and run the generated harness.",
-            "module " <> owner <> " ("
-          ]
-            <> exportLines
-            <> [") where", ""]
-            <> map ("import " <>) imports
-            <> [""]
-            <> intercalateBlank (map renderObligation obligations),
-      kind = HoleStub,
-      origin = "consumer binding skeleton " <> owner
-    }
-  where
-    exportLines =
-      [ (if index == (0 :: Int) then "    " else "  , ") <> obligationSymbol obligation
-      | (index, obligation) <- zip [0 ..] obligations
-      ]
-    imports =
-      sort . nub $
-        [ hsModule (sdHaskell declaration) <> " qualified"
-        | obligation <- obligations,
-          Just (declaration, _) <- [structuralFor obligation]
-        ]
-          <> [ structuralShapeModule ctx (sdName declaration) <> " qualified"
-             | obligation <- obligations,
-               obligationKind obligation == BindingValue,
-               Just (declaration, _) <- [structuralFor obligation]
-             ]
-          <> [ "Keiro.Codec.Structural (FixtureCases, StructuralBinding (..))"
-             | any (\obligation -> obligationCategory obligation == "structural" && obligationKind obligation `elem` [BindingValue, FixtureValue]) obligations
-             ]
-          <> [ hsModule (consumerNominalHaskell binding) <> " qualified"
-             | obligation <- obligations,
-               Just (_, binding) <- [nominalFor obligation]
-             ]
-          <> [ nominalRepresentationModule ctx (resolvedNominalName nominal) <> " qualified"
-             | obligation <- obligations,
-               obligationKind obligation == BindingValue,
-               Just (nominal, _) <- [nominalFor obligation],
-               EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
-             ]
-          <> [ "Keiro.Codec.Nominal (NominalBinding (..), NominalFixtureCases)"
-             | any ((/= "structural") . obligationCategory) obligations
-             ]
-          <> [ "Data.KindID (KindID)"
-             | obligation <- obligations,
-               Just (nominal, _) <- [nominalFor obligation],
-               IdRepresentation {} <- [resolvedNominalRepresentation nominal]
-             ]
-          <> [ "Data.Text (Text)"
-             | obligation <- obligations,
-               Just (nominal, _) <- [nominalFor obligation],
-               ScalarRepresentation NominalText <- [resolvedNominalRepresentation nominal]
-             ]
-          <> [ "Data.Time (UTCTime)"
-             | obligation <- obligations,
-               Just (nominal, _) <- [nominalFor obligation],
-               ScalarRepresentation NominalTime <- [resolvedNominalRepresentation nominal]
-             ]
-          <> [ "Numeric.Natural (Natural)"
-             | obligation <- obligations,
-               Just (nominal, _) <- [nominalFor obligation],
-               ScalarRepresentation NominalNatural <- [resolvedNominalRepresentation nominal]
-             ]
-    renderObligation obligation = case structuralFor obligation of
-      Nothing -> case nominalFor obligation of
-        Just (nominal, _) -> renderNominalObligation nominal obligation
-        Nothing -> ["-- HOLE: declaration disappeared before skeleton rendering"]
-      Just (declaration, shape) -> case obligationKind obligation of
-        BindingValue -> renderBinding ctx declaration shape obligation
-        FixtureValue ->
-          [ "-- HOLE: provide deterministic labelled conformance fixtures for " <> sdName declaration,
-            obligationSignature obligation,
-            obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> sdName declaration <> " fixtures")
-          ]
-        InitialValue ->
-          [ "-- HOLE: provide the initial register value for " <> sdName declaration,
-            obligationSignature obligation,
-            obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> sdName declaration <> " initial value")
-          ]
-    structuralFor obligation = case Map.lookup (MappedKey (obligationMappedName obligation)) (tgDeclarations graph) of
-      Just (ResolvedStructural declaration shape) -> Just (declaration, shape)
-      _ -> Nothing
-    nominalFor obligation = do
-      registry <- either (const Nothing) Just (resolveNominalTypes spec)
-      nominal <- lookupNominalType (obligationMappedName obligation) registry
-      binding <- case resolvedNominalOwnership nominal of
-        ConsumerNominal value -> Just value
-        GeneratedNominal -> Nothing
-      pure (nominal, binding)
-    renderNominalObligation nominal obligation = case obligationKind obligation of
-      BindingValue ->
-        [ "-- HOLE: complete both total directions; the generated codec remains wire authority.",
-          obligationSignature obligation,
-          obligationSymbol obligation <> " =",
-          "  NominalBinding",
-          "    { nominalToRepresentation = \\_domainValue -> error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " nominalToRepresentation"),
-          "    , nominalFromRepresentation = \\_representationValue -> error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " nominalFromRepresentation"),
-          "    }"
-        ]
-      FixtureValue ->
-        [ "-- HOLE: provide deterministic labelled expected-wire fixtures for " <> resolvedNominalName nominal,
-          obligationSignature obligation,
-          obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " fixtures")
-        ]
-      InitialValue ->
-        [ "-- HOLE: provide the initial register value for " <> resolvedNominalName nominal,
-          obligationSignature obligation,
-          obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " initial value")
-        ]
-    intercalateBlank [] = []
-    intercalateBlank (section : rest) = section <> concatMap ("" :) rest
-
-renderBinding :: Context -> StructuralDecl -> ResolvedMappedShape -> BindingObligation -> [Text]
-renderBinding ctx declaration shape obligation =
-  [ "-- HOLE: complete both total directions; wire policy remains in the generated codec.",
-    obligationSymbol obligation <> " :: StructuralBinding " <> domainType <> " " <> shapeType,
-    obligationSymbol obligation <> " =",
-    "  StructuralBinding",
-    "    { bindingToShape = \\case"
-  ]
-    <> indentCases (bindingCases True)
-    <> ["    , bindingFromShape = \\case"]
-    <> indentCases (bindingCases False)
-    <> ["    }"]
-  where
-    domainModule = hsModule (sdHaskell declaration)
-    domainType = domainModule <> "." <> hsType (sdHaskell declaration)
-    shapeModuleName = structuralShapeModule ctx (sdName declaration)
-    shapeType = shapeModuleName <> "." <> sdName declaration <> "Shape"
-    domainCtor constructor = domainModule <> "." <> constructor
-    shapeCtor constructor = shapeModuleName <> "." <> constructor
-    indentCases = map ("      " <>)
-    bindingCases toShapeDirection =
-      foldMappedShape
-        MappedShapeAlgebra
-          { onRecord = \constructor _ fields -> [recordCase toShapeDirection constructor fields],
-            onEnum = \entries -> map (enumCase toShapeDirection . weCtor) entries,
-            onUnion = \_ arms -> map (unionCase toShapeDirection) arms
-          }
-        shape
-    recordCase toShapeDirection constructor fields =
-      sourceCtor
-        <> arguments variables
-        <> " -> "
-        <> targetCtor
-        <> arguments (map (holeFor toShapeDirection . rwfHaskell) fields)
-      where
-        variables = map (("_" <>) . (<> "Value") . rwfHaskell) fields
-        sourceCtor = if toShapeDirection then domainCtor constructor else shapeCtor constructor
-        targetCtor = if toShapeDirection then shapeCtor constructor else domainCtor constructor
-    enumCase toShapeDirection constructor =
-      sourceCtor <> " -> " <> holeFor toShapeDirection constructor
-      where
-        sourceCtor = if toShapeDirection then domainCtor constructor else shapeCtor constructor
-    unionCase toShapeDirection arm =
-      sourceCtor
-        <> maybe "" (const " _payloadValue") (rwaPayload arm)
-        <> " -> "
-        <> case rwaPayload arm of
-          Nothing -> holeFor toShapeDirection (rwaCtor arm)
-          Just _ -> targetCtor <> " " <> holeFor toShapeDirection (rwaCtor arm <> ".payload")
-      where
-        sourceCtor = if toShapeDirection then domainCtor (rwaCtor arm) else shapeCtor (rwaCtor arm)
-        targetCtor = if toShapeDirection then shapeCtor (rwaCtor arm) else domainCtor (rwaCtor arm)
-    arguments [] = ""
-    arguments values = " " <> T.unwords values
-    holeFor toShapeDirection fieldName =
-      "(error "
-        <> tshow
-          ( "HOLE: fill "
-              <> sdName declaration
-              <> (if toShapeDirection then " bindingToShape." else " bindingFromShape.")
-              <> fieldName
-          )
-        <> ")"
-
-shapeModule :: Context -> TypeGraph -> (StructuralDecl, ResolvedMappedShape) -> ScaffoldModule
-shapeModule ctx graph (declaration, shape) =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" (structuralShapeModule ctx (sdName declaration)) <> ".hs"),
-      moduleText = emitShape ctx graph declaration shape,
-      kind = Generated,
-      origin = nodeOrigin "mapped structural" (sdName declaration) (sdLoc declaration)
-    }
-
-structuralPrefix :: Context -> Text
-structuralPrefix ctx = case placement ctx of
-  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Structural"
-  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.Structural"
-
-structuralShapeModule :: Context -> Name -> Text
-structuralShapeModule ctx name = structuralPrefix ctx <> ".Shape." <> name
-
-nominalRepresentationModule :: Context -> Name -> Text
-nominalRepresentationModule ctx name = case placement ctx of
-  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Nominal.Shape." <> name
-  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Nominal.Shape." <> name <> ".Generated"
-
--- | Emit the one generated nominal authority for the complete context. The
--- empty declaration attribution is intentional: in a workspace this module is
--- context-level even when all declarations currently happen to live in one
--- member, so moving that member cannot move Haskell type ownership.
-generatedNominalOwners :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
-generatedNominalOwners ctx service = case planNominalGenerationForService ctx service of
-  Left _ -> []
-  Right [] -> []
-  Right owners ->
-    [ ( ScaffoldModule
-          { modulePath = T.unpack (T.replace "." "/" (generatedNominalModule ctx) <> ".hs"),
-            moduleText = emitGeneratedNominals languageContract ctx owners,
-            kind = Generated,
-            origin = "context " <> specContext spec <> " generated nominal declarations"
-          },
-        []
-      )
-    ]
-      <> [ ( ScaffoldModule
-               { modulePath = T.unpack (T.replace "." "/" (generatedNominalInternalModule ctx) <> ".hs"),
-                 moduleText = emitGeneratedNominalInternals ctx enforcingIds,
-                 kind = Generated,
-                 origin = "context " <> specContext spec <> " generated nominal ID internals"
-               },
-             []
-           )
-         | not (null enforcingIds)
-         ]
-    where
-      enforcingIds =
-        [ (nominal, contract)
-        | owner <- owners,
-          let nominal = nominalDeclaration owner,
-          IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
-          Just contract <- [idDomainContractFor languageContract prefix]
-        ]
-  where
-    spec = checkedSpec service
-    languageContract = checkedLanguageContract service
-
-generatedNominalInternalModule :: Context -> Text
-generatedNominalInternalModule ctx = generatedNominalModule ctx <> ".Internal"
-
-emitGeneratedNominals :: EffectiveLanguageContract -> Context -> [NominalGenerationOwner] -> Text
-emitGeneratedNominals languageContract ctx owners =
-  nl
-    ( equalityPragmas
-        <> [ "{-# LANGUAGE DeriveAnyClass #-}",
-             "{-# LANGUAGE DeriveGeneric #-}",
-             "{-# LANGUAGE LambdaCase #-}",
-             generatedBanner,
-             moduleHeader,
-             "",
-             "import Data.Aeson (FromJSON, ToJSON)",
-             "import Data.Text (Text)",
-             "import GHC.Generics (Generic)",
-             "import Keiki.Shape (CanonicalTypeName)"
-           ]
-        <> internalImports
-        <> equalityImports
-        <> [ "",
-             sectionsOf [map emitOwner owners]
-           ]
-    )
-  where
-    usesEquality = any nominalEqualityUsed owners
-    usesExactEquality = any (\owner -> nominalEqualityUsed owner && exactOwner (nominalDeclaration owner)) owners
-    enforcingIds =
-      [ nominal
-      | owner <- owners,
-        let nominal = nominalDeclaration owner,
-        IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
-        Just _ <- [idDomainContractFor languageContract prefix]
-      ]
-    moduleHeader
-      | null enforcingIds = "module " <> generatedNominalModule ctx <> " where"
-      | otherwise =
-          nl
-            [ "module " <> generatedNominalModule ctx,
-              "  ( " <> T.intercalate "\n  , " (concatMap ownerExports owners),
-              "  ) where"
-            ]
-    ownerExports owner =
-      baseExports <> equalityExports
-      where
-        nominal = nominalDeclaration owner
-        name = resolvedNominalName nominal
-        baseExports = case resolvedNominalRepresentation nominal of
-          IdRepresentation prefix
-            | Just _ <- idDomainContractFor languageContract prefix ->
-                [name, "parse" <> name, "mk" <> name, nominalTextName nominal]
-          _ -> [name <> " (..)", nominalTextName nominal]
-        equalityExports =
-          if nominalEqualityUsed owner
-            then [nominalEqualityTagName nominal, nominalEqualityWitnessName nominal]
-            else []
-    equalityPragmas =
-      if usesEquality
-        then
-          [ "{-# LANGUAGE DataKinds #-}",
-            "{-# LANGUAGE TypeApplications #-}",
-            "{-# LANGUAGE TypeFamilies #-}"
-          ]
-        else []
-    equalityImports =
-      ["import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)" | usesEquality]
-        <> ["import Data.List.NonEmpty (NonEmpty (..))" | usesExactEquality]
-        <> ["import Keiki.ProjectionDomain (finiteProjectionDomain)" | usesExactEquality && null enforcingIds]
-        <> ["import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)" | usesExactEquality && not (null enforcingIds)]
-        <> ["import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)" | not (null enforcingIds)]
-    internalImports =
-      [ "import "
-          <> generatedNominalInternalModule ctx
-          <> " ("
-          <> T.intercalate
-            ", "
-            (concatMap (\nominal -> [resolvedNominalName nominal, "mk" <> resolvedNominalName nominal, "parse" <> resolvedNominalName nominal, nominalTextName nominal]) enforcingIds)
-          <> ")"
-      | not (null enforcingIds)
-      ]
-    emitOwner owner = emitGeneratedNominal languageContract (nominalEqualityUsed owner) (nominalDeclaration owner)
-    exactOwner nominal = case resolvedNominalRepresentation nominal of
-      EnumRepresentation {} -> True
-      IdRepresentation prefix -> isJust (idDomainContractFor languageContract prefix)
-      ScalarRepresentation {} -> False
-
-emitGeneratedNominal :: EffectiveLanguageContract -> Bool -> ResolvedNominalType -> Text
-emitGeneratedNominal languageContract equalityUsed nominal = case resolvedNominalRepresentation nominal of
-  IdRepresentation prefix
-    | Just _ <- idDomainContractFor languageContract prefix ->
-        nl $
-          ["instance CanonicalTypeName " <> name]
-            <> equalitySection
-  IdRepresentation {} ->
-    nl $
-      [ "newtype " <> name <> " = " <> name <> " Text",
-        "  deriving stock (Generic, Eq, Ord, Show)",
-        "  deriving anyclass (ToJSON, FromJSON)",
-        "",
-        "instance CanonicalTypeName " <> name,
-        "",
-        nominalTextName nominal <> " :: " <> name <> " -> Text",
-        nominalTextName nominal <> " (" <> name <> " value) = value"
-      ]
-        <> equalitySection
-  EnumRepresentation constructors ->
-    nl $
-      [ "data " <> name <> " = " <> T.intercalate " | " (map fst (NE.toList constructors)),
-        "  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)",
-        "  deriving anyclass (ToJSON, FromJSON)",
-        "",
-        "instance CanonicalTypeName " <> name,
-        "",
-        nominalTextName nominal <> " :: " <> name <> " -> Text",
-        nominalTextName nominal <> " = \\case",
-        nl ["  " <> constructor <> " -> " <> tshow wire | (constructor, wire) <- NE.toList constructors]
-      ]
-        <> equalitySection
-  ScalarRepresentation {} ->
-    error "generated nominal scalar reached generated declaration emission"
-  where
-    name = resolvedNominalName nominal
-    equalitySection = if equalityUsed then ["", emitGeneratedNominalEquality languageContract nominal] else []
-
-emitGeneratedNominalEquality :: EffectiveLanguageContract -> ResolvedNominalType -> Text
-emitGeneratedNominalEquality languageContract nominal =
-  nl $
-    [ "data " <> tagName,
-      "",
-      "instance FieldProjection " <> tagName <> " where",
-      "  type FieldName " <> tagName <> " = " <> tshow name,
-      "  type FieldOwner " <> tagName <> " = " <> name,
-      "  type FieldResult " <> tagName <> " = Text",
-      "  fieldShapeId _ = " <> tshow equalityIdentity,
-      "  projectFieldValue _ = " <> nominalTextName nominal
-    ]
-      <> exactInstance
-      <> [ "",
-           witnessName <> " :: FieldWitness " <> tagName,
-           witnessName <> " = " <> witnessConstructor <> " @" <> tagName
-         ]
-  where
-    name = resolvedNominalName nominal
-    tagName = nominalEqualityTagName nominal
-    witnessName = nominalEqualityWitnessName nominal
-    equalityIdentity = fromMaybe (error "generated nominal equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
-    (exactInstance, witnessConstructor) = case resolvedNominalRepresentation nominal of
-      IdRepresentation prefix -> case idDomainContractFor languageContract prefix of
-        Nothing -> ([], "fieldWitness")
-        Just _ ->
-          ( [ "",
-              patternName <> " :: TextPattern",
-              patternName <> " = either (error . show) id (idDomainTextPattern (typeIdV7Domain " <> tshow prefix <> "))",
-              "",
-              "instance ExactFieldProjection " <> tagName <> " where",
-              "  fieldProjectionDomain _ = textProjectionDomain " <> patternName,
-              "  reconstructFieldOwner _ = either (const Nothing) Just . parse" <> name
-            ],
-            "exactFieldWitness"
-          )
-      EnumRepresentation constructors ->
-        ( [ "",
-            "instance ExactFieldProjection " <> tagName <> " where",
-            "  fieldProjectionDomain _ = finiteProjectionDomain (" <> renderNonEmpty (map (tshow . snd) (NE.toList constructors)) <> ")",
-            "  reconstructFieldOwner _ = \\case"
-          ]
-            <> ["    " <> tshow wire <> " -> Just " <> constructor | (constructor, wire) <- NE.toList constructors]
-            <> ["    _ -> Nothing"],
-          "exactFieldWitness"
-        )
-      ScalarRepresentation {} -> error "generated nominal scalar equality emission"
-    patternName = lowerFirst name <> "IdDomainPattern"
-
-emitGeneratedNominalInternals :: Context -> [(ResolvedNominalType, IdDomainContract)] -> Text
-emitGeneratedNominalInternals ctx nominals =
-  nl
-    [ "{-# LANGUAGE DeriveGeneric #-}",
-      generatedBanner,
-      "module " <> generatedNominalInternalModule ctx,
-      "  ( " <> T.intercalate "\n  , " (concatMap exportsFor nominals),
-      "  ) where",
-      "",
-      "import Data.Aeson (FromJSON (..), ToJSON (..), withText)",
-      "import Data.Text (Text)",
-      "import Data.Text qualified as T",
-      "import GHC.Generics (Generic)",
-      "import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)",
-      "",
-      sectionsOf [map emitInternal nominals]
-    ]
-  where
-    exportsFor (nominal, _) =
-      [ resolvedNominalName nominal,
-        "parse" <> resolvedNominalName nominal,
-        "mk" <> resolvedNominalName nominal,
-        nominalTextName nominal,
-        legacyNominalConstructorName nominal
-      ]
-    emitInternal (nominal, contract) =
-      nl
-        [ "newtype " <> name <> " = " <> name <> " Text",
-          "  deriving stock (Generic, Eq, Ord, Show)",
-          "",
-          "instance ToJSON " <> name <> " where",
-          "  toJSON = toJSON . " <> textName,
-          "",
-          "instance FromJSON " <> name <> " where",
-          "  parseJSON = withText " <> tshow name <> " (either (fail . T.unpack) pure . parse" <> name <> ")",
-          "",
-          "parse" <> name <> " :: Text -> Either Text " <> name,
-          "parse" <> name <> " input = case validateIdDomainText (typeIdV7Domain " <> tshow (idDomainPrefix contract) <> ") input of",
-          "  Left reason -> Left (T.pack (show reason))",
-          "  Right () -> Right (" <> name <> " input)",
-          "",
-          "mk" <> name <> " :: Text -> Either Text " <> name,
-          "mk" <> name <> " = parse" <> name,
-          "",
-          textName <> " :: " <> name <> " -> Text",
-          textName <> " (" <> name <> " value) = value",
-          "",
-          legacyNominalConstructorName nominal <> " :: Text -> " <> name,
-          legacyNominalConstructorName nominal <> " = " <> name
-        ]
-      where
-        name = resolvedNominalName nominal
-        textName = nominalTextName nominal
-
-nominalEqualityTagName :: ResolvedNominalType -> Text
-nominalEqualityTagName nominal = resolvedNominalName nominal <> "EqualityProjection"
-
-nominalEqualityWitnessName :: ResolvedNominalType -> Text
-nominalEqualityWitnessName nominal = lowerFirst (resolvedNominalName nominal) <> "EqualityWitness"
-
-renderNonEmpty :: [Text] -> Text
-renderNonEmpty values = case values of
-  [] -> error "cannot render an empty exact projection domain"
-  firstValue : rest -> firstValue <> " :| [" <> T.intercalate ", " rest <> "]"
-
-nominalTextName :: ResolvedNominalType -> Text
-nominalTextName = (<> "Text") . lowerFirst . resolvedNominalName
-
--- | Explicit type/constructor imports for exactly the generated declarations a
--- generated aggregate module uses. Keeping an import list avoids making every
--- aggregate depend on every service declaration merely because they share the
--- one owner module.
-generatedNominalTypeImports :: Context -> [ResolvedNominalType] -> [Text]
-generatedNominalTypeImports _ [] = []
-generatedNominalTypeImports ctx nominals =
-  [ "import "
-      <> generatedNominalModule ctx
-      <> " ("
-      <> T.intercalate ", " [resolvedNominalName nominal <> " (..)" | nominal <- stableNominals nominals]
-      <> ")"
-  ]
-
-generatedNominalTypeImportsForService :: CheckedService -> Context -> [ResolvedNominalType] -> [Text]
-generatedNominalTypeImportsForService _ _ [] = []
-generatedNominalTypeImportsForService service ctx nominals =
-  [ "import "
-      <> generatedNominalModule ctx
-      <> " ("
-      <> T.intercalate ", " (concatMap importsFor (stableNominals nominals))
-      <> ")"
-  ]
-  where
-    importsFor nominal = case resolvedNominalRepresentation nominal of
-      IdRepresentation prefix
-        | Just _ <- idDomainContractFor (checkedLanguageContract service) prefix ->
-            [resolvedNominalName nominal, "parse" <> resolvedNominalName nominal]
-      _ -> [resolvedNominalName nominal <> " (..)"]
-
-generatedNominalCodecImports :: CheckedService -> Context -> [ResolvedNominalType] -> [Text]
-generatedNominalCodecImports _ _ [] = []
-generatedNominalCodecImports service ctx nominals =
-  [ "import "
-      <> generatedNominalModule ctx
-      <> " ("
-      <> T.intercalate
-        ", "
-        ( concat
-            [ [typeImport nominal, nominalTextName nominal]
-            | nominal <- stableNominals nominals
-            ]
-        )
-      <> ")"
-  ]
-    <> [ "import "
-           <> generatedNominalInternalModule ctx
-           <> " ("
-           <> T.intercalate ", " [legacyNominalConstructorName nominal | nominal <- enforcingIds]
-           <> ")"
-       | not (null enforcingIds)
-       ]
-  where
-    typeImport nominal = case resolvedNominalRepresentation nominal of
-      IdRepresentation prefix
-        | Just _ <- idDomainContractFor (checkedLanguageContract service) prefix -> resolvedNominalName nominal
-      _ -> resolvedNominalName nominal <> " (..)"
-    enforcingIds =
-      [ nominal
-      | nominal <- stableNominals nominals,
-        IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
-        Just _ <- [idDomainContractFor (checkedLanguageContract service) prefix]
-      ]
-
-legacyNominalConstructorName :: ResolvedNominalType -> Text
-legacyNominalConstructorName nominal = "unsafe" <> resolvedNominalName nominal <> "FromLegacyText"
-
-stableNominals :: [ResolvedNominalType] -> [ResolvedNominalType]
-stableNominals = Map.elems . Map.fromList . map (\nominal -> (resolvedNominalName nominal, nominal))
-
-nominalRepresentationOwners :: Context -> Spec -> [(ScaffoldModule, [Name])]
-nominalRepresentationOwners ctx spec = case resolveNominalTypes spec of
-  Left _ -> []
-  Right registry ->
-    [ (nominalRepresentationModuleValue ctx nominal constructors, [resolvedNominalName nominal])
-    | nominal <- Map.elems (nominalTypes registry),
-      ConsumerNominal {} <- [resolvedNominalOwnership nominal],
-      EnumRepresentation constructors <- [resolvedNominalRepresentation nominal]
-    ]
-
-nominalRepresentationModuleValue :: Context -> ResolvedNominalType -> NonEmpty (Name, Text) -> ScaffoldModule
-nominalRepresentationModuleValue ctx nominal constructors =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
-      moduleText =
-        nl
-          [ "{-# LANGUAGE DeriveGeneric #-}",
-            "{-# LANGUAGE LambdaCase #-}",
-            generatedBanner,
-            "module " <> moduleName <> " (" <> representationType <> " (..), " <> encoderName <> ") where",
-            "",
-            "import Data.Text (Text)",
-            "import GHC.Generics (Generic)",
-            "",
-            "data " <> representationType <> " = " <> T.intercalate " | " (map fst (NE.toList constructors)),
-            "  deriving stock (Eq, Generic, Ord, Show, Enum, Bounded)",
-            "",
-            encoderName <> " :: " <> representationType <> " -> Text",
-            encoderName <> " = \\case",
-            nl ["  " <> constructor <> " -> " <> tshow wire | (constructor, wire) <- NE.toList constructors]
-          ],
-      kind = Generated,
-      origin = nodeOrigin "bound nominal enum representation" (resolvedNominalName nominal) (resolvedNominalLoc nominal)
-    }
-  where
-    moduleName = nominalRepresentationModule ctx (resolvedNominalName nominal)
-    representationType = resolvedNominalName nominal <> "Representation"
-    encoderName = lowerFirst (resolvedNominalName nominal) <> "RepresentationText"
-
-nominalProjectionModule :: Context -> Text
-nominalProjectionModule ctx = case placement ctx of
-  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".NominalProjections"
-  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.NominalProjections"
-
-nominalProjectionOwners :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
-nominalProjectionOwners ctx service = case nominalProjectionTypes spec of
-  [] -> []
-  nominals ->
-    [ ( ScaffoldModule
-          { modulePath = T.unpack (T.replace "." "/" (nominalProjectionModule ctx) <> ".hs"),
-            moduleText = emitNominalProjections (checkedLanguageContract service) ctx nominals,
-            kind = Generated,
-            origin = "context " <> specContext spec <> " nominal scalar projection facade"
-          },
-        []
-      )
-    ]
-  where
-    spec = checkedSpec service
-
-nominalProjectionTypes :: Spec -> [ResolvedNominalType]
-nominalProjectionTypes spec =
-  Map.elems . Map.fromList $
-    [ (resolvedNominalName nominal, nominal)
-    | aggregate <- [value | NAggregate value <- specNodes spec],
-      resolved <- registerTypes aggregate <> commandTypes aggregate,
-      AggregateNominal nominal <- [resolved],
-      ConsumerNominal {} <- [resolvedNominalOwnership nominal]
-    ]
-  where
-    symbols = aggregateSymbols spec
-    registerTypes aggregate =
-      [ resolved
-      | register <- aggRegs aggregate,
-        Right resolved <- [resolveAggregateType symbols (regLoc register) RegisterUse (regType register)]
-      ]
-    commandTypes aggregate =
-      [ resolved
-      | command <- aggCommands aggregate,
-        field <- cmdFields command,
-        Right resolved <- [inferAggregateFieldType symbols aggregate CommandFieldUse field]
-      ]
-
-emitNominalProjections :: EffectiveLanguageContract -> Context -> [ResolvedNominalType] -> Text
-emitNominalProjections languageContract ctx nominals =
-  nl $
-    [ "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE TypeApplications #-}",
-      "{-# LANGUAGE TypeFamilies #-}",
-      generatedBanner,
-      "module " <> moduleName <> " where",
-      ""
-    ]
-      <> map ("import " <>) imports
-      <> [""]
-      <> [T.intercalate "\n\n" (map emitNominalProjection nominals)]
-  where
-    moduleName = nominalProjectionModule ctx
-    imports =
-      sort . nub $
-        [ "Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)",
-          "Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)"
-        ]
-          <> [hsModule (consumerNominalHaskell binding) <> " qualified" | nominal <- nominals, ConsumerNominal binding <- [resolvedNominalOwnership nominal]]
-          <> [qualifiedModule (consumerNominalBinding binding) <> " qualified" | nominal <- nominals, ConsumerNominal binding <- [resolvedNominalOwnership nominal]]
-          <> [nominalRepresentationModule ctx (resolvedNominalName nominal) <> " qualified" | nominal <- nominals, EnumRepresentation {} <- [resolvedNominalRepresentation nominal]]
-          <> ["Data.KindID qualified as KindID" | any hasId nominals]
-          <> ["Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain, validateIdDomainText)" | any hasEnforcedId nominals]
-          <> ["Data.List.NonEmpty (NonEmpty (..))" | any hasExactDomain nominals]
-          <> ["Data.Text (Text)" | any usesText nominals]
-          <> ["Data.Time (UTCTime)" | any (hasScalar NominalTime) nominals]
-          <> ["Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, matchesTextPattern, textCharSet, textConcat, textLiteral, textProjectionDomain, textRepeatBetween)" | any hasExactDomain nominals]
-          <> ["Numeric.Natural (Natural)" | any (hasScalar NominalNatural) nominals]
-    hasScalar wanted nominal = resolvedNominalRepresentation nominal == ScalarRepresentation wanted
-    hasId nominal = case resolvedNominalRepresentation nominal of IdRepresentation {} -> True; _ -> False
-    hasEnforcedId nominal = case resolvedNominalRepresentation nominal of
-      IdRepresentation prefix -> isJust (idDomainContractFor languageContract prefix)
-      _ -> False
-    hasExactDomain nominal = case resolvedNominalRepresentation nominal of ScalarRepresentation {} -> False; _ -> True
-    usesText nominal = case resolvedNominalRepresentation nominal of ScalarRepresentation NominalText -> True; IdRepresentation {} -> True; EnumRepresentation {} -> True; _ -> False
-    emitNominalProjection nominal = case resolvedNominalOwnership nominal of
-      GeneratedNominal -> ""
-      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
-        ScalarRepresentation {} -> emitScalarProjection nominal binding
-        IdRepresentation prefix -> emitConsumerIdProjection nominal binding prefix
-        EnumRepresentation constructors -> emitConsumerEnumProjection nominal binding constructors
-    emitScalarProjection nominal binding =
-      nl
-        [ "data " <> tagName,
-          "",
-          "instance FieldProjection " <> tagName <> " where",
-          "  type FieldName " <> tagName <> " = " <> tshow name,
-          "  type FieldOwner " <> tagName <> " = " <> renderHaskellSource (consumerNominalHaskell binding),
-          "  type FieldResult " <> tagName <> " = " <> scalarHaskellType (resolvedNominalRepresentation nominal),
-          "  fieldShapeId _ = " <> tshow (unCanonicalTypeId (consumerNominalCanonical binding)),
-          "  projectFieldValue _ = nominalToRepresentation " <> unQualifiedValueName (consumerNominalBinding binding),
-          "",
-          witnessName <> " :: FieldWitness " <> tagName,
-          witnessName <> " = fieldWitness @" <> tagName
-        ]
-      where
-        name = resolvedNominalName nominal
-        tagName = name <> "NominalProjection"
-        witnessName = lowerFirst name <> "Witness"
-    emitConsumerIdProjection nominal binding prefix =
-      nl
-        ( patternLines
-            <> [ "",
-                 "data " <> tagName,
-                 "",
-                 "instance FieldProjection " <> tagName <> " where",
-                 "  type FieldName " <> tagName <> " = " <> tshow name,
-                 "  type FieldOwner " <> tagName <> " = " <> ownerType,
-                 "  type FieldResult " <> tagName <> " = Text",
-                 "  fieldShapeId _ = " <> tshow equalityIdentity,
-                 "  projectFieldValue _ = KindID.toText . nominalToRepresentation " <> bindingName,
-                 "",
-                 "instance ExactFieldProjection " <> tagName <> " where",
-                 "  fieldProjectionDomain _ = textProjectionDomain " <> patternName,
-                 "  reconstructFieldOwner _ value"
-               ]
-            <> validationGuard
-            <> [ "    | not (matchesTextPattern " <> patternName <> " value) = Nothing",
-                 "    | otherwise = case KindID.parseText @" <> tshow prefix <> " value of",
-                 "        Left _ -> Nothing",
-                 "        Right representation -> Just (nominalFromRepresentation " <> bindingName <> " representation)",
-                 "",
-                 witnessName <> " :: FieldWitness " <> tagName,
-                 witnessName <> " = exactFieldWitness @" <> tagName
-               ]
-        )
-      where
-        name = resolvedNominalName nominal
-        tagName = nominalEqualityTagName nominal
-        witnessName = nominalEqualityWitnessName nominal
-        patternName = lowerFirst name <> "EqualityPattern"
-        ownerType = renderHaskellSource (consumerNominalHaskell binding)
-        bindingName = unQualifiedValueName (consumerNominalBinding binding)
-        equalityIdentity = fromMaybe (error "consumer ID equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
-        enforced = isJust (idDomainContractFor languageContract prefix)
-        patternLines
-          | enforced =
-              [ patternName <> " :: TextPattern",
-                patternName <> " = either (error . show) id (idDomainTextPattern (typeIdV7Domain " <> tshow prefix <> "))"
-              ]
-          | otherwise =
-              [ patternName <> " :: TextPattern",
-                patternName <> " = either (error . show) id $ do",
-                "  prefix <- textLiteral " <> tshow (prefix <> "_"),
-                "  leading <- textCharSet ('0' :| \"1234567\")",
-                "  crockford <- textCharSet ('0' :| \"123456789abcdefghjkmnpqrstvwxyz\")",
-                "  suffix <- textRepeatBetween 25 25 crockford",
-                "  pure (textConcat (prefix :| [leading, suffix]))"
-              ]
-        validationGuard =
-          [ "    | Left _ <- validateIdDomainText (typeIdV7Domain " <> tshow prefix <> ") value = Nothing"
-          | enforced
-          ]
-    emitConsumerEnumProjection nominal binding constructors =
-      nl $
-        [ "data " <> tagName,
-          "",
-          "instance FieldProjection " <> tagName <> " where",
-          "  type FieldName " <> tagName <> " = " <> tshow name,
-          "  type FieldOwner " <> tagName <> " = " <> ownerType,
-          "  type FieldResult " <> tagName <> " = Text",
-          "  fieldShapeId _ = " <> tshow equalityIdentity,
-          "  projectFieldValue _ = " <> encoderName <> " . nominalToRepresentation " <> bindingName,
-          "",
-          "instance ExactFieldProjection " <> tagName <> " where",
-          "  fieldProjectionDomain _ = finiteProjectionDomain (" <> renderNonEmpty (map (tshow . snd) (NE.toList constructors)) <> ")",
-          "  reconstructFieldOwner _ = \\case"
-        ]
-          <> [ "    " <> tshow wire <> " -> Just (nominalFromRepresentation " <> bindingName <> " " <> representationModule <> "." <> constructor <> ")"
-             | (constructor, wire) <- NE.toList constructors
-             ]
-          <> [ "    _ -> Nothing",
-               "",
-               witnessName <> " :: FieldWitness " <> tagName,
-               witnessName <> " = exactFieldWitness @" <> tagName
-             ]
-      where
-        name = resolvedNominalName nominal
-        tagName = nominalEqualityTagName nominal
-        witnessName = nominalEqualityWitnessName nominal
-        ownerType = renderHaskellSource (consumerNominalHaskell binding)
-        bindingName = unQualifiedValueName (consumerNominalBinding binding)
-        representationModule = nominalRepresentationModule ctx name
-        encoderName = representationModule <> "." <> lowerFirst name <> "RepresentationText"
-        equalityIdentity = fromMaybe (error "consumer enum equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
-    scalarHaskellType representation = case representation of
-      ScalarRepresentation NominalText -> "Text"
-      ScalarRepresentation NominalInt -> "Int"
-      ScalarRepresentation NominalNatural -> "Natural"
-      ScalarRepresentation NominalBool -> "Bool"
-      ScalarRepresentation NominalTime -> "UTCTime"
-      IdRepresentation {} -> "()"
-      EnumRepresentation {} -> "()"
-
-structuralProjectionModule :: Context -> Text
-structuralProjectionModule ctx = structuralPrefix ctx <> "Projections"
-
-emitShape :: Context -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
-emitShape ctx graph declaration shape =
-  nl $
-    languagePragmas
-      <> [ generatedBanner,
-           "module " <> moduleName <> " (" <> shapeType <> " (..)) where",
-           ""
-         ]
-      <> map ("import " <>) imports
-      <> ["" | not (null imports)]
-      <> [shapeDeclaration]
-  where
-    moduleName = structuralShapeModule ctx (sdName declaration)
-    shapeType = sdName declaration <> "Shape"
-    requirements = shapeRequirements ctx graph shape
-    languagePragmas =
-      ["{-# LANGUAGE DeriveGeneric #-}"]
-        <> ["{-# LANGUAGE DuplicateRecordFields #-}" | shapeHasRecord shape]
-    imports =
-      sort . nub $
-        ["Data.Aeson (Value)" | ReqJson `elem` requirements]
-          <> ["Data.Map.Strict (Map)" | ReqMap `elem` requirements]
-          <> ["Data.Text (Text)" | ReqText `elem` requirements]
-          <> ["Data.Time (UTCTime)" | ReqTime `elem` requirements]
-          <> ["GHC.Generics (Generic)"]
-          <> ["Numeric.Natural (Natural)" | ReqNatural `elem` requirements]
-          <> [m <> " qualified" | ReqModule m <- requirements]
-    shapeDeclaration =
-      foldMappedShape
-        MappedShapeAlgebra
-          { onRecord = \constructor _ fields ->
-              nl $
-                ["data " <> shapeType <> " = " <> constructor]
-                  <> recordFields
-                    [ (rwfHaskell field, renderShapeType ctx graph (rwfType field))
-                    | field <- fields
-                    ]
-                  <> ["  deriving stock (Eq, Generic, Show)"],
-            onEnum = \entries ->
-              "data "
-                <> shapeType
-                <> " = "
-                <> T.intercalate " | " (map weCtor entries)
-                <> "\n  deriving stock (Eq, Generic, Show)",
-            onUnion = \_ arms ->
-              nl $
-                case arms of
-                  [] -> ["data " <> shapeType <> " = " <> shapeType <> "Empty", "  deriving stock (Eq, Generic, Show)"]
-                  firstArm : rest ->
-                    ["data " <> shapeType <> " = " <> renderArm firstArm]
-                      <> ["  | " <> renderArm arm | arm <- rest]
-                      <> ["  deriving stock (Eq, Generic, Show)"]
-          }
-        shape
-    renderArm arm = rwaCtor arm <> maybe "" ((" !" <>) . renderShapeType ctx graph) (rwaPayload arm)
-
-data ShapeRequirement
-  = ReqJson
-  | ReqMap
-  | ReqText
-  | ReqTime
-  | ReqNatural
-  | ReqModule !Text
-  deriving stock (Eq, Ord, Show)
-
-shapeHasRecord :: ResolvedMappedShape -> Bool
-shapeHasRecord =
-  foldMappedShape
-    MappedShapeAlgebra
-      { onRecord = \_ _ _ -> True,
-        onEnum = const False,
-        onUnion = \_ _ -> False
-      }
-
-shapeRequirements :: Context -> TypeGraph -> ResolvedMappedShape -> [ShapeRequirement]
-shapeRequirements ctx graph =
-  foldMappedShape
-    MappedShapeAlgebra
-      { onRecord = \_ _ fields -> concatMap (exprRequirements ctx graph . rwfType) fields,
-        onEnum = const [],
-        onUnion = \_ arms -> concatMap (maybe [] (exprRequirements ctx graph) . rwaPayload) arms
-      }
-
-exprRequirements :: Context -> TypeGraph -> ResolvedTypeExpr -> [ShapeRequirement]
-exprRequirements ctx graph =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = [ReqText],
-        onInt = [],
-        onInteger = [],
-        onBool = [],
-        onNatural = [ReqNatural],
-        onTime = [ReqTime],
-        onJson = [ReqJson],
-        onOptional = id,
-        onList = id,
-        onMap = (ReqMap :) . (ReqText :),
-        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
-          Just (ResolvedStructural declaration _) -> [ReqModule (structuralShapeModule ctx (sdName declaration))]
-          Just (ResolvedOpaque declaration) -> [ReqModule (hsModule (odHaskell declaration))]
-          Nothing -> []
-      }
-
-renderShapeType :: Context -> TypeGraph -> ResolvedTypeExpr -> Text
-renderShapeType ctx graph =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = "Text",
-        onInt = "Int",
-        onInteger = "Integer",
-        onBool = "Bool",
-        onNatural = "Natural",
-        onTime = "UTCTime",
-        onJson = "Value",
-        onOptional = \value -> "(Maybe (" <> value <> "))",
-        onList = \value -> "([" <> value <> "])",
-        onMap = \value -> "(Map Text (" <> value <> "))",
-        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
-          Just (ResolvedStructural nested _) ->
-            structuralShapeModule ctx (sdName nested) <> "." <> sdName nested <> "Shape"
-          Just (ResolvedOpaque opaque) ->
-            hsModule (odHaskell opaque) <> "." <> hsType (odHaskell opaque)
-          Nothing -> "()"
-      }
-
-data StructuralProjection = StructuralProjection
-  { spTag :: !Text,
-    spWitness :: !Text,
-    spPointer :: !Text,
-    spOwner :: !HaskellSource,
-    spResult :: !Text,
-    spCanonical :: !CanonicalTypeId,
-    spBinding :: !QualifiedValueName,
-    spSelectors :: ![(Text, Text)]
-  }
-  deriving stock (Eq, Show)
-
-projectionSpecs :: TypeGraph -> [StructuralProjection]
-projectionSpecs graph =
-  sortOn spTag . concat $
-    [ projectionsForRoot graph declaration shape
-    | ResolvedStructural declaration shape <- Map.elems (tgDeclarations graph)
-    ]
-
-projectionsForRoot :: TypeGraph -> StructuralDecl -> ResolvedMappedShape -> [StructuralProjection]
-projectionsForRoot graph root rootShape = case rootShape of
-  RRecord _ _ fields -> concatMap (walkField [] []) fields
-  REnum {} -> []
-  RUnion {} -> []
-  where
-    walkField keys selectors field
-      | rwfPresence field /= PRequired = []
-      | otherwise = case projectionScalar (rwfType field) of
-          Just result -> [mkProjection (keys <> [rwfKey field]) (selectors <> [(shapeModuleForOwner, rwfHaskell field)]) result]
-          Nothing -> case rwfType field of
-            RRef key -> case Map.lookup key (tgDeclarations graph) of
-              Just (ResolvedStructural nested (RRecord _ _ nestedFields)) ->
-                concatMap
-                  (walkNested nested (keys <> [rwfKey field]) (selectors <> [(shapeModuleForOwner, rwfHaskell field)]))
-                  nestedFields
-              _ -> []
-            _ -> []
-      where
-        shapeModuleForOwner = "__SHAPE__." <> sdName root
-
-    walkNested owner keys selectors field
-      | rwfPresence field /= PRequired = []
-      | otherwise = case projectionScalar (rwfType field) of
-          Just result -> [mkProjection (keys <> [rwfKey field]) (selectors <> [(shapeModuleFor owner, rwfHaskell field)]) result]
-          Nothing -> case rwfType field of
-            RRef key -> case Map.lookup key (tgDeclarations graph) of
-              Just (ResolvedStructural nested (RRecord _ _ nestedFields)) ->
-                concatMap
-                  (walkNested nested (keys <> [rwfKey field]) (selectors <> [(shapeModuleFor owner, rwfHaskell field)]))
-                  nestedFields
-              _ -> []
-            _ -> []
-
-    -- Context is supplied when rendering; this marker is replaced there.
-    shapeModuleFor declaration = "__SHAPE__." <> sdName declaration
-    mkProjection keys selectors result =
-      StructuralProjection
-        { spTag = projectionTag (sdName root) pointer,
-          spWitness = lowerFirst (projectionTag (sdName root) pointer) <> "Witness",
-          spPointer = pointer,
-          spOwner = sdHaskell root,
-          spResult = result,
-          spCanonical = sdCanonical root,
-          spBinding = sdBinding root,
-          spSelectors = selectors
-        }
-      where
-        pointer = T.concat ["/" <> escapePointer key | key <- keys]
-
-projectionScalar :: ResolvedTypeExpr -> Maybe Text
-projectionScalar = \case
-  RText -> Just "Text"
-  RInt -> Just "Int"
-  RInteger -> Just "Integer"
-  RBool -> Just "Bool"
-  RTime -> Just "UTCTime"
-  RNatural -> Just "Natural"
-  RJson -> Nothing
-  ROptional {} -> Nothing
-  RList {} -> Nothing
-  RMap {} -> Nothing
-  RRef {} -> Nothing
-
-escapePointer :: Text -> Text
-escapePointer = T.replace "/" "~1" . T.replace "~" "~0"
-
-projectionTag :: Name -> Text -> Text
-projectionTag owner pointer = "StructuralProjection" <> encodeIdentifier (owner <> pointer)
-
-encodeIdentifier :: Text -> Text
-encodeIdentifier = T.concatMap (\character -> "C" <> T.pack (showHex (ord character) "") <> "Z")
-
-emitStructuralProjections :: Context -> TypeGraph -> Text
-emitStructuralProjections ctx graph =
-  nl $
-    [ "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE TypeApplications #-}",
-      "{-# LANGUAGE TypeFamilies #-}",
-      generatedBanner,
-      "-- Equality witnesses are emitted for Text, Int, Bool, Natural, and UTCTime.",
-      "-- Int, Natural, and UTCTime belong to Keiki's ordered subset.",
-      "module " <> moduleName,
-      "  ( " <> T.intercalate "\n  , " (map spWitness specs),
-      "  ) where",
-      "",
-      "import Data.Text (Text)",
-      "import Data.Time (UTCTime)",
-      "import Numeric.Natural (Natural)",
-      "import Keiro.Codec.Structural (bindingToShape)",
-      "import Keiki.Core (FieldProjection (..), FieldWitness, fieldWitness)"
-    ]
-      <> map ("import " <>) imports
-      <> concatMap renderProjection specs
-  where
-    moduleName = structuralProjectionModule ctx
-    specs = map (resolveProjectionModules ctx) (projectionSpecs graph)
-    imports =
-      sort . nub $
-        [hsModule (spOwner spec) <> " qualified" | spec <- specs]
-          <> [qualifiedModule (spBinding spec) <> " qualified" | spec <- specs]
-          <> [shapeModuleName <> " qualified" | spec <- specs, (shapeModuleName, _) <- spSelectors spec]
-    renderProjection spec =
-      [ "",
-        "data " <> spTag spec,
-        "",
-        "instance FieldProjection " <> spTag spec <> " where",
-        "  type FieldName " <> spTag spec <> " = " <> tshow (spPointer spec),
-        "  type FieldOwner " <> spTag spec <> " = " <> renderHaskellSource (spOwner spec),
-        "  type FieldResult " <> spTag spec <> " = " <> spResult spec,
-        "  fieldShapeId _ = " <> tshow (unCanonicalTypeId (spCanonical spec)),
-        "  projectFieldValue _ owner = " <> renderGetter spec,
-        "",
-        spWitness spec <> " :: FieldWitness " <> spTag spec,
-        spWitness spec <> " = fieldWitness @" <> spTag spec
-      ]
-    renderGetter spec =
-      foldl
-        (\value (shapeModuleName, selector) -> shapeModuleName <> "." <> selector <> " (" <> value <> ")")
-        ("bindingToShape " <> unQualifiedValueName (spBinding spec) <> " owner")
-        (spSelectors spec)
-
-resolveProjectionModules :: Context -> StructuralProjection -> StructuralProjection
-resolveProjectionModules ctx spec =
-  spec
-    { spSelectors =
-        [ (replaceModule marker, selector)
-        | (marker, selector) <- spSelectors spec
-        ]
-    }
-  where
-    replaceModule marker
-      | Just name <- T.stripPrefix "__SHAPE__." marker = structuralShapeModule ctx name
-      | otherwise = structuralShapeModule ctx (lastSegment marker)
-
-qualifiedModule :: QualifiedValueName -> Text
-qualifiedModule = fst . splitQualified . unQualifiedValueName
-
-renderHaskellSource :: HaskellSource -> Text
-renderHaskellSource source = hsModule source <> "." <> hsType source
-
-splitQualified :: Text -> (Text, Text)
-splitQualified value =
-  let (prefix, name) = T.breakOnEnd "." value
-   in (T.dropEnd 1 prefix, name)
-
-lastSegment :: Text -> Text
-lastSegment = snd . T.breakOnEnd "."
-
--- | Emit all modules for one aggregate. The 'Spec' is needed for the shared
--- id\/enum declarations.
-scaffoldAggregate :: Context -> Spec -> Aggregate -> [ScaffoldModule]
-scaffoldAggregate ctx spec = scaffoldAggregateForService ctx (legacyCheckedService spec)
-
--- | Emit all modules for one aggregate after selecting the effective semantic
--- contract. This is the normal source/workspace generation entry point.
-scaffoldAggregateForService :: Context -> CheckedService -> Aggregate -> [ScaffoldModule]
-scaffoldAggregateForService ctx service agg =
-  [ genModule a "Domain" (emitDomain a),
-    genModule a "Codec" (emitCodec a)
-  ]
-    ++ ( if hasVersion2Ownership a
-           then
-             [ genModule a "Expressions" (emitExpressions a),
-               genModule a "Transducer" (emitGeneratedTransducer a),
-               genModule a "BehaviorContract" (emitBehaviorContract a),
-               behaviorHoleModule a
-             ]
-           else []
-       )
-    ++ [ genModule a "EventStream" (emitEventStream a),
-         genModule a "Projection" (emitProjection a)
-       ]
-    ++ [holeModule a (emitHoles a) | aggregateNeedsHoleModule a]
-  where
-    a = resolveAggForService ctx service agg
-
-aggregateNeedsHoleModule :: Agg -> Bool
-aggregateNeedsHoleModule aggregate
-  | hasVersion2Ownership aggregate = not (null (version2HoleExports aggregate))
-  | otherwise = True
-
--- | The generated behavioral contract is deliberately separate from both the
--- authoritative transducer and the create-once witness list.  Regeneration can
--- replace this module freely while stale textual keys in @BehaviorHoles@ keep
--- compiling and are reported by reconciliation.
-emitBehaviorContract :: Agg -> Text
-emitBehaviorContract aggregate =
-  nl $
-    [ "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE OverloadedLabels #-}",
-      "{-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}",
-      generatedBanner,
-      "module " <> aGenPrefix aggregate <> ".BehaviorContract where",
-      "",
-      "import " <> aGenPrefix aggregate <> ".Codec (encode" <> name <> "Event, parse" <> name <> "Event, " <> valueStem <> "Codec)",
-      "import " <> aGenPrefix aggregate <> ".Domain",
-      "import " <> aGenPrefix aggregate <> ".Transducer (" <> valueStem <> "Transducer)",
-      "import Data.Aeson (ToJSON (..), object, (.=))",
-      "import Data.List (sortOn)",
-      "import Data.List.NonEmpty (NonEmpty)",
-      "import Data.List.NonEmpty qualified as NonEmpty",
-      "import Data.Map.Strict qualified as Map",
-      "import Data.Text (Text)",
-      "import Data.Text qualified as T",
-      "import Keiki.Core qualified as K (EdgeMode (..), EdgeRef (..), RegFile, ReplayAttribution (..), ReplayEventSpan (..), ReplaySuccess (..), StepFailure (..), StepSuccess (..), applyEventsDetailedEither, stepDetailedEither, (!))",
-      "import Keiro.Codec qualified as Codec (Codec (eventType), EventType (..))",
-      "",
-      "newtype BehaviorKey = BehaviorKey { unBehaviorKey :: Text }",
-      "  deriving stock (Eq, Ord, Show)",
-      "",
-      "data ObligationKind = LiveTransition | RequiredRejection | ReplayTransition",
-      "  deriving stock (Eq, Ord, Show)",
-      "",
-      "data EvidenceLevel = GeneratedAuthoritative | HoleWitnessed | LegacyRuntimeWitness",
-      "  deriving stock (Eq, Ord, Show)",
-      "",
-      "data GuardCoverage = GuardTotal | GuardPartial | GuardUnknown | GuardNotApplicable",
-      "  deriving stock (Eq, Ord, Show)",
-      "",
-      "data BehaviorRequirement = BehaviorRequirement",
-      "  { requirementKey :: !BehaviorKey",
-      "  , requirementKind :: !ObligationKind",
-      "  , requirementEvidence :: !EvidenceLevel",
-      "  , requirementGuardCoverage :: !GuardCoverage",
-      "  , requirementSource :: !" <> aVertexType aggregate,
-      "  , requirementCommandName :: !Text",
-      "  , requirementExpectedEdge :: !(Maybe (K.EdgeRef " <> aVertexType aggregate <> "))",
-      "  , requirementTarget :: !(Maybe " <> aVertexType aggregate <> ")",
-      "  , requirementEventKinds :: ![Text]",
-      "  , requirementLine :: !Int",
-      "  }",
-      "  deriving stock (Eq, Show)",
-      "",
-      "data RejectionClass = RejectNoOutgoingEdges | RejectNoMatchingEdge",
-      "  deriving stock (Eq, Show)",
-      "",
-      "data LiveExpectation",
-      "  = Emits (NonEmpty " <> name <> "Event)",
-      "  | Rejects RejectionClass",
-      "  | NoOp",
-      "  deriving stock (Eq, Show)",
-      "",
-      "data BehaviorWitness",
-      "  = Pending BehaviorKey",
-      "  | LiveWitness",
-      "      { witnessKey :: BehaviorKey",
-      "      , witnessHistory :: [" <> name <> "Event]",
-      "      , witnessCommand :: " <> name <> "Command",
-      "      , witnessExpected :: LiveExpectation",
-      "      }",
-      "  | ReplayWitness",
-      "      { witnessKey :: BehaviorKey",
-      "      , witnessHistoryPrefix :: [" <> name <> "Event]",
-      "      , witnessObservedChunk :: [" <> name <> "Event]",
-      "      }",
-      "  deriving stock (Eq, Show)",
-      "",
-      "data BehaviorFailure = BehaviorFailure",
-      "  { failureKey :: !BehaviorKey",
-      "  , failureCode :: !Text",
-      "  , failureDetail :: !Text",
-      "  }",
-      "  deriving stock (Eq, Show)",
-      "",
-      "instance ToJSON BehaviorFailure where",
-      "  toJSON failure = object",
-      "    [ \"key\" .= unBehaviorKey (failureKey failure)",
-      "    , \"code\" .= failureCode failure",
-      "    , \"detail\" .= failureDetail failure",
-      "    ]",
-      "",
-      "data BehaviorConformanceReport = BehaviorConformanceReport",
-      "  { reportRequired :: ![BehaviorKey]",
-      "  , reportFilled :: ![BehaviorKey]",
-      "  , reportPending :: ![BehaviorKey]",
-      "  , reportMissing :: ![BehaviorKey]",
-      "  , reportDuplicate :: ![BehaviorKey]",
-      "  , reportStale :: ![BehaviorKey]",
-      "  , reportFailed :: ![BehaviorFailure]",
-      "  , reportVerified :: ![BehaviorKey]",
-      "  , reportUnverified :: ![BehaviorKey]",
-      "  }",
-      "  deriving stock (Eq, Show)",
-      "",
-      "instance ToJSON BehaviorConformanceReport where",
-      "  toJSON report = object",
-      "    [ \"schema\" .= (\"keiro/behavior-conformance/1\" :: Text)",
-      "    , \"required\" .= keyTexts (reportRequired report)",
-      "    , \"filled\" .= keyTexts (reportFilled report)",
-      "    , \"pending\" .= keyTexts (reportPending report)",
-      "    , \"missing\" .= keyTexts (reportMissing report)",
-      "    , \"duplicate\" .= keyTexts (reportDuplicate report)",
-      "    , \"stale\" .= keyTexts (reportStale report)",
-      "    , \"failed\" .= reportFailed report",
-      "    , \"verified\" .= keyTexts (reportVerified report)",
-      "    , \"unverified\" .= keyTexts (reportUnverified report)",
-      "    ]",
-      "",
-      "behaviorRequirements :: [BehaviorRequirement]",
-      "behaviorRequirements ="
-    ]
-      <> renderBehaviorRequirementList aggregate
-      <> [ "",
-           "behaviorCoverageReport :: [BehaviorWitness] -> BehaviorConformanceReport",
-           "behaviorCoverageReport witnesses =",
-           "  BehaviorConformanceReport",
-           "    { reportRequired = sortedKeys (Map.keys requiredByKey)",
-           "    , reportFilled = sortedKeys [key | (key, [witness]) <- Map.toList witnessGroups, Map.member key requiredByKey, not (isPending witness)]",
-           "    , reportPending = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, Map.member key requiredByKey, any isPending rows]",
-           "    , reportMissing = sortedKeys [key | key <- Map.keys requiredByKey, Map.notMember key witnessGroups]",
-           "    , reportDuplicate = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, length rows > 1]",
-           "    , reportStale = sortedKeys [key | key <- Map.keys witnessGroups, Map.notMember key requiredByKey]",
-           "    , reportFailed = sortOn (unBehaviorKey . failureKey) failures",
-           "    , reportVerified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, proofStrength requirement]",
-           "    , reportUnverified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, not (proofStrength requirement)]",
-           "    }",
-           " where",
-           "  requiredByKey = Map.fromList [(requirementKey requirement, requirement) | requirement <- behaviorRequirements]",
-           "  witnessGroups = Map.fromListWith (flip (<>)) [(behaviorWitnessKey witness, [witness]) | witness <- witnesses]",
-           "  executions =",
-           "    [ (requirement, runWitness requirement witness)",
-           "    | (key, [witness]) <- Map.toList witnessGroups",
-           "    , not (isPending witness)",
-           "    , Just requirement <- [Map.lookup key requiredByKey]",
-           "    ]",
-           "  failures = [failure | (_, Left failure) <- executions]",
-           "",
-           "behaviorConformancePassed :: BehaviorConformanceReport -> Bool",
-           "behaviorConformancePassed = behaviorConformancePassedWith False",
-           "",
-           "behaviorConformancePassedWith :: Bool -> BehaviorConformanceReport -> Bool",
-           "behaviorConformancePassedWith failOnUnverified report =",
-           "  null (reportPending report)",
-           "    && null (reportMissing report)",
-           "    && null (reportDuplicate report)",
-           "    && null (reportStale report)",
-           "    && null (reportFailed report)",
-           "    && (not failOnUnverified || null (reportUnverified report))",
-           "",
-           "renderBehaviorConformanceText :: BehaviorConformanceReport -> Text",
-           "renderBehaviorConformanceText report = T.unlines",
-           "  [ \"behavior conformance: " <> name <> "\"",
-           "  , \"schema: keiro/behavior-conformance/1\"",
-           "  , countLine \"required\" (reportRequired report)",
-           "  , countLine \"filled\" (reportFilled report)",
-           "  , countLine \"pending\" (reportPending report)",
-           "  , countLine \"missing\" (reportMissing report)",
-           "  , countLine \"duplicate\" (reportDuplicate report)",
-           "  , countLine \"stale\" (reportStale report)",
-           "  , \"failed: \" <> tshow (length (reportFailed report))",
-           "  , countLine \"verified\" (reportVerified report)",
-           "  , countLine \"unverified\" (reportUnverified report)",
-           "  ] <> T.unlines [\"FAIL \" <> unBehaviorKey (failureKey failure) <> \" [\" <> failureCode failure <> \"] \" <> failureDetail failure | failure <- reportFailed report]",
-           "",
-           "runWitness :: BehaviorRequirement -> BehaviorWitness -> Either BehaviorFailure ()",
-           "runWitness requirement witness = case witness of",
-           "  Pending _ -> failure requirement \"pending\" \"witness is still Pending\"",
-           "  LiveWitness _ history command expectation -> runLive requirement history command expectation",
-           "  ReplayWitness _ prefix chunk -> runReplay requirement prefix chunk",
-           "",
-           "runLive :: BehaviorRequirement -> [" <> name <> "Event] -> " <> name <> "Command -> LiveExpectation -> Either BehaviorFailure ()",
-           "runLive requirement history command expectation = do",
-           "  settled <- settleHistory requirement \"history\" history",
-           "  ensure requirement (K.replaySuccessState settled == requirementSource requirement) \"history-wrong-source\" \"history does not settle at the required source vertex\"",
-           "  ensure requirement (commandKind command == requirementCommandName requirement) \"command-mismatch\" \"witness command constructor does not match the required state/command cell\"",
-           "  case requirementKind requirement of",
-           "    ReplayTransition -> failure requirement \"witness-kind\" \"a replay-only requirement needs ReplayWitness\"",
-           "    RequiredRejection -> runRejection requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation",
-           "    LiveTransition -> runAcceptance requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation",
-           "",
-           "runRejection requirement seed command expectation = case expectation of",
-           "  Emits _ -> failure requirement \"expectation-kind\" \"a rejection requirement cannot expect emitted events\"",
-           "  NoOp -> failure requirement \"expectation-kind\" \"a rejection requirement cannot expect an accepted no-op\"",
-           "  Rejects expectedClass -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
-           "    Left K.NoOutgoingEdges {} -> ensure requirement (expectedClass == RejectNoOutgoingEdges) \"rejection-class\" \"expected NoMatchingEdge but runtime returned NoOutgoingEdges\"",
-           "    Left K.NoMatchingEdge {} -> ensure requirement (expectedClass == RejectNoMatchingEdge) \"rejection-class\" \"expected NoOutgoingEdges but runtime returned NoMatchingEdge\"",
-           "    Left K.AmbiguousEdges {} -> failure requirement \"ambiguous-edges\" \"AmbiguousEdges can never satisfy a rejection witness\"",
-           "    Right _ -> failure requirement \"unexpected-acceptance\" \"runtime accepted a command required to reject\"",
-           "",
-           "runAcceptance requirement seed command expectation = case expectation of",
-           "  Rejects _ -> failure requirement \"expectation-kind\" \"a live-transition requirement needs Emits or NoOp\"",
-           "  NoOp -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
-           "    Left stepFailure -> failure requirement \"unexpected-rejection\" (tshow stepFailure)",
-           "    Right success -> do",
-           "      checkAcceptedEnvelope requirement success",
-           "      ensure requirement (null (K.stepSuccessOutputs success)) \"noop-emitted\" \"NoOp emitted one or more events\"",
-           "      ensure requirement (K.stepSuccessState success == fst seed) \"noop-vertex-change\" \"NoOp changed the control vertex\"",
-           "      ensure requirement (regsEqual (K.stepSuccessRegs success) (snd seed)) \"noop-register-change\" \"NoOp changed one or more registers\"",
-           "  Emits expectedEvents -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
-           "    Left stepFailure -> failure requirement \"unexpected-rejection\" (tshow stepFailure)",
-           "    Right success -> do",
-           "      checkAcceptedEnvelope requirement success",
-           "      let expected = NonEmpty.toList expectedEvents",
-           "          actual = K.stepSuccessOutputs success",
-           "      ensure requirement (actual == expected) \"event-value-mismatch\" \"runtime event values differ from the exact witness expectation\"",
-           "      ensure requirement (map eventKind actual == requirementEventKinds requirement) \"event-envelope-mismatch\" \"runtime event kinds differ from the declared ordered envelope\"",
-           "      decoded <- either (failure requirement \"emitted-codec-decode\") Right (decodeEvents actual)",
-           "      replayed <- case K.applyEventsDetailedEither " <> valueStem <> "Transducer seed decoded of",
-           "        Left replayFailure -> failure requirement \"emitted-replay-failed\" (tshow replayFailure)",
-           "        Right replaySuccess -> Right replaySuccess",
-           "      ensure requirement (K.replaySuccessState replayed == K.stepSuccessState success) \"forward-replay-vertex\" \"decoded emissions replay to a different vertex\"",
-           "      ensure requirement (regsEqual (K.replaySuccessRegs replayed) (K.stepSuccessRegs success)) \"forward-replay-registers\" \"decoded emissions replay to different registers\"",
-           "      checkSingleAttribution requirement K.Live (length decoded) (K.replaySuccessTrace replayed)",
-           "",
-           "checkAcceptedEnvelope requirement success = do",
-           "  ensure requirement (K.stepSuccessMode success == K.Live) \"forward-mode\" \"forward execution selected a non-live edge\"",
-           "  ensure requirement (Just (K.stepSuccessEdge success) == requirementExpectedEdge requirement) \"edge-attribution\" \"runtime selected a different guarded sibling\"",
-           "  ensure requirement (Just (K.stepSuccessState success) == requirementTarget requirement) \"target-mismatch\" \"runtime reached a different target vertex\"",
-           "",
-           "runReplay :: BehaviorRequirement -> [" <> name <> "Event] -> [" <> name <> "Event] -> Either BehaviorFailure ()",
-           "runReplay requirement prefix chunk = case requirementKind requirement of",
-           "  ReplayTransition -> do",
-           "    settled <- settleHistory requirement \"history-prefix\" prefix",
-           "    ensure requirement (K.replaySuccessState settled == requirementSource requirement) \"history-wrong-source\" \"history prefix does not settle at the replay edge source\"",
-           "    ensure requirement (not (null chunk)) \"empty-replay-chunk\" \"a replay-only edge has no observable empty chunk\"",
-           "    decoded <- either (failure requirement \"replay-chunk-codec-decode\") Right (decodeEvents chunk)",
-           "    replayed <- case K.applyEventsDetailedEither " <> valueStem <> "Transducer (K.replaySuccessState settled, K.replaySuccessRegs settled) decoded of",
-           "      Left replayFailure -> failure requirement \"replay-chunk-failed\" (tshow replayFailure)",
-           "      Right replaySuccess -> Right replaySuccess",
-           "    ensure requirement (Just (K.replaySuccessState replayed) == requirementTarget requirement) \"target-mismatch\" \"replay chunk reached a different target vertex\"",
-           "    checkSingleAttribution requirement K.ReplayOnly (length decoded) (K.replaySuccessTrace replayed)",
-           "  _ -> failure requirement \"witness-kind\" \"ReplayWitness supplied for a non-replay requirement\"",
-           "",
-           "checkSingleAttribution requirement expectedMode eventCount trace = case trace of",
-           "  [attribution] -> do",
-           "    ensure requirement (Just (K.replayAttributionEdge attribution) == requirementExpectedEdge requirement) \"replay-edge-attribution\" \"replay selected a different edge\"",
-           "    ensure requirement (K.replayAttributionMode attribution == expectedMode) \"replay-mode-attribution\" \"replay selected the wrong live/replay-only phase\"",
-           "    ensure requirement (K.replayAttributionSource attribution == requirementSource requirement) \"replay-source-attribution\" \"replay attribution starts at the wrong source\"",
-           "    ensure requirement (Just (K.replayAttributionTarget attribution) == requirementTarget requirement) \"replay-target-attribution\" \"replay attribution ends at the wrong target\"",
-           "    ensure requirement (K.replayAttributionSpan attribution == K.ReplayEventSpan 0 eventCount) \"replay-span-attribution\" \"replay attribution did not consume the exact chunk\"",
-           "  _ -> failure requirement \"replay-trace-cardinality\" \"expected exactly one completed-edge attribution\"",
-           "",
-           "settleHistory requirement label history = do",
-           "  decoded <- either (failure requirement (label <> \"-codec-decode\")) Right (decodeEvents history)",
-           "  case K.applyEventsDetailedEither " <> valueStem <> "Transducer (" <> initialVertex aggregate <> ", initial" <> name <> "Regs) decoded of",
-           "    Left replayFailure -> failure requirement (label <> \"-replay-failed\") (tshow replayFailure)",
-           "    Right replaySuccess -> Right replaySuccess",
-           "",
-           "decodeEvents :: [" <> name <> "Event] -> Either Text [" <> name <> "Event]",
-           "decodeEvents = traverse (\\event -> parse" <> name <> "Event (Codec.eventType " <> valueStem <> "Codec event) (encode" <> name <> "Event event))"
-         ]
-      <> renderCommandKind aggregate
-      <> [ "",
-           "eventKind event = case Codec.eventType " <> valueStem <> "Codec event of Codec.EventType tag -> tag",
-           "",
-           "regsEqual :: K.RegFile " <> name <> "Regs -> K.RegFile " <> name <> "Regs -> Bool",
-           regsEqualityExpression aggregate,
-           "",
-           "proofStrength requirement =",
-           "  requirementEvidence requirement == GeneratedAuthoritative",
-           "    && requirementGuardCoverage requirement `elem` [GuardTotal, GuardNotApplicable]",
-           "",
-           "behaviorWitnessKey witness = case witness of",
-           "  Pending key -> key",
-           "  LiveWitness { witnessKey = key } -> key",
-           "  ReplayWitness { witnessKey = key } -> key",
-           "",
-           "isPending Pending {} = True",
-           "isPending _ = False",
-           "",
-           "ensure requirement condition code detail = if condition then Right () else failure requirement code detail",
-           "failure requirement code detail = Left (BehaviorFailure (requirementKey requirement) code detail)",
-           "sortedKeys = sortOn unBehaviorKey",
-           "keyTexts = map unBehaviorKey",
-           "countLine label values = label <> \": \" <> tshow (length values)",
-           "tshow :: Show value => value -> Text",
-           "tshow = T.pack . show"
-         ]
-  where
-    name = aName aggregate
-    valueStem = lowerFirst name
-
-renderCommandKind :: Agg -> [Text]
-renderCommandKind aggregate = case aCommands aggregate of
-  [] -> ["", "commandKind _ = \"\""]
-  commands ->
-    [ "",
-      "commandKind command = case command of"
-    ]
-      <> ["  " <> rcName command <> " _ -> " <> tshow (rcName command) | command <- commands]
-
-renderBehaviorRequirementList :: Agg -> [Text]
-renderBehaviorRequirementList aggregate =
-  case behaviorRequirementsFor aggregate of
-    [] -> ["  []"]
-    requirements ->
-      [ (if index == (0 :: Int) then "  [ " else "  , ") <> render requirement
-      | (index, requirement) <- zip [0 ..] requirements
-      ]
-        <> ["  ]"]
-  where
-    render requirement =
-      "BehaviorRequirement "
-        <> keyExpr requirement
-        <> " "
-        <> T.pack (show (Behavior.requirementKind requirement))
-        <> " "
-        <> T.pack (show (Behavior.requirementEvidence requirement))
-        <> " "
-        <> T.pack (show (Behavior.requirementGuardCoverage requirement))
-        <> " "
-        <> vertexCtor aggregate (Behavior.requirementSource requirement)
-        <> " "
-        <> tshow (Behavior.requirementCommand requirement)
-        <> " "
-        <> edgeExpr aggregate requirement
-        <> " "
-        <> maybe "Nothing" (\target -> "(Just " <> vertexCtor aggregate target <> ")") (Behavior.requirementTarget requirement)
-        <> " "
-        <> renderBehaviorTextList (Behavior.requirementEvents requirement)
-        <> " "
-        <> tshow' (unLoc (Behavior.requirementLocation requirement))
-    keyExpr requirement = "(BehaviorKey " <> tshow (Behavior.unBehaviorKey (Behavior.requirementKey requirement)) <> ")"
-
-edgeExpr :: Agg -> Behavior.BehaviorRequirement -> Text
-edgeExpr aggregate requirement = case Behavior.requirementKind requirement of
-  Behavior.RequiredRejection -> "Nothing"
-  _ -> case behaviorEdgeIndex aggregate requirement of
-    Nothing -> error ("required behavior transition missing from resolved aggregate: " <> T.unpack (Behavior.requirementCanonical requirement))
-    Just edgeIndex ->
-      "(Just (K.EdgeRef "
-        <> vertexCtor aggregate (Behavior.requirementSource requirement)
-        <> " "
-        <> tshow' edgeIndex
-        <> "))"
-
-behaviorEdgeIndex :: Agg -> Behavior.BehaviorRequirement -> Maybe Int
-behaviorEdgeIndex aggregate requirement =
-  findIndex
-    matches
-    [ transition
-    | transition <- aTransitions aggregate,
-      tSource transition == Behavior.requirementSource requirement
-    ]
-  where
-    matches transition =
-      unLoc (tLoc transition) == unLoc (Behavior.requirementLocation requirement)
-        && tCommand transition == Behavior.requirementCommand requirement
-
-behaviorRequirementsFor :: Agg -> [Behavior.BehaviorRequirement]
-behaviorRequirementsFor aggregate =
-  case Behavior.deriveAggregateBehaviorRequirements (aSpec aggregate) (aAggregate aggregate) of
-    Left derivationError -> error ("validated aggregate failed behavior derivation: " <> show derivationError)
-    Right requirements -> sortOn Behavior.requirementKey requirements
-
-renderBehaviorTextList :: [Text] -> Text
-renderBehaviorTextList values = "[" <> T.intercalate ", " (map tshow values) <> "]"
-
-regsEqualityExpression :: Agg -> Text
-regsEqualityExpression aggregate = case aRegs aggregate of
-  [] -> "regsEqual _ _ = True"
-  registers ->
-    "regsEqual left right = "
-      <> T.intercalate
-        " && "
-        [ "(left K.! #" <> rrName register <> ") == (right K.! #" <> rrName register <> ")"
-        | register <- registers
-        ]
-
-behaviorHoleModule :: Agg -> ScaffoldModule
-behaviorHoleModule aggregate =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" (aHolePrefix aggregate) <> "/BehaviorHoles.hs"),
-      moduleText = emitBehaviorHoles aggregate,
-      kind = HoleStub,
-      origin = nodeOrigin "aggregate behavior witnesses" (aName aggregate) (aLoc aggregate)
-    }
-
-emitBehaviorHoles :: Agg -> Text
-emitBehaviorHoles aggregate =
-  nl $
-    [ "-- Consumer-owned behavioral witnesses. Created once; never overwritten.",
-      "module " <> aHolePrefix aggregate <> ".BehaviorHoles (behaviorWitnesses) where",
-      "",
-      "import " <> aGenPrefix aggregate <> ".BehaviorContract",
-      "",
-      "behaviorWitnesses :: [BehaviorWitness]",
-      "behaviorWitnesses ="
-    ]
-      <> case behaviorRequirementsFor aggregate of
-        [] -> ["  []"]
-        requirements ->
-          [ (if index == (0 :: Int) then "  [ " else "  , ")
-              <> "Pending (BehaviorKey "
-              <> tshow (Behavior.unBehaviorKey (Behavior.requirementKey requirement))
-              <> ")"
-          | (index, requirement) <- zip [0 ..] requirements
-          ]
-            <> ["  ]"]
-
--- | Emit the context-wide replay-audit target assembly.
---
--- There is one existential target per aggregate declaration. Process saga
--- aggregates are ordinary aggregate nodes referenced by 'SagaRef', so they are
--- included by the same single source of truth rather than being duplicated from
--- the process declaration.
-scaffoldReplayAudit :: Context -> Spec -> [ScaffoldModule]
-scaffoldReplayAudit ctx spec
-  | null aggregates = []
-  | otherwise =
-      [ ScaffoldModule
-          { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
-            moduleText = emitReplayAudit,
-            kind = Generated,
-            origin = "context " <> specContext spec <> " replay-audit assembly"
-          }
-      ]
-  where
-    aggregates = [aggregate | NAggregate aggregate <- specNodes spec]
-    moduleName = contextGeneratedPrefix ctx <> ".ReplayAudit"
-    contextGeneratedPrefix context = case placement context of
-      GeneratedPrefix -> rootPrefix context <> "Generated." <> ctxPascalOf context
-      CollocatedLeaf -> rootPrefix context <> ctxPascalOf context <> ".Generated"
-    emitReplayAudit =
-      nl $
-        [ "{-# LANGUAGE GADTs #-}",
-          generatedBanner,
-          "--",
-          "-- Deployment contract:",
-          "--   * replay-neutral diff: no data audit is required;",
-          "--   * affected diff: run AuditTargeted with the emitted affected set",
-          "--     against a production copy under the candidate binary;",
-          "--   * one-time runtime cutover: run AuditFull;",
-          "--   * any non-zero audit exit blocks deployment.",
-          "module " <> moduleName <> " (auditTargets) where",
-          ""
-        ]
-          ++ [ "import " <> genPrefixFor ctx (aggName aggregate) <> ".EventStream qualified as " <> aggName aggregate
-             | aggregate <- aggregates
-             ]
-          ++ [ "import Keiro.ReplayAudit (AuditTarget (..), SomeAuditTarget (..), streamInCategory)",
-               "import Keiro.Stream qualified as Stream",
-               "",
-               "auditTargets :: [SomeAuditTarget]",
-               "auditTargets ="
-             ]
-          ++ concat
-            [ [ if index == (0 :: Int) then "  [ SomeAuditTarget" else "  , SomeAuditTarget",
-                "      AuditTarget",
-                "        { eventStream = " <> aggregateName <> "." <> lowerFirst aggregateName <> "EventStream",
-                "        , category = Stream.categoryText " <> aggregateName <> "." <> lowerFirst aggregateName <> "Category",
-                "        , mkStream = streamInCategory (Stream.categoryText " <> aggregateName <> "." <> lowerFirst aggregateName <> "Category)",
-                "        }"
-              ]
-            | (index, aggregate) <- zip [0 ..] aggregates,
-              let aggregateName = aggName aggregate
-            ]
-          ++ ["  ]"]
-
-genModule :: Agg -> Text -> Text -> ScaffoldModule
-genModule a name body =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" (aGenPrefix a) <> "/" <> name <> ".hs"),
-      moduleText = body,
-      kind = Generated,
-      origin = nodeOrigin "aggregate" (aName a) (aLoc a)
-    }
-
-holeModule :: Agg -> Text -> ScaffoldModule
-holeModule a body =
-  ScaffoldModule
-    { modulePath = T.unpack (T.replace "." "/" (aHolePrefix a) <> "/" <> "Holes.hs"),
-      moduleText = body,
-      kind = HoleStub,
-      origin = nodeOrigin "aggregate" (aName a) (aLoc a)
-    }
-
---------------------------------------------------------------------------------
--- Integration contract (EP-4): a self-contained payload ADT + codec
---------------------------------------------------------------------------------
-
--- | Emit the deterministic, symbol-free contract layer: a payload ADT
--- (per-event records), the topic constants, the @messageType@ discriminator, and a
--- strict encode\/decode keyed by it. Self-contained (base\/text\/aeson), so it
--- compiles standalone — the cross-service schema both producer and consumer agree
--- on. No keiki symbolic operator (firewall holds).
-scaffoldContract :: Context -> ContractNode -> [ScaffoldModule]
-scaffoldContract ctx c =
-  [ ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Contract.hs"),
-        moduleText = emitContractGen genPrefix c,
-        kind = Generated,
-        origin = nodeOrigin "contract" (ctrName c) (ctrLoc c)
-      }
-  ]
-  where
-    genPrefix = genPrefixFor ctx (pascal (ctrName c))
-
-emitContractGen :: Text -> ContractNode -> Text
-emitContractGen genPrefix c =
-  nl $
-    [ "{-# LANGUAGE DuplicateRecordFields #-}",
-      "{-# LANGUAGE OverloadedRecordDot #-}",
-      "{-# OPTIONS_GHC -Wno-unused-top-binds #-}",
-      generatedBanner,
-      "module " <> genPrefix <> ".Contract",
-      "  ( " <> payloadTy <> " (..)",
-      nl ["  , " <> ceName e <> "Data (..)" | e <- ctrEvents c],
-      "  , messageTypeOf",
-      "  , encode" <> payloadTy,
-      "  , parse" <> payloadTy,
-      "  ) where",
-      "",
-      "import Data.Aeson (Value, object, withObject, (.:), (.=))",
-      "import Data.Aeson.Types (Parser, parseEither)",
-      "import Data.Text (Text)",
-      "import qualified Data.Text as T",
-      "",
-      "-- topic constants"
-    ]
-      ++ [lowerFirst alias <> "Topic :: Text\n" <> lowerFirst alias <> "Topic = " <> tshow t | (alias, t) <- ctrTopics c]
-      ++ [ "",
-           "-- the closed payload set (discriminated by " <> tshow (ctrDiscriminator c) <> ")"
-         ]
-      ++ [emitPayloadAdt payloadTy (ctrEvents c)]
-      ++ [ "",
-           "messageTypeOf :: " <> payloadTy <> " -> Text",
-           "messageTypeOf = \\case"
-         ]
-      ++ ["  " <> ceName e <> " {} -> " <> tshow (ceName e) | e <- ctrEvents c]
-      ++ [ "",
-           "encode" <> payloadTy <> " :: " <> payloadTy <> " -> Value",
-           "encode" <> payloadTy <> " = \\case"
-         ]
-      ++ concatMap encodeArm (ctrEvents c)
-      ++ [ "",
-           "parse" <> payloadTy <> " :: Value -> Either Text " <> payloadTy,
-           "parse" <> payloadTy <> " = mapLeftText . parseEither (withObject " <> tshow payloadTy <> " go)",
-           "  where",
-           "    go o = do",
-           "      kind <- o .: " <> tshow (ctrDiscriminator c) <> " :: Parser Text",
-           "      case kind of"
-         ]
-      ++ concatMap decodeArm (ctrEvents c)
-      ++ [ "        _ -> fail \"unknown message type\"",
-           "",
-           "mapLeftText :: Either String b -> Either Text b",
-           "mapLeftText = either (Left . T.pack) Right"
-         ]
-  where
-    payloadTy = pascal (ctrName c) <> "Payload"
-    encodeArm e =
-      [ "  " <> ceName e <> " payload ->",
-        "    object"
-      ]
-        ++ [lead i kv | (i, kv) <- zip [(0 :: Int) ..] ((tshow (ctrDiscriminator c) <> " .= (" <> tshow (ceName e) <> " :: Text)") : [tshow (cfName f) <> " .= payload." <> cfName f | f <- ceFields e])]
-        ++ ["      ]"]
-    lead 0 kv = "      [ " <> kv
-    lead _ kv = "      , " <> kv
-    decodeArm e =
-      [ "        " <> tshow (ceName e) <> " ->",
-        "          " <> ceName e <> " <$> (" <> ceName e <> "Data" <> fieldApps (ceFields e) <> ")"
-      ]
-    fieldApps [] = ""
-    fieldApps fs = " <$> " <> T.intercalate " <*> " ["o .: " <> tshow (cfName f) | f <- fs]
-
-emitPayloadAdt :: Text -> [ContractEvent] -> Text
-emitPayloadAdt tyName events =
-  sectionsOf [map dataRecord events, [sumDecl]]
-  where
-    hsType CText = "Text"
-    hsType CInt = "Int"
-    hsType (CTypeId _) = "Text"
-    dataRecord e =
-      "data "
-        <> ceName e
-        <> "Data = "
-        <> ceName e
-        <> "Data { "
-        <> T.intercalate ", " [cfName f <> " :: !" <> hsType (cfType f) | f <- ceFields e]
-        <> " }\n  deriving stock (Eq, Show)"
-    arm e = ceName e <> " !" <> ceName e <> "Data"
-    sumDecl = case events of
-      [] -> "data " <> tyName <> " = " <> tyName <> "Empty\n  deriving stock (Eq, Show)"
-      (e : es) ->
-        nl $
-          ["data " <> tyName <> " = " <> arm e]
-            ++ ["  | " <> arm e2 | e2 <- es]
-            ++ ["  deriving stock (Eq, Show)"]
-
---------------------------------------------------------------------------------
--- Integration intake (EP-4): inbox disposition vs the live Keiro.Inbox runtime
---------------------------------------------------------------------------------
-
--- | Emit the inbox node's deterministic disposition wiring compiled against the
--- LIVE @Keiro.Inbox.Types@: the dedupe policy (a real 'InboxDedupePolicy') and a
--- disposition function over the real @InboxResult@ (Processed\/Duplicate\/
--- InProgress\/PreviouslyFailed). This pins the dangerous inversions
--- (duplicate ⇒ ackOk, previouslyFailed ⇒ deadLetter) as compiled code over the
--- runtime types. The handler-level decode\/dedupe\/store failures are noted but not
--- part of @InboxResult@. Firewall holds (no keiki symbolic operator).
-scaffoldIntake :: Context -> IntakeNode -> [ScaffoldModule]
-scaffoldIntake ctx i =
-  [ ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Inbox.hs"),
-        moduleText = emitIntakeGen genPrefix i,
-        kind = Generated,
-        origin = nodeOrigin "intake" (inkName i) (inkLoc i)
-      }
-  ]
-  where
-    genPrefix = genPrefixFor ctx (pascal (inkName i))
-
-emitIntakeGen :: Text -> IntakeNode -> Text
-emitIntakeGen genPrefix i =
-  nl
-    [ "{-# OPTIONS_GHC -Wno-unused-top-binds #-}",
-      generatedBanner,
-      "module " <> genPrefix <> ".Inbox",
-      "  ( InboxAck (..)",
-      "  , inboxDedupePolicy",
-      "  , inboxPersistence",
-      "  , inboxDisposition",
-      "  ) where",
-      "",
-      "import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..))",
-      "",
-      "-- The dedupe policy (hole-kind 4), lowered to the live InboxDedupePolicy.",
-      "inboxDedupePolicy :: InboxDedupePolicy",
-      "inboxDedupePolicy = " <> inkDedupePolicy i,
-      "",
-      "{- | Success-path envelope retention passed to runInboxTransactionWith.",
-      "Failures always retain their full operator-facing dead-letter envelope.",
-      "Dedupe-only success rows decode with an empty payload.",
-      "-}",
-      "inboxPersistence :: InboxPersistence",
-      "inboxPersistence = " <> persistenceCtor (inkPersist i),
-      "",
-      "-- The service's ack decision for each inbox classification.",
-      "data InboxAck = InboxAckOk | InboxRetry | InboxDeadLetter",
-      "  deriving stock (Eq, Show)",
-      "",
-      "-- The disposition table (hole-kind 2) over the LIVE Keiro.Inbox.Types.InboxResult.",
-      "-- duplicate => ackOk and previouslyFailed => deadLetter are the dangerous",
-      "-- inversions the spec states explicitly.",
-      "inboxDisposition :: InboxResult a -> InboxAck",
-      "inboxDisposition r = case r of",
-      "  InboxProcessed _ -> " <> ackFor "processed",
-      "  InboxDuplicate -> " <> ackFor "duplicate",
-      "  InboxInProgress -> " <> ackFor "inProgress",
-      "  InboxPreviouslyFailed _ -> " <> ackFor "previouslyFailed",
-      "",
-      "-- handler-level failures (not InboxResult): decodeFailed => "
-        <> ackText "decodeFailed"
-        <> ", dedupeFailed => "
-        <> ackText "dedupeFailed"
-        <> ", storeFailed => "
-        <> ackText "storeFailed"
-    ]
-  where
-    act o = lookup o [(drOutcome r, drAction r) | r <- inkDisposition i]
-    ackFor o = case act o of
-      Just IAckOk -> "InboxAckOk"
-      Just (IRetry _) -> "InboxRetry"
-      Just (IDeadLetter _) -> "InboxDeadLetter"
-      Nothing -> "InboxRetry"
-    ackText o = case act o of
-      Just IAckOk -> "ackOk"
-      Just (IRetry _) -> "retry"
-      Just (IDeadLetter _) -> "deadLetter"
-      Nothing -> "retry"
-    persistenceCtor InkPersistFull = "PersistFullEnvelope"
-    persistenceCtor InkPersistDedupeOnly = "PersistDedupeOnly"
-
---------------------------------------------------------------------------------
--- Integration publisher (EP-4): config vs the live Keiro.Outbox runtime
---------------------------------------------------------------------------------
-
--- | Emit the publisher's at-least-once policy compiled against the LIVE
--- @Keiro.Outbox.Types@: the ordering policy (a real 'OrderingPolicy'), the backoff
--- curve (a real 'BackoffSchedule'), and the max-attempts ceiling. Firewall holds.
-scaffoldPublisher :: Context -> PublisherNode -> [ScaffoldModule]
-scaffoldPublisher ctx pb =
-  [ ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Publisher.hs"),
-        moduleText = emitPublisherGen genPrefix pb,
-        kind = Generated,
-        origin = nodeOrigin "publisher" (pubName pb) (pubLoc pb)
-      }
-  ]
-  where
-    genPrefix = genPrefixFor ctx (pascal (pubName pb))
-
-emitPublisherGen :: Text -> PublisherNode -> Text
-emitPublisherGen genPrefix pb =
-  nl
-    [ "{-# OPTIONS_GHC -Wno-unused-top-binds #-}",
-      generatedBanner,
-      "module " <> genPrefix <> ".Publisher",
-      "  ( publisherOrdering",
-      "  , publisherBackoff",
-      "  , publisherMaxAttempts",
-      "  ) where",
-      "",
-      "import Keiro.Outbox.Types (BackoffSchedule (..), ExponentialBackoffOptions (..), OrderingPolicy (..))",
-      "",
-      "publisherOrdering :: OrderingPolicy",
-      "publisherOrdering = " <> pubOrdering pb,
-      "",
-      "publisherBackoff :: BackoffSchedule",
-      "publisherBackoff = " <> backoffExpr (pubBackoff pb),
-      "",
-      "publisherMaxAttempts :: Int",
-      "publisherMaxAttempts = " <> tshow' (pubMaxAttempts pb)
-    ]
-  where
-    backoffExpr b = case boKind b of
-      "constant" -> "ConstantBackoff " <> windowText (boWindow b)
-      "exponential" ->
-        "ExponentialBackoff ExponentialBackoffOptions { initial = "
-          <> windowText (boWindow b)
-          <> ", maxDelay = "
-          <> maybe "0" windowText (boMax b)
-          <> ", multiplier = "
-          <> fromMaybe "0" (boMultiplier b)
-          <> " }"
-      _ -> "error \"keiro-dsl: unlowerable backoff kind\""
-
---------------------------------------------------------------------------------
--- pgmq workqueue (EP-5): a self-contained Job payload record + codec
---------------------------------------------------------------------------------
-
--- | Emit the deterministic, symbol-free pgmq layer: the Job payload record, the
--- field→wire-name JSON codec, and the captured physical\/dlq\/table name constants.
--- Self-contained (base\/text\/aeson). The fan-out body and the raw-SQL dedup
--- predicate are holes (not emitted). Firewall holds.
-scaffoldWorkqueue :: Context -> WorkqueueNode -> [ScaffoldModule]
-scaffoldWorkqueue ctx w =
-  [ ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Queue.hs"),
-        moduleText = emitWorkqueueGen genPrefix w,
-        kind = Generated,
-        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
-      },
-    ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/QueuePolicy.hs"),
-        moduleText = emitQueuePolicy genPrefix w,
-        kind = Generated,
-        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
-      },
-    ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/QueueCodec.hs"),
-        moduleText = emitQueueCodec genPrefix w,
-        kind = Generated,
-        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
-      }
-  ]
-  where
-    genPrefix = genPrefixFor ctx (pascal (wqName w))
-
-emitWorkqueueGen :: Text -> WorkqueueNode -> Text
-emitWorkqueueGen genPrefix w =
-  nl $
-    [ "{-# LANGUAGE OverloadedRecordDot #-}",
-      "{-# OPTIONS_GHC -Wno-unused-top-binds #-}",
-      generatedBanner,
-      "module " <> genPrefix <> ".Queue",
-      "  ( " <> payloadTy <> " (..)",
-      "  , encode" <> payloadTy,
-      "  , parse" <> payloadTy,
-      "  , queuePhysical, queueDlq, queueTable",
-      groupKeyExport,
-      "  ) where",
-      "",
-      "import Data.Aeson (Value, object, withObject, (.:), (.=))",
-      "import Data.Aeson.Types (parseEither)",
-      "import Data.Text (Text)",
-      "import qualified Data.Text as T",
-      "",
-      "queuePhysical, queueDlq, queueTable :: Text",
-      "queuePhysical = " <> tshow (wqPhysical w),
-      "queueDlq = " <> tshow (wqDlq w),
-      "queueTable = " <> tshow (wqTable w),
-      ""
-    ]
-      ++ groupKeyLines
-      ++ [ "data " <> payloadTy <> " = " <> payloadTy,
-           "  { " <> T.intercalate "\n  , " [wqfName f <> " :: !" <> hsType (wqfType f) | f <- wqPayload w],
-           "  }",
-           "  deriving stock (Eq, Show)",
-           "",
-           "encode" <> payloadTy <> " :: " <> payloadTy <> " -> Value",
-           "encode" <> payloadTy <> " p =",
-           "  object"
-         ]
-      ++ [lead i (tshow (wqfWire f) <> " .= p." <> wqfName f) | (i, f) <- zip [(0 :: Int) ..] (wqPayload w)]
-      ++ [ "    ]",
-           "",
-           "parse" <> payloadTy <> " :: Value -> Either Text " <> payloadTy,
-           "parse" <> payloadTy <> " = mapLeftText . parseEither (withObject " <> tshow payloadTy <> " go)",
-           "  where",
-           "    go o = " <> payloadTy <> fieldApps (wqPayload w),
-           "",
-           "mapLeftText :: Either String b -> Either Text b",
-           "mapLeftText = either (Left . T.pack) Right"
-         ]
-  where
-    payloadTy = wqPayloadName w
-    groupKeyExport = case wqGroupKey w of
-      Nothing -> ""
-      Just groupKey
-        | gkVia groupKey == "raw" -> "  , groupKeyField, groupKeyFor"
-        | otherwise -> "  , groupKeyField"
-    groupKeyLines = case wqGroupKey w of
-      Nothing -> []
-      Just groupKey -> common <> derivationLines groupKey
-        where
-          common =
-            [ "groupKeyField :: Text",
-              "groupKeyField = " <> tshow (gkField groupKey),
-              ""
-            ]
-          derivationLines key
-            | gkVia key == "raw" =
-                [ "groupKeyFor :: " <> payloadTy <> " -> Text",
-                  "groupKeyFor payload = payload." <> gkField key,
-                  ""
-                ]
-            | otherwise =
-                [ "-- Opaque group-key derivation '" <> gkVia key <> "' remains hand-owned.",
-                  "-- Captured fixture: " <> fromMaybe "<missing>" (gkFixture key),
-                  ""
-                ]
-    hsType "bool" = "Bool"
-    hsType "int" = "Int"
-    hsType _ = "Text"
-    lead 0 kv = "    [ " <> kv
-    lead _ kv = "    , " <> kv
-    fieldApps [] = ""
-    fieldApps fs = " <$> " <> T.intercalate " <*> " ["o .: " <> tshow (wqfWire f) | f <- fs]
-
--- | Emit the versioned PGMQ envelope adapter.  The payload record remains
--- symbol-free and dependency-light in Queue.hs; this runtime-facing module is
--- the opt-in assembly point applications import into their Job values.
-emitQueueCodec :: Text -> WorkqueueNode -> Text
-emitQueueCodec genPrefix w =
-  nl
-    [ generatedBanner,
-      "{- | Versioned job payload envelope: @{\\\"v\\\",\\\"t\\\",\\\"data\\\"}@.",
-      "",
-      "Deploy workers before producers when raising its schema version. Do not",
-      "adopt this codec on a non-empty bare-payload queue without draining it",
-      "(or supplying a transitional codec), or in-flight messages will",
-      "dead-letter. This is telemetry-neutral:",
-      "docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns",
-      "spans and acknowledgement vocabulary.",
-      "-}",
-      "module " <> genPrefix <> ".QueueCodec (" <> stem <> "PayloadCodec, " <> stem <> "JobCodec) where",
-      "",
-      "import Data.List.NonEmpty (NonEmpty (..))",
-      "import Keiro.Codec (Codec (..), EventType (..))",
-      "import Keiro.PGMQ.Codec (JobCodec, keiroJobCodec)",
-      "import " <> genPrefix <> ".Queue (" <> payloadTy <> ", encode" <> payloadTy <> ", parse" <> payloadTy <> ")",
-      "",
-      stem <> "PayloadCodec :: Codec " <> payloadTy,
-      stem <> "PayloadCodec =",
-      "  Codec",
-      "    { eventTypes = EventType " <> tshow payloadTy <> " :| []",
-      "    , eventType = \\_ -> EventType " <> tshow payloadTy,
-      "    , schemaVersion = 1",
-      "    , encode = encode" <> payloadTy,
-      "    , decode = \\_ -> parse" <> payloadTy,
-      "    , upcasters = []",
-      "    }",
-      "",
-      stem <> "JobCodec :: JobCodec " <> payloadTy,
-      stem <> "JobCodec = keiroJobCodec " <> stem <> "PayloadCodec"
-    ]
-  where
-    payloadTy = wqPayloadName w
-    stem = lowerFirst (T.concat (map pascal (T.splitOn "_" (wqName w))))
-
--- | Emit the pgmq retry policy + JobOutcome disposition compiled against the
--- LIVE @Keiro.PGMQ.Job@ runtime (RetryPolicy / JobOutcome / RetryDelay). This pins
--- the dangerous inversions over the runtime types: storeFailure ⇒ Retry (transient)
--- and decodeFailure ⇒ Dead (poison).
-emitQueuePolicy :: Text -> WorkqueueNode -> Text
-emitQueuePolicy genPrefix w =
-  nl $
-    [ generatedBanner,
-      "module " <> genPrefix <> ".QueuePolicy",
-      "  ( retryPolicy, jobOutcomeFor",
-      "  , jobOrdering, jobTuningFor, queueProvision",
-      "  ) where",
-      "",
-      "import Data.Text (Text)",
-      "import Keiro.PGMQ.Job (JobOrdering (..), JobOutcome (..), JobTuning, PartitionSpec (..), QueueProvision, RetryDelay (..), RetryPolicy (..), partitionedProvision, standardProvision, unloggedProvision, withFifoIndexProvision, withOrdering)",
-      "",
-      "jobOrdering :: JobOrdering",
-      "jobOrdering = " <> orderingCtor,
-      "",
-      "-- Deployment owns visibility timeout, batch size, and polling; the spec owns ordering.",
-      "jobTuningFor :: JobTuning -> JobTuning",
-      "jobTuningFor = withOrdering jobOrdering",
-      "",
-      "-- Pass this to ensureJobQueueWith at worker startup. FIFO adds the required GIN index; the DLQ remains standard.",
-      "queueProvision :: QueueProvision",
-      "queueProvision = " <> provisionExpr,
-      "",
-      "retryPolicy :: RetryPolicy",
-      "retryPolicy =",
-      "  RetryPolicy",
-      "    { maxRetries = " <> tshow' (wqMaxRetries w),
-      "    , defaultRetryDelay = RetryDelay " <> windowText (wqDelay w),
-      "    , useDeadLetter = " <> (if wqDlqOn w then "True" else "False"),
-      "    }",
-      "",
-      "-- The consumer JobOutcome disposition over the spec's named domain outcomes,",
-      "-- lowered to the live Keiro.PGMQ.Job.JobOutcome.",
-      "jobOutcomeFor :: Text -> JobOutcome",
-      "jobOutcomeFor o = case o of"
-    ]
-      ++ ["  " <> tshow (wqdOutcome r) <> " -> " <> outcome (wqdAction r) | r <- wqDisposition w]
-      ++ ["  _ -> Retry (RetryDelay " <> windowText (wqDelay w) <> ")"]
-  where
-    orderingCtor = case wqOrdering w of
-      WqUnordered -> "Unordered"
-      WqFifoThroughput -> "FifoThroughput"
-      WqFifoRoundRobin -> "FifoRoundRobin"
-    provisionExpr = fifoWrap baseProvision
-    fifoWrap expression = case wqOrdering w of
-      WqUnordered -> expression
-      _ -> "withFifoIndexProvision (" <> expression <> ")"
-    baseProvision = case wqProvision w of
-      WqStandard -> "standardProvision"
-      WqUnlogged -> "unloggedProvision"
-      WqPartitioned interval retention ->
-        "partitionedProvision (PartitionSpec { partitionInterval = "
-          <> tshow interval
-          <> ", retentionInterval = "
-          <> tshow retention
-          <> " })"
-    outcome IAckOk = "Done"
-    outcome (IRetry win) = "Retry (RetryDelay " <> windowText win <> ")"
-    outcome (IDeadLetter mr) = "Dead " <> tshow (fromMaybe "dead-lettered" mr)
-
---------------------------------------------------------------------------------
--- First-class read models (EP-107)
---------------------------------------------------------------------------------
-
--- | Emit an acyclic three-module read-model vertical. @ReadModelTable@ owns the
--- qualified-table constant shared by the hand-owned query and the generated
--- runtime record; @ReadModel@ re-exports it as part of the public surface.
-scaffoldReadModel :: Context -> ReadModelNode -> [ScaffoldModule]
-scaffoldReadModel ctx readModel =
-  [ generated "ReadModelTable" (emitReadModelTable tableModule stem readModel),
-    generated "ReadModel" (emitReadModelGen ctx readModelModule tableModule readModelHolePrefix stem readModel),
-    ScaffoldModule
-      { modulePath = modulePathFor readModelHolePrefix "ReadModelHoles",
-        moduleText = emitReadModelHoles tableModule readModelHolePrefix stem readModel,
-        kind = HoleStub,
-        origin = readModelOrigin
-      }
-  ]
-  where
-    nodeSegment = pascal (rmName readModel)
-    stem = readModelStem readModel
-    readModelModule = genPrefixFor ctx nodeSegment
-    tableModule = readModelModule <> ".ReadModelTable"
-    readModelHolePrefix = holePrefixFor ctx nodeSegment
-    readModelOrigin = nodeOrigin "readmodel" (rmName readModel) (rmLoc readModel)
-    generated leaf body =
-      ScaffoldModule
-        { modulePath = modulePathFor readModelModule leaf,
-          moduleText = body,
-          kind = Generated,
-          origin = readModelOrigin
-        }
-
-modulePathFor :: Text -> Text -> FilePath
-modulePathFor prefix leaf = T.unpack (T.replace "." "/" prefix <> "/" <> leaf <> ".hs")
-
-readModelStem :: ReadModelNode -> Text
-readModelStem = lowerFirst . T.concat . map pascal . T.splitOn "_" . rmName
-
-emitReadModelTable :: Text -> Text -> ReadModelNode -> Text
-emitReadModelTable tableModule stem readModel =
-  nl
-    [ generatedBanner,
-      "module " <> tableModule <> " (" <> qualifiedName <> ") where",
-      "",
-      "import Data.Text (Text)",
-      "import Keiro.Connection (qualifyTable)",
-      "",
-      "-- The fully-qualified, double-quoted data-table reference.",
-      qualifiedName <> " :: Text",
-      qualifiedName <> " = qualifyTable " <> tshow (rmSchema readModel) <> " " <> tshow (rmTable readModel)
-    ]
-  where
-    qualifiedName = stem <> "QualifiedTable"
-
-emitReadModelGen :: Context -> Text -> Text -> Text -> Text -> ReadModelNode -> Text
-emitReadModelGen ctx readModelModule tableModule readModelHolePrefix stem readModel =
-  nl $
-    [ "{-# LANGUAGE OverloadedRecordDot #-}",
-      generatedBanner,
-      "module " <> readModelModule <> ".ReadModel",
-      "  ( " <> T.intercalate "\n  , " exports,
-      "  ) where",
-      "",
-      "import Data.Functor (void)",
-      "import Effectful (Eff, (:>))",
-      "import " <> tableModule <> " (" <> qualifiedName <> ")",
-      "import " <> readModelHolePrefix <> ".ReadModelHoles (" <> T.intercalate ", " holeImports <> ")"
-    ]
-      ++ asyncImports
-      ++ [ "import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)",
-           "import Keiro.ReadModel.Rebuild qualified as Rebuild",
-           "import Kiroku.Store.Effect (Store)",
-           "import Kiroku.Store.Types (" <> kirokuTypes <> ")",
-           "",
-           readModelName <> " :: ReadModel " <> queryInputType <> " " <> queryResultType,
-           readModelName <> " =",
-           "  ReadModel",
-           "    { name = " <> tshow registryName,
-           "    , tableName = " <> tshow (rmTable readModel),
-           "    , schema = " <> tshow (rmSchema readModel),
-           "    , subscriptionName = " <> tshow subscriptionName,
-           "    , version = " <> tshow' (rmVersion readModel),
-           "    , shapeHash = " <> tshow (rmShape readModel),
-           "    , defaultConsistency = " <> consistencyExpr (rmConsistency readModel),
-           "    , strongScope = " <> scopeExpr (rmScope readModel),
-           "    , query = " <> queryName,
-           "    }",
-           "",
-           "-- Call once at projection startup before serving queries.",
-           registerName <> " :: (Store :> es) => Eff es ()",
-           registerName <> " =",
-           "  void (registerReadModel " <> tshow registryName <> " " <> tshow' (rmVersion readModel) <> " " <> tshow (rmShape readModel) <> ")",
-           "",
-           startName <> " :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata",
-           startName <> " =",
-           "  Rebuild.startRebuild " <> readModelName <> " " <> projectionNames,
-           "",
-           finishName <> " :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)",
-           finishName <> " =",
-           "  Rebuild.finishRebuild " <> readModelName <> " " <> projectionNames,
-           "",
-           abandonName <> " :: (Store :> es) => Eff es ReadModelMetadata",
-           abandonName <> " = Rebuild.abandonRebuild " <> readModelName
-         ]
-      ++ asyncDefinition
-  where
-    registryName = registryNameFor (contextName ctx) readModel
-    subscriptionName = subscriptionNameFor (contextName ctx) readModel
-    asyncName = registryName <> "-async"
-    readModelName = stem <> "ReadModel"
-    qualifiedName = stem <> "QualifiedTable"
-    registerName = "register" <> pascal stem
-    startName = "start" <> pascal stem <> "Rebuild"
-    finishName = "finish" <> pascal stem <> "Rebuild"
-    abandonName = "abandon" <> pascal stem <> "Rebuild"
-    asyncValueName = stem <> "AsyncProjection"
-    queryInputType = pascal stem <> "QueryInput"
-    queryResultType = pascal stem <> "QueryResult"
-    queryName = stem <> "Query"
-    applyName = "apply" <> pascal stem
-    exports =
-      [ readModelName,
-        qualifiedName,
-        registerName,
-        startName,
-        finishName,
-        abandonName
-      ]
-        ++ [asyncValueName | rmFeed readModel == RmSubscription]
-    holeImports = [queryInputType, queryResultType, queryName] ++ [applyName | rmFeed readModel == RmSubscription]
-    asyncImports = case rmFeed readModel of
-      RmInline -> []
-      RmSubscription -> ["import Keiro.Projection (AsyncProjection (..))"]
-    kirokuTypes = case rmFeed readModel of
-      RmInline -> "GlobalPosition"
-      RmSubscription -> "GlobalPosition, RecordedEvent (..)"
-    projectionNames = case rmFeed readModel of
-      RmInline -> "[]"
-      RmSubscription -> "[" <> tshow asyncName <> "]"
-    asyncDefinition = case rmFeed readModel of
-      RmInline -> []
-      RmSubscription ->
-        [ "",
-          asyncValueName <> " :: AsyncProjection",
-          asyncValueName <> " =",
-          "  AsyncProjection",
-          "    { name = " <> tshow asyncName,
-          "    , readModelName = " <> tshow registryName,
-          "    , subscriptionName = " <> tshow subscriptionName,
-          "    , applyRecorded = " <> applyName,
-          "    , idempotencyKey = \\recorded -> recorded.eventId",
-          "    }"
-        ]
-    consistencyExpr Strong = "Strong"
-    consistencyExpr Eventual = "Eventual"
-    scopeExpr Nothing = "EntireLog"
-    scopeExpr (Just RmEntireLog) = "EntireLog"
-    scopeExpr (Just (RmCategory categoryName)) = "CategoryHead " <> tshow categoryName
-
-emitReadModelHoles :: Text -> Text -> Text -> ReadModelNode -> Text
-emitReadModelHoles tableModule readModelHolePrefix stem readModel =
-  nl $
-    [ "-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never overwrites it.",
-      "module " <> readModelHolePrefix <> ".ReadModelHoles",
-      "  ( " <> T.intercalate "\n  , " exports,
-      "  ) where",
-      "",
-      "import " <> tableModule <> " (" <> qualifiedName <> ")",
-      "import Hasql.Transaction qualified as Tx"
-    ]
-      ++ ["import Kiroku.Store.Types (RecordedEvent(..))" | rmFeed readModel == RmSubscription]
-      ++ [ "",
-           "-- HOLE: replace these aliases with the real query input and result types.",
-           "type " <> queryInputType <> " = ()",
-           "type " <> queryResultType <> " = ()",
-           "",
-           "-- HOLE: query " <> qualifiedTableLiteral readModel <> " via " <> qualifiedName <> "; never rely on search_path.",
-           "-- Declared columns:"
-         ]
-      ++ map (("--   " <>) . readModelColumnDoc) (rmColumns readModel)
-      ++ [ queryName <> " :: " <> queryInputType <> " -> Tx.Transaction " <> queryResultType,
-           queryName <> " _input = " <> qualifiedName <> " `seq` error " <> tshow ("HOLE: fill " <> rmName readModel <> " query")
-         ]
-      ++ applyStub
-  where
-    qualifiedName = stem <> "QualifiedTable"
-    queryInputType = pascal stem <> "QueryInput"
-    queryResultType = pascal stem <> "QueryResult"
-    queryName = stem <> "Query"
-    applyName = "apply" <> pascal stem
-    exports = [queryInputType, queryResultType, queryName] ++ [applyName | rmFeed readModel == RmSubscription]
-    applyStub = case rmFeed readModel of
-      RmInline -> []
-      RmSubscription ->
-        [ "",
-          "-- HOLE: apply one recorded event; runtime deduplication makes redelivery safe.",
-          applyName <> " :: RecordedEvent -> Tx.Transaction ()",
-          applyName <> " _recorded = error " <> tshow ("HOLE: fill " <> rmName readModel <> " async apply")
-        ]
-
-qualifiedTableLiteral :: ReadModelNode -> Text
-qualifiedTableLiteral readModel = quoteSqlIdentifier (rmSchema readModel) <> "." <> quoteSqlIdentifier (rmTable readModel)
-
-quoteSqlIdentifier :: Text -> Text
-quoteSqlIdentifier identifier = "\"" <> T.replace "\"" "\"\"" identifier <> "\""
-
-readModelColumnDoc :: RmColumn -> Text
-readModelColumnDoc columnDecl =
-  rmcName columnDecl
-    <> " "
-    <> rmcType columnDecl
-    <> if rmcRequired columnDecl then " NOT NULL" else ""
-
---------------------------------------------------------------------------------
--- Router + shared worker-policy lowering (EP-108)
---------------------------------------------------------------------------------
-
-scaffoldRouter :: Context -> RouterNode -> [ScaffoldModule]
-scaffoldRouter ctx router =
-  [ ScaffoldModule
-      { modulePath = modulePathFor genPrefix "Router",
-        moduleText = emitRouterGen genPrefix router,
-        kind = Generated,
-        origin = routerOrigin
-      },
-    ScaffoldModule
-      { modulePath = modulePathFor holePrefix "RouterHoles",
-        moduleText = emitRouterHoles holePrefix router,
-        kind = HoleStub,
-        origin = routerOrigin
-      }
-  ]
-  where
-    genPrefix = genPrefixFor ctx (rtId router)
-    holePrefix = holePrefixFor ctx (rtId router)
-    routerOrigin = nodeOrigin "router" (rtId router) (rtLoc router)
-
-emitRouterGen :: Text -> RouterNode -> Text
-emitRouterGen genPrefix router =
-  nl $
-    [ generatedBanner,
-      "module " <> genPrefix <> ".Router",
-      "  ( " <> stem <> "Name",
-      "  , " <> stem <> "WorkerOptions",
-      "  ) where",
-      "",
-      "import Data.Text (Text)"
-    ]
-      ++ workerPolicyImports (rtPoison router)
-      ++ [ "",
-           "-- The STABLE router name. It participates in every target-keyed",
-           "-- deterministicRouterCommandId; renaming it re-keys replayed dispatches.",
-           stem <> "Name :: Text",
-           stem <> "Name = " <> tshow (rtName router),
-           "",
-           "-- Runtime-owned dispatch id inputs: (name, key, sourceEventId,",
-           "-- targetStreamName, occurrence). Target-keyed, not positional.",
-           "",
-           "-- Node-level worker policy lowered from the spec. Pass this value to",
-           "-- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions."
-         ]
-      ++ workerOptionsLines (stem <> "WorkerOptions") (rtRejected router) (rtPoison router)
-  where
-    stem = lowerFirst (rtId router)
-
-emitRouterHoles :: Text -> RouterNode -> Text
-emitRouterHoles holePrefix router =
-  nl
-    [ "-- HAND-OWNED hole module for the router's behaviour-bearing bodies.",
-      "-- keiro-dsl creates it once and never overwrites it.",
-      "module " <> holePrefix <> ".RouterHoles () where",
-      "",
-      "-- HOLE resolve :: " <> inName (rtInput router) <> " -> Eff es [PMCommand targetCommand]",
-      "--   Spec source: " <> resolveSourceText (rvSource (rtResolve router)) <> ".",
-      "--   The spec's 'stable' keyword acknowledges that retry attempts accumulate",
-      "--   the UNION of resolved target identities. Keep the recipient set stable",
-      "--   for a source event whenever an exact recipient set matters.",
-      "-- HOLE router value: assemble Keiro.Router.Router with name = " <> lowerFirst (rtId router) <> "Name,",
-      "--   key, resolve, targetEventStream, and targetProjections; run it with",
-      "--   runRouterWorkerWith " <> lowerFirst (rtId router) <> "WorkerOptions.",
-      "-- HOLE targetProjections: spec projections = " <> renderNames (rtProjections router) <> ".",
-      "-- NOTE on-duplicate AckOk is sound because Keiro.Router confirms a duplicate",
-      "--   event id against the TARGET stream via confirmBenignDuplicate before",
-      "--   returning PMCommandDuplicate. Hand-rolled dispatch paths must do likewise."
-    ]
-  where
-    renderNames names = "[" <> T.intercalate ", " names <> "]"
-
-resolveSourceText :: ResolveSource -> Text
-resolveSourceText (ResolveReadModel name) = "read-model " <> name <> " (typically Keiro.ReadModel.runQuery)"
-resolveSourceText ResolveHole = "typed resolver hole"
-
-workerPolicyImports :: PolicyChoice -> [Text]
-workerPolicyImports poison =
-  [ "import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))",
-    "import Shibuya.Core.Ack (RetryDelay (..))"
-  ]
-    ++ if poison == PolHalt
-      then []
-      else ["import Effectful (Eff)", "import Shibuya.Core.Types (Envelope)"]
-
-workerOptionsLines :: Text -> PolicyChoice -> PolicyChoice -> [Text]
-workerOptionsLines valueName rejected poison =
-  [ valueName <> signature,
-    valueName <> argument <> " =",
-    "  WorkerOptions",
-    "    { poisonPolicy = " <> poisonExpr,
-    "    , rejectedCommandPolicy = " <> rejectedExpr rejected,
-    "    , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning",
-    "    , metrics = Nothing                  -- runtime configuration; install at call site",
-    "    }"
-  ]
-  where
-    signature = case poison of
-      PolHalt -> " :: WorkerOptions es msg"
-      _ -> " :: (Envelope msg -> Eff es ()) -> WorkerOptions es msg"
-    argument = case poison of
-      PolHalt -> ""
-      _ -> " poisonCallback"
-    poisonExpr = case poison of
-      PolHalt -> "PoisonHalt"
-      PolDeadLetter -> "PoisonDeadLetter poisonCallback"
-      PolSkip -> "PoisonSkip poisonCallback"
-    rejectedExpr = \case
-      PolHalt -> "RejectedHalt"
-      PolDeadLetter -> "RejectedDeadLetter"
-      PolSkip -> "RejectedSkip"
-
---------------------------------------------------------------------------------
--- Process manager + durable timer (EP-3)
---------------------------------------------------------------------------------
-
--- | Emit the symbol-free deterministic wiring for a process manager + its timer
--- into a @Generated@ module, plus a create-if-absent @ProcessHoles@ module for the
--- behaviour-bearing bodies (the @handle@ reaction, the deadline window, and the
--- fire command). The @Generated@ module contains no keiki symbolic operator (the
--- saga's transducer is the separate aggregate hole), so the firewall invariant
--- holds. The timer worker uses the spec's @max-attempts@ ceiling, never the
--- dangerous @defaultTimerWorkerOptions@ (@Nothing@) default.
-scaffoldProcess :: Context -> ProcessNode -> [ScaffoldModule]
-scaffoldProcess ctx p =
-  [ ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Process.hs"),
-        moduleText = emitProcessGen ctxPascal genPrefix holePrefix p,
-        kind = Generated,
-        origin = nodeOrigin "process" (procId p) (procLoc p)
-      },
-    ScaffoldModule
-      { modulePath = T.unpack (T.replace "." "/" holePrefix <> "/ProcessHoles.hs"),
-        moduleText = emitProcessHoles genPrefix holePrefix p,
-        kind = HoleStub,
-        origin = nodeOrigin "process" (procId p) (procLoc p)
-      }
-  ]
-  where
-    ctxPascal = pascalFromKebab (contextName ctx)
-    genPrefix = genPrefixFor ctx (procId p)
-    holePrefix = holePrefixFor ctx (procId p)
-
-emitProcessGen :: Text -> Text -> Text -> ProcessNode -> Text
-emitProcessGen _ctxPascal genPrefix _holePrefix p =
-  nl $
-    [ generatedBanner,
-      "module " <> genPrefix <> ".Process",
-      "  ( " <> lo <> "ProcessName",
-      "  , " <> lo <> "Category",
-      "  , " <> lo <> "ProcessWorkerOptions",
-      "  , " <> lo <> "TimerRequest",
-      "  , " <> lo <> "FireOutcome",
-      "  ) where",
-      "",
-      "import Data.Aeson (Value, object, (.=))",
-      "import Data.Text (Text)",
-      "import qualified Data.Text as T",
-      "import Data.Time (UTCTime)",
-      "import Data.UUID (UUID)",
-      "import qualified Data.UUID.V5 as UUID.V5",
-      "import Keiro.Command (CommandError (..))",
-      "import Keiro.Stream qualified as Stream",
-      "import Keiro.Timer (TimerId (..), TimerRequest (..))"
-    ]
-      ++ workerPolicyImports (procPoison p)
-      ++ [ "",
-           "-- The define-once ProcessManager name (hole-kind 5: referenced, never retyped).",
-           lo <> "ProcessName :: Text",
-           lo <> "ProcessName = " <> tshow (procName p),
-           "",
-           "-- The validated saga stream category (hole-kind 5: referenced, never retyped).",
-           "-- Saga streams are '<category>-<correlationId>' via Keiro.Stream.entityStream.",
-           "-- categoryUnsafe is safe here because keiro-dsl check proved the literal legal.",
-           lo <> "Category :: Stream.StreamCategory a",
-           lo <> "Category = Stream.categoryUnsafe " <> tshow categoryName,
-           "",
-           "-- Node-level worker policy lowered from the spec. Pass this value to",
-           "-- Keiro.ProcessManager.runProcessManagerWorkerWith."
-         ]
-      ++ workerOptionsLines (lo <> "ProcessWorkerOptions") (procRejected p) (procPoison p)
-      ++ [ "",
-           "-- The deterministic timer-request builder: id derived from the correlation",
-           "-- key (hole-kind 1), processManagerName referenced, payload from the spec.",
-           "-- (timer id derived as uuidv5 of " <> tshow (idePrefix (tmId timer)) <> " <> correlationId)",
-           lo <> "TimerRequest :: Text -> UTCTime -> TimerRequest",
-           lo <> "TimerRequest correlationId fireAtTime =",
-           "  TimerRequest",
-           "    { timerId = TimerId (namedUuid (" <> tshow (idePrefix (tmId timer)) <> " <> correlationId))",
-           "    , processManagerName = " <> lo <> "ProcessName",
-           "    , correlationId = correlationId",
-           "    , fireAt = fireAtTime",
-           "    , payload = " <> payloadExpr (tmPayload timer),
-           "    }",
-           "",
-           "-- The timer-fire disposition table (hole-kind 2), derived from the spec.",
-           "-- on-reject => " <> showOutcome (onReject fd) <> " is the benign inversion.",
-           "-- A duplicate append reaches on-error unless it is confirmed against the",
-           "-- target stream. Use Keiro.ProcessManager.confirmBenignDuplicate:",
-           "--   StreamName -> EventId -> CommandError -> Eff es Bool",
-           "-- Fold True into the duplicate result and surface False as the failure.",
-           lo <> "FireOutcome :: Either CommandError a -> Maybe ()",
-           lo <> "FireOutcome result = case result of",
-           "  Right{} -> " <> outcomeToMaybe (onOk fd),
-           "  Left CommandRejected -> " <> outcomeToMaybe (onReject fd),
-           "  Left (CommandAmbiguous _) -> " <> outcomeToMaybe (onAmbiguous fd) <> "  -- explicit definition-bug arm",
-           "  Left{} -> " <> outcomeToMaybe (onError fd),
-           "",
-           "-- max-attempts = " <> tshow' (tmMaxAttempts timer) <> ", dead-letter = " <> tshow (tmDeadLetter timer),
-           "-- (the timer worker must pass Just " <> tshow' (tmMaxAttempts timer) <> " to runTimerWorkerWith, never the",
-           "--  defaultTimerWorkerOptions Nothing ceiling that retries forever).",
-           "",
-           "-- deterministic v5 UUID of a correlation-keyed string (hole-kind 1).",
-           "namedUuid :: Text -> UUID",
-           "namedUuid v = UUID.V5.generateNamed UUID.V5.namespaceURL (map (fromIntegral . fromEnum) (T.unpack v))"
-         ]
-  where
-    lo = lowerFirst (procId p)
-    categoryName = staticCategory ("process " <> procId p) (sagaCategory (procSaga p))
-    timer = procTimer p
-    fd = fireDisposition (tmFire timer)
-
--- | The timer payload, restricted to the spec's literal (@name=\"value\"@)
--- bindings so it compiles in the deterministic builder. Bare fields and
--- ref-valued bindings are input-driven (the agent-written hole), not emitted.
-payloadExpr :: [FieldBinding] -> Text
-payloadExpr fs = case [b | b <- fs, isLiteral b] of
-  [] -> "object []"
-  lits -> "object [ " <> T.intercalate ", " (map kv lits) <> " ]"
-  where
-    isLiteral b = maybe False (const True) (fbValue b >>= stripWrappingQuotes)
-    kv b = tshow (fbName b) <> " .= (" <> maybe "\"\"" tshow (fbValue b >>= stripWrappingQuotes) <> " :: Value)"
-    stripWrappingQuotes value = T.stripPrefix "\"" value >>= T.stripSuffix "\""
-
-showOutcome :: FireOutcome -> Text
-showOutcome OFired = "Fired"
-showOutcome ORetry = "Retry"
-
-outcomeToMaybe :: FireOutcome -> Text
-outcomeToMaybe OFired = "Just ()  -- Fired"
-outcomeToMaybe ORetry = "Nothing  -- Retry"
-
-emitProcessHoles :: Text -> Text -> ProcessNode -> Text
-emitProcessHoles _genPrefix holePrefix p =
-  nl
-    [ "-- HAND-OWNED hole module for the process manager's behaviour-bearing bodies.",
-      "-- keiro-dsl creates it once and never overwrites it.",
-      "module " <> holePrefix <> ".ProcessHoles () where",
-      "",
-      "-- HOLE handle: build the ProcessManagerAction (the self-advance",
-      "--   '" <> advCommand (hAdvance (procHandle p)) <> "', the dispatch(es), and the timer) from the input.",
-      "-- HOLE streams: build streamFor with entityStream " <> lowerFirst (procId p) <> "Category;",
-      "--   build target streams with entityStream " <> lowerFirst (procTarget p) <> "Category. Never concatenate raw stream names.",
-      "-- HOLE window: the deadline policy, e.g. surgeWindow :: NominalDiffTime;",
-      "--   surgeDeadline observedAt = addUTCTime surgeWindow observedAt  (TIME INJECTED).",
-      "-- HOLE fire command: construct " <> fireCommand (tmFire (procTimer p)) <> " for the timer fire,",
-      "--   keyed by correlationId; the fired-event-id is the deterministic uuidv5 of",
-      "--   " <> tshow (idePrefix (fireFiredEventId (tmFire (procTimer p)))) <> " <> correlationId.",
-      "-- NOTE on-duplicate AckOk is sound because the runtime confirms a duplicate",
-      "--   event id against the TARGET stream via confirmBenignDuplicate before",
-      "--   returning PMCommandDuplicate. Its effective signature is:",
-      "--     StreamName -> EventId -> CommandError -> Eff es Bool",
-      "--   Hand-rolled paths must call it with the target stream and attempted event id,",
-      "--   fold True into the duplicate result, and surface False as the original failure.",
-      "--   Never pattern-match DuplicateEvent as success: event ids are globally unique."
-    ]
-
---------------------------------------------------------------------------------
--- Domain module
---------------------------------------------------------------------------------
-
-emitDomain :: Agg -> Text
-emitDomain a =
-  nl $
-    [ "{-# LANGUAGE DataKinds #-}"
-    ]
-      ++ ["{-# LANGUAGE DeriveAnyClass #-}" | hasSnapshot a]
-      ++ [ "{-# LANGUAGE DuplicateRecordFields #-}",
-           "{-# LANGUAGE TemplateHaskell #-}",
-           "{-# LANGUAGE TypeApplications #-}",
-           "{-# OPTIONS_GHC -Wno-unused-top-binds #-}",
-           generatedBanner,
-           "module " <> aGenPrefix a <> ".Domain where",
-           ""
-         ]
-      ++ ["import Data.Aeson (FromJSON, ToJSON)" | hasSnapshot a]
-      ++ [ "import Data.Proxy (Proxy (..))",
-           "import Data.Text (Text)",
-           "import GHC.Generics (Generic)",
-           "import Keiki.Core (RegFile (..))"
-         ]
-      ++ ["import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)" | hasSnapshot a]
-      ++ generatedNominalTypeImportsForService (aggregateCheckedService a) (aContext a) (aGeneratedNominals a)
-      ++ map ("import " <>) (domainConsumerImports a)
-      ++ [ "import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)",
-           "",
-           sectionsOf
-             [ [emitVertex a],
-               map (emitRecord a) (aCommands a),
-               [emitSum (aName a <> "Command") (aCommands a)],
-               map (emitRecord a) (aEvents a),
-               [emitSum (aName a <> "Event") (aEvents a)],
-               [emitRegsType a, emitInitialRegs a],
-               [ "$(deriveAggregateCtorsAll ''" <> aName a <> "Command ''" <> aName a <> "Regs)",
-                 "",
-                 "$(deriveWireCtorsAll ''" <> aName a <> "Event)"
-               ]
-             ]
-         ]
-
-hasSnapshot :: Agg -> Bool
-hasSnapshot = maybe False (const True) . aSnapshot
-
-emitVertex :: Agg -> Text
-emitVertex a =
-  nl $
-    [ "data " <> aVertexType a <> " = " <> T.intercalate " | " (map (vertexCtor a . stName) (aStates a)),
-      "  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)"
-    ]
-      ++ ["  deriving anyclass (ToJSON, FromJSON)" | hasSnapshot a]
-      ++ [ line
-         | hasSnapshot a,
-           line <-
-             [ "instance CanonicalStateShape " <> aVertexType a,
-               "instance CanonicalTypeName " <> aVertexType a
-             ]
-         ]
-
-emitRecord :: Agg -> ResolvedCtor -> Text
-emitRecord a rc =
-  nl $
-    [ "data " <> rcName rc <> "Data = " <> rcName rc <> "Data"
-    ]
-      ++ recordFields [(name, renderDomainType a fieldType) | (name, fieldType) <- rcFields rc]
-      ++ ["  deriving stock (Generic, Eq, Show)"]
-
-recordFields :: [(Text, Text)] -> [Text]
-recordFields [] =
-  ["  {"]
-    <> ["  }"]
-recordFields fs =
-  [ lead i <> n <> " :: !" <> ty
-  | (i, (n, ty)) <- zip [(0 :: Int) ..] fs
-  ]
-    ++ ["  }"]
-  where
-    lead 0 = "  { "
-    lead _ = "  , "
-
-emitSum :: Text -> [ResolvedCtor] -> Text
-emitSum tyName ctors =
-  nl $
-    [firstLine] ++ restLines ++ ["  deriving stock (Generic, Eq, Show)"]
-  where
-    arm rc = rc' rc
-    rc' rc = rcName rc <> " !" <> rcName rc <> "Data"
-    (firstLine, restLines) = case ctors of
-      [] -> ("data " <> tyName <> " = ()", [])
-      (c : cs) ->
-        ( "data " <> tyName <> " = " <> arm c,
-          ["  | " <> arm c2 | c2 <- cs]
-        )
-
-emitRegsType :: Agg -> Text
-emitRegsType a =
-  nl $
-    ["type " <> aName a <> "Regs ="]
-      ++ regListLines a (aRegs a)
-
-regListLines :: Agg -> [ResolvedRegister] -> [Text]
-regListLines _ [] = ["  '[]"]
-regListLines a rs =
-  [ lead i <> "'(" <> tshow (rrName r) <> ", " <> renderDomainType a (rrType r) <> ")"
-  | (i, r) <- zip [(0 :: Int) ..] rs
-  ]
-    ++ ["   ]"]
-  where
-    lead 0 = "  '[ "
-    lead _ = "   , "
-
-emitInitialRegs :: Agg -> Text
-emitInitialRegs a =
-  nl $
-    [ "initial" <> aName a <> "Regs :: RegFile " <> aName a <> "Regs",
-      "initial" <> aName a <> "Regs ="
-    ]
-      ++ chain (aRegs a)
-  where
-    chain [] = ["  RNil"]
-    chain rs =
-      [ "  RCons (Proxy @" <> tshow (rrName r) <> ") " <> regInitialValue a r <> " $"
-      | r <- init rs
-      ]
-        ++ ["  RCons (Proxy @" <> tshow (rrName lastR) <> ") " <> regInitialValue a lastR <> " RNil"]
-      where
-        lastR = last rs
-
--- | The Haskell initial value for a register, by the category of its type.
-regInitialValue :: Agg -> ResolvedRegister -> Text
-regInitialValue aggregate register = case rrInitial register of
-  InitialId name -> case find ((== name) . resolvedNominalName) (aGeneratedNominals aggregate) >>= generatedIdSampleHaskell aggregate of
-    Just value -> value
-    Nothing -> renderRegisterInitial (rrInitial register)
-  _ -> renderRegisterInitial (rrInitial register)
-
-domainConsumerImports :: Agg -> [Text]
-domainConsumerImports a =
-  sort . nub $
-    Set.toList (Set.unions [aggregateImports (aSymbols a) resolved | resolved <- aggregateTypes])
-      <> [ qualifiedModule initialValue <> " qualified"
-         | declaration <- mappedUses a,
-           initialValue <- maybeToListText (mappedInitial declaration)
-         ]
-      <> [ qualifiedModule initialValue <> " qualified"
-         | resolvedType <- aggregateTypes,
-           AggregateNominal nominal <- [resolvedType],
-           ConsumerNominal binding <- [resolvedNominalOwnership nominal],
-           initialValue <- maybeToListText (consumerNominalInitial binding)
-         ]
-  where
-    aggregateTypes = map snd (concatMap rcFields (aCommands a <> aEvents a)) <> map rrType (aRegs a)
-
-mappedUses :: Agg -> [ResolvedMappedDecl]
-mappedUses a =
-  [ declaration
-  | resolvedType <-
-      map snd (concatMap rcFields (aCommands a <> aEvents a))
-        <> map rrType (aRegs a),
-    declaration <- maybeToListText (mappedDeclFor a resolvedType)
-  ]
-
-mappedDeclFor :: Agg -> ResolvedAggregateType -> Maybe ResolvedMappedDecl
-mappedDeclFor a resolvedType = do
-  key <- case resolvedType of
-    AggregateMapped mappedKey -> Just mappedKey
-    _ -> Nothing
-  graph <- aTypeGraph a
-  Map.lookup key (tgDeclarations graph)
-
-mappedInitial :: ResolvedMappedDecl -> Maybe QualifiedValueName
-mappedInitial (ResolvedStructural declaration _) = sdInitial declaration
-mappedInitial (ResolvedOpaque declaration) = odInitial declaration
-
-renderDomainType :: Agg -> ResolvedAggregateType -> Text
-renderDomainType a = aggregateHaskellType (aSymbols a)
-
-maybeToListText :: Maybe value -> [value]
-maybeToListText = maybe [] pure
-
---------------------------------------------------------------------------------
--- Codec module
---------------------------------------------------------------------------------
-
-emitCodec :: Agg -> Text
-emitCodec a =
-  nl $
-    ["{-# LANGUAGE DataKinds #-}" | hasConsumerNominalIdCodec a]
-      ++ ["{-# LANGUAGE TypeApplications #-}" | hasConsumerNominalIdCodec a]
-      ++ ["{-# LANGUAGE LambdaCase #-}" | hasConsumerNominalCodec a || hasGeneratedNominalEnumCodec a]
-      ++ [ "{-# LANGUAGE OverloadedRecordDot #-}",
-           generatedBanner,
-           "module " <> aGenPrefix a <> ".Codec (",
-           "    " <> lowerFirst (aName a) <> "Codec,",
-           "    parse" <> aName a <> "Event,",
-           "    encode" <> aName a <> "Event,"
-         ]
-      ++ concatMap mappedExports (codecMappedDeclarations a)
-      ++ [ ") where",
-           "",
-           "import " <> aGenPrefix a <> ".Domain"
-         ]
-      ++ generatedNominalCodecImports (aggregateCheckedService a) (aContext a) (codecGeneratedNominals a)
-      ++ ( if hasMappedCodec a
-             then
-               [ "import Control.Monad (unless)",
-                 "import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))",
-                 "import Data.Aeson.Key qualified as Key",
-                 "import Data.Aeson.KeyMap qualified as KeyMap"
-               ]
-             else ["import Data.Aeson (Value, object, withObject, (.:), (.=))"]
-         )
-      ++ [ "import Data.Aeson.Types (Parser, parseEither)",
-           "import Data.List.NonEmpty (NonEmpty (..))"
-         ]
-      ++ ( if hasMappedCodec a
-             then ["import Data.Map.Strict (Map)", "import Data.Map.Strict qualified as Map"]
-             else []
-         )
-      ++ [ "import Data.Text (Text)",
-           "import qualified Data.Text as T"
-         ]
-      ++ ["import Data.KindID qualified as KindID" | hasConsumerNominalIdCodec a]
-      ++ ["import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)" | hasEnforcedConsumerNominalIdCodec a]
-      ++ ["import Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)" | hasConsumerNominalCodec a]
-      ++ ["import Keiro.Codec.Structural (bindingFromShape, bindingToShape)" | hasMappedCodec a]
-      ++ [ "import Keiro.Codec (Codec (..), EventType (..))",
-           upcasterImport a
-         ]
-      ++ [nl (map ("import " <>) (codecMappedImports a)) | hasMappedCodec a]
-      ++ [nl (map ("import " <>) (codecNominalImports a)) | hasConsumerNominalCodec a]
-      ++ [ "",
-           emitEnumParsers a,
-           emitConsumerNominalParsers a
-         ]
-      ++ [emitMappedCodecs a | hasMappedCodec a]
-      ++ [ "",
-           emitCodecValue a,
-           "",
-           emitEncode a,
-           "",
-           emitDecode a,
-           "",
-           "mapLeftText :: Either String b -> Either Text b",
-           "mapLeftText = either (Left . T.pack) Right"
-         ]
-      ++ ( if hasMappedCodec a
-             then
-               [ "",
-                 "rejectUnknownFields :: String -> [Text] -> KeyMap.KeyMap Value -> Parser ()",
-                 "rejectUnknownFields label allowed objectValue =",
-                 "  unless (null extras) (fail (label <> \" contains unknown fields: \" <> show extras))",
-                 "  where",
-                 "    extras = filter (`notElem` allowed) (map Key.toText (KeyMap.keys objectValue))"
-               ]
-             else []
-         )
-  where
-    mappedExports (ResolvedStructural declaration _) =
-      [ "    encode" <> sdName declaration <> "Mapped,",
-        "    decode" <> sdName declaration <> "Mapped,"
-      ]
-    mappedExports ResolvedOpaque {} = []
-
-hasMappedCodec :: Agg -> Bool
-hasMappedCodec = not . null . codecMappedDeclarations
-
-hasConsumerNominalCodec :: Agg -> Bool
-hasConsumerNominalCodec = not . null . codecConsumerNominals
-
-hasConsumerNominalIdCodec :: Agg -> Bool
-hasConsumerNominalIdCodec aggregate =
-  any
-    (\nominal -> case resolvedNominalRepresentation nominal of IdRepresentation {} -> True; _ -> False)
-    (codecConsumerNominals aggregate)
-
-hasEnforcedConsumerNominalIdCodec :: Agg -> Bool
-hasEnforcedConsumerNominalIdCodec aggregate =
-  any
-    ( \nominal -> case resolvedNominalRepresentation nominal of
-        IdRepresentation prefix -> isJust (idDomainContractFor (aLanguageContract aggregate) prefix)
-        _ -> False
-    )
-    (codecConsumerNominals aggregate)
-
-hasGeneratedNominalEnumCodec :: Agg -> Bool
-hasGeneratedNominalEnumCodec aggregate =
-  any
-    (\nominal -> case resolvedNominalRepresentation nominal of EnumRepresentation {} -> True; _ -> False)
-    (codecGeneratedNominals aggregate)
-
-emitEnumParsers :: Agg -> Text
-emitEnumParsers a =
-  sectionsOf
-    [ [emitEnumParser nominal | nominal <- codecGeneratedNominals a, EnumRepresentation {} <- [resolvedNominalRepresentation nominal]]
-    ]
-
-emitEnumParser :: ResolvedNominalType -> Text
-emitEnumParser nominal = case resolvedNominalRepresentation nominal of
-  EnumRepresentation constructors ->
-    nl $
-      [ "parse" <> name <> " :: Text -> Parser " <> name,
-        "parse" <> name <> " = \\case"
-      ]
-        ++ ["  " <> tshow wire <> " -> pure " <> constructor | (constructor, wire) <- NE.toList constructors]
-        ++ ["  _ -> fail " <> tshow ("unknown " <> name)]
-  _ -> error "non-enum reached generated enum parser emission"
-  where
-    name = resolvedNominalName nominal
-
-emitConsumerNominalParsers :: Agg -> Text
-emitConsumerNominalParsers aggregate = sectionsOf [map emitParser (codecConsumerNominals aggregate)]
-  where
-    emitParser nominal = case (resolvedNominalRepresentation nominal, resolvedNominalOwnership nominal) of
-      (IdRepresentation prefix, ConsumerNominal binding) ->
-        nl
-          [ parserName nominal <> " :: Text -> Parser " <> renderHaskellSource (consumerNominalHaskell binding),
-            parserName nominal <> " input = " <> validationPrefix prefix <> "case KindID.parseText @" <> tshow prefix <> " input of",
-            "  Left reason -> fail (show reason)",
-            "  Right representation -> pure (nominalFromRepresentation " <> unQualifiedValueName (consumerNominalBinding binding) <> " representation)"
-          ]
-      (EnumRepresentation constructors, ConsumerNominal binding) ->
-        nl $
-          [ parserName nominal <> " :: Text -> Parser " <> renderHaskellSource (consumerNominalHaskell binding),
-            parserName nominal <> " = \\case"
-          ]
-            <> [ "  "
-                   <> tshow wire
-                   <> " -> pure (nominalFromRepresentation "
-                   <> unQualifiedValueName (consumerNominalBinding binding)
-                   <> " "
-                   <> nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal)
-                   <> "."
-                   <> constructor
-                   <> ")"
-               | (constructor, wire) <- NE.toList constructors
-               ]
-            <> ["  _ -> fail " <> tshow ("unknown " <> resolvedNominalName nominal <> " wire value")]
-      _ -> ""
-    parserName nominal = "parse" <> resolvedNominalName nominal <> "Nominal"
-    validationPrefix prefix = case idDomainContractFor (aLanguageContract aggregate) prefix of
-      Nothing -> ""
-      Just _ ->
-        "case validateIdDomainText (typeIdV7Domain "
-          <> tshow prefix
-          <> ") input of Left reason -> fail (show reason); Right () -> "
-
-emitCodecValue :: Agg -> Text
-emitCodecValue a =
-  nl $
-    [ lowerFirst (aName a) <> "Codec :: Codec " <> aName a <> "Event",
-      lowerFirst (aName a) <> "Codec =",
-      "  Codec",
-      "    { eventTypes = " <> eventTypesExpr,
-      "    , eventType = \\case"
-    ]
-      ++ ["        " <> rcName e <> "{} -> EventType " <> tshow (rcName e) | e <- aEvents a]
-      ++ [ "    , schemaVersion = " <> tshow' (maxEventVersion a),
-           "    , encode = encode" <> aName a <> "Event",
-           "    , decode = parse" <> aName a <> "Event",
-           "    , upcasters = " <> upcastersExpr a,
-           "    }"
-         ]
-      ++ upcasterRungDecls a
-  where
-    eventTypesExpr = case map rcName (aEvents a) of
-      [] -> "error \"no events\""
-      (e : es) -> "EventType " <> tshow e <> " :| [" <> T.intercalate ", " (map (("EventType " <>) . tshow) es) <> "]"
-
--- | The codec's @schemaVersion@: the maximum declared event version (EP-2).
-maxEventVersion :: Agg -> Int
-maxEventVersion a = maximum (1 : map rcVersion (aEvents a))
-
--- | One @(sourceVersion, upcasterName)@ entry per event that declares an
--- @upcast from@. The upcaster name is per-event (e.g. @upcastFooV1@) and its
--- body is a hole in the hand-owned Holes module.
-upcasterEntries :: Agg -> [(Int, Text, Text)]
-upcasterEntries a =
-  [ (m, rcName e, "upcast" <> rcName e <> "V" <> tshow' m)
-  | e <- aEvents a,
-    Just m <- [rcUpcastFrom e]
-  ]
-
-upcastersExpr :: Agg -> Text
-upcastersExpr a =
-  "[" <> T.intercalate ", " ["(" <> tshow' m <> ", upcastRungV" <> tshow' m <> ")" | (m, _) <- upcasterRungs a] <> "]"
-
--- | Group event-specific holes into one migration rung per aggregate-global
--- source version.  Event metadata stamps every kind with the aggregate's
--- schema version, so a rung must explicitly pass foreign event kinds through.
-upcasterRungs :: Agg -> [(Int, [(Text, Text)])]
-upcasterRungs a =
-  [ (source, [(eventName, fn) | (_, eventName, fn) <- entries])
-  | entries@((source, _, _) : _) <- groupBy sameSource (sortOn firstSource (upcasterEntries a))
-  ]
-  where
-    firstSource (source, _, _) = source
-    sameSource (source, _, _) (otherSource, _, _) = source == otherSource
-
-upcasterRungDecls :: Agg -> [Text]
-upcasterRungDecls a = concatMap rung (upcasterRungs a)
-  where
-    rung (source, entries) =
-      [ "",
-        "upcastRungV" <> tshow' source <> " :: EventType -> Value -> Either Text Value"
-      ]
-        ++ [ "upcastRungV" <> tshow' source <> " (EventType " <> tshow eventName <> ") value = " <> fn <> " value"
-           | (eventName, fn) <- entries
-           ]
-        ++ [ "-- Kinds whose shape did not change at this rung pass through unchanged; their",
-             "-- stamped version is aggregate-global, not their own shape history.",
-             "upcastRungV" <> tshow' source <> " _ value = Right value"
-           ]
-
--- | When the codec references upcasters, it imports their (hole) definitions
--- from the hand-owned Holes module.
-upcasterImport :: Agg -> Text
-upcasterImport a = case upcasterEntries a of
-  [] -> ""
-  es -> "import " <> aHolePrefix a <> ".Holes (" <> T.intercalate ", " [fn | (_, _, fn) <- es] <> ")"
-
-emitEncode :: Agg -> Text
-emitEncode a =
-  nl $
-    [ "encode" <> aName a <> "Event :: " <> aName a <> "Event -> Value",
-      "encode" <> aName a <> "Event = \\case"
-    ]
-      ++ concatMap encodeArm (aEvents a)
-  where
-    encodeArm e =
-      [ "  " <> rcName e <> " payload ->",
-        "    object"
-      ]
-        ++ [ lead i <> kv
-           | (i, kv) <- zip [(0 :: Int) ..] (("\"kind\" .= (" <> tshow (rcName e) <> " :: Text)") : map encodeField (rcFields e))
-           ]
-        ++ ["      ]"]
-    lead 0 = "      [ "
-    lead _ = "      , "
-    encodeField (n, ty) =
-      tshow n
-        <> " .= "
-        <> encodeFieldValue n ty
-    encodeFieldValue name ty = case ty of
-      AggregateNominal nominal -> encodeNominalValue nominal ("payload." <> name)
-      _ -> case fieldCat a ty of
-        MappedStructuralCat declaration _ -> "encode" <> sdName declaration <> "Mapped payload." <> name
-        MappedOpaqueCat {} -> "toJSON payload." <> name
-        _ -> "payload." <> name
-    encodeNominalValue nominal value = case resolvedNominalOwnership nominal of
-      GeneratedNominal -> case resolvedNominalRepresentation nominal of
-        IdRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Text " <> value
-        EnumRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Text " <> value
-        ScalarRepresentation {} -> value
-      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
-        IdRepresentation {} -> "KindID.toText (nominalToRepresentation " <> bindingName binding <> " " <> value <> ")"
-        EnumRepresentation {} ->
-          nominalRepresentationModule (aContext a) (resolvedNominalName nominal)
-            <> "."
-            <> lowerFirst (resolvedNominalName nominal)
-            <> "RepresentationText (nominalToRepresentation "
-            <> bindingName binding
-            <> " "
-            <> value
-            <> ")"
-        ScalarRepresentation {} -> "nominalToRepresentation " <> bindingName binding <> " " <> value
-    bindingName = unQualifiedValueName . consumerNominalBinding
-
-emitDecode :: Agg -> Text
-emitDecode a =
-  nl $
-    [ "parse" <> aName a <> "Event :: EventType -> Value -> Either Text " <> aName a <> "Event",
-      "parse" <> aName a <> "Event (EventType tag) = mapLeftText . parseEither (withObject " <> tshow (aName a <> "Event") <> " go)",
-      "  where",
-      "    go o = do",
-      "      case tag of"
-    ]
-      ++ concatMap decodeArm (aEvents a)
-      ++ ["        _ -> fail \"unknown event type\""]
-  where
-    decodeArm e =
-      [ "        " <> tshow (rcName e) <> " ->",
-        "          " <> rcName e <> " <$> (" <> rcName e <> "Data" <> fieldApps (rcFields e) <> ")"
-      ]
-    fieldApps [] = ""
-    fieldApps fs = " <$> " <> T.intercalate " <*> " (map decodeField fs)
-    -- The first field uses <$> (handled above), the rest <*>. We instead build
-    -- a uniform list and join; for an empty record there are no fields.
-    decodeField (n, ty) = case ty of
-      AggregateNominal nominal -> decodeNominalField n nominal
-      _ -> case fieldCat a ty of
-        MappedStructuralCat declaration _ -> "(o .: " <> tshow n <> " >>= parse" <> sdName declaration <> "Mapped)"
-        MappedOpaqueCat {} -> "o .: " <> tshow n
-        _ -> "o .: " <> tshow n
-    decodeNominalField name nominal = case resolvedNominalOwnership nominal of
-      GeneratedNominal -> case resolvedNominalRepresentation nominal of
-        IdRepresentation prefix -> case idDomainContractFor (aLanguageContract a) prefix of
-          Nothing -> "(" <> resolvedNominalName nominal <> " <$> o .: " <> tshow name <> ")"
-          Just _ -> "(" <> legacyNominalConstructorName nominal <> " <$> o .: " <> tshow name <> ")"
-        EnumRepresentation {} -> "(o .: " <> tshow name <> " >>= parse" <> resolvedNominalName nominal <> ")"
-        ScalarRepresentation {} -> "o .: " <> tshow name
-      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
-        IdRepresentation {} -> "(o .: " <> tshow name <> " >>= parse" <> resolvedNominalName nominal <> "Nominal)"
-        EnumRepresentation {} -> "(o .: " <> tshow name <> " >>= parse" <> resolvedNominalName nominal <> "Nominal)"
-        ScalarRepresentation {} -> "(nominalFromRepresentation " <> unQualifiedValueName (consumerNominalBinding binding) <> " <$> o .: " <> tshow name <> ")"
-
-codecConsumerNominals :: Agg -> [ResolvedNominalType]
-codecConsumerNominals aggregate =
-  Map.elems . Map.fromList $
-    [ (resolvedNominalName nominal, nominal)
-    | event <- aEvents aggregate,
-      (_, AggregateNominal nominal) <- rcFields event,
-      ConsumerNominal {} <- [resolvedNominalOwnership nominal]
-    ]
-
-codecGeneratedNominals :: Agg -> [ResolvedNominalType]
-codecGeneratedNominals aggregate =
-  generatedNominalsInTypes
-    [ resolvedType
-    | event <- aEvents aggregate,
-      (_, resolvedType) <- rcFields event
-    ]
-
-codecNominalImports :: Agg -> [Text]
-codecNominalImports aggregate =
-  sort . nub $
-    concat
-      [ [ hsModule (consumerNominalHaskell binding) <> " qualified",
-          qualifiedModule (consumerNominalBinding binding) <> " qualified"
-        ]
-          <> [ nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal) <> " qualified"
-             | EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
-             ]
-      | nominal <- codecConsumerNominals aggregate,
-        ConsumerNominal binding <- [resolvedNominalOwnership nominal]
-      ]
-
-codecMappedImports :: Agg -> [Text]
-codecMappedImports a = case aTypeGraph a of
-  Nothing -> []
-  Just graph ->
-    sort . nub $
-      [ structuralShapeModule (aContext a) (sdName declaration) <> " qualified"
-      | ResolvedStructural declaration _ <- codecMappedDeclarations a
-      ]
-        <> [ hsModule (sdHaskell declaration) <> " qualified"
-           | ResolvedStructural declaration _ <- codecMappedDeclarations a
-           ]
-        <> [ qualifiedModule (sdBinding declaration) <> " qualified"
-           | ResolvedStructural declaration _ <- codecMappedDeclarations a
-           ]
-        <> [ hsModule (odHaskell declaration) <> " qualified"
-           | ResolvedOpaque declaration <- codecMappedDeclarations a
-           ]
-        <> [ hsModule (odHaskell declaration) <> " qualified"
-           | ResolvedStructural _ shape <- codecMappedDeclarations a,
-             key <- directShapeRefs shape,
-             Just (ResolvedOpaque declaration) <- [Map.lookup key (tgDeclarations graph)]
-           ]
-
-codecMappedDeclarations :: Agg -> [ResolvedMappedDecl]
-codecMappedDeclarations a = case aTypeGraph a of
-  Nothing -> []
-  Just graph ->
-    mapMaybe (\key -> Map.lookup key (tgDeclarations graph)) (sort (Map.keys selected))
-    where
-      roots =
-        [ key
-        | event <- aEvents a,
-          (_, AggregateMapped key) <- rcFields event,
-          Map.member key (tgDeclarations graph)
-        ]
-      selected =
-        Map.fromList
-          [ (key, ())
-          | root <- roots,
-            key <- root : maybe [] (Map.keys . Map.fromSet (const ())) (Map.lookup root (tgReachability graph))
-          ]
-
-directShapeRefs :: ResolvedMappedShape -> [MappedKey]
-directShapeRefs =
-  foldMappedShape
-    MappedShapeAlgebra
-      { onRecord = \_ _ fields -> concatMap (exprRefs . rwfType) fields,
-        onEnum = const [],
-        onUnion = \_ arms -> concatMap (maybe [] exprRefs . rwaPayload) arms
-      }
-
-exprRefs :: ResolvedTypeExpr -> [MappedKey]
-exprRefs =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = [],
-        onInt = [],
-        onInteger = [],
-        onBool = [],
-        onNatural = [],
-        onTime = [],
-        onJson = [],
-        onOptional = id,
-        onList = id,
-        onMap = id,
-        onRef = pure
-      }
-
-emitMappedCodecs :: Agg -> Text
-emitMappedCodecs a = case aTypeGraph a of
-  Nothing -> ""
-  Just graph ->
-    T.intercalate
-      "\n\n"
-      [ emitStructuralCodec a graph declaration shape
-      | ResolvedStructural declaration shape <- codecMappedDeclarations a
-      ]
-
-emitStructuralCodec :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
-emitStructuralCodec a graph declaration shape =
-  nl
-    [ "encode" <> name <> "Mapped :: " <> consumerType <> " -> Value",
-      "encode" <> name <> "Mapped = encode" <> name <> "Shape . bindingToShape " <> binding,
-      "",
-      "parse" <> name <> "Mapped :: Value -> Parser " <> consumerType,
-      "parse" <> name <> "Mapped value = bindingFromShape " <> binding <> " <$> parse" <> name <> "Shape value",
-      "",
-      "decode" <> name <> "Mapped :: Value -> Either Text " <> consumerType,
-      "decode" <> name <> "Mapped = mapLeftText . parseEither parse" <> name <> "Mapped",
-      "",
-      "encode" <> name <> "Shape :: " <> shapeType <> " -> Value",
-      emitShapeEncoder a graph declaration shape,
-      "",
-      "parse" <> name <> "Shape :: Value -> Parser " <> shapeType,
-      emitShapeDecoder a graph declaration shape
-    ]
-  where
-    name = sdName declaration
-    consumerType = renderHaskellSource (sdHaskell declaration)
-    shapeType = structuralShapeModule (aContext a) name <> "." <> name <> "Shape"
-    binding = unQualifiedValueName (sdBinding declaration)
-
-emitShapeEncoder :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
-emitShapeEncoder a graph declaration =
-  foldMappedShape
-    MappedShapeAlgebra
-      { onRecord = \_ _ fields ->
-          nl $
-            ["encode" <> name <> "Shape shape =", "  object"]
-              <> objectEntries
-                [ tshow (rwfKey field)
-                    <> " .= "
-                    <> encodeShapeExpr a graph (rwfType field) (shapeModuleName <> "." <> rwfHaskell field <> " shape")
-                | field <- fields
-                ],
-        onEnum = \entries ->
-          nl $
-            ["encode" <> name <> "Shape = \\case"]
-              <> ["  " <> shapeModuleName <> "." <> weCtor entry <> " -> String " <> tshow (weTag entry) | entry <- entries],
-        onUnion = \encoding arms ->
-          nl $
-            ["encode" <> name <> "Shape = \\case"]
-              <> concatMap (unionEncodeArm encoding) arms
-      }
-  where
-    name = sdName declaration
-    shapeModuleName = structuralShapeModule (aContext a) name
-    unionEncodeArm encoding arm =
-      [ "  " <> shapeModuleName <> "." <> rwaCtor arm <> payloadPattern <> " ->",
-        "    object"
-      ]
-        <> objectEntries
-          ( [tshow (ueTagField encoding) <> " .= (" <> tshow (rwaTag arm) <> " :: Text)"]
-              <> [ tshow (ueContentsField encoding) <> " .= " <> encodeShapeExpr a graph payload "payload"
-                 | payload <- maybeToListText (rwaPayload arm)
-                 ]
-          )
-      where
-        payloadPattern = maybe "" (const " payload") (rwaPayload arm)
-
-emitShapeDecoder :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
-emitShapeDecoder a graph declaration =
-  foldMappedShape
-    MappedShapeAlgebra
-      { onRecord = \constructor unknownFields fields ->
-          nl $
-            [ "parse" <> name <> "Shape = withObject " <> tshow (name <> "Shape") <> " $ \\objectValue -> do"
-            ]
-              <> rejectLine "  " unknownFields (map rwfKey fields) "objectValue"
-              <> [ "  " <> shapeModuleName <> "." <> constructor,
-                   "    <$> " <> T.intercalate "\n    <*> " (map (decodeRecordField a graph) fields)
-                 ],
-        onEnum = \entries ->
-          nl $
-            [ "parse" <> name <> "Shape = withText " <> tshow (name <> "Shape") <> " $ \\tag -> case tag of"
-            ]
-              <> ["  " <> tshow (weTag entry) <> " -> pure " <> shapeModuleName <> "." <> weCtor entry | entry <- entries]
-              <> ["  _ -> fail " <> tshow ("unknown " <> name <> " wire value")],
-        onUnion = \encoding arms ->
-          nl $
-            [ "parse" <> name <> "Shape = withObject " <> tshow (name <> "Shape") <> " $ \\objectValue -> do",
-              "  tag <- objectValue .: " <> tshow (ueTagField encoding) <> " :: Parser Text",
-              "  case tag of"
-            ]
-              <> concatMap (unionDecodeArm encoding) arms
-              <> ["    _ -> fail " <> tshow ("unknown " <> name <> " union tag")]
-      }
-  where
-    name = sdName declaration
-    shapeModuleName = structuralShapeModule (aContext a) name
-    rejectLine _ IgnoreUnknown _ _ = []
-    rejectLine indent RejectUnknown allowed objectName =
-      [indent <> "rejectUnknownFields " <> tshow name <> " " <> renderTextList allowed <> " " <> objectName]
-    unionDecodeArm encoding arm =
-      ["    " <> tshow (rwaTag arm) <> " -> do"]
-        <> rejectLine "      " (ueUnknownFields encoding) allowed "objectValue"
-        <> [ case rwaPayload arm of
-               Nothing -> "      pure " <> shapeModuleName <> "." <> rwaCtor arm
-               Just payload ->
-                 "      "
-                   <> shapeModuleName
-                   <> "."
-                   <> rwaCtor arm
-                   <> " <$> (objectValue .: "
-                   <> tshow (ueContentsField encoding)
-                   <> " >>= ("
-                   <> decodeShapeExpr a graph payload
-                   <> "))"
-           ]
-      where
-        allowed = ueTagField encoding : [ueContentsField encoding | rwaPayload arm /= Nothing]
-
-decodeRecordField :: Agg -> TypeGraph -> ResolvedWireField -> Text
-decodeRecordField a graph field = case rwfPresence field of
-  PRequired ->
-    "((objectValue .: " <> key <> " :: Parser Value) >>= (" <> decoder <> "))"
-  POptional ->
-    "(case KeyMap.lookup (Key.fromText "
-      <> key
-      <> ") objectValue of Nothing -> "
-      <> missing
-      <> "; Just presentValue -> "
-      <> "("
-      <> decoder
-      <> ") presentValue)"
-  where
-    key = tshow (rwfKey field)
-    decoder = decodeShapeExpr a graph (rwfType field)
-    missing = case rwfOnMissing field of
-      Nothing -> "fail " <> tshow ("missing optional field without default: " <> rwfKey field)
-      Just onMissing -> "pure " <> renderMissingDefault a graph (rwfType field) onMissing
-
-encodeShapeExpr :: Agg -> TypeGraph -> ResolvedTypeExpr -> Text -> Text
-encodeShapeExpr _a graph expression value =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = \v -> "toJSON (" <> v <> ")",
-        onInt = \v -> "toJSON (" <> v <> ")",
-        onInteger = \v -> "toJSON (" <> v <> ")",
-        onBool = \v -> "toJSON (" <> v <> ")",
-        onNatural = \v -> "toJSON (" <> v <> ")",
-        onTime = \v -> "toJSON (" <> v <> ")",
-        onJson = id,
-        onOptional = \encode v -> "maybe Null (\\item -> " <> encode "item" <> ") (" <> v <> ")",
-        onList = \encode v -> "toJSON (map (\\item -> " <> encode "item" <> ") (" <> v <> "))",
-        onMap = \encode v -> "toJSON (Map.map (\\item -> " <> encode "item" <> ") (" <> v <> "))",
-        onRef = \key v -> case Map.lookup key (tgDeclarations graph) of
-          Just (ResolvedStructural nested _) -> "encode" <> sdName nested <> "Shape (" <> v <> ")"
-          Just (ResolvedOpaque _) -> "toJSON (" <> v <> ")"
-          Nothing -> "toJSON (" <> v <> ")"
-      }
-    expression
-    value
-
-decodeShapeExpr :: Agg -> TypeGraph -> ResolvedTypeExpr -> Text
-decodeShapeExpr _a graph =
-  foldTypeExpr
-    TypeExprAlgebra
-      { onText = "parseJSON",
-        onInt = "parseJSON",
-        onInteger = "parseJSON",
-        onBool = "parseJSON",
-        onNatural = "parseJSON",
-        onTime = "parseJSON",
-        onJson = "pure",
-        onOptional = \decode -> "\\value -> case value of Null -> pure Nothing; other -> Just <$> " <> decode <> " other",
-        onList = \decode -> "\\value -> (parseJSON value :: Parser [Value]) >>= traverse (" <> decode <> ")",
-        onMap = \decode -> "\\value -> (parseJSON value :: Parser (Map Text Value)) >>= traverse (" <> decode <> ")",
-        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
-          Just (ResolvedStructural nested _) -> "parse" <> sdName nested <> "Shape"
-          Just (ResolvedOpaque _) -> "parseJSON"
-          Nothing -> "parseJSON"
-      }
-
-renderMissingDefault :: Agg -> TypeGraph -> ResolvedTypeExpr -> OnMissing -> Text
-renderMissingDefault a graph expression = \case
-  OmNull -> "Nothing"
-  OmText value -> tshow value
-  OmInt value -> T.pack (show value)
-  OmBool value -> if value then "True" else "False"
-  OmEmptyList -> "[]"
-  OmEmptyMap -> "Map.empty"
-  OmCtor constructor -> case expression of
-    RRef key -> case Map.lookup key (tgDeclarations graph) of
-      Just (ResolvedStructural declaration _) -> structuralShapeModule (aContext a) (sdName declaration) <> "." <> constructor
-      _ -> constructor
-    _ -> constructor
-
-objectEntries :: [Text] -> [Text]
-objectEntries entries =
-  [lead index <> entry | (index, entry) <- zip [(0 :: Int) ..] entries]
-    <> ["      ]"]
-  where
-    lead 0 = "      [ "
-    lead _ = "      , "
-
-renderTextList :: [Text] -> Text
-renderTextList values = "[" <> T.intercalate ", " (map tshow values) <> "]"
-
---------------------------------------------------------------------------------
--- Authoritative version-2 expressions and transducer
---------------------------------------------------------------------------------
-
-hasVersion2Ownership :: Agg -> Bool
-hasVersion2Ownership = any ((/= LegacyHoleImplementation) . tImplementation) . aTransitions
-
-transitionEntries :: Agg -> [(Int, Transition)]
-transitionEntries aggregate = zip [1 ..] (aTransitions aggregate)
-
-transitionStem :: Int -> Transition -> Text
-transitionStem index transition =
-  "transition"
-    <> tshow' index
-    <> pascal (tSource transition)
-    <> pascal (tCommand transition)
-
-guardFunctionName :: Int -> Transition -> Text
-guardFunctionName index transition = transitionStem index transition <> "Guard"
-
-writeFunctionName :: Int -> Transition -> Name -> Text
-writeFunctionName index transition registerName =
-  transitionStem index transition <> "Write" <> pascal registerName
-
-holeFunctionName :: Int -> Transition -> Text
-holeFunctionName index transition = transitionStem index transition <> "Hole"
-
-holeFoldVersionName :: Int -> Transition -> Text
-holeFoldVersionName index transition = holeFunctionName index transition <> "FoldVersion"
-
-outputFunctionName :: Int -> Transition -> Int -> Name -> Text
-outputFunctionName transitionIndex transition emitIndex eventName =
-  transitionStem transitionIndex transition
-    <> "Output"
-    <> tshow' emitIndex
-    <> pascal eventName
-
--- | Legacy create-once output-hook names made obsolete by authoritative
--- version-2 @fields(Command)@ generation.  Scaffolding reports these names as
--- safe-to-remove candidates without parsing or modifying consumer Haskell.
-obsoleteGeneratedOutputHooks :: Spec -> [(Name, Text)]
-obsoleteGeneratedOutputHooks spec =
-  [ ( aggName aggregate,
-      outputFunctionName transitionIndex transition emitIndex eventName
-    )
-  | aggregate <- [value | NAggregate value <- specNodes spec],
-    (transitionIndex, transition) <- zip [1 ..] (aggTransitions aggregate),
-    (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
-    Right GeneratedCommandIdentity {} <- [eventOutputMapping spec aggregate transition emitIndex eventName]
-  ]
-
-commandForTransition :: Agg -> Transition -> ResolvedCtor
-commandForTransition aggregate transition =
-  fromMaybe
-    (error ("validated aggregate command disappeared: " <> T.unpack (tCommand transition)))
-    (find ((== tCommand transition) . rcName) (aCommands aggregate))
-
-eventForName :: Agg -> Name -> ResolvedCtor
-eventForName aggregate eventName =
-  fromMaybe
-    (error ("validated aggregate event disappeared: " <> T.unpack eventName))
-    (find ((== eventName) . rcName) (aEvents aggregate))
-
-commandFieldsType :: Transition -> Text
-commandFieldsType transition = "RegFieldsOf " <> tCommand transition <> "Data"
-
-payloadProjectionType :: Agg -> Transition -> Text
-payloadProjectionType aggregate transition =
-  "B.PayloadProj "
-    <> aName aggregate
-    <> "Regs "
-    <> aName aggregate
-    <> "Command ("
-    <> commandFieldsType transition
-    <> ")"
-
-expressionFunctionNames :: Agg -> [Text]
-expressionFunctionNames aggregate =
-  concat
-    [ maybe [] (const [guardFunctionName index transition]) (tGuard transition)
-        <> [writeFunctionName index transition registerName | (registerName, _) <- tWrites transition]
-    | (index, transition) <- transitionEntries aggregate,
-      tImplementation transition == GeneratedImplementation
-    ]
-
-emitExpressions :: Agg -> Text
-emitExpressions aggregate
-  | null exports = nl [generatedBanner, "module " <> aGenPrefix aggregate <> ".Expressions () where"]
-  | otherwise =
-      nl $
-        [ "{-# LANGUAGE DataKinds #-}",
-          "{-# LANGUAGE OverloadedLabels #-}",
-          "{-# LANGUAGE OverloadedRecordDot #-}",
-          "{-# LANGUAGE TypeApplications #-}",
-          generatedBanner
-        ]
-          ++ moduleDeclaration
-          ++ [ "",
-               "import " <> aGenPrefix aggregate <> ".Domain",
-               "import Keiki.Builder qualified as B",
-               "import Keiki.Core qualified as K",
-               "import Keiki.Generics (RegFieldsOf)"
-             ]
-          ++ ["import Data.Text (Text)" | expressionUsesType AggregateText || expressionUsesNominalEqualityKey]
-          ++ ["import Data.Time.Clock (UTCTime)" | expressionUsesType AggregateTime && not expressionUsesTimeLiteral]
-          ++ ["import Data.Time.Calendar (fromGregorian)" | expressionUsesTimeLiteral]
-          ++ ["import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)" | expressionUsesTimeLiteral]
-          ++ ["import Numeric.Natural (Natural)" | expressionUsesType AggregateNatural]
-          ++ generatedNominalTypeImportsForService (aggregateCheckedService aggregate) (aContext aggregate) generatedExpressionNominals
-          ++ structuralProjectionImport
-          ++ generatedNominalProjectionImport
-          ++ consumerNominalProjectionImport
-          ++ consumerImports
-          ++ ["import Data.KindID qualified as KindID" | expressionUsesConsumerIdLiteral]
-          ++ ["import Keiro.Codec.Nominal (nominalFromRepresentation)" | expressionUsesConsumerNominalLiteral]
-          ++ consumerLiteralImports
-          ++ concatMap (uncurry (emitTransitionExpressions aggregate)) (transitionEntries aggregate)
-  where
-    exports = expressionFunctionNames aggregate
-    moduleDeclaration = case exports of
-      firstExport : rest ->
-        [ "module " <> aGenPrefix aggregate <> ".Expressions",
-          "  ( " <> firstExport
-        ]
-          ++ ["  , " <> value | value <- rest]
-          ++ ["  ) where"]
-      [] -> error "non-empty expression export invariant"
-    structuralProjectionImport =
-      [ "import " <> structuralProjectionModule (aContext aggregate) <> " qualified as StructuralProjections"
-      | maybe False (not . null . projectionSpecs) (aTypeGraph aggregate)
-      ]
-    generatedNominalProjectionImport =
-      [ "import " <> generatedNominalModule (aContext aggregate) <> " qualified as GeneratedNominals"
-      | any expressionUsesGeneratedNominalProjection resolvedExpressions
-      ]
-    consumerNominalProjectionImport =
-      [ "import " <> nominalProjectionModule (aContext aggregate) <> " qualified as NominalProjections"
-      | any expressionUsesConsumerNominalProjection resolvedExpressions
-      ]
-    consumerImports =
-      map ("import " <>)
-        . filter (not . builtinExpressionImport)
-        . sort
-        . nub
-        . Set.toList
-        . Set.unions
-        $ [ aggregateImports (aSymbols aggregate) resolvedType
-          | resolvedType <- map rrType (aRegs aggregate) <> map snd (concatMap rcFields (aCommands aggregate))
-          ]
-    consumerLiteralImports =
-      [ "import " <> nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal) <> " qualified"
-      | nominal <- consumerLiteralNominals,
-        EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
-      ]
-        <> [ "import " <> qualifiedModule (consumerNominalBinding binding) <> " qualified"
-           | nominal <- consumerLiteralNominals,
-             ConsumerNominal binding <- [resolvedNominalOwnership nominal]
-           ]
-    resolvedExpressions = resolvedGeneratedExpressions aggregate
-    expressionUsesType wanted = any (anyTypedExpression ((== wanted) . typedScalarType)) resolvedExpressions
-    expressionUsesTimeLiteral = any (anyTypedExpression isTimeLiteral) resolvedExpressions
-    expressionUsesConsumerNominalLiteral = not (null consumerLiteralNominals)
-    expressionUsesConsumerIdLiteral = any (isIdRepresentation . resolvedNominalRepresentation) consumerLiteralNominals
-    expressionUsesNominalEqualityKey = any (anyTypedExpression isNominalEqualityOperand) resolvedExpressions
-    consumerLiteralNominals = nub [nominal | expression <- resolvedExpressions, nominal <- typedConsumerLiteralNominals expression]
-    generatedExpressionNominals =
-      stableNominals
-        [ nominal
-        | expression <- resolvedExpressions,
-          nominal <- typedGeneratedNominals expression
-        ]
-    isTimeLiteral expression = case typedScalarNode expression of
-      TypedLiteral ScalarTimeValue {} -> True
-      _ -> False
-    isIdRepresentation IdRepresentation {} = True
-    isIdRepresentation _ = False
-
-builtinExpressionImport :: Text -> Bool
-builtinExpressionImport imported =
-  any (`T.isPrefixOf` imported) ["Data.Text", "Data.Time", "Numeric.Natural"]
-
-resolvedGeneratedExpressions :: Agg -> [TypedScalarExpr]
-resolvedGeneratedExpressions aggregate =
-  concat
-    [ maybe [] (pure . resolvedGuard index transition) (tGuard transition)
-        <> [ resolvedWrite index transition registerName expression
-           | (registerName, expression) <- tWrites transition
-           ]
-    | (index, transition) <- transitionEntries aggregate,
-      tImplementation transition == GeneratedImplementation
-    ]
-  where
-    environment transition = expressionEnvironment (aSpec aggregate) (aAggregate aggregate) transition
-    resolvedGuard index transition expression =
-      expressionOrDie (guardFunctionName index transition) (resolveGuardExpr (environment transition) expression)
-    resolvedWrite index transition registerName expression =
-      expressionOrDie (writeFunctionName index transition registerName) (resolveWriteExpr (environment transition) registerName expression)
-
-anyTypedExpression :: (TypedScalarExpr -> Bool) -> TypedScalarExpr -> Bool
-anyTypedExpression predicate expression =
-  predicate expression || any (anyTypedExpression predicate) (typedExpressionChildren expression)
-
-typedExpressionChildren :: TypedScalarExpr -> [TypedScalarExpr]
-typedExpressionChildren expression = case typedScalarNode expression of
-  TypedLiteral {} -> []
-  TypedRoot {} -> []
-  TypedProject {} -> []
-  TypedAdd _ left right -> [left, right]
-  TypedSubtract _ left right -> [left, right]
-  TypedMultiply _ left right -> [left, right]
-  TypedEqual left right -> [left, right]
-  TypedNotEqual left right -> [left, right]
-  TypedCompare _ left right -> [left, right]
-  TypedAnd left right -> [left, right]
-  TypedOr left right -> [left, right]
-
-isNominalEqualityOperand :: TypedScalarExpr -> Bool
-isNominalEqualityOperand expression = case (typedScalarType expression, typedScalarNode expression) of
-  (AggregateNominal nominal, TypedRoot {}) -> case (resolvedNominalOwnership nominal, resolvedNominalRepresentation nominal) of
-    (ConsumerNominal {}, ScalarRepresentation {}) -> True
-    (_, IdRepresentation {}) -> True
-    (_, EnumRepresentation {}) -> True
-    _ -> False
-  _ -> False
-
-expressionUsesGeneratedNominalProjection :: TypedScalarExpr -> Bool
-expressionUsesGeneratedNominalProjection = anyTypedExpression $ \expression -> case (typedScalarType expression, typedScalarNode expression) of
-  (AggregateNominal nominal, TypedRoot {}) -> case (resolvedNominalOwnership nominal, resolvedNominalRepresentation nominal) of
-    (GeneratedNominal, IdRepresentation {}) -> True
-    (GeneratedNominal, EnumRepresentation {}) -> True
-    _ -> False
-  _ -> False
-
-expressionUsesConsumerNominalProjection :: TypedScalarExpr -> Bool
-expressionUsesConsumerNominalProjection = anyTypedExpression $ \expression -> case (typedScalarType expression, typedScalarNode expression) of
-  (AggregateNominal nominal, TypedRoot {}) -> case resolvedNominalOwnership nominal of
-    ConsumerNominal {} -> True
-    GeneratedNominal -> False
-  _ -> False
-
-typedConsumerLiteralNominals :: TypedScalarExpr -> [ResolvedNominalType]
-typedConsumerLiteralNominals expression = own <> concatMap typedConsumerLiteralNominals (typedExpressionChildren expression)
-  where
-    own = case (typedScalarType expression, typedScalarNode expression) of
-      (AggregateNominal nominal, TypedLiteral ScalarEnumValue {})
-        | ConsumerNominal {} <- resolvedNominalOwnership nominal -> [nominal]
-      (AggregateNominal nominal, TypedLiteral ScalarIdValue {})
-        | ConsumerNominal {} <- resolvedNominalOwnership nominal -> [nominal]
-      _ -> []
-
-typedGeneratedNominals :: TypedScalarExpr -> [ResolvedNominalType]
-typedGeneratedNominals expression = own <> concatMap typedGeneratedNominals (typedExpressionChildren expression)
-  where
-    own = case typedScalarType expression of
-      AggregateNominal nominal
-        | GeneratedNominal <- resolvedNominalOwnership nominal -> [nominal]
-      _ -> []
-
-emitTransitionExpressions :: Agg -> Int -> Transition -> [Text]
-emitTransitionExpressions aggregate index transition
-  | tImplementation transition /= GeneratedImplementation = []
-  | otherwise =
-      concat
-        [ maybe [] (emitGuardDefinition aggregate index transition) (tGuard transition),
-          concatMap (uncurry (emitWriteDefinition aggregate index transition)) (tWrites transition)
-        ]
-
-emitGuardDefinition :: Agg -> Int -> Transition -> Expr -> [Text]
-emitGuardDefinition aggregate index transition expression =
-  [ "",
-    functionName <> " :: " <> payloadProjectionType aggregate transition <> " -> K.HsPred " <> aName aggregate <> "Regs " <> aName aggregate <> "Command",
-    functionName <> " d = " <> renderKeikiPredicate aggregate transition resolved
-  ]
-  where
-    functionName = guardFunctionName index transition
-    resolved = expressionOrDie functionName (resolveGuardExpr (expressionEnvironment (aSpec aggregate) (aAggregate aggregate) transition) expression)
-
-emitWriteDefinition :: Agg -> Int -> Transition -> Name -> Expr -> [Text]
-emitWriteDefinition aggregate index transition registerName expression =
-  [ "",
-    functionName
-      <> " :: "
-      <> payloadProjectionType aggregate transition
-      <> " -> K.Term "
-      <> aName aggregate
-      <> "Regs "
-      <> aName aggregate
-      <> "Command ("
-      <> commandFieldsType transition
-      <> ") "
-      <> renderDomainType aggregate (typedScalarType resolved),
-    functionName <> " d = " <> renderKeikiTerm aggregate transition resolved
-  ]
-  where
-    functionName = writeFunctionName index transition registerName
-    resolved = expressionOrDie functionName (resolveWriteExpr (expressionEnvironment (aSpec aggregate) (aAggregate aggregate) transition) registerName expression)
-
-expressionOrDie :: Text -> Either (NonEmpty ExpressionDiagnostic) TypedScalarExpr -> TypedScalarExpr
-expressionOrDie owner = either (error . (("validated expression disappeared for " <> T.unpack owner <> ": ") <>) . show) id
-
-renderKeikiPredicate :: Agg -> Transition -> TypedScalarExpr -> Text
-renderKeikiPredicate aggregate transition expression = case typedScalarNode expression of
-  TypedEqual left right -> "K.PEq " <> atom (renderComparisonTerm aggregate transition left) <> " " <> atom (renderComparisonTerm aggregate transition right)
-  TypedNotEqual left right -> "K.pnot (K.PEq " <> atom (renderComparisonTerm aggregate transition left) <> " " <> atom (renderComparisonTerm aggregate transition right) <> ")"
-  TypedCompare operator left right ->
-    "K.PCmp "
-      <> renderKeikiCmp operator
-      <> " "
-      <> atom (renderComparisonTerm aggregate transition left)
-      <> " "
-      <> atom (renderComparisonTerm aggregate transition right)
-  TypedAnd left right -> "K.PAnd " <> atom (renderKeikiPredicate aggregate transition left) <> " " <> atom (renderKeikiPredicate aggregate transition right)
-  TypedOr left right -> "K.POr " <> atom (renderKeikiPredicate aggregate transition left) <> " " <> atom (renderKeikiPredicate aggregate transition right)
-  _ -> "K.PEq " <> atom (renderKeikiTerm aggregate transition expression) <> " (K.lit True)"
-  where
-    atom value = "(" <> value <> ")"
-
-renderKeikiCmp :: CmpOp -> Text
-renderKeikiCmp = \case
-  OpEq -> error "equality is rendered as PEq"
-  OpNeq -> error "inequality is rendered as pnot PEq"
-  OpLt -> "K.CmpLt"
-  OpLe -> "K.CmpLe"
-  OpGt -> "K.CmpGt"
-  OpGe -> "K.CmpGe"
-
-renderComparisonTerm :: Agg -> Transition -> TypedScalarExpr -> Text
-renderComparisonTerm aggregate transition expression = case (typedScalarType expression, typedScalarNode expression) of
-  (AggregateNominal nominal, TypedRoot provenance)
-    | nominalComparisonProjection nominal -> renderNominalProjectionTerm aggregate transition nominal provenance
-  (AggregateNominal nominal, TypedLiteral (ScalarEnumValue _ constructor)) ->
-    "K.lit (" <> tshow (enumWireFor nominal constructor) <> " :: Text)"
-  (AggregateNominal _, TypedLiteral (ScalarIdValue _ value)) ->
-    "K.lit (" <> tshow value <> " :: Text)"
-  _ -> renderKeikiTerm aggregate transition expression
-
-nominalComparisonProjection :: ResolvedNominalType -> Bool
-nominalComparisonProjection nominal = case resolvedNominalRepresentation nominal of
-  IdRepresentation {} -> True
-  EnumRepresentation {} -> True
-  ScalarRepresentation {} -> case resolvedNominalOwnership nominal of
-    ConsumerNominal {} -> True
-    GeneratedNominal -> False
-
-enumWireFor :: ResolvedNominalType -> Name -> Text
-enumWireFor nominal constructor = case resolvedNominalRepresentation nominal of
-  EnumRepresentation constructors -> fromMaybe (error "validated enum literal lost its wire spelling") (lookup constructor (NE.toList constructors))
-  _ -> error "validated enum literal lost its enum representation"
-
-renderNominalProjectionTerm :: Agg -> Transition -> ResolvedNominalType -> ScalarRootProvenance -> Text
-renderNominalProjectionTerm aggregate transition nominal provenance = case provenance of
-  ScalarRegisterRoot registerName ownerType ->
-    "K.regProj "
-      <> projectionQualifier
-      <> "."
-      <> witness
-      <> " (#"
-      <> registerName
-      <> " :: K.Index "
-      <> aName aggregate
-      <> "Regs "
-      <> renderDomainType aggregate ownerType
-      <> ")"
-  ScalarCommandRoot fieldName ownerType ->
-    "K.inpProj "
-      <> projectionQualifier
-      <> "."
-      <> witness
-      <> " inCtor"
-      <> tCommand transition
-      <> " (#"
-      <> fieldName
-      <> " :: K.Index ("
-      <> commandFieldsType transition
-      <> ") "
-      <> renderDomainType aggregate ownerType
-      <> ")"
-  where
-    projectionQualifier = case resolvedNominalOwnership nominal of
-      GeneratedNominal -> "GeneratedNominals"
-      ConsumerNominal {} -> "NominalProjections"
-    witness = case resolvedNominalRepresentation nominal of
-      ScalarRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Witness"
-      IdRepresentation {} -> nominalEqualityWitnessName nominal
-      EnumRepresentation {} -> nominalEqualityWitnessName nominal
-
-renderKeikiTerm :: Agg -> Transition -> TypedScalarExpr -> Text
-renderKeikiTerm aggregate transition expression = case typedScalarNode expression of
-  TypedLiteral value -> renderKeikiLiteral aggregate (typedScalarType expression) value
-  TypedRoot (ScalarRegisterRoot registerName _) -> "B.reg @" <> tshow registerName
-  TypedRoot (ScalarCommandRoot fieldName _) -> "d." <> fieldName
-  TypedProject provenance projection -> renderStructuralProjectionTerm aggregate transition provenance projection
-  TypedAdd _ left right -> binary "K.tadd" left right
-  TypedSubtract _ left right -> binary "K.tsub" left right
-  TypedMultiply _ left right -> binary "K.tmul" left right
-  TypedEqual {} -> impossiblePredicate
-  TypedNotEqual {} -> impossiblePredicate
-  TypedCompare {} -> impossiblePredicate
-  TypedAnd {} -> impossiblePredicate
-  TypedOr {} -> impossiblePredicate
-  where
-    binary operator left right = operator <> " " <> parenthesized left <> " " <> parenthesized right
-    parenthesized = (\value -> "(" <> value <> ")") . renderKeikiTerm aggregate transition
-    impossiblePredicate = error "predicate-valued Boolean expressions cannot be lowered as register terms"
-
-renderStructuralProjectionTerm :: Agg -> Transition -> ScalarRootProvenance -> ResolvedScalarProjection -> Text
-renderStructuralProjectionTerm aggregate transition provenance projection = case provenance of
-  ScalarRegisterRoot registerName ownerType ->
-    "K.regProj StructuralProjections."
-      <> witness
-      <> " (#"
-      <> registerName
-      <> " :: K.Index "
-      <> aName aggregate
-      <> "Regs "
-      <> renderDomainType aggregate ownerType
-      <> ")"
-  ScalarCommandRoot fieldName ownerType ->
-    "K.inpProj StructuralProjections."
-      <> witness
-      <> " inCtor"
-      <> tCommand transition
-      <> " (#"
-      <> fieldName
-      <> " :: K.Index ("
-      <> commandFieldsType transition
-      <> ") "
-      <> renderDomainType aggregate ownerType
-      <> ")"
-  where
-    witness = lowerFirst (projectionTag (unMappedKey (scalarProjectionOwner projection)) (scalarProjectionPointer projection)) <> "Witness"
-
-renderKeikiLiteral :: Agg -> ResolvedAggregateType -> ScalarValue -> Text
-renderKeikiLiteral aggregate scalarType = \case
-  ScalarTextValue value -> "K.lit (" <> tshow value <> " :: Text)"
-  ScalarIntValue value -> "K.lit (" <> tshow' value <> " :: Int)"
-  ScalarIntegerValue value -> "K.lit (" <> T.pack (show value) <> " :: Integer)"
-  ScalarNaturalValue value -> "K.lit (" <> T.pack (show value) <> " :: Natural)"
-  ScalarBoolValue value -> "K.lit " <> if value then "True" else "False"
-  ScalarTimeValue value -> "K.lit " <> renderRegisterInitial (InitialTime value)
-  ScalarEnumValue typeName constructor -> case scalarType of
-    AggregateNominal nominal -> case resolvedNominalOwnership nominal of
-      GeneratedNominal -> "K.lit " <> constructor
-      ConsumerNominal binding ->
-        "K.lit (nominalFromRepresentation "
-          <> unQualifiedValueName (consumerNominalBinding binding)
-          <> " "
-          <> nominalRepresentationModule (aContext aggregate) typeName
-          <> "."
-          <> constructor
-          <> ")"
-    _ -> error "validated enum literal lost its nominal type"
-  ScalarIdValue typeName value -> case scalarType of
-    AggregateNominal nominal -> case resolvedNominalOwnership nominal of
-      GeneratedNominal -> case idDomainContractFor (aLanguageContract aggregate) =<< idPrefixOf nominal of
-        Nothing -> "K.lit (" <> typeName <> " " <> tshow value <> ")"
-        Just _ ->
-          "K.lit (case parse"
-            <> typeName
-            <> " "
-            <> tshow value
-            <> " of Right parsed -> parsed; Left _ -> error \"validated ID literal failed to parse\")"
-      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
-        IdRepresentation prefix ->
-          "K.lit (nominalFromRepresentation "
-            <> unQualifiedValueName (consumerNominalBinding binding)
-            <> " (case KindID.parseText @"
-            <> tshow prefix
-            <> " "
-            <> tshow value
-            <> " of Right parsed -> parsed; Left _ -> error \"validated ID literal failed to parse\"))"
-        _ -> error "validated ID literal lost its ID representation"
-    _ -> error "validated ID literal lost its nominal type"
-  where
-    idPrefixOf nominal = case resolvedNominalRepresentation nominal of
-      IdRepresentation prefix -> Just prefix
-      _ -> Nothing
-
-generatedIdSampleHaskell :: Agg -> ResolvedNominalType -> Maybe Text
-generatedIdSampleHaskell aggregate nominal = do
-  prefix <- case resolvedNominalRepresentation nominal of
-    IdRepresentation value -> Just value
-    _ -> Nothing
-  contract <- idDomainContractFor (aLanguageContract aggregate) prefix
-  let name = resolvedNominalName nominal
-      sample = idDomainSampleText contract
-  pure
-    ( "(case parse"
-        <> name
-        <> " "
-        <> tshow sample
-        <> " of Right parsed -> parsed; Left _ -> error \"generated valid ID sample failed to parse\")"
-    )
-
-emitGeneratedTransducer :: Agg -> Text
-emitGeneratedTransducer aggregate =
-  nl $
-    [ "{-# LANGUAGE BlockArguments #-}",
-      "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE GADTs #-}",
-      "{-# LANGUAGE OverloadedRecordDot #-}",
-      "{-# LANGUAGE QualifiedDo #-}",
-      "{-# LANGUAGE TypeApplications #-}",
-      generatedBanner,
-      "module " <> aGenPrefix aggregate <> ".Transducer",
-      "  ( " <> lowerFirst (aName aggregate) <> "Transducer",
-      "  , " <> lowerFirst (aName aggregate) <> "FoldFingerprint",
-      "  , BehaviorOwnership (..)",
-      "  , " <> lowerFirst (aName aggregate) <> "PredicateVerifications",
-      "  ) where",
-      "",
-      "import " <> aGenPrefix aggregate <> ".Domain",
-      "import Data.Text (Text)",
-      "import Keiki.Builder qualified as B",
-      "import Keiki.Core (HsPred, SymTransducer)",
-      "import Keiki.Core qualified as K",
-      "import Keiki.Symbolic qualified as S"
-    ]
-      ++ ["import " <> aHolePrefix aggregate <> ".Holes qualified as Holes" | transducerUsesHoles aggregate]
-      ++ ["import " <> aGenPrefix aggregate <> ".Expressions qualified as Expressions" | not (null (expressionFunctionNames aggregate))]
-      ++ ["import Data.Text qualified as T" | anyHoleOwned aggregate]
-      ++ ["import Keiki.Builder ((=:))" | any (not . null . tWrites . snd) (transitionEntries aggregate)]
-      ++ ["import Keiro.Snapshot.Codec (FoldVersion (..))" | anyHoleOwned aggregate]
-      ++ [ "",
-           lowerFirst (aName aggregate) <> "Transducer",
-           "  :: SymTransducer",
-           "       (HsPred " <> aName aggregate <> "Regs " <> aName aggregate <> "Command)",
-           "       " <> aName aggregate <> "Regs",
-           "       " <> aVertexType aggregate,
-           "       " <> aName aggregate <> "Command",
-           "       " <> aName aggregate <> "Event",
-           lowerFirst (aName aggregate) <> "Transducer =",
-           "  B.buildTransducer " <> initialVertex aggregate <> " initial" <> aName aggregate <> "Regs isTerminal do",
-           nl (concatMap (generatedFromBlock aggregate) (groupTransitionEntriesBySource aggregate)),
-           " where",
-           "  isTerminal = \\case",
-           nl ["    " <> vertexCtor aggregate (stName state) <> " -> True" | state <- aStates aggregate, stTerminal state],
-           "    _ -> False",
-           "",
-           lowerFirst (aName aggregate) <> "FoldFingerprint :: Text",
-           lowerFirst (aName aggregate) <> "FoldFingerprint = " <> foldFingerprintExpression aggregate,
-           "",
-           "data BehaviorOwnership = GeneratedOwned | HoleOwned",
-           "  deriving stock (Eq, Show)",
-           "",
-           "-- Every checked transition predicate is audited through Keiki's conservative",
-           "-- symbolic verifier. Opaque Hole terms remain explicitly unverified.",
-           lowerFirst (aName aggregate) <> "PredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]",
-           lowerFirst (aName aggregate) <> "PredicateVerifications = sequence",
-           nl (renderVerificationList aggregate),
-           " where",
-           "  verifyTransition label owner source edgeIndex =",
-           "    case drop edgeIndex (K.edgesOut " <> lowerFirst (aName aggregate) <> "Transducer source) of",
-           "      K.Edge predicate _ _ _ _ : _ -> (\\result -> (label, owner, result)) <$> S.verifyPredicate predicate",
-           "      [] -> pure (label, owner, S.UnverifiedSolverFailure \"generated transition edge missing\")"
-         ]
-
-anyHoleOwned :: Agg -> Bool
-anyHoleOwned = any ((== HoleImplementation) . tImplementation) . aTransitions
-
-transducerUsesHoles :: Agg -> Bool
-transducerUsesHoles aggregate =
-  anyHoleOwned aggregate
-    || any isHandOwned (Map.elems (aOutputMappings aggregate))
-  where
-    isHandOwned HandOwnedEventOutput {} = True
-    isHandOwned GeneratedCommandIdentity {} = False
-
-renderVerificationList :: Agg -> [Text]
-renderVerificationList aggregate =
-  [ (if listIndex == (0 :: Int) then "  [ " else "  , ")
-      <> "verifyTransition "
-      <> tshow (transitionStem transitionIndex transition)
-      <> " "
-      <> ownership
-      <> " "
-      <> vertexCtor aggregate source
-      <> " "
-      <> tshow' edgeIndex
-  | (listIndex, (source, edgeIndex, transitionIndex, transition)) <- zip [0 ..] entries,
-    let ownership = case tImplementation transition of
-          GeneratedImplementation -> "GeneratedOwned"
-          HoleImplementation -> "HoleOwned"
-          LegacyHoleImplementation -> error "legacy transition reached version-2 verification generation"
-  ]
-    <> ["  ]"]
-  where
-    entries =
-      [ (source, edgeIndex, transitionIndex, transition)
-      | (source, transitions) <- groupTransitionEntriesBySource aggregate,
-        (edgeIndex, (transitionIndex, transition)) <- zip [0 ..] transitions
-      ]
-
-foldFingerprintExpression :: Agg -> Text
-foldFingerprintExpression aggregate = case holeVersions of
-  [] -> tshow (aFoldFingerprint aggregate)
-  _ ->
-    "T.intercalate \"|\" ("
-      <> tshow (aFoldFingerprint aggregate)
-      <> " : [foldToken "
-      <> T.intercalate ", foldToken " holeVersions
-      <> "] ) where foldToken (FoldVersion token) = T.pack (show (T.length token)) <> \":\" <> token"
-  where
-    holeVersions =
-      [ "Holes." <> holeFoldVersionName index transition
-      | (index, transition) <- transitionEntries aggregate,
-        tImplementation transition == HoleImplementation
-      ]
-
-groupTransitionEntriesBySource :: Agg -> [(Text, [(Int, Transition)])]
-groupTransitionEntriesBySource aggregate = go [] (transitionEntries aggregate)
-  where
-    go accumulated [] = reverse accumulated
-    go accumulated (entry@(_, transition) : remaining) =
-      let source = tSource transition
-          (same, rest) = span ((== source) . tSource . snd) remaining
-       in go ((source, entry : same) : accumulated) rest
-
-generatedFromBlock :: Agg -> (Text, [(Int, Transition)]) -> [Text]
-generatedFromBlock aggregate (source, transitions) =
-  ["    B.from " <> vertexCtor aggregate source <> " do"]
-    ++ concatMap (uncurry (generatedOnCmdBlock aggregate)) transitions
-
-generatedOnCmdBlock :: Agg -> Int -> Transition -> [Text]
-generatedOnCmdBlock aggregate index transition =
-  ["      B.onCmd inCtor" <> tCommand transition <> " $ \\d -> B.do"]
-    ++ ["        B.replayOnly" | tMode transition == TmReplayOnly]
-    ++ generatedBehavior
-    ++ outputLines
-    ++ ["        B.noEmit" | null (tEmits transition)]
-    ++ ["        B.goto " <> vertexCtor aggregate (tGoto transition)]
-  where
-    generatedBehavior = case tImplementation transition of
-      GeneratedImplementation ->
-        maybe [] (const ["        B.requireGuard (Expressions." <> guardFunctionName index transition <> " d)"]) (tGuard transition)
-          ++ [ "        B.slot @" <> tshow registerName <> " =: Expressions." <> writeFunctionName index transition registerName <> " d"
-             | (registerName, _) <- tWrites transition
-             ]
-      HoleImplementation -> ["        Holes." <> holeFunctionName index transition <> " d"]
-      LegacyHoleImplementation -> error "legacy transition reached version-2 transducer generation"
-    outputLines =
-      concat
-        [ generatedOutputLines aggregate index transition emitIndex eventName
-        | (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
-        ]
-
-generatedOutputLines :: Agg -> Int -> Transition -> Int -> Name -> [Text]
-generatedOutputLines aggregate transitionIndex transition emitIndex eventName =
-  case outputMappingFor aggregate transitionIndex emitIndex of
-    GeneratedCommandIdentity _ fields -> case fields of
-      [] -> ["        B.emit wire" <> eventName <> " B.oNil"]
-      _ ->
-        [ "        B.emit wire" <> eventName <> " (" <> eventName <> "TermFields"
-        ]
-          <> [ lead fieldIndex
-                 <> outputSelector field
-                 <> " = d."
-                 <> outputSelector field
-             | (fieldIndex, field) <- zip [0 :: Int ..] fields
-             ]
-          <> ["          })"]
-    HandOwnedEventOutput {} ->
-      [ "        B.emit wire"
-          <> eventName
-          <> " (Holes."
-          <> outputFunctionName transitionIndex transition emitIndex eventName
-          <> " d)"
-      ]
-  where
-    lead 0 = "          { "
-    lead _ = "          , "
-
-outputMappingFor :: Agg -> Int -> Int -> EventOutputMapping
-outputMappingFor aggregate transitionIndex emitIndex =
-  fromMaybe
-    (error ("missing checked event-output mapping for transition " <> show transitionIndex <> ", emit " <> show emitIndex))
-    (Map.lookup (transitionIndex, emitIndex) (aOutputMappings aggregate))
-
---------------------------------------------------------------------------------
--- EventStream module
---------------------------------------------------------------------------------
-
-emitEventStream :: Agg -> Text
-emitEventStream a =
-  nl $
-    [ generatedBanner,
-      "module " <> aGenPrefix a <> ".EventStream",
-      "  ( " <> lowerFirst (aName a) <> "Category",
-      "  , " <> lowerFirst (aName a) <> "EventStream",
-      "  , " <> lowerFirst (aName a) <> "EventStreamDef",
-      "  , " <> aName a <> "EventStream",
-      "  , " <> aName a <> "EventStreamDef"
-    ]
-      ++ ["  , " <> lowerFirst (aName a) <> "SnapshotFixture" | hasSnapshot a]
-      ++ [ "  ) where",
-           "",
-           "import " <> aGenPrefix a <> ".Domain",
-           "import " <> aGenPrefix a <> ".Codec (" <> lowerFirst (aName a) <> "Codec)",
-           transducerImport a,
-           "import Keiki.Core (HsPred)",
-           "import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))",
-           "import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)"
-         ]
-      ++ ["import Data.Text (Text)" | hasSnapshot a]
-      ++ ["import Keiro.Snapshot.Codec (defaultStateCodec, withFoldFingerprint)" | hasSnapshot a]
-      ++ [ "import Keiro.Stream qualified as Stream",
-           "",
-           "-- The validated aggregate stream category (hole-kind 5: referenced, never retyped).",
-           "-- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.",
-           "-- categoryUnsafe is safe here because this generated literal passed the DSL category proof.",
-           lowerFirst (aName a) <> "Category :: Stream.StreamCategory a",
-           lowerFirst (aName a) <> "Category = Stream.categoryUnsafe " <> tshow categoryName,
-           "",
-           "type " <> aName a <> "EventStreamDef =",
-           "  EventStream (HsPred " <> aName a <> "Regs " <> aName a <> "Command) " <> aName a <> "Regs " <> aVertexType a <> " " <> aName a <> "Command " <> aName a <> "Event",
-           "",
-           "type " <> aName a <> "EventStream =",
-           "  ValidatedEventStream (HsPred " <> aName a <> "Regs " <> aName a <> "Command) " <> aName a <> "Regs " <> aVertexType a <> " " <> aName a <> "Command " <> aName a <> "Event",
-           "",
-           lowerFirst (aName a) <> "EventStreamDef :: " <> aName a <> "EventStreamDef",
-           lowerFirst (aName a) <> "EventStreamDef =",
-           "  EventStream",
-           "    { transducer = " <> lowerFirst (aName a) <> "Transducer",
-           "    , initialState = " <> initialVertex a,
-           "    , initialRegisters = initial" <> aName a <> "Regs",
-           "    , eventCodec = " <> lowerFirst (aName a) <> "Codec",
-           "    , resolveStreamName = Stream.streamName",
-           "    , snapshotPolicy = " <> snapshotPolicyExpr a
-         ]
-      ++ stateCodecFieldLines a
-      ++ [ "    }",
-           ""
-         ]
-      ++ snapshotFixtureLines a
-      ++ [ lowerFirst (aName a) <> "EventStream :: " <> aName a <> "EventStream",
-           lowerFirst (aName a) <> "EventStream =",
-           "  mkEventStreamOrThrow " <> tshow (aName a) <> " " <> lowerFirst (aName a) <> "EventStreamDef"
-         ]
-  where
-    categoryName = staticCategory ("aggregate " <> aName a) (lowerFirst (aName a))
-
-snapshotPolicyExpr :: Agg -> Text
-snapshotPolicyExpr aggregate = case aSnapshot aggregate of
-  Nothing -> "Never"
-  Just snapshot -> case snapPolicy snapshot of
-    SnapEvery interval -> "Every " <> tshow' interval
-    SnapOnTerminal -> "OnTerminal"
-
-stateCodecExpr :: Agg -> Text
-stateCodecExpr aggregate = case aSnapshot aggregate of
-  Nothing -> "Nothing"
-  Just snapshot ->
-    "Just (withFoldFingerprint "
-      <> foldFingerprintValue aggregate
-      <> " (defaultStateCodec "
-      <> tshow' (snapCodecVersion snapshot)
-      <> "))"
-
-transducerImport :: Agg -> Text
-transducerImport aggregate
-  | hasVersion2Ownership aggregate =
-      "import "
-        <> aGenPrefix aggregate
-        <> ".Transducer ("
-        <> lowerFirst (aName aggregate)
-        <> "FoldFingerprint, "
-        <> lowerFirst (aName aggregate)
-        <> "Transducer)"
-  | otherwise =
-      "import "
-        <> aHolePrefix aggregate
-        <> ".Holes ("
-        <> lowerFirst (aName aggregate)
-        <> "Transducer)"
-
-foldFingerprintValue :: Agg -> Text
-foldFingerprintValue aggregate
-  | hasVersion2Ownership aggregate = lowerFirst (aName aggregate) <> "FoldFingerprint"
-  | otherwise = tshow (aFoldFingerprint aggregate)
-
-stateCodecFieldLines :: Agg -> [Text]
-stateCodecFieldLines aggregate = case aSnapshot aggregate of
-  Nothing -> ["    , stateCodec = Nothing"]
-  Just _
-    | hasVersion2Ownership aggregate ->
-        [ "    -- The snapshot discriminator composes: the spec's state-codec version (bump it",
-          "    -- in the spec's `state-codec version=` clause), keiki's register and",
-          "    -- control-state shape hashes, and this fold fingerprint derived from the",
-          "    -- spec's transition surface (guards, writes, emits, states, register",
-          "    -- initials, referenced rules). Spec-visible fold changes invalidate old",
-          "    -- snapshots automatically. Version-2 Hole-owned transitions additionally",
-          "    -- compose their explicit hand-owned FoldVersion tokens here; bump the",
-          "    -- corresponding token whenever that Hole behavior changes.",
-          "    , stateCodec = " <> stateCodecExpr aggregate
-        ]
-    | otherwise ->
-        [ "    -- The snapshot discriminator composes: the spec's state-codec version (bump it",
-          "    -- in the spec's `state-codec version=` clause), keiki's register and",
-          "    -- control-state shape hashes, and this fold fingerprint derived from the",
-          "    -- spec's transition surface (guards, writes, emits, states, register",
-          "    -- initials, referenced rules). Spec-visible fold changes invalidate old",
-          "    -- snapshots automatically. Fold changes made ONLY in the hand-owned Holes",
-          "    -- module are invisible here: bump `state-codec version=` manually or old",
-          "    -- snapshots will be served stale.",
-          "    , stateCodec = " <> stateCodecExpr aggregate
-        ]
-
-snapshotFixtureLines :: Agg -> [Text]
-snapshotFixtureLines aggregate = case aSnapshot aggregate of
-  Nothing -> []
-  Just snapshot ->
-    [ lowerFirst (aName aggregate) <> "SnapshotFixture :: (Int, Text)",
-      lowerFirst (aName aggregate) <> "SnapshotFixture = (" <> tshow' (snapCodecVersion snapshot) <> ", " <> tshow (snapShapeHash snapshot) <> ")",
-      ""
-    ]
-
---------------------------------------------------------------------------------
--- Projection module
---------------------------------------------------------------------------------
-
-emitProjection :: Agg -> Text
-emitProjection a = case aProjection a of
-  Nothing -> nl [generatedBanner, "module " <> aGenPrefix a <> ".Projection () where"]
-  Just p ->
-    nl
-      [ "{-# LANGUAGE OverloadedRecordDot #-}",
-        generatedBanner,
-        "module " <> aGenPrefix a <> ".Projection",
-        "  ( " <> lowerFirst (projTable p) <> "Projection",
-        "  , " <> lowerFirst (projTable p) <> "StatusFor",
-        "  ) where",
-        "",
-        "import " <> aGenPrefix a <> ".Domain",
-        "import " <> aHolePrefix a <> ".Holes (apply" <> pascal (projTable p) <> ")",
-        "import Data.Text (Text)",
-        "import Keiro.Projection (InlineProjection (..))",
-        "",
-        "-- The deterministic event->status mapping (hole-kind 3, /mapping/), derived",
-        "-- from the spec's status-map. The read-model SQL that consumes it lives in",
-        "-- the hand-owned Holes module (a DB-coupled hole, delegated to codd).",
-        projectionTableComment a p,
-        lowerFirst (projTable p) <> "StatusFor :: " <> aName a <> "Event -> Maybe Text",
-        lowerFirst (projTable p) <> "StatusFor = \\case",
-        nl (statusArms a p),
-        "",
-        lowerFirst (projTable p) <> "Projection :: InlineProjection " <> aName a <> "Event",
-        lowerFirst (projTable p) <> "Projection =",
-        "  InlineProjection",
-        "    { name = " <> tshow (contextNameToProjName a p),
-        "    , apply = apply" <> pascal (projTable p),
-        "    }"
-      ]
-
-statusArms :: Agg -> ProjectionSpec -> [Text]
-statusArms a p =
-  [ "  " <> rcName e <> " {} -> " <> statusFor e
-  | e <- aEvents a
-  ]
-    ++ ["  _ -> Nothing" | hasWildcard]
-  where
-    pairs = maybe [] mapPairs (projStatusMap p)
-    statusFor e = case lookup (rcName e) pairs of
-      Just value -> "Just " <> tshow value
-      Nothing -> "Nothing"
-    -- A wildcard is only needed if some event is uncovered; otherwise every arm
-    -- is explicit and a wildcard would be redundant (and -Wall would warn).
-    hasWildcard = False
-
-contextNameToProjName :: Agg -> ProjectionSpec -> Text
-contextNameToProjName a p = contextKebab a <> "-" <> projTable p <> "-inline"
-
-contextKebab :: Agg -> Text
-contextKebab = kebabFromPascal . aCtxPascal
-
-projectionReadModel :: Agg -> Maybe ReadModelNode
-projectionReadModel aggregate = do
-  projection <- aProjection aggregate
-  find ((== projTable projection) . rmName) (aReadModels aggregate)
-
-projectionTableComment :: Agg -> ProjectionSpec -> Text
-projectionTableComment aggregate projection = case projectionReadModel aggregate of
-  Nothing ->
-    "-- WARNING: no readmodel node declares '"
-      <> projTable projection
-      <> "'; unqualified SQL depends on search_path."
-  Just readModel ->
-    "-- Qualified table "
-      <> qualifiedTableLiteral readModel
-      <> "; use "
-      <> genPrefixFor (aContext aggregate) (pascal (rmName readModel))
-      <> ".ReadModelTable."
-      <> readModelStem readModel
-      <> "QualifiedTable."
-
---------------------------------------------------------------------------------
--- Holes module (create-if-absent)
---------------------------------------------------------------------------------
-
-emitHoles :: Agg -> Text
-emitHoles aggregate
-  | hasVersion2Ownership aggregate = emitVersion2Holes aggregate
-  | otherwise = emitLegacyHoles aggregate
-
-emitLegacyHoles :: Agg -> Text
-emitLegacyHoles a =
-  nl
-    [ "{-# LANGUAGE BlockArguments #-}",
-      "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE OverloadedRecordDot #-}",
-      "{-# LANGUAGE QualifiedDo #-}",
-      "{-# LANGUAGE TypeApplications #-}",
-      "-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never",
-      "-- overwrites it. Fill the transducer body (and any other holes) against the",
-      "-- generated signatures, then run the harness to confirm behaviour.",
-      "module " <> aHolePrefix a <> ".Holes",
-      "  ( " <> lowerFirst (aName a) <> "Transducer",
-      holeProjectionExport a,
-      holeUpcasterExports a,
-      "  ) where",
-      "",
-      "import " <> aGenPrefix a <> ".Domain",
-      "import Keiki.Builder ((=:))",
-      "import qualified Keiki.Builder as B",
-      "import Keiki.Core (HsPred, RegFile, SymTransducer, lit, (.==), (./=), (.||))",
-      holeUpcasterImports a,
-      holeProjectionImports a,
-      "",
-      "-- HOLE: the transducer body. Reproduce the structure below, replacing each",
-      "-- `-- HOLE` line with the keiki symbolic operators it describes.",
-      lowerFirst (aName a) <> "Transducer",
-      "  :: SymTransducer",
-      "       (HsPred " <> aName a <> "Regs " <> aName a <> "Command)",
-      "       " <> aName a <> "Regs",
-      "       " <> aVertexType a,
-      "       " <> aName a <> "Command",
-      "       " <> aName a <> "Event",
-      lowerFirst (aName a) <> "Transducer =",
-      "  B.buildTransducer " <> initialVertex a <> " initial" <> aName a <> "Regs isTerminal do",
-      nl (concatMap (fromBlock a) (groupBySource a)),
-      " where",
-      "  isTerminal = \\case",
-      nl ["    " <> vertexCtor a (stName s) <> " -> True" | s <- aStates a, stTerminal s],
-      "    _ -> False",
-      holeProjectionStub a,
-      holeUpcasterStubs a
-    ]
-
-emitVersion2Holes :: Agg -> Text
-emitVersion2Holes aggregate =
-  nl $
-    [ "{-# LANGUAGE BlockArguments #-}",
-      "{-# LANGUAGE DataKinds #-}",
-      "{-# LANGUAGE DuplicateRecordFields #-}",
-      "{-# LANGUAGE OverloadedRecordDot #-}",
-      "{-# LANGUAGE QualifiedDo #-}",
-      "{-# LANGUAGE TypeApplications #-}",
-      "-- This is a HAND-OWNED version-2 hook module. keiro-dsl creates it once",
-      "-- and never overwrites it. Generated code owns every transition envelope",
-      "-- and every declared guard/write. This module supplies explicit event-field",
-      "-- mappings and explicitly selected Hole behavior only; fields(Command)",
-      "-- identity mappings are generated directly and have no hook."
-    ]
-      ++ version2HoleModuleDeclaration aggregate
-      ++ [ "",
-           "import " <> aGenPrefix aggregate <> ".Domain",
-           "import Keiki.Builder qualified as B",
-           "import Keiki.Generics (RegFieldsOf)",
-           holeUpcasterImports aggregate,
-           holeProjectionImports aggregate
-         ]
-      ++ ["import Keiki.Core qualified as K" | anyHoleOwned aggregate || anyZeroFieldOutput aggregate]
-      ++ ["import Keiro.Snapshot.Codec (FoldVersion (..))" | anyHoleOwned aggregate]
-      ++ concatMap (uncurry (emitOutputHooks aggregate)) (transitionEntries aggregate)
-      ++ concatMap (uncurry (emitHoleImplementation aggregate)) (transitionEntries aggregate)
-      ++ [holeProjectionStub aggregate, holeUpcasterStubs aggregate]
-
-anyZeroFieldOutput :: Agg -> Bool
-anyZeroFieldOutput aggregate =
-  or
-    [ isHandOwned (outputMappingFor aggregate transitionIndex emitIndex)
-        && null (rcFields (eventForName aggregate eventName))
-    | (transitionIndex, transition) <- transitionEntries aggregate,
-      (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
-    ]
-  where
-    isHandOwned HandOwnedEventOutput {} = True
-    isHandOwned GeneratedCommandIdentity {} = False
-
-version2HoleModuleDeclaration :: Agg -> [Text]
-version2HoleModuleDeclaration aggregate = case version2HoleExports aggregate of
-  [] -> ["module " <> aHolePrefix aggregate <> ".Holes () where"]
-  firstExport : rest ->
-    [ "module " <> aHolePrefix aggregate <> ".Holes",
-      "  ( " <> firstExport
-    ]
-      ++ ["  , " <> value | value <- rest]
-      ++ ["  ) where"]
-
-version2HoleExports :: Agg -> [Text]
-version2HoleExports aggregate =
-  outputExports
-    <> holeExports
-    <> projectionExports
-    <> [functionName | (_, _, functionName) <- upcasterEntries aggregate]
-  where
-    outputExports =
-      [ outputFunctionName transitionIndex transition emitIndex eventName
-      | (transitionIndex, transition) <- transitionEntries aggregate,
-        (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
-        HandOwnedEventOutput {} <- [outputMappingFor aggregate transitionIndex emitIndex]
-      ]
-    holeExports =
-      concat
-        [ [holeFunctionName index transition, holeFoldVersionName index transition]
-        | (index, transition) <- transitionEntries aggregate,
-          tImplementation transition == HoleImplementation
-        ]
-    projectionExports = case aProjection aggregate of
-      Nothing -> []
-      Just projection -> ["apply" <> pascal (projTable projection)]
-
-emitOutputHooks :: Agg -> Int -> Transition -> [Text]
-emitOutputHooks aggregate transitionIndex transition =
-  concat
-    [ emitOutputHook aggregate transitionIndex transition emitIndex (eventForName aggregate eventName)
-    | (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
-      HandOwnedEventOutput {} <- [outputMappingFor aggregate transitionIndex emitIndex]
-    ]
-
-emitOutputHook :: Agg -> Int -> Transition -> Int -> ResolvedCtor -> [Text]
-emitOutputHook aggregate transitionIndex transition emitIndex event =
-  [ "",
-    "-- Hand-owned event-field hook inside the generated transition envelope.",
-    functionName
-      <> " :: "
-      <> payloadProjectionType aggregate transition
-      <> " -> "
-      <> outputType,
-    functionName <> " d = " <> outputValue
-  ]
-  where
-    functionName = outputFunctionName transitionIndex transition emitIndex (rcName event)
-    inputFields = "(" <> commandFieldsType transition <> ")"
-    outputType
-      | null (rcFields event) =
-          "K.OutFields "
-            <> aName aggregate
-            <> "Regs "
-            <> aName aggregate
-            <> "Command "
-            <> inputFields
-            <> " ()"
-      | otherwise =
-          rcName event
-            <> "TermFields "
-            <> aName aggregate
-            <> "Regs "
-            <> aName aggregate
-            <> "Command "
-            <> inputFields
-    outputValue
-      | null (rcFields event) = "B.oNil"
-      | otherwise =
-          rcName event
-            <> "TermFields\n"
-            <> nl (valueRecord [(fieldName, outputFieldValue fieldName fieldType) | (fieldName, fieldType) <- rcFields event])
-    command = commandForTransition aggregate transition
-    outputFieldValue fieldName fieldType
-      | Just commandType <- lookup fieldName (rcFields command),
-        commandType == fieldType =
-          "d." <> fieldName
-      | Just register <- find ((== fieldName) . rrName) (aRegs aggregate),
-        rrType register == fieldType =
-          "B.reg @" <> tshow fieldName
-      | otherwise = "error " <> tshow ("HOLE: fill output field " <> rcName event <> "." <> fieldName)
-    valueRecord fields =
-      [ lead fieldIndex <> fieldName <> " = " <> fieldValue
-      | (fieldIndex, (fieldName, fieldValue)) <- zip [0 :: Int ..] fields
-      ]
-        ++ ["  }"]
-    lead 0 = "  { "
-    lead _ = "  , "
-
-emitHoleImplementation :: Agg -> Int -> Transition -> [Text]
-emitHoleImplementation _ index transition
-  | tImplementation transition /= HoleImplementation = []
-  | otherwise =
-      [ "",
-        "-- HOLE: add the predicate and ordered register updates for this transition.",
-        "-- The generated transducer still owns command matching, mode, emits, and goto.",
-        holeFunctionName index transition <> " _d = B.requireGuard K.PTop",
-        "",
-        "-- Bump this token whenever the Hole predicate or updates change.",
-        holeFoldVersionName index transition <> " :: FoldVersion",
-        holeFoldVersionName index transition <> " = FoldVersion " <> tshow (transitionStem index transition <> "-fold-v1")
-      ]
-
--- | Export, import, and stub the per-event upcaster holes (EP-2 evolution).
-holeUpcasterExports :: Agg -> Text
-holeUpcasterExports a = case upcasterEntries a of
-  [] -> ""
-  es -> nl ["  , " <> fn | (_, _, fn) <- es]
-
-holeUpcasterImports :: Agg -> Text
-holeUpcasterImports a = case upcasterEntries a of
-  [] -> ""
-  _ -> nl ["import Data.Aeson (Value)", "import Data.Text (Text)"]
-
-holeUpcasterStubs :: Agg -> Text
-holeUpcasterStubs a = case upcasterEntries a of
-  [] -> ""
-  es ->
-    nl $
-      concat
-        [ [ "",
-            "-- HOLE upcaster: this hole receives ONLY " <> eventName <> " payloads stored at",
-            "-- aggregate schema version " <> tshow' source <> "; other event kinds pass through the",
-            "-- generated rung dispatch automatically. Bring this payload up one version and decide",
-            "-- the default/derivation for any field added at the new version here.",
-            fn <> " :: Value -> Either Text Value",
-            fn <> " _ = Left \"HOLE: upcaster not implemented\""
-          ]
-        | (source, eventName, fn) <- es
-        ]
-
-holeProjectionExport :: Agg -> Text
-holeProjectionExport a = case aProjection a of
-  Nothing -> "  -- (no projection)"
-  Just p -> "  , apply" <> pascal (projTable p)
-
-holeProjectionImports :: Agg -> Text
-holeProjectionImports aggregate = case projectionReadModel aggregate of
-  Nothing -> ""
-  Just readModel ->
-    "import "
-      <> genPrefixFor (aContext aggregate) (pascal (rmName readModel))
-      <> ".ReadModelTable ("
-      <> readModelStem readModel
-      <> "QualifiedTable)"
-
-holeProjectionStub :: Agg -> Text
-holeProjectionStub a = case aProjection a of
-  Nothing -> ""
-  Just p ->
-    nl
-      ( [ "",
-          "-- HOLE: the read-model SQL for the projection (a DB-coupled hole; the",
-          "-- pure event->status mapping is generated as " <> lowerFirst (projTable p) <> "StatusFor)."
-        ]
-          ++ projectionGuidance
-          ++ [ "apply" <> pascal (projTable p) <> " :: " <> aName a <> "Event -> recorded -> txn ()",
-               "apply" <> pascal (projTable p) <> " _event _recorded = " <> projectionTableUse <> "error \"HOLE: fill " <> projTable p <> " projection apply\""
-             ]
-      )
-    where
-      projectionGuidance = case projectionReadModel a of
-        Nothing ->
-          ["-- WARNING: no readmodel node declares this table's schema; unqualified SQL depends on search_path."]
-        Just readModel ->
-          [ "-- Table: " <> qualifiedTableLiteral readModel <> ". Use " <> readModelStem readModel <> "QualifiedTable; never rely on search_path.",
-            "-- Declared columns:"
-          ]
-            ++ map (("--   " <>) . readModelColumnDoc) (rmColumns readModel)
-      projectionTableUse = case projectionReadModel a of
-        Nothing -> ""
-        Just readModel -> readModelStem readModel <> "QualifiedTable `seq` "
-
--- Group transitions by source state, preserving order, for the B.from blocks.
-groupBySource :: Agg -> [(Text, [Transition])]
-groupBySource a = go [] (transitionsOf a)
-  where
-    go acc [] = reverse acc
-    go acc (t : ts) =
-      let src = tSource t
-          (same, rest) = span ((== src) . tSource) ts
-       in go ((src, t : same) : acc) rest
-
--- We don't keep the original Aggregate around in Agg, so reconstruct
--- transitions from a stored field. (Filled in resolveAgg via aTransitions.)
-transitionsOf :: Agg -> [Transition]
-transitionsOf = aTransitions
-
-fromBlock :: Agg -> (Text, [Transition]) -> [Text]
-fromBlock a (src, ts) =
-  [ "    B.from " <> vertexCtor a src <> " do"
-  ]
-    ++ concatMap (onCmdBlock a) ts
-
-onCmdBlock :: Agg -> Transition -> [Text]
-onCmdBlock a t =
-  [ "      B.onCmd inCtor" <> tCommand t <> " $ \\d -> B.do"
-  ]
-    -- Plan 143: the mode is structural, not hole-owned — a replay-only
-    -- transition lowers to B.replayOnly (keiki ReplayOnly edge).
-    ++ ["        B.replayOnly" | tMode t == TmReplayOnly]
-    ++ maybe [] (\g -> ["        -- HOLE guard: " <> renderGuard g]) (tGuard t)
-    ++ ["        -- HOLE write " <> r <> " := " <> renderGuard e | (r, e) <- tWrites t]
-    ++ ["        -- HOLE emit " <> ev <> " (B.emit wire" <> ev <> " ...)" | ev <- tEmits t]
-    ++ ["        B.goto " <> vertexCtor a (tGoto t)]
-
---------------------------------------------------------------------------------
--- Field categories and shared helpers
---------------------------------------------------------------------------------
-
-data FieldCat
-  = IdCat
-  | EnumCat
-  | MappedStructuralCat !StructuralDecl !ResolvedMappedShape
-  | MappedOpaqueCat !OpaqueDecl
-  | OtherCat
-  deriving stock (Eq, Show)
-
-fieldCat :: Agg -> ResolvedAggregateType -> FieldCat
-fieldCat a ty
-  | AggregateNominal nominal <- ty,
-    IdRepresentation {} <- resolvedNominalRepresentation nominal =
-      IdCat
-  | AggregateNominal nominal <- ty,
-    EnumRepresentation {} <- resolvedNominalRepresentation nominal =
-      EnumCat
-  | Just (ResolvedStructural declaration shape) <- mappedDeclFor a ty = MappedStructuralCat declaration shape
-  | Just (ResolvedOpaque declaration) <- mappedDeclFor a ty = MappedOpaqueCat declaration
-  | otherwise = OtherCat
-
--- | The first constructor of a declared enum, used to build sample values.
-firstEnumCtor :: Agg -> Text -> Maybe Text
-firstEnumCtor a ty =
-  case [c | e <- aEnums a, enumName e == ty, (c, _) <- take 1 (enumCtors e)] of
-    (c : _) -> Just c
-    [] -> Nothing
-
-vertexCtor :: Agg -> Text -> Text
-vertexCtor a s = aName a <> s
-
-initialVertex :: Agg -> Text
-initialVertex a = case aStates a of
-  (s : _) -> vertexCtor a (stName s)
-  [] -> aName a <> "Init"
-
-generatedBanner :: Text
-generatedBanner = "-- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec."
+-- service and output module-namespace root), it emits deterministic
+-- @-- \@generated@ modules plus create-if-absent Hole modules for the behavior
+-- that remains explicitly hand-owned.
+--
+-- The load-bearing invariant of this module is the __firewall__: only a
+-- generated aggregate @Transducer.hs@ may contain Keiki symbolic operators
+-- (@./=@, @.==@, @.||@, @lit@, @B.slot@, @B.requireGuard@). Hand-owned Hole
+-- modules remain the other intentional construction boundary. A generated-text
+-- scan enforces the exception by path.
+--
+-- Version-2 generated-owned transition bodies are rendered authoritatively in
+-- that transducer. Explicit Hole-owned transitions keep only predicate/update
+-- ownership; the generated command/event/target envelope remains authoritative.
+-- Read-model SQL (the projection @apply@) is still a DB-coupled Hole delegated
+-- to @codd@/the agent; the @Generated@ Projection module emits deterministic
+-- @InlineProjection@ wiring and the pure event→status mapping. The decode emitted
+-- here is /strict/ (every field required); lenient\/optional decode is EP-4's
+-- concern.
+module Keiro.Dsl.Scaffold
+  ( ScaffoldModule (..),
+    ModuleKind (..),
+    Context (..),
+    Placement (..),
+    defaultContext,
+    genPrefixFor,
+    holePrefixFor,
+    generatedNominalModule,
+    NominalUseSite (..),
+    NominalGenerationOwner (..),
+    planNominalGeneration,
+    planNominalGenerationForService,
+    generatedNominalsInTypes,
+    generatedNominalTypeImports,
+    generatedNominalTypeImportsForService,
+    generatedIdSampleHaskell,
+    scaffoldReplayAudit,
+    scaffoldStructural,
+    scaffoldStructuralForService,
+    scaffoldStructuralOwners,
+    scaffoldStructuralOwnersForService,
+    codecComparisonModule,
+    codecComparisonBanner,
+    bindingSkeletonModules,
+    bindingSkeletonOwners,
+    scaffoldAggregateForService,
+    scaffoldAggregate,
+    obsoleteGeneratedOutputHooks,
+    scaffoldProcess,
+    scaffoldRouter,
+    scaffoldContract,
+    scaffoldContractForService,
+    scaffoldIntake,
+    scaffoldPublisher,
+    scaffoldWorkqueue,
+    scaffoldReadModel,
+    scaffoldRefusals,
+    windowSeconds,
+
+    -- * Firewall self-check (M3)
+    FirewallSurface (..),
+    firewallSurface,
+    firewallBreaches,
+
+    -- * Internal resolution, shared with "Keiro.Dsl.Harness"
+    Agg (..),
+    aggregateCheckedService,
+    ResolvedRegister (..),
+    ResolvedCtor (..),
+    StructuralProjection (..),
+    resolveAggForService,
+    resolveAgg,
+    nominalEqualityUsedInGeneratedExpressions,
+    projectionSpecs,
+    resolveProjectionModules,
+    nominalProjectionModule,
+    codecMappedDeclarations,
+    FieldCat (..),
+    fieldCat,
+    vertexCtor,
+    initialVertex,
+    firstEnumCtor,
+    lowerFirst,
+    pascal,
+    pascalFromKebab,
+    generatedBanner,
+    generatedBannerFor,
+    isGeneratedBannerLine,
+    stampGeneratedModule,
+    stampGeneratedModules,
+  )
+where
+
+import Data.Char (isAlpha, isAlphaNum, isDigit, isUpper, toLower, toUpper)
+import Data.List (find, findIndex, groupBy, isSuffixOf, nub, sort, sortOn)
+import Data.List.NonEmpty (NonEmpty)
+import Data.List.NonEmpty qualified as NE
+import Data.Map.Strict qualified as Map
+import Data.Maybe (fromMaybe, isJust, mapMaybe)
+import Data.Set qualified as Set
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Version (showVersion)
+import Keiro.Dsl.AggregateType
+import Keiro.Dsl.BehaviorCoverage qualified as Behavior
+import Keiro.Dsl.CodecCompare (BranchArm (..), BranchField (..), BranchSchema (..))
+import Keiro.Dsl.EventOutput
+import Keiro.Dsl.ExplainBindings (BindingObligation (..), BindingObligationKind (..), bindingObligations)
+import Keiro.Dsl.Expression
+import Keiro.Dsl.FoldFingerprint (aggregateFoldFingerprintForService, renderFoldSurfaceError)
+import Keiro.Dsl.Grammar
+import Keiro.Dsl.IdDomain (IdDomainContract, contractIdDomainContractFor, idDomainContractFor, idDomainPrefix, idDomainSampleText)
+import Keiro.Dsl.LanguageVersion (SourceLanguage (LegacyUnversioned), languageVersionText)
+import Keiro.Dsl.NominalType
+import Keiro.Dsl.PrettyPrint (renderExpr)
+import Keiro.Dsl.ReadModelShape (fnv1a64, registryNameFor, subscriptionNameFor)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveContractLanguageVersion, effectiveLanguageContract, legacyCheckedService)
+import Keiro.Dsl.TypeGraph
+import Keiro.Dsl.Validate (sagaCategoryError)
+import Paths_keiro_dsl qualified as Package
+import Text.Read (readMaybe)
+
+-- | One emitted module: its on-disk path (relative to the scaffold @--out@
+-- directory), its full text, and whether it is overwritten every run
+-- ('Generated') or written only when absent ('HoleStub').
+data ScaffoldModule = ScaffoldModule
+  { modulePath :: !FilePath,
+    moduleText :: !Text,
+    kind :: !ModuleKind,
+    origin :: !Text
+  }
+  deriving stock (Eq, Show)
+
+data ModuleKind
+  = -- | @-- \@generated@; overwritten on every scaffold.
+    Generated
+  | -- | Hand-owned; created only when absent, never overwritten.
+    HoleStub
+  deriving stock (Eq, Show)
+
+-- | The threading context: the spec's @context@ name, the chosen output
+-- module-namespace root, and the placement style. Extended additively (never
+-- re-shaped) by later verticals.
+data Context = Context
+  { contextName :: !Text,
+    -- | @""@ means no namespace prefix (the historical default).
+    moduleRoot :: !Text,
+    -- | 'GeneratedPrefix' is the historical default.
+    placement :: !Placement
+  }
+  deriving stock (Eq, Show)
+
+-- | One aggregate-level reason a generated nominal declaration must be visible.
+-- The declaration itself is context-owned; these use sites determine the
+-- aggregate modules that import it.
+data NominalUseSite = NominalUseSite
+  { nominalUseAggregate :: !Name,
+    nominalUseKind :: !AggregateUseSite
+  }
+  deriving stock (Eq, Ord, Show)
+
+-- | The checked generation owner for one unbound ID or enum. Every owner in a
+-- service points at the same context-level module, while retaining its source
+-- location through 'ResolvedNominalType' and all aggregate use sites explicitly.
+data NominalGenerationOwner = NominalGenerationOwner
+  { nominalDeclaration :: !ResolvedNominalType,
+    nominalModule :: !Text,
+    nominalUseSites :: !(Set.Set NominalUseSite),
+    nominalEqualityUsed :: !Bool
+  }
+  deriving stock (Eq, Show)
+
+-- | A context with today's default placement ('GeneratedPrefix', no root prefix)
+-- for the given @context@ name. Callers that do not care about placement (the
+-- @parse@ path, tests) build their context with this.
+defaultContext :: Text -> Context
+defaultContext name = Context {contextName = name, moduleRoot = "", placement = GeneratedPrefix}
+
+-- | The generated-layer namespace for a node, honouring the root prefix and the
+-- placement style. The 'Text' argument is the already-pascalised node name (e.g.
+-- @Reservation@, @HospitalSurge@). For 'GeneratedPrefix' this is
+-- @\<root\>.Generated.\<Ctx\>.\<Node\>@ (identical to the historical layout); for
+-- 'CollocatedLeaf' it is @\<root\>.\<Ctx\>.\<Node\>.Generated@.
+genPrefixFor :: Context -> Text -> Text
+genPrefixFor ctx node = case placement ctx of
+  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> "." <> node
+  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> "." <> node <> ".Generated"
+
+-- | The hand-owned (hole) namespace for a node: @\<root\>.\<Ctx\>.\<Node\>@ —
+-- the same for both placement styles (holes always sit beside the domain).
+holePrefixFor :: Context -> Text -> Text
+holePrefixFor ctx node = rootPrefix ctx <> ctxPascalOf ctx <> "." <> node
+
+-- | The one context-level Haskell owner for generated IDs and enums.
+generatedNominalModule :: Context -> Text
+generatedNominalModule ctx = case placement ctx of
+  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Nominals"
+  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.Nominals"
+
+-- | The root namespace prefix, dot-terminated, or @""@ when no root is set.
+rootPrefix :: Context -> Text
+rootPrefix ctx = case moduleRoot ctx of r | T.null r -> ""; r -> r <> "."
+
+-- | The context name in PascalCase, e.g. @hospital-capacity@ -> @HospitalCapacity@.
+ctxPascalOf :: Context -> Text
+ctxPascalOf = pascalFromKebab . contextName
+
+--------------------------------------------------------------------------------
+-- Firewall self-check (M3)
+--------------------------------------------------------------------------------
+
+-- | The canonical keiki surface forbidden in generated modules. Symbolic
+-- operators are matched as maximal Haskell symbol tokens, identifiers as complete
+-- tokens, qualifiers by their leading module alias, and imports structurally.
+data FirewallSurface = FirewallSurface
+  { forbiddenSymbolic :: ![Text],
+    forbiddenIdents :: ![Text],
+    forbiddenQualifiers :: ![Text],
+    forbiddenImports :: ![Text],
+    restrictedImports :: ![(Text, [Text])]
+  }
+  deriving stock (Eq, Show)
+
+firewallSurface :: FirewallSurface
+firewallSurface =
+  FirewallSurface
+    { forbiddenSymbolic = [".==", "./=", ".<", ".<=", ".>", ".>=", ".&&", ".||", ".+", ".-", ".*", "=:", "*:"],
+      forbiddenIdents = ["lit", "pnot", "tadd", "tsub", "tmul"],
+      forbiddenQualifiers = ["B"],
+      forbiddenImports = ["Keiki.Builder", "Keiki.Operators", "Keiki.Symbolic"],
+      -- Generated aggregate modules use the first two names; generated
+      -- harnesses validate, step, and replay filled holes register by register.
+      restrictedImports =
+        [ ( "Keiki.Core",
+            [ "RegFile",
+              "HsPred",
+              "FieldProjection",
+              "ExactFieldProjection",
+              "FieldWitness",
+              "fieldWitness",
+              "exactFieldWitness",
+              "fieldWitnessAgrees",
+              "applyEventsEither",
+              "defaultValidationOptions",
+              "step",
+              "validateTransducer",
+              "EdgeMode",
+              "EdgeRef",
+              "StepSuccess",
+              "StepFailure",
+              "ReplayEventSpan",
+              "ReplayAttribution",
+              "ReplaySuccess",
+              "applyEventsDetailedEither",
+              "stepDetailedEither",
+              "!"
+            ]
+          )
+        ]
+    }
+
+-- | Scan generated modules for firewall breaches, returning every offending
+-- @(module path, token, 1-based line number)@. Only modules whose 'kind' is
+-- 'Generated' are scanned. Strings and comments are skipped, symbol runs use
+-- maximal munch, and keiki imports are checked independently of token spelling.
+firewallBreaches :: [ScaffoldModule] -> [(FilePath, Text, Int)]
+firewallBreaches mods =
+  [ (modulePath m, breach, n)
+  | m <- mods,
+    kind m == Generated,
+    not (authoritativeScalarModule (modulePath m)),
+    (n, line) <- zip [1 ..] (T.lines (moduleText m)),
+    breach <- lineBreaches line
+  ]
+
+-- The version-2 aggregate transducer is the narrow, intentional exception to
+-- the generated symbolic-operator firewall: it is precisely the generated
+-- authority that constructs Keiki terms. Every other generated module remains
+-- subject to the original firewall.
+authoritativeScalarModule :: FilePath -> Bool
+authoritativeScalarModule path = "/Transducer.hs" `isSuffixOf` path
+
+lineBreaches :: Text -> [Text]
+lineBreaches line = case importModule line of
+  Just _ -> importBreaches line
+  Nothing -> tokenBreaches (codeTokens line)
+  where
+    tokenBreaches = mapMaybe breachFor
+    breachFor (IdentToken ident)
+      | ident `elem` forbiddenIdents firewallSurface = Just ident
+    breachFor (QualifiedToken qualifier)
+      | qualifier `elem` forbiddenQualifiers firewallSurface = Just (qualifier <> ".*")
+    breachFor (SymbolToken symbol)
+      | symbol `elem` forbiddenSymbolic firewallSurface = Just symbol
+    breachFor _ = Nothing
+
+data CodeToken = IdentToken !Text | QualifiedToken !Text | SymbolToken !Text
+
+codeTokens :: Text -> [CodeToken]
+codeTokens = go . T.unpack
+  where
+    go [] = []
+    go ('-' : '-' : _) = []
+    go ('"' : rest) = go (dropString rest)
+    go ('\'' : rest) = go (dropChar rest)
+    go (c : rest)
+      | isIdentStart c =
+          let (identTail, afterIdent) = span isIdentContinue rest
+              ident = T.pack (c : identTail)
+           in case afterIdent of
+                '.' : next : more
+                  | isUpper c && isIdentStart next ->
+                      let (_member, afterMember) = span isIdentContinue more
+                       in QualifiedToken ident : go afterMember
+                _ -> IdentToken ident : go afterIdent
+      | isSymbolChar c =
+          let (symbolTail, afterSymbol) = span isSymbolChar rest
+           in SymbolToken (T.pack (c : symbolTail)) : go afterSymbol
+      | otherwise = go rest
+    isIdentStart c = isAlpha c || c == '_'
+    isIdentContinue c = isAlphaNum c || c == '_' || c == '\''
+    isSymbolChar c = c `elem` ("!#$%&*+./<=>?@\\^|-~:" :: String)
+    dropString [] = []
+    dropString ('\\' : _escaped : rest) = dropString rest
+    dropString ('"' : rest) = rest
+    dropString (_ : rest) = dropString rest
+    dropChar [] = []
+    dropChar ('\\' : _escaped : rest) = dropChar rest
+    dropChar ('\'' : rest) = rest
+    dropChar (_ : rest) = dropChar rest
+
+importBreaches :: Text -> [Text]
+importBreaches line = case importModule line of
+  Nothing -> []
+  Just imported
+    | imported `elem` forbiddenImports firewallSurface -> ["import:" <> imported]
+    | Just allowed <- lookup imported (restrictedImports firewallSurface),
+      not (hasAllowedExplicitImportList allowed line) ->
+        ["import:" <> imported]
+    | otherwise -> []
+
+importModule :: Text -> Maybe Text
+importModule line = case T.words (T.strip line) of
+  "import" : rest -> find (T.isPrefixOf "Keiki.") rest
+  _ -> Nothing
+
+hasAllowedExplicitImportList :: [Text] -> Text -> Bool
+hasAllowedExplicitImportList allowed line =
+  case (T.breakOn "(" line, T.breakOnEnd ")" line) of
+    ((_, open), (close, _))
+      | not (T.null open) && not (T.null close) ->
+          let inside = T.takeWhile (/= ')') (T.drop 1 open)
+              names = filter (not . T.null) (T.split (not . isAlphaNum) inside)
+           in all (`elem` allowed) names
+    _ -> False
+
+--------------------------------------------------------------------------------
+-- Derived naming
+--------------------------------------------------------------------------------
+
+-- | Resolved, denormalized view of an aggregate used by every emitter.
+data Agg = Agg
+  { aContext :: !Context,
+    aLanguageContract :: !EffectiveLanguageContract,
+    aSpec :: !Spec,
+    aAggregate :: !Aggregate,
+    aCtxPascal :: !Text,
+    aName :: !Text,
+    aLoc :: !Loc,
+    aVertexType :: !Text,
+    aIds :: ![IdDecl],
+    aEnums :: ![EnumDecl],
+    aRegs :: ![ResolvedRegister],
+    aStates :: ![StateDecl],
+    aCommands :: ![ResolvedCtor],
+    aEvents :: ![ResolvedCtor],
+    -- | Generated IDs and enums used by this aggregate, in stable name order.
+    aGeneratedNominals :: ![ResolvedNominalType],
+    aTransitions :: ![Transition],
+    aOutputMappings :: !(Map.Map (Int, Int) EventOutputMapping),
+    aWire :: !WireSpec,
+    aProjection :: !(Maybe ProjectionSpec),
+    aSnapshot :: !(Maybe SnapshotSpec),
+    aFoldFingerprint :: !Text,
+    aReadModels :: ![ReadModelNode],
+    aTypeGraph :: !(Maybe TypeGraph),
+    aSymbols :: !AggregateSymbols,
+    -- | e.g. @Generated.HospitalCapacity.Reservation@
+    aGenPrefix :: !Text,
+    -- | e.g. @HospitalCapacity.Reservation@
+    aHolePrefix :: !Text
+  }
+
+aggregateCheckedService :: Agg -> CheckedService
+aggregateCheckedService aggregate =
+  CheckedService
+    { checkedLanguageContract = aLanguageContract aggregate,
+      checkedSpec = aSpec aggregate
+    }
+
+data ResolvedRegister = ResolvedRegister
+  { rrName :: !Name,
+    rrType :: !ResolvedAggregateType,
+    rrInitial :: !ResolvedRegisterInitial,
+    rrLoc :: !Loc
+  }
+  deriving stock (Eq, Show)
+
+-- | A command or event constructor with its fully-resolved field types.
+data ResolvedCtor = ResolvedCtor
+  { rcName :: !Text,
+    -- | (field name, canonical aggregate type)
+    rcFields :: ![(Text, ResolvedAggregateType)],
+    -- | EP-2: schema version (1 for commands and unversioned events).
+    rcVersion :: !Int,
+    -- | EP-2: the source version this event migrates from (the upcaster step).
+    rcUpcastFrom :: !(Maybe Int)
+  }
+
+defaultWire :: WireSpec
+defaultWire = WireSpec {wireKind = "ctorName", wireFields = "camelCase", wireSchemaVersion = 1}
+
+resolveAgg :: Context -> Spec -> Aggregate -> Agg
+resolveAgg ctx spec = resolveAggForService ctx (legacyCheckedService spec)
+
+-- | Resolve one aggregate under the service's effective runtime semantics.
+resolveAggForService :: Context -> CheckedService -> Aggregate -> Agg
+resolveAggForService ctx service agg =
+  Agg
+    { aContext = ctx,
+      aLanguageContract = checkedLanguageContract service,
+      aSpec = spec,
+      aAggregate = agg,
+      aCtxPascal = ctxPascal,
+      aName = nm,
+      aLoc = aggLoc agg,
+      aVertexType = vertexType,
+      aIds = specIds spec,
+      aEnums = specEnums spec,
+      aRegs = map resolveRegister (aggRegs agg),
+      aStates = aggStates agg,
+      aCommands = map resolveCommand (aggCommands agg),
+      aEvents = map resolveEvent (aggEvents agg),
+      aGeneratedNominals = generatedNominalsInTypes aggregateResolvedTypes,
+      aTransitions = aggTransitions agg,
+      aOutputMappings =
+        Map.fromList
+          [ ( (transitionIndex, emitIndex),
+              orDieOutput (eventOutputMapping spec agg transition emitIndex eventName)
+            )
+          | (transitionIndex, transition) <- zip [1 ..] (aggTransitions agg),
+            (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
+          ],
+      aWire = fromMaybe defaultWire (aggWire agg),
+      aProjection = aggProjection agg,
+      aSnapshot = aggSnapshot agg,
+      aFoldFingerprint = either (error . T.unpack . renderFoldSurfaceError) id (aggregateFoldFingerprintForService service agg),
+      aReadModels = [readModel | NReadModel readModel <- specNodes spec],
+      aTypeGraph = either (const Nothing) Just (resolveTypeGraph spec),
+      aSymbols = symbols,
+      aGenPrefix = genPrefixFor ctx nm,
+      aHolePrefix = holePrefixFor ctx nm
+    }
+  where
+    spec = checkedSpec service
+    nm = aggName agg
+    symbols = aggregateSymbols spec
+    ctxPascal = pascalFromKebab (contextName ctx)
+    vertexType = nm <> "Vertex"
+    commandFieldTypes = [(cmdName c, cmdFields c) | c <- aggCommands agg]
+    resolveCommand c = (mkCtor CommandFieldUse (cmdName c) (cmdFields c)) {rcVersion = 1, rcUpcastFrom = Nothing}
+    resolveEvent e =
+      (mkCtor EventFieldUse (evName e) (eventFields e))
+        { rcVersion = evVersion e,
+          rcUpcastFrom = fst <$> evUpcastFrom e
+        }
+      where
+        eventFields ev = case evBody ev of
+          EventFields fs -> fs
+          EventFromCommand cn -> fromMaybe [] (lookup cn commandFieldTypes)
+    mkCtor useSite cn fs =
+      ResolvedCtor
+        { rcName = cn,
+          rcFields = map (\field -> (aggregateFieldName field, orDie (inferAggregateFieldType symbols agg useSite field))) fs,
+          rcVersion = 1,
+          rcUpcastFrom = Nothing
+        }
+    aggregateResolvedTypes =
+      map rrType (map resolveRegister (aggRegs agg))
+        <> map snd (concatMap rcFields (map resolveCommand (aggCommands agg)))
+        <> map snd (concatMap rcFields (map resolveEvent (aggEvents agg)))
+    resolveRegister register =
+      let resolvedType = orDie (resolveAggregateType symbols (regLoc register) RegisterUse (regType register))
+          resolvedInitial = orDie (resolveRegisterInitial symbols (regLoc register) resolvedType (regInitial register))
+       in ResolvedRegister
+            { rrName = regName register,
+              rrType = resolvedType,
+              rrInitial = resolvedInitial,
+              rrLoc = regLoc register
+            }
+    orDie = either (error . ("validated aggregate resolution failed: " <>) . show) id
+    orDieOutput = either (error . ("validated aggregate output resolution failed: " <>) . show) id
+
+-- | Keep only generated nominal IDs/enums from a resolved aggregate type list.
+-- The map both deduplicates and makes declaration/import order independent of
+-- member and field order.
+generatedNominalsInTypes :: [ResolvedAggregateType] -> [ResolvedNominalType]
+generatedNominalsInTypes resolvedTypes =
+  Map.elems . Map.fromList $
+    [ (resolvedNominalName nominal, nominal)
+    | AggregateNominal nominal <- resolvedTypes,
+      GeneratedNominal <- [resolvedNominalOwnership nominal]
+    ]
+
+-- | Plan declaration ownership and use closure without emitting text. Parsing
+-- and validation already reject malformed declarations; retaining the checked
+-- error here keeps this function total for direct library callers.
+planNominalGeneration :: Context -> Spec -> Either (NonEmpty NominalTypeError) [NominalGenerationOwner]
+planNominalGeneration ctx spec = planNominalGenerationForService ctx (legacyCheckedService spec)
+
+planNominalGenerationForService :: Context -> CheckedService -> Either (NonEmpty NominalTypeError) [NominalGenerationOwner]
+planNominalGenerationForService ctx service = do
+  registry <- resolveNominalTypes spec
+  let aggregates = [resolveAggForService ctx service aggregate | NAggregate aggregate <- specNodes spec]
+      generated =
+        [ nominal
+        | nominal <- Map.elems (nominalTypes registry),
+          GeneratedNominal <- [resolvedNominalOwnership nominal]
+        ]
+  pure
+    [ NominalGenerationOwner
+        { nominalDeclaration = nominal,
+          nominalModule = generatedNominalModule ctx,
+          nominalUseSites = Set.fromList (concatMap (usesFor nominal) aggregates),
+          nominalEqualityUsed = any (nominalEqualityUsedInGeneratedExpressions nominal) aggregates
+        }
+    | nominal <- generated
+    ]
+  where
+    spec = checkedSpec service
+    usesFor nominal aggregate =
+      [ NominalUseSite (aName aggregate) useKind
+      | useKind <- aggregateUseKinds nominal aggregate
+      ]
+
+nominalEqualityUsedInGeneratedExpressions :: ResolvedNominalType -> Agg -> Bool
+nominalEqualityUsedInGeneratedExpressions nominal aggregate =
+  any (anyTypedExpression comparesNominal) (resolvedGeneratedExpressions aggregate)
+  where
+    comparesNominal expression = case typedScalarNode expression of
+      TypedEqual left _ -> typedScalarType left == AggregateNominal nominal
+      TypedNotEqual left _ -> typedScalarType left == AggregateNominal nominal
+      _ -> False
+
+aggregateUseKinds :: ResolvedNominalType -> Agg -> [AggregateUseSite]
+aggregateUseKinds nominal aggregate =
+  nub $
+    [RegisterUse | nominal `elem` registerNominals]
+      <> [CommandFieldUse | nominal `elem` commandNominals]
+      <> [EventFieldUse | nominal `elem` eventNominals]
+      <> [CodecUse | nominal `elem` eventNominals]
+      <> [SnapshotUse | hasSnapshot aggregate && nominal `elem` registerNominals]
+      <> [HarnessSampleUse | nominal `elem` commandNominals || nominal `elem` eventNominals]
+      <> [HaskellLoweringUse | nominal `elem` aGeneratedNominals aggregate]
+  where
+    registerNominals = generatedNominalsInTypes (map rrType (aRegs aggregate))
+    commandNominals = generatedNominalsInTypes (map snd (concatMap rcFields (aCommands aggregate)))
+    eventNominals = generatedNominalsInTypes (map snd (concatMap rcFields (aEvents aggregate)))
+
+--------------------------------------------------------------------------------
+-- Entry point
+--------------------------------------------------------------------------------
+
+-- | Emit the context-level private structural stratum. Shape modules contain
+-- only generated wire representations. The projection facade contains only
+-- schema-derived Keiki field witnesses; neither layer owns consumer behavior.
+scaffoldStructural :: Context -> Spec -> [ScaffoldModule]
+scaffoldStructural ctx spec = scaffoldStructuralForService ctx (legacyCheckedService spec)
+
+scaffoldStructuralForService :: Context -> CheckedService -> [ScaffoldModule]
+scaffoldStructuralForService ctx service = map fst (scaffoldStructuralOwnersForService ctx service)
+
+-- | 'scaffoldStructural' paired with the mapped declarations each module was
+-- emitted for. A shape module names exactly one declaration; a binding skeleton
+-- names every declaration whose obligations it carries (several declarations may
+-- share one leaf binding module); the projection facade names __none__, because it
+-- is emitted once for the whole context from the complete resolved graph.
+--
+-- This is the attribution seam whole-workspace scaffolding needs: a workspace
+-- emits from one merged spec, and this list says which declaration — and therefore
+-- which member file — produced each structural module, without parsing the
+-- human-readable 'origin' string.
+scaffoldStructuralOwners :: Context -> Spec -> [(ScaffoldModule, [Name])]
+scaffoldStructuralOwners ctx spec = scaffoldStructuralOwnersForService ctx (legacyCheckedService spec)
+
+scaffoldStructuralOwnersForService :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
+scaffoldStructuralOwnersForService ctx service = case resolveTypeGraph (checkedSpec service) of
+  Left _ -> []
+  Right graph ->
+    [(shapeModule ctx graph entry, [sdName (fst entry)]) | entry <- structural]
+      <> projectionModules
+      <> generatedNominalOwners ctx service
+      <> nominalRepresentationOwners ctx spec
+      <> nominalProjectionOwners ctx service
+      <> bindingSkeletonOwners ctx spec graph
+    where
+      structural =
+        [ (declaration, shape)
+        | ResolvedStructural declaration shape <- Map.elems (tgDeclarations graph)
+        ]
+      projectionModules =
+        [ ( ScaffoldModule
+              { modulePath = T.unpack (T.replace "." "/" (structuralProjectionModule ctx) <> ".hs"),
+                moduleText = emitStructuralProjections ctx graph,
+                kind = Generated,
+                origin = "context " <> specContext spec <> " mapped structural facade"
+              },
+            []
+          )
+        | not (null (projectionSpecs graph))
+        ]
+      spec = checkedSpec service
+
+-- | Plan one opt-in, non-production historical-codec comparison module.
+--
+-- The module is intentionally absent from 'scaffoldStructural' and therefore
+-- from production manifests and scaffold records. It must be requested by name
+-- and is compiled only by consumer-owned test/tool components.
+codecComparisonModule :: Context -> Spec -> Name -> Either Text ScaffoldModule
+codecComparisonModule ctx spec requestedName = do
+  graph <- either (Left . ("mapped type graph did not resolve: " <>) . T.pack . show) Right (resolveTypeGraph spec)
+  (declaration, shape) <- case Map.lookup (MappedKey requestedName) (tgDeclarations graph) of
+    Nothing -> Left ("codec comparison target is not a mapped declaration: " <> requestedName)
+    Just (ResolvedOpaque _) ->
+      Left
+        ( "codec comparison target "
+            <> requestedName
+            <> " is opaque; finite evidence must never upgrade an opaque declaration to a structural claim"
+        )
+    Just (ResolvedStructural declaration shape) -> Right (declaration, shape)
+  owner <- case sortOn aggName (comparisonOwners declaration) of
+    [] ->
+      Left
+        ( "codec comparison target "
+            <> requestedName
+            <> " is not reachable from a persisted private event payload"
+        )
+    aggregate : _ -> Right aggregate
+  let moduleName = structuralPrefix ctx <> ".CodecCompare." <> requestedName
+  pure
+    ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
+        moduleText = emitCodecComparison ctx moduleName graph declaration shape owner,
+        kind = Generated,
+        origin = "non-production codec comparison " <> requestedName
+      }
+  where
+    comparisonOwners declaration =
+      [ aggregate
+      | NAggregate aggregate <- specNodes spec,
+        let resolved = resolveAgg ctx spec aggregate,
+        any ((== sdName declaration) . mappedName) (codecMappedDeclarations resolved)
+      ]
+      where
+        mappedName (ResolvedStructural structural _) = sdName structural
+        mappedName (ResolvedOpaque opaque) = odName opaque
+
+codecComparisonBanner :: Text
+codecComparisonBanner =
+  "-- @generated by keiro-dsl codec comparison; non-production migration evidence; do not edit."
+
+emitCodecComparison :: Context -> Text -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Aggregate -> Text
+emitCodecComparison ctx moduleName graph declaration shape owner =
+  nl
+    [ "",
+      codecComparisonBanner,
+      "-- This module compares historical and generated codecs in consumer-owned tests only.",
+      "-- It is never a runtime fallback and never changes the generated codec's authority.",
+      "module " <> moduleName <> " (compareWithHistorical) where",
+      "",
+      "import Control.Monad (filterM)",
+      "import Data.Aeson (Value)",
+      "import Data.Aeson qualified as Aeson",
+      "import Data.List (sort)",
+      "import Data.List.NonEmpty qualified as NonEmpty",
+      "import Data.Text (Text)",
+      "import Data.Text qualified",
+      "import " <> codecModule <> " qualified as GeneratedCodec",
+      "import Keiro.Codec.Structural (FixtureCases (..))",
+      "import Keiro.Dsl.CodecCompare",
+      "import Keiro.Dsl.TypeGraph (BindingVersion (..), CanonicalTypeId (..), QualifiedValueName (..))",
+      "import System.Directory (doesFileExist, listDirectory)",
+      "import System.FilePath (takeExtension, (</>))",
+      "",
+      "import " <> fixtureModule <> " qualified as ConsumerFixtures",
+      "import " <> hsModule (sdHaskell declaration) <> " qualified as ConsumerDomain",
+      "",
+      "compareWithHistorical :: HistoricalCodec " <> domainType <> " -> FilePath -> IO CompareReport",
+      "compareWithHistorical historicalCodec goldenDirectory = do",
+      "  names <- sort . filter ((== \".json\") . takeExtension) <$> listDirectory goldenDirectory",
+      "  files <- filterM doesFileExist [goldenDirectory </> name | name <- names]",
+      "  loaded <- traverse (loadGolden historicalCodec) files",
+      "  let inputIssues = [issue | Left issue <- loaded]",
+      "      entries = [entry | Right entry <- loaded]",
+      "      typedCases = NonEmpty.toList (fixtureCases ConsumerFixtures." <> fixtureSymbol <> ")",
+      "      encodeObservations =",
+      "        [ EncodeObservation label (hcEncode historicalCodec value) (GeneratedCodec.encode" <> name <> "Mapped value)",
+      "        | (label, value) <- typedCases",
+      "        ]",
+      "      decodeObservations = [observation | (observation, _) <- entries]",
+      "      typedObserved =",
+      "        concat",
+      "          [ observedBranchesFor FromBinding branchSchema (GeneratedCodec.encode" <> name <> "Mapped value)",
+      "          | (_, value) <- typedCases",
+      "          ]",
+      "      historicalObserved =",
+      "        concat [observedBranchesFor HistoricalGolden branchSchema value | (_, values) <- entries, value <- values]",
+      "      declared = declaredBranchesFor FromBinding branchSchema <> declaredBranchesFor HistoricalGolden branchSchema",
+      "      provenance =",
+      "        CompareProvenance",
+      "          { cpHistoricalCodecIdentity = hcIdentity historicalCodec",
+      "          , cpHistoricalCodecVersion = hcVersion historicalCodec",
+      "          , cpCanonicalType = CanonicalTypeId " <> tshow (unCanonicalTypeId (sdCanonical declaration)),
+      "          , cpBindingSymbol = QualifiedValueName " <> tshow (unQualifiedValueName (sdBinding declaration)),
+      "          , cpBindingVersion = BindingVersion " <> tshow (unBindingVersion (sdBindingVersion declaration)),
+      "          , cpWireFingerprint = " <> tshow (wireFingerprint graph name),
+      "          }",
+      "  pure (compareReport provenance inputIssues (encodeObservations <> decodeObservations) declared (typedObserved <> historicalObserved))",
+      "",
+      "loadGolden :: HistoricalCodec " <> domainType <> " -> FilePath -> IO (Either CompareInputIssue (CompareObservation, [Value]))",
+      "loadGolden historicalCodec path = do",
+      "  decoded <- Aeson.eitherDecodeFileStrict path",
+      "  pure $ case decoded of",
+      "    Left reason -> Left (HistoricalGoldenUnreadable path (fromString reason))",
+      "    Right inputValue ->",
+      "      let historicalDecoded = hcDecode historicalCodec inputValue",
+      "          historicalOutcome = normalizeDecode historicalDecoded",
+      "          generatedOutcome = normalizeDecode (GeneratedCodec.decode" <> name <> "Mapped inputValue)",
+      "          observation = DecodeObservation path inputValue historicalOutcome generatedOutcome",
+      "          coveredValues = case historicalDecoded of",
+      "            Right value -> [inputValue, GeneratedCodec.encode" <> name <> "Mapped value]",
+      "            Left _ -> []",
+      "       in Right (observation, coveredValues)",
+      "",
+      "normalizeDecode :: Either Text " <> domainType <> " -> DecodeOutcome",
+      "normalizeDecode = either DecodeFailed (DecodedShape . GeneratedCodec.encode" <> name <> "Mapped)",
+      "",
+      "fromString :: String -> Text",
+      "fromString = Data.Text.pack",
+      "",
+      "branchSchema :: BranchSchema",
+      "branchSchema = " <> renderBranchSchema (branchSchemaFor graph (ResolvedStructural declaration shape))
+    ]
+  where
+    name = sdName declaration
+    domainType = "ConsumerDomain." <> hsType (sdHaskell declaration)
+    codecModule = genPrefixFor ctx (aggName owner) <> ".Codec"
+    fixtureModule = qualifiedModule (sdFixtures declaration)
+    fixtureSymbol = lastSegment (unQualifiedValueName (sdFixtures declaration))
+
+branchSchemaFor :: TypeGraph -> ResolvedMappedDecl -> BranchSchema
+branchSchemaFor graph =
+  foldMappedDecl
+    MappedDeclAlgebra
+      { onStructuralDecl = \_ shape ->
+          foldMappedShape
+            MappedShapeAlgebra
+              { onRecord = \_ _ fields ->
+                  BranchRecord
+                    [ BranchField
+                        (rwfKey field)
+                        (rwfPresence field == POptional)
+                        (branchExpr graph (rwfType field))
+                    | field <- fields
+                    ],
+                onEnum = const BranchScalar,
+                onUnion = \encoding arms ->
+                  BranchUnion
+                    (ueTagField encoding)
+                    (ueContentsField encoding)
+                    [BranchArm (rwaTag arm) (branchExpr graph <$> rwaPayload arm) | arm <- arms]
+              }
+            shape,
+        onOpaqueDecl = const BranchScalar
+      }
+
+branchExpr :: TypeGraph -> ResolvedTypeExpr -> BranchSchema
+branchExpr graph =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = BranchScalar,
+        onInt = BranchScalar,
+        onInteger = BranchScalar,
+        onBool = BranchScalar,
+        onNatural = BranchScalar,
+        onTime = BranchScalar,
+        onJson = BranchScalar,
+        onOptional = BranchOptional,
+        onList = BranchList,
+        onMap = BranchMap,
+        onRef = \key -> maybe BranchScalar (branchSchemaFor graph) (Map.lookup key (tgDeclarations graph))
+      }
+
+renderBranchSchema :: BranchSchema -> Text
+renderBranchSchema schema = case schema of
+  BranchScalar -> "BranchScalar"
+  BranchOptional nested -> "BranchOptional (" <> renderBranchSchema nested <> ")"
+  BranchList nested -> "BranchList (" <> renderBranchSchema nested <> ")"
+  BranchMap nested -> "BranchMap (" <> renderBranchSchema nested <> ")"
+  BranchRecord fields ->
+    "BranchRecord ["
+      <> T.intercalate
+        ", "
+        [ "BranchField "
+            <> tshow (bfWireKey field)
+            <> " "
+            <> (if bfPresenceOptional field then "True" else "False")
+            <> " ("
+            <> renderBranchSchema (bfSchema field)
+            <> ")"
+        | field <- fields
+        ]
+      <> "]"
+  BranchUnion tagField contentsField arms ->
+    "BranchUnion "
+      <> tshow tagField
+      <> " "
+      <> tshow contentsField
+      <> " ["
+      <> T.intercalate
+        ", "
+        [ "BranchArm "
+            <> tshow (baWireTag arm)
+            <> " "
+            <> maybe "Nothing" (\nested -> "(Just (" <> renderBranchSchema nested <> "))") (baPayloadSchema arm)
+        | arm <- arms
+        ]
+      <> "]"
+
+-- | Emit one create-once consumer module per distinct qualified obligation
+-- owner. Multiple mapped declarations may intentionally share a leaf binding
+-- module, so grouping happens by module rather than by declaration.
+bindingSkeletonModules :: Context -> Spec -> TypeGraph -> [ScaffoldModule]
+bindingSkeletonModules ctx spec graph = map fst (bindingSkeletonOwners ctx spec graph)
+
+-- | 'bindingSkeletonModules' paired with the mapped declarations whose
+-- obligations each skeleton carries, in first-appearance order. A skeleton shared
+-- by declarations from different member files therefore names all of them, which
+-- is what lets whole-workspace scaffolding treat it as context-level rather than
+-- attributing it to an arbitrary member.
+bindingSkeletonOwners :: Context -> Spec -> TypeGraph -> [(ScaffoldModule, [Name])]
+bindingSkeletonOwners ctx spec graph = case bindingObligations spec of
+  Left _ -> []
+  Right obligations ->
+    [ (emitBindingSkeleton ctx spec graph owner entries, nub (map obligationMappedName entries))
+    | (owner, entries) <- Map.toAscList (Map.fromListWith (<>) [(obligationModule obligation, [obligation]) | obligation <- obligations])
+    ]
+
+emitBindingSkeleton :: Context -> Spec -> TypeGraph -> Text -> [BindingObligation] -> ScaffoldModule
+emitBindingSkeleton ctx spec graph owner obligations =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" owner <> ".hs"),
+      moduleText =
+        nl $
+          [ "{-# LANGUAGE DataKinds #-}",
+            "{-# LANGUAGE LambdaCase #-}",
+            "",
+            "-- This is a HAND-OWNED consumer binding skeleton. keiro-dsl creates it once",
+            "-- and never overwrites it. Fill each HOLE and run the generated harness.",
+            "module " <> owner <> " ("
+          ]
+            <> exportLines
+            <> [") where", ""]
+            <> map ("import " <>) imports
+            <> [""]
+            <> intercalateBlank (map renderObligation obligations),
+      kind = HoleStub,
+      origin = "consumer binding skeleton " <> owner
+    }
+  where
+    exportLines =
+      [ (if index == (0 :: Int) then "    " else "  , ") <> obligationSymbol obligation
+      | (index, obligation) <- zip [0 ..] obligations
+      ]
+    imports =
+      sort . nub $
+        [ hsModule (sdHaskell declaration) <> " qualified"
+        | obligation <- obligations,
+          Just (declaration, _) <- [structuralFor obligation]
+        ]
+          <> [ structuralShapeModule ctx (sdName declaration) <> " qualified"
+             | obligation <- obligations,
+               obligationKind obligation == BindingValue,
+               Just (declaration, _) <- [structuralFor obligation]
+             ]
+          <> [ "Keiro.Codec.Structural (FixtureCases, StructuralBinding (..))"
+             | any (\obligation -> obligationCategory obligation == "structural" && obligationKind obligation `elem` [BindingValue, FixtureValue]) obligations
+             ]
+          <> [ hsModule (consumerNominalHaskell binding) <> " qualified"
+             | obligation <- obligations,
+               Just (_, binding) <- [nominalFor obligation]
+             ]
+          <> [ nominalRepresentationModule ctx (resolvedNominalName nominal) <> " qualified"
+             | obligation <- obligations,
+               obligationKind obligation == BindingValue,
+               Just (nominal, _) <- [nominalFor obligation],
+               EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
+             ]
+          <> [ "Keiro.Codec.Nominal (NominalBinding (..), NominalFixtureCases)"
+             | any ((/= "structural") . obligationCategory) obligations
+             ]
+          <> [ "Data.KindID (KindID)"
+             | obligation <- obligations,
+               Just (nominal, _) <- [nominalFor obligation],
+               IdRepresentation {} <- [resolvedNominalRepresentation nominal]
+             ]
+          <> [ "Data.Text (Text)"
+             | obligation <- obligations,
+               Just (nominal, _) <- [nominalFor obligation],
+               ScalarRepresentation NominalText <- [resolvedNominalRepresentation nominal]
+             ]
+          <> [ "Data.Time (UTCTime)"
+             | obligation <- obligations,
+               Just (nominal, _) <- [nominalFor obligation],
+               ScalarRepresentation NominalTime <- [resolvedNominalRepresentation nominal]
+             ]
+          <> [ "Numeric.Natural (Natural)"
+             | obligation <- obligations,
+               Just (nominal, _) <- [nominalFor obligation],
+               ScalarRepresentation NominalNatural <- [resolvedNominalRepresentation nominal]
+             ]
+    renderObligation obligation = case structuralFor obligation of
+      Nothing -> case nominalFor obligation of
+        Just (nominal, _) -> renderNominalObligation nominal obligation
+        Nothing -> ["-- HOLE: declaration disappeared before skeleton rendering"]
+      Just (declaration, shape) -> case obligationKind obligation of
+        BindingValue -> renderBinding ctx declaration shape obligation
+        FixtureValue ->
+          [ "-- HOLE: provide deterministic labelled conformance fixtures for " <> sdName declaration,
+            obligationSignature obligation,
+            obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> sdName declaration <> " fixtures")
+          ]
+        InitialValue ->
+          [ "-- HOLE: provide the initial register value for " <> sdName declaration,
+            obligationSignature obligation,
+            obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> sdName declaration <> " initial value")
+          ]
+    structuralFor obligation = case Map.lookup (MappedKey (obligationMappedName obligation)) (tgDeclarations graph) of
+      Just (ResolvedStructural declaration shape) -> Just (declaration, shape)
+      _ -> Nothing
+    nominalFor obligation = do
+      registry <- either (const Nothing) Just (resolveNominalTypes spec)
+      nominal <- lookupNominalType (obligationMappedName obligation) registry
+      binding <- case resolvedNominalOwnership nominal of
+        ConsumerNominal value -> Just value
+        GeneratedNominal -> Nothing
+      pure (nominal, binding)
+    renderNominalObligation nominal obligation = case obligationKind obligation of
+      BindingValue ->
+        [ "-- HOLE: complete both total directions; the generated codec remains wire authority.",
+          obligationSignature obligation,
+          obligationSymbol obligation <> " =",
+          "  NominalBinding",
+          "    { nominalToRepresentation = \\_domainValue -> error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " nominalToRepresentation"),
+          "    , nominalFromRepresentation = \\_representationValue -> error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " nominalFromRepresentation"),
+          "    }"
+        ]
+      FixtureValue ->
+        [ "-- HOLE: provide deterministic labelled expected-wire fixtures for " <> resolvedNominalName nominal,
+          obligationSignature obligation,
+          obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " fixtures")
+        ]
+      InitialValue ->
+        [ "-- HOLE: provide the initial register value for " <> resolvedNominalName nominal,
+          obligationSignature obligation,
+          obligationSymbol obligation <> " = error " <> tshow ("HOLE: fill " <> resolvedNominalName nominal <> " initial value")
+        ]
+    intercalateBlank [] = []
+    intercalateBlank (section : rest) = section <> concatMap ("" :) rest
+
+renderBinding :: Context -> StructuralDecl -> ResolvedMappedShape -> BindingObligation -> [Text]
+renderBinding ctx declaration shape obligation =
+  [ "-- HOLE: complete both total directions; wire policy remains in the generated codec.",
+    obligationSymbol obligation <> " :: StructuralBinding " <> domainType <> " " <> shapeType,
+    obligationSymbol obligation <> " =",
+    "  StructuralBinding",
+    "    { bindingToShape = \\case"
+  ]
+    <> indentCases (bindingCases True)
+    <> ["    , bindingFromShape = \\case"]
+    <> indentCases (bindingCases False)
+    <> ["    }"]
+  where
+    domainModule = hsModule (sdHaskell declaration)
+    domainType = domainModule <> "." <> hsType (sdHaskell declaration)
+    shapeModuleName = structuralShapeModule ctx (sdName declaration)
+    shapeType = shapeModuleName <> "." <> sdName declaration <> "Shape"
+    domainCtor constructor = domainModule <> "." <> constructor
+    shapeCtor constructor = shapeModuleName <> "." <> constructor
+    indentCases = map ("      " <>)
+    bindingCases toShapeDirection =
+      foldMappedShape
+        MappedShapeAlgebra
+          { onRecord = \constructor _ fields -> [recordCase toShapeDirection constructor fields],
+            onEnum = \entries -> map (enumCase toShapeDirection . weCtor) entries,
+            onUnion = \_ arms -> map (unionCase toShapeDirection) arms
+          }
+        shape
+    recordCase toShapeDirection constructor fields =
+      sourceCtor
+        <> arguments variables
+        <> " -> "
+        <> targetCtor
+        <> arguments (map (holeFor toShapeDirection . rwfHaskell) fields)
+      where
+        variables = map (("_" <>) . (<> "Value") . rwfHaskell) fields
+        sourceCtor = if toShapeDirection then domainCtor constructor else shapeCtor constructor
+        targetCtor = if toShapeDirection then shapeCtor constructor else domainCtor constructor
+    enumCase toShapeDirection constructor =
+      sourceCtor <> " -> " <> holeFor toShapeDirection constructor
+      where
+        sourceCtor = if toShapeDirection then domainCtor constructor else shapeCtor constructor
+    unionCase toShapeDirection arm =
+      sourceCtor
+        <> maybe "" (const " _payloadValue") (rwaPayload arm)
+        <> " -> "
+        <> case rwaPayload arm of
+          Nothing -> holeFor toShapeDirection (rwaCtor arm)
+          Just _ -> targetCtor <> " " <> holeFor toShapeDirection (rwaCtor arm <> ".payload")
+      where
+        sourceCtor = if toShapeDirection then domainCtor (rwaCtor arm) else shapeCtor (rwaCtor arm)
+        targetCtor = if toShapeDirection then shapeCtor (rwaCtor arm) else domainCtor (rwaCtor arm)
+    arguments [] = ""
+    arguments values = " " <> T.unwords values
+    holeFor toShapeDirection fieldName =
+      "(error "
+        <> tshow
+          ( "HOLE: fill "
+              <> sdName declaration
+              <> (if toShapeDirection then " bindingToShape." else " bindingFromShape.")
+              <> fieldName
+          )
+        <> ")"
+
+shapeModule :: Context -> TypeGraph -> (StructuralDecl, ResolvedMappedShape) -> ScaffoldModule
+shapeModule ctx graph (declaration, shape) =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" (structuralShapeModule ctx (sdName declaration)) <> ".hs"),
+      moduleText = emitShape ctx graph declaration shape,
+      kind = Generated,
+      origin = nodeOrigin "mapped structural" (sdName declaration) (sdLoc declaration)
+    }
+
+structuralPrefix :: Context -> Text
+structuralPrefix ctx = case placement ctx of
+  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Structural"
+  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.Structural"
+
+structuralShapeModule :: Context -> Name -> Text
+structuralShapeModule ctx name = structuralPrefix ctx <> ".Shape." <> name
+
+nominalRepresentationModule :: Context -> Name -> Text
+nominalRepresentationModule ctx name = case placement ctx of
+  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".Nominal.Shape." <> name
+  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Nominal.Shape." <> name <> ".Generated"
+
+-- | Emit the one generated nominal authority for the complete context. The
+-- empty declaration attribution is intentional: in a workspace this module is
+-- context-level even when all declarations currently happen to live in one
+-- member, so moving that member cannot move Haskell type ownership.
+generatedNominalOwners :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
+generatedNominalOwners ctx service = case planNominalGenerationForService ctx service of
+  Left _ -> []
+  Right [] -> []
+  Right owners ->
+    [ ( ScaffoldModule
+          { modulePath = T.unpack (T.replace "." "/" (generatedNominalModule ctx) <> ".hs"),
+            moduleText = emitGeneratedNominals languageContract ctx owners,
+            kind = Generated,
+            origin = "context " <> specContext spec <> " generated nominal declarations"
+          },
+        []
+      )
+    ]
+      <> [ ( ScaffoldModule
+               { modulePath = T.unpack (T.replace "." "/" (generatedNominalInternalModule ctx) <> ".hs"),
+                 moduleText = emitGeneratedNominalInternals ctx enforcingIds,
+                 kind = Generated,
+                 origin = "context " <> specContext spec <> " generated nominal ID internals"
+               },
+             []
+           )
+         | not (null enforcingIds)
+         ]
+    where
+      enforcingIds =
+        [ (nominal, contract)
+        | owner <- owners,
+          let nominal = nominalDeclaration owner,
+          IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
+          Just contract <- [idDomainContractFor languageContract prefix]
+        ]
+  where
+    spec = checkedSpec service
+    languageContract = checkedLanguageContract service
+
+generatedNominalInternalModule :: Context -> Text
+generatedNominalInternalModule ctx = generatedNominalModule ctx <> ".Internal"
+
+emitGeneratedNominals :: EffectiveLanguageContract -> Context -> [NominalGenerationOwner] -> Text
+emitGeneratedNominals languageContract ctx owners =
+  nl
+    ( equalityPragmas
+        <> [ "{-# LANGUAGE DeriveAnyClass #-}",
+             "{-# LANGUAGE DeriveGeneric #-}",
+             "{-# LANGUAGE LambdaCase #-}",
+             generatedBanner,
+             moduleHeader,
+             "",
+             "import Data.Aeson (FromJSON, ToJSON)",
+             "import Data.Text (Text)",
+             "import GHC.Generics (Generic)",
+             "import Keiki.Shape (CanonicalTypeName)"
+           ]
+        <> internalImports
+        <> equalityImports
+        <> [ "",
+             sectionsOf [map emitOwner owners]
+           ]
+    )
+  where
+    usesEquality = any nominalEqualityUsed owners
+    usesExactEquality = any (\owner -> nominalEqualityUsed owner && exactOwner (nominalDeclaration owner)) owners
+    enforcingIds =
+      [ nominal
+      | owner <- owners,
+        let nominal = nominalDeclaration owner,
+        IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
+        Just _ <- [idDomainContractFor languageContract prefix]
+      ]
+    moduleHeader
+      | null enforcingIds = "module " <> generatedNominalModule ctx <> " where"
+      | otherwise =
+          nl
+            [ "module " <> generatedNominalModule ctx,
+              "  ( " <> T.intercalate "\n  , " (concatMap ownerExports owners),
+              "  ) where"
+            ]
+    ownerExports owner =
+      baseExports <> equalityExports
+      where
+        nominal = nominalDeclaration owner
+        name = resolvedNominalName nominal
+        baseExports = case resolvedNominalRepresentation nominal of
+          IdRepresentation prefix
+            | Just _ <- idDomainContractFor languageContract prefix ->
+                [name, "parse" <> name, "mk" <> name, nominalTextName nominal]
+          _ -> [name <> " (..)", nominalTextName nominal]
+        equalityExports =
+          if nominalEqualityUsed owner
+            then [nominalEqualityTagName nominal, nominalEqualityWitnessName nominal]
+            else []
+    equalityPragmas =
+      if usesEquality
+        then
+          [ "{-# LANGUAGE DataKinds #-}",
+            "{-# LANGUAGE TypeApplications #-}",
+            "{-# LANGUAGE TypeFamilies #-}"
+          ]
+        else []
+    equalityImports =
+      ["import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)" | usesEquality]
+        <> ["import Data.List.NonEmpty (NonEmpty (..))" | usesExactEquality]
+        <> ["import Keiki.ProjectionDomain (finiteProjectionDomain)" | usesExactEquality && null enforcingIds]
+        <> ["import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)" | usesExactEquality && not (null enforcingIds)]
+        <> ["import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)" | not (null enforcingIds)]
+    internalImports =
+      [ "import "
+          <> generatedNominalInternalModule ctx
+          <> " ("
+          <> T.intercalate
+            ", "
+            (concatMap (\nominal -> [resolvedNominalName nominal, "mk" <> resolvedNominalName nominal, "parse" <> resolvedNominalName nominal, nominalTextName nominal]) enforcingIds)
+          <> ")"
+      | not (null enforcingIds)
+      ]
+    emitOwner owner = emitGeneratedNominal languageContract (nominalEqualityUsed owner) (nominalDeclaration owner)
+    exactOwner nominal = case resolvedNominalRepresentation nominal of
+      EnumRepresentation {} -> True
+      IdRepresentation prefix -> isJust (idDomainContractFor languageContract prefix)
+      ScalarRepresentation {} -> False
+
+emitGeneratedNominal :: EffectiveLanguageContract -> Bool -> ResolvedNominalType -> Text
+emitGeneratedNominal languageContract equalityUsed nominal = case resolvedNominalRepresentation nominal of
+  IdRepresentation prefix
+    | Just _ <- idDomainContractFor languageContract prefix ->
+        nl $
+          ["instance CanonicalTypeName " <> name]
+            <> equalitySection
+  IdRepresentation {} ->
+    nl $
+      [ "newtype " <> name <> " = " <> name <> " Text",
+        "  deriving stock (Generic, Eq, Ord, Show)",
+        "  deriving anyclass (ToJSON, FromJSON)",
+        "",
+        "instance CanonicalTypeName " <> name,
+        "",
+        nominalTextName nominal <> " :: " <> name <> " -> Text",
+        nominalTextName nominal <> " (" <> name <> " value) = value"
+      ]
+        <> equalitySection
+  EnumRepresentation constructors ->
+    nl $
+      [ "data " <> name <> " = " <> T.intercalate " | " (map fst (NE.toList constructors)),
+        "  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)",
+        "  deriving anyclass (ToJSON, FromJSON)",
+        "",
+        "instance CanonicalTypeName " <> name,
+        "",
+        nominalTextName nominal <> " :: " <> name <> " -> Text",
+        nominalTextName nominal <> " = \\case",
+        nl ["  " <> constructor <> " -> " <> tshow wire | (constructor, wire) <- NE.toList constructors]
+      ]
+        <> equalitySection
+  ScalarRepresentation {} ->
+    error "generated nominal scalar reached generated declaration emission"
+  where
+    name = resolvedNominalName nominal
+    equalitySection = if equalityUsed then ["", emitGeneratedNominalEquality languageContract nominal] else []
+
+emitGeneratedNominalEquality :: EffectiveLanguageContract -> ResolvedNominalType -> Text
+emitGeneratedNominalEquality languageContract nominal =
+  nl $
+    [ "data " <> tagName,
+      "",
+      "instance FieldProjection " <> tagName <> " where",
+      "  type FieldName " <> tagName <> " = " <> tshow name,
+      "  type FieldOwner " <> tagName <> " = " <> name,
+      "  type FieldResult " <> tagName <> " = Text",
+      "  fieldShapeId _ = " <> tshow equalityIdentity,
+      "  projectFieldValue _ = " <> nominalTextName nominal
+    ]
+      <> exactInstance
+      <> [ "",
+           witnessName <> " :: FieldWitness " <> tagName,
+           witnessName <> " = " <> witnessConstructor <> " @" <> tagName
+         ]
+  where
+    name = resolvedNominalName nominal
+    tagName = nominalEqualityTagName nominal
+    witnessName = nominalEqualityWitnessName nominal
+    equalityIdentity = fromMaybe (error "generated nominal equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
+    (exactInstance, witnessConstructor) = case resolvedNominalRepresentation nominal of
+      IdRepresentation prefix -> case idDomainContractFor languageContract prefix of
+        Nothing -> ([], "fieldWitness")
+        Just _ ->
+          ( [ "",
+              patternName <> " :: TextPattern",
+              patternName <> " = either (error . show) id (idDomainTextPattern (typeIdV7Domain " <> tshow prefix <> "))",
+              "",
+              "instance ExactFieldProjection " <> tagName <> " where",
+              "  fieldProjectionDomain _ = textProjectionDomain " <> patternName,
+              "  reconstructFieldOwner _ = either (const Nothing) Just . parse" <> name
+            ],
+            "exactFieldWitness"
+          )
+      EnumRepresentation constructors ->
+        ( [ "",
+            "instance ExactFieldProjection " <> tagName <> " where",
+            "  fieldProjectionDomain _ = finiteProjectionDomain (" <> renderNonEmpty (map (tshow . snd) (NE.toList constructors)) <> ")",
+            "  reconstructFieldOwner _ = \\case"
+          ]
+            <> ["    " <> tshow wire <> " -> Just " <> constructor | (constructor, wire) <- NE.toList constructors]
+            <> ["    _ -> Nothing"],
+          "exactFieldWitness"
+        )
+      ScalarRepresentation {} -> error "generated nominal scalar equality emission"
+    patternName = lowerFirst name <> "IdDomainPattern"
+
+emitGeneratedNominalInternals :: Context -> [(ResolvedNominalType, IdDomainContract)] -> Text
+emitGeneratedNominalInternals ctx nominals =
+  nl
+    [ "{-# LANGUAGE DeriveGeneric #-}",
+      generatedBanner,
+      "module " <> generatedNominalInternalModule ctx,
+      "  ( " <> T.intercalate "\n  , " (concatMap exportsFor nominals),
+      "  ) where",
+      "",
+      "import Data.Aeson (FromJSON (..), ToJSON (..), withText)",
+      "import Data.Text (Text)",
+      "import Data.Text qualified as T",
+      "import GHC.Generics (Generic)",
+      "import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)",
+      "",
+      sectionsOf [map emitInternal nominals]
+    ]
+  where
+    exportsFor (nominal, _) =
+      [ resolvedNominalName nominal,
+        "parse" <> resolvedNominalName nominal,
+        "mk" <> resolvedNominalName nominal,
+        nominalTextName nominal,
+        legacyNominalConstructorName nominal
+      ]
+    emitInternal (nominal, contract) =
+      nl
+        [ "newtype " <> name <> " = " <> name <> " Text",
+          "  deriving stock (Generic, Eq, Ord, Show)",
+          "",
+          "instance ToJSON " <> name <> " where",
+          "  toJSON = toJSON . " <> textName,
+          "",
+          "instance FromJSON " <> name <> " where",
+          "  parseJSON = withText " <> tshow name <> " (either (fail . T.unpack) pure . parse" <> name <> ")",
+          "",
+          "parse" <> name <> " :: Text -> Either Text " <> name,
+          "parse" <> name <> " input = case validateIdDomainText (typeIdV7Domain " <> tshow (idDomainPrefix contract) <> ") input of",
+          "  Left reason -> Left (T.pack (show reason))",
+          "  Right () -> Right (" <> name <> " input)",
+          "",
+          "mk" <> name <> " :: Text -> Either Text " <> name,
+          "mk" <> name <> " = parse" <> name,
+          "",
+          textName <> " :: " <> name <> " -> Text",
+          textName <> " (" <> name <> " value) = value",
+          "",
+          legacyNominalConstructorName nominal <> " :: Text -> " <> name,
+          legacyNominalConstructorName nominal <> " = " <> name
+        ]
+      where
+        name = resolvedNominalName nominal
+        textName = nominalTextName nominal
+
+nominalEqualityTagName :: ResolvedNominalType -> Text
+nominalEqualityTagName nominal = resolvedNominalName nominal <> "EqualityProjection"
+
+nominalEqualityWitnessName :: ResolvedNominalType -> Text
+nominalEqualityWitnessName nominal = lowerFirst (resolvedNominalName nominal) <> "EqualityWitness"
+
+renderNonEmpty :: [Text] -> Text
+renderNonEmpty values = case values of
+  [] -> error "cannot render an empty exact projection domain"
+  firstValue : rest -> firstValue <> " :| [" <> T.intercalate ", " rest <> "]"
+
+nominalTextName :: ResolvedNominalType -> Text
+nominalTextName = (<> "Text") . lowerFirst . resolvedNominalName
+
+-- | Explicit type/constructor imports for exactly the generated declarations a
+-- generated aggregate module uses. Keeping an import list avoids making every
+-- aggregate depend on every service declaration merely because they share the
+-- one owner module.
+generatedNominalTypeImports :: Context -> [ResolvedNominalType] -> [Text]
+generatedNominalTypeImports _ [] = []
+generatedNominalTypeImports ctx nominals =
+  [ "import "
+      <> generatedNominalModule ctx
+      <> " ("
+      <> T.intercalate ", " [resolvedNominalName nominal <> " (..)" | nominal <- stableNominals nominals]
+      <> ")"
+  ]
+
+generatedNominalTypeImportsForService :: CheckedService -> Context -> [ResolvedNominalType] -> [Text]
+generatedNominalTypeImportsForService _ _ [] = []
+generatedNominalTypeImportsForService service ctx nominals =
+  [ "import "
+      <> generatedNominalModule ctx
+      <> " ("
+      <> T.intercalate ", " (concatMap importsFor (stableNominals nominals))
+      <> ")"
+  ]
+  where
+    importsFor nominal = case resolvedNominalRepresentation nominal of
+      IdRepresentation prefix
+        | Just _ <- idDomainContractFor (checkedLanguageContract service) prefix ->
+            [resolvedNominalName nominal, "parse" <> resolvedNominalName nominal]
+      _ -> [resolvedNominalName nominal <> " (..)"]
+
+generatedNominalCodecImports :: CheckedService -> Context -> [ResolvedNominalType] -> [Text]
+generatedNominalCodecImports _ _ [] = []
+generatedNominalCodecImports service ctx nominals =
+  [ "import "
+      <> generatedNominalModule ctx
+      <> " ("
+      <> T.intercalate
+        ", "
+        ( concat
+            [ [typeImport nominal, nominalTextName nominal]
+            | nominal <- stableNominals nominals
+            ]
+        )
+      <> ")"
+  ]
+    <> [ "import "
+           <> generatedNominalInternalModule ctx
+           <> " ("
+           <> T.intercalate ", " [legacyNominalConstructorName nominal | nominal <- enforcingIds]
+           <> ")"
+       | not (null enforcingIds)
+       ]
+  where
+    typeImport nominal = case resolvedNominalRepresentation nominal of
+      IdRepresentation prefix
+        | Just _ <- idDomainContractFor (checkedLanguageContract service) prefix -> resolvedNominalName nominal
+      _ -> resolvedNominalName nominal <> " (..)"
+    enforcingIds =
+      [ nominal
+      | nominal <- stableNominals nominals,
+        IdRepresentation prefix <- [resolvedNominalRepresentation nominal],
+        Just _ <- [idDomainContractFor (checkedLanguageContract service) prefix]
+      ]
+
+legacyNominalConstructorName :: ResolvedNominalType -> Text
+legacyNominalConstructorName nominal = "unsafe" <> resolvedNominalName nominal <> "FromLegacyText"
+
+stableNominals :: [ResolvedNominalType] -> [ResolvedNominalType]
+stableNominals = Map.elems . Map.fromList . map (\nominal -> (resolvedNominalName nominal, nominal))
+
+nominalRepresentationOwners :: Context -> Spec -> [(ScaffoldModule, [Name])]
+nominalRepresentationOwners ctx spec = case resolveNominalTypes spec of
+  Left _ -> []
+  Right registry ->
+    [ (nominalRepresentationModuleValue ctx nominal constructors, [resolvedNominalName nominal])
+    | nominal <- Map.elems (nominalTypes registry),
+      ConsumerNominal {} <- [resolvedNominalOwnership nominal],
+      EnumRepresentation constructors <- [resolvedNominalRepresentation nominal]
+    ]
+
+nominalRepresentationModuleValue :: Context -> ResolvedNominalType -> NonEmpty (Name, Text) -> ScaffoldModule
+nominalRepresentationModuleValue ctx nominal constructors =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
+      moduleText =
+        nl
+          [ "{-# LANGUAGE DeriveGeneric #-}",
+            "{-# LANGUAGE LambdaCase #-}",
+            generatedBanner,
+            "module " <> moduleName <> " (" <> representationType <> " (..), " <> encoderName <> ") where",
+            "",
+            "import Data.Text (Text)",
+            "import GHC.Generics (Generic)",
+            "",
+            "data " <> representationType <> " = " <> T.intercalate " | " (map fst (NE.toList constructors)),
+            "  deriving stock (Eq, Generic, Ord, Show, Enum, Bounded)",
+            "",
+            encoderName <> " :: " <> representationType <> " -> Text",
+            encoderName <> " = \\case",
+            nl ["  " <> constructor <> " -> " <> tshow wire | (constructor, wire) <- NE.toList constructors]
+          ],
+      kind = Generated,
+      origin = nodeOrigin "bound nominal enum representation" (resolvedNominalName nominal) (resolvedNominalLoc nominal)
+    }
+  where
+    moduleName = nominalRepresentationModule ctx (resolvedNominalName nominal)
+    representationType = resolvedNominalName nominal <> "Representation"
+    encoderName = lowerFirst (resolvedNominalName nominal) <> "RepresentationText"
+
+nominalProjectionModule :: Context -> Text
+nominalProjectionModule ctx = case placement ctx of
+  GeneratedPrefix -> rootPrefix ctx <> "Generated." <> ctxPascalOf ctx <> ".NominalProjections"
+  CollocatedLeaf -> rootPrefix ctx <> ctxPascalOf ctx <> ".Generated.NominalProjections"
+
+nominalProjectionOwners :: Context -> CheckedService -> [(ScaffoldModule, [Name])]
+nominalProjectionOwners ctx service = case nominalProjectionTypes spec of
+  [] -> []
+  nominals ->
+    [ ( ScaffoldModule
+          { modulePath = T.unpack (T.replace "." "/" (nominalProjectionModule ctx) <> ".hs"),
+            moduleText = emitNominalProjections (checkedLanguageContract service) ctx nominals,
+            kind = Generated,
+            origin = "context " <> specContext spec <> " nominal scalar projection facade"
+          },
+        []
+      )
+    ]
+  where
+    spec = checkedSpec service
+
+nominalProjectionTypes :: Spec -> [ResolvedNominalType]
+nominalProjectionTypes spec =
+  Map.elems . Map.fromList $
+    [ (resolvedNominalName nominal, nominal)
+    | aggregate <- [value | NAggregate value <- specNodes spec],
+      resolved <- registerTypes aggregate <> commandTypes aggregate,
+      AggregateNominal nominal <- [resolved],
+      ConsumerNominal {} <- [resolvedNominalOwnership nominal]
+    ]
+  where
+    symbols = aggregateSymbols spec
+    registerTypes aggregate =
+      [ resolved
+      | register <- aggRegs aggregate,
+        Right resolved <- [resolveAggregateType symbols (regLoc register) RegisterUse (regType register)]
+      ]
+    commandTypes aggregate =
+      [ resolved
+      | command <- aggCommands aggregate,
+        field <- cmdFields command,
+        Right resolved <- [inferAggregateFieldType symbols aggregate CommandFieldUse field]
+      ]
+
+emitNominalProjections :: EffectiveLanguageContract -> Context -> [ResolvedNominalType] -> Text
+emitNominalProjections languageContract ctx nominals =
+  nl $
+    [ "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE TypeApplications #-}",
+      "{-# LANGUAGE TypeFamilies #-}",
+      generatedBanner,
+      "module " <> moduleName <> " where",
+      ""
+    ]
+      <> map ("import " <>) imports
+      <> [""]
+      <> [T.intercalate "\n\n" (map emitNominalProjection nominals)]
+  where
+    moduleName = nominalProjectionModule ctx
+    imports =
+      sort . nub $
+        [ "Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)",
+          "Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)"
+        ]
+          <> [hsModule (consumerNominalHaskell binding) <> " qualified" | nominal <- nominals, ConsumerNominal binding <- [resolvedNominalOwnership nominal]]
+          <> [qualifiedModule (consumerNominalBinding binding) <> " qualified" | nominal <- nominals, ConsumerNominal binding <- [resolvedNominalOwnership nominal]]
+          <> [nominalRepresentationModule ctx (resolvedNominalName nominal) <> " qualified" | nominal <- nominals, EnumRepresentation {} <- [resolvedNominalRepresentation nominal]]
+          <> ["Data.KindID qualified as KindID" | any hasId nominals]
+          <> ["Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain, validateIdDomainText)" | any hasEnforcedId nominals]
+          <> ["Data.List.NonEmpty (NonEmpty (..))" | any hasExactDomain nominals]
+          <> ["Data.Text (Text)" | any usesText nominals]
+          <> ["Data.Time (UTCTime)" | any (hasScalar NominalTime) nominals]
+          <> ["Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, matchesTextPattern, textCharSet, textConcat, textLiteral, textProjectionDomain, textRepeatBetween)" | any hasExactDomain nominals]
+          <> ["Numeric.Natural (Natural)" | any (hasScalar NominalNatural) nominals]
+    hasScalar wanted nominal = resolvedNominalRepresentation nominal == ScalarRepresentation wanted
+    hasId nominal = case resolvedNominalRepresentation nominal of IdRepresentation {} -> True; _ -> False
+    hasEnforcedId nominal = case resolvedNominalRepresentation nominal of
+      IdRepresentation prefix -> isJust (idDomainContractFor languageContract prefix)
+      _ -> False
+    hasExactDomain nominal = case resolvedNominalRepresentation nominal of ScalarRepresentation {} -> False; _ -> True
+    usesText nominal = case resolvedNominalRepresentation nominal of ScalarRepresentation NominalText -> True; IdRepresentation {} -> True; EnumRepresentation {} -> True; _ -> False
+    emitNominalProjection nominal = case resolvedNominalOwnership nominal of
+      GeneratedNominal -> ""
+      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
+        ScalarRepresentation {} -> emitScalarProjection nominal binding
+        IdRepresentation prefix -> emitConsumerIdProjection nominal binding prefix
+        EnumRepresentation constructors -> emitConsumerEnumProjection nominal binding constructors
+    emitScalarProjection nominal binding =
+      nl
+        [ "data " <> tagName,
+          "",
+          "instance FieldProjection " <> tagName <> " where",
+          "  type FieldName " <> tagName <> " = " <> tshow name,
+          "  type FieldOwner " <> tagName <> " = " <> renderHaskellSource (consumerNominalHaskell binding),
+          "  type FieldResult " <> tagName <> " = " <> scalarHaskellType (resolvedNominalRepresentation nominal),
+          "  fieldShapeId _ = " <> tshow (unCanonicalTypeId (consumerNominalCanonical binding)),
+          "  projectFieldValue _ = nominalToRepresentation " <> unQualifiedValueName (consumerNominalBinding binding),
+          "",
+          witnessName <> " :: FieldWitness " <> tagName,
+          witnessName <> " = fieldWitness @" <> tagName
+        ]
+      where
+        name = resolvedNominalName nominal
+        tagName = name <> "NominalProjection"
+        witnessName = lowerFirst name <> "Witness"
+    emitConsumerIdProjection nominal binding prefix =
+      nl
+        ( patternLines
+            <> [ "",
+                 "data " <> tagName,
+                 "",
+                 "instance FieldProjection " <> tagName <> " where",
+                 "  type FieldName " <> tagName <> " = " <> tshow name,
+                 "  type FieldOwner " <> tagName <> " = " <> ownerType,
+                 "  type FieldResult " <> tagName <> " = Text",
+                 "  fieldShapeId _ = " <> tshow equalityIdentity,
+                 "  projectFieldValue _ = KindID.toText . nominalToRepresentation " <> bindingName,
+                 "",
+                 "instance ExactFieldProjection " <> tagName <> " where",
+                 "  fieldProjectionDomain _ = textProjectionDomain " <> patternName,
+                 "  reconstructFieldOwner _ value"
+               ]
+            <> validationGuard
+            <> [ "    | not (matchesTextPattern " <> patternName <> " value) = Nothing",
+                 "    | otherwise = case KindID.parseText @" <> tshow prefix <> " value of",
+                 "        Left _ -> Nothing",
+                 "        Right representation -> Just (nominalFromRepresentation " <> bindingName <> " representation)",
+                 "",
+                 witnessName <> " :: FieldWitness " <> tagName,
+                 witnessName <> " = exactFieldWitness @" <> tagName
+               ]
+        )
+      where
+        name = resolvedNominalName nominal
+        tagName = nominalEqualityTagName nominal
+        witnessName = nominalEqualityWitnessName nominal
+        patternName = lowerFirst name <> "EqualityPattern"
+        ownerType = renderHaskellSource (consumerNominalHaskell binding)
+        bindingName = unQualifiedValueName (consumerNominalBinding binding)
+        equalityIdentity = fromMaybe (error "consumer ID equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
+        enforced = isJust (idDomainContractFor languageContract prefix)
+        patternLines
+          | enforced =
+              [ patternName <> " :: TextPattern",
+                patternName <> " = either (error . show) id (idDomainTextPattern (typeIdV7Domain " <> tshow prefix <> "))"
+              ]
+          | otherwise =
+              [ patternName <> " :: TextPattern",
+                patternName <> " = either (error . show) id $ do",
+                "  prefix <- textLiteral " <> tshow (prefix <> "_"),
+                "  leading <- textCharSet ('0' :| \"1234567\")",
+                "  crockford <- textCharSet ('0' :| \"123456789abcdefghjkmnpqrstvwxyz\")",
+                "  suffix <- textRepeatBetween 25 25 crockford",
+                "  pure (textConcat (prefix :| [leading, suffix]))"
+              ]
+        validationGuard =
+          [ "    | Left _ <- validateIdDomainText (typeIdV7Domain " <> tshow prefix <> ") value = Nothing"
+          | enforced
+          ]
+    emitConsumerEnumProjection nominal binding constructors =
+      nl $
+        [ "data " <> tagName,
+          "",
+          "instance FieldProjection " <> tagName <> " where",
+          "  type FieldName " <> tagName <> " = " <> tshow name,
+          "  type FieldOwner " <> tagName <> " = " <> ownerType,
+          "  type FieldResult " <> tagName <> " = Text",
+          "  fieldShapeId _ = " <> tshow equalityIdentity,
+          "  projectFieldValue _ = " <> encoderName <> " . nominalToRepresentation " <> bindingName,
+          "",
+          "instance ExactFieldProjection " <> tagName <> " where",
+          "  fieldProjectionDomain _ = finiteProjectionDomain (" <> renderNonEmpty (map (tshow . snd) (NE.toList constructors)) <> ")",
+          "  reconstructFieldOwner _ = \\case"
+        ]
+          <> [ "    " <> tshow wire <> " -> Just (nominalFromRepresentation " <> bindingName <> " " <> representationModule <> "." <> constructor <> ")"
+             | (constructor, wire) <- NE.toList constructors
+             ]
+          <> [ "    _ -> Nothing",
+               "",
+               witnessName <> " :: FieldWitness " <> tagName,
+               witnessName <> " = exactFieldWitness @" <> tagName
+             ]
+      where
+        name = resolvedNominalName nominal
+        tagName = nominalEqualityTagName nominal
+        witnessName = nominalEqualityWitnessName nominal
+        ownerType = renderHaskellSource (consumerNominalHaskell binding)
+        bindingName = unQualifiedValueName (consumerNominalBinding binding)
+        representationModule = nominalRepresentationModule ctx name
+        encoderName = representationModule <> "." <> lowerFirst name <> "RepresentationText"
+        equalityIdentity = fromMaybe (error "consumer enum equality contract missing") (nominalEqualityIdentityForService languageContract nominal)
+    scalarHaskellType representation = case representation of
+      ScalarRepresentation NominalText -> "Text"
+      ScalarRepresentation NominalInt -> "Int"
+      ScalarRepresentation NominalNatural -> "Natural"
+      ScalarRepresentation NominalBool -> "Bool"
+      ScalarRepresentation NominalTime -> "UTCTime"
+      IdRepresentation {} -> "()"
+      EnumRepresentation {} -> "()"
+
+structuralProjectionModule :: Context -> Text
+structuralProjectionModule ctx = structuralPrefix ctx <> "Projections"
+
+emitShape :: Context -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
+emitShape ctx graph declaration shape =
+  nl $
+    languagePragmas
+      <> [ generatedBanner,
+           "module " <> moduleName <> " (" <> shapeType <> " (..)) where",
+           ""
+         ]
+      <> map ("import " <>) imports
+      <> ["" | not (null imports)]
+      <> [shapeDeclaration]
+  where
+    moduleName = structuralShapeModule ctx (sdName declaration)
+    shapeType = sdName declaration <> "Shape"
+    requirements = shapeRequirements ctx graph shape
+    languagePragmas =
+      ["{-# LANGUAGE DeriveGeneric #-}"]
+        <> ["{-# LANGUAGE DuplicateRecordFields #-}" | shapeHasRecord shape]
+    imports =
+      sort . nub $
+        ["Data.Aeson (Value)" | ReqJson `elem` requirements]
+          <> ["Data.Map.Strict (Map)" | ReqMap `elem` requirements]
+          <> ["Data.Text (Text)" | ReqText `elem` requirements]
+          <> ["Data.Time (UTCTime)" | ReqTime `elem` requirements]
+          <> ["GHC.Generics (Generic)"]
+          <> ["Numeric.Natural (Natural)" | ReqNatural `elem` requirements]
+          <> [m <> " qualified" | ReqModule m <- requirements]
+    shapeDeclaration =
+      foldMappedShape
+        MappedShapeAlgebra
+          { onRecord = \constructor _ fields ->
+              nl $
+                ["data " <> shapeType <> " = " <> constructor]
+                  <> recordFields
+                    [ (rwfHaskell field, renderShapeType ctx graph (rwfType field))
+                    | field <- fields
+                    ]
+                  <> ["  deriving stock (Eq, Generic, Show)"],
+            onEnum = \entries ->
+              "data "
+                <> shapeType
+                <> " = "
+                <> T.intercalate " | " (map weCtor entries)
+                <> "\n  deriving stock (Eq, Generic, Show)",
+            onUnion = \_ arms ->
+              nl $
+                case arms of
+                  [] -> ["data " <> shapeType <> " = " <> shapeType <> "Empty", "  deriving stock (Eq, Generic, Show)"]
+                  firstArm : rest ->
+                    ["data " <> shapeType <> " = " <> renderArm firstArm]
+                      <> ["  | " <> renderArm arm | arm <- rest]
+                      <> ["  deriving stock (Eq, Generic, Show)"]
+          }
+        shape
+    renderArm arm = rwaCtor arm <> maybe "" ((" !" <>) . renderShapeType ctx graph) (rwaPayload arm)
+
+data ShapeRequirement
+  = ReqJson
+  | ReqMap
+  | ReqText
+  | ReqTime
+  | ReqNatural
+  | ReqModule !Text
+  deriving stock (Eq, Ord, Show)
+
+shapeHasRecord :: ResolvedMappedShape -> Bool
+shapeHasRecord =
+  foldMappedShape
+    MappedShapeAlgebra
+      { onRecord = \_ _ _ -> True,
+        onEnum = const False,
+        onUnion = \_ _ -> False
+      }
+
+shapeRequirements :: Context -> TypeGraph -> ResolvedMappedShape -> [ShapeRequirement]
+shapeRequirements ctx graph =
+  foldMappedShape
+    MappedShapeAlgebra
+      { onRecord = \_ _ fields -> concatMap (exprRequirements ctx graph . rwfType) fields,
+        onEnum = const [],
+        onUnion = \_ arms -> concatMap (maybe [] (exprRequirements ctx graph) . rwaPayload) arms
+      }
+
+exprRequirements :: Context -> TypeGraph -> ResolvedTypeExpr -> [ShapeRequirement]
+exprRequirements ctx graph =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = [ReqText],
+        onInt = [],
+        onInteger = [],
+        onBool = [],
+        onNatural = [ReqNatural],
+        onTime = [ReqTime],
+        onJson = [ReqJson],
+        onOptional = id,
+        onList = id,
+        onMap = (ReqMap :) . (ReqText :),
+        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
+          Just (ResolvedStructural declaration _) -> [ReqModule (structuralShapeModule ctx (sdName declaration))]
+          Just (ResolvedOpaque declaration) -> [ReqModule (hsModule (odHaskell declaration))]
+          Nothing -> []
+      }
+
+renderShapeType :: Context -> TypeGraph -> ResolvedTypeExpr -> Text
+renderShapeType ctx graph =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = "Text",
+        onInt = "Int",
+        onInteger = "Integer",
+        onBool = "Bool",
+        onNatural = "Natural",
+        onTime = "UTCTime",
+        onJson = "Value",
+        onOptional = \value -> "(Maybe (" <> value <> "))",
+        onList = \value -> "([" <> value <> "])",
+        onMap = \value -> "(Map Text (" <> value <> "))",
+        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
+          Just (ResolvedStructural nested _) ->
+            structuralShapeModule ctx (sdName nested) <> "." <> sdName nested <> "Shape"
+          Just (ResolvedOpaque opaque) ->
+            hsModule (odHaskell opaque) <> "." <> hsType (odHaskell opaque)
+          Nothing -> "()"
+      }
+
+data StructuralProjection = StructuralProjection
+  { spTag :: !Text,
+    spWitness :: !Text,
+    spPointer :: !Text,
+    spOwner :: !HaskellSource,
+    spResult :: !Text,
+    spCanonical :: !CanonicalTypeId,
+    spBinding :: !QualifiedValueName,
+    spSelectors :: ![(Text, Text)]
+  }
+  deriving stock (Eq, Show)
+
+projectionSpecs :: TypeGraph -> [StructuralProjection]
+projectionSpecs graph =
+  sortOn spTag . allocateProjectionNames . concat $
+    [ projectionsForRoot graph declaration shape
+    | ResolvedStructural declaration shape <- Map.elems (tgDeclarations graph)
+    ]
+
+projectionsForRoot :: TypeGraph -> StructuralDecl -> ResolvedMappedShape -> [StructuralProjection]
+projectionsForRoot graph root rootShape = case rootShape of
+  RRecord _ _ fields -> concatMap (walkField [] []) fields
+  REnum {} -> []
+  RUnion {} -> []
+  where
+    walkField keys selectors field
+      | rwfPresence field /= PRequired = []
+      | otherwise = case projectionScalar (rwfType field) of
+          Just result -> [mkProjection (keys <> [rwfKey field]) (selectors <> [(shapeModuleForOwner, rwfHaskell field)]) result]
+          Nothing -> case rwfType field of
+            RRef key -> case Map.lookup key (tgDeclarations graph) of
+              Just (ResolvedStructural nested (RRecord _ _ nestedFields)) ->
+                concatMap
+                  (walkNested nested (keys <> [rwfKey field]) (selectors <> [(shapeModuleForOwner, rwfHaskell field)]))
+                  nestedFields
+              _ -> []
+            _ -> []
+      where
+        shapeModuleForOwner = "__SHAPE__." <> sdName root
+
+    walkNested owner keys selectors field
+      | rwfPresence field /= PRequired = []
+      | otherwise = case projectionScalar (rwfType field) of
+          Just result -> [mkProjection (keys <> [rwfKey field]) (selectors <> [(shapeModuleFor owner, rwfHaskell field)]) result]
+          Nothing -> case rwfType field of
+            RRef key -> case Map.lookup key (tgDeclarations graph) of
+              Just (ResolvedStructural nested (RRecord _ _ nestedFields)) ->
+                concatMap
+                  (walkNested nested (keys <> [rwfKey field]) (selectors <> [(shapeModuleFor owner, rwfHaskell field)]))
+                  nestedFields
+              _ -> []
+            _ -> []
+
+    -- Context is supplied when rendering; this marker is replaced there.
+    shapeModuleFor declaration = "__SHAPE__." <> sdName declaration
+    mkProjection keys selectors result =
+      StructuralProjection
+        { spTag = nameStem <> "Projection",
+          spWitness = lowerFirst nameStem <> "Witness",
+          spPointer = pointer,
+          spOwner = sdHaskell root,
+          spResult = result,
+          spCanonical = sdCanonical root,
+          spBinding = sdBinding root,
+          spSelectors = selectors
+        }
+      where
+        pointer = T.concat ["/" <> escapePointer key | key <- keys]
+        nameStem = projectionNameStem (sdName root) pointer
+
+projectionScalar :: ResolvedTypeExpr -> Maybe Text
+projectionScalar = \case
+  RText -> Just "Text"
+  RInt -> Just "Int"
+  RInteger -> Just "Integer"
+  RBool -> Just "Bool"
+  RTime -> Just "UTCTime"
+  RNatural -> Just "Natural"
+  RJson -> Nothing
+  ROptional {} -> Nothing
+  RList {} -> Nothing
+  RMap {} -> Nothing
+  RRef {} -> Nothing
+
+escapePointer :: Text -> Text
+escapePointer = T.replace "/" "~1" . T.replace "~" "~0"
+
+projectionNameStem :: Name -> Text -> Text
+projectionNameStem owner pointer =
+  pascal owner
+    <> T.concat
+      [ normaliseAliasPart (unescapePointer segment)
+      | segment <- filter (not . T.null) (T.splitOn "/" pointer)
+      ]
+
+-- | Add a stable digest only when two distinct wire paths normalize to the
+-- same Haskell name. Digest collisions receive a deterministic ordinal, so the
+-- emitter never produces duplicate declarations even in that unlikely case.
+allocateProjectionNames :: [StructuralProjection] -> [StructuralProjection]
+allocateProjectionNames specs = concatMap allocateGroup groups
+  where
+    groups = groupBy (\left right -> spTag left == spTag right) (sortOn spTag specs)
+    allocateGroup [spec] = [spec]
+    allocateGroup collided = reverse named
+      where
+        ordered = sortOn projectionIdentity collided
+        digest spec = T.take 8 (fnv1a64 (projectionIdentity spec))
+        digestCounts = Map.fromListWith (+) [(digest spec, 1 :: Int) | spec <- ordered]
+        (_, named) = foldl allocate (Map.empty, []) ordered
+        allocate (seen, allocated) spec =
+          let shortDigest = digest spec
+              occurrence = Map.findWithDefault 0 shortDigest seen + 1
+              suffix =
+                shortDigest
+                  <> if Map.findWithDefault 0 shortDigest digestCounts == 1
+                    then ""
+                    else tshow' occurrence
+           in (Map.insert shortDigest occurrence seen, renameWithSuffix suffix spec : allocated)
+    projectionIdentity spec = unCanonicalTypeId (spCanonical spec) <> "#" <> spPointer spec
+    renameWithSuffix suffix spec =
+      spec
+        { spTag = nameStem <> suffix <> "Projection",
+          spWitness = lowerFirst nameStem <> suffix <> "Witness"
+        }
+      where
+        nameStem = fromMaybe (spTag spec) (T.stripSuffix "Projection" (spTag spec))
+
+projectionWitnessName :: TypeGraph -> MappedKey -> Text -> Maybe Text
+projectionWitnessName graph owner pointer = do
+  ResolvedStructural declaration _ <- Map.lookup owner (tgDeclarations graph)
+  spWitness
+    <$> find
+      (\spec -> spCanonical spec == sdCanonical declaration && spPointer spec == pointer)
+      (projectionSpecs graph)
+
+emitStructuralProjections :: Context -> TypeGraph -> Text
+emitStructuralProjections ctx graph =
+  nl $
+    [ "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE TypeApplications #-}",
+      "{-# LANGUAGE TypeFamilies #-}",
+      generatedBanner,
+      "-- Equality witnesses are emitted for Text, Int, Bool, Natural, and UTCTime.",
+      "-- Int, Natural, and UTCTime belong to Keiki's ordered subset.",
+      "module " <> moduleName,
+      "  ( " <> T.intercalate "\n  , " (map spWitness specs),
+      "  ) where",
+      "",
+      "import Data.Text (Text)",
+      "import Data.Time (UTCTime)",
+      "import Numeric.Natural (Natural)",
+      "import Keiro.Codec.Structural (bindingToShape)",
+      "import Keiki.Core (FieldProjection (..), FieldWitness, fieldWitness)"
+    ]
+      <> map ("import " <>) imports
+      <> concatMap renderProjection specs
+  where
+    moduleName = structuralProjectionModule ctx
+    specs = map (resolveProjectionModules ctx) (projectionSpecs graph)
+    imports =
+      sort . nub $
+        [hsModule (spOwner spec) <> " qualified" | spec <- specs]
+          <> [qualifiedModule (spBinding spec) <> " qualified" | spec <- specs]
+          <> [shapeModuleName <> " qualified" | spec <- specs, (shapeModuleName, _) <- spSelectors spec]
+    renderProjection spec =
+      [ "",
+        "data " <> spTag spec,
+        "",
+        "instance FieldProjection " <> spTag spec <> " where",
+        "  type FieldName " <> spTag spec <> " = " <> tshow (spPointer spec),
+        "  type FieldOwner " <> spTag spec <> " = " <> renderHaskellSource (spOwner spec),
+        "  type FieldResult " <> spTag spec <> " = " <> spResult spec,
+        "  fieldShapeId _ = " <> tshow (unCanonicalTypeId (spCanonical spec)),
+        "  projectFieldValue _ owner = " <> renderGetter spec,
+        "",
+        spWitness spec <> " :: FieldWitness " <> spTag spec,
+        spWitness spec <> " = fieldWitness @" <> spTag spec
+      ]
+    renderGetter spec =
+      foldl
+        (\value (shapeModuleName, selector) -> shapeModuleName <> "." <> selector <> " (" <> value <> ")")
+        ("bindingToShape " <> unQualifiedValueName (spBinding spec) <> " owner")
+        (spSelectors spec)
+
+resolveProjectionModules :: Context -> StructuralProjection -> StructuralProjection
+resolveProjectionModules ctx spec =
+  spec
+    { spSelectors =
+        [ (replaceModule marker, selector)
+        | (marker, selector) <- spSelectors spec
+        ]
+    }
+  where
+    replaceModule marker
+      | Just name <- T.stripPrefix "__SHAPE__." marker = structuralShapeModule ctx name
+      | otherwise = structuralShapeModule ctx (lastSegment marker)
+
+qualifiedModule :: QualifiedValueName -> Text
+qualifiedModule = fst . splitQualified . unQualifiedValueName
+
+renderHaskellSource :: HaskellSource -> Text
+renderHaskellSource source = hsModule source <> "." <> hsType source
+
+splitQualified :: Text -> (Text, Text)
+splitQualified value =
+  let (prefix, name) = T.breakOnEnd "." value
+   in (T.dropEnd 1 prefix, name)
+
+lastSegment :: Text -> Text
+lastSegment = snd . T.breakOnEnd "."
+
+-- | Emit all modules for one aggregate. The 'Spec' is needed for the shared
+-- id\/enum declarations.
+scaffoldAggregate :: Context -> Spec -> Aggregate -> [ScaffoldModule]
+scaffoldAggregate ctx spec = scaffoldAggregateForService ctx (legacyCheckedService spec)
+
+-- | Emit all modules for one aggregate after selecting the effective semantic
+-- contract. This is the normal source/workspace generation entry point.
+scaffoldAggregateForService :: Context -> CheckedService -> Aggregate -> [ScaffoldModule]
+scaffoldAggregateForService ctx service agg =
+  [ genModule a "Domain" (emitDomain a),
+    genModule a "Codec" (emitCodec a)
+  ]
+    ++ ( if hasVersion2Ownership a
+           then
+             [ genModule a "Transducer" (emitGeneratedTransducer a),
+               genModule a "BehaviorContract" (emitBehaviorContract a),
+               behaviorHoleModule a
+             ]
+           else []
+       )
+    ++ [ genModule a "EventStream" (emitEventStream a),
+         genModule a "Projection" (emitProjection a)
+       ]
+    ++ [holeModule a (emitHoles a) | aggregateNeedsHoleModule a]
+  where
+    a = resolveAggForService ctx service agg
+
+aggregateNeedsHoleModule :: Agg -> Bool
+aggregateNeedsHoleModule aggregate
+  | hasVersion2Ownership aggregate = not (null (version2HoleExports aggregate))
+  | otherwise = True
+
+-- | The generated behavioral contract is deliberately separate from both the
+-- authoritative transducer and the create-once witness list.  Regeneration can
+-- replace this module freely while stale textual keys in @BehaviorHoles@ keep
+-- compiling and are reported by reconciliation.
+emitBehaviorContract :: Agg -> Text
+emitBehaviorContract aggregate =
+  nl $
+    [ "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE OverloadedLabels #-}",
+      "{-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}",
+      generatedBanner,
+      "module " <> aGenPrefix aggregate <> ".BehaviorContract where",
+      "",
+      "import " <> aGenPrefix aggregate <> ".Codec (encode" <> name <> "Event, parse" <> name <> "Event, " <> valueStem <> "Codec)",
+      "import " <> aGenPrefix aggregate <> ".Domain",
+      "import " <> aGenPrefix aggregate <> ".Transducer (" <> valueStem <> "Transducer)",
+      "import Data.Aeson (ToJSON (..), object, (.=))",
+      "import Data.List (sortOn)",
+      "import Data.List.NonEmpty (NonEmpty)",
+      "import Data.List.NonEmpty qualified as NonEmpty",
+      "import Data.Map.Strict qualified as Map",
+      "import Data.Text (Text)",
+      "import Data.Text qualified as T",
+      "import Keiki.Core qualified as K (EdgeMode (..), EdgeRef (..), RegFile, ReplayAttribution (..), ReplayEventSpan (..), ReplaySuccess (..), StepFailure (..), StepSuccess (..), applyEventsDetailedEither, stepDetailedEither, (!))",
+      "import Keiro.Codec qualified as Codec (Codec (eventType), EventType (..))",
+      "",
+      "newtype BehaviorKey = BehaviorKey { unBehaviorKey :: Text }",
+      "  deriving stock (Eq, Ord, Show)",
+      "",
+      "data ObligationKind = LiveTransition | RequiredRejection | ReplayTransition",
+      "  deriving stock (Eq, Ord, Show)",
+      "",
+      "data EvidenceLevel = GeneratedAuthoritative | HoleWitnessed | LegacyRuntimeWitness",
+      "  deriving stock (Eq, Ord, Show)",
+      "",
+      "data GuardCoverage = GuardTotal | GuardPartial | GuardUnknown | GuardNotApplicable",
+      "  deriving stock (Eq, Ord, Show)",
+      "",
+      "data BehaviorRequirement = BehaviorRequirement",
+      "  { requirementKey :: !BehaviorKey",
+      "  , requirementKind :: !ObligationKind",
+      "  , requirementEvidence :: !EvidenceLevel",
+      "  , requirementGuardCoverage :: !GuardCoverage",
+      "  , requirementSource :: !" <> aVertexType aggregate,
+      "  , requirementCommandName :: !Text",
+      "  , requirementExpectedEdge :: !(Maybe (K.EdgeRef " <> aVertexType aggregate <> "))",
+      "  , requirementTarget :: !(Maybe " <> aVertexType aggregate <> ")",
+      "  , requirementEventKinds :: ![Text]",
+      "  , requirementLine :: !Int",
+      "  }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "data RejectionClass = RejectNoOutgoingEdges | RejectNoMatchingEdge",
+      "  deriving stock (Eq, Show)",
+      "",
+      "data LiveExpectation",
+      "  = Emits (NonEmpty " <> name <> "Event)",
+      "  | Rejects RejectionClass",
+      "  | NoOp",
+      "  deriving stock (Eq, Show)",
+      "",
+      "data BehaviorWitness",
+      "  = Pending BehaviorKey",
+      "  | LiveWitness",
+      "      { witnessKey :: BehaviorKey",
+      "      , witnessHistory :: [" <> name <> "Event]",
+      "      , witnessCommand :: " <> name <> "Command",
+      "      , witnessExpected :: LiveExpectation",
+      "      }",
+      "  | ReplayWitness",
+      "      { witnessKey :: BehaviorKey",
+      "      , witnessHistoryPrefix :: [" <> name <> "Event]",
+      "      , witnessObservedChunk :: [" <> name <> "Event]",
+      "      }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "data BehaviorFailure = BehaviorFailure",
+      "  { failureKey :: !BehaviorKey",
+      "  , failureCode :: !Text",
+      "  , failureDetail :: !Text",
+      "  }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "instance ToJSON BehaviorFailure where",
+      "  toJSON failure = object",
+      "    [ \"key\" .= unBehaviorKey (failureKey failure)",
+      "    , \"code\" .= failureCode failure",
+      "    , \"detail\" .= failureDetail failure",
+      "    ]",
+      "",
+      "data BehaviorConformanceReport = BehaviorConformanceReport",
+      "  { reportRequired :: ![BehaviorKey]",
+      "  , reportFilled :: ![BehaviorKey]",
+      "  , reportPending :: ![BehaviorKey]",
+      "  , reportMissing :: ![BehaviorKey]",
+      "  , reportDuplicate :: ![BehaviorKey]",
+      "  , reportStale :: ![BehaviorKey]",
+      "  , reportFailed :: ![BehaviorFailure]",
+      "  , reportVerified :: ![BehaviorKey]",
+      "  , reportUnverified :: ![BehaviorKey]",
+      "  }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "instance ToJSON BehaviorConformanceReport where",
+      "  toJSON report = object",
+      "    [ \"schema\" .= (\"keiro/behavior-conformance/1\" :: Text)",
+      "    , \"required\" .= keyTexts (reportRequired report)",
+      "    , \"filled\" .= keyTexts (reportFilled report)",
+      "    , \"pending\" .= keyTexts (reportPending report)",
+      "    , \"missing\" .= keyTexts (reportMissing report)",
+      "    , \"duplicate\" .= keyTexts (reportDuplicate report)",
+      "    , \"stale\" .= keyTexts (reportStale report)",
+      "    , \"failed\" .= reportFailed report",
+      "    , \"verified\" .= keyTexts (reportVerified report)",
+      "    , \"unverified\" .= keyTexts (reportUnverified report)",
+      "    ]",
+      "",
+      "behaviorRequirements :: [BehaviorRequirement]",
+      "behaviorRequirements ="
+    ]
+      <> renderBehaviorRequirementList aggregate
+      <> [ "",
+           "behaviorCoverageReport :: [BehaviorWitness] -> BehaviorConformanceReport",
+           "behaviorCoverageReport witnesses =",
+           "  BehaviorConformanceReport",
+           "    { reportRequired = sortedKeys (Map.keys requiredByKey)",
+           "    , reportFilled = sortedKeys [key | (key, [witness]) <- Map.toList witnessGroups, Map.member key requiredByKey, not (isPending witness)]",
+           "    , reportPending = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, Map.member key requiredByKey, any isPending rows]",
+           "    , reportMissing = sortedKeys [key | key <- Map.keys requiredByKey, Map.notMember key witnessGroups]",
+           "    , reportDuplicate = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, length rows > 1]",
+           "    , reportStale = sortedKeys [key | key <- Map.keys witnessGroups, Map.notMember key requiredByKey]",
+           "    , reportFailed = sortOn (unBehaviorKey . failureKey) failures",
+           "    , reportVerified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, proofStrength requirement]",
+           "    , reportUnverified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, not (proofStrength requirement)]",
+           "    }",
+           " where",
+           "  requiredByKey = Map.fromList [(requirementKey requirement, requirement) | requirement <- behaviorRequirements]",
+           "  witnessGroups = Map.fromListWith (flip (<>)) [(behaviorWitnessKey witness, [witness]) | witness <- witnesses]",
+           "  executions =",
+           "    [ (requirement, runWitness requirement witness)",
+           "    | (key, [witness]) <- Map.toList witnessGroups",
+           "    , not (isPending witness)",
+           "    , Just requirement <- [Map.lookup key requiredByKey]",
+           "    ]",
+           "  failures = [failure | (_, Left failure) <- executions]",
+           "",
+           "behaviorConformancePassed :: BehaviorConformanceReport -> Bool",
+           "behaviorConformancePassed = behaviorConformancePassedWith False",
+           "",
+           "behaviorConformancePassedWith :: Bool -> BehaviorConformanceReport -> Bool",
+           "behaviorConformancePassedWith failOnUnverified report =",
+           "  null (reportPending report)",
+           "    && null (reportMissing report)",
+           "    && null (reportDuplicate report)",
+           "    && null (reportStale report)",
+           "    && null (reportFailed report)",
+           "    && (not failOnUnverified || null (reportUnverified report))",
+           "",
+           "renderBehaviorConformanceText :: BehaviorConformanceReport -> Text",
+           "renderBehaviorConformanceText report = T.unlines",
+           "  [ \"behavior conformance: " <> name <> "\"",
+           "  , \"schema: keiro/behavior-conformance/1\"",
+           "  , countLine \"required\" (reportRequired report)",
+           "  , countLine \"filled\" (reportFilled report)",
+           "  , countLine \"pending\" (reportPending report)",
+           "  , countLine \"missing\" (reportMissing report)",
+           "  , countLine \"duplicate\" (reportDuplicate report)",
+           "  , countLine \"stale\" (reportStale report)",
+           "  , \"failed: \" <> tshow (length (reportFailed report))",
+           "  , countLine \"verified\" (reportVerified report)",
+           "  , countLine \"unverified\" (reportUnverified report)",
+           "  ] <> T.unlines [\"FAIL \" <> unBehaviorKey (failureKey failure) <> \" [\" <> failureCode failure <> \"] \" <> failureDetail failure | failure <- reportFailed report]",
+           "",
+           "runWitness :: BehaviorRequirement -> BehaviorWitness -> Either BehaviorFailure ()",
+           "runWitness requirement witness = case witness of",
+           "  Pending _ -> failure requirement \"pending\" \"witness is still Pending\"",
+           "  LiveWitness _ history command expectation -> runLive requirement history command expectation",
+           "  ReplayWitness _ prefix chunk -> runReplay requirement prefix chunk",
+           "",
+           "runLive :: BehaviorRequirement -> [" <> name <> "Event] -> " <> name <> "Command -> LiveExpectation -> Either BehaviorFailure ()",
+           "runLive requirement history command expectation = do",
+           "  settled <- settleHistory requirement \"history\" history",
+           "  ensure requirement (K.replaySuccessState settled == requirementSource requirement) \"history-wrong-source\" \"history does not settle at the required source vertex\"",
+           "  ensure requirement (commandKind command == requirementCommandName requirement) \"command-mismatch\" \"witness command constructor does not match the required state/command cell\"",
+           "  case requirementKind requirement of",
+           "    ReplayTransition -> failure requirement \"witness-kind\" \"a replay-only requirement needs ReplayWitness\"",
+           "    RequiredRejection -> runRejection requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation",
+           "    LiveTransition -> runAcceptance requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation",
+           "",
+           "runRejection requirement seed command expectation = case expectation of",
+           "  Emits _ -> failure requirement \"expectation-kind\" \"a rejection requirement cannot expect emitted events\"",
+           "  NoOp -> failure requirement \"expectation-kind\" \"a rejection requirement cannot expect an accepted no-op\"",
+           "  Rejects expectedClass -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
+           "    Left K.NoOutgoingEdges {} -> ensure requirement (expectedClass == RejectNoOutgoingEdges) \"rejection-class\" \"expected NoMatchingEdge but runtime returned NoOutgoingEdges\"",
+           "    Left K.NoMatchingEdge {} -> ensure requirement (expectedClass == RejectNoMatchingEdge) \"rejection-class\" \"expected NoOutgoingEdges but runtime returned NoMatchingEdge\"",
+           "    Left K.AmbiguousEdges {} -> failure requirement \"ambiguous-edges\" \"AmbiguousEdges can never satisfy a rejection witness\"",
+           "    Right _ -> failure requirement \"unexpected-acceptance\" \"runtime accepted a command required to reject\"",
+           "",
+           "runAcceptance requirement seed command expectation = case expectation of",
+           "  Rejects _ -> failure requirement \"expectation-kind\" \"a live-transition requirement needs Emits or NoOp\"",
+           "  NoOp -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
+           "    Left stepFailure -> failure requirement \"unexpected-rejection\" (tshow stepFailure)",
+           "    Right success -> do",
+           "      checkAcceptedEnvelope requirement success",
+           "      ensure requirement (null (K.stepSuccessOutputs success)) \"noop-emitted\" \"NoOp emitted one or more events\"",
+           "      ensure requirement (K.stepSuccessState success == fst seed) \"noop-vertex-change\" \"NoOp changed the control vertex\"",
+           "      ensure requirement (regsEqual (K.stepSuccessRegs success) (snd seed)) \"noop-register-change\" \"NoOp changed one or more registers\"",
+           "  Emits expectedEvents -> case K.stepDetailedEither " <> valueStem <> "Transducer seed command of",
+           "    Left stepFailure -> failure requirement \"unexpected-rejection\" (tshow stepFailure)",
+           "    Right success -> do",
+           "      checkAcceptedEnvelope requirement success",
+           "      let expected = NonEmpty.toList expectedEvents",
+           "          actual = K.stepSuccessOutputs success",
+           "      ensure requirement (actual == expected) \"event-value-mismatch\" \"runtime event values differ from the exact witness expectation\"",
+           "      ensure requirement (map eventKind actual == requirementEventKinds requirement) \"event-envelope-mismatch\" \"runtime event kinds differ from the declared ordered envelope\"",
+           "      decoded <- either (failure requirement \"emitted-codec-decode\") Right (decodeEvents actual)",
+           "      replayed <- case K.applyEventsDetailedEither " <> valueStem <> "Transducer seed decoded of",
+           "        Left replayFailure -> failure requirement \"emitted-replay-failed\" (tshow replayFailure)",
+           "        Right replaySuccess -> Right replaySuccess",
+           "      ensure requirement (K.replaySuccessState replayed == K.stepSuccessState success) \"forward-replay-vertex\" \"decoded emissions replay to a different vertex\"",
+           "      ensure requirement (regsEqual (K.replaySuccessRegs replayed) (K.stepSuccessRegs success)) \"forward-replay-registers\" \"decoded emissions replay to different registers\"",
+           "      checkSingleAttribution requirement K.Live (length decoded) (K.replaySuccessTrace replayed)",
+           "",
+           "checkAcceptedEnvelope requirement success = do",
+           "  ensure requirement (K.stepSuccessMode success == K.Live) \"forward-mode\" \"forward execution selected a non-live edge\"",
+           "  ensure requirement (Just (K.stepSuccessEdge success) == requirementExpectedEdge requirement) \"edge-attribution\" \"runtime selected a different guarded sibling\"",
+           "  ensure requirement (Just (K.stepSuccessState success) == requirementTarget requirement) \"target-mismatch\" \"runtime reached a different target vertex\"",
+           "",
+           "runReplay :: BehaviorRequirement -> [" <> name <> "Event] -> [" <> name <> "Event] -> Either BehaviorFailure ()",
+           "runReplay requirement prefix chunk = case requirementKind requirement of",
+           "  ReplayTransition -> do",
+           "    settled <- settleHistory requirement \"history-prefix\" prefix",
+           "    ensure requirement (K.replaySuccessState settled == requirementSource requirement) \"history-wrong-source\" \"history prefix does not settle at the replay edge source\"",
+           "    ensure requirement (not (null chunk)) \"empty-replay-chunk\" \"a replay-only edge has no observable empty chunk\"",
+           "    decoded <- either (failure requirement \"replay-chunk-codec-decode\") Right (decodeEvents chunk)",
+           "    replayed <- case K.applyEventsDetailedEither " <> valueStem <> "Transducer (K.replaySuccessState settled, K.replaySuccessRegs settled) decoded of",
+           "      Left replayFailure -> failure requirement \"replay-chunk-failed\" (tshow replayFailure)",
+           "      Right replaySuccess -> Right replaySuccess",
+           "    ensure requirement (Just (K.replaySuccessState replayed) == requirementTarget requirement) \"target-mismatch\" \"replay chunk reached a different target vertex\"",
+           "    checkSingleAttribution requirement K.ReplayOnly (length decoded) (K.replaySuccessTrace replayed)",
+           "  _ -> failure requirement \"witness-kind\" \"ReplayWitness supplied for a non-replay requirement\"",
+           "",
+           "checkSingleAttribution requirement expectedMode eventCount trace = case trace of",
+           "  [attribution] -> do",
+           "    ensure requirement (Just (K.replayAttributionEdge attribution) == requirementExpectedEdge requirement) \"replay-edge-attribution\" \"replay selected a different edge\"",
+           "    ensure requirement (K.replayAttributionMode attribution == expectedMode) \"replay-mode-attribution\" \"replay selected the wrong live/replay-only phase\"",
+           "    ensure requirement (K.replayAttributionSource attribution == requirementSource requirement) \"replay-source-attribution\" \"replay attribution starts at the wrong source\"",
+           "    ensure requirement (Just (K.replayAttributionTarget attribution) == requirementTarget requirement) \"replay-target-attribution\" \"replay attribution ends at the wrong target\"",
+           "    ensure requirement (K.replayAttributionSpan attribution == K.ReplayEventSpan 0 eventCount) \"replay-span-attribution\" \"replay attribution did not consume the exact chunk\"",
+           "  _ -> failure requirement \"replay-trace-cardinality\" \"expected exactly one completed-edge attribution\"",
+           "",
+           "settleHistory requirement label history = do",
+           "  decoded <- either (failure requirement (label <> \"-codec-decode\")) Right (decodeEvents history)",
+           "  case K.applyEventsDetailedEither " <> valueStem <> "Transducer (" <> initialVertex aggregate <> ", initial" <> name <> "Regs) decoded of",
+           "    Left replayFailure -> failure requirement (label <> \"-replay-failed\") (tshow replayFailure)",
+           "    Right replaySuccess -> Right replaySuccess",
+           "",
+           "decodeEvents :: [" <> name <> "Event] -> Either Text [" <> name <> "Event]",
+           "decodeEvents = traverse (\\event -> parse" <> name <> "Event (Codec.eventType " <> valueStem <> "Codec event) (encode" <> name <> "Event event))"
+         ]
+      <> renderCommandKind aggregate
+      <> [ "",
+           "eventKind event = case Codec.eventType " <> valueStem <> "Codec event of Codec.EventType tag -> tag",
+           "",
+           "regsEqual :: K.RegFile " <> name <> "Regs -> K.RegFile " <> name <> "Regs -> Bool",
+           regsEqualityExpression aggregate,
+           "",
+           "proofStrength requirement =",
+           "  requirementEvidence requirement == GeneratedAuthoritative",
+           "    && requirementGuardCoverage requirement `elem` [GuardTotal, GuardNotApplicable]",
+           "",
+           "behaviorWitnessKey witness = case witness of",
+           "  Pending key -> key",
+           "  LiveWitness { witnessKey = key } -> key",
+           "  ReplayWitness { witnessKey = key } -> key",
+           "",
+           "isPending Pending {} = True",
+           "isPending _ = False",
+           "",
+           "ensure requirement condition code detail = if condition then Right () else failure requirement code detail",
+           "failure requirement code detail = Left (BehaviorFailure (requirementKey requirement) code detail)",
+           "sortedKeys = sortOn unBehaviorKey",
+           "keyTexts = map unBehaviorKey",
+           "countLine label values = label <> \": \" <> tshow (length values)",
+           "tshow :: Show value => value -> Text",
+           "tshow = T.pack . show"
+         ]
+  where
+    name = aName aggregate
+    valueStem = lowerFirst name
+
+renderCommandKind :: Agg -> [Text]
+renderCommandKind aggregate = case aCommands aggregate of
+  [] -> ["", "commandKind _ = \"\""]
+  commands ->
+    [ "",
+      "commandKind command = case command of"
+    ]
+      <> ["  " <> rcName command <> " _ -> " <> tshow (rcName command) | command <- commands]
+
+renderBehaviorRequirementList :: Agg -> [Text]
+renderBehaviorRequirementList aggregate =
+  case behaviorRequirementsFor aggregate of
+    [] -> ["  []"]
+    requirements ->
+      [ (if index == (0 :: Int) then "  [ " else "  , ") <> render requirement
+      | (index, requirement) <- zip [0 ..] requirements
+      ]
+        <> ["  ]"]
+  where
+    render requirement =
+      "BehaviorRequirement "
+        <> keyExpr requirement
+        <> " "
+        <> T.pack (show (Behavior.requirementKind requirement))
+        <> " "
+        <> T.pack (show (Behavior.requirementEvidence requirement))
+        <> " "
+        <> T.pack (show (Behavior.requirementGuardCoverage requirement))
+        <> " "
+        <> vertexCtor aggregate (Behavior.requirementSource requirement)
+        <> " "
+        <> tshow (Behavior.requirementCommand requirement)
+        <> " "
+        <> edgeExpr aggregate requirement
+        <> " "
+        <> maybe "Nothing" (\target -> "(Just " <> vertexCtor aggregate target <> ")") (Behavior.requirementTarget requirement)
+        <> " "
+        <> renderBehaviorTextList (Behavior.requirementEvents requirement)
+        <> " "
+        <> tshow' (unLoc (Behavior.requirementLocation requirement))
+    keyExpr requirement = "(BehaviorKey " <> tshow (Behavior.unBehaviorKey (Behavior.requirementKey requirement)) <> ")"
+
+edgeExpr :: Agg -> Behavior.BehaviorRequirement -> Text
+edgeExpr aggregate requirement = case Behavior.requirementKind requirement of
+  Behavior.RequiredRejection -> "Nothing"
+  _ -> case behaviorEdgeIndex aggregate requirement of
+    Nothing -> error ("required behavior transition missing from resolved aggregate: " <> T.unpack (Behavior.requirementCanonical requirement))
+    Just edgeIndex ->
+      "(Just (K.EdgeRef "
+        <> vertexCtor aggregate (Behavior.requirementSource requirement)
+        <> " "
+        <> tshow' edgeIndex
+        <> "))"
+
+behaviorEdgeIndex :: Agg -> Behavior.BehaviorRequirement -> Maybe Int
+behaviorEdgeIndex aggregate requirement =
+  findIndex
+    matches
+    [ transition
+    | transition <- aTransitions aggregate,
+      tSource transition == Behavior.requirementSource requirement
+    ]
+  where
+    matches transition =
+      unLoc (tLoc transition) == unLoc (Behavior.requirementLocation requirement)
+        && tCommand transition == Behavior.requirementCommand requirement
+
+behaviorRequirementsFor :: Agg -> [Behavior.BehaviorRequirement]
+behaviorRequirementsFor aggregate =
+  case Behavior.deriveAggregateBehaviorRequirements (aSpec aggregate) (aAggregate aggregate) of
+    Left derivationError -> error ("validated aggregate failed behavior derivation: " <> show derivationError)
+    Right requirements -> sortOn Behavior.requirementKey requirements
+
+renderBehaviorTextList :: [Text] -> Text
+renderBehaviorTextList values = "[" <> T.intercalate ", " (map tshow values) <> "]"
+
+regsEqualityExpression :: Agg -> Text
+regsEqualityExpression aggregate = case aRegs aggregate of
+  [] -> "regsEqual _ _ = True"
+  registers ->
+    "regsEqual left right = "
+      <> T.intercalate
+        " && "
+        [ "(left K.! #" <> rrName register <> ") == (right K.! #" <> rrName register <> ")"
+        | register <- registers
+        ]
+
+behaviorHoleModule :: Agg -> ScaffoldModule
+behaviorHoleModule aggregate =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" (aHolePrefix aggregate) <> "/BehaviorHoles.hs"),
+      moduleText = emitBehaviorHoles aggregate,
+      kind = HoleStub,
+      origin = nodeOrigin "aggregate behavior witnesses" (aName aggregate) (aLoc aggregate)
+    }
+
+emitBehaviorHoles :: Agg -> Text
+emitBehaviorHoles aggregate =
+  nl $
+    [ "-- Consumer-owned behavioral witnesses. Created once; never overwritten.",
+      "module " <> aHolePrefix aggregate <> ".BehaviorHoles (behaviorWitnesses) where",
+      "",
+      "import " <> aGenPrefix aggregate <> ".BehaviorContract",
+      "",
+      "behaviorWitnesses :: [BehaviorWitness]",
+      "behaviorWitnesses ="
+    ]
+      <> case behaviorRequirementsFor aggregate of
+        [] -> ["  []"]
+        requirements ->
+          [ (if index == (0 :: Int) then "  [ " else "  , ")
+              <> "Pending (BehaviorKey "
+              <> tshow (Behavior.unBehaviorKey (Behavior.requirementKey requirement))
+              <> ")"
+          | (index, requirement) <- zip [0 ..] requirements
+          ]
+            <> ["  ]"]
+
+-- | Emit the context-wide replay-audit target assembly.
+--
+-- There is one existential target per aggregate declaration. Process saga
+-- aggregates are ordinary aggregate nodes referenced by 'SagaRef', so they are
+-- included by the same single source of truth rather than being duplicated from
+-- the process declaration.
+scaffoldReplayAudit :: Context -> Spec -> [ScaffoldModule]
+scaffoldReplayAudit ctx spec
+  | null aggregates = []
+  | otherwise =
+      [ ScaffoldModule
+          { modulePath = T.unpack (T.replace "." "/" moduleName <> ".hs"),
+            moduleText = emitReplayAudit,
+            kind = Generated,
+            origin = "context " <> specContext spec <> " replay-audit assembly"
+          }
+      ]
+  where
+    aggregates = [aggregate | NAggregate aggregate <- specNodes spec]
+    moduleName = contextGeneratedPrefix ctx <> ".ReplayAudit"
+    contextGeneratedPrefix context = case placement context of
+      GeneratedPrefix -> rootPrefix context <> "Generated." <> ctxPascalOf context
+      CollocatedLeaf -> rootPrefix context <> ctxPascalOf context <> ".Generated"
+    emitReplayAudit =
+      nl $
+        [ "{-# LANGUAGE GADTs #-}",
+          generatedBanner,
+          "--",
+          "-- Deployment contract:",
+          "--   * replay-neutral diff: no data audit is required;",
+          "--   * affected diff: run AuditTargeted with the emitted affected set",
+          "--     against a production copy under the candidate binary;",
+          "--   * one-time runtime cutover: run AuditFull;",
+          "--   * any non-zero audit exit blocks deployment.",
+          "module " <> moduleName <> " (auditTargets) where",
+          ""
+        ]
+          ++ [ "import " <> genPrefixFor ctx (aggName aggregate) <> ".EventStream qualified as " <> aggName aggregate
+             | aggregate <- aggregates
+             ]
+          ++ [ "import Keiro.ReplayAudit (AuditTarget (..), SomeAuditTarget (..), streamInCategory)",
+               "import Keiro.Stream qualified as Stream",
+               "",
+               "auditTargets :: [SomeAuditTarget]",
+               "auditTargets ="
+             ]
+          ++ concat
+            [ [ if index == (0 :: Int) then "  [ SomeAuditTarget" else "  , SomeAuditTarget",
+                "      AuditTarget",
+                "        { eventStream = " <> aggregateName <> "." <> lowerFirst aggregateName <> "EventStream",
+                "        , category = Stream.categoryText " <> aggregateName <> "." <> lowerFirst aggregateName <> "Category",
+                "        , mkStream = streamInCategory (Stream.categoryText " <> aggregateName <> "." <> lowerFirst aggregateName <> "Category)",
+                "        }"
+              ]
+            | (index, aggregate) <- zip [0 ..] aggregates,
+              let aggregateName = aggName aggregate
+            ]
+          ++ ["  ]"]
+
+genModule :: Agg -> Text -> Text -> ScaffoldModule
+genModule a name body =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" (aGenPrefix a) <> "/" <> name <> ".hs"),
+      moduleText = body,
+      kind = Generated,
+      origin = nodeOrigin "aggregate" (aName a) (aLoc a)
+    }
+
+holeModule :: Agg -> Text -> ScaffoldModule
+holeModule a body =
+  ScaffoldModule
+    { modulePath = T.unpack (T.replace "." "/" (aHolePrefix a) <> "/" <> "Holes.hs"),
+      moduleText = body,
+      kind = HoleStub,
+      origin = nodeOrigin "aggregate" (aName a) (aLoc a)
+    }
+
+--------------------------------------------------------------------------------
+-- Integration contract (EP-4): a self-contained payload ADT + codec
+--------------------------------------------------------------------------------
+
+-- | Emit the deterministic, symbol-free contract layer: a payload ADT
+-- (per-event records), the topic constants, the @messageType@ discriminator, and a
+-- strict encode\/decode keyed by it. Self-contained (base\/text\/aeson), so it
+-- compiles standalone — the cross-service schema both producer and consumer agree
+-- on. No keiki symbolic operator (firewall holds).
+scaffoldContract :: Context -> ContractNode -> [ScaffoldModule]
+scaffoldContract ctx = scaffoldContractWithLanguage ctx (effectiveLanguageContract LegacyUnversioned)
+
+-- | Emit a contract under the checked service's released semantic contract.
+-- Language versions 1 through 3 retain the legacy Text representation; only
+-- runtime semantics 3 lowers declared TypeID fields to prefix-indexed KindIDs.
+scaffoldContractForService :: Context -> CheckedService -> ContractNode -> [ScaffoldModule]
+scaffoldContractForService ctx service = scaffoldContractWithLanguage ctx (checkedLanguageContract service)
+
+scaffoldContractWithLanguage :: Context -> EffectiveLanguageContract -> ContractNode -> [ScaffoldModule]
+scaffoldContractWithLanguage ctx languageContract c =
+  [ ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Contract.hs"),
+        moduleText = emitContractGen languageContract genPrefix c,
+        kind = Generated,
+        origin = nodeOrigin "contract" (ctrName c) (ctrLoc c)
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (pascal (ctrName c))
+
+emitContractGen :: EffectiveLanguageContract -> Text -> ContractNode -> Text
+emitContractGen languageContract genPrefix c =
+  ( nl $
+      pragmas
+        ++ ["" | hasTypedTypeIds]
+        ++ [generatedBanner]
+        ++ moduleHeader
+        ++ [ "",
+             "import Data.Aeson (Value, object, withObject, withText, (.:), (.=))",
+             aesonTypesImport
+           ]
+        ++ typedKindIdImports
+        ++ [ "import Data.Text (Text)",
+             "import qualified Data.Text as T"
+           ]
+        ++ ["import Keiro.Codec.IdDomain (parseKindIdV7Value)" | hasTypedTypeIds]
+        ++ [ "",
+             "-- topic constants"
+           ]
+        ++ topicConstants
+        ++ [ "",
+             "-- the closed payload set (discriminated by " <> tshow (ctrDiscriminator c) <> ")"
+           ]
+        ++ [emitPayloadAdt languageContract payloadTy (ctrEvents c)]
+        ++ [ "",
+             "messageTypeOf :: " <> payloadTy <> " -> Text",
+             "messageTypeOf = \\case"
+           ]
+        ++ ["  " <> ceName e <> " {} -> " <> tshow (ceName e) | e <- ctrEvents c]
+        ++ [ "",
+             "encode" <> payloadTy <> " :: " <> payloadTy <> " -> Value",
+             "encode" <> payloadTy <> " = \\case"
+           ]
+        ++ concatMap encodeArm (ctrEvents c)
+        ++ [ "",
+             "parse" <> payloadTy <> " :: Value -> Either Text " <> payloadTy,
+             "parse" <> payloadTy <> " = mapLeftText . parseEither (withObject " <> tshow payloadTy <> " go)",
+             "  where",
+             "    go o = do",
+             "      kind <- explicitParseField (withText " <> tshow (ctrDiscriminator c) <> " validateMessageType) o " <> tshow (ctrDiscriminator c),
+             "      case kind of"
+           ]
+        ++ concatMap decodeArm (ctrEvents c)
+        ++ [ "        _ -> fail \"validated message type was not handled\"",
+             "",
+             "mapLeftText :: Either String b -> Either Text b",
+             "mapLeftText = either (Left . T.pack) Right",
+             "",
+             "validateMessageType :: Text -> Parser Text",
+             "validateMessageType kind",
+             "  | kind `elem` " <> renderTextList (map ceName (ctrEvents c)) <> " = pure kind",
+             "  | otherwise = " <> renderUnknownFailure "message type" "kind" (map ceName (ctrEvents c))
+           ]
+  )
+    <> if hasTypedTypeIds then "\n" else ""
+  where
+    payloadTy = pascal (ctrName c) <> "Payload"
+    hasTypedTypeIds = any (any (isTypedTypeId . cfType) . ceFields) (ctrEvents c)
+    pragmas =
+      ["{-# LANGUAGE DataKinds #-}" | hasTypedTypeIds]
+        ++ [ "{-# LANGUAGE DuplicateRecordFields #-}",
+             "{-# LANGUAGE OverloadedRecordDot #-}"
+           ]
+        ++ ["{-# LANGUAGE TypeApplications #-}" | hasTypedTypeIds]
+    typedKindIdImports
+      | hasTypedTypeIds = ["import Data.KindID (KindID)", "import qualified Data.KindID as KindID"]
+      | otherwise = []
+    moduleHeader =
+      [ "module " <> genPrefix <> ".Contract",
+        "  ( " <> payloadTy <> " (..)"
+      ]
+        ++ ["  , " <> ceName event <> "Data (..)" | event <- ctrEvents c]
+        ++ ["  , " <> lowerFirst alias <> "Topic" | (alias, _) <- ctrTopics c]
+        ++ [ "  , messageTypeOf",
+             "  , encode" <> payloadTy,
+             "  , parse" <> payloadTy,
+             "  ) where"
+           ]
+    topicConstants
+      | hasTypedTypeIds =
+          [ T.intercalate
+              "\n\n"
+              [lowerFirst alias <> "Topic :: Text\n" <> lowerFirst alias <> "Topic = " <> tshow topic | (alias, topic) <- ctrTopics c]
+          ]
+      | otherwise = [lowerFirst alias <> "Topic :: Text\n" <> lowerFirst alias <> "Topic = " <> tshow topic | (alias, topic) <- ctrTopics c]
+    isTypedTypeId (CTypeId prefix) = isJust (contractIdDomainContractFor languageContract prefix)
+    isTypedTypeId _ = False
+    aesonTypesImport = "import Data.Aeson.Types (Parser, explicitParseField, parseEither)"
+    encodeArm e =
+      [ "  " <> ceName e <> " payload ->",
+        "    object"
+      ]
+        ++ objectEntriesFor ((tshow (ctrDiscriminator c) <> " .= (" <> tshow (ceName e) <> " :: Text)") : map encodeField (ceFields e))
+        ++ ["      ]"]
+    lead 0 kv = "      [ " <> kv
+    lead _ kv = "      , " <> kv
+    objectEntriesFor entries
+      | hasTypedTypeIds =
+          [ (if index == 0 then "      [ " else "        ")
+              <> entry
+              <> if index < length entries - 1 then "," else ""
+          | (index, entry) <- zip [(0 :: Int) ..] entries
+          ]
+      | otherwise = [lead index entry | (index, entry) <- zip [(0 :: Int) ..] entries]
+    decodeArm e =
+      ["        " <> tshow (ceName e) <> " ->"]
+        ++ case ceFields e of
+          [] -> ["          pure (" <> ceName e <> " " <> ceName e <> "Data)"]
+          fields ->
+            [ "          " <> ceName e,
+              "            <$> ( " <> ceName e <> "Data"
+            ]
+              ++ [ (if index == 0 then "                    <$> " else "                    <*> ") <> decodeField field
+                 | (index, field) <- zip [(0 :: Int) ..] fields
+                 ]
+              ++ ["                )"]
+    encodeField field =
+      tshow (cfName field)
+        <> " .= "
+        <> case cfType field of
+          CTypeId prefix
+            | isJust (contractIdDomainContractFor languageContract prefix) -> "KindID.toText payload." <> cfName field
+          _ -> "payload." <> cfName field
+    decodeField field = case cfType field of
+      CTypeId prefix
+        | isJust (contractIdDomainContractFor languageContract prefix) ->
+            "explicitParseField (parseKindIdV7Value @" <> tshow prefix <> ") o " <> tshow (cfName field)
+      _ -> "o .: " <> tshow (cfName field)
+
+emitPayloadAdt :: EffectiveLanguageContract -> Text -> [ContractEvent] -> Text
+emitPayloadAdt languageContract tyName events =
+  sectionsOf [map dataRecord events, [sumDecl]]
+  where
+    hasTypedTypeIds = any (any (isTypedTypeId . cfType) . ceFields) events
+    isTypedTypeId (CTypeId prefix) = isJust (contractIdDomainContractFor languageContract prefix)
+    isTypedTypeId _ = False
+    hsType CText = "Text"
+    hsType CInt = "Int"
+    hsType (CTypeId prefix)
+      | isJust (contractIdDomainContractFor languageContract prefix) = "(KindID " <> tshow prefix <> ")"
+      | otherwise = "Text"
+    dataRecord e =
+      "data "
+        <> ceName e
+        <> "Data = "
+        <> ceName e
+        <> (if hasTypedTypeIds then "Data {" else "Data { ")
+        <> T.intercalate ", " [cfName f <> " :: !" <> hsType (cfType f) | f <- ceFields e]
+        <> (if hasTypedTypeIds then "}\n  deriving stock (Eq, Show)" else " }\n  deriving stock (Eq, Show)")
+    arm e = ceName e <> " !" <> ceName e <> "Data"
+    sumDecl = case events of
+      [] -> "data " <> tyName <> " = " <> tyName <> "Empty\n  deriving stock (Eq, Show)"
+      (e : es) ->
+        nl
+          ( (if hasTypedTypeIds then ["data " <> tyName, "  = " <> arm e] else ["data " <> tyName <> " = " <> arm e])
+              ++ ["  | " <> arm e2 | e2 <- es]
+              ++ ["  deriving stock (Eq, Show)"]
+          )
+
+--------------------------------------------------------------------------------
+-- Integration intake (EP-4): inbox disposition vs the live Keiro.Inbox runtime
+--------------------------------------------------------------------------------
+
+-- | Emit the inbox node's deterministic disposition wiring compiled against the
+-- LIVE @Keiro.Inbox.Types@: the dedupe policy (a real 'InboxDedupePolicy') and a
+-- disposition function over the real @InboxResult@ (including handler
+-- failures). This pins the dangerous inversions
+-- (duplicate ⇒ ackOk, previouslyFailed ⇒ deadLetter) as compiled code over the
+-- runtime types. The complete declared classification table is also available
+-- to handler holes through a closed generated outcome type. Firewall holds (no
+-- keiki symbolic operator).
+scaffoldIntake :: Context -> IntakeNode -> [ScaffoldModule]
+scaffoldIntake ctx i =
+  [ ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Inbox.hs"),
+        moduleText = emitIntakeGen genPrefix i,
+        kind = Generated,
+        origin = nodeOrigin "intake" (inkName i) (inkLoc i)
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (pascal (inkName i))
+
+emitIntakeGen :: Text -> IntakeNode -> Text
+emitIntakeGen genPrefix i =
+  nl $
+    [ "{-# LANGUAGE OverloadedStrings #-}",
+      generatedBanner,
+      "module " <> genPrefix <> ".Inbox",
+      "  ( InboxFailure (..)",
+      "  , " <> outcomeType <> " (..)",
+      "  , " <> dispositionType <> " (..)",
+      "  , inboxDedupePolicy",
+      "  , inboxPersistence",
+      "  , inboxDispositionFor",
+      "  , inboxDisposition",
+      "  ) where",
+      "",
+      "import Data.Text (Text)",
+      "import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..), RetryDelay (..))",
+      "",
+      "-- The dedupe policy (hole-kind 4), lowered to the live InboxDedupePolicy.",
+      "inboxDedupePolicy :: InboxDedupePolicy",
+      "inboxDedupePolicy = " <> inkDedupePolicy i,
+      "",
+      "-- | Success-path envelope retention passed to runInboxTransactionWith.",
+      "-- Failures always retain their full operator-facing dead-letter envelope.",
+      "-- Dedupe-only success rows decode with an empty payload.",
+      "inboxPersistence :: InboxPersistence",
+      "inboxPersistence = " <> persistenceCtor (inkPersist i),
+      "",
+      "-- Runtime failure detail retained when the inbox wrapper reports a failed handler attempt.",
+      "data InboxFailure = InboxFailure",
+      "  { inboxFailureReason :: !Text",
+      "  , inboxFailureAttempt :: !(Maybe Int)",
+      "  }",
+      "  deriving stock (Eq, Show)",
+      "",
+      "-- Every classification named by the spec. Keeping this closed makes the",
+      "-- generated table exhaustive and gives handler holes typed inputs.",
+      "data " <> outcomeType,
+      "  = " <> T.intercalate "\n  | " outcomeConstructors,
+      "  deriving stock (Eq, Show)",
+      "",
+      "-- The service's declared acknowledgement decision, including its details.",
+      "data " <> dispositionType,
+      "  = InboxAccept",
+      "  | InboxRetryAfter !RetryDelay !(Maybe InboxFailure)",
+      "  | InboxDeadLetter !(Maybe Text) !(Maybe InboxFailure)",
+      "  deriving stock (Eq, Show)",
+      "",
+      "-- The complete disposition table (hole-kind 2).",
+      "inboxDispositionFor :: " <> outcomeType <> " -> " <> dispositionType,
+      "inboxDispositionFor outcome = case outcome of"
+    ]
+      ++ ["  " <> outcomeConstructor (drOutcome row) <> " -> " <> actionExpression (drAction row) | row <- inkDisposition i]
+      ++ [ "",
+           "-- Lower the LIVE Keiro.Inbox.Types.InboxResult without an open fallback.",
+           "inboxDisposition :: InboxResult a -> " <> dispositionType,
+           "inboxDisposition r = case r of",
+           "  InboxProcessed _ -> inboxDispositionFor " <> outcomeConstructor "processed",
+           "  InboxDuplicate -> inboxDispositionFor " <> outcomeConstructor "duplicate",
+           "  InboxInProgress -> inboxDispositionFor " <> outcomeConstructor "inProgress",
+           "  InboxPreviouslyFailed failureReason ->",
+           "    maybe (inboxDispositionFor " <> outcomeConstructor "previouslyFailed" <> ")",
+           "      (\\reason -> attachFailure (InboxFailure reason Nothing) (inboxDispositionFor " <> outcomeConstructor "previouslyFailed" <> "))",
+           "      failureReason",
+           "  InboxHandlerFailed reason attempts ->",
+           "    attachFailure (InboxFailure reason (Just attempts)) (inboxDispositionFor " <> outcomeConstructor "storeFailed" <> ")",
+           "",
+           "attachFailure :: InboxFailure -> " <> dispositionType <> " -> " <> dispositionType,
+           "attachFailure failure disposition = case disposition of",
+           "  InboxRetryAfter delay _ -> InboxRetryAfter delay (Just failure)",
+           "  InboxDeadLetter reason _ -> InboxDeadLetter reason (Just failure)",
+           "  InboxAccept -> InboxAccept"
+         ]
+  where
+    stem = pascal (inkName i)
+    outcomeType = stem <> "Outcome"
+    dispositionType = stem <> "Disposition"
+    outcomeConstructor = (stem <>) . pascal
+    outcomeConstructors = map (outcomeConstructor . drOutcome) (inkDisposition i)
+    actionExpression IAckOk = "InboxAccept"
+    actionExpression (IRetry win) = "InboxRetryAfter (RetryDelay " <> windowText win <> ") Nothing"
+    actionExpression (IDeadLetter mr) = "InboxDeadLetter " <> maybe "Nothing" (\reason -> "(Just " <> tshow reason <> ")") mr <> " Nothing"
+    persistenceCtor InkPersistFull = "PersistFullEnvelope"
+    persistenceCtor InkPersistDedupeOnly = "PersistDedupeOnly"
+
+--------------------------------------------------------------------------------
+-- Integration publisher (EP-4): config vs the live Keiro.Outbox runtime
+--------------------------------------------------------------------------------
+
+-- | Emit the publisher's at-least-once policy compiled against the LIVE
+-- @Keiro.Outbox.Types@: the ordering policy (a real 'OrderingPolicy'), the backoff
+-- curve (a real 'BackoffSchedule'), and the max-attempts ceiling. Firewall holds.
+scaffoldPublisher :: Context -> PublisherNode -> [ScaffoldModule]
+scaffoldPublisher ctx pb =
+  [ ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Publisher.hs"),
+        moduleText = emitPublisherGen genPrefix pb,
+        kind = Generated,
+        origin = nodeOrigin "publisher" (pubName pb) (pubLoc pb)
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (pascal (pubName pb))
+
+emitPublisherGen :: Text -> PublisherNode -> Text
+emitPublisherGen genPrefix pb =
+  nl
+    [ generatedBanner,
+      "module " <> genPrefix <> ".Publisher",
+      "  ( publisherOrdering",
+      "  , publisherBackoff",
+      "  , publisherMaxAttempts",
+      "  ) where",
+      "",
+      "import Keiro.Outbox.Types (BackoffSchedule (..), ExponentialBackoffOptions (..), OrderingPolicy (..))",
+      "",
+      "publisherOrdering :: OrderingPolicy",
+      "publisherOrdering = " <> pubOrdering pb,
+      "",
+      "publisherBackoff :: BackoffSchedule",
+      "publisherBackoff = " <> backoffExpr (pubBackoff pb),
+      "",
+      "publisherMaxAttempts :: Int",
+      "publisherMaxAttempts = " <> tshow' (pubMaxAttempts pb)
+    ]
+  where
+    backoffExpr b = case boKind b of
+      "constant" -> "ConstantBackoff " <> windowText (boWindow b)
+      "exponential" ->
+        "ExponentialBackoff ExponentialBackoffOptions { initial = "
+          <> windowText (boWindow b)
+          <> ", maxDelay = "
+          <> maybe "0" windowText (boMax b)
+          <> ", multiplier = "
+          <> fromMaybe "0" (boMultiplier b)
+          <> " }"
+      _ -> "error \"keiro-dsl: unlowerable backoff kind\""
+
+--------------------------------------------------------------------------------
+-- pgmq workqueue (EP-5): a self-contained Job payload record + codec
+--------------------------------------------------------------------------------
+
+-- | Emit the deterministic, symbol-free pgmq layer: the Job payload record, the
+-- field→wire-name JSON codec, and the captured physical\/dlq\/table name constants.
+-- Self-contained (base\/text\/aeson). The fan-out body and the raw-SQL dedup
+-- predicate are holes (not emitted). Firewall holds.
+scaffoldWorkqueue :: Context -> WorkqueueNode -> [ScaffoldModule]
+scaffoldWorkqueue ctx w =
+  [ ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Queue.hs"),
+        moduleText = emitWorkqueueGen genPrefix w,
+        kind = Generated,
+        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
+      },
+    ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/QueuePolicy.hs"),
+        moduleText = emitQueuePolicy genPrefix w,
+        kind = Generated,
+        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
+      },
+    ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/QueueCodec.hs"),
+        moduleText = emitQueueCodec genPrefix w,
+        kind = Generated,
+        origin = nodeOrigin "workqueue" (wqName w) (wqLoc w)
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (pascal (wqName w))
+
+emitWorkqueueGen :: Text -> WorkqueueNode -> Text
+emitWorkqueueGen genPrefix w =
+  nl $
+    [ "{-# LANGUAGE OverloadedRecordDot #-}",
+      generatedBanner,
+      "module " <> genPrefix <> ".Queue",
+      "  ( " <> payloadTy <> " (..)",
+      "  , encode" <> payloadTy,
+      "  , parse" <> payloadTy,
+      "  , queuePhysical, queueDlq, queueTable",
+      groupKeyExport,
+      "  ) where",
+      "",
+      "import Data.Aeson (Value, object, withObject, (.:), (.=))",
+      "import Data.Aeson.Types (parseEither)",
+      "import Data.Text (Text)",
+      "import qualified Data.Text as T",
+      "",
+      "queuePhysical, queueDlq, queueTable :: Text",
+      "queuePhysical = " <> tshow (wqPhysical w),
+      "queueDlq = " <> tshow (wqDlq w),
+      "queueTable = " <> tshow (wqTable w),
+      ""
+    ]
+      ++ groupKeyLines
+      ++ [ "data " <> payloadTy <> " = " <> payloadTy,
+           "  { " <> T.intercalate "\n  , " [wqfName f <> " :: !" <> hsType (wqfType f) | f <- wqPayload w],
+           "  }",
+           "  deriving stock (Eq, Show)",
+           "",
+           "encode" <> payloadTy <> " :: " <> payloadTy <> " -> Value",
+           "encode" <> payloadTy <> " p =",
+           "  object"
+         ]
+      ++ [lead i (tshow (wqfWire f) <> " .= p." <> wqfName f) | (i, f) <- zip [(0 :: Int) ..] (wqPayload w)]
+      ++ [ "    ]",
+           "",
+           "parse" <> payloadTy <> " :: Value -> Either Text " <> payloadTy,
+           "parse" <> payloadTy <> " = mapLeftText . parseEither (withObject " <> tshow payloadTy <> " go)",
+           "  where",
+           "    go o = " <> payloadTy <> fieldApps (wqPayload w),
+           "",
+           "mapLeftText :: Either String b -> Either Text b",
+           "mapLeftText = either (Left . T.pack) Right"
+         ]
+  where
+    payloadTy = wqPayloadName w
+    groupKeyExport = case wqGroupKey w of
+      Nothing -> ""
+      Just groupKey
+        | gkVia groupKey == "raw" -> "  , groupKeyField, groupKeyFor"
+        | otherwise -> "  , groupKeyField"
+    groupKeyLines = case wqGroupKey w of
+      Nothing -> []
+      Just groupKey -> common <> derivationLines groupKey
+        where
+          common =
+            [ "groupKeyField :: Text",
+              "groupKeyField = " <> tshow (gkField groupKey),
+              ""
+            ]
+          derivationLines key
+            | gkVia key == "raw" =
+                [ "groupKeyFor :: " <> payloadTy <> " -> Text",
+                  "groupKeyFor payload = payload." <> gkField key,
+                  ""
+                ]
+            | otherwise =
+                [ "-- Opaque group-key derivation '" <> gkVia key <> "' remains hand-owned.",
+                  "-- Captured fixture: " <> fromMaybe "<missing>" (gkFixture key),
+                  ""
+                ]
+    hsType "bool" = "Bool"
+    hsType "int" = "Int"
+    hsType _ = "Text"
+    lead 0 kv = "    [ " <> kv
+    lead _ kv = "    , " <> kv
+    fieldApps [] = ""
+    fieldApps fs = " <$> " <> T.intercalate " <*> " ["o .: " <> tshow (wqfWire f) | f <- fs]
+
+-- | Emit the versioned PGMQ envelope adapter.  The payload record remains
+-- symbol-free and dependency-light in Queue.hs; this runtime-facing module is
+-- the opt-in assembly point applications import into their Job values.
+emitQueueCodec :: Text -> WorkqueueNode -> Text
+emitQueueCodec genPrefix w =
+  nl
+    [ generatedBanner,
+      "-- | Versioned job payload envelope: @{\\\"v\\\",\\\"t\\\",\\\"data\\\"}@.",
+      "--",
+      "-- Deploy workers before producers when raising its schema version. Do not",
+      "-- adopt this codec on a non-empty bare-payload queue without draining it",
+      "-- (or supplying a transitional codec), or in-flight messages will",
+      "-- dead-letter. This is telemetry-neutral:",
+      "-- docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns",
+      "-- spans and acknowledgement vocabulary.",
+      "module " <> genPrefix <> ".QueueCodec (" <> stem <> "PayloadCodec, " <> stem <> "JobCodec) where",
+      "",
+      "import Data.List.NonEmpty (NonEmpty (..))",
+      "import Keiro.Codec (Codec (..), EventType (..))",
+      "import Keiro.PGMQ.Codec (JobCodec, keiroJobCodec)",
+      "import " <> genPrefix <> ".Queue (" <> payloadTy <> ", encode" <> payloadTy <> ", parse" <> payloadTy <> ")",
+      "",
+      stem <> "PayloadCodec :: Codec " <> payloadTy,
+      stem <> "PayloadCodec =",
+      "  Codec",
+      "    { eventTypes = EventType " <> tshow payloadTy <> " :| []",
+      "    , eventType = \\_ -> EventType " <> tshow payloadTy,
+      "    , schemaVersion = 1",
+      "    , encode = encode" <> payloadTy,
+      "    , decode = \\_ -> parse" <> payloadTy,
+      "    , upcasters = []",
+      "    }",
+      "",
+      stem <> "JobCodec :: JobCodec " <> payloadTy,
+      stem <> "JobCodec = keiroJobCodec " <> stem <> "PayloadCodec"
+    ]
+  where
+    payloadTy = wqPayloadName w
+    stem = lowerFirst (T.concat (map pascal (T.splitOn "_" (wqName w))))
+
+-- | Emit the pgmq retry policy + JobOutcome disposition compiled against the
+-- LIVE @Keiro.PGMQ.Job@ runtime (RetryPolicy / JobOutcome / RetryDelay). This pins
+-- the dangerous inversions over the runtime types: storeFailure ⇒ Retry (transient)
+-- and decodeFailure ⇒ Dead (poison).
+emitQueuePolicy :: Text -> WorkqueueNode -> Text
+emitQueuePolicy genPrefix w =
+  nl $
+    [ generatedBanner,
+      "module " <> genPrefix <> ".QueuePolicy",
+      "  ( " <> outcomeType <> " (..)",
+      "  , retryPolicy, jobOutcomeFor",
+      "  , jobOrdering, jobTuningFor, queueProvision",
+      "  ) where",
+      "",
+      "import Keiro.PGMQ.Job (JobOrdering (..), JobOutcome (..), JobTuning, PartitionSpec (..), QueueProvision, RetryDelay (..), RetryPolicy (..), partitionedProvision, standardProvision, unloggedProvision, withFifoIndexProvision, withOrdering)",
+      "",
+      "jobOrdering :: JobOrdering",
+      "jobOrdering = " <> orderingCtor,
+      "",
+      "-- Deployment owns visibility timeout, batch size, and polling; the spec owns ordering.",
+      "jobTuningFor :: JobTuning -> JobTuning",
+      "jobTuningFor = withOrdering jobOrdering",
+      "",
+      "-- Pass this to ensureJobQueueWith at worker startup. FIFO adds the required GIN index; the DLQ remains standard.",
+      "queueProvision :: QueueProvision",
+      "queueProvision = " <> provisionExpr,
+      "",
+      "retryPolicy :: RetryPolicy",
+      "retryPolicy =",
+      "  RetryPolicy",
+      "    { maxRetries = " <> tshow' (wqMaxRetries w),
+      "    , defaultRetryDelay = RetryDelay " <> windowText (wqDelay w),
+      "    , useDeadLetter = " <> (if wqDlqOn w then "True" else "False"),
+      "    }",
+      "",
+      "-- The consumer JobOutcome disposition over the spec's named domain outcomes,",
+      "-- lowered to the live Keiro.PGMQ.Job.JobOutcome.",
+      "data " <> outcomeType,
+      "  = " <> T.intercalate "\n  | " (map (pascal . wqdOutcome) (wqDisposition w)),
+      "  deriving stock (Eq, Show)",
+      "",
+      "jobOutcomeFor :: " <> outcomeType <> " -> JobOutcome",
+      "jobOutcomeFor o = case o of"
+    ]
+      ++ ["  " <> pascal (wqdOutcome r) <> " -> " <> outcome (wqdAction r) | r <- wqDisposition w]
+  where
+    outcomeType = T.concat (map pascal (T.splitOn "_" (wqName w))) <> "Outcome"
+    orderingCtor = case wqOrdering w of
+      WqUnordered -> "Unordered"
+      WqFifoThroughput -> "FifoThroughput"
+      WqFifoRoundRobin -> "FifoRoundRobin"
+    provisionExpr = fifoWrap baseProvision
+    fifoWrap expression = case wqOrdering w of
+      WqUnordered -> expression
+      _ -> "withFifoIndexProvision (" <> expression <> ")"
+    baseProvision = case wqProvision w of
+      WqStandard -> "standardProvision"
+      WqUnlogged -> "unloggedProvision"
+      WqPartitioned interval retention ->
+        "partitionedProvision (PartitionSpec { partitionInterval = "
+          <> tshow interval
+          <> ", retentionInterval = "
+          <> tshow retention
+          <> " })"
+    outcome IAckOk = "Done"
+    outcome (IRetry win) = "Retry (RetryDelay " <> windowText win <> ")"
+    outcome (IDeadLetter mr) = "Dead " <> tshow (fromMaybe "dead-lettered" mr)
+
+--------------------------------------------------------------------------------
+-- First-class read models (EP-107)
+--------------------------------------------------------------------------------
+
+-- | Emit an acyclic three-module read-model vertical. @ReadModelTable@ owns the
+-- qualified-table constant shared by the hand-owned query and the generated
+-- runtime record; @ReadModel@ re-exports it as part of the public surface.
+scaffoldReadModel :: Context -> ReadModelNode -> [ScaffoldModule]
+scaffoldReadModel ctx readModel =
+  [ generated "ReadModelTable" (emitReadModelTable tableModule stem readModel),
+    generated "ReadModel" (emitReadModelGen ctx readModelModule tableModule readModelHolePrefix stem readModel),
+    ScaffoldModule
+      { modulePath = modulePathFor readModelHolePrefix "ReadModelHoles",
+        moduleText = emitReadModelHoles tableModule readModelHolePrefix stem readModel,
+        kind = HoleStub,
+        origin = readModelOrigin
+      }
+  ]
+  where
+    nodeSegment = pascal (rmName readModel)
+    stem = readModelStem readModel
+    readModelModule = genPrefixFor ctx nodeSegment
+    tableModule = readModelModule <> ".ReadModelTable"
+    readModelHolePrefix = holePrefixFor ctx nodeSegment
+    readModelOrigin = nodeOrigin "readmodel" (rmName readModel) (rmLoc readModel)
+    generated leaf body =
+      ScaffoldModule
+        { modulePath = modulePathFor readModelModule leaf,
+          moduleText = body,
+          kind = Generated,
+          origin = readModelOrigin
+        }
+
+modulePathFor :: Text -> Text -> FilePath
+modulePathFor prefix leaf = T.unpack (T.replace "." "/" prefix <> "/" <> leaf <> ".hs")
+
+readModelStem :: ReadModelNode -> Text
+readModelStem = lowerFirst . T.concat . map pascal . T.splitOn "_" . rmName
+
+emitReadModelTable :: Text -> Text -> ReadModelNode -> Text
+emitReadModelTable tableModule stem readModel =
+  nl
+    [ generatedBanner,
+      "module " <> tableModule <> " (" <> qualifiedName <> ") where",
+      "",
+      "import Data.Text (Text)",
+      "import Keiro.Connection (qualifyTable)",
+      "",
+      "-- The fully-qualified, double-quoted data-table reference.",
+      qualifiedName <> " :: Text",
+      qualifiedName <> " = qualifyTable " <> tshow (rmSchema readModel) <> " " <> tshow (rmTable readModel)
+    ]
+  where
+    qualifiedName = stem <> "QualifiedTable"
+
+emitReadModelGen :: Context -> Text -> Text -> Text -> Text -> ReadModelNode -> Text
+emitReadModelGen ctx readModelModule tableModule readModelHolePrefix stem readModel =
+  nl $
+    [ "{-# LANGUAGE OverloadedRecordDot #-}",
+      generatedBanner,
+      "module " <> readModelModule <> ".ReadModel",
+      "  ( " <> T.intercalate "\n  , " exports,
+      "  ) where",
+      "",
+      "import Data.Functor (void)",
+      "import Effectful (Eff, (:>))",
+      "import " <> tableModule <> " (" <> qualifiedName <> ")",
+      "import " <> readModelHolePrefix <> ".ReadModelHoles (" <> T.intercalate ", " holeImports <> ")"
+    ]
+      ++ asyncImports
+      ++ [ "import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)",
+           "import Keiro.ReadModel.Rebuild qualified as Rebuild",
+           "import Kiroku.Store.Effect (Store)",
+           "import Kiroku.Store.Types (" <> kirokuTypes <> ")",
+           "",
+           readModelName <> " :: ReadModel " <> queryInputType <> " " <> queryResultType,
+           readModelName <> " =",
+           "  ReadModel",
+           "    { name = " <> tshow registryName,
+           "    , tableName = " <> tshow (rmTable readModel),
+           "    , schema = " <> tshow (rmSchema readModel),
+           "    , subscriptionName = " <> tshow subscriptionName,
+           "    , version = " <> tshow' (rmVersion readModel),
+           "    , shapeHash = " <> tshow (rmShape readModel),
+           "    , defaultConsistency = " <> consistencyExpr (rmConsistency readModel),
+           "    , strongScope = " <> scopeExpr (rmScope readModel),
+           "    , query = " <> queryName,
+           "    }",
+           "",
+           "-- Call once at projection startup before serving queries.",
+           registerName <> " :: (Store :> es) => Eff es ()",
+           registerName <> " =",
+           "  void (registerReadModel " <> tshow registryName <> " " <> tshow' (rmVersion readModel) <> " " <> tshow (rmShape readModel) <> ")",
+           "",
+           startName <> " :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata",
+           startName <> " =",
+           "  Rebuild.startRebuild " <> readModelName <> " " <> projectionNames,
+           "",
+           finishName <> " :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)",
+           finishName <> " =",
+           "  Rebuild.finishRebuild " <> readModelName <> " " <> projectionNames,
+           "",
+           abandonName <> " :: (Store :> es) => Eff es ReadModelMetadata",
+           abandonName <> " = Rebuild.abandonRebuild " <> readModelName
+         ]
+      ++ asyncDefinition
+  where
+    registryName = registryNameFor (contextName ctx) readModel
+    subscriptionName = subscriptionNameFor (contextName ctx) readModel
+    asyncName = registryName <> "-async"
+    readModelName = stem <> "ReadModel"
+    qualifiedName = stem <> "QualifiedTable"
+    registerName = "register" <> pascal stem
+    startName = "start" <> pascal stem <> "Rebuild"
+    finishName = "finish" <> pascal stem <> "Rebuild"
+    abandonName = "abandon" <> pascal stem <> "Rebuild"
+    asyncValueName = stem <> "AsyncProjection"
+    queryInputType = pascal stem <> "QueryInput"
+    queryResultType = pascal stem <> "QueryResult"
+    queryName = stem <> "Query"
+    applyName = "apply" <> pascal stem
+    exports =
+      [ readModelName,
+        qualifiedName,
+        registerName,
+        startName,
+        finishName,
+        abandonName
+      ]
+        ++ [asyncValueName | rmFeed readModel == RmSubscription]
+    holeImports = [queryInputType, queryResultType, queryName] ++ [applyName | rmFeed readModel == RmSubscription]
+    asyncImports = case rmFeed readModel of
+      RmInline -> []
+      RmSubscription -> ["import Keiro.Projection (AsyncProjection (..))"]
+    kirokuTypes = case rmFeed readModel of
+      RmInline -> "GlobalPosition"
+      RmSubscription -> "GlobalPosition, RecordedEvent (..)"
+    projectionNames = case rmFeed readModel of
+      RmInline -> "[]"
+      RmSubscription -> "[" <> tshow asyncName <> "]"
+    asyncDefinition = case rmFeed readModel of
+      RmInline -> []
+      RmSubscription ->
+        [ "",
+          asyncValueName <> " :: AsyncProjection",
+          asyncValueName <> " =",
+          "  AsyncProjection",
+          "    { name = " <> tshow asyncName,
+          "    , readModelName = " <> tshow registryName,
+          "    , subscriptionName = " <> tshow subscriptionName,
+          "    , applyRecorded = " <> applyName,
+          "    , idempotencyKey = \\recorded -> recorded.eventId",
+          "    }"
+        ]
+    consistencyExpr Strong = "Strong"
+    consistencyExpr Eventual = "Eventual"
+    scopeExpr Nothing = "EntireLog"
+    scopeExpr (Just RmEntireLog) = "EntireLog"
+    scopeExpr (Just (RmCategory categoryName)) = "CategoryHead " <> tshow categoryName
+
+emitReadModelHoles :: Text -> Text -> Text -> ReadModelNode -> Text
+emitReadModelHoles tableModule readModelHolePrefix stem readModel =
+  nl $
+    [ "-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never overwrites it.",
+      "module " <> readModelHolePrefix <> ".ReadModelHoles",
+      "  ( " <> T.intercalate "\n  , " exports,
+      "  ) where",
+      "",
+      "import " <> tableModule <> " (" <> qualifiedName <> ")",
+      "import Hasql.Transaction qualified as Tx"
+    ]
+      ++ ["import Kiroku.Store.Types (RecordedEvent(..))" | rmFeed readModel == RmSubscription]
+      ++ [ "",
+           "-- HOLE: replace these aliases with the real query input and result types.",
+           "type " <> queryInputType <> " = ()",
+           "type " <> queryResultType <> " = ()",
+           "",
+           "-- HOLE: query " <> qualifiedTableLiteral readModel <> " via " <> qualifiedName <> "; never rely on search_path.",
+           "-- Declared columns:"
+         ]
+      ++ map (("--   " <>) . readModelColumnDoc) (rmColumns readModel)
+      ++ [ queryName <> " :: " <> queryInputType <> " -> Tx.Transaction " <> queryResultType,
+           queryName <> " _input = " <> qualifiedName <> " `seq` error " <> tshow ("HOLE: fill " <> rmName readModel <> " query")
+         ]
+      ++ applyStub
+  where
+    qualifiedName = stem <> "QualifiedTable"
+    queryInputType = pascal stem <> "QueryInput"
+    queryResultType = pascal stem <> "QueryResult"
+    queryName = stem <> "Query"
+    applyName = "apply" <> pascal stem
+    exports = [queryInputType, queryResultType, queryName] ++ [applyName | rmFeed readModel == RmSubscription]
+    applyStub = case rmFeed readModel of
+      RmInline -> []
+      RmSubscription ->
+        [ "",
+          "-- HOLE: apply one recorded event; runtime deduplication makes redelivery safe.",
+          applyName <> " :: RecordedEvent -> Tx.Transaction ()",
+          applyName <> " _recorded = error " <> tshow ("HOLE: fill " <> rmName readModel <> " async apply")
+        ]
+
+qualifiedTableLiteral :: ReadModelNode -> Text
+qualifiedTableLiteral readModel = quoteSqlIdentifier (rmSchema readModel) <> "." <> quoteSqlIdentifier (rmTable readModel)
+
+quoteSqlIdentifier :: Text -> Text
+quoteSqlIdentifier identifier = "\"" <> T.replace "\"" "\"\"" identifier <> "\""
+
+readModelColumnDoc :: RmColumn -> Text
+readModelColumnDoc columnDecl =
+  rmcName columnDecl
+    <> " "
+    <> rmcType columnDecl
+    <> if rmcRequired columnDecl then " NOT NULL" else ""
+
+--------------------------------------------------------------------------------
+-- Router + shared worker-policy lowering (EP-108)
+--------------------------------------------------------------------------------
+
+scaffoldRouter :: Context -> RouterNode -> [ScaffoldModule]
+scaffoldRouter ctx router =
+  [ ScaffoldModule
+      { modulePath = modulePathFor genPrefix "Router",
+        moduleText = emitRouterGen genPrefix router,
+        kind = Generated,
+        origin = routerOrigin
+      },
+    ScaffoldModule
+      { modulePath = modulePathFor holePrefix "RouterHoles",
+        moduleText = emitRouterHoles holePrefix router,
+        kind = HoleStub,
+        origin = routerOrigin
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (rtId router)
+    holePrefix = holePrefixFor ctx (rtId router)
+    routerOrigin = nodeOrigin "router" (rtId router) (rtLoc router)
+
+emitRouterGen :: Text -> RouterNode -> Text
+emitRouterGen genPrefix router =
+  nl $
+    [ generatedBanner,
+      "module " <> genPrefix <> ".Router",
+      "  ( " <> stem <> "Name",
+      "  , " <> stem <> "WorkerOptions",
+      "  ) where",
+      "",
+      "import Data.Text (Text)"
+    ]
+      ++ workerPolicyImports (rtPoison router)
+      ++ [ "",
+           "-- The STABLE router name. It participates in every target-keyed",
+           "-- deterministicRouterCommandId; renaming it re-keys replayed dispatches.",
+           stem <> "Name :: Text",
+           stem <> "Name = " <> tshow (rtName router),
+           "",
+           "-- Runtime-owned dispatch id inputs: (name, key, sourceEventId,",
+           "-- targetStreamName, occurrence). Target-keyed, not positional.",
+           "",
+           "-- Node-level worker policy lowered from the spec. Pass this value to",
+           "-- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions."
+         ]
+      ++ workerOptionsLines (stem <> "WorkerOptions") (rtRejected router) (rtPoison router)
+  where
+    stem = lowerFirst (rtId router)
+
+emitRouterHoles :: Text -> RouterNode -> Text
+emitRouterHoles holePrefix router =
+  nl
+    [ "-- HAND-OWNED hole module for the router's behaviour-bearing bodies.",
+      "-- keiro-dsl creates it once and never overwrites it.",
+      "module " <> holePrefix <> ".RouterHoles () where",
+      "",
+      "-- HOLE resolve :: " <> inName (rtInput router) <> " -> Eff es [PMCommand targetCommand]",
+      "--   Spec source: " <> resolveSourceText (rvSource (rtResolve router)) <> ".",
+      "--   The spec's 'stable' keyword acknowledges that retry attempts accumulate",
+      "--   the UNION of resolved target identities. Keep the recipient set stable",
+      "--   for a source event whenever an exact recipient set matters.",
+      "-- HOLE router value: assemble Keiro.Router.Router with name = " <> lowerFirst (rtId router) <> "Name,",
+      "--   key, resolve, targetEventStream, and targetProjections; run it with",
+      "--   runRouterWorkerWith " <> lowerFirst (rtId router) <> "WorkerOptions.",
+      "-- HOLE targetProjections: spec projections = " <> renderNames (rtProjections router) <> ".",
+      "-- NOTE on-duplicate AckOk is sound because Keiro.Router confirms a duplicate",
+      "--   event id against the TARGET stream via confirmBenignDuplicate before",
+      "--   returning PMCommandDuplicate. Hand-rolled dispatch paths must do likewise."
+    ]
+  where
+    renderNames names = "[" <> T.intercalate ", " names <> "]"
+
+resolveSourceText :: ResolveSource -> Text
+resolveSourceText (ResolveReadModel name) = "read-model " <> name <> " (typically Keiro.ReadModel.runQuery)"
+resolveSourceText ResolveHole = "typed resolver hole"
+
+workerPolicyImports :: PolicyChoice -> [Text]
+workerPolicyImports poison =
+  [ "import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))",
+    "import Shibuya.Core.Ack (RetryDelay (..))"
+  ]
+    ++ if poison == PolHalt
+      then []
+      else ["import Effectful (Eff)", "import Shibuya.Core.Types (Envelope)"]
+
+workerOptionsLines :: Text -> PolicyChoice -> PolicyChoice -> [Text]
+workerOptionsLines valueName rejected poison =
+  [ valueName <> signature,
+    valueName <> argument <> " =",
+    "  WorkerOptions",
+    "    { poisonPolicy = " <> poisonExpr <> ",",
+    "      rejectedCommandPolicy = " <> rejectedExpr rejected <> ",",
+    "      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning",
+    "      metrics = Nothing -- runtime configuration; install at call site",
+    "    }"
+  ]
+  where
+    signature = case poison of
+      PolHalt -> " :: WorkerOptions es msg"
+      _ -> " :: (Envelope msg -> Eff es ()) -> WorkerOptions es msg"
+    argument = case poison of
+      PolHalt -> ""
+      _ -> " poisonCallback"
+    poisonExpr = case poison of
+      PolHalt -> "PoisonHalt"
+      PolDeadLetter -> "PoisonDeadLetter poisonCallback"
+      PolSkip -> "PoisonSkip poisonCallback"
+    rejectedExpr = \case
+      PolHalt -> "RejectedHalt"
+      PolDeadLetter -> "RejectedDeadLetter"
+      PolSkip -> "RejectedSkip"
+
+--------------------------------------------------------------------------------
+-- Process manager + durable timer (EP-3)
+--------------------------------------------------------------------------------
+
+-- | Emit the symbol-free deterministic wiring for a process manager + its timer
+-- into a @Generated@ module, plus a create-if-absent @ProcessHoles@ module for the
+-- behaviour-bearing bodies (the @handle@ reaction, the deadline window, and the
+-- fire command). The @Generated@ module contains no keiki symbolic operator (the
+-- saga's transducer is the separate aggregate hole), so the firewall invariant
+-- holds. The timer worker uses the spec's @max-attempts@ ceiling, never the
+-- dangerous @defaultTimerWorkerOptions@ (@Nothing@) default.
+scaffoldProcess :: Context -> ProcessNode -> [ScaffoldModule]
+scaffoldProcess ctx p =
+  [ ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" genPrefix <> "/Process.hs"),
+        moduleText = emitProcessGen sagaGenPrefix genPrefix holePrefix p,
+        kind = Generated,
+        origin = nodeOrigin "process" (procId p) (procLoc p)
+      },
+    ScaffoldModule
+      { modulePath = T.unpack (T.replace "." "/" holePrefix <> "/ProcessHoles.hs"),
+        moduleText = emitProcessHoles genPrefix holePrefix p,
+        kind = HoleStub,
+        origin = nodeOrigin "process" (procId p) (procLoc p)
+      }
+  ]
+  where
+    genPrefix = genPrefixFor ctx (procId p)
+    holePrefix = holePrefixFor ctx (procId p)
+    sagaGenPrefix = genPrefixFor ctx (pascal (sagaAgg (procSaga p)))
+
+emitProcessGen :: Text -> Text -> Text -> ProcessNode -> Text
+emitProcessGen sagaGenPrefix genPrefix _holePrefix p =
+  nl $
+    [ generatedBanner,
+      "module " <> genPrefix <> ".Process",
+      "  ( " <> lo <> "ProcessName",
+      "  , " <> lo <> "Category",
+      "  , " <> lo <> "ProcessWorkerOptions",
+      "  , " <> lo <> "TimerRequest",
+      "  , " <> lo <> "FireOutcome",
+      "  ) where",
+      "",
+      "import Data.Aeson (Value, object, (.=))",
+      "import Data.Text (Text)",
+      "import qualified Data.Text as T",
+      "import Data.Time (UTCTime)",
+      "import Data.UUID (UUID)",
+      "import qualified Data.UUID.V5 as UUID.V5",
+      "import " <> sagaGenPrefix <> ".EventStream (" <> sagaEventStreamType <> ")",
+      "import Keiro.Command (CommandError (..))",
+      "import Keiro.Stream qualified as Stream",
+      "import Keiro.Timer (TimerId (..), TimerRequest (..))"
+    ]
+      ++ workerPolicyImports (procPoison p)
+      ++ [ "",
+           "-- The define-once ProcessManager name (hole-kind 5: referenced, never retyped).",
+           lo <> "ProcessName :: Text",
+           lo <> "ProcessName = " <> tshow (procName p),
+           "",
+           "-- The validated saga stream category (hole-kind 5: referenced, never retyped).",
+           "-- Saga streams are '<category>-<correlationId>' via Keiro.Stream.entityStream.",
+           "-- categoryUnsafe is safe here because keiro-dsl check proved the literal legal.",
+           lo <> "Category :: Stream.StreamCategory " <> sagaEventStreamType,
+           lo <> "Category = Stream.categoryUnsafe " <> tshow categoryName,
+           "",
+           "-- Node-level worker policy lowered from the spec. Pass this value to",
+           "-- Keiro.ProcessManager.runProcessManagerWorkerWith."
+         ]
+      ++ workerOptionsLines (lo <> "ProcessWorkerOptions") (procRejected p) (procPoison p)
+      ++ [ "",
+           "-- The deterministic timer-request builder: id derived from the correlation",
+           "-- key (hole-kind 1), processManagerName referenced, payload from the spec.",
+           "-- (timer id derived as uuidv5 of " <> tshow (idePrefix (tmId timer)) <> " <> correlationId)",
+           lo <> "TimerRequest :: Text -> UTCTime -> TimerRequest",
+           lo <> "TimerRequest correlationId fireAtTime =",
+           "  TimerRequest",
+           "    { timerId = TimerId (namedUuid (" <> tshow (idePrefix (tmId timer)) <> " <> correlationId))",
+           "    , processManagerName = " <> lo <> "ProcessName",
+           "    , correlationId = correlationId",
+           "    , fireAt = fireAtTime",
+           "    , payload = " <> payloadExpr (tmPayload timer),
+           "    }",
+           "",
+           "-- The timer-fire disposition table (hole-kind 2), derived from the spec.",
+           "-- on-reject => " <> showOutcome (onReject fd) <> " is the benign inversion.",
+           "-- A duplicate append reaches on-error unless it is confirmed against the",
+           "-- target stream. Use Keiro.ProcessManager.confirmBenignDuplicate:",
+           "--   StreamName -> EventId -> CommandError -> Eff es Bool",
+           "-- Fold True into the duplicate result and surface False as the failure.",
+           lo <> "FireOutcome :: Either CommandError a -> Maybe ()",
+           lo <> "FireOutcome result = case result of",
+           "  Right{} -> " <> outcomeToMaybe (onOk fd),
+           "  Left CommandRejected -> " <> outcomeToMaybe (onReject fd),
+           "  Left (CommandAmbiguous _) -> " <> outcomeToMaybe (onAmbiguous fd) <> "  -- explicit definition-bug arm",
+           "  Left{} -> " <> outcomeToMaybe (onError fd),
+           "",
+           "-- max-attempts = " <> tshow' (tmMaxAttempts timer) <> ", dead-letter = " <> tshow (tmDeadLetter timer),
+           "-- (the timer worker must pass Just " <> tshow' (tmMaxAttempts timer) <> " to runTimerWorkerWith, never the",
+           "--  defaultTimerWorkerOptions Nothing ceiling that retries forever).",
+           "",
+           "-- deterministic v5 UUID of a correlation-keyed string (hole-kind 1).",
+           "namedUuid :: Text -> UUID",
+           "namedUuid v = UUID.V5.generateNamed UUID.V5.namespaceURL (map (fromIntegral . fromEnum) (T.unpack v))"
+         ]
+  where
+    lo = lowerFirst (procId p)
+    sagaEventStreamType = pascal (sagaAgg (procSaga p)) <> "EventStreamDef"
+    categoryName = staticCategory ("process " <> procId p) (sagaCategory (procSaga p))
+    timer = procTimer p
+    fd = fireDisposition (tmFire timer)
+
+-- | The timer payload, restricted to the spec's literal (@name=\"value\"@)
+-- bindings so it compiles in the deterministic builder. Bare fields and
+-- ref-valued bindings are input-driven (the agent-written hole), not emitted.
+payloadExpr :: [FieldBinding] -> Text
+payloadExpr fs = case [b | b <- fs, isLiteral b] of
+  [] -> "object []"
+  lits -> "object [ " <> T.intercalate ", " (map kv lits) <> " ]"
+  where
+    isLiteral b = maybe False (const True) (fbValue b >>= stripWrappingQuotes)
+    kv b = tshow (fbName b) <> " .= (" <> maybe "\"\"" tshow (fbValue b >>= stripWrappingQuotes) <> " :: Value)"
+    stripWrappingQuotes value = T.stripPrefix "\"" value >>= T.stripSuffix "\""
+
+showOutcome :: FireOutcome -> Text
+showOutcome OFired = "Fired"
+showOutcome ORetry = "Retry"
+
+outcomeToMaybe :: FireOutcome -> Text
+outcomeToMaybe OFired = "Just ()  -- Fired"
+outcomeToMaybe ORetry = "Nothing  -- Retry"
+
+emitProcessHoles :: Text -> Text -> ProcessNode -> Text
+emitProcessHoles _genPrefix holePrefix p =
+  nl
+    [ "-- HAND-OWNED hole module for the process manager's behaviour-bearing bodies.",
+      "-- keiro-dsl creates it once and never overwrites it.",
+      "module " <> holePrefix <> ".ProcessHoles () where",
+      "",
+      "-- HOLE handle: build the ProcessManagerAction (the self-advance",
+      "--   '" <> advCommand (hAdvance (procHandle p)) <> "', the dispatch(es), and the timer) from the input.",
+      "-- HOLE streams: build streamFor with entityStream " <> lowerFirst (procId p) <> "Category;",
+      "--   build target streams with entityStream " <> lowerFirst (procTarget p) <> "CommandCategory. Never concatenate raw stream names.",
+      "-- HOLE window: the deadline policy, e.g. surgeWindow :: NominalDiffTime;",
+      "--   surgeDeadline observedAt = addUTCTime surgeWindow observedAt  (TIME INJECTED).",
+      "-- HOLE fire command: construct " <> fireCommand (tmFire (procTimer p)) <> " for the timer fire,",
+      "--   keyed by correlationId; the fired-event-id is the deterministic uuidv5 of",
+      "--   " <> tshow (idePrefix (fireFiredEventId (tmFire (procTimer p)))) <> " <> correlationId.",
+      "-- NOTE on-duplicate AckOk is sound because the runtime confirms a duplicate",
+      "--   event id against the TARGET stream via confirmBenignDuplicate before",
+      "--   returning PMCommandDuplicate. Its effective signature is:",
+      "--     StreamName -> EventId -> CommandError -> Eff es Bool",
+      "--   Hand-rolled paths must call it with the target stream and attempted event id,",
+      "--   fold True into the duplicate result, and surface False as the original failure.",
+      "--   Never pattern-match DuplicateEvent as success: event ids are globally unique."
+    ]
+
+--------------------------------------------------------------------------------
+-- Domain module
+--------------------------------------------------------------------------------
+
+emitDomain :: Agg -> Text
+emitDomain a =
+  nl $
+    [ "{-# LANGUAGE DataKinds #-}"
+    ]
+      ++ ["{-# LANGUAGE DeriveAnyClass #-}" | hasSnapshot a]
+      ++ ["{-# LANGUAGE EmptyDataDecls #-}" | null (aCommands a) || null (aEvents a)]
+      ++ [ "{-# LANGUAGE DuplicateRecordFields #-}",
+           "{-# LANGUAGE TemplateHaskell #-}",
+           "{-# LANGUAGE TypeApplications #-}",
+           generatedBanner,
+           "module " <> aGenPrefix a <> ".Domain where",
+           ""
+         ]
+      ++ ["import Data.Aeson (FromJSON, ToJSON)" | hasSnapshot a]
+      ++ [ "import Data.Proxy (Proxy (..))",
+           "import Data.Text (Text)",
+           "import GHC.Generics (Generic)",
+           "import Keiki.Core (RegFile (..))"
+         ]
+      ++ ["import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)" | hasSnapshot a]
+      ++ generatedNominalTypeImportsForService (aggregateCheckedService a) (aContext a) (aGeneratedNominals a)
+      ++ map ("import " <>) (domainConsumerImports a)
+      ++ [ "import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)",
+           "",
+           sectionsOf
+             [ [emitVertex a],
+               map (emitRecord a) (aCommands a),
+               [emitSum (aName a <> "Command") (aCommands a)],
+               map (emitRecord a) (aEvents a),
+               [emitSum (aName a <> "Event") (aEvents a)],
+               [emitRegsType a, emitInitialRegs a],
+               [ "$(deriveAggregateCtorsAll ''" <> aName a <> "Command ''" <> aName a <> "Regs)",
+                 "",
+                 "$(deriveWireCtorsAll ''" <> aName a <> "Event)"
+               ]
+             ]
+         ]
+
+hasSnapshot :: Agg -> Bool
+hasSnapshot = maybe False (const True) . aSnapshot
+
+emitVertex :: Agg -> Text
+emitVertex a =
+  nl $
+    [ "data " <> aVertexType a <> " = " <> T.intercalate " | " (map (vertexCtor a . stName) (aStates a)),
+      "  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)"
+    ]
+      ++ ["  deriving anyclass (ToJSON, FromJSON)" | hasSnapshot a]
+      ++ [ line
+         | hasSnapshot a,
+           line <-
+             [ "instance CanonicalStateShape " <> aVertexType a,
+               "instance CanonicalTypeName " <> aVertexType a
+             ]
+         ]
+
+emitRecord :: Agg -> ResolvedCtor -> Text
+emitRecord a rc =
+  nl $
+    [ "data " <> rcName rc <> "Data = " <> rcName rc <> "Data"
+    ]
+      ++ recordFields [(name, renderDomainType a fieldType) | (name, fieldType) <- rcFields rc]
+      ++ ["  deriving stock (Generic, Eq, Show)"]
+
+recordFields :: [(Text, Text)] -> [Text]
+recordFields [] =
+  ["  {"]
+    <> ["  }"]
+recordFields fs =
+  [ lead i <> n <> " :: !" <> ty
+  | (i, (n, ty)) <- zip [(0 :: Int) ..] fs
+  ]
+    ++ ["  }"]
+  where
+    lead 0 = "  { "
+    lead _ = "  , "
+
+emitSum :: Text -> [ResolvedCtor] -> Text
+emitSum tyName ctors =
+  nl $
+    [firstLine] ++ restLines ++ ["  deriving stock (Generic, Eq, Show)"]
+  where
+    arm rc = rc' rc
+    rc' rc = rcName rc <> " !" <> rcName rc <> "Data"
+    (firstLine, restLines) = case ctors of
+      [] -> ("data " <> tyName, [])
+      (c : cs) ->
+        ( "data " <> tyName <> " = " <> arm c,
+          ["  | " <> arm c2 | c2 <- cs]
+        )
+
+emitRegsType :: Agg -> Text
+emitRegsType a =
+  nl $
+    ["type " <> aName a <> "Regs ="]
+      ++ regListLines a (aRegs a)
+
+regListLines :: Agg -> [ResolvedRegister] -> [Text]
+regListLines _ [] = ["  '[]"]
+regListLines a rs =
+  [ lead i <> "'(" <> tshow (rrName r) <> ", " <> renderDomainType a (rrType r) <> ")"
+  | (i, r) <- zip [(0 :: Int) ..] rs
+  ]
+    ++ ["   ]"]
+  where
+    lead 0 = "  '[ "
+    lead _ = "   , "
+
+emitInitialRegs :: Agg -> Text
+emitInitialRegs a =
+  nl $
+    [ "initial" <> aName a <> "Regs :: RegFile " <> aName a <> "Regs",
+      "initial" <> aName a <> "Regs ="
+    ]
+      ++ chain (aRegs a)
+  where
+    chain [] = ["  RNil"]
+    chain rs =
+      [ "  RCons (Proxy @" <> tshow (rrName r) <> ") " <> regInitialValue a r <> " $"
+      | r <- init rs
+      ]
+        ++ ["  RCons (Proxy @" <> tshow (rrName lastR) <> ") " <> regInitialValue a lastR <> " RNil"]
+      where
+        lastR = last rs
+
+-- | The Haskell initial value for a register, by the category of its type.
+regInitialValue :: Agg -> ResolvedRegister -> Text
+regInitialValue aggregate register = case rrInitial register of
+  InitialId name -> case find ((== name) . resolvedNominalName) (aGeneratedNominals aggregate) >>= generatedIdSampleHaskell aggregate of
+    Just value -> value
+    Nothing -> renderRegisterInitial (rrInitial register)
+  _ -> renderRegisterInitial (rrInitial register)
+
+domainConsumerImports :: Agg -> [Text]
+domainConsumerImports a =
+  sort . nub $
+    Set.toList (Set.unions [aggregateImports (aSymbols a) resolved | resolved <- aggregateTypes])
+      <> [ qualifiedModule initialValue <> " qualified"
+         | declaration <- mappedUses a,
+           initialValue <- maybeToListText (mappedInitial declaration)
+         ]
+      <> [ qualifiedModule initialValue <> " qualified"
+         | resolvedType <- aggregateTypes,
+           AggregateNominal nominal <- [resolvedType],
+           ConsumerNominal binding <- [resolvedNominalOwnership nominal],
+           initialValue <- maybeToListText (consumerNominalInitial binding)
+         ]
+  where
+    aggregateTypes = map snd (concatMap rcFields (aCommands a <> aEvents a)) <> map rrType (aRegs a)
+
+mappedUses :: Agg -> [ResolvedMappedDecl]
+mappedUses a =
+  [ declaration
+  | resolvedType <-
+      map snd (concatMap rcFields (aCommands a <> aEvents a))
+        <> map rrType (aRegs a),
+    declaration <- maybeToListText (mappedDeclFor a resolvedType)
+  ]
+
+mappedDeclFor :: Agg -> ResolvedAggregateType -> Maybe ResolvedMappedDecl
+mappedDeclFor a resolvedType = do
+  key <- case resolvedType of
+    AggregateMapped mappedKey -> Just mappedKey
+    _ -> Nothing
+  graph <- aTypeGraph a
+  Map.lookup key (tgDeclarations graph)
+
+mappedInitial :: ResolvedMappedDecl -> Maybe QualifiedValueName
+mappedInitial (ResolvedStructural declaration _) = sdInitial declaration
+mappedInitial (ResolvedOpaque declaration) = odInitial declaration
+
+renderDomainType :: Agg -> ResolvedAggregateType -> Text
+renderDomainType a = aggregateHaskellType (aSymbols a)
+
+maybeToListText :: Maybe value -> [value]
+maybeToListText = maybe [] pure
+
+--------------------------------------------------------------------------------
+-- Codec module
+--------------------------------------------------------------------------------
+
+emitCodec :: Agg -> Text
+emitCodec a =
+  nl $
+    ["{-# LANGUAGE DataKinds #-}" | hasConsumerNominalIdCodec a]
+      ++ ["{-# LANGUAGE TypeApplications #-}" | hasConsumerNominalIdCodec a]
+      ++ ["{-# LANGUAGE LambdaCase #-}" | hasConsumerNominalCodec a || hasGeneratedNominalEnumCodec a]
+      ++ [ "{-# LANGUAGE OverloadedRecordDot #-}",
+           generatedBanner,
+           "module " <> aGenPrefix a <> ".Codec (",
+           "    " <> lowerFirst (aName a) <> "Codec,",
+           "    parse" <> aName a <> "Event,",
+           "    encode" <> aName a <> "Event,"
+         ]
+      ++ concatMap mappedExports (codecMappedDeclarations a)
+      ++ [ ") where",
+           "",
+           "import " <> aGenPrefix a <> ".Domain"
+         ]
+      ++ generatedNominalCodecImports (aggregateCheckedService a) (aContext a) (codecGeneratedNominals a)
+      ++ ( if hasMappedCodec a
+             then
+               [ "import Control.Monad (unless)",
+                 "import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))",
+                 "import Data.Aeson.Key qualified as Key",
+                 "import Data.Aeson.KeyMap qualified as KeyMap"
+               ]
+             else ["import Data.Aeson (Value, object, withObject, withText, (.:), (.=))"]
+         )
+      ++ [ "import Data.Aeson.Types (Parser, explicitParseField, parseEither)",
+           "import Data.List.NonEmpty (NonEmpty (..))"
+         ]
+      ++ ( if hasMappedCodec a
+             then ["import Data.Map.Strict (Map)", "import Data.Map.Strict qualified as Map"]
+             else []
+         )
+      ++ [ "import Data.Text (Text)",
+           "import qualified Data.Text as T"
+         ]
+      ++ ["import Data.KindID qualified as KindID" | hasConsumerNominalIdCodec a]
+      ++ ["import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)" | hasEnforcedConsumerNominalIdCodec a]
+      ++ ["import Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)" | hasConsumerNominalCodec a]
+      ++ ["import Keiro.Codec.Structural (bindingFromShape, bindingToShape)" | hasMappedCodec a]
+      ++ [ "import Keiro.Codec (Codec (..), EventType (..))",
+           upcasterImport a
+         ]
+      ++ [nl (map ("import " <>) (codecMappedImports a)) | hasMappedCodec a]
+      ++ [nl (map ("import " <>) (codecNominalImports a)) | hasConsumerNominalCodec a]
+      ++ [ "",
+           emitEnumParsers a,
+           emitConsumerNominalParsers a
+         ]
+      ++ [emitMappedCodecs a | hasMappedCodec a]
+      ++ [ "",
+           emitCodecValue a,
+           "",
+           emitEncode a,
+           "",
+           emitDecode a,
+           "",
+           "mapLeftText :: Either String b -> Either Text b",
+           "mapLeftText = either (Left . T.pack) Right"
+         ]
+      ++ ( if hasMappedCodec a
+             then
+               [ "",
+                 "rejectUnknownFields :: String -> [Text] -> KeyMap.KeyMap Value -> Parser ()",
+                 "rejectUnknownFields label allowed objectValue =",
+                 "  unless (null extras) (fail (label <> \" contains unknown fields: \" <> show extras))",
+                 "  where",
+                 "    extras = filter (`notElem` allowed) (map Key.toText (KeyMap.keys objectValue))"
+               ]
+             else []
+         )
+  where
+    mappedExports (ResolvedStructural declaration _) =
+      [ "    encode" <> sdName declaration <> "Mapped,",
+        "    decode" <> sdName declaration <> "Mapped,"
+      ]
+    mappedExports ResolvedOpaque {} = []
+
+hasMappedCodec :: Agg -> Bool
+hasMappedCodec = not . null . codecMappedDeclarations
+
+hasConsumerNominalCodec :: Agg -> Bool
+hasConsumerNominalCodec = not . null . codecConsumerNominals
+
+hasConsumerNominalIdCodec :: Agg -> Bool
+hasConsumerNominalIdCodec aggregate =
+  any
+    (\nominal -> case resolvedNominalRepresentation nominal of IdRepresentation {} -> True; _ -> False)
+    (codecConsumerNominals aggregate)
+
+hasEnforcedConsumerNominalIdCodec :: Agg -> Bool
+hasEnforcedConsumerNominalIdCodec aggregate =
+  any
+    ( \nominal -> case resolvedNominalRepresentation nominal of
+        IdRepresentation prefix -> isJust (idDomainContractFor (aLanguageContract aggregate) prefix)
+        _ -> False
+    )
+    (codecConsumerNominals aggregate)
+
+hasGeneratedNominalEnumCodec :: Agg -> Bool
+hasGeneratedNominalEnumCodec aggregate =
+  any
+    (\nominal -> case resolvedNominalRepresentation nominal of EnumRepresentation {} -> True; _ -> False)
+    (codecGeneratedNominals aggregate)
+
+emitEnumParsers :: Agg -> Text
+emitEnumParsers a =
+  sectionsOf
+    [ [emitEnumParser nominal | nominal <- codecGeneratedNominals a, EnumRepresentation {} <- [resolvedNominalRepresentation nominal]]
+    ]
+
+emitEnumParser :: ResolvedNominalType -> Text
+emitEnumParser nominal = case resolvedNominalRepresentation nominal of
+  EnumRepresentation constructors ->
+    nl $
+      [ "parse" <> name <> " :: Text -> Parser " <> name,
+        "parse" <> name <> " = \\case"
+      ]
+        ++ ["  " <> tshow wire <> " -> pure " <> constructor | (constructor, wire) <- NE.toList constructors]
+        ++ ["  tag -> " <> renderUnknownFailure name "tag" (map snd (NE.toList constructors))]
+  _ -> error "non-enum reached generated enum parser emission"
+  where
+    name = resolvedNominalName nominal
+
+emitConsumerNominalParsers :: Agg -> Text
+emitConsumerNominalParsers aggregate = sectionsOf [map emitParser (codecConsumerNominals aggregate)]
+  where
+    emitParser nominal = case (resolvedNominalRepresentation nominal, resolvedNominalOwnership nominal) of
+      (IdRepresentation prefix, ConsumerNominal binding) ->
+        nl $
+          [ parserName nominal <> " :: Text -> Parser " <> renderHaskellSource (consumerNominalHaskell binding)
+          ]
+            <> parserBody nominal prefix binding
+      (EnumRepresentation constructors, ConsumerNominal binding) ->
+        nl $
+          [ parserName nominal <> " :: Text -> Parser " <> renderHaskellSource (consumerNominalHaskell binding),
+            parserName nominal <> " = \\case"
+          ]
+            <> [ "  "
+                   <> tshow wire
+                   <> " -> pure (nominalFromRepresentation "
+                   <> unQualifiedValueName (consumerNominalBinding binding)
+                   <> " "
+                   <> nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal)
+                   <> "."
+                   <> constructor
+                   <> ")"
+               | (constructor, wire) <- NE.toList constructors
+               ]
+            <> ["  tag -> " <> renderUnknownFailure (resolvedNominalName nominal <> " wire value") "tag" (map snd (NE.toList constructors))]
+      _ -> ""
+    parserName nominal = "parse" <> resolvedNominalName nominal <> "Nominal"
+    parserBody nominal prefix binding = case idDomainContractFor (aLanguageContract aggregate) prefix of
+      Nothing ->
+        [ parserName nominal <> " input = case KindID.parseText @" <> tshow prefix <> " input of",
+          "  Left reason -> fail (show reason)",
+          "  Right representation -> pure (nominalFromRepresentation " <> unQualifiedValueName (consumerNominalBinding binding) <> " representation)"
+        ]
+      Just _ ->
+        [ parserName nominal <> " input = case validateIdDomainText (typeIdV7Domain " <> tshow prefix <> ") input of",
+          "  Left reason -> fail (show reason)",
+          "  Right () -> case KindID.parseText @" <> tshow prefix <> " input of",
+          "    Left reason -> fail (show reason)",
+          "    Right representation -> pure (nominalFromRepresentation " <> unQualifiedValueName (consumerNominalBinding binding) <> " representation)"
+        ]
+
+emitCodecValue :: Agg -> Text
+emitCodecValue a =
+  nl $
+    [ lowerFirst (aName a) <> "Codec :: Codec " <> aName a <> "Event",
+      lowerFirst (aName a) <> "Codec =",
+      "  Codec",
+      "    { eventTypes = " <> eventTypesExpr,
+      "    , eventType = \\case"
+    ]
+      ++ ["        " <> rcName e <> "{} -> EventType " <> tshow (rcName e) | e <- aEvents a]
+      ++ [ "    , schemaVersion = " <> tshow' (maxEventVersion a),
+           "    , encode = encode" <> aName a <> "Event",
+           "    , decode = parse" <> aName a <> "Event",
+           "    , upcasters = " <> upcastersExpr a,
+           "    }"
+         ]
+      ++ upcasterRungDecls a
+  where
+    eventTypesExpr = case map rcName (aEvents a) of
+      [] -> "error \"no events\""
+      (e : es) -> "EventType " <> tshow e <> " :| [" <> T.intercalate ", " (map (("EventType " <>) . tshow) es) <> "]"
+
+-- | The codec's @schemaVersion@: the maximum declared event version (EP-2).
+maxEventVersion :: Agg -> Int
+maxEventVersion a = maximum (1 : map rcVersion (aEvents a))
+
+-- | One @(sourceVersion, upcasterName)@ entry per event that declares an
+-- @upcast from@. The upcaster name is per-event (e.g. @upcastFooV1@) and its
+-- body is a hole in the hand-owned Holes module.
+upcasterEntries :: Agg -> [(Int, Text, Text)]
+upcasterEntries a =
+  [ (m, rcName e, "upcast" <> rcName e <> "V" <> tshow' m)
+  | e <- aEvents a,
+    Just m <- [rcUpcastFrom e]
+  ]
+
+upcastersExpr :: Agg -> Text
+upcastersExpr a =
+  "[" <> T.intercalate ", " ["(" <> tshow' m <> ", upcastRungV" <> tshow' m <> ")" | (m, _) <- upcasterRungs a] <> "]"
+
+-- | Group event-specific holes into one migration rung per aggregate-global
+-- source version.  Event metadata stamps every kind with the aggregate's
+-- schema version, so a rung must explicitly pass foreign event kinds through.
+upcasterRungs :: Agg -> [(Int, [(Text, Text)])]
+upcasterRungs a =
+  [ (source, [(eventName, fn) | (_, eventName, fn) <- entries])
+  | entries@((source, _, _) : _) <- groupBy sameSource (sortOn firstSource (upcasterEntries a))
+  ]
+  where
+    firstSource (source, _, _) = source
+    sameSource (source, _, _) (otherSource, _, _) = source == otherSource
+
+upcasterRungDecls :: Agg -> [Text]
+upcasterRungDecls a = concatMap rung (upcasterRungs a)
+  where
+    rung (source, entries) =
+      [ "",
+        "upcastRungV" <> tshow' source <> " :: EventType -> Value -> Either Text Value"
+      ]
+        ++ [ "upcastRungV" <> tshow' source <> " (EventType " <> tshow eventName <> ") value = " <> fn <> " value"
+           | (eventName, fn) <- entries
+           ]
+        ++ [ "-- Kinds whose shape did not change at this rung pass through unchanged; their",
+             "-- stamped version is aggregate-global, not their own shape history.",
+             "upcastRungV" <> tshow' source <> " _ value = Right value"
+           ]
+
+-- | When the codec references upcasters, it imports their (hole) definitions
+-- from the hand-owned Holes module.
+upcasterImport :: Agg -> Text
+upcasterImport a = case upcasterEntries a of
+  [] -> ""
+  es -> "import " <> aHolePrefix a <> ".Holes (" <> T.intercalate ", " [fn | (_, _, fn) <- es] <> ")"
+
+emitEncode :: Agg -> Text
+emitEncode a =
+  nl $
+    [ "encode" <> aName a <> "Event :: " <> aName a <> "Event -> Value",
+      "encode" <> aName a <> "Event = \\case"
+    ]
+      ++ concatMap encodeArm (aEvents a)
+  where
+    encodeArm e =
+      [ "  " <> rcName e <> " payload ->",
+        "    object"
+      ]
+        ++ [ lead i <> kv
+           | (i, kv) <- zip [(0 :: Int) ..] (("\"kind\" .= (" <> tshow (rcName e) <> " :: Text)") : map encodeField (rcFields e))
+           ]
+        ++ ["      ]"]
+    lead 0 = "      [ "
+    lead _ = "      , "
+    encodeField (n, ty) =
+      tshow n
+        <> " .= "
+        <> encodeFieldValue n ty
+    encodeFieldValue name ty = case ty of
+      AggregateNominal nominal -> encodeNominalValue nominal ("payload." <> name)
+      _ -> case fieldCat a ty of
+        MappedStructuralCat declaration _ -> "encode" <> sdName declaration <> "Mapped payload." <> name
+        MappedOpaqueCat {} -> "toJSON payload." <> name
+        _ -> "payload." <> name
+    encodeNominalValue nominal value = case resolvedNominalOwnership nominal of
+      GeneratedNominal -> case resolvedNominalRepresentation nominal of
+        IdRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Text " <> value
+        EnumRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Text " <> value
+        ScalarRepresentation {} -> value
+      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
+        IdRepresentation {} -> "KindID.toText (nominalToRepresentation " <> bindingName binding <> " " <> value <> ")"
+        EnumRepresentation {} ->
+          nominalRepresentationModule (aContext a) (resolvedNominalName nominal)
+            <> "."
+            <> lowerFirst (resolvedNominalName nominal)
+            <> "RepresentationText (nominalToRepresentation "
+            <> bindingName binding
+            <> " "
+            <> value
+            <> ")"
+        ScalarRepresentation {} -> "nominalToRepresentation " <> bindingName binding <> " " <> value
+    bindingName = unQualifiedValueName . consumerNominalBinding
+
+emitDecode :: Agg -> Text
+emitDecode a =
+  nl $
+    [ "parse" <> aName a <> "Event :: EventType -> Value -> Either Text " <> aName a <> "Event",
+      "parse" <> aName a <> "Event (EventType tag) = mapLeftText . parseEither (withObject " <> tshow (aName a <> "Event") <> " go)",
+      "  where",
+      "    go o = do",
+      "      case tag of"
+    ]
+      ++ concatMap decodeArm (aEvents a)
+      ++ ["        _ -> " <> renderUnknownFailure "event type" "tag" (map rcName (aEvents a))]
+  where
+    decodeArm e =
+      ["        " <> tshow (rcName e) <> " ->"]
+        ++ case rcFields e of
+          [] -> ["          pure (" <> rcName e <> " " <> rcName e <> "Data)"]
+          fields ->
+            [ "          " <> rcName e,
+              "            <$> ( " <> rcName e <> "Data"
+            ]
+              ++ [ (if index == 0 then "                    <$> " else "                    <*> ") <> decodeField field
+                 | (index, field) <- zip [(0 :: Int) ..] fields
+                 ]
+              ++ ["                )"]
+    decodeField (n, ty) = case ty of
+      AggregateNominal nominal -> decodeNominalField n nominal
+      _ -> case fieldCat a ty of
+        MappedStructuralCat declaration _ -> "explicitParseField parse" <> sdName declaration <> "Mapped o " <> tshow n
+        MappedOpaqueCat {} -> "o .: " <> tshow n
+        _ -> "o .: " <> tshow n
+    decodeNominalField name nominal = case resolvedNominalOwnership nominal of
+      GeneratedNominal -> case resolvedNominalRepresentation nominal of
+        IdRepresentation prefix -> case idDomainContractFor (aLanguageContract a) prefix of
+          Nothing -> "(" <> resolvedNominalName nominal <> " <$> o .: " <> tshow name <> ")"
+          Just _ -> "(" <> legacyNominalConstructorName nominal <> " <$> o .: " <> tshow name <> ")"
+        EnumRepresentation {} ->
+          "explicitParseField (withText "
+            <> tshow (resolvedNominalName nominal)
+            <> " parse"
+            <> resolvedNominalName nominal
+            <> ") o "
+            <> tshow name
+        ScalarRepresentation {} -> "o .: " <> tshow name
+      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
+        IdRepresentation {} -> consumerNominalFieldParser name nominal
+        EnumRepresentation {} -> consumerNominalFieldParser name nominal
+        ScalarRepresentation {} -> "(nominalFromRepresentation " <> unQualifiedValueName (consumerNominalBinding binding) <> " <$> o .: " <> tshow name <> ")"
+    consumerNominalFieldParser fieldName nominal =
+      "explicitParseField (withText "
+        <> tshow (resolvedNominalName nominal)
+        <> " parse"
+        <> resolvedNominalName nominal
+        <> "Nominal) o "
+        <> tshow fieldName
+
+codecConsumerNominals :: Agg -> [ResolvedNominalType]
+codecConsumerNominals aggregate =
+  Map.elems . Map.fromList $
+    [ (resolvedNominalName nominal, nominal)
+    | event <- aEvents aggregate,
+      (_, AggregateNominal nominal) <- rcFields event,
+      ConsumerNominal {} <- [resolvedNominalOwnership nominal]
+    ]
+
+codecGeneratedNominals :: Agg -> [ResolvedNominalType]
+codecGeneratedNominals aggregate =
+  generatedNominalsInTypes
+    [ resolvedType
+    | event <- aEvents aggregate,
+      (_, resolvedType) <- rcFields event
+    ]
+
+codecNominalImports :: Agg -> [Text]
+codecNominalImports aggregate =
+  sort . nub $
+    concat
+      [ [ hsModule (consumerNominalHaskell binding) <> " qualified",
+          qualifiedModule (consumerNominalBinding binding) <> " qualified"
+        ]
+          <> [ nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal) <> " qualified"
+             | EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
+             ]
+      | nominal <- codecConsumerNominals aggregate,
+        ConsumerNominal binding <- [resolvedNominalOwnership nominal]
+      ]
+
+codecMappedImports :: Agg -> [Text]
+codecMappedImports a = case aTypeGraph a of
+  Nothing -> []
+  Just graph ->
+    sort . nub $
+      [ structuralShapeModule (aContext a) (sdName declaration) <> " qualified"
+      | ResolvedStructural declaration _ <- codecMappedDeclarations a
+      ]
+        <> [ hsModule (sdHaskell declaration) <> " qualified"
+           | ResolvedStructural declaration _ <- codecMappedDeclarations a
+           ]
+        <> [ qualifiedModule (sdBinding declaration) <> " qualified"
+           | ResolvedStructural declaration _ <- codecMappedDeclarations a
+           ]
+        <> [ hsModule (odHaskell declaration) <> " qualified"
+           | ResolvedOpaque declaration <- codecMappedDeclarations a
+           ]
+        <> [ hsModule (odHaskell declaration) <> " qualified"
+           | ResolvedStructural _ shape <- codecMappedDeclarations a,
+             key <- directShapeRefs shape,
+             Just (ResolvedOpaque declaration) <- [Map.lookup key (tgDeclarations graph)]
+           ]
+
+codecMappedDeclarations :: Agg -> [ResolvedMappedDecl]
+codecMappedDeclarations a = case aTypeGraph a of
+  Nothing -> []
+  Just graph ->
+    mapMaybe (\key -> Map.lookup key (tgDeclarations graph)) (sort (Map.keys selected))
+    where
+      roots =
+        [ key
+        | event <- aEvents a,
+          (_, AggregateMapped key) <- rcFields event,
+          Map.member key (tgDeclarations graph)
+        ]
+      selected =
+        Map.fromList
+          [ (key, ())
+          | root <- roots,
+            key <- root : maybe [] (Map.keys . Map.fromSet (const ())) (Map.lookup root (tgReachability graph))
+          ]
+
+directShapeRefs :: ResolvedMappedShape -> [MappedKey]
+directShapeRefs =
+  foldMappedShape
+    MappedShapeAlgebra
+      { onRecord = \_ _ fields -> concatMap (exprRefs . rwfType) fields,
+        onEnum = const [],
+        onUnion = \_ arms -> concatMap (maybe [] exprRefs . rwaPayload) arms
+      }
+
+exprRefs :: ResolvedTypeExpr -> [MappedKey]
+exprRefs =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = [],
+        onInt = [],
+        onInteger = [],
+        onBool = [],
+        onNatural = [],
+        onTime = [],
+        onJson = [],
+        onOptional = id,
+        onList = id,
+        onMap = id,
+        onRef = pure
+      }
+
+emitMappedCodecs :: Agg -> Text
+emitMappedCodecs a = case aTypeGraph a of
+  Nothing -> ""
+  Just graph ->
+    T.intercalate
+      "\n\n"
+      [ emitStructuralCodec a graph declaration shape
+      | ResolvedStructural declaration shape <- codecMappedDeclarations a
+      ]
+
+emitStructuralCodec :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
+emitStructuralCodec a graph declaration shape =
+  nl
+    [ "encode" <> name <> "Mapped :: " <> consumerType <> " -> Value",
+      "encode" <> name <> "Mapped = encode" <> name <> "Shape . bindingToShape " <> binding,
+      "",
+      "parse" <> name <> "Mapped :: Value -> Parser " <> consumerType,
+      "parse" <> name <> "Mapped value = bindingFromShape " <> binding <> " <$> parse" <> name <> "Shape value",
+      "",
+      "decode" <> name <> "Mapped :: Value -> Either Text " <> consumerType,
+      "decode" <> name <> "Mapped = mapLeftText . parseEither parse" <> name <> "Mapped",
+      "",
+      "encode" <> name <> "Shape :: " <> shapeType <> " -> Value",
+      emitShapeEncoder a graph declaration shape,
+      "",
+      "parse" <> name <> "Shape :: Value -> Parser " <> shapeType,
+      emitShapeDecoder a graph declaration shape
+    ]
+  where
+    name = sdName declaration
+    consumerType = renderHaskellSource (sdHaskell declaration)
+    shapeType = structuralShapeModule (aContext a) name <> "." <> name <> "Shape"
+    binding = unQualifiedValueName (sdBinding declaration)
+
+emitShapeEncoder :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
+emitShapeEncoder a graph declaration =
+  foldMappedShape
+    MappedShapeAlgebra
+      { onRecord = \_ _ fields ->
+          nl $
+            ["encode" <> name <> "Shape shape =", "  object"]
+              <> objectEntries
+                [ tshow (rwfKey field)
+                    <> " .= "
+                    <> encodeShapeExpr a graph (rwfType field) (shapeModuleName <> "." <> rwfHaskell field <> " shape")
+                | field <- fields
+                ],
+        onEnum = \entries ->
+          nl $
+            ["encode" <> name <> "Shape = \\case"]
+              <> ["  " <> shapeModuleName <> "." <> weCtor entry <> " -> String " <> tshow (weTag entry) | entry <- entries],
+        onUnion = \encoding arms ->
+          nl $
+            ["encode" <> name <> "Shape = \\case"]
+              <> concatMap (unionEncodeArm encoding) arms
+      }
+  where
+    name = sdName declaration
+    shapeModuleName = structuralShapeModule (aContext a) name
+    unionEncodeArm encoding arm =
+      [ "  " <> shapeModuleName <> "." <> rwaCtor arm <> payloadPattern <> " ->",
+        "    object"
+      ]
+        <> objectEntries
+          ( [tshow (ueTagField encoding) <> " .= (" <> tshow (rwaTag arm) <> " :: Text)"]
+              <> [ tshow (ueContentsField encoding) <> " .= " <> encodeShapeExpr a graph payload "payload"
+                 | payload <- maybeToListText (rwaPayload arm)
+                 ]
+          )
+      where
+        payloadPattern = maybe "" (const " payload") (rwaPayload arm)
+
+emitShapeDecoder :: Agg -> TypeGraph -> StructuralDecl -> ResolvedMappedShape -> Text
+emitShapeDecoder a graph declaration =
+  foldMappedShape
+    MappedShapeAlgebra
+      { onRecord = \constructor unknownFields fields ->
+          nl $
+            [ "parse" <> name <> "Shape = withObject " <> tshow (name <> "Shape") <> " $ \\objectValue -> do"
+            ]
+              <> rejectLine "  " unknownFields (map rwfKey fields) "objectValue"
+              <> [ "  " <> shapeModuleName <> "." <> constructor,
+                   "    <$> " <> T.intercalate "\n    <*> " (map (decodeRecordField a graph) fields)
+                 ],
+        onEnum = \entries ->
+          nl $
+            [ "parse" <> name <> "Shape = withText " <> tshow (name <> "Shape") <> " $ \\tag -> case tag of"
+            ]
+              <> ["  " <> tshow (weTag entry) <> " -> pure " <> shapeModuleName <> "." <> weCtor entry | entry <- entries]
+              <> ["  tag -> " <> renderUnknownFailure (name <> " wire value") "tag" (map weTag entries)],
+        onUnion = \encoding arms ->
+          nl $
+            [ "parse" <> name <> "Shape = withObject " <> tshow (name <> "Shape") <> " $ \\objectValue -> do",
+              "  tag <- explicitParseField (withText " <> tshow (name <> " tag") <> " validate" <> name <> "Tag) objectValue " <> tshow (ueTagField encoding),
+              "  case tag of"
+            ]
+              <> concatMap (unionDecodeArm encoding) arms
+              <> [ "    _ -> fail \"validated union tag was not handled\"",
+                   "",
+                   "validate" <> name <> "Tag :: Text -> Parser Text",
+                   "validate" <> name <> "Tag tag",
+                   "  | tag `elem` " <> renderTextList (map rwaTag arms) <> " = pure tag",
+                   "  | otherwise = " <> renderUnknownFailure (name <> " union tag") "tag" (map rwaTag arms)
+                 ]
+      }
+  where
+    name = sdName declaration
+    shapeModuleName = structuralShapeModule (aContext a) name
+    rejectLine _ IgnoreUnknown _ _ = []
+    rejectLine indent RejectUnknown allowed objectName =
+      [indent <> "rejectUnknownFields " <> tshow name <> " " <> renderTextList allowed <> " " <> objectName]
+    unionDecodeArm encoding arm =
+      ["    " <> tshow (rwaTag arm) <> " -> do"]
+        <> rejectLine "      " (ueUnknownFields encoding) allowed "objectValue"
+        <> [ case rwaPayload arm of
+               Nothing -> "      pure " <> shapeModuleName <> "." <> rwaCtor arm
+               Just payload ->
+                 "      "
+                   <> shapeModuleName
+                   <> "."
+                   <> rwaCtor arm
+                   <> " <$> explicitParseField ("
+                   <> decodeShapeExpr a graph payload
+                   <> ") objectValue "
+                   <> tshow (ueContentsField encoding)
+           ]
+      where
+        allowed = ueTagField encoding : [ueContentsField encoding | rwaPayload arm /= Nothing]
+
+decodeRecordField :: Agg -> TypeGraph -> ResolvedWireField -> Text
+decodeRecordField a graph field = case rwfPresence field of
+  PRequired ->
+    "explicitParseField (" <> decoder <> ") objectValue " <> key
+  POptional ->
+    "(case KeyMap.lookup (Key.fromText "
+      <> key
+      <> ") objectValue of Nothing -> "
+      <> missing
+      <> "; Just _ -> explicitParseField ("
+      <> decoder
+      <> ") objectValue "
+      <> key
+      <> ")"
+  where
+    key = tshow (rwfKey field)
+    decoder = decodeShapeExpr a graph (rwfType field)
+    missing = case rwfOnMissing field of
+      Nothing -> "fail " <> tshow ("missing optional field without default: " <> rwfKey field)
+      Just onMissing -> "pure " <> renderMissingDefault a graph (rwfType field) onMissing
+
+encodeShapeExpr :: Agg -> TypeGraph -> ResolvedTypeExpr -> Text -> Text
+encodeShapeExpr _a graph expression value =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = \v -> "toJSON (" <> v <> ")",
+        onInt = \v -> "toJSON (" <> v <> ")",
+        onInteger = \v -> "toJSON (" <> v <> ")",
+        onBool = \v -> "toJSON (" <> v <> ")",
+        onNatural = \v -> "toJSON (" <> v <> ")",
+        onTime = \v -> "toJSON (" <> v <> ")",
+        onJson = id,
+        onOptional = \encode v -> "maybe Null (\\item -> " <> encode "item" <> ") (" <> v <> ")",
+        onList = \encode v -> "toJSON (map (\\item -> " <> encode "item" <> ") (" <> v <> "))",
+        onMap = \encode v -> "toJSON (Map.map (\\item -> " <> encode "item" <> ") (" <> v <> "))",
+        onRef = \key v -> case Map.lookup key (tgDeclarations graph) of
+          Just (ResolvedStructural nested _) -> "encode" <> sdName nested <> "Shape (" <> v <> ")"
+          Just (ResolvedOpaque _) -> "toJSON (" <> v <> ")"
+          Nothing -> "toJSON (" <> v <> ")"
+      }
+    expression
+    value
+
+decodeShapeExpr :: Agg -> TypeGraph -> ResolvedTypeExpr -> Text
+decodeShapeExpr _a graph =
+  foldTypeExpr
+    TypeExprAlgebra
+      { onText = "parseJSON",
+        onInt = "parseJSON",
+        onInteger = "parseJSON",
+        onBool = "parseJSON",
+        onNatural = "parseJSON",
+        onTime = "parseJSON",
+        onJson = "pure",
+        onOptional = \decode -> "\\value -> case value of Null -> pure Nothing; other -> Just <$> " <> decode <> " other",
+        onList = \decode -> "\\value -> (parseJSON value :: Parser [Value]) >>= traverse (" <> decode <> ")",
+        onMap = \decode -> "\\value -> (parseJSON value :: Parser (Map Text Value)) >>= traverse (" <> decode <> ")",
+        onRef = \key -> case Map.lookup key (tgDeclarations graph) of
+          Just (ResolvedStructural nested _) -> "parse" <> sdName nested <> "Shape"
+          Just (ResolvedOpaque _) -> "parseJSON"
+          Nothing -> "parseJSON"
+      }
+
+renderMissingDefault :: Agg -> TypeGraph -> ResolvedTypeExpr -> OnMissing -> Text
+renderMissingDefault a graph expression = \case
+  OmNull -> "Nothing"
+  OmText value -> tshow value
+  OmInt value -> T.pack (show value)
+  OmBool value -> if value then "True" else "False"
+  OmEmptyList -> "[]"
+  OmEmptyMap -> "Map.empty"
+  OmCtor constructor -> case expression of
+    RRef key -> case Map.lookup key (tgDeclarations graph) of
+      Just (ResolvedStructural declaration _) -> structuralShapeModule (aContext a) (sdName declaration) <> "." <> constructor
+      _ -> constructor
+    _ -> constructor
+
+objectEntries :: [Text] -> [Text]
+objectEntries entries =
+  [lead index <> entry | (index, entry) <- zip [(0 :: Int) ..] entries]
+    <> ["      ]"]
+  where
+    lead 0 = "      [ "
+    lead _ = "      , "
+
+renderTextList :: [Text] -> Text
+renderTextList values = "[" <> T.intercalate ", " (map tshow values) <> "]"
+
+-- | Emit a parser failure that names the rejected runtime value and the full,
+-- deterministic wire set accepted at that point.
+renderUnknownFailure :: Text -> Text -> [Text] -> Text
+renderUnknownFailure label variable expected =
+  "fail ("
+    <> tshow ("unknown " <> label <> " ")
+    <> " <> show "
+    <> variable
+    <> " <> "
+    <> tshow ("; expected one of: " <> expectedText)
+    <> ")"
+  where
+    expectedText = case expected of
+      [] -> "<none>"
+      values -> T.intercalate ", " values
+
+--------------------------------------------------------------------------------
+-- Authoritative version-2 expressions and transducer
+--------------------------------------------------------------------------------
+
+hasVersion2Ownership :: Agg -> Bool
+hasVersion2Ownership = any ((/= LegacyHoleImplementation) . tImplementation) . aTransitions
+
+transitionEntries :: Agg -> [(Int, Transition)]
+transitionEntries aggregate = zip [1 ..] (aTransitions aggregate)
+
+transitionStem :: Int -> Transition -> Text
+transitionStem index transition =
+  "transition"
+    <> tshow' index
+    <> pascal (tSource transition)
+    <> pascal (tCommand transition)
+
+guardFunctionName :: Int -> Transition -> Text
+guardFunctionName index transition = transitionStem index transition <> "Guard"
+
+writeFunctionName :: Int -> Transition -> Name -> Text
+writeFunctionName index transition registerName =
+  transitionStem index transition <> "Write" <> pascal registerName
+
+holeFunctionName :: Int -> Transition -> Text
+holeFunctionName index transition = transitionStem index transition <> "Hole"
+
+holeFoldVersionName :: Int -> Transition -> Text
+holeFoldVersionName index transition = holeFunctionName index transition <> "FoldVersion"
+
+outputFunctionName :: Int -> Transition -> Int -> Name -> Text
+outputFunctionName transitionIndex transition emitIndex eventName =
+  transitionStem transitionIndex transition
+    <> "Output"
+    <> tshow' emitIndex
+    <> pascal eventName
+
+-- | Legacy create-once output-hook names made obsolete by authoritative
+-- version-2 @fields(Command)@ generation.  Scaffolding reports these names as
+-- safe-to-remove candidates without parsing or modifying consumer Haskell.
+obsoleteGeneratedOutputHooks :: Spec -> [(Name, Text)]
+obsoleteGeneratedOutputHooks spec =
+  [ ( aggName aggregate,
+      outputFunctionName transitionIndex transition emitIndex eventName
+    )
+  | aggregate <- [value | NAggregate value <- specNodes spec],
+    (transitionIndex, transition) <- zip [1 ..] (aggTransitions aggregate),
+    (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
+    Right GeneratedCommandIdentity {} <- [eventOutputMapping spec aggregate transition emitIndex eventName]
+  ]
+
+commandForTransition :: Agg -> Transition -> ResolvedCtor
+commandForTransition aggregate transition =
+  fromMaybe
+    (error ("validated aggregate command disappeared: " <> T.unpack (tCommand transition)))
+    (find ((== tCommand transition) . rcName) (aCommands aggregate))
+
+eventForName :: Agg -> Name -> ResolvedCtor
+eventForName aggregate eventName =
+  fromMaybe
+    (error ("validated aggregate event disappeared: " <> T.unpack eventName))
+    (find ((== eventName) . rcName) (aEvents aggregate))
+
+commandFieldsType :: Transition -> Text
+commandFieldsType transition = "RegFieldsOf " <> tCommand transition <> "Data"
+
+payloadProjectionType :: Agg -> Transition -> Text
+payloadProjectionType aggregate transition =
+  "B.PayloadProj "
+    <> aName aggregate
+    <> "Regs "
+    <> aName aggregate
+    <> "Command ("
+    <> commandFieldsType transition
+    <> ")"
+
+data ResolvedGeneratedTransition = ResolvedGeneratedTransition
+  { resolvedTransitionIndex :: !Int,
+    resolvedTransitionSource :: !Transition,
+    resolvedTransitionGuard :: !(Maybe TypedScalarExpr),
+    resolvedTransitionWrites :: ![(Name, TypedScalarExpr)]
+  }
+  deriving stock (Eq, Show)
+
+-- Resolve each generated-owned transition exactly once. Import analysis,
+-- projection planning, and Haskell emission all consume this inventory.
+resolvedGeneratedTransitions :: Agg -> [ResolvedGeneratedTransition]
+resolvedGeneratedTransitions aggregate =
+  [ ResolvedGeneratedTransition
+      { resolvedTransitionIndex = index,
+        resolvedTransitionSource = transition,
+        resolvedTransitionGuard = resolvedGuard index transition <$> tGuard transition,
+        resolvedTransitionWrites =
+          [ (registerName, resolvedWrite index transition registerName expression)
+          | (registerName, expression) <- tWrites transition
+          ]
+      }
+  | (index, transition) <- transitionEntries aggregate,
+    tImplementation transition == GeneratedImplementation
+  ]
+  where
+    environment transition = expressionEnvironment (aSpec aggregate) (aAggregate aggregate) transition
+    resolvedGuard index transition expression =
+      expressionOrDie (guardFunctionName index transition) (resolveGuardExpr (environment transition) expression)
+    resolvedWrite index transition registerName expression =
+      expressionOrDie (writeFunctionName index transition registerName) (resolveWriteExpr (environment transition) registerName expression)
+
+resolvedGeneratedExpressions :: Agg -> [TypedScalarExpr]
+resolvedGeneratedExpressions = generatedTransitionExpressions . resolvedGeneratedTransitions
+
+generatedTransitionExpressions :: [ResolvedGeneratedTransition] -> [TypedScalarExpr]
+generatedTransitionExpressions = concatMap transitionExpressions
+  where
+    transitionExpressions resolved =
+      maybe [] pure (resolvedTransitionGuard resolved)
+        <> map snd (resolvedTransitionWrites resolved)
+
+anyTypedExpression :: (TypedScalarExpr -> Bool) -> TypedScalarExpr -> Bool
+anyTypedExpression predicate expression =
+  predicate expression || any (anyTypedExpression predicate) (typedExpressionChildren expression)
+
+typedExpressionChildren :: TypedScalarExpr -> [TypedScalarExpr]
+typedExpressionChildren expression = case typedScalarNode expression of
+  TypedLiteral {} -> []
+  TypedRoot {} -> []
+  TypedProject {} -> []
+  TypedAdd _ left right -> [left, right]
+  TypedSubtract _ left right -> [left, right]
+  TypedMultiply _ left right -> [left, right]
+  TypedEqual left right -> [left, right]
+  TypedNotEqual left right -> [left, right]
+  TypedCompare _ left right -> [left, right]
+  TypedAnd left right -> [left, right]
+  TypedOr left right -> [left, right]
+
+typedConsumerLiteralNominals :: TypedScalarExpr -> [ResolvedNominalType]
+typedConsumerLiteralNominals expression = own <> concatMap typedConsumerLiteralNominals (typedExpressionChildren expression)
+  where
+    own = case (typedScalarType expression, typedScalarNode expression) of
+      (AggregateNominal nominal, TypedLiteral ScalarEnumValue {})
+        | ConsumerNominal {} <- resolvedNominalOwnership nominal -> [nominal]
+      (AggregateNominal nominal, TypedLiteral ScalarIdValue {})
+        | ConsumerNominal {} <- resolvedNominalOwnership nominal -> [nominal]
+      _ -> []
+
+typedGeneratedNominals :: TypedScalarExpr -> [ResolvedNominalType]
+typedGeneratedNominals expression = own <> concatMap typedGeneratedNominals (typedExpressionChildren expression)
+  where
+    own = case typedScalarType expression of
+      AggregateNominal nominal
+        | GeneratedNominal <- resolvedNominalOwnership nominal -> [nominal]
+      _ -> []
+
+expressionOrDie :: Text -> Either (NonEmpty ExpressionDiagnostic) TypedScalarExpr -> TypedScalarExpr
+expressionOrDie owner = either (error . (("validated expression disappeared for " <> T.unpack owner <> ": ") <>) . show) id
+
+data ProjectionAliasTarget
+  = StructuralProjectionAlias !ScalarRootProvenance !ResolvedScalarProjection
+  | NominalProjectionAlias !ResolvedNominalType !ScalarRootProvenance
+  deriving stock (Eq, Show)
+
+data ProjectionAlias = ProjectionAlias
+  { projectionAliasTarget :: !ProjectionAliasTarget,
+    projectionAliasName :: !Text
+  }
+  deriving stock (Eq, Show)
+
+projectionAliasesForTransition :: ResolvedGeneratedTransition -> [ProjectionAlias]
+projectionAliasesForTransition resolved = allocateAliases targets
+  where
+    expressions =
+      maybe [] pure (resolvedTransitionGuard resolved)
+        <> map snd (resolvedTransitionWrites resolved)
+    targets = nub (concatMap projectionAliasTargets expressions)
+
+projectionAliasTargets :: TypedScalarExpr -> [ProjectionAliasTarget]
+projectionAliasTargets expression = own <> comparisonTargets <> concatMap projectionAliasTargets children
+  where
+    children = typedExpressionChildren expression
+    own = case typedScalarNode expression of
+      TypedProject provenance projection -> [StructuralProjectionAlias provenance projection]
+      _ -> []
+    comparisonTargets = case typedScalarNode expression of
+      TypedEqual left right -> mapMaybe nominalTarget [left, right]
+      TypedNotEqual left right -> mapMaybe nominalTarget [left, right]
+      _ -> []
+    nominalTarget operand = case (typedScalarType operand, typedScalarNode operand) of
+      (AggregateNominal nominal, TypedRoot provenance)
+        | nominalComparisonProjection nominal -> Just (NominalProjectionAlias nominal provenance)
+      _ -> Nothing
+
+allocateAliases :: [ProjectionAliasTarget] -> [ProjectionAlias]
+allocateAliases = snd . foldl allocate (Map.empty, [])
+  where
+    allocate (counts, aliases) target =
+      let base = projectionAliasBase target
+          occurrence = Map.findWithDefault 0 base counts + 1
+          alias = if occurrence == 1 then base else base <> tshow' occurrence
+       in (Map.insert base occurrence counts, aliases <> [ProjectionAlias target alias])
+
+projectionAliasBase :: ProjectionAliasTarget -> Text
+projectionAliasBase target = prefix <> pascal rootName <> pathSuffix
+  where
+    provenance = case target of
+      StructuralProjectionAlias value _ -> value
+      NominalProjectionAlias _ value -> value
+    (prefix, rootName) = case provenance of
+      ScalarRegisterRoot name _ -> ("register", name)
+      ScalarCommandRoot name _ -> ("command", name)
+    pathSuffix = case target of
+      NominalProjectionAlias {} -> ""
+      StructuralProjectionAlias _ projection ->
+        T.concat
+          [ normaliseAliasPart (unescapePointer segment)
+          | segment <- filter (not . T.null) (T.splitOn "/" (scalarProjectionPointer projection))
+          ]
+
+normaliseAliasPart :: Text -> Text
+normaliseAliasPart value = case filter (not . T.null) (T.split (not . isAlphaNum) value) of
+  [] -> "Field"
+  pieces -> T.concat (map pascal pieces)
+
+unescapePointer :: Text -> Text
+unescapePointer = T.replace "~0" "~" . T.replace "~1" "/"
+
+projectionAliasFor :: [ProjectionAlias] -> ProjectionAliasTarget -> Text
+projectionAliasFor aliases target =
+  maybe
+    (error ("resolved projection alias disappeared: " <> show target))
+    projectionAliasName
+    (find ((== target) . projectionAliasTarget) aliases)
+
+data RenderAssociativity = RenderLeft | RenderRight | RenderNonAssociative
+  deriving stock (Eq, Show)
+
+data RenderOperandSide = RenderLeftOperand | RenderRightOperand
+  deriving stock (Eq, Show)
+
+data RenderedKeikiExpr = RenderedKeikiExpr
+  { renderedKeikiText :: !Text,
+    renderedKeikiPrecedence :: !Int
+  }
+  deriving stock (Eq, Show)
+
+renderedAtom :: Text -> RenderedKeikiExpr
+renderedAtom value = RenderedKeikiExpr value 10
+
+renderedInfix :: Int -> RenderAssociativity -> Text -> RenderedKeikiExpr -> RenderedKeikiExpr -> RenderedKeikiExpr
+renderedInfix precedence associativity operator left right =
+  RenderedKeikiExpr
+    ( renderInfixChild precedence associativity RenderLeftOperand left
+        <> " "
+        <> operator
+        <> " "
+        <> renderInfixChild precedence associativity RenderRightOperand right
+    )
+    precedence
+
+renderInfixChild :: Int -> RenderAssociativity -> RenderOperandSide -> RenderedKeikiExpr -> Text
+renderInfixChild parentPrecedence associativity side child
+  | renderedKeikiPrecedence child > parentPrecedence = renderedKeikiText child
+  | renderedKeikiPrecedence child < parentPrecedence = parenthesized
+  | otherwise = case associativity of
+      RenderLeft
+        | side == RenderLeftOperand -> renderedKeikiText child
+      RenderRight
+        | side == RenderRightOperand -> renderedKeikiText child
+      _ -> parenthesized
+  where
+    parenthesized = "(" <> renderedKeikiText child <> ")"
+
+renderKeikiPredicate :: [ProjectionAlias] -> Agg -> Transition -> TypedScalarExpr -> Text
+renderKeikiPredicate aliases aggregate transition =
+  renderedKeikiText . renderPredicate
+  where
+    renderPredicate expression = case typedScalarNode expression of
+      TypedEqual left right -> comparison ".==" left right
+      TypedNotEqual left right -> comparison "./=" left right
+      TypedCompare operator left right -> comparison (renderComparisonOperator operator) left right
+      TypedAnd left right -> boolean 3 RenderRight ".&&" left right
+      TypedOr left right -> boolean 2 RenderRight ".||" left right
+      _ ->
+        renderedInfix
+          4
+          RenderNonAssociative
+          ".=="
+          (renderKeikiTerm aliases aggregate transition expression)
+          (renderedAtom "K.lit True")
+    comparison operator left right =
+      renderedInfix
+        4
+        RenderNonAssociative
+        operator
+        (renderComparisonTerm aliases aggregate transition left)
+        (renderComparisonTerm aliases aggregate transition right)
+    boolean precedence associativity operator left right =
+      renderedInfix precedence associativity operator (renderPredicate left) (renderPredicate right)
+
+renderComparisonOperator :: CmpOp -> Text
+renderComparisonOperator = \case
+  OpEq -> ".=="
+  OpNeq -> "./="
+  OpLt -> ".<"
+  OpLe -> ".<="
+  OpGt -> ".>"
+  OpGe -> ".>="
+
+renderComparisonTerm :: [ProjectionAlias] -> Agg -> Transition -> TypedScalarExpr -> RenderedKeikiExpr
+renderComparisonTerm aliases aggregate transition expression = case (typedScalarType expression, typedScalarNode expression) of
+  (AggregateNominal nominal, TypedRoot provenance)
+    | nominalComparisonProjection nominal ->
+        renderedAtom (projectionAliasFor aliases (NominalProjectionAlias nominal provenance))
+  (AggregateNominal nominal, TypedLiteral (ScalarEnumValue _ constructor)) ->
+    renderedAtom ("K.lit (" <> tshow (enumWireFor nominal constructor) <> " :: Text)")
+  (AggregateNominal _, TypedLiteral (ScalarIdValue _ value)) ->
+    renderedAtom ("K.lit (" <> tshow value <> " :: Text)")
+  _ -> renderKeikiTerm aliases aggregate transition expression
+
+nominalComparisonProjection :: ResolvedNominalType -> Bool
+nominalComparisonProjection nominal = case resolvedNominalRepresentation nominal of
+  IdRepresentation {} -> True
+  EnumRepresentation {} -> True
+  ScalarRepresentation {} -> case resolvedNominalOwnership nominal of
+    ConsumerNominal {} -> True
+    GeneratedNominal -> False
+
+enumWireFor :: ResolvedNominalType -> Name -> Text
+enumWireFor nominal constructor = case resolvedNominalRepresentation nominal of
+  EnumRepresentation constructors -> fromMaybe (error "validated enum literal lost its wire spelling") (lookup constructor (NE.toList constructors))
+  _ -> error "validated enum literal lost its enum representation"
+
+renderNominalProjectionTerm :: Agg -> Transition -> ResolvedNominalType -> ScalarRootProvenance -> Text
+renderNominalProjectionTerm aggregate transition nominal provenance = case provenance of
+  ScalarRegisterRoot registerName ownerType ->
+    "K.regProj "
+      <> projectionQualifier
+      <> "."
+      <> witness
+      <> " (#"
+      <> registerName
+      <> " :: K.Index "
+      <> aName aggregate
+      <> "Regs "
+      <> renderDomainType aggregate ownerType
+      <> ")"
+  ScalarCommandRoot fieldName ownerType ->
+    "K.inpProj "
+      <> projectionQualifier
+      <> "."
+      <> witness
+      <> " inCtor"
+      <> tCommand transition
+      <> " (#"
+      <> fieldName
+      <> " :: K.Index ("
+      <> commandFieldsType transition
+      <> ") "
+      <> renderDomainType aggregate ownerType
+      <> ")"
+  where
+    projectionQualifier = case resolvedNominalOwnership nominal of
+      GeneratedNominal -> "GeneratedNominals"
+      ConsumerNominal {} -> "NominalProjections"
+    witness = case resolvedNominalRepresentation nominal of
+      ScalarRepresentation {} -> lowerFirst (resolvedNominalName nominal) <> "Witness"
+      IdRepresentation {} -> nominalEqualityWitnessName nominal
+      EnumRepresentation {} -> nominalEqualityWitnessName nominal
+
+renderKeikiTerm :: [ProjectionAlias] -> Agg -> Transition -> TypedScalarExpr -> RenderedKeikiExpr
+renderKeikiTerm aliases aggregate transition expression = case typedScalarNode expression of
+  TypedLiteral value -> renderedAtom (renderKeikiLiteral aggregate (typedScalarType expression) value)
+  TypedRoot (ScalarRegisterRoot registerName _) -> renderedAtom ("B.reg @" <> tshow registerName)
+  TypedRoot (ScalarCommandRoot fieldName _) -> renderedAtom ("d." <> fieldName)
+  TypedProject provenance projection ->
+    renderedAtom (projectionAliasFor aliases (StructuralProjectionAlias provenance projection))
+  TypedAdd _ left right -> arithmetic 6 ".+" left right
+  TypedSubtract _ left right -> arithmetic 6 ".-" left right
+  TypedMultiply _ left right -> arithmetic 7 ".*" left right
+  TypedEqual {} -> impossiblePredicate
+  TypedNotEqual {} -> impossiblePredicate
+  TypedCompare {} -> impossiblePredicate
+  TypedAnd {} -> impossiblePredicate
+  TypedOr {} -> impossiblePredicate
+  where
+    arithmetic precedence operator left right =
+      renderedInfix
+        precedence
+        RenderLeft
+        operator
+        (renderKeikiTerm aliases aggregate transition left)
+        (renderKeikiTerm aliases aggregate transition right)
+    impossiblePredicate = error "predicate-valued Boolean expressions cannot be lowered as register terms"
+
+renderStructuralProjectionTerm :: Agg -> Transition -> ScalarRootProvenance -> ResolvedScalarProjection -> Text
+renderStructuralProjectionTerm aggregate transition provenance projection = case provenance of
+  ScalarRegisterRoot registerName ownerType ->
+    "K.regProj StructuralProjections."
+      <> witness
+      <> " (#"
+      <> registerName
+      <> " :: K.Index "
+      <> aName aggregate
+      <> "Regs "
+      <> renderDomainType aggregate ownerType
+      <> ")"
+  ScalarCommandRoot fieldName ownerType ->
+    "K.inpProj StructuralProjections."
+      <> witness
+      <> " inCtor"
+      <> tCommand transition
+      <> " (#"
+      <> fieldName
+      <> " :: K.Index ("
+      <> commandFieldsType transition
+      <> ") "
+      <> renderDomainType aggregate ownerType
+      <> ")"
+  where
+    witness =
+      fromMaybe
+        (error ("resolved structural projection witness disappeared: " <> show projection))
+        (aTypeGraph aggregate >>= \graph -> projectionWitnessName graph (scalarProjectionOwner projection) (scalarProjectionPointer projection))
+
+renderKeikiLiteral :: Agg -> ResolvedAggregateType -> ScalarValue -> Text
+renderKeikiLiteral aggregate scalarType = \case
+  ScalarTextValue value -> "K.lit (" <> tshow value <> " :: Text)"
+  ScalarIntValue value -> "K.lit (" <> tshow' value <> " :: Int)"
+  ScalarIntegerValue value -> "K.lit (" <> T.pack (show value) <> " :: Integer)"
+  ScalarNaturalValue value -> "K.lit (" <> T.pack (show value) <> " :: Natural)"
+  ScalarBoolValue value -> "K.lit " <> if value then "True" else "False"
+  ScalarTimeValue value -> "K.lit " <> renderRegisterInitial (InitialTime value)
+  ScalarEnumValue typeName constructor -> case scalarType of
+    AggregateNominal nominal -> case resolvedNominalOwnership nominal of
+      GeneratedNominal -> "K.lit " <> constructor
+      ConsumerNominal binding ->
+        "K.lit (nominalFromRepresentation "
+          <> unQualifiedValueName (consumerNominalBinding binding)
+          <> " "
+          <> nominalRepresentationModule (aContext aggregate) typeName
+          <> "."
+          <> constructor
+          <> ")"
+    _ -> error "validated enum literal lost its nominal type"
+  ScalarIdValue typeName value -> case scalarType of
+    AggregateNominal nominal -> case resolvedNominalOwnership nominal of
+      GeneratedNominal -> case idDomainContractFor (aLanguageContract aggregate) =<< idPrefixOf nominal of
+        Nothing -> "K.lit (" <> typeName <> " " <> tshow value <> ")"
+        Just _ ->
+          "K.lit (case parse"
+            <> typeName
+            <> " "
+            <> tshow value
+            <> " of Right parsed -> parsed; Left _ -> error \"validated ID literal failed to parse\")"
+      ConsumerNominal binding -> case resolvedNominalRepresentation nominal of
+        IdRepresentation prefix ->
+          "K.lit (nominalFromRepresentation "
+            <> unQualifiedValueName (consumerNominalBinding binding)
+            <> " (case KindID.parseText @"
+            <> tshow prefix
+            <> " "
+            <> tshow value
+            <> " of Right parsed -> parsed; Left _ -> error \"validated ID literal failed to parse\"))"
+        _ -> error "validated ID literal lost its ID representation"
+    _ -> error "validated ID literal lost its nominal type"
+  where
+    idPrefixOf nominal = case resolvedNominalRepresentation nominal of
+      IdRepresentation prefix -> Just prefix
+      _ -> Nothing
+
+generatedIdSampleHaskell :: Agg -> ResolvedNominalType -> Maybe Text
+generatedIdSampleHaskell aggregate nominal = do
+  prefix <- case resolvedNominalRepresentation nominal of
+    IdRepresentation value -> Just value
+    _ -> Nothing
+  contract <- idDomainContractFor (aLanguageContract aggregate) prefix
+  let name = resolvedNominalName nominal
+      sample = idDomainSampleText contract
+  pure
+    ( "(case parse"
+        <> name
+        <> " "
+        <> tshow sample
+        <> " of Right parsed -> parsed; Left _ -> error \"generated valid ID sample failed to parse\")"
+    )
+
+emitGeneratedTransducer :: Agg -> Text
+emitGeneratedTransducer aggregate =
+  nl $
+    [ "{-# LANGUAGE BlockArguments #-}",
+      "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE GADTs #-}",
+      "{-# LANGUAGE OverloadedRecordDot #-}"
+    ]
+      ++ ["{-# LANGUAGE OverloadedLabels #-}" | not (null projectionAliases)]
+      ++ [ "{-# LANGUAGE QualifiedDo #-}",
+           "{-# LANGUAGE TypeApplications #-}",
+           generatedBanner,
+           "module " <> aGenPrefix aggregate <> ".Transducer",
+           "  ( " <> lowerFirst (aName aggregate) <> "Transducer",
+           "  , " <> lowerFirst (aName aggregate) <> "FoldFingerprint",
+           "  , BehaviorOwnership (..)",
+           "  , " <> lowerFirst (aName aggregate) <> "PredicateVerifications",
+           "  ) where",
+           "",
+           "import " <> aGenPrefix aggregate <> ".Domain",
+           "import Data.Text (Text)"
+         ]
+      ++ ["import Data.Time.Calendar (fromGregorian)" | expressionUsesTimeLiteral]
+      ++ ["import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)" | expressionUsesTimeLiteral]
+      ++ ["import Numeric.Natural (Natural)" | expressionUsesNaturalLiteral]
+      ++ generatedNominalTypeImportsForService (aggregateCheckedService aggregate) (aContext aggregate) generatedExpressionNominals
+      ++ structuralProjectionImport
+      ++ generatedNominalProjectionImport
+      ++ consumerNominalProjectionImport
+      ++ consumerImports
+      ++ ["import Data.KindID qualified as KindID" | expressionUsesConsumerIdLiteral]
+      ++ ["import Keiro.Codec.Nominal (nominalFromRepresentation)" | expressionUsesConsumerNominalLiteral]
+      ++ consumerLiteralImports
+      ++ [ "import Keiki.Builder qualified as B",
+           "import Keiki.Core (" <> T.intercalate ", " keikiCoreImports <> ")",
+           "import Keiki.Core qualified as K",
+           "import Keiki.Symbolic qualified as S"
+         ]
+      ++ ["import " <> aHolePrefix aggregate <> ".Holes qualified as Holes" | transducerUsesHoles aggregate]
+      ++ ["import Data.Text qualified as T" | anyHoleOwned aggregate]
+      ++ ["import Keiki.Builder ((=:))" | any (not . null . tWrites . snd) (transitionEntries aggregate)]
+      ++ ["import Keiki.Generics (RegFieldsOf)" | not (null projectionAliases)]
+      ++ ["import Keiro.Snapshot.Codec (FoldVersion (..))" | anyHoleOwned aggregate]
+      ++ [ "",
+           lowerFirst (aName aggregate) <> "Transducer",
+           "  :: SymTransducer",
+           "       (HsPred " <> aName aggregate <> "Regs " <> aName aggregate <> "Command)",
+           "       " <> aName aggregate <> "Regs",
+           "       " <> aVertexType aggregate,
+           "       " <> aName aggregate <> "Command",
+           "       " <> aName aggregate <> "Event",
+           lowerFirst (aName aggregate) <> "Transducer =",
+           "  B.buildTransducer " <> initialVertex aggregate <> " initial" <> aName aggregate <> "Regs isTerminal do",
+           nl (concatMap (generatedFromBlock aggregate resolvedTransitions) (groupTransitionEntriesBySource aggregate)),
+           " where",
+           "  isTerminal = \\case",
+           nl ["    " <> vertexCtor aggregate (stName state) <> " -> True" | state <- aStates aggregate, stTerminal state],
+           "    _ -> False",
+           "",
+           lowerFirst (aName aggregate) <> "FoldFingerprint :: Text",
+           lowerFirst (aName aggregate) <> "FoldFingerprint = " <> foldFingerprintExpression aggregate,
+           "",
+           "data BehaviorOwnership = GeneratedOwned | HoleOwned",
+           "  deriving stock (Eq, Show)",
+           "",
+           "-- Every checked transition predicate is audited through Keiki's conservative",
+           "-- symbolic verifier. Opaque Hole terms remain explicitly unverified.",
+           lowerFirst (aName aggregate) <> "PredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]",
+           lowerFirst (aName aggregate) <> "PredicateVerifications = sequence",
+           nl (renderVerificationList aggregate),
+           " where",
+           "  verifyTransition label owner source edgeIndex =",
+           "    case drop edgeIndex (K.edgesOut " <> lowerFirst (aName aggregate) <> "Transducer source) of",
+           "      K.Edge predicate _ _ _ _ : _ -> (\\result -> (label, owner, result)) <$> S.verifyPredicate predicate",
+           "      [] -> pure (label, owner, S.UnverifiedSolverFailure \"generated transition edge missing\")"
+         ]
+  where
+    resolvedTransitions = resolvedGeneratedTransitions aggregate
+    resolvedExpressions = generatedTransitionExpressions resolvedTransitions
+    projectionAliases = concatMap projectionAliasesForTransition resolvedTransitions
+    projectionTargets = map projectionAliasTarget projectionAliases
+    structuralProjectionImport =
+      [ "import " <> structuralProjectionModule (aContext aggregate) <> " qualified as StructuralProjections"
+      | any isStructuralProjection projectionTargets
+      ]
+    generatedNominalProjectionImport =
+      [ "import " <> generatedNominalModule (aContext aggregate) <> " qualified as GeneratedNominals"
+      | any isGeneratedNominalProjection projectionTargets
+      ]
+    consumerNominalProjectionImport =
+      [ "import " <> nominalProjectionModule (aContext aggregate) <> " qualified as NominalProjections"
+      | any isConsumerNominalProjection projectionTargets
+      ]
+    consumerImports =
+      map ("import " <>)
+        . filter (not . builtinExpressionImport)
+        . sort
+        . nub
+        . Set.toList
+        . Set.unions
+        $ [ aggregateImports (aSymbols aggregate) resolvedType
+          | resolvedType <- expressionImportTypes
+          ]
+    expressionImportTypes = nub (concatMap typedExpressionImportTypes resolvedExpressions)
+    consumerLiteralImports =
+      [ "import " <> nominalRepresentationModule (aContext aggregate) (resolvedNominalName nominal) <> " qualified"
+      | nominal <- consumerLiteralNominals,
+        EnumRepresentation {} <- [resolvedNominalRepresentation nominal]
+      ]
+        <> [ "import " <> qualifiedModule (consumerNominalBinding binding) <> " qualified"
+           | nominal <- consumerLiteralNominals,
+             ConsumerNominal binding <- [resolvedNominalOwnership nominal]
+           ]
+    consumerLiteralNominals = nub [nominal | expression <- resolvedExpressions, nominal <- typedConsumerLiteralNominals expression]
+    generatedExpressionNominals =
+      stableNominals
+        [ nominal
+        | expression <- resolvedExpressions,
+          nominal <- typedGeneratedNominals expression
+        ]
+    expressionUsesTimeLiteral = any (anyTypedExpression isTimeLiteral) resolvedExpressions
+    expressionUsesNaturalLiteral = any (anyTypedExpression isNaturalLiteral) resolvedExpressions
+    expressionUsesConsumerNominalLiteral = not (null consumerLiteralNominals)
+    expressionUsesConsumerIdLiteral = any (isIdRepresentation . resolvedNominalRepresentation) consumerLiteralNominals
+    usedOperators = nub (concatMap generatedTransitionOperators resolvedTransitions)
+    keikiCoreImports = ["HsPred", "SymTransducer"] <> ["(" <> operator <> ")" | operator <- expressionOperatorOrder, operator `elem` usedOperators]
+    isTimeLiteral expression = case typedScalarNode expression of
+      TypedLiteral ScalarTimeValue {} -> True
+      _ -> False
+    isNaturalLiteral expression = case typedScalarNode expression of
+      TypedLiteral ScalarNaturalValue {} -> True
+      _ -> False
+    isIdRepresentation IdRepresentation {} = True
+    isIdRepresentation _ = False
+
+builtinExpressionImport :: Text -> Bool
+builtinExpressionImport imported =
+  any (`T.isPrefixOf` imported) ["Data.Text", "Data.Time", "Numeric.Natural"]
+
+typedExpressionImportTypes :: TypedScalarExpr -> [ResolvedAggregateType]
+typedExpressionImportTypes expression = own <> concatMap typedExpressionImportTypes (typedExpressionChildren expression)
+  where
+    own = case typedScalarNode expression of
+      TypedLiteral {} -> [typedScalarType expression]
+      TypedRoot provenance -> [scalarRootType provenance]
+      TypedProject provenance _ -> [scalarRootType provenance]
+      _ -> []
+
+scalarRootType :: ScalarRootProvenance -> ResolvedAggregateType
+scalarRootType = \case
+  ScalarRegisterRoot _ resolvedType -> resolvedType
+  ScalarCommandRoot _ resolvedType -> resolvedType
+
+isStructuralProjection :: ProjectionAliasTarget -> Bool
+isStructuralProjection StructuralProjectionAlias {} = True
+isStructuralProjection NominalProjectionAlias {} = False
+
+isGeneratedNominalProjection :: ProjectionAliasTarget -> Bool
+isGeneratedNominalProjection (NominalProjectionAlias nominal _) = resolvedNominalOwnership nominal == GeneratedNominal
+isGeneratedNominalProjection StructuralProjectionAlias {} = False
+
+isConsumerNominalProjection :: ProjectionAliasTarget -> Bool
+isConsumerNominalProjection (NominalProjectionAlias nominal _) = case resolvedNominalOwnership nominal of
+  ConsumerNominal {} -> True
+  GeneratedNominal -> False
+isConsumerNominalProjection StructuralProjectionAlias {} = False
+
+expressionOperatorOrder :: [Text]
+expressionOperatorOrder = [".*", ".+", ".-", ".==", "./=", ".<", ".<=", ".>", ".>=", ".&&", ".||"]
+
+generatedTransitionOperators :: ResolvedGeneratedTransition -> [Text]
+generatedTransitionOperators resolved =
+  maybe [] predicateOperators (resolvedTransitionGuard resolved)
+    <> concatMap (termOperators . snd) (resolvedTransitionWrites resolved)
+  where
+    predicateOperators expression = case typedScalarNode expression of
+      TypedEqual left right -> ".==" : termOperators left <> termOperators right
+      TypedNotEqual left right -> "./=" : termOperators left <> termOperators right
+      TypedCompare operator left right -> renderComparisonOperator operator : termOperators left <> termOperators right
+      TypedAnd left right -> ".&&" : predicateOperators left <> predicateOperators right
+      TypedOr left right -> ".||" : predicateOperators left <> predicateOperators right
+      _ -> ".==" : termOperators expression
+    termOperators expression = case typedScalarNode expression of
+      TypedAdd _ left right -> ".+" : termOperators left <> termOperators right
+      TypedSubtract _ left right -> ".-" : termOperators left <> termOperators right
+      TypedMultiply _ left right -> ".*" : termOperators left <> termOperators right
+      _ -> concatMap termOperators (typedExpressionChildren expression)
+
+anyHoleOwned :: Agg -> Bool
+anyHoleOwned = any ((== HoleImplementation) . tImplementation) . aTransitions
+
+transducerUsesHoles :: Agg -> Bool
+transducerUsesHoles aggregate =
+  anyHoleOwned aggregate
+    || any isHandOwned (Map.elems (aOutputMappings aggregate))
+  where
+    isHandOwned HandOwnedEventOutput {} = True
+    isHandOwned GeneratedCommandIdentity {} = False
+
+renderVerificationList :: Agg -> [Text]
+renderVerificationList aggregate =
+  [ (if listIndex == (0 :: Int) then "  [ " else "  , ")
+      <> "verifyTransition "
+      <> tshow (transitionStem transitionIndex transition)
+      <> " "
+      <> ownership
+      <> " "
+      <> vertexCtor aggregate source
+      <> " "
+      <> tshow' edgeIndex
+  | (listIndex, (source, edgeIndex, transitionIndex, transition)) <- zip [0 ..] entries,
+    let ownership = case tImplementation transition of
+          GeneratedImplementation -> "GeneratedOwned"
+          HoleImplementation -> "HoleOwned"
+          LegacyHoleImplementation -> error "legacy transition reached version-2 verification generation"
+  ]
+    <> ["  ]"]
+  where
+    entries =
+      [ (source, edgeIndex, transitionIndex, transition)
+      | (source, transitions) <- groupTransitionEntriesBySource aggregate,
+        (edgeIndex, (transitionIndex, transition)) <- zip [0 ..] transitions
+      ]
+
+foldFingerprintExpression :: Agg -> Text
+foldFingerprintExpression aggregate = case holeVersions of
+  [] -> tshow (aFoldFingerprint aggregate)
+  _ ->
+    "T.intercalate \"|\" ("
+      <> tshow (aFoldFingerprint aggregate)
+      <> " : [foldToken "
+      <> T.intercalate ", foldToken " holeVersions
+      <> "] ) where foldToken (FoldVersion token) = T.pack (show (T.length token)) <> \":\" <> token"
+  where
+    holeVersions =
+      [ "Holes." <> holeFoldVersionName index transition
+      | (index, transition) <- transitionEntries aggregate,
+        tImplementation transition == HoleImplementation
+      ]
+
+groupTransitionEntriesBySource :: Agg -> [(Text, [(Int, Transition)])]
+groupTransitionEntriesBySource aggregate = go [] (transitionEntries aggregate)
+  where
+    go accumulated [] = reverse accumulated
+    go accumulated (entry@(_, transition) : remaining) =
+      let source = tSource transition
+          (same, rest) = span ((== source) . tSource . snd) remaining
+       in go ((source, entry : same) : accumulated) rest
+
+generatedFromBlock :: Agg -> [ResolvedGeneratedTransition] -> (Text, [(Int, Transition)]) -> [Text]
+generatedFromBlock aggregate resolvedTransitions (source, transitions) =
+  ["    B.from " <> vertexCtor aggregate source <> " do"]
+    ++ concatMap (uncurry (generatedOnCmdBlock aggregate resolvedTransitions)) transitions
+
+generatedOnCmdBlock :: Agg -> [ResolvedGeneratedTransition] -> Int -> Transition -> [Text]
+generatedOnCmdBlock aggregate resolvedTransitions index transition =
+  ["      B.onCmd inCtor" <> tCommand transition <> " $ \\" <> payloadBinder <> " -> B.do"]
+    ++ projectionBindingLines
+    ++ ["        B.replayOnly" | tMode transition == TmReplayOnly]
+    ++ generatedBehavior
+    ++ outputLines
+    ++ ["        B.noEmit" | null (tEmits transition)]
+    ++ ["        B.goto " <> vertexCtor aggregate (tGoto transition)]
+  where
+    generatedBehavior = case tImplementation transition of
+      GeneratedImplementation ->
+        maybe [] (renderGuardLines aliases aggregate transition) (resolvedTransitionGuard resolved)
+          ++ [ "        B.slot @" <> tshow registerName <> " =: " <> renderAssignmentOperand (renderKeikiTerm aliases aggregate transition expression)
+             | (registerName, expression) <- resolvedTransitionWrites resolved
+             ]
+      HoleImplementation -> ["        Holes." <> holeFunctionName index transition <> " d"]
+      LegacyHoleImplementation -> error "legacy transition reached version-2 transducer generation"
+    resolved =
+      fromMaybe
+        (error ("resolved generated transition disappeared: " <> show index))
+        (find ((== index) . resolvedTransitionIndex) resolvedTransitions)
+    aliases
+      | tImplementation transition == GeneratedImplementation = projectionAliasesForTransition resolved
+      | otherwise = []
+    projectionBindingLines = case aliases of
+      [] -> []
+      firstAlias : remainingAliases ->
+        ["        let " <> renderProjectionAliasBinding aggregate transition firstAlias]
+          <> ["            " <> renderProjectionAliasBinding aggregate transition alias | alias <- remainingAliases]
+    outputLines =
+      concat
+        [ generatedOutputLines aggregate index transition emitIndex eventName
+        | (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
+        ]
+    payloadBinder
+      | payloadIsUsed = "d"
+      | otherwise = "_d"
+    payloadIsUsed = case tImplementation transition of
+      GeneratedImplementation ->
+        isJust (resolvedTransitionGuard resolved)
+          || not (null (resolvedTransitionWrites resolved))
+          || any outputUsesPayload (zip [1 ..] (tEmits transition))
+      HoleImplementation -> True
+      LegacyHoleImplementation -> True
+    outputUsesPayload (emitIndex, _) = case outputMappingFor aggregate index emitIndex of
+      GeneratedCommandIdentity _ fields -> not (null fields)
+      HandOwnedEventOutput {} -> True
+
+renderProjectionAliasBinding :: Agg -> Transition -> ProjectionAlias -> Text
+renderProjectionAliasBinding aggregate transition alias =
+  projectionAliasName alias <> " = " <> case projectionAliasTarget alias of
+    StructuralProjectionAlias provenance projection -> renderStructuralProjectionTerm aggregate transition provenance projection
+    NominalProjectionAlias nominal provenance -> renderNominalProjectionTerm aggregate transition nominal provenance
+
+renderGuardLines :: [ProjectionAlias] -> Agg -> Transition -> TypedScalarExpr -> [Text]
+renderGuardLines aliases aggregate transition expression =
+  ["        B.requireGuard $"]
+    <> ["          " <> line | line <- T.lines readable]
+  where
+    readable =
+      T.replace " .|| " "\n.|| "
+        . T.replace " .&& " "\n.&& "
+        $ renderKeikiPredicate aliases aggregate transition expression
+
+renderAssignmentOperand :: RenderedKeikiExpr -> Text
+renderAssignmentOperand expression
+  | renderedKeikiPrecedence expression <= 6 = "(" <> renderedKeikiText expression <> ")"
+  | otherwise = renderedKeikiText expression
+
+generatedOutputLines :: Agg -> Int -> Transition -> Int -> Name -> [Text]
+generatedOutputLines aggregate transitionIndex transition emitIndex eventName =
+  case outputMappingFor aggregate transitionIndex emitIndex of
+    GeneratedCommandIdentity _ fields -> case fields of
+      [] -> ["        B.emit wire" <> eventName <> " B.oNil"]
+      _ ->
+        [ "        B.emit wire" <> eventName <> " (" <> eventName <> "TermFields"
+        ]
+          <> [ lead fieldIndex
+                 <> outputSelector field
+                 <> " = d."
+                 <> outputSelector field
+             | (fieldIndex, field) <- zip [0 :: Int ..] fields
+             ]
+          <> ["          })"]
+    HandOwnedEventOutput {} ->
+      [ "        B.emit wire"
+          <> eventName
+          <> " (Holes."
+          <> outputFunctionName transitionIndex transition emitIndex eventName
+          <> " d)"
+      ]
+  where
+    lead 0 = "          { "
+    lead _ = "          , "
+
+outputMappingFor :: Agg -> Int -> Int -> EventOutputMapping
+outputMappingFor aggregate transitionIndex emitIndex =
+  fromMaybe
+    (error ("missing checked event-output mapping for transition " <> show transitionIndex <> ", emit " <> show emitIndex))
+    (Map.lookup (transitionIndex, emitIndex) (aOutputMappings aggregate))
+
+--------------------------------------------------------------------------------
+-- EventStream module
+--------------------------------------------------------------------------------
+
+emitEventStream :: Agg -> Text
+emitEventStream a =
+  nl $
+    [ generatedBanner,
+      "module " <> aGenPrefix a <> ".EventStream",
+      "  ( " <> lowerFirst (aName a) <> "Category",
+      "  , " <> lowerFirst (aName a) <> "CommandCategory",
+      "  , " <> lowerFirst (aName a) <> "EventStream",
+      "  , " <> lowerFirst (aName a) <> "EventStreamDef",
+      "  , " <> aName a <> "EventStream",
+      "  , " <> aName a <> "EventStreamDef"
+    ]
+      ++ ["  , " <> lowerFirst (aName a) <> "SnapshotFixture" | hasSnapshot a]
+      ++ [ "  ) where",
+           "",
+           "import " <> aGenPrefix a <> ".Domain",
+           "import " <> aGenPrefix a <> ".Codec (" <> lowerFirst (aName a) <> "Codec)",
+           transducerImport a,
+           "import Keiki.Core (HsPred)",
+           "import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))",
+           "import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)"
+         ]
+      ++ ["import Data.Text (Text)" | hasSnapshot a]
+      ++ ["import Keiro.Snapshot.Codec (defaultStateCodec, withFoldFingerprint)" | hasSnapshot a]
+      ++ [ "import Keiro.Stream qualified as Stream",
+           "",
+           "-- The validated aggregate stream category (hole-kind 5: referenced, never retyped).",
+           "-- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.",
+           "-- categoryUnsafe is safe here because this generated literal passed the DSL category proof.",
+           lowerFirst (aName a) <> "Category :: Stream.StreamCategory " <> aName a <> "EventStreamDef",
+           lowerFirst (aName a) <> "Category = Stream.categoryUnsafe " <> tshow categoryName,
+           "",
+           "-- The same category text, typed for command envelopes such as PMCommand.",
+           lowerFirst (aName a) <> "CommandCategory :: Stream.StreamCategory " <> aName a <> "Command",
+           lowerFirst (aName a) <> "CommandCategory = Stream.categoryUnsafe " <> tshow categoryName,
+           "",
+           "type " <> aName a <> "EventStreamDef =",
+           "  EventStream (HsPred " <> aName a <> "Regs " <> aName a <> "Command) " <> aName a <> "Regs " <> aVertexType a <> " " <> aName a <> "Command " <> aName a <> "Event",
+           "",
+           "type " <> aName a <> "EventStream =",
+           "  ValidatedEventStream (HsPred " <> aName a <> "Regs " <> aName a <> "Command) " <> aName a <> "Regs " <> aVertexType a <> " " <> aName a <> "Command " <> aName a <> "Event",
+           "",
+           lowerFirst (aName a) <> "EventStreamDef :: " <> aName a <> "EventStreamDef",
+           lowerFirst (aName a) <> "EventStreamDef =",
+           "  EventStream",
+           "    { transducer = " <> lowerFirst (aName a) <> "Transducer,",
+           "      initialState = " <> initialVertex a <> ",",
+           "      initialRegisters = initial" <> aName a <> "Regs,",
+           "      eventCodec = " <> lowerFirst (aName a) <> "Codec,",
+           "      resolveStreamName = Stream.streamName,",
+           "      snapshotPolicy = " <> snapshotPolicyExpr a <> ","
+         ]
+      ++ stateCodecFieldLines a
+      ++ [ "    }",
+           ""
+         ]
+      ++ snapshotFixtureLines a
+      ++ [ lowerFirst (aName a) <> "EventStream :: " <> aName a <> "EventStream",
+           lowerFirst (aName a) <> "EventStream =",
+           "  mkEventStreamOrThrow " <> tshow (aName a) <> " " <> lowerFirst (aName a) <> "EventStreamDef"
+         ]
+  where
+    categoryName = staticCategory ("aggregate " <> aName a) (lowerFirst (aName a))
+
+snapshotPolicyExpr :: Agg -> Text
+snapshotPolicyExpr aggregate = case aSnapshot aggregate of
+  Nothing -> "Never"
+  Just snapshot -> case snapPolicy snapshot of
+    SnapEvery interval -> "Every " <> tshow' interval
+    SnapOnTerminal -> "OnTerminal"
+
+stateCodecExpr :: Agg -> Text
+stateCodecExpr aggregate = case aSnapshot aggregate of
+  Nothing -> "Nothing"
+  Just snapshot ->
+    "Just (withFoldFingerprint "
+      <> foldFingerprintValue aggregate
+      <> " (defaultStateCodec "
+      <> tshow' (snapCodecVersion snapshot)
+      <> "))"
+
+transducerImport :: Agg -> Text
+transducerImport aggregate
+  | hasVersion2Ownership aggregate =
+      "import "
+        <> aGenPrefix aggregate
+        <> ".Transducer ("
+        <> lowerFirst (aName aggregate)
+        <> "FoldFingerprint, "
+        <> lowerFirst (aName aggregate)
+        <> "Transducer)"
+  | otherwise =
+      "import "
+        <> aHolePrefix aggregate
+        <> ".Holes ("
+        <> lowerFirst (aName aggregate)
+        <> "Transducer)"
+
+foldFingerprintValue :: Agg -> Text
+foldFingerprintValue aggregate
+  | hasVersion2Ownership aggregate = lowerFirst (aName aggregate) <> "FoldFingerprint"
+  | otherwise = tshow (aFoldFingerprint aggregate)
+
+stateCodecFieldLines :: Agg -> [Text]
+stateCodecFieldLines aggregate = case aSnapshot aggregate of
+  Nothing -> ["      stateCodec = Nothing"]
+  Just _
+    | hasVersion2Ownership aggregate ->
+        [ "      -- The snapshot discriminator composes: the spec's state-codec version (bump it",
+          "      -- in the spec's `state-codec version=` clause), keiki's register and",
+          "      -- control-state shape hashes, and this fold fingerprint derived from the",
+          "      -- spec's transition surface (guards, writes, emits, states, register",
+          "      -- initials, referenced rules). Spec-visible fold changes invalidate old",
+          "      -- snapshots automatically. Version-2 Hole-owned transitions additionally",
+          "      -- compose their explicit hand-owned FoldVersion tokens here; bump the",
+          "      -- corresponding token whenever that Hole behavior changes.",
+          "      stateCodec = " <> stateCodecExpr aggregate
+        ]
+    | otherwise ->
+        [ "      -- The snapshot discriminator composes: the spec's state-codec version (bump it",
+          "      -- in the spec's `state-codec version=` clause), keiki's register and",
+          "      -- control-state shape hashes, and this fold fingerprint derived from the",
+          "      -- spec's transition surface (guards, writes, emits, states, register",
+          "      -- initials, referenced rules). Spec-visible fold changes invalidate old",
+          "      -- snapshots automatically. Fold changes made ONLY in the hand-owned Holes",
+          "      -- module are invisible here: bump `state-codec version=` manually or old",
+          "      -- snapshots will be served stale.",
+          "      stateCodec = " <> stateCodecExpr aggregate
+        ]
+
+snapshotFixtureLines :: Agg -> [Text]
+snapshotFixtureLines aggregate = case aSnapshot aggregate of
+  Nothing -> []
+  Just snapshot ->
+    [ lowerFirst (aName aggregate) <> "SnapshotFixture :: (Int, Text)",
+      lowerFirst (aName aggregate) <> "SnapshotFixture = (" <> tshow' (snapCodecVersion snapshot) <> ", " <> tshow (snapShapeHash snapshot) <> ")",
+      ""
+    ]
+
+--------------------------------------------------------------------------------
+-- Projection module
+--------------------------------------------------------------------------------
+
+emitProjection :: Agg -> Text
+emitProjection a = case aProjection a of
+  Nothing -> nl [generatedBanner, "module " <> aGenPrefix a <> ".Projection () where"]
+  Just p ->
+    nl
+      [ "{-# LANGUAGE OverloadedRecordDot #-}",
+        generatedBanner,
+        "module " <> aGenPrefix a <> ".Projection",
+        "  ( " <> lowerFirst (projTable p) <> "Projection",
+        "  , " <> lowerFirst (projTable p) <> "StatusFor",
+        "  ) where",
+        "",
+        "import " <> aGenPrefix a <> ".Domain",
+        "import " <> aHolePrefix a <> ".Holes (apply" <> pascal (projTable p) <> ")",
+        "import Data.Text (Text)",
+        "import Keiro.Projection (InlineProjection (..))",
+        "",
+        "-- The deterministic event->status mapping (hole-kind 3, /mapping/), derived",
+        "-- from the spec's status-map. The read-model SQL that consumes it lives in",
+        "-- the hand-owned Holes module (a DB-coupled hole, delegated to codd).",
+        projectionTableComment a p,
+        lowerFirst (projTable p) <> "StatusFor :: " <> aName a <> "Event -> Maybe Text",
+        lowerFirst (projTable p) <> "StatusFor = \\case",
+        nl (statusArms a p),
+        "",
+        lowerFirst (projTable p) <> "Projection :: InlineProjection " <> aName a <> "Event",
+        lowerFirst (projTable p) <> "Projection =",
+        "  InlineProjection",
+        "    { name = " <> tshow (contextNameToProjName a p),
+        "    , apply = apply" <> pascal (projTable p),
+        "    }"
+      ]
+
+statusArms :: Agg -> ProjectionSpec -> [Text]
+statusArms a p =
+  [ "  " <> rcName e <> " {} -> " <> statusFor e
+  | e <- aEvents a
+  ]
+    ++ ["  _ -> Nothing" | hasWildcard]
+  where
+    pairs = maybe [] mapPairs (projStatusMap p)
+    statusFor e = case lookup (rcName e) pairs of
+      Just value -> "Just " <> tshow value
+      Nothing -> "Nothing"
+    -- A wildcard is only needed if some event is uncovered; otherwise every arm
+    -- is explicit and a wildcard would be redundant (and -Wall would warn).
+    hasWildcard = False
+
+contextNameToProjName :: Agg -> ProjectionSpec -> Text
+contextNameToProjName a p = contextKebab a <> "-" <> projTable p <> "-inline"
+
+contextKebab :: Agg -> Text
+contextKebab = kebabFromPascal . aCtxPascal
+
+projectionReadModel :: Agg -> Maybe ReadModelNode
+projectionReadModel aggregate = do
+  projection <- aProjection aggregate
+  find ((== projTable projection) . rmName) (aReadModels aggregate)
+
+projectionTableComment :: Agg -> ProjectionSpec -> Text
+projectionTableComment aggregate projection = case projectionReadModel aggregate of
+  Nothing ->
+    "-- WARNING: no readmodel node declares '"
+      <> projTable projection
+      <> "'; unqualified SQL depends on search_path."
+  Just readModel ->
+    "-- Qualified table "
+      <> qualifiedTableLiteral readModel
+      <> "; use "
+      <> genPrefixFor (aContext aggregate) (pascal (rmName readModel))
+      <> ".ReadModelTable."
+      <> readModelStem readModel
+      <> "QualifiedTable."
+
+--------------------------------------------------------------------------------
+-- Holes module (create-if-absent)
+--------------------------------------------------------------------------------
+
+emitHoles :: Agg -> Text
+emitHoles aggregate
+  | hasVersion2Ownership aggregate = emitVersion2Holes aggregate
+  | otherwise = emitLegacyHoles aggregate
+
+emitLegacyHoles :: Agg -> Text
+emitLegacyHoles a =
+  nl
+    [ "{-# LANGUAGE BlockArguments #-}",
+      "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE OverloadedRecordDot #-}",
+      "{-# LANGUAGE QualifiedDo #-}",
+      "{-# LANGUAGE TypeApplications #-}",
+      "-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never",
+      "-- overwrites it. Fill the transducer body (and any other holes) against the",
+      "-- generated signatures, then run the harness to confirm behaviour.",
+      "module " <> aHolePrefix a <> ".Holes",
+      "  ( " <> lowerFirst (aName a) <> "Transducer",
+      holeProjectionExport a,
+      holeUpcasterExports a,
+      "  ) where",
+      "",
+      "import " <> aGenPrefix a <> ".Domain",
+      "import Keiki.Builder ((=:))",
+      "import qualified Keiki.Builder as B",
+      "import Keiki.Core (HsPred, RegFile, SymTransducer, lit, (.==), (./=), (.||))",
+      holeUpcasterImports a,
+      holeProjectionImports a,
+      "",
+      "-- HOLE: the transducer body. Reproduce the structure below, replacing each",
+      "-- `-- HOLE` line with the keiki symbolic operators it describes.",
+      lowerFirst (aName a) <> "Transducer",
+      "  :: SymTransducer",
+      "       (HsPred " <> aName a <> "Regs " <> aName a <> "Command)",
+      "       " <> aName a <> "Regs",
+      "       " <> aVertexType a,
+      "       " <> aName a <> "Command",
+      "       " <> aName a <> "Event",
+      lowerFirst (aName a) <> "Transducer =",
+      "  B.buildTransducer " <> initialVertex a <> " initial" <> aName a <> "Regs isTerminal do",
+      nl (concatMap (fromBlock a) (groupBySource a)),
+      " where",
+      "  isTerminal = \\case",
+      nl ["    " <> vertexCtor a (stName s) <> " -> True" | s <- aStates a, stTerminal s],
+      "    _ -> False",
+      holeProjectionStub a,
+      holeUpcasterStubs a
+    ]
+
+emitVersion2Holes :: Agg -> Text
+emitVersion2Holes aggregate =
+  nl $
+    [ "{-# LANGUAGE BlockArguments #-}",
+      "{-# LANGUAGE DataKinds #-}",
+      "{-# LANGUAGE DuplicateRecordFields #-}",
+      "{-# LANGUAGE OverloadedRecordDot #-}",
+      "{-# LANGUAGE QualifiedDo #-}",
+      "{-# LANGUAGE TypeApplications #-}",
+      "-- This is a HAND-OWNED version-2 hook module. keiro-dsl creates it once",
+      "-- and never overwrites it. Generated code owns every transition envelope",
+      "-- and every declared guard/write. This module supplies explicit event-field",
+      "-- mappings and explicitly selected Hole behavior only; fields(Command)",
+      "-- identity mappings are generated directly and have no hook."
+    ]
+      ++ version2HoleModuleDeclaration aggregate
+      ++ [ "",
+           "import " <> aGenPrefix aggregate <> ".Domain",
+           "import Keiki.Builder qualified as B",
+           "import Keiki.Generics (RegFieldsOf)",
+           holeUpcasterImports aggregate,
+           holeProjectionImports aggregate
+         ]
+      ++ ["import Keiki.Core qualified as K" | anyHoleOwned aggregate || anyZeroFieldOutput aggregate]
+      ++ ["import Keiro.Snapshot.Codec (FoldVersion (..))" | anyHoleOwned aggregate]
+      ++ concatMap (uncurry (emitOutputHooks aggregate)) (transitionEntries aggregate)
+      ++ concatMap (uncurry (emitHoleImplementation aggregate)) (transitionEntries aggregate)
+      ++ [holeProjectionStub aggregate, holeUpcasterStubs aggregate]
+
+anyZeroFieldOutput :: Agg -> Bool
+anyZeroFieldOutput aggregate =
+  or
+    [ isHandOwned (outputMappingFor aggregate transitionIndex emitIndex)
+        && null (rcFields (eventForName aggregate eventName))
+    | (transitionIndex, transition) <- transitionEntries aggregate,
+      (emitIndex, eventName) <- zip [1 ..] (tEmits transition)
+    ]
+  where
+    isHandOwned HandOwnedEventOutput {} = True
+    isHandOwned GeneratedCommandIdentity {} = False
+
+version2HoleModuleDeclaration :: Agg -> [Text]
+version2HoleModuleDeclaration aggregate = case version2HoleExports aggregate of
+  [] -> ["module " <> aHolePrefix aggregate <> ".Holes () where"]
+  firstExport : rest ->
+    [ "module " <> aHolePrefix aggregate <> ".Holes",
+      "  ( " <> firstExport
+    ]
+      ++ ["  , " <> value | value <- rest]
+      ++ ["  ) where"]
+
+version2HoleExports :: Agg -> [Text]
+version2HoleExports aggregate =
+  outputExports
+    <> holeExports
+    <> projectionExports
+    <> [functionName | (_, _, functionName) <- upcasterEntries aggregate]
+  where
+    outputExports =
+      [ outputFunctionName transitionIndex transition emitIndex eventName
+      | (transitionIndex, transition) <- transitionEntries aggregate,
+        (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
+        HandOwnedEventOutput {} <- [outputMappingFor aggregate transitionIndex emitIndex]
+      ]
+    holeExports =
+      concat
+        [ [holeFunctionName index transition, holeFoldVersionName index transition]
+        | (index, transition) <- transitionEntries aggregate,
+          tImplementation transition == HoleImplementation
+        ]
+    projectionExports = case aProjection aggregate of
+      Nothing -> []
+      Just projection -> ["apply" <> pascal (projTable projection)]
+
+emitOutputHooks :: Agg -> Int -> Transition -> [Text]
+emitOutputHooks aggregate transitionIndex transition =
+  concat
+    [ emitOutputHook aggregate transitionIndex transition emitIndex (eventForName aggregate eventName)
+    | (emitIndex, eventName) <- zip [1 ..] (tEmits transition),
+      HandOwnedEventOutput {} <- [outputMappingFor aggregate transitionIndex emitIndex]
+    ]
+
+emitOutputHook :: Agg -> Int -> Transition -> Int -> ResolvedCtor -> [Text]
+emitOutputHook aggregate transitionIndex transition emitIndex event =
+  [ "",
+    "-- Hand-owned event-field hook inside the generated transition envelope.",
+    functionName
+      <> " :: "
+      <> payloadProjectionType aggregate transition
+      <> " -> "
+      <> outputType,
+    functionName <> " d = " <> outputValue
+  ]
+  where
+    functionName = outputFunctionName transitionIndex transition emitIndex (rcName event)
+    inputFields = "(" <> commandFieldsType transition <> ")"
+    outputType
+      | null (rcFields event) =
+          "K.OutFields "
+            <> aName aggregate
+            <> "Regs "
+            <> aName aggregate
+            <> "Command "
+            <> inputFields
+            <> " ()"
+      | otherwise =
+          rcName event
+            <> "TermFields "
+            <> aName aggregate
+            <> "Regs "
+            <> aName aggregate
+            <> "Command "
+            <> inputFields
+    outputValue
+      | null (rcFields event) = "B.oNil"
+      | otherwise =
+          rcName event
+            <> "TermFields\n"
+            <> nl (valueRecord [(fieldName, outputFieldValue fieldName fieldType) | (fieldName, fieldType) <- rcFields event])
+    command = commandForTransition aggregate transition
+    outputFieldValue fieldName fieldType
+      | Just commandType <- lookup fieldName (rcFields command),
+        commandType == fieldType =
+          "d." <> fieldName
+      | Just register <- find ((== fieldName) . rrName) (aRegs aggregate),
+        rrType register == fieldType =
+          "B.reg @" <> tshow fieldName
+      | otherwise = "error " <> tshow ("HOLE: fill output field " <> rcName event <> "." <> fieldName)
+    valueRecord fields =
+      [ lead fieldIndex <> fieldName <> " = " <> fieldValue
+      | (fieldIndex, (fieldName, fieldValue)) <- zip [0 :: Int ..] fields
+      ]
+        ++ ["  }"]
+    lead 0 = "  { "
+    lead _ = "  , "
+
+emitHoleImplementation :: Agg -> Int -> Transition -> [Text]
+emitHoleImplementation _ index transition
+  | tImplementation transition /= HoleImplementation = []
+  | otherwise =
+      [ "",
+        "-- HOLE: add the predicate and ordered register updates for this transition.",
+        "-- The generated transducer still owns command matching, mode, emits, and goto.",
+        holeFunctionName index transition <> " _d = B.requireGuard K.PTop",
+        "",
+        "-- Bump this token whenever the Hole predicate or updates change.",
+        holeFoldVersionName index transition <> " :: FoldVersion",
+        holeFoldVersionName index transition <> " = FoldVersion " <> tshow (transitionStem index transition <> "-fold-v1")
+      ]
+
+-- | Export, import, and stub the per-event upcaster holes (EP-2 evolution).
+holeUpcasterExports :: Agg -> Text
+holeUpcasterExports a = case upcasterEntries a of
+  [] -> ""
+  es -> nl ["  , " <> fn | (_, _, fn) <- es]
+
+holeUpcasterImports :: Agg -> Text
+holeUpcasterImports a = case upcasterEntries a of
+  [] -> ""
+  _ -> nl ["import Data.Aeson (Value)", "import Data.Text (Text)"]
+
+holeUpcasterStubs :: Agg -> Text
+holeUpcasterStubs a = case upcasterEntries a of
+  [] -> ""
+  es ->
+    nl $
+      concat
+        [ [ "",
+            "-- HOLE upcaster: this hole receives ONLY " <> eventName <> " payloads stored at",
+            "-- aggregate schema version " <> tshow' source <> "; other event kinds pass through the",
+            "-- generated rung dispatch automatically. Bring this payload up one version and decide",
+            "-- the default/derivation for any field added at the new version here.",
+            fn <> " :: Value -> Either Text Value",
+            fn <> " _ = Left \"HOLE: upcaster not implemented\""
+          ]
+        | (source, eventName, fn) <- es
+        ]
+
+holeProjectionExport :: Agg -> Text
+holeProjectionExport a = case aProjection a of
+  Nothing -> "  -- (no projection)"
+  Just p -> "  , apply" <> pascal (projTable p)
+
+holeProjectionImports :: Agg -> Text
+holeProjectionImports aggregate = case projectionReadModel aggregate of
+  Nothing -> ""
+  Just readModel ->
+    "import "
+      <> genPrefixFor (aContext aggregate) (pascal (rmName readModel))
+      <> ".ReadModelTable ("
+      <> readModelStem readModel
+      <> "QualifiedTable)"
+
+holeProjectionStub :: Agg -> Text
+holeProjectionStub a = case aProjection a of
+  Nothing -> ""
+  Just p ->
+    nl
+      ( [ "",
+          "-- HOLE: the read-model SQL for the projection (a DB-coupled hole; the",
+          "-- pure event->status mapping is generated as " <> lowerFirst (projTable p) <> "StatusFor)."
+        ]
+          ++ projectionGuidance
+          ++ [ "apply" <> pascal (projTable p) <> " :: " <> aName a <> "Event -> recorded -> txn ()",
+               "apply" <> pascal (projTable p) <> " _event _recorded = " <> projectionTableUse <> "error \"HOLE: fill " <> projTable p <> " projection apply\""
+             ]
+      )
+    where
+      projectionGuidance = case projectionReadModel a of
+        Nothing ->
+          ["-- WARNING: no readmodel node declares this table's schema; unqualified SQL depends on search_path."]
+        Just readModel ->
+          [ "-- Table: " <> qualifiedTableLiteral readModel <> ". Use " <> readModelStem readModel <> "QualifiedTable; never rely on search_path.",
+            "-- Declared columns:"
+          ]
+            ++ map (("--   " <>) . readModelColumnDoc) (rmColumns readModel)
+      projectionTableUse = case projectionReadModel a of
+        Nothing -> ""
+        Just readModel -> readModelStem readModel <> "QualifiedTable `seq` "
+
+-- Group transitions by source state, preserving order, for the B.from blocks.
+groupBySource :: Agg -> [(Text, [Transition])]
+groupBySource a = go [] (transitionsOf a)
+  where
+    go acc [] = reverse acc
+    go acc (t : ts) =
+      let src = tSource t
+          (same, rest) = span ((== src) . tSource) ts
+       in go ((src, t : same) : acc) rest
+
+-- We don't keep the original Aggregate around in Agg, so reconstruct
+-- transitions from a stored field. (Filled in resolveAgg via aTransitions.)
+transitionsOf :: Agg -> [Transition]
+transitionsOf = aTransitions
+
+fromBlock :: Agg -> (Text, [Transition]) -> [Text]
+fromBlock a (src, ts) =
+  [ "    B.from " <> vertexCtor a src <> " do"
+  ]
+    ++ concatMap (onCmdBlock a) ts
+
+onCmdBlock :: Agg -> Transition -> [Text]
+onCmdBlock a t =
+  [ "      B.onCmd inCtor" <> tCommand t <> " $ \\d -> B.do"
+  ]
+    -- Plan 143: the mode is structural, not hole-owned — a replay-only
+    -- transition lowers to B.replayOnly (keiki ReplayOnly edge).
+    ++ ["        B.replayOnly" | tMode t == TmReplayOnly]
+    ++ maybe [] (\g -> ["        -- HOLE guard: " <> renderGuard g]) (tGuard t)
+    ++ ["        -- HOLE write " <> r <> " := " <> renderGuard e | (r, e) <- tWrites t]
+    ++ ["        -- HOLE emit " <> ev <> " (B.emit wire" <> ev <> " ...)" | ev <- tEmits t]
+    ++ ["        B.goto " <> vertexCtor a (tGoto t)]
+
+--------------------------------------------------------------------------------
+-- Field categories and shared helpers
+--------------------------------------------------------------------------------
+
+data FieldCat
+  = IdCat
+  | EnumCat
+  | MappedStructuralCat !StructuralDecl !ResolvedMappedShape
+  | MappedOpaqueCat !OpaqueDecl
+  | OtherCat
+  deriving stock (Eq, Show)
+
+fieldCat :: Agg -> ResolvedAggregateType -> FieldCat
+fieldCat a ty
+  | AggregateNominal nominal <- ty,
+    IdRepresentation {} <- resolvedNominalRepresentation nominal =
+      IdCat
+  | AggregateNominal nominal <- ty,
+    EnumRepresentation {} <- resolvedNominalRepresentation nominal =
+      EnumCat
+  | Just (ResolvedStructural declaration shape) <- mappedDeclFor a ty = MappedStructuralCat declaration shape
+  | Just (ResolvedOpaque declaration) <- mappedDeclFor a ty = MappedOpaqueCat declaration
+  | otherwise = OtherCat
+
+-- | The first constructor of a declared enum, used to build sample values.
+firstEnumCtor :: Agg -> Text -> Maybe Text
+firstEnumCtor a ty =
+  case [c | e <- aEnums a, enumName e == ty, (c, _) <- take 1 (enumCtors e)] of
+    (c : _) -> Just c
+    [] -> Nothing
+
+vertexCtor :: Agg -> Text -> Text
+vertexCtor a s = aName a <> s
+
+initialVertex :: Agg -> Text
+initialVertex a = case aStates a of
+  (s : _) -> vertexCtor a (stName s)
+  [] -> aName a <> "Init"
+
+generatedBanner :: Text
+generatedBanner = "-- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec."
+
+-- | The provenance line written by a concrete scaffold plan. The package
+-- version comes from Cabal's build metadata, while the language and origin come
+-- from the checked semantic input and the emitted module respectively.
+generatedBannerFor :: EffectiveLanguageContract -> Text -> Text
+generatedBannerFor languageContract sourceOrigin =
+  "-- @generated by keiro-dsl "
+    <> T.pack (showVersion Package.version)
+    <> " (language keiro-dsl "
+    <> languageVersionText (effectiveContractLanguageVersion languageContract)
+    <> ") from "
+    <> stableBannerOrigin sourceOrigin
+    <> "; do not edit."
+
+-- Source line numbers are useful refusal metadata but are not stable ownership:
+-- moving an unchanged node within or between workspace members must not rewrite
+-- every Generated file. The node kind and name remain the banner authority.
+stableBannerOrigin :: Text -> Text
+stableBannerOrigin sourceOrigin =
+  case T.breakOnEnd " (line " withoutMemberPath of
+    (prefixWithMarker, lineWithClose)
+      | not (T.null prefixWithMarker),
+        Just lineNumber <- T.stripSuffix ")" lineWithClose,
+        not (T.null lineNumber),
+        T.all isDigit lineNumber ->
+          T.dropEnd (T.length " (line ") prefixWithMarker
+    _ -> withoutMemberPath
+  where
+    withoutMemberPath = case T.breakOn ": " sourceOrigin of
+      (memberPath, attributedOrigin)
+        | ".keiro" `T.isSuffixOf` memberPath,
+          not (T.null attributedOrigin) ->
+            T.drop 2 attributedOrigin
+      _ -> sourceOrigin
+
+-- | Recognize the exact historical banner or the frozen stamped format. This
+-- intentionally rejects arbitrary comments that merely start with
+-- @-- \@generated@.
+isGeneratedBannerLine :: Text -> Bool
+isGeneratedBannerLine line =
+  line == generatedBanner
+    || ( stampedPrefix `T.isPrefixOf` line
+           && " (language keiro-dsl " `T.isInfixOf` line
+           && ") from " `T.isInfixOf` line
+           && "; do not edit." `T.isSuffixOf` line
+       )
+  where
+    stampedPrefix = "-- @generated by keiro-dsl "
+
+-- | Replace an emitter's legacy placeholder (or an earlier stamp) with the
+-- provenance for this plan. A Generated module with a specialized banner gets
+-- the standard stamp prepended, so every planned Generated file is covered.
+stampGeneratedModule :: EffectiveLanguageContract -> ScaffoldModule -> ScaffoldModule
+stampGeneratedModule languageContract moduleValue
+  | kind moduleValue == HoleStub = moduleValue
+  | otherwise = moduleValue {moduleText = stampedText}
+  where
+    banner = generatedBannerFor languageContract (origin moduleValue)
+    sourceLines = T.splitOn "\n" (moduleText moduleValue)
+    stampedText = case replaceFirstGeneratedBanner banner sourceLines of
+      Nothing -> banner <> "\n" <> moduleText moduleValue
+      Just linesWithStamp -> T.intercalate "\n" linesWithStamp
+
+stampGeneratedModules :: EffectiveLanguageContract -> [ScaffoldModule] -> [ScaffoldModule]
+stampGeneratedModules languageContract = map (stampGeneratedModule languageContract)
+
+replaceFirstGeneratedBanner :: Text -> [Text] -> Maybe [Text]
+replaceFirstGeneratedBanner _ [] = Nothing
+replaceFirstGeneratedBanner replacement (line : rest)
+  | isGeneratedBannerLine line = Just (replacement : rest)
+  | otherwise = (line :) <$> replaceFirstGeneratedBanner replacement rest
 
 nodeOrigin :: Text -> Text -> Loc -> Text
 nodeOrigin nodeKind nodeName loc =
diff --git a/src/Keiro/Dsl/ScaffoldRun.hs b/src/Keiro/Dsl/ScaffoldRun.hs
--- a/src/Keiro/Dsl/ScaffoldRun.hs
+++ b/src/Keiro/Dsl/ScaffoldRun.hs
@@ -3,6 +3,7 @@
 module Keiro.Dsl.ScaffoldRun
   ( Refusal (..),
     WriteDisposition (..),
+    StaleGeneratedEvidence (..),
     StaleModule (..),
     MappingDrift (..),
     SourceLanguageDrift (..),
@@ -45,12 +46,13 @@
 import Data.Text.IO qualified as TIO
 import Keiro.Dsl.BehaviorCoverage (BehaviorDerivationError, BehaviorKey (..), BehaviorRecordRow (..), behaviorRecordRows, deriveBehaviorRequirements)
 import Keiro.Dsl.ExplainBindings (BindingHole (..), BindingObligationKind (..), bindingHolesForService)
+import Keiro.Dsl.FoldFingerprint (FoldSurfaceError, aggregateFoldSurfaceForService, renderFoldSurfaceError)
 import Keiro.Dsl.Goldens (GoldenPayload)
 import Keiro.Dsl.Grammar (Node (..), Spec (..))
 import Keiro.Dsl.Harness (harnessForServiceWithGoldens, harnessProcess, harnessReadModel, harnessRouter, harnessWorkflow)
 import Keiro.Dsl.IdDomain (idDomainIdentitiesForService)
 import Keiro.Dsl.LanguageVersion (SourceLanguage (..), effectiveLanguageVersion, languageVersionText, sourceFormText)
-import Keiro.Dsl.Manifest (moduleNameOf, renderManifest)
+import Keiro.Dsl.Manifest (moduleNameOf, renderManifestForService)
 import Keiro.Dsl.MappedConsumer (ConsumerPlan (..), MappingIdentity (..), consumerPlan)
 import Keiro.Dsl.NominalType (nominalEqualityIdentitiesForService)
 import Keiro.Dsl.Scaffold
@@ -75,6 +77,8 @@
   | MissingGeneratedBanner ![FilePath]
   | ImportCycle ![Text]
   | BehaviorRefusal ![BehaviorDerivationError]
+  | -- | Persisted fold identity could not be resolved canonically.
+    FoldSurfaceRefusal !FoldSurfaceError
   | -- | Source provenance and semantic planning selected different contracts.
     --       This is an internal/API misuse refusal and is detected before writes.
     SemanticContractMismatch !Text
@@ -90,9 +94,15 @@
 data WriteDisposition = Overwritten | Created | Skipped | Unchanged
   deriving stock (Eq, Show)
 
+data StaleGeneratedEvidence
+  = ExactGeneratedBannerPresent
+  | ExactGeneratedBannerMissing
+  deriving stock (Eq, Show)
+
 data StaleModule = StaleModule
   { staleKind :: !ModuleKind,
-    stalePath :: !FilePath
+    stalePath :: !FilePath,
+    staleGeneratedEvidence :: !(Maybe StaleGeneratedEvidence)
   }
   deriving stock (Eq, Show)
 
@@ -144,7 +154,7 @@
           NAggregate agg -> scaffoldAggregateForService ctx service agg <> harnessForServiceWithGoldens goldens ctx service agg
           NProcess process -> scaffoldProcess ctx process <> harnessProcess ctx process
           NRouter router -> scaffoldRouter ctx router <> harnessRouter ctx router
-          NContract contract -> scaffoldContract ctx contract
+          NContract contract -> scaffoldContractForService ctx service contract
           NIntake intake -> scaffoldIntake ctx intake
           NPublisher publisher -> scaffoldPublisher ctx publisher
           NWorkqueue workqueue -> scaffoldWorkqueue ctx workqueue
@@ -171,13 +181,15 @@
 
 planServiceScaffoldWithGoldens :: [GoldenPayload] -> Context -> CheckedService -> Either [Refusal] [ScaffoldModule]
 planServiceScaffoldWithGoldens goldens ctx service =
-  case scaffoldRefusals spec of
-    lowering@(_ : _) -> Left [LoweringRefusal lowering]
-    [] ->
-      let modules = scaffoldServiceModulesWithGoldens goldens ctx service
-       in case pureRefusals ctx spec modules of
-            [] -> Right modules
-            refusals -> Left refusals
+  case traverse (aggregateFoldSurfaceForService service) [aggregate | NAggregate aggregate <- specNodes spec] of
+    Left surfaceError -> Left [FoldSurfaceRefusal surfaceError]
+    Right _ -> case scaffoldRefusals spec of
+      lowering@(_ : _) -> Left [LoweringRefusal lowering]
+      [] ->
+        let modules = stampGeneratedModules (checkedLanguageContract service) (scaffoldServiceModulesWithGoldens goldens ctx service)
+         in case pureRefusals ctx spec modules of
+              [] -> Right modules
+              refusals -> Left refusals
   where
     spec = checkedSpec service
 
@@ -279,12 +291,13 @@
 -- and the source declaration provenance written to history. A mismatch refuses
 -- before checking or creating any output path.
 executeServiceScaffold :: FilePath -> Bool -> FilePath -> SourceLanguage -> Context -> CheckedService -> [ScaffoldModule] -> IO (Either [Refusal] ScaffoldReport)
-executeServiceScaffold out forceGeneratedOverwrite specPath sourceLanguage ctx service modules
+executeServiceScaffold out forceGeneratedOverwrite specPath sourceLanguage ctx service plannedModules
   | effectiveLanguageContract sourceLanguage /= checkedLanguageContract service =
       pure (Left [SemanticContractMismatch "source provenance and checked service selected different effective language contracts"])
   | otherwise = executeCheckedScaffold
   where
     spec = checkedSpec service
+    modules = stampGeneratedModules (checkedLanguageContract service) plannedModules
     executeCheckedScaffold =
       case deriveBehaviorRequirements spec of
         Left errors -> pure (Left [BehaviorRefusal errors])
@@ -310,7 +323,7 @@
               createDirectoryIfMissing True out
               dispositions <- mapM (writeModule out) modules
               let manifestPath = out </> ("keiro-dsl-manifest." <> T.unpack (specContext spec) <> ".txt")
-              TIO.writeFile manifestPath (renderManifest (T.pack specPath) modules spec)
+              TIO.writeFile manifestPath (renderManifestForService (T.pack specPath) modules service)
               TIO.writeFile recordPath (renderRecord (currentRecord specPath sourceLanguage ctx service modules currentBehavior))
               pure $
                 Right
@@ -402,8 +415,20 @@
     currentPaths = Set.fromList currentPathList
     removed = [(fileKind, path) | (fileKind, path) <- previous, path `Set.notMember` currentPaths]
     stillExists (fileKind, path) = do
-      exists <- doesFileExist (out </> path)
-      pure [StaleModule fileKind path | exists]
+      let fullPath = out </> path
+      exists <- doesFileExist fullPath
+      if not exists
+        then pure []
+        else do
+          evidence <- case fileKind of
+            HoleStub -> pure Nothing
+            Generated -> do
+              contents <- TIO.readFile fullPath
+              pure . Just $
+                if any isGeneratedBannerLine (T.lines contents)
+                  then ExactGeneratedBannerPresent
+                  else ExactGeneratedBannerMissing
+          pure [StaleModule fileKind path evidence]
 
 currentRecord :: FilePath -> SourceLanguage -> Context -> CheckedService -> [ScaffoldModule] -> [BehaviorRecordRow] -> ScaffoldRecord
 currentRecord specPath sourceLanguage ctx service modules currentBehavior =
@@ -434,7 +459,7 @@
         then pure []
         else do
           contents <- TIO.readFile path
-          pure [modulePath m | not (any (T.isPrefixOf "-- @generated") (T.lines contents))]
+          pure [modulePath m | not (any isGeneratedBannerLine (T.lines contents))]
 
 writeModule :: FilePath -> ScaffoldModule -> IO (ScaffoldModule, WriteDisposition)
 writeModule out m = do
@@ -479,6 +504,10 @@
     render (BehaviorRefusal errors) =
       ["error: behavior obligations cannot be derived soundly -- refusing to scaffold; nothing was written"]
         <> ["  " <> T.pack (show behaviorError) | behaviorError <- errors]
+    render (FoldSurfaceRefusal surfaceError) =
+      [ "error: aggregate fold identity could not be resolved -- refusing to scaffold; nothing was written",
+        "  " <> renderFoldSurfaceError surfaceError
+      ]
     render (SemanticContractMismatch detail) =
       [ "error: semantic language contract mismatch -- refusing to scaffold; nothing was written",
         "  " <> detail
@@ -610,9 +639,12 @@
         ]
           <> map staleLine stale
           <> ["note: keiro-dsl never deletes files."]
-    staleLine stale = case staleKind stale of
-      Generated -> "  generated " <> T.pack (stalePath stale) <> "  (safe to delete; still on disk)"
-      HoleStub -> "  hole      " <> T.pack (stalePath stale) <> "  (hand-owned — review before deleting)"
+    staleLine stale = case (staleKind stale, staleGeneratedEvidence stale) of
+      (Generated, Just ExactGeneratedBannerPresent) ->
+        "  generated " <> T.pack (stalePath stale) <> "  (exact generated banner present; verify unchanged bytes before deleting)"
+      (Generated, _) ->
+        "  generated " <> T.pack (stalePath stale) <> "  (exact generated banner missing; preserve and review)"
+      (HoleStub, _) -> "  hole      " <> T.pack (stalePath stale) <> "  (hand-owned — preserve and review)"
 
 sourceLanguageLabel :: SourceLanguage -> Text
 sourceLanguageLabel sourceLanguage =
diff --git a/src/Keiro/Dsl/SemanticContract.hs b/src/Keiro/Dsl/SemanticContract.hs
--- a/src/Keiro/Dsl/SemanticContract.hs
+++ b/src/Keiro/Dsl/SemanticContract.hs
@@ -8,10 +8,12 @@
 module Keiro.Dsl.SemanticContract
   ( EffectiveLanguageContract,
     effectiveContractLanguageVersion,
+    effectiveRuntimeProfile,
     effectiveRuntimeSemantics,
+    effectiveLanguageSupport,
     effectiveLanguageContract,
     effectiveLanguageContractForVersion,
-    runtimeSemanticsFingerprintSegment,
+    runtimeSemanticsFingerprintSegments,
     CheckedService (..),
     checkedSource,
     checkedService,
@@ -19,20 +21,25 @@
   )
 where
 
-import Control.Monad (guard)
 import Data.Aeson (FromJSON (..), ToJSON (..), object, withObject, (.:), (.=))
 import Data.Maybe (fromMaybe)
 import Data.Text (Text)
 import Keiro.Dsl.Grammar (Spec)
 import Keiro.Dsl.LanguageVersion
-  ( LanguageVersion,
+  ( LanguageSupport,
+    LanguageVersion,
     ParsedSource (..),
+    RuntimeSemanticsProfile,
     SourceLanguage (..),
-    definitionRuntimeSemantics,
+    definitionRuntimeSemanticsProfile,
     effectiveLanguageVersion,
+    languageSupportForVersion,
+    languageSupportText,
     languageVersion,
     languageVersionNumber,
     lookupLanguageDefinition,
+    runtimeProfileFoldSegments,
+    runtimeProfileIdentifier,
   )
 
 -- | One effective released-language selection plus the runtime-semantics
@@ -43,15 +50,28 @@
 -- than re-deriving policy from source text.
 data EffectiveLanguageContract = EffectiveLanguageContract
   { effectiveContractLanguageVersion :: !LanguageVersion,
-    effectiveRuntimeSemantics :: !Text
+    effectiveRuntimeProfile :: !RuntimeSemanticsProfile
   }
   deriving stock (Eq, Ord, Show)
 
+-- | Stable compatibility projection for records, JSON, and diagnostics.
+-- Runtime behavior queries 'effectiveRuntimeProfile' capabilities instead.
+effectiveRuntimeSemantics :: EffectiveLanguageContract -> Text
+effectiveRuntimeSemantics = runtimeProfileIdentifier . effectiveRuntimeProfile
+
+-- | Lifecycle classification derived from the authoritative language registry.
+effectiveLanguageSupport :: EffectiveLanguageContract -> LanguageSupport
+effectiveLanguageSupport contract =
+  fromMaybe
+    (error "keiro-dsl internal invariant: effective contract selected an unregistered language version")
+    (languageSupportForVersion (effectiveContractLanguageVersion contract))
+
 instance ToJSON EffectiveLanguageContract where
   toJSON contract =
     object
       [ "languageVersion" .= languageVersionNumber (effectiveContractLanguageVersion contract),
-        "runtimeSemantics" .= effectiveRuntimeSemantics contract
+        "runtimeSemantics" .= effectiveRuntimeSemantics contract,
+        "languageSupport" .= languageSupportText (effectiveLanguageSupport contract)
       ]
 
 instance FromJSON EffectiveLanguageContract where
@@ -60,8 +80,17 @@
     runtimeSemantics <- fields .: "runtimeSemantics"
     version <- maybe (fail "semantic contract language version must be positive") pure (languageVersion rawVersion)
     contract <- maybe (fail "semantic contract language version is unsupported") pure (effectiveLanguageContractForVersion version)
-    guard (effectiveRuntimeSemantics contract == runtimeSemantics)
-    pure contract
+    if effectiveRuntimeSemantics contract == runtimeSemantics
+      then pure contract
+      else
+        fail
+          ( "semantic contract runtimeSemantics does not match language version "
+              <> show rawVersion
+              <> ": expected "
+              <> show (effectiveRuntimeSemantics contract)
+              <> ", received "
+              <> show (runtimeSemantics :: Text)
+          )
 
 -- | Resolve source provenance to the semantic contract used by every
 -- downstream planner. This function is total for parsed sources: the parser has
@@ -81,15 +110,14 @@
   pure
     EffectiveLanguageContract
       { effectiveContractLanguageVersion = version,
-        effectiveRuntimeSemantics = definitionRuntimeSemantics definition
+        effectiveRuntimeProfile = definitionRuntimeSemanticsProfile definition
       }
 
--- | Fold/replay discriminator for runtime semantics newer than the historical
--- baseline. Grammar-only language changes deliberately contribute no segment.
-runtimeSemanticsFingerprintSegment :: EffectiveLanguageContract -> Maybe Text
-runtimeSemanticsFingerprintSegment contract
-  | effectiveRuntimeSemantics contract == "keiro-dsl/runtime-semantics/1" = Nothing
-  | otherwise = Just ("semantic-contract:" <> effectiveRuntimeSemantics contract)
+-- | Deduplicated, stable replay-fold segments explicitly declared by the
+-- effective runtime capabilities. Grammar-, validation-, and codec-only
+-- changes deliberately contribute no segment.
+runtimeSemanticsFingerprintSegments :: EffectiveLanguageContract -> [Text]
+runtimeSemanticsFingerprintSegments = runtimeProfileFoldSegments . effectiveRuntimeProfile
 
 -- | A normalized service graph paired with the effective contract under which
 -- it was checked. Member-level declared/legacy provenance intentionally stays
diff --git a/src/Keiro/Dsl/Skeleton.hs b/src/Keiro/Dsl/Skeleton.hs
--- a/src/Keiro/Dsl/Skeleton.hs
+++ b/src/Keiro/Dsl/Skeleton.hs
@@ -15,6 +15,7 @@
 
 import Data.Text (Text)
 import Data.Text qualified as T
+import Keiro.Dsl.LanguageVersion (currentStableLanguageVersion, languageVersionText)
 
 -- | The valid @new \<kind\>@ arguments, in help/listing order.
 skeletonKinds :: [Text]
@@ -51,7 +52,7 @@
     Left $
       "unknown kind '" <> other <> "'. Valid kinds: " <> T.intercalate ", " skeletonKinds
   where
-    versioned source = "language keiro-dsl 1\n" <> source
+    versioned source = "language keiro-dsl " <> languageVersionText currentStableLanguageVersion <> "\n" <> source
 
 aggregateSkeleton :: Text
 aggregateSkeleton =
@@ -63,14 +64,12 @@
       "aggregate Thing",
       "  regs",
       "    thingId ThingId = placeholder",
-      "    state ThingVertex = Pending",
       "  states Pending Done!",
       "",
       "  command DoThing { thingId attempt:Int }",
       "  event ThingCompleted { thingId attempt:Int }",
       "",
       "  Pending -- DoThing -->",
-      "    write state := Done",
       "    emit ThingCompleted",
       "    goto Done",
       "",
diff --git a/src/Keiro/Dsl/Validate.hs b/src/Keiro/Dsl/Validate.hs
--- a/src/Keiro/Dsl/Validate.hs
+++ b/src/Keiro/Dsl/Validate.hs
@@ -36,12 +36,14 @@
 import Keiro.Dsl.EventOutput
 import Keiro.Dsl.Expression
 import Keiro.Dsl.Grammar
-import Keiro.Dsl.IdDomain (idDomainContractFor)
+import Keiro.Dsl.IdDomain (contractIdDomainContractFor, idDomainContractFor)
+import Keiro.Dsl.LanguageVersion (RuntimeCapability (..), runtimeProfileHasCapability)
 import Keiro.Dsl.NominalType qualified as Nominal
 import Keiro.Dsl.ReadModelShape (deriveShapeHash)
-import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, legacyCheckedService)
+import Keiro.Dsl.SemanticContract (CheckedService (..), EffectiveLanguageContract, effectiveRuntimeProfile, legacyCheckedService)
 import Keiro.Dsl.TypeGraph
 import Numeric (showHex)
+import Text.Read (readMaybe)
 
 data Severity = Error | Warning
   deriving stock (Eq, Show)
@@ -331,6 +333,39 @@
   | -- ExecPlan 159 / IR-13: @fields(Command)@ output authority.
     EventOutputCommandMismatch
   | AggregateEventlessStateChange
+  | -- ExecPlan 178: language-4 integration contract TypeID admission.
+    ContractInvalidTypeIdPrefix
+  | ContractTypeIdDomainChanged
+  | -- ExecPlan 180: accepted-but-unenforced spec surfaces.
+    PublisherOrderingUnknown
+  | PublisherBackoffInvalid
+  | IntakeDedupePolicyUnknown
+  | PublisherMaxAttemptsBelowMinimum
+  | ContractSchemaVersionBelowMinimum
+  | ReadModelVersionBelowMinimum
+  | IntakeDecodeSchemaVersionBelowMinimum
+  | AggregateDuplicateFieldName
+  | ContractDuplicateFieldName
+  | ContractFieldShadowsDiscriminator
+  | TransitionDuplicateUnguarded
+  | ContractDuplicateEvent
+  | ContractDuplicateTopicAlias
+  | AggregateDuplicateState
+  | AggregateDuplicateRegister
+  | NominalDuplicateDeclaration
+  | EmitMapDuplicateCase
+  | TransitionUnguardedSibling
+  | RuntimeIdentityInvalid
+  | RuntimeIdentityDuplicate
+  | ContractTopicNameInvalid
+  | ReadModelIdentifierInvalid
+  | ReadModelDuplicateColumn
+  | IntakeBindUnresolved
+  | IntakeDedupeKeyUnresolved
+  | IntakeEnvelopePolicyUnknown
+  | IntakeDecodeSchemaVersionMismatch
+  | ContractTopicAliasUnresolved
+  | WireClauseUnsupported
   deriving stock (Eq, Show)
 
 -- | A line-numbered, structured diagnostic.
@@ -377,8 +412,15 @@
 
 validateCheckedSpec :: EffectiveLanguageContract -> Spec -> [Diagnostic]
 validateCheckedSpec languageContract spec =
-  sortOn line (validateNames spec ++ validateMapped spec ++ validateNominal languageContract spec ++ validateAggregateTypes spec ++ specLevelRules spec ++ concatMap (validateNode spec) (specNodes spec))
+  sortOn line (validateNames spec ++ validateMapped spec ++ validateNominal languageContract spec ++ validateAggregateTypes spec ++ specLevelRules languageContract spec ++ concatMap (validateNode languageContract spec) (specNodes spec))
 
+-- | Rules added before language 4 ships consult the effective semantic
+-- contract, not the numeric source spelling. Versions 1 through 3 retain their
+-- released acceptance; runtime semantics 3 is the unreleased tightening gate.
+enforcesSpecSurfaceClosures :: EffectiveLanguageContract -> Bool
+enforcesSpecSurfaceClosures languageContract =
+  runtimeProfileHasCapability (effectiveRuntimeProfile languageContract) StrictSpecSurfaceValidation
+
 validateNominal :: EffectiveLanguageContract -> Spec -> [Diagnostic]
 validateNominal languageContract spec = domainErrors <> resolutionErrors
   where
@@ -1258,9 +1300,29 @@
 asciiAlphaNumOrUnderscore :: Char -> Bool
 asciiAlphaNumOrUnderscore c = asciiUpper c || asciiLower c || (c >= '0' && c <= '9') || c == '_'
 
+kafkaTopicError :: Text -> Maybe Text
+kafkaTopicError topic
+  | T.null topic = Just "is empty"
+  | T.length topic > 249 = Just "is longer than Kafka's 249-character limit"
+  | topic == "." || topic == ".." = Just "is reserved by Kafka"
+  | Just illegal <- T.find (not . kafkaTopicCharacter) topic =
+      Just ("contains character " <> T.pack (show illegal) <> "; use only ASCII letters, digits, '.', '_', or '-'")
+  | otherwise = Nothing
+  where
+    kafkaTopicCharacter character = asciiAlphaNumOrUnderscore character || character == '.' || character == '-'
+
+validPostgresIdentifier :: Text -> Bool
+validPostgresIdentifier identifier =
+  T.length identifier <= 63
+    && case T.uncons identifier of
+      Nothing -> False
+      Just (firstCharacter, rest) ->
+        (asciiLower firstCharacter || firstCharacter == '_')
+          && T.all (\character -> asciiLower character || (character >= '0' && character <= '9') || character == '_') rest
+
 -- | Rules over namespaces shared by the whole specification.
-specLevelRules :: Spec -> [Diagnostic]
-specLevelRules spec = duplicateNodes ++ duplicateEnumMembers ++ duplicateIdPrefixes ++ ruleDiagnostics
+specLevelRules :: EffectiveLanguageContract -> Spec -> [Diagnostic]
+specLevelRules languageContract spec = duplicateNodes ++ duplicateEnumMembers ++ duplicateIdPrefixes ++ duplicateDeclarations ++ runtimeIdentities ++ duplicateRuntimeIdentities ++ ruleDiagnostics
   where
     duplicateNodes =
       [ mkErr (locLine loc) DuplicateNodeName $
@@ -1284,6 +1346,35 @@
           "id '" <> idName d <> "' reuses prefix '" <> idPrefix d <> "'"
       | d <- duplicatesBy idPrefix (specIds spec)
       ]
+    duplicateDeclarations =
+      [ mkErr (locLine loc) NominalDuplicateDeclaration $
+          "duplicate " <> category <> " declaration '" <> name <> "'; the last declaration would silently replace the earlier one"
+      | enforcesSpecSurfaceClosures languageContract,
+        (category, name, loc) <- duplicatesBy (\(category, name, _) -> (category, name)) declarationOrigins
+      ]
+    declarationOrigins =
+      [("id", idName value, idLoc value) | value <- specIds spec]
+        <> [("enum", enumName value, enumLoc value) | value <- specEnums spec]
+        <> [("nominal scalar", nominalScalarName value, nominalScalarLoc value) | value <- specNominalScalars spec]
+        <> [("mapped", mappedName value, mappedLoc value) | value <- specMapped spec]
+        <> [("rule", ruleName value, ruleLoc value) | value <- specRules spec]
+    runtimeIdentities =
+      [ mkErr (locLine loc) RuntimeIdentityInvalid $
+          kind <> " stable identity " <> T.pack (show identity) <> " " <> reason
+      | enforcesSpecSurfaceClosures languageContract,
+        (kind, identity, loc) <- stableIdentityOrigins,
+        Just reason <- [stableIdentityError identity]
+      ]
+    duplicateRuntimeIdentities =
+      [ mkErr (locLine loc) RuntimeIdentityDuplicate $
+          kind <> " stable identity " <> T.pack (show identity) <> " is already used by another workflow, process, or router"
+      | enforcesSpecSurfaceClosures languageContract,
+        (kind, identity, loc) <- duplicatesBy (\(_, identity, _) -> identity) stableIdentityOrigins
+      ]
+    stableIdentityOrigins =
+      [("workflow", wfStable workflow, workflowNodeLoc workflow) | NWorkflow workflow <- specNodes spec]
+        <> [("process", procName process, procLoc process) | NProcess process <- specNodes spec]
+        <> [("router", rtName router, rtLoc router) | NRouter router <- specNodes spec]
     ruleDiagnostics = concatMap (validateRule spec) (specRules spec)
 
 nodeIdentity :: Node -> (Text, Name, Loc)
@@ -1300,20 +1391,100 @@
 nodeIdentity (NWorkflow w) = ("workflow", wfId w, workflowNodeLoc w)
 nodeIdentity (NOperation o) = ("operation", opName o, opLoc o)
 
-validateNode :: Spec -> Node -> [Diagnostic]
-validateNode spec (NAggregate agg) = validateAggregate spec agg
-validateNode spec (NProcess p) = validateProcess spec p
-validateNode spec (NRouter router) = validateRouter spec router
-validateNode _spec (NContract _) = [] -- a contract is a declaration; coupling is checked at the referrers
-validateNode spec (NIntake i) = validateIntake i ++ intakeCoupling spec i
-validateNode spec (NEmit e) = validateEmit spec e
-validateNode spec (NPublisher p) = validatePublisher spec p
-validateNode _spec (NWorkqueue w) = validateWorkqueue w
-validateNode spec (NPgmqDispatch d) = validatePgmqDispatch spec d
-validateNode spec (NReadModel readModel) = validateReadModel spec readModel
-validateNode _spec (NWorkflow w) = validateWorkflow w
-validateNode spec (NOperation o) = validateOperation spec o
+validateNode :: EffectiveLanguageContract -> Spec -> Node -> [Diagnostic]
+validateNode languageContract spec (NAggregate agg) = validateAggregate languageContract spec agg
+validateNode _languageContract spec (NProcess p) = validateProcess spec p
+validateNode _languageContract spec (NRouter router) = validateRouter spec router
+validateNode languageContract _spec (NContract contract) = validateContract languageContract contract
+validateNode languageContract spec (NIntake i) = validateIntake languageContract i ++ intakeCoupling languageContract spec i
+validateNode languageContract spec (NEmit e) = validateEmit languageContract spec e
+validateNode languageContract spec (NPublisher p) = validatePublisher languageContract spec p
+validateNode _languageContract _spec (NWorkqueue w) = validateWorkqueue w
+validateNode _languageContract spec (NPgmqDispatch d) = validatePgmqDispatch spec d
+validateNode languageContract spec (NReadModel readModel) = validateReadModel languageContract spec readModel
+validateNode _languageContract _spec (NWorkflow w) = validateWorkflow w
+validateNode _languageContract spec (NOperation o) = validateOperation spec o
 
+validateContract :: EffectiveLanguageContract -> ContractNode -> [Diagnostic]
+validateContract languageContract contract =
+  typeIdPrefixErrors
+    <> schemaVersionFloor
+    <> topicNames
+    <> duplicateEvents
+    <> duplicateTopicAliases
+    <> duplicateFields
+    <> discriminatorShadows
+    <> unresolvedTopicAliases
+  where
+    typeIdPrefixErrors =
+      [ mkErr (locLine (ctrLoc contract)) ContractInvalidTypeIdPrefix $
+          "contract '"
+            <> ctrName contract
+            <> "' event '"
+            <> ceName event
+            <> "' field '"
+            <> cfName field
+            <> "' has invalid TypeID prefix '"
+            <> prefix
+            <> "': "
+            <> T.pack (show reason)
+      | event <- ctrEvents contract,
+        field <- ceFields event,
+        CTypeId prefix <- [cfType field],
+        Just _ <- [contractIdDomainContractFor languageContract prefix],
+        Just reason <- [TypeID.checkPrefix prefix]
+      ]
+    schemaVersionFloor =
+      [ mkErr (locLine (ctrLoc contract)) ContractSchemaVersionBelowMinimum $
+          "contract '" <> ctrName contract <> "' schemaVersion must be at least 1"
+      | enforcesSpecSurfaceClosures languageContract,
+        ctrSchemaVersion contract < 1
+      ]
+    topicNames =
+      [ mkErr (locLine (ctrLoc contract)) ContractTopicNameInvalid $
+          "contract '" <> ctrName contract <> "' topic alias '" <> alias <> "' has invalid Kafka topic " <> T.pack (show topic) <> ": " <> reason
+      | (alias, topic) <- ctrTopics contract,
+        T.null topic || enforcesSpecSurfaceClosures languageContract,
+        Just reason <- [kafkaTopicError topic]
+      ]
+    duplicateEvents =
+      [ mkErr (locLine (ctrLoc contract)) ContractDuplicateEvent $
+          "contract '" <> ctrName contract <> "' declares event '" <> ceName event <> "' more than once"
+      | event <- duplicatesBy ceName (ctrEvents contract)
+      ]
+    duplicateTopicAliases =
+      [ mkErr (locLine (ctrLoc contract)) ContractDuplicateTopicAlias $
+          "contract '" <> ctrName contract <> "' declares topic alias '" <> alias <> "' more than once"
+      | (alias, _) <- duplicatesBy fst (ctrTopics contract)
+      ]
+    duplicateFields =
+      [ mkErr (locLine (ctrLoc contract)) ContractDuplicateFieldName $
+          "contract '" <> ctrName contract <> "' event '" <> ceName event <> "' declares field '" <> cfName field <> "' more than once"
+      | event <- ctrEvents contract,
+        field <- duplicatesBy cfName (ceFields event)
+      ]
+    discriminatorShadows =
+      [ mkErr (locLine (ctrLoc contract)) ContractFieldShadowsDiscriminator $
+          "contract '"
+            <> ctrName contract
+            <> "' event '"
+            <> ceName event
+            <> "' field '"
+            <> cfName field
+            <> "' shadows the payload discriminator"
+      | enforcesSpecSurfaceClosures languageContract,
+        event <- ctrEvents contract,
+        field <- ceFields event,
+        cfName field == ctrDiscriminator contract
+      ]
+    unresolvedTopicAliases =
+      [ mkErr (locLine (ctrLoc contract)) ContractTopicAliasUnresolved $
+          "contract '" <> ctrName contract <> "' event '" <> ceName event <> "' names undeclared topic alias '" <> ceTopic event <> "'"
+      | enforcesSpecSurfaceClosures languageContract,
+        event <- ctrEvents contract,
+        ceTopic event `notElem` map fst (ctrTopics contract)
+      ]
+
 -- | Workflow replay keys, patch guards, rotation, and injected inputs must be unambiguous.
 validateWorkflow :: WorkflowNode -> [Diagnostic]
 validateWorkflow w = duplicateLabels ++ sleepFields ++ patchDuplicates ++ patchIds ++ continuePositions ++ idField
@@ -1497,9 +1668,9 @@
   ]
 
 -- | Validate captured identity, feed semantics, and the declared column surface.
-validateReadModel :: Spec -> ReadModelNode -> [Diagnostic]
-validateReadModel spec readModel =
-  shapeFixture ++ columnTypes ++ strongFeed ++ scopeMode ++ inlineReference
+validateReadModel :: EffectiveLanguageContract -> Spec -> ReadModelNode -> [Diagnostic]
+validateReadModel languageContract spec readModel =
+  shapeFixture ++ columnTypes ++ strongFeed ++ scopeMode ++ inlineReference ++ versionFloor ++ identifiers ++ duplicateColumns
   where
     readModelLine = locLine (rmLoc readModel)
     expectedShape = deriveShapeHash readModel
@@ -1541,6 +1712,27 @@
       | rmFeed readModel == RmInline,
         rmName readModel `notElem` [projTable projection | NAggregate aggregate <- specNodes spec, Just projection <- [aggProjection aggregate]]
       ]
+    versionFloor =
+      [ mkErr readModelLine ReadModelVersionBelowMinimum $
+          "readmodel '" <> rmName readModel <> "' version must be at least 1"
+      | enforcesSpecSurfaceClosures languageContract,
+        rmVersion readModel < 1
+      ]
+    identifiers =
+      [ mkErr readModelLine ReadModelIdentifierInvalid $
+          "readmodel '" <> rmName readModel <> "' " <> kind <> " " <> T.pack (show identifier) <> " is not a PostgreSQL unquoted identifier"
+      | enforcesSpecSurfaceClosures languageContract,
+        (kind, identifier) <-
+          [("schema", rmSchema readModel), ("table", rmTable readModel)]
+            <> [("column", rmcName columnDecl) | columnDecl <- rmColumns readModel],
+        not (validPostgresIdentifier identifier)
+      ]
+    duplicateColumns =
+      [ mkErr readModelLine ReadModelDuplicateColumn $
+          "readmodel '" <> rmName readModel <> "' declares column '" <> rmcName columnDecl <> "' more than once"
+      | enforcesSpecSurfaceClosures languageContract,
+        columnDecl <- duplicatesBy rmcName (rmColumns readModel)
+      ]
 
 -- | EP-5 workqueue rules: the captured physical name must match the queueRef
 -- derivation; the disposition inversions (storeFailure transient => must retry;
@@ -1674,8 +1866,8 @@
 specContracts spec = [c | NContract c <- specNodes spec]
 
 -- | EP-4 cross-node coupling: an intake's contract/topic/accepted-events resolve.
-intakeCoupling :: Spec -> IntakeNode -> [Diagnostic]
-intakeCoupling spec i = case lookupContract (inkContract i) of
+intakeCoupling :: EffectiveLanguageContract -> Spec -> IntakeNode -> [Diagnostic]
+intakeCoupling languageContract spec i = case lookupContract (inkContract i) of
   Nothing ->
     [mkErr (locLine (inkLoc i)) IntakeUnresolvedContract ("intake '" <> inkName i <> "' references undeclared contract '" <> inkContract i <> "'")]
   Just c ->
@@ -1692,17 +1884,54 @@
            ceName event `elem` inkAccept i,
            ceTopic event /= inkTopic i
          ]
+      ++ [ mkErr (locLine (inkLoc i)) IntakeBindUnresolved $
+             "intake '" <> inkName i <> "' binds undeclared envelope or accepted-event field '" <> brField binding <> "'"
+         | enforcesSpecSurfaceClosures languageContract,
+           binding <- inkBinds i,
+           brField binding `Set.notMember` resolvableFields c
+         ]
+      ++ [ mkErr (locLine (inkLoc i)) IntakeDedupeKeyUnresolved $
+             "intake '" <> inkName i <> "' dedupe key '" <> inkDedupeKey i <> "' is not an envelope or accepted-event field"
+         | enforcesSpecSurfaceClosures languageContract,
+           inkDedupeKey i `Set.notMember` resolvableFields c
+         ]
+      ++ [ mkErr (locLine (inkLoc i)) IntakeDecodeSchemaVersionMismatch $
+             "intake '"
+               <> inkName i
+               <> "' decode schemaVersion "
+               <> tInt (decBodySchemaVersion (inkDecode i))
+               <> " does not match contract '"
+               <> ctrName c
+               <> "' schemaVersion "
+               <> tInt (ctrSchemaVersion c)
+         | enforcesSpecSurfaceClosures languageContract,
+           decBodySchemaVersion (inkDecode i) /= ctrSchemaVersion c
+         ]
   where
     lookupContract n = case [c | c <- specContracts spec, ctrName c == n] of (c : _) -> Just c; [] -> Nothing
+    resolvableFields contract =
+      canonicalIntakeEnvelopeFields
+        <> Set.fromList
+          [ cfName field
+          | event <- ctrEvents contract,
+            ceName event `elem` inkAccept i,
+            field <- ceFields event
+          ]
 
-validateEmit :: Spec -> EmitNode -> [Diagnostic]
-validateEmit spec e = skipRule ++ coupling
+validateEmit :: EffectiveLanguageContract -> Spec -> EmitNode -> [Diagnostic]
+validateEmit languageContract spec e = skipRule ++ duplicateCases ++ coupling
   where
     el = locLine (emLoc e)
     skipRule =
       [ mkErr el EmitSkipMissing ("emit '" <> emName e <> "' map must end with an explicit '_ => skip' catch-all (hole-kind 7 optionality)")
       | not (emSkip e)
       ]
+    duplicateCases =
+      [ mkErr (locLine (emrLoc row)) EmitMapDuplicateCase $
+          "emit '" <> emName e <> "' repeats map discriminant '" <> emrValue row <> "'; the first row would shadow this row"
+      | enforcesSpecSurfaceClosures languageContract,
+        row <- duplicatesBy emrValue (emMap e)
+      ]
     coupling = case [c | c <- specContracts spec, ctrName c == emContract e] of
       [] -> [mkErr el EmitUnresolvedContract ("emit '" <> emName e <> "' references undeclared contract '" <> emContract e <> "'")]
       (c : _) ->
@@ -1721,16 +1950,66 @@
                ceTopic event /= emTopic e
              ]
 
-validatePublisher :: Spec -> PublisherNode -> [Diagnostic]
-validatePublisher spec p =
-  [ mkErr (locLine (pubLoc p)) PublisherUnresolvedEmit ("publisher '" <> pubName p <> "' references undeclared emit '" <> pubEmit p <> "'")
-  | pubEmit p `notElem` [emName e | NEmit e <- specNodes spec]
-  ]
+validatePublisher :: EffectiveLanguageContract -> Spec -> PublisherNode -> [Diagnostic]
+validatePublisher languageContract spec p =
+  unresolvedEmit ++ orderingVocabulary ++ backoffPolicy ++ attemptsFloor
+  where
+    publisherLine = locLine (pubLoc p)
+    unresolvedEmit =
+      [ mkErr publisherLine PublisherUnresolvedEmit ("publisher '" <> pubName p <> "' references undeclared emit '" <> pubEmit p <> "'")
+      | pubEmit p `notElem` [emName e | NEmit e <- specNodes spec]
+      ]
+    orderingVocabulary =
+      [ mkErr publisherLine PublisherOrderingUnknown $
+          "publisher '"
+            <> pubName p
+            <> "' has unknown ordering '"
+            <> pubOrdering p
+            <> "'; expected PerKeyHeadOfLine, PerSourceStream, StopTheLine, or BestEffort"
+      | pubOrdering p `Set.notMember` publisherOrderings
+      ]
+    backoffPolicy =
+      [ mkErr publisherLine PublisherBackoffInvalid $
+          "publisher '" <> pubName p <> "' has an invalid " <> problem
+      | Just problem <- [backoffProblemMaybe (pubBackoff p)]
+      ]
+    attemptsFloor =
+      [ mkErr publisherLine PublisherMaxAttemptsBelowMinimum $
+          "publisher '" <> pubName p <> "' maxAttempts must be at least 1"
+      | enforcesSpecSurfaceClosures languageContract,
+        pubMaxAttempts p < 1
+      ]
 
+publisherOrderings :: Set Name
+publisherOrderings = Set.fromList ["PerKeyHeadOfLine", "PerSourceStream", "StopTheLine", "BestEffort"]
+
+backoffProblemMaybe :: BackoffSpec -> Maybe Text
+backoffProblemMaybe backoff = case boKind backoff of
+  "constant" -> Nothing
+  "exponential" -> case (boMax backoff, boMultiplier backoff) of
+    (Just maximumWindow, Just multiplierText) ->
+      case (validationWindowSeconds (boWindow backoff), validationWindowSeconds maximumWindow, readMaybe (T.unpack multiplierText) :: Maybe Double) of
+        (Just initialSeconds, Just maximumSeconds, Just multiplier)
+          | initialSeconds > 0 && maximumSeconds >= initialSeconds && multiplier >= 1 -> Nothing
+        _ -> Just "exponential backoff; initial must be positive, max must be at least initial, and multiplier must be at least 1"
+    _ -> Just "exponential backoff; both max and multiplier are required"
+  other -> Just ("backoff kind '" <> other <> "'; expected constant or exponential")
+
+validationWindowSeconds :: Text -> Maybe Int
+validationWindowSeconds window = case T.unsnoc window of
+  Just (digits, unit) -> do
+    amount <- readMaybe (T.unpack digits)
+    case unit of
+      's' -> Just amount
+      'm' -> Just (amount * 60)
+      'h' -> Just (amount * 3600)
+      _ -> Nothing
+  Nothing -> Nothing
+
 -- | EP-4 inbox disposition rules: the table must be complete over the seven
 -- outcomes, and the three dangerous inversions must be stated the safe way.
-validateIntake :: IntakeNode -> [Diagnostic]
-validateIntake i = concat [completeness, duplicateRows, inversions]
+validateIntake :: EffectiveLanguageContract -> IntakeNode -> [Diagnostic]
+validateIntake languageContract i = concat [completeness, duplicateRows, inversions, dedupeVocabulary, decodeVersionFloor, envelopeVocabulary]
   where
     il = locLine (inkLoc i)
     rows = inkDisposition i
@@ -1747,6 +2026,27 @@
           "intake '" <> inkName i <> "' repeats disposition outcome '" <> drOutcome row <> "'; the first row would shadow this row"
       | row <- duplicatesBy drOutcome rows
       ]
+    dedupeVocabulary =
+      [ mkErr il IntakeDedupePolicyUnknown $
+          "intake '"
+            <> inkName i
+            <> "' has unknown dedupe policy '"
+            <> inkDedupePolicy i
+            <> "'; expected PreferIntegrationMessageId, PreferSourceEventIdentity, or KafkaDeliveryIdentity"
+      | inkDedupePolicy i `Set.notMember` intakeDedupePolicies
+      ]
+    decodeVersionFloor =
+      [ mkErr il IntakeDecodeSchemaVersionBelowMinimum $
+          "intake '" <> inkName i <> "' decode schemaVersion must be at least 1"
+      | enforcesSpecSurfaceClosures languageContract,
+        decBodySchemaVersion (inkDecode i) < 1
+      ]
+    envelopeVocabulary =
+      [ mkErr il IntakeEnvelopePolicyUnknown $
+          "intake '" <> inkName i <> "' has unsupported envelope policy " <> T.pack (show (decEnvelope (inkDecode i))) <> "; expected \"strict-required lenient-optional\""
+      | enforcesSpecSurfaceClosures languageContract,
+        decEnvelope (inkDecode i) /= "strict-required lenient-optional"
+      ]
     firstRow outcome = case [row | row <- rows, drOutcome row == outcome] of
       (row : _) -> Just row
       [] -> Nothing
@@ -1768,6 +2068,31 @@
              isRetry row
            ]
 
+intakeDedupePolicies :: Set Name
+intakeDedupePolicies = Set.fromList ["PreferIntegrationMessageId", "PreferSourceEventIdentity", "KafkaDeliveryIdentity"]
+
+canonicalIntakeEnvelopeFields :: Set Name
+canonicalIntakeEnvelopeFields =
+  Set.fromList
+    [ "messageId",
+      "source",
+      "destination",
+      "key",
+      "eventType",
+      "schemaVersion",
+      "contentType",
+      "schemaReference",
+      "sourceEventId",
+      "sourceGlobalPosition",
+      "payloadBytes",
+      "occurredAt",
+      "causationId",
+      "correlationId",
+      "traceContext",
+      "attributes",
+      "idempotencyKey"
+    ]
+
 -- | EP-3 rules for a process manager + its nested timer.
 validateProcess :: Spec -> ProcessNode -> [Diagnostic]
 validateProcess spec p =
@@ -1912,13 +2237,19 @@
 -- the toolchain library.  The final @:@ case is deliberately stricter because
 -- that prefix is reserved for the @wf:<name>@ workflow stream family.
 sagaCategoryError :: Text -> Maybe Text
-sagaCategoryError categoryName
-  | T.null categoryName = Just "is empty; use a non-empty camelCase category"
-  | categoryName == "$all" = Just "is reserved by the event store; choose a service-owned camelCase category"
-  | T.isInfixOf "-" categoryName = Just "contains '-' (kiroku's category/id boundary); write compound categories in camelCase, for example \"hospitalSurge\""
-  | Just illegal <- T.find (\character -> isSpace character || isControl character) categoryName =
+sagaCategoryError = runtimeIdentityError False
+
+stableIdentityError :: Text -> Maybe Text
+stableIdentityError = runtimeIdentityError True
+
+runtimeIdentityError :: Bool -> Text -> Maybe Text
+runtimeIdentityError allowsHyphen identity
+  | T.null identity = Just "is empty; use a non-empty stable name"
+  | identity == "$all" = Just "is reserved by the event store; choose a service-owned stable name"
+  | not allowsHyphen && T.isInfixOf "-" identity = Just "contains '-' (kiroku's category/id boundary); write compound categories in camelCase, for example \"hospitalSurge\""
+  | Just illegal <- T.find (\character -> isSpace character || isControl character) identity =
       Just ("contains whitespace or control character " <> T.pack (show illegal) <> "; remove it and use camelCase")
-  | T.isInfixOf ":" categoryName = Just "contains ':' which is reserved for the wf:<name> workflow stream family; choose a camelCase category without ':'"
+  | T.isInfixOf ":" identity = Just "contains ':' which is reserved for runtime stream-family prefixes; choose a stable name without ':'"
   | otherwise = Nothing
 
 -- | EP-108 rules for a stateless content-based router.
@@ -2057,8 +2388,8 @@
     isDeadLetter DDeadLetter {} = True
     isDeadLetter _ = False
 
-validateAggregate :: Spec -> Aggregate -> [Diagnostic]
-validateAggregate spec agg =
+validateAggregate :: EffectiveLanguageContract -> Spec -> Aggregate -> [Diagnostic]
+validateAggregate languageContract spec agg =
   concat
     [ duplicateMembers,
       declaredRefs,
@@ -2074,7 +2405,8 @@
       evolutionRules,
       snapshotRules,
       replayOnlyRules,
-      eventlessStateChangeRules
+      eventlessStateChangeRules,
+      wirePolicyRules
     ]
   where
     states = Set.fromList (map stName (aggStates agg))
@@ -2100,6 +2432,21 @@
              | snapCodecVersion snapshot < 1 || T.null (snapShapeHash snapshot)
              ]
 
+    wirePolicyRules = case aggWire agg of
+      Just wire
+        | enforcesSpecSurfaceClosures languageContract,
+          wireKind wire /= "ctorName" || wireFields wire /= "camelCase" ->
+            [ mkErr (locLine (aggLoc agg)) WireClauseUnsupported $
+                "aggregate '"
+                  <> aggName agg
+                  <> "' wire clause describes kind="
+                  <> wireKind wire
+                  <> " fields="
+                  <> wireFields wire
+                  <> "; generated bytes currently support only kind=ctorName fields=camelCase"
+            ]
+      _ -> []
+
     duplicateMembers =
       [ mkErr (locLine (cmdLoc c)) DuplicateCommandName $
           "aggregate '" <> aggName agg <> "' declares command '" <> cmdName c <> "' more than once"
@@ -2109,6 +2456,54 @@
                "aggregate '" <> aggName agg <> "' declares event '" <> evName e <> "' more than once"
            | e <- duplicatesBy evName (aggEvents agg)
            ]
+        ++ [ mkErr (locLine (aggregateFieldLoc field)) AggregateDuplicateFieldName $
+               "aggregate '" <> aggName agg <> "' command '" <> cmdName command <> "' declares field '" <> aggregateFieldName field <> "' more than once"
+           | command <- aggCommands agg,
+             field <- duplicatesBy aggregateFieldName (cmdFields command)
+           ]
+        ++ [ mkErr (locLine (aggregateFieldLoc field)) AggregateDuplicateFieldName $
+               "aggregate '" <> aggName agg <> "' event '" <> evName event <> "' declares field '" <> aggregateFieldName field <> "' more than once"
+           | event <- aggEvents agg,
+             EventFields fields <- [evBody event],
+             field <- duplicatesBy aggregateFieldName fields
+           ]
+        ++ [ mkErr (locLine (stLoc state)) AggregateDuplicateState $
+               "aggregate '" <> aggName agg <> "' declares state '" <> stName state <> "' more than once"
+           | state <- duplicatesBy stName (aggStates agg)
+           ]
+        ++ [ mkErr (locLine (regLoc register)) AggregateDuplicateRegister $
+               "aggregate '" <> aggName agg <> "' declares register '" <> regName register <> "' more than once"
+           | enforcesSpecSurfaceClosures languageContract,
+             register <- duplicatesBy regName (aggRegs agg)
+           ]
+        ++ [ mkErr (locLine (tLoc transition)) TransitionDuplicateUnguarded $
+               "aggregate '"
+                 <> aggName agg
+                 <> "' has more than one live unguarded transition for '"
+                 <> tSource transition
+                 <> " -- "
+                 <> tCommand transition
+                 <> "'; every matching command would be ambiguous"
+           | group <- duplicateGroupsBy transitionKey unguardedTransitions,
+             transition <- group
+           ]
+        ++ [ mkErr (locLine (tLoc guarded)) TransitionUnguardedSibling $
+               "aggregate '"
+                 <> aggName agg
+                 <> "' guarded transition '"
+                 <> tSource guarded
+                 <> " -- "
+                 <> tCommand guarded
+                 <> "' overlaps an unguarded sibling at line "
+                 <> tInt (locLine (tLoc unguarded))
+           | enforcesSpecSurfaceClosures languageContract,
+             guarded <- liveTransitions,
+             tGuard guarded /= Nothing,
+             unguarded : _ <- [[candidate | candidate <- unguardedTransitions, transitionKey candidate == transitionKey guarded]]
+           ]
+    liveTransitions = [transition | transition <- aggTransitions agg, tMode transition == TmLive]
+    unguardedTransitions = [transition | transition <- liveTransitions, tGuard transition == Nothing]
+    transitionKey transition = (tSource transition, tCommand transition)
 
     eventBodyRefs =
       [ mkErr (locLine (evLoc e)) UndeclaredCommand $
@@ -2556,3 +2951,11 @@
   | (index, x) <- zip [0 :: Int ..] xs,
     key x `elem` map key (take index xs)
   ]
+
+-- | Return every source-ordered group whose selected key occurs at least
+-- twice. New diagnostics that describe a relationship use this helper; the
+-- older 'duplicatesBy' contract remains first-shadow only.
+duplicateGroupsBy :: (Ord key) => (a -> key) -> [a] -> [[a]]
+duplicateGroupsBy key values =
+  filter ((> 1) . length) . Map.elems $
+    Map.fromListWith (flip (<>)) [(key value, [value]) | value <- values]
diff --git a/src/Keiro/Dsl/Workspace.hs b/src/Keiro/Dsl/Workspace.hs
--- a/src/Keiro/Dsl/Workspace.hs
+++ b/src/Keiro/Dsl/Workspace.hs
@@ -1277,22 +1277,14 @@
 
 instance HasLocs DecodeSpec
 
-instance HasLocs DerivStrategy
-
-instance HasLocs Derivation
-
 instance HasLocs DeriveSpec
 
 instance HasLocs Disp
 
-instance HasLocs DispAction
-
 instance HasLocs DispatchDisposition
 
 instance HasLocs DispatchNode
 
-instance HasLocs Disposition
-
 instance HasLocs DispositionRow
 
 instance HasLocs EmitMapRow
@@ -1300,10 +1292,6 @@
 instance HasLocs EmitNode
 
 instance HasLocs EnumDecl
-
-instance HasLocs EnvelopeBinding
-
-instance HasLocs EnvelopeLayer
 
 instance HasLocs Event
 
diff --git a/src/Keiro/Dsl/WorkspaceAdoption.hs b/src/Keiro/Dsl/WorkspaceAdoption.hs
--- a/src/Keiro/Dsl/WorkspaceAdoption.hs
+++ b/src/Keiro/Dsl/WorkspaceAdoption.hs
@@ -47,9 +47,9 @@
 import Data.Text (Text)
 import Data.Text qualified as T
 import Data.Text.IO qualified as TIO
-import Keiro.Dsl.Scaffold (ModuleKind (..), ScaffoldModule (..))
+import Keiro.Dsl.Scaffold (ModuleKind (..), ScaffoldModule (..), isGeneratedBannerLine)
 import Keiro.Dsl.ScaffoldRecord (ScaffoldRecord (..), parseRecord, recordFileName)
-import Keiro.Dsl.ScaffoldRun (StaleModule (..))
+import Keiro.Dsl.ScaffoldRun (StaleGeneratedEvidence (..), StaleModule (..), staleAgainst)
 import Keiro.Dsl.WorkspaceRecord (AdoptedRow (..), supersededByLine, workspaceMigrationReportFileName)
 import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)
 import System.FilePath ((</>))
@@ -120,18 +120,13 @@
         onDisk path,
         path `Set.notMember` recordClaimedPaths
       ]
+  likelyStale <- staleAgainst out (map modulePath modules) recordedFiles
   let claimedFromBanner =
         [ ClaimedFile {cfPath = path, cfEvidence = ClaimedFromBanner, cfSource = Nothing, cfSpec = Nothing}
         | (path, True) <- bannerCandidates
         ]
       claimed = claimedFromRecord <> claimedFromBanner
 
-      likelyStale =
-        [ StaleModule fileKind path
-        | (fileKind, path) <- recordedFiles,
-          path `Set.notMember` plannedAll,
-          onDisk path
-        ]
       staleOrGenerated =
         Set.fromList (map stalePath likelyStale) <> Set.fromList plannedGenerated
 
@@ -213,7 +208,7 @@
 hasGeneratedBanner :: FilePath -> IO Bool
 hasGeneratedBanner path = do
   contents <- TIO.readFile path
-  pure (any (T.isPrefixOf "-- @generated") (T.lines contents))
+  pure (any isGeneratedBannerLine (T.lines contents))
 
 -- | Render the report a human reviews. It is printed in the scaffold output and
 -- written to @keiro-dsl-migration-report.workspace.\<service\>.txt@.
@@ -249,9 +244,12 @@
             <> " file(s) the legacy scaffold recorded that this workspace does not produce:"
         ]
           <> map staleLine stale
-    staleLine stale = case staleKind stale of
-      Generated -> "    generated " <> T.pack (stalePath stale) <> "  (safe to delete; still on disk)"
-      HoleStub -> "    hole      " <> T.pack (stalePath stale) <> "  (hand-owned — review before deleting)"
+    staleLine stale = case (staleKind stale, staleGeneratedEvidence stale) of
+      (Generated, Just ExactGeneratedBannerPresent) ->
+        "    generated " <> T.pack (stalePath stale) <> "  (exact generated banner present; verify unchanged bytes before deleting)"
+      (Generated, _) ->
+        "    generated " <> T.pack (stalePath stale) <> "  (exact generated banner missing; preserve and review)"
+      (HoleStub, _) -> "    hole      " <> T.pack (stalePath stale) <> "  (hand-owned — preserve and review)"
     unclaimedSection = case mrUnclaimed report of
       [] -> []
       unclaimed ->
diff --git a/src/Keiro/Dsl/WorkspaceDiff.hs b/src/Keiro/Dsl/WorkspaceDiff.hs
--- a/src/Keiro/Dsl/WorkspaceDiff.hs
+++ b/src/Keiro/Dsl/WorkspaceDiff.hs
@@ -23,18 +23,22 @@
 import Data.Text qualified as T
 import Keiro.Dsl.Diff (Change (..), ChangeKind (..), advisoryAt, consumerBuildContext, diffServices, sourceLanguageChange)
 import Keiro.Dsl.DiffReport (OwnedSite (..), WorkspaceChange (..), WorkspaceDiffReport, WorkspaceMeta (..), renderFinding, workspaceDiffReport)
+import Keiro.Dsl.FoldFingerprint (FoldSurfaceError)
 import Keiro.Dsl.Grammar (Loc (..), Name, Placement (..))
 import Keiro.Dsl.LanguageVersion (SourceLanguage (..))
 import Keiro.Dsl.Validate (DiagnosticCode (..))
 import Keiro.Dsl.Workspace (OwnershipIndex (..), WorkspaceMember (..), WorkspaceSpec (..), checkedWorkspace)
 
 -- | Diff two composed service graphs and cite every participant we can resolve.
-diffWorkspaces :: WorkspaceSpec -> WorkspaceSpec -> [WorkspaceChange]
-diffWorkspaces old new =
-  memberLanguageChanges old new
-    <> map annotate (diffServices (checkedWorkspace old) (checkedWorkspace new))
-    <> ownershipMoveChanges old new
-    <> authorityChanges old new
+diffWorkspaces :: WorkspaceSpec -> WorkspaceSpec -> Either FoldSurfaceError [WorkspaceChange]
+diffWorkspaces old new = do
+  semanticChanges <- diffServices (checkedWorkspace old) (checkedWorkspace new)
+  pure
+    ( memberLanguageChanges old new
+        <> map annotate semanticChanges
+        <> ownershipMoveChanges old new
+        <> authorityChanges old new
+    )
   where
     annotate change =
       WorkspaceChange
diff --git a/src/Keiro/Dsl/WorkspaceScaffold.hs b/src/Keiro/Dsl/WorkspaceScaffold.hs
--- a/src/Keiro/Dsl/WorkspaceScaffold.hs
+++ b/src/Keiro/Dsl/WorkspaceScaffold.hs
@@ -55,18 +55,20 @@
 import Data.Text.IO qualified as TIO
 import Keiro.Dsl.BehaviorCoverage (BehaviorKey (..), BehaviorRecordRow (..), attributeBehaviorOwner, behaviorRecordRows, deriveBehaviorRequirements)
 import Keiro.Dsl.ExplainBindings (BindingHole (..), bindingHolesForService)
+import Keiro.Dsl.FoldFingerprint (aggregateFoldSurfaceForService)
 import Keiro.Dsl.Goldens (GoldenPayload)
 import Keiro.Dsl.Grammar
 import Keiro.Dsl.Harness (harnessForServiceWithGoldens, harnessProcess, harnessReadModel, harnessRouter, harnessWorkflow)
 import Keiro.Dsl.IdDomain (idDomainIdentitiesForService)
 import Keiro.Dsl.LanguageVersion (SourceLanguage, effectiveLanguageVersion, languageVersionText, sourceFormText)
-import Keiro.Dsl.Manifest (moduleNameOf, renderManifest)
+import Keiro.Dsl.Manifest (moduleNameOf, renderManifestForService)
 import Keiro.Dsl.MappedConsumer (ConsumerPlan (..), consumerPlan)
 import Keiro.Dsl.NominalType (nominalEqualityIdentitiesForService)
 import Keiro.Dsl.Scaffold
 import Keiro.Dsl.ScaffoldRun
   ( MappingDrift (..),
     Refusal (..),
+    StaleGeneratedEvidence (..),
     StaleModule (..),
     WriteDisposition (..),
     behaviorDrift,
@@ -137,17 +139,19 @@
   WorkspaceSpec ->
   Either [Refusal] WorkspacePlan
 planWorkspaceScaffoldWithGoldens goldens goldenRoot ctx workspace =
-  case pureRefusals ctx merged (map fst tagged) of
-    [] ->
-      Right
-        WorkspacePlan
-          { wpWorkspace = workspace,
-            wpCheckedService = service,
-            wpContext = ctx,
-            wpGoldenRoot = goldenRoot,
-            wpModules = tagged
-          }
-    refusals -> Left refusals
+  case traverse (aggregateFoldSurfaceForService service) [aggregate | NAggregate aggregate <- specNodes merged] of
+    Left surfaceError -> Left [FoldSurfaceRefusal surfaceError]
+    Right _ -> case pureRefusals ctx merged (map fst tagged) of
+      [] ->
+        Right
+          WorkspacePlan
+            { wpWorkspace = workspace,
+              wpCheckedService = service,
+              wpContext = ctx,
+              wpGoldenRoot = goldenRoot,
+              wpModules = tagged
+            }
+      refusals -> Left refusals
   where
     service = checkedWorkspace workspace
     merged = checkedSpec service
@@ -163,22 +167,26 @@
 -- member file.
 workspaceModules :: [GoldenPayload] -> Context -> WorkspaceSpec -> [(ScaffoldModule, ModuleProvenance)]
 workspaceModules goldens ctx workspace =
-  [attributed (declarationProvenance names) m | (m, names) <- scaffoldStructuralOwnersForService ctx service]
-    <> [attributed ContextLevel m | m <- scaffoldReplayAudit ctx merged]
+  [attributedStamped (declarationProvenance names) m | (m, names) <- scaffoldStructuralOwnersForService ctx service]
+    <> [attributedStamped ContextLevel m | m <- scaffoldReplayAudit ctx merged]
     <> concat
-      [ map (attributed (nodeProvenance node)) (emittersFor node)
+      [ map (attributedStamped (nodeProvenance node)) (emittersFor node)
       | node <- specNodes merged
       ]
   where
     service = checkedWorkspace workspace
     merged = checkedSpec service
     ownership = wsOwnership workspace
+    stamp = stampGeneratedModule (checkedLanguageContract service)
+    attributedStamped provenance moduleValue =
+      let (annotated, attribution) = attributed provenance moduleValue
+       in (stamp annotated, attribution)
 
     emittersFor node = case node of
       NAggregate aggregate -> scaffoldAggregateForService ctx service aggregate <> harnessForServiceWithGoldens goldens ctx service aggregate
       NProcess process -> scaffoldProcess ctx process <> harnessProcess ctx process
       NRouter router -> scaffoldRouter ctx router <> harnessRouter ctx router
-      NContract contract -> scaffoldContract ctx contract
+      NContract contract -> scaffoldContractForService ctx service contract
       NIntake intake -> scaffoldIntake ctx intake
       NPublisher publisher -> scaffoldPublisher ctx publisher
       NWorkqueue workqueue -> scaffoldWorkqueue ctx workqueue
@@ -364,7 +372,7 @@
           (addedBehavior, removedBehavior) = maybe (currentBehavior, []) (behaviorDrift currentBehavior . wrBehaviorRequirements) previous
       createDirectoryIfMissing True out
       dispositions <- traverse (writeWorkspaceModule out) (wpModules plan)
-      TIO.writeFile buildManifestPath (renderManifest (T.pack manifestName) modules merged)
+      TIO.writeFile buildManifestPath (renderManifestForService (T.pack manifestName) modules (wpCheckedService plan))
       -- Adoption provenance is durable history, not a one-run note: a
       -- later run that adopts nothing carries the previous rows forward,
       -- or the record would silently forget where its files came from.
@@ -684,9 +692,12 @@
         ]
           <> map staleLine stale
           <> ["note: keiro-dsl never deletes files."]
-    staleLine stale = case staleKind stale of
-      Generated -> "  generated " <> T.pack (stalePath stale) <> "  (safe to delete; still on disk)"
-      HoleStub -> "  hole      " <> T.pack (stalePath stale) <> "  (hand-owned — review before deleting)"
+    staleLine stale = case (staleKind stale, staleGeneratedEvidence stale) of
+      (Generated, Just ExactGeneratedBannerPresent) ->
+        "  generated " <> T.pack (stalePath stale) <> "  (exact generated banner present; verify unchanged bytes before deleting)"
+      (Generated, _) ->
+        "  generated " <> T.pack (stalePath stale) <> "  (exact generated banner missing; preserve and review)"
+      (HoleStub, _) -> "  hole      " <> T.pack (stalePath stale) <> "  (hand-owned — preserve and review)"
 
 workspaceSourceLanguageLabel :: SourceLanguage -> Text
 workspaceSourceLanguageLabel sourceLanguage =
diff --git a/test/Keiro/Dsl/ConformanceBaseline.hs b/test/Keiro/Dsl/ConformanceBaseline.hs
new file mode 100644
--- /dev/null
+++ b/test/Keiro/Dsl/ConformanceBaseline.hs
@@ -0,0 +1,293 @@
+module Keiro.Dsl.ConformanceBaseline (conformanceBaselineSpec) where
+
+import Control.Monad (filterM, forM, forM_, unless)
+import Data.Aeson (FromJSON (..), withObject, (.:), (.:?))
+import Data.Aeson qualified as Aeson
+import Data.List (nub, sort, (\\))
+import Data.Maybe (listToMaybe, mapMaybe)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Text.IO qualified as TIO
+import Keiro.Dsl.FrontendCompatibility (SourceExpectation (..), observeSource, readRepoText)
+import Keiro.Dsl.Grammar (Spec (..))
+import Keiro.Dsl.LanguageVersion (currentStableLanguageVersion, languageVersionNumber)
+import Keiro.Dsl.Parser (parseSource)
+import Keiro.Dsl.Scaffold (Context (..), ModuleKind (..), Placement (..), ScaffoldModule (..), defaultContext)
+import Keiro.Dsl.ScaffoldRun (scaffoldServiceModules)
+import Keiro.Dsl.SemanticContract (CheckedService (..), checkedSource)
+import Keiro.Dsl.Skeleton (skeletonFor)
+import Keiro.Dsl.Workspace (WorkspaceSpec (..), fileContentSource, loadWorkspace)
+import Keiro.Dsl.WorkspaceScaffold (WorkspacePlan (..), planWorkspaceScaffold)
+import Numeric.Natural (Natural)
+import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)
+import System.Environment (lookupEnv)
+import System.FilePath (takeDirectory, takeExtension, (</>))
+import Test.Hspec
+
+data FixtureException = FixtureException
+  { exceptionPath :: !FilePath,
+    exceptionSourceForm :: !Text,
+    exceptionEffectiveVersion :: !(Maybe Natural),
+    exceptionRole :: !Text,
+    exceptionReason :: !Text
+  }
+  deriving stock (Eq, Show)
+
+instance FromJSON FixtureException where
+  parseJSON = withObject "FixtureException" $ \fields ->
+    FixtureException
+      <$> fields .: "path"
+      <*> fields .: "sourceForm"
+      <*> fields .:? "effectiveVersion"
+      <*> fields .: "role"
+      <*> fields .: "reason"
+
+data CompiledSuite = CompiledSuite
+  { suiteComponent :: !Text,
+    suiteDirectory :: !FilePath,
+    suiteSource :: !(Maybe FilePath),
+    suiteGeneration :: !Text,
+    suiteRole :: !Text,
+    suiteReason :: !Text
+  }
+  deriving stock (Eq, Show)
+
+instance FromJSON CompiledSuite where
+  parseJSON = withObject "CompiledSuite" $ \fields ->
+    CompiledSuite
+      <$> fields .: "component"
+      <*> fields .: "directory"
+      <*> fields .:? "source"
+      <*> fields .: "generation"
+      <*> fields .: "role"
+      <*> fields .: "reason"
+
+data ConformanceBaseline = ConformanceBaseline
+  { baselineSchema :: !Text,
+    baselineStableLanguageVersion :: !Natural,
+    baselineFixtureExceptions :: ![FixtureException],
+    baselineCompiledSuites :: ![CompiledSuite]
+  }
+  deriving stock (Eq, Show)
+
+instance FromJSON ConformanceBaseline where
+  parseJSON = withObject "ConformanceBaseline" $ \fields ->
+    ConformanceBaseline
+      <$> fields .: "schema"
+      <*> fields .: "stableLanguageVersion"
+      <*> fields .: "fixtureExceptions"
+      <*> fields .: "compiledSuites"
+
+conformanceBaselineSpec :: SpecWith ()
+conformanceBaselineSpec = describe "conformance baseline" $ do
+  it "uses the registered stable language and explicit non-stable fixture rows" $ do
+    baseline <- readBaseline
+    baselineSchema baseline `shouldBe` "keiro-dsl/conformance-baseline/1"
+    baselineStableLanguageVersion baseline
+      `shouldBe` languageVersionNumber currentStableLanguageVersion
+    paths <- fixturePaths
+    observations <- forM paths $ \path -> (path,) <$> observeSource path
+    let stableVersion = languageVersionNumber currentStableLanguageVersion
+        nonStablePaths =
+          sort
+            [ path
+            | (path, observation) <- observations,
+              sourceForm observation /= "declared"
+                || sourceEffectiveVersion observation /= Just stableVersion
+            ]
+        exceptionPaths = sort (map exceptionPath (baselineFixtureExceptions baseline))
+    (nonStablePaths \\ exceptionPaths)
+      `shouldBe` ([] :: [FilePath])
+    (exceptionPaths \\ nonStablePaths)
+      `shouldBe` ([] :: [FilePath])
+    forM_ (baselineFixtureExceptions baseline) $ \exception -> do
+      observation <- observeSource (exceptionPath exception)
+      sourceForm observation `shouldBe` exceptionSourceForm exception
+      sourceEffectiveVersion observation `shouldBe` exceptionEffectiveVersion exception
+      exceptionRole exception
+        `shouldBe` "compatibility-proof"
+      exceptionReason exception `shouldSatisfy` (not . T.null . T.strip)
+
+  it "accounts for every compiled conformance component and stable generated banner" $ do
+    baseline <- readBaseline
+    cabal <- readRepoText "keiro-dsl/keiro-dsl.cabal"
+    let cabalComponents = conformanceComponents cabal
+        manifestComponents = sort (map suiteComponent (baselineCompiledSuites baseline))
+    (cabalComponents \\ manifestComponents)
+      `shouldBe` ([] :: [Text])
+    (manifestComponents \\ cabalComponents)
+      `shouldBe` ([] :: [Text])
+    forM_ (baselineCompiledSuites baseline) $ \suite -> do
+      suiteRole suite
+        `shouldSatisfy` (`elem` ["stable-primary", "compatibility-proof", "version-independent"])
+      suiteReason suite `shouldSatisfy` (not . T.null . T.strip)
+      directory <- resolveRepoDirectory ("keiro-dsl" </> suiteDirectory suite)
+      doesDirectoryExist directory `shouldReturn` True
+      case suiteRole suite of
+        "stable-primary" -> do
+          unless (suiteGeneration suite `elem` ["workspace", "skeletons"]) $ do
+            source <- requiredSuiteSource suite
+            observation <- observeSource source
+            sourceForm observation `shouldBe` "declared"
+            sourceResult observation `shouldBe` "accept"
+            sourceEffectiveVersion observation
+              `shouldBe` Just (languageVersionNumber currentStableLanguageVersion)
+          banners <- generatedBannerLines directory
+          unless (not (null banners)) $
+            expectationFailure (T.unpack (suiteComponent suite <> " has no generated banners"))
+          let expected =
+                "language keiro-dsl "
+                  <> T.pack (show (languageVersionNumber currentStableLanguageVersion))
+              stableBanners = [(path, banner) | (path, banner) <- banners, expected `T.isInfixOf` banner]
+              isVersionIndependentAuxiliary banner = "@generated by keiro-dsl codec comparison" `T.isInfixOf` banner
+          unless (not (null stableBanners)) $
+            expectationFailure (T.unpack (suiteComponent suite <> " has no stable generated banners"))
+          forM_ banners $ \(path, banner) ->
+            unless (expected `T.isInfixOf` banner || isVersionIndependentAuxiliary banner) $
+              expectationFailure (T.unpack (decorate path banner <> " (expected " <> expected <> ")"))
+          expectedPaths <- expectedStableGeneratedPaths suite
+          let actualPaths = sort (nub (map fst stableBanners))
+          unless (actualPaths == expectedPaths) $
+            expectationFailure
+              ( T.unpack
+                  ( suiteComponent suite
+                      <> " generated module inventory differs\nexpected: "
+                      <> T.pack (show expectedPaths)
+                      <> "\n but got: "
+                      <> T.pack (show actualPaths)
+                  )
+              )
+        _ -> pure ()
+
+expectedStableGeneratedPaths :: CompiledSuite -> IO [FilePath]
+expectedStableGeneratedPaths suite = case suiteGeneration suite of
+  "source" -> do
+    source <- requiredSuiteSource suite
+    generatedPathsForSource source
+  "workspace" -> do
+    source <- requiredSuiteSource suite
+    resolved <- resolveRepoFile ("keiro-dsl" </> source)
+    loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) resolved
+    workspace <- case loaded of
+      Left problem -> expectationFailure (show problem) >> fail "invalid stable workspace"
+      Right value -> pure value
+    plan <- case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
+      Left refusals -> expectationFailure (show refusals) >> fail "stable workspace scaffold refusal"
+      Right value -> pure value
+    pure (generatedPaths (map fst (wpModules plan)))
+  "skeletons" -> fmap (sort . nub . concat) . forM skeletonModuleRoots $ \(skeletonKind, root) -> do
+    source <- case skeletonFor skeletonKind of
+      Left problem -> expectationFailure (T.unpack problem) >> fail "invalid stable skeleton"
+      Right value -> pure value
+    service <- parseCheckedSource ("new:" <> T.unpack skeletonKind) source
+    let scaffoldContext = (defaultContext (specContext (checkedSpec service))) {moduleRoot = root}
+    pure (generatedPaths (scaffoldServiceModules scaffoldContext service))
+  other -> expectationFailure (T.unpack (suiteComponent suite <> " has invalid stable generation mode " <> other)) >> fail "invalid stable generation mode"
+
+generatedPathsForSource :: FilePath -> IO [FilePath]
+generatedPathsForSource path = do
+  source <- readRepoText path
+  service <- parseCheckedSource path source
+  pure (generatedPaths (scaffoldServiceModules (defaultContext (specContext (checkedSpec service))) service))
+
+parseCheckedSource :: FilePath -> Text -> IO CheckedService
+parseCheckedSource path source = case parseSource path source of
+  Left problem -> expectationFailure (show problem) >> fail "invalid stable source"
+  Right parsed -> pure (checkedSource parsed)
+
+generatedPaths :: [ScaffoldModule] -> [FilePath]
+generatedPaths = sort . map modulePath . filter ((== Generated) . kind)
+
+requiredSuiteSource :: CompiledSuite -> IO FilePath
+requiredSuiteSource suite = case suiteSource suite of
+  Nothing -> expectationFailure (T.unpack (suiteComponent suite <> " has no source")) >> fail "missing stable source"
+  Just source -> pure source
+
+workspaceContext :: WorkspaceSpec -> Context
+workspaceContext workspace =
+  Context
+    { contextName = wsContext workspace,
+      moduleRoot = maybe "" id (wsModuleRoot workspace),
+      placement = maybe GeneratedPrefix id (wsLayout workspace)
+    }
+
+skeletonModuleRoots :: [(Text, Text)]
+skeletonModuleRoots =
+  [ ("aggregate", "SkelAggregate"),
+    ("process", "SkelProcess"),
+    ("router", "SkelRouter"),
+    ("contract", "SkelContract"),
+    ("intake", "SkelIntake"),
+    ("emit", "SkelEmit"),
+    ("workqueue", "SkelQueue"),
+    ("workflow", "SkelWorkflow")
+  ]
+
+readBaseline :: IO ConformanceBaseline
+readBaseline = do
+  path <- resolveRepoFile "keiro-dsl/test/conformance-baseline.json"
+  decoded <- Aeson.eitherDecodeFileStrict' path
+  case decoded of
+    Left problem -> expectationFailure problem >> fail "invalid conformance baseline"
+    Right baseline -> pure baseline
+
+fixturePaths :: IO [FilePath]
+fixturePaths = do
+  root <- resolveRepoDirectory "keiro-dsl/test/fixtures"
+  relative <- walk root ""
+  pure . sort $ ["test/fixtures" </> path | path <- relative, takeExtension path == ".keiro"]
+
+conformanceComponents :: Text -> [Text]
+conformanceComponents =
+  sort
+    . mapMaybe (listToMaybe . T.words)
+    . filter ("keiro-dsl-conformance" `T.isPrefixOf`)
+    . mapMaybeTestSuite
+    . T.lines
+  where
+    mapMaybeTestSuite = foldr collect []
+    collect line rest = case T.stripPrefix "test-suite " (T.strip line) of
+      Just component -> component : rest
+      Nothing -> rest
+
+generatedBannerLines :: FilePath -> IO [(FilePath, Text)]
+generatedBannerLines root = do
+  relative <- walk root ""
+  fmap concat . forM [path | path <- relative, takeExtension path == ".hs"] $ \path -> do
+    contents <- TIO.readFile (root </> path)
+    pure
+      [ (path, line)
+      | line <- T.lines contents,
+        "@generated" `T.isInfixOf` line
+      ]
+
+walk :: FilePath -> FilePath -> IO [FilePath]
+walk root relative = do
+  entries <- sort <$> listDirectory (root </> relative)
+  fmap concat . forM entries $ \entry -> do
+    let child = if null relative then entry else relative </> entry
+    isDirectory <- doesDirectoryExist (root </> child)
+    if isDirectory then walk root child else pure [child]
+
+resolveRepoFile :: FilePath -> IO FilePath
+resolveRepoFile path = do
+  override <- lookupEnv "KEIRO_DSL_TEST_ROOT"
+  let packageRelative = maybe path T.unpack (T.stripPrefix "keiro-dsl/" (T.pack path))
+      candidates = nub ([packageRelative, path] <> maybe [] (\root -> [root </> packageRelative, root </> path]) override)
+  existing <- filterM doesFileExist candidates
+  case existing of
+    candidate : _ -> pure candidate
+    [] -> fail ("unable to locate conformance baseline file " <> show path)
+
+resolveRepoDirectory :: FilePath -> IO FilePath
+resolveRepoDirectory path = do
+  override <- lookupEnv "KEIRO_DSL_TEST_ROOT"
+  let packageRelative = maybe path T.unpack (T.stripPrefix "keiro-dsl/" (T.pack path))
+      candidates = nub ([packageRelative, path] <> maybe [] (\root -> [root </> packageRelative, root </> path]) override)
+  existing <- filterM doesDirectoryExist candidates
+  case existing of
+    candidate : _ -> pure candidate
+    [] -> fail ("unable to locate conformance directory " <> show path)
+
+decorate :: FilePath -> Text -> Text
+decorate path banner = T.pack path <> ": " <> banner
diff --git a/test/Keiro/Dsl/FrontendCompatibility.hs b/test/Keiro/Dsl/FrontendCompatibility.hs
--- a/test/Keiro/Dsl/FrontendCompatibility.hs
+++ b/test/Keiro/Dsl/FrontendCompatibility.hs
@@ -1,5 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
 -- | The executable compatibility oracle for the released keiro-dsl-0.7.0.0
 -- language frontend. The checked JSON is deliberately data, not an update mode:
 -- a behavior change must produce a reviewed manifest diff.
@@ -271,16 +269,16 @@
 readCompatibilityManifest = readManifest
 
 sourceFixturePaths :: IO [FilePath]
-sourceFixturePaths = fixturePaths ".keiro"
+sourceFixturePaths = fixturePaths "keiro-dsl/test/frontend-0.7/sources" ".keiro"
 
 workspaceFixturePaths :: IO [FilePath]
-workspaceFixturePaths = fixturePaths ".keiro-workspace"
+workspaceFixturePaths = fixturePaths "keiro-dsl/test/frontend-0.7/workspaces" ".keiro-workspace"
 
-fixturePaths :: String -> IO [FilePath]
-fixturePaths extension = do
-  fixtureRoot <- resolveRepoDirectory "keiro-dsl/test/fixtures"
+fixturePaths :: FilePath -> String -> IO [FilePath]
+fixturePaths rootPath extension = do
+  fixtureRoot <- resolveRepoDirectory rootPath
   relative <- walk fixtureRoot ""
-  pure . sort $ ["keiro-dsl/test/fixtures" </> path | path <- relative, takeExtension path == extension]
+  pure . sort $ [rootPath </> path | path <- relative, takeExtension path == extension]
   where
     walk root relative = do
       entries <- sort <$> listDirectory (root </> relative)
@@ -370,11 +368,12 @@
 
 renderWorkspaceMemberFailure :: IO Text
 renderWorkspaceMemberFailure = do
-  let path = "keiro-dsl/test/fixtures/workspace-member-parse-failed/service.keiro-workspace"
-  resolved <- resolveRepoPath path
-  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) path
+  let vectorPath = "keiro-dsl/test/frontend-0.7/workspaces/member-parse-failed/service.keiro-workspace"
+      releasedDiagnosticPath = "keiro-dsl/test/fixtures/workspace-member-parse-failed/service.keiro-workspace"
+  resolved <- resolveRepoPath vectorPath
+  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) releasedDiagnosticPath
   pure $ case loaded of
-    Left failure -> T.stripEnd (T.unlines (renderWorkspaceFailure path failure)) <> "\n"
+    Left failure -> T.stripEnd (T.unlines (renderWorkspaceFailure releasedDiagnosticPath failure)) <> "\n"
     Right _ -> error "workspace-member-parse-failed unexpectedly composed"
 
 integerFeatureSource :: Text
diff --git a/test/Keiro/Dsl/FrontendProfiles.hs b/test/Keiro/Dsl/FrontendProfiles.hs
--- a/test/Keiro/Dsl/FrontendProfiles.hs
+++ b/test/Keiro/Dsl/FrontendProfiles.hs
@@ -1,9 +1,9 @@
-{-# LANGUAGE ImportQualifiedPost #-}
 {-# LANGUAGE NoFieldSelectors #-}
 
 module Keiro.Dsl.FrontendProfiles (frontendProfilesSpec) where
 
 import Control.Monad (forM_)
+import Data.List (tails)
 import Data.List.NonEmpty qualified as NE
 import Data.Text (Text)
 import Data.Text qualified as T
@@ -24,26 +24,68 @@
       map definitionRow (NE.toList languageRegistry)
         `shouldBe` [ (1, Nothing, "keiro-dsl/syntax-profile/1", "keiro-dsl/runtime-semantics/1"),
                      (2, Just 1, "keiro-dsl/syntax-profile/2", "keiro-dsl/runtime-semantics/1"),
-                     (3, Just 2, "keiro-dsl/syntax-profile/2", "keiro-dsl/runtime-semantics/2")
+                     (3, Just 2, "keiro-dsl/syntax-profile/2", "keiro-dsl/runtime-semantics/2"),
+                     (4, Just 3, "keiro-dsl/syntax-profile/2", "keiro-dsl/runtime-semantics/3")
                    ]
+      map definitionCapabilities (NE.toList languageRegistry)
+        `shouldBe` [ [],
+                     [],
+                     [GeneratedIdDomainTypeIdV7, NominalEqualityV2],
+                     [GeneratedIdDomainTypeIdV7, NominalEqualityV2, ContractIdDomainTypeIdV7, StrictSpecSurfaceValidation]
+                   ]
+      map (runtimeProfileFoldSegments . definitionRuntimeSemanticsProfile) (NE.toList languageRegistry)
+        `shouldBe` [ [],
+                     [],
+                     ["semantic-contract:keiro-dsl/runtime-semantics/2"],
+                     ["semantic-contract:keiro-dsl/runtime-semantics/2"]
+                   ]
+      map definitionSupport (NE.toList languageRegistry)
+        `shouldBe` [CompatibilityOnly, CompatibilityOnly, CompatibilityOnly, Stable]
+      currentStableLanguageVersion `shouldBe` version 4
+      languageSupportForVersion (version 1) `shouldBe` Just CompatibilityOnly
+      languageSupportForVersion (version 2) `shouldBe` Just CompatibilityOnly
+      languageSupportForVersion (version 3) `shouldBe` Just CompatibilityOnly
+      languageSupportForVersion (version 4) `shouldBe` Just Stable
+      languageSupportForVersion (version 5) `shouldBe` Nothing
+      [definitionVersion definition | definition <- NE.toList languageRegistry, definitionSupport definition == Stable]
+        `shouldBe` [currentStableLanguageVersion]
+      definitionVersion (NE.last languageRegistry) `shouldBe` currentStableLanguageVersion
+      definitionPredecessor (NE.last languageRegistry) `shouldBe` Just (version 3)
+      forM_ (adjacent (NE.toList languageRegistry)) $ \(predecessor, successor) ->
+        forM_ allRuntimeCapabilities $ \capability ->
+          runtimeProfileHasCapability (definitionRuntimeSemanticsProfile predecessor) capability
+            `shouldSatisfy` \wasSupported ->
+              not wasSupported
+                || runtimeProfileHasCapability (definitionRuntimeSemanticsProfile successor) capability
       forM_ allFeatures $ \feature -> do
         languageFeatureMinimumVersion feature `shouldBe` version 2
         languageSupportsFeature (version 1) feature `shouldBe` False
         languageSupportsFeature (version 2) feature `shouldBe` True
         languageSupportsFeature (version 3) feature `shouldBe` True
+        languageSupportsFeature (version 4) feature `shouldBe` True
 
     it "does not infer a hypothetical successor profile or runtime contract" $ do
-      lookupLanguageDefinition (version 4) `shouldBe` Nothing
-      effectiveLanguageContractForVersion (version 4) `shouldBe` Nothing
-      forM_ allFeatures $ \feature -> languageSupportsFeature (version 4) feature `shouldBe` False
+      lookupLanguageDefinition (version 5) `shouldBe` Nothing
+      effectiveLanguageContractForVersion (version 5) `shouldBe` Nothing
+      forM_ allFeatures $ \feature -> languageSupportsFeature (version 5) feature `shouldBe` False
       languageVersionPolicy <- readRepoText "keiro-dsl/src/Keiro/Dsl/LanguageVersion.hs"
       semanticPolicy <- readRepoText "keiro-dsl/src/Keiro/Dsl/SemanticContract.hs"
       languageVersionPolicy `shouldNotSatisfy` T.isInfixOf "version >="
       semanticPolicy `shouldNotSatisfy` T.isInfixOf "version >="
 
+    it "keeps runtime identifier strings out of semantic gates" $ do
+      forM_
+        [ "keiro-dsl/src/Keiro/Dsl/IdDomain.hs",
+          "keiro-dsl/src/Keiro/Dsl/NominalType.hs",
+          "keiro-dsl/src/Keiro/Dsl/Validate.hs"
+        ]
+        $ \path -> do
+          policy <- readRepoText path
+          policy `shouldNotSatisfy` T.isInfixOf "keiro-dsl/runtime-semantics/"
+
     it "checks every real feature marker against the exact selected profile" $ do
       forM_ featureCases $ \FeatureCase {feature, marker, body} ->
-        forM_ [1, 2, 3] $ \versionNumber -> do
+        forM_ [1, 2, 3, 4] $ \versionNumber -> do
           let sourceName = "profile-" <> show versionNumber <> ".keiro"
               source = preamble versionNumber <> body
           case (versionNumber, parseSurfaceSource sourceName source) of
@@ -51,7 +93,7 @@
               phase `shouldBe` BodyParsingPhase
               code `shouldBe` SourceLanguageError LanguageFeatureRequiresVersion
               spanText source span `shouldBe` marker
-              supportedVersions `shouldBe` [version 2, version 3]
+              supportedVersions `shouldBe` [version 2, version 3, version 4]
               languageSupportsFeature (version versionNumber) feature `shouldBe` False
             (1, result) -> expectationFailure ("expected v1 feature refusal, got " <> show result)
             (_, Right _) -> languageSupportsFeature (version versionNumber) feature `shouldBe` True
@@ -59,14 +101,14 @@
 
     it "keeps feature spellings inert in comments, strings, wire keys, and identifiers" $ do
       inertBody <- readRepoText "keiro-dsl/test/fixtures/language-identifier-v1.keiro"
-      forM_ [1, 2, 3] $ \versionNumber ->
+      forM_ [1, 2, 3, 4] $ \versionNumber ->
         parseSurfaceSource ("inert-" <> show versionNumber <> ".keiro") (preamble versionNumber <> inertBody)
           `shouldSatisfy` isRight
 
   describe "frontend diagnostics" $ do
     it "classifies malformed and unsupported preambles at source selection with exact spans" $ do
       assertSourceSelection InvalidLanguageVersion "language keiro-dsl nope\ncontext malformed\n" "language keiro-dsl nope"
-      assertSourceSelection UnsupportedLanguageVersion "language keiro-dsl 4\ncontext future\n" "language keiro-dsl 4"
+      assertSourceSelection UnsupportedLanguageVersion "language keiro-dsl 5\ncontext future\n" "language keiro-dsl 5"
 
     it "reports ordinary body syntax with expected items and a point span" $ do
       let source = "language keiro-dsl 1\ncontext body\nlayout\n"
@@ -101,7 +143,7 @@
     it "keeps the released compatibility renderer byte-identical" $ do
       forM_
         [ "language keiro-dsl nope\ncontext malformed\n",
-          "language keiro-dsl 4\ncontext future\n",
+          "language keiro-dsl 5\ncontext future\n",
           preamble 1 <> featureBody TypedAggregateExpressionSyntax,
           "language keiro-dsl 1\ncontext\n"
         ]
@@ -152,6 +194,19 @@
 
 allFeatures :: [LanguageFeature]
 allFeatures = [NominalBindingSyntax, IntegerScalarSyntax, TypedAggregateExpressionSyntax, ExplicitTransitionImplementationSyntax]
+
+allRuntimeCapabilities :: [RuntimeCapability]
+allRuntimeCapabilities = [minBound .. maxBound]
+
+definitionCapabilities :: LanguageDefinition -> [RuntimeCapability]
+definitionCapabilities definition =
+  [ capability
+  | capability <- allRuntimeCapabilities,
+    runtimeProfileHasCapability (definitionRuntimeSemanticsProfile definition) capability
+  ]
+
+adjacent :: [a] -> [(a, a)]
+adjacent values = [(left, right) | left : right : _ <- tails values]
 
 preamble :: Int -> Text
 preamble versionNumber = "language keiro-dsl " <> T.pack (show versionNumber) <> "\n"
diff --git a/test/Keiro/Dsl/FrontendSurface.hs b/test/Keiro/Dsl/FrontendSurface.hs
--- a/test/Keiro/Dsl/FrontendSurface.hs
+++ b/test/Keiro/Dsl/FrontendSurface.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE ImportQualifiedPost #-}
 {-# LANGUAGE NoFieldSelectors #-}
 
 module Keiro.Dsl.FrontendSurface (frontendSurfaceSpec) where
@@ -84,7 +83,7 @@
             let fields = [(name, spanText aggregateSource fieldSpan) | Located {span = fieldSpan, value = SurfaceField name} <- elements]
                 expressions = [spanText aggregateSource expressionSpan | Located {span = expressionSpan, value = SurfaceExpression _} <- elements]
             fields `shouldContain` [("observedAt", "observedAt:Time")]
-            expressions `shouldContain` ["observedAt >= observedAt && revision >= revision"]
+            expressions `shouldContain` ["cmd.observedAt >= reg.observedAt && cmd.revision >= reg.revision"]
         other -> expectationFailure ("unexpected aggregate surface shape: " <> show other)
       emptySurface <- parseSurfaceRight "empty.keiro" "context empty   # trailing\n"
       case emptySurface of
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,5805 +1,7014 @@
-{-# LANGUAGE ImportQualifiedPost #-}
-
--- | Test driver for keiro-dsl. EP-1 milestone 1 tests: the @parse . pretty@
--- round-trip property over generated specs, and a unit test pinning the shape
--- of the canonical Reservation fixture.
-module Main (main) where
-
-import Control.Exception (bracket)
-import Control.Monad (filterM, forM, forM_, unless)
-import Data.Aeson (Value, object, (.=))
-import Data.Aeson qualified as Aeson
-import Data.Aeson.KeyMap qualified as KeyMap
-import Data.Either (isLeft, isRight)
-import Data.Foldable (toList)
-import Data.List (partition, sort, (\\))
-import Data.List.NonEmpty (NonEmpty (..))
-import Data.List.NonEmpty qualified as NE
-import Data.Map.Strict qualified as Map
-import Data.Set qualified as Set
-import Data.Text qualified as T
-import Data.Text.IO qualified as TIO
-import Keiki.ProjectionDomain (matchesTextPattern)
-import Keiro.Codec (Codec (..), EventType (..), decodeRaw)
-import Keiro.Codec.IdDomain (idDomainSampleText, idDomainTextPattern, typeIdV7Domain, validateIdDomainText)
-import Keiro.Dsl.AggregateType
-import Keiro.Dsl.BehaviorCoverage qualified as Behavior
-import Keiro.Dsl.CodecCompare
-import Keiro.Dsl.Coverage qualified as Coverage
-import Keiro.Dsl.Diff (Change (..), ChangeKind (..), CompatibilitySurface (..), CompatibilityVector (..), FamilyDiff (..), Label (..), NodeFamily, RolloutConstraint (..), SurfaceVerdict (..), defaultGate, deriveLabel, diffServices, diffSources, diffSpecs, familyRegistry, gateWith, gatedBreaking, isAdvisory, isBreaking, verdictFor)
-import Keiro.Dsl.DiffReport (Remedy (..), diffReport, parseSurfaceName, remediationFor, renderExplainBlock, renderFinding)
-import Keiro.Dsl.EventOutput
-import Keiro.Dsl.ExplainBindings (BindingHole (..), BindingObligation (..), BindingObligationKind (..), bindingHoles, bindingObligations, bindingObligationsForService, renderBindingObligations)
-import Keiro.Dsl.Expression
-import Keiro.Dsl.FoldFingerprint (aggregateFoldFingerprint, aggregateFoldFingerprintForService, aggregateFoldSurface, aggregateFoldSurfaceForService)
-import Keiro.Dsl.FrontendCompatibility (frontendCompatibilitySpec)
-import Keiro.Dsl.FrontendProfiles (frontendProfilesSpec)
-import Keiro.Dsl.FrontendSurface (frontendSurfaceSpec)
-import Keiro.Dsl.Goldens (GoldenEvidence (..), GoldenPayload (..), emitGoldenPayloads, goldenRelativePath, goldensForDiff)
-import Keiro.Dsl.Grammar
-import Keiro.Dsl.Harness (harnessFor, harnessForWithGoldens, harnessReadModel, harnessRouter, harnessWorkflow)
-import Keiro.Dsl.IdDomain (IdDomainContract (..), idDomainContractFor, idDomainIdentitiesForService)
-import Keiro.Dsl.LanguageVersion
-import Keiro.Dsl.Manifest (manifestDependencies, moduleNameOf, renderManifest)
-import Keiro.Dsl.MappedConsumer (ConsumerPlan (..), MappingIdentity (..), consumerPlan)
-import Keiro.Dsl.NominalType hiding (NominalInvalidHaskellSource, NominalInvalidIdPrefix, NominalInvalidIdentity, NominalMissingIngredient)
-import Keiro.Dsl.Parser (parseSource, parseSpec)
-import Keiro.Dsl.PrettyPrint (renderSource, renderSpec, renderTransition)
-import Keiro.Dsl.ReadModelShape (canonicalShape, deriveShapeHash, registryNameFor, subscriptionNameFor)
-import Keiro.Dsl.ReplayImpact (AggregateImpact (..), ReplayImpact (..))
-import Keiro.Dsl.ReplayImpact qualified as ReplayImpact
-import Keiro.Dsl.Scaffold (Context (..), ModuleKind (..), NominalGenerationOwner (..), NominalUseSite (..), ScaffoldModule (..), codecComparisonBanner, codecComparisonModule, defaultContext, firewallBreaches, genPrefixFor, generatedNominalModule, holePrefixFor, obsoleteGeneratedOutputHooks, planNominalGeneration, scaffoldAggregate, scaffoldIntake, scaffoldProcess, scaffoldPublisher, scaffoldReadModel, scaffoldRefusals, scaffoldReplayAudit, scaffoldRouter, scaffoldWorkqueue, windowSeconds)
-import Keiro.Dsl.ScaffoldRecord (ScaffoldRecord (..), parseRecord, recordFileName, renderRecord)
-import Keiro.Dsl.ScaffoldRun (MappingDrift (..), Refusal (..), ScaffoldReport (..), SourceLanguageDrift (..), StaleModule (..), WriteDisposition (..), executeScaffold, executeScaffoldWithLanguage, executeServiceScaffold, planScaffold, planServiceScaffold, renderRefusals, renderScaffoldReport, scaffoldModules, scaffoldServiceModules)
-import Keiro.Dsl.SemanticContract
-import Keiro.Dsl.Skeleton (skeletonFor, skeletonKinds)
-import Keiro.Dsl.TypeGraph
-import Keiro.Dsl.Validate (Diagnostic (..), DiagnosticCode (..), Severity (..), derivedQueueTrio, renderDiagnostic, validateService, validateSpec)
-import Keiro.Dsl.Workspace
-import Keiro.Dsl.WorkspaceAdoption
-import Keiro.Dsl.WorkspaceDiff
-import Keiro.Dsl.WorkspaceRecord
-import Keiro.Dsl.WorkspaceScaffold
-import System.Directory (createDirectory, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getTemporaryDirectory, listDirectory, removeFile, removePathForcibly)
-import System.Environment (lookupEnv)
-import System.Exit (ExitCode (..))
-import System.FilePath (takeDirectory, takeExtension, (</>))
-import System.IO (hClose, openTempFile)
-import System.Process (readProcessWithExitCode)
-import Test.Hspec hiding (Spec)
-import Test.QuickCheck
-
-main :: IO ()
-main = hspec $ do
-  frontendCompatibilitySpec
-  frontendSurfaceSpec
-  frontendProfilesSpec
-
-  describe "source language version" $ do
-    let legacy = "context hospital-capacity\n"
-        declared = "# leading comment\n\nlanguage keiro-dsl 1\ncontext hospital-capacity\n"
-        failureCode source = case parseSource "source.keiro" source of
-          Left (SourceLanguageFailure diagnostic) -> Just (sourceLanguageErrorCode diagnostic)
-          _ -> Nothing
-        parseRight name source = case parseSource name source of
-          Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-          Right value -> pure value
-        declaredVersionOf DeclaredLanguage {declaredLanguageVersion = version} = Just version
-        declaredVersionOf LegacyUnversioned = Nothing
-        orderedSubstrings needles haystack = go (map T.pack needles) (T.pack haystack)
-          where
-            go [] _ = True
-            go (needle : rest) remaining =
-              let (_, suffix) = T.breakOn needle remaining
-               in not (T.null suffix) && go rest (T.drop (T.length needle) suffix)
-
-    it "selects declared v1 after comments while preserving semantic equality" $ do
-      legacySource <- parseRight "legacy.keiro" legacy
-      declaredSource <- parseRight "declared.keiro" declared
-      parsedSpec legacySource `shouldBe` parsedSpec declaredSource
-      parsedSourceLanguage legacySource `shouldBe` LegacyUnversioned
-      declaredVersionOf (parsedSourceLanguage declaredSource) `shouldBe` languageVersion 1
-      effectiveLanguageVersion (parsedSourceLanguage legacySource)
-        `shouldBe` effectiveLanguageVersion (parsedSourceLanguage declaredSource)
-
-    it "threads paired released versions through one checked semantic boundary" $ do
-      let body = T.unlines ["context semantic-pair", "aggregate Counter", "  regs", "  states Open"]
-          v1Text = "language keiro-dsl 1\n" <> body
-          v2Text = "language keiro-dsl 2\n" <> body
-      v1Source <- parseRight "reservation-v1.keiro" v1Text
-      v2Source <- parseRight "reservation-v2.keiro" v2Text
-      let v1Service = checkedSource v1Source
-          v2Service = checkedSource v2Source
-          v1Spec = checkedSpec v1Service
-          v2Spec = checkedSpec v2Service
-          ctx = defaultContext (specContext v1Spec)
-          aggregates spec = [aggregate | NAggregate aggregate <- specNodes spec]
-      v1Spec `shouldBe` v2Spec
-      Just (effectiveContractLanguageVersion (checkedLanguageContract v1Service)) `shouldBe` languageVersion 1
-      Just (effectiveContractLanguageVersion (checkedLanguageContract v2Service)) `shouldBe` languageVersion 2
-      effectiveRuntimeSemantics (checkedLanguageContract v1Service)
-        `shouldBe` effectiveRuntimeSemantics (checkedLanguageContract v2Service)
-      validateService v1Service `shouldBe` validateService v2Service
-      scaffoldServiceModules ctx v1Service `shouldBe` scaffoldServiceModules ctx v2Service
-      case (aggregates v1Spec, aggregates v2Spec) of
-        ([v1Aggregate], [v2Aggregate]) -> do
-          aggregateFoldSurfaceForService v1Service v1Aggregate
-            `shouldBe` aggregateFoldSurfaceForService v2Service v2Aggregate
-          aggregateFoldFingerprintForService v1Service v1Aggregate
-            `shouldBe` aggregateFoldFingerprintForService v2Service v2Aggregate
-        other -> expectationFailure ("expected one aggregate per paired source, got " <> show (fmap length other))
-      diffServices v1Service v2Service `shouldBe` []
-      ReplayImpact.replayImpactServices v1Service v2Service `shouldBe` ReplayNeutral
-
-    it "retains one effective contract for same-version workspaces and refuses mixed versions" $ do
-      let manifest = "service semantic-workspace\nspec domain/a.keiro\nspec domain/b.keiro\n"
-          v1Body = "language keiro-dsl 1\ncontext semantic-workspace\n"
-          v2Body = "language keiro-dsl 2\ncontext semantic-workspace\n"
-          sourceWith b =
-            memoryContentSource
-              ( Map.fromList
-                  [ ("service.keiro-workspace", manifest),
-                    ("domain/a.keiro", b),
-                    ("domain/b.keiro", b)
-                  ]
-              )
-          mixedSource =
-            memoryContentSource
-              ( Map.fromList
-                  [ ("service.keiro-workspace", manifest),
-                    ("domain/a.keiro", v1Body),
-                    ("domain/b.keiro", v2Body)
-                  ]
-              )
-      sameVersion <- loadWorkspace (sourceWith v2Body) "service.keiro-workspace"
-      case sameVersion of
-        Right workspace -> do
-          Just (effectiveContractLanguageVersion (checkedLanguageContract (checkedWorkspace workspace))) `shouldBe` languageVersion 2
-          validateService (checkedWorkspace workspace) `shouldBe` []
-        Left failure -> expectationFailure (show failure)
-      mixed <- loadWorkspace mixedSource "service.keiro-workspace"
-      case mixed of
-        Left (WorkspaceRefused diagnostics) ->
-          map wdCode (NE.toList diagnostics) `shouldContain` [WorkspaceLanguageVersionMismatch]
-        other -> expectationFailure ("expected a mixed-version refusal, got " <> show other)
-
-    it "refuses a source/service contract mismatch before creating the output directory" $ do
-      v1Version <- maybe (expectationFailure "version 1 missing" >> fail "unreachable") pure (languageVersion 1)
-      parsed <- parseRight "semantic-v2.keiro" "language keiro-dsl 2\ncontext semantic-refusal\n"
-      let service = checkedSource parsed
-          ctx = defaultContext "semantic-refusal"
-      modules <- case planServiceScaffold ctx service of
-        Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
-        Right planned -> pure planned
-      withTempDirectory "keiro-dsl-semantic-refusal" $ \root -> do
-        let out = root </> "not-created"
-        result <- executeServiceScaffold out False "semantic-v2.keiro" (DeclaredLanguage v1Version noLoc) ctx service modules
-        result `shouldBe` Left [SemanticContractMismatch "source provenance and checked service selected different effective language contracts"]
-        doesDirectoryExist out `shouldReturn` False
-
-    it "retains explicit declarations in source rendering and leaves legacy unversioned" $ do
-      legacySource <- parseRight "legacy.keiro" legacy
-      declaredSource <- parseRight "declared.keiro" declared
-      renderSource legacySource `shouldBe` "context hospital-capacity\n"
-      renderSource declaredSource `shouldBe` "language keiro-dsl 1\ncontext hospital-capacity\n"
-      parseSpec "declared.keiro" declared `shouldBe` Right (parsedSpec declaredSource)
-
-    it "classifies invalid, unsupported, duplicate, and misplaced preambles" $ do
-      failureCode "language keiro-dsl 0\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
-      failureCode "language keiro-dsl nope\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
-      failureCode "language keiro-dsl -1\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
-      failureCode "language keiro-dsl 4\ncontext hospital-capacity\n" `shouldBe` Just UnsupportedLanguageVersion
-      failureCode "language keiro-dsl 1\nlanguage keiro-dsl 1\ncontext hospital-capacity\n" `shouldBe` Just DuplicateLanguagePreamble
-      failureCode "context hospital-capacity\nlanguage keiro-dsl 1\n" `shouldBe` Just MisplacedLanguagePreamble
-
-    it "treats language and successor spellings as data in nested grammar positions" $ do
-      forM_ ["language-identifier-v1.keiro", "language-identifier-v2.keiro"] $ \fixture -> do
-        source <- readTestText ("test/fixtures/" <> fixture)
-        parsed <- parseRight fixture source
-        validateSpec (parsedSpec parsed) `shouldBe` []
-      v1 <- readTestText "test/fixtures/language-identifier-v1.keiro"
-      let manifest = "service language-collisions\nspec domain/collisions.keiro\n"
-          workspaceSource = memoryContentSource (Map.fromList [("service.keiro-workspace", manifest), ("domain/collisions.keiro", v1)])
-      loaded <- loadWorkspace workspaceSource "service.keiro-workspace"
-      loaded `shouldSatisfy` isRight
-
-    it "keeps duplicate and misplaced preamble diagnostics on their grammar lines" $ do
-      let sourceFailureAt expectedCode expectedLine source =
-            case parseSource "located.keiro" source of
-              Left (SourceLanguageFailure diagnostic) -> do
-                sourceLanguageErrorCode diagnostic `shouldBe` expectedCode
-                unLoc (sourceLanguageLoc diagnostic) `shouldBe` expectedLine
-              other -> expectationFailure ("expected located source-language failure, got " <> show other)
-      sourceFailureAt DuplicateLanguagePreamble 2 "language keiro-dsl 1\nlanguage keiro-dsl 1\ncontext located\n"
-      sourceFailureAt MisplacedLanguagePreamble 3 "context located\nid language prefix=lang\nlanguage keiro-dsl 1\n"
-
-    it "rejects a future version before parsing an invalid v1 body" $
-      case parseSource "future.keiro" "language keiro-dsl 4\nthis is not a v2 body\n" of
-        Left failure@(SourceLanguageFailure diagnostic) -> do
-          sourceLanguageErrorCode diagnostic `shouldBe` UnsupportedLanguageVersion
-          renderParseFailure failure `shouldSatisfy` T.isInfixOf "supported versions: 1, 2, 3"
-          renderParseFailure failure `shouldNotSatisfy` T.isInfixOf "expecting `context`"
-        other -> expectationFailure ("expected source-language failure, got " <> show other)
-
-    it "accepts and canonically round-trips nominal declarations only in v2" $ do
-      let nominalSource =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context orders",
-                "id OrderId prefix=ord using {",
-                "  haskell package=orders-domain module=Orders.Id type=OrderId",
-                "  binding = \"Orders.KeiroBindings.orderIdBinding\"",
-                "  binding-version = \"1\"",
-                "  canonical-type = \"orders.OrderId.v1\"",
-                "  fixtures = \"Orders.KeiroBindings.orderIdFixtures\"",
-                "}",
-                "enum OrderStatus { Draft=draft Submitted=submitted } using {",
-                "  haskell package=orders-domain module=Orders.Order type=OrderStatus",
-                "  binding = \"Orders.KeiroBindings.orderStatusBinding\"",
-                "  binding-version = \"1\"",
-                "  canonical-type = \"orders.OrderStatus.v1\"",
-                "  fixtures = \"Orders.KeiroBindings.orderStatusFixtures\"",
-                "}",
-                "mapped nominal AccountNumber : Text {",
-                "  haskell package=orders-domain module=Orders.Account type=AccountNumber",
-                "  binding = \"Orders.KeiroBindings.accountNumberBinding\"",
-                "  binding-version = \"1\"",
-                "  canonical-type = \"orders.AccountNumber.v1\"",
-                "  fixtures = \"Orders.KeiroBindings.accountNumberFixtures\"",
-                "  initial = \"Orders.KeiroBindings.initialAccountNumber\"",
-                "}"
-              ]
-      parsed <- parseRight "nominal.keiro" nominalSource
-      length (specIds (parsedSpec parsed)) `shouldBe` 1
-      length (specEnums (parsedSpec parsed)) `shouldBe` 1
-      length (specNominalScalars (parsedSpec parsed)) `shouldBe` 1
-      parseSource "nominal-round-trip.keiro" (renderSource parsed) `shouldBe` Right parsed
-
-    it "reports successor nominal syntax as one language-version diagnostic under v1 and legacy" $ do
-      let body = "context orders\nmapped nominal AccountNumber : Text {}\n"
-      failureCode ("language keiro-dsl 1\n" <> body) `shouldBe` Just LanguageFeatureRequiresVersion
-      failureCode body `shouldBe` Just LanguageFeatureRequiresVersion
-
-    it "attributes every successor feature gate to its owning grammar production" $ do
-      let featureFailureAt expectedLine source =
-            case parseSource "feature.keiro" source of
-              Left (SourceLanguageFailure diagnostic) -> do
-                sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
-                unLoc (sourceLanguageLoc diagnostic) `shouldBe` expectedLine
-              other -> expectationFailure ("expected a located feature gate, got " <> show other)
-          aggregateWith clause =
-            T.unlines
-              [ "language keiro-dsl 1",
-                "context feature-gates",
-                "aggregate Account",
-                "  regs",
-                "    balance Text = \"0\"",
-                "  states Open",
-                "  command Adjust { amount:Text }",
-                "  event Adjusted = fields(Adjust)",
-                "  Open -- Adjust --> " <> clause <> " ; emit Adjusted ; goto Open"
-              ]
-      featureFailureAt 3 (T.unlines ["language keiro-dsl 1", "context feature-gates", "id AccountId prefix=acct using {"])
-      featureFailureAt 3 (T.unlines ["language keiro-dsl 1", "context feature-gates", "mapped nominal AccountNumber : Text {}"])
-      featureFailureAt 5 (T.unlines ["language keiro-dsl 1", "context feature-gates", "aggregate Account", "  regs", "    balance Integer = 0", "  states Open"])
-      featureFailureAt 9 (aggregateWith "guard reg.balance == cmd.amount")
-      featureFailureAt 9 (aggregateWith "implementation hole")
-
-    it "reports a declaration-only rewrite without semantic, generated, fold, or replay impact" $ do
-      fixture <- readTestText "test/fixtures/reservation.keiro"
-      let legacyFixture = T.unlines (drop 1 (T.lines fixture))
-      legacySource <- parseRight "legacy.keiro" legacyFixture
-      declaredSource <- parseRight "declared.keiro" fixture
-      let oldSpec = parsedSpec legacySource
-          newSpec = parsedSpec declaredSource
-          changes = diffSources legacySource declaredSource
-          vectors = [ckVector kind | change <- changes, let kind = workspaceChangeKind change]
-      map changeCode changes `shouldBe` [SourceLanguageDeclarationChanged]
-      diffSpecs oldSpec newSpec `shouldBe` []
-      vectors `shouldSatisfy` all (\compatibility -> all ((== VCompatible) . (`verdictFor` compatibility)) [minBound .. maxBound])
-      case changes of
-        [change] ->
-          remediationFor (ckContext (workspaceChangeKind change)) SourceLanguageDeclarationChanged
-            `shouldBe` (RemedyNoSemanticAction :| [])
-        _ -> expectationFailure "expected one source-language change"
-      let generatedSurface spec =
-            [ (modulePath scaffoldModule, moduleText scaffoldModule, kind scaffoldModule)
-            | scaffoldModule <- scaffoldModules (defaultContext (specContext spec)) spec
-            ]
-      generatedSurface oldSpec `shouldBe` generatedSurface newSpec
-      [aggregateFoldFingerprint oldSpec aggregate | NAggregate aggregate <- specNodes oldSpec]
-        `shouldBe` [aggregateFoldFingerprint newSpec aggregate | NAggregate aggregate <- specNodes newSpec]
-      ReplayImpact.replayImpact oldSpec newSpec `shouldBe` ReplayNeutral
-
-    it "exposes stable JSON inspection for a source and canonically ordered workspace members" $ do
-      (sourceCode, sourceOut, sourceErr) <- runKeiroDsl ["inspect", "test/fixtures/reservation.keiro", "--format=json"]
-      sourceCode `shouldBe` ExitSuccess
-      sourceErr `shouldBe` ""
-      sourceOut `shouldContain` "\"schema\":\"keiro-dsl/source-inspection/1\""
-      sourceOut `shouldContain` "\"kind\":\"source\""
-      sourceOut `shouldContain` "\"sourceForm\":\"declared\""
-      sourceOut `shouldContain` "\"declaredLanguageVersion\":1"
-      sourceOut `shouldContain` "\"effectiveLanguageVersion\":1"
-      sourceOut `shouldContain` "\"effectiveSemanticContract\":{"
-      sourceOut `shouldContain` "\"runtimeSemantics\":\"keiro-dsl/runtime-semantics/1\""
-      (workspaceCode, workspaceOut, workspaceErr) <- runKeiroDsl ["inspect", canonicalWorkspacePath, "--format=json"]
-      workspaceCode `shouldBe` ExitSuccess
-      workspaceErr `shouldBe` ""
-      workspaceOut `shouldContain` "\"kind\":\"workspace\""
-      workspaceOut `shouldContain` "\"service\":\"demo-project\""
-      workspaceOut `shouldContain` "\"effectiveSemanticContract\":{"
-      workspaceOut `shouldSatisfy` orderedSubstrings ["domain/project-artifact.keiro", "domain/project.keiro", "domain/shared.keiro"]
-
-    it "keeps only the named source-version compatibility fixtures outside canonical v1" $ do
-      fixtureTree <- treeSnapshot "test/fixtures"
-      let withoutCanonicalV1 =
-            sort
-              [ path
-              | (path, contents) <- fixtureTree,
-                takeExtension path == ".keiro",
-                "language keiro-dsl 1" `notElem` T.lines contents
-              ]
-      withoutCanonicalV1
-        `shouldBe` sort
-          [ "language-duplicate.keiro",
-            "aggregate-collection-expressions-v2-rejects.keiro",
-            "aggregate-scalar-expressions-v2.keiro",
-            "behavior-complete-workspace/declarations.keiro",
-            "behavior-complete-workspace/journey.keiro",
-            "behavior-complete.keiro",
-            "id-domain-migration-v3.keiro",
-            "language-future.keiro",
-            "language-identifier-v1.keiro",
-            "language-identifier-v2.keiro",
-            "language-legacy.keiro",
-            "language-malformed.keiro",
-            "language-misplaced.keiro",
-            "language-zero.keiro",
-            "nominal-bad-qualified.keiro",
-            "nominal-invalid-prefix.keiro",
-            "nominal-missing-facts.keiro",
-            "nominal-missing-initial.keiro",
-            "nominal-name-collision.keiro",
-            "nominal-scalars.keiro",
-            "nominal-unsupported-representation.keiro",
-            "workspace-nominals/domain/project-artifact.keiro",
-            "workspace-nominals/domain/project.keiro",
-            "workspace-nominals/domain/shared.keiro"
-          ]
-
-    it "checks v1, rejects a future contract once, and inspects legacy explicitly" $ do
-      (v1Code, v1Out, v1Err) <- runKeiroDsl ["check", "test/fixtures/language-v1.keiro"]
-      v1Code `shouldBe` ExitSuccess
-      v1Out `shouldBe` "OK\n"
-      v1Err `shouldBe` ""
-      (futureCode, _, futureErr) <- runKeiroDsl ["check", "test/fixtures/language-future.keiro"]
-      futureCode `shouldBe` ExitFailure 1
-      T.count "UnsupportedLanguageVersion" (T.pack futureErr) `shouldBe` 1
-      futureErr `shouldContain` "supported versions: 1, 2, 3"
-      futureErr `shouldNotContain` "expecting `context`"
-      (legacyCode, legacyOut, legacyErr) <- runKeiroDsl ["inspect", "test/fixtures/language-legacy.keiro", "--format=json"]
-      legacyCode `shouldBe` ExitSuccess
-      legacyErr `shouldBe` ""
-      legacyOut `shouldContain` "\"sourceForm\":\"legacy-unversioned\""
-      legacyOut `shouldContain` "\"declaredLanguageVersion\":null"
-      legacyOut `shouldContain` "\"effectiveLanguageVersion\":1"
-
-    it "checks and scaffolds contextual language identifiers through the CLI" $ do
-      forM_ ["language-identifier-v1.keiro", "language-identifier-v2.keiro"] $ \fixture -> do
-        let sourcePath = "test/fixtures/" <> fixture
-        (checkCode, checkOut, checkErr) <- runKeiroDsl ["check", sourcePath]
-        checkCode `shouldBe` ExitSuccess
-        checkOut `shouldBe` "OK\n"
-        checkErr `shouldBe` ""
-        withTempDirectory ("keiro-dsl-" <> fixture) $ \out -> do
-          (scaffoldCode, _, scaffoldErr) <- runKeiroDsl ["scaffold", sourcePath, "--out", out]
-          scaffoldCode `shouldBe` ExitSuccess
-          scaffoldErr `shouldContain` "firewall: OK"
-
-    it "preserves a workspace member's source-selection code beneath outer attribution" $ do
-      let manifest = "service demo\nspec domain/future.keiro\n"
-          futureSource = "language keiro-dsl 4\nthis body must not parse\n"
-          source = memoryContentSource (Map.fromList [("service.keiro-workspace", manifest), ("domain/future.keiro", futureSource)])
-      loaded <- loadWorkspace source "service.keiro-workspace"
-      case loaded of
-        Left (WorkspaceRefused (diagnostic :| [])) -> do
-          wdCode diagnostic `shouldBe` WorkspaceMemberParseFailed
-          sourceLanguageErrorCode <$> wdSourceLanguageCause diagnostic
-            `shouldBe` Just UnsupportedLanguageVersion
-          renderWorkspaceDiagnostic "service.keiro-workspace" diagnostic
-            `shouldSatisfy` T.isInfixOf "UnsupportedLanguageVersion"
-        other -> expectationFailure ("expected one attributed source-language refusal, got " <> show other)
-
-    it "attributes a workspace provenance-only diff to the changed member" $ do
-      workspace <- shouldComposeWorkspace canonicalWorkspacePath
-      case wsMembers workspace of
-        firstMember : remaining -> do
-          let changedMember = firstMember {wmSourceLanguage = LegacyUnversioned}
-              changedWorkspace = workspace {wsMembers = changedMember : remaining}
-              changes = diffWorkspaces workspace changedWorkspace
-          map (changeCode . wcChange) changes `shouldBe` [SourceLanguageDeclarationChanged]
-          map (fmap osFile . wcDeclarationSite) changes `shouldBe` [Just (wmPath firstMember)]
-          map wcChange changes `shouldSatisfy` all (not . gatedBreaking (gateWith [minBound .. maxBound]))
-        _ -> expectationFailure "canonical workspace had no member"
-
-  describe "ID domain" $ do
-    it "registers language 3 as the first enforced runtime contract" $ do
-      parsed <- case parseSource "id-domain-v3.keiro" "language keiro-dsl 3\ncontext id-domain\n" of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      let contract = checkedLanguageContract (checkedSource parsed)
-      effectiveRuntimeSemantics contract `shouldBe` "keiro-dsl/runtime-semantics/2"
-      effectiveContractLanguageVersion contract `shouldBe` maybe (error "missing v3") id (languageVersion 3)
-      idDomainContractFor contract "req" `shouldSatisfy` (/= Nothing)
-
-    it "keeps runtime validation and the exact Keiki text image in agreement" $ do
-      let contract = typeIdV7Domain "req"
-          sample = idDomainSampleText contract
-          suffix = T.drop (T.length "req_") sample
-          replaceAt position replacement value =
-            T.take position value <> T.singleton replacement <> T.drop (position + 1) value
-          accepted =
-            [ sample,
-              replaceAt (T.length "req_" + 10) 'f' sample,
-              replaceAt (T.length "req_" + 13) 'v' sample
-            ]
-          rejected =
-            [ "",
-              "req_",
-              "other_" <> suffix,
-              "req__" <> suffix,
-              T.dropEnd 1 sample,
-              sample <> "0",
-              T.toUpper sample,
-              replaceAt (T.length "req_" + 0) '8' sample,
-              replaceAt (T.length "req_" + 10) 'd' sample,
-              replaceAt (T.length "req_" + 13) 'c' sample
-            ]
-          patternValue = either (error . show) id (idDomainTextPattern contract)
-      idDomainVersion contract `shouldBe` "keiro-dsl/id-domain/typeid-v7/1"
-      idDomainSeparator contract `shouldBe` '_'
-      idDomainSuffixLength contract `shouldBe` 26
-      idDomainMaxLength contract `shouldBe` T.length sample
-      forM_ accepted $ \value -> do
-        validateIdDomainText contract value `shouldBe` Right ()
-        matchesTextPattern patternValue value `shouldBe` True
-      forM_ rejected $ \value -> do
-        validateIdDomainText contract value `shouldSatisfy` isLeft
-        matchesTextPattern patternValue value `shouldBe` False
-
-    it "agrees for generated canonical and malformed domain values" $ property $ do
-      let crockford = "0123456789abcdefghjkmnpqrstvwxyz"
-          segment count = vectorOf count (elements crockford)
-      leading <- elements "01234567"
-      beforeVersion <- segment 9
-      version <- elements "ef"
-      beforeVariant <- segment 2
-      variant <- elements "89abrstv"
-      afterVariant <- segment 12
-      let value = T.pack ("req_" <> [leading] <> beforeVersion <> [version] <> beforeVariant <> [variant] <> afterVariant)
-          contract = typeIdV7Domain "req"
-          patternValue = either (error . show) id (idDomainTextPattern contract)
-          invalidValues = [T.toUpper value, "other_" <> T.drop 4 value, T.dropEnd 1 value, value <> "0"]
-      pure $
-        conjoin
-          ( counterexample (T.unpack value) (validateIdDomainText contract value == Right () && matchesTextPattern patternValue value)
-              : [counterexample (T.unpack invalid) (isLeft (validateIdDomainText contract invalid) && not (matchesTextPattern patternValue invalid)) | invalid <- invalidValues]
-          )
-
-    it "enforces the same contract before consumer binding conversion and explains its version" $ do
-      v2Source <- readTestText "test/fixtures/nominal-scalars.keiro"
-      let v3Text = T.replace "language keiro-dsl 2" "language keiro-dsl 3" v2Source
-      parsed <- case parseSource "nominal-scalars-v3.keiro" v3Text of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      let service = checkedSource parsed
-          spec = checkedSpec service
-          modules = scaffoldServiceModules (defaultContext (specContext spec)) service
-          generatedText suffix = case [moduleText value | value <- modules, T.pack suffix `T.isSuffixOf` T.pack (modulePath value)] of
-            [value] -> value
-            values -> error ("expected one generated module ending in " <> suffix <> ", got " <> show (length values))
-          codecModule = generatedText "NominalLedger/Codec.hs"
-          projectionModule = generatedText "NominalProjections.hs"
-          harnessModule = generatedText "NominalLedger/Harness.hs"
-      validateService service `shouldBe` []
-      codecModule `shouldSatisfy` T.isInfixOf "case validateIdDomainText (typeIdV7Domain \"ord\") input of"
-      codecModule `shouldSatisfy` T.isInfixOf "Right () -> case KindID.parseText @\"ord\" input of"
-      projectionModule `shouldSatisfy` T.isInfixOf "idDomainTextPattern (typeIdV7Domain \"ord\")"
-      projectionModule `shouldSatisfy` T.isInfixOf "validateIdDomainText (typeIdV7Domain \"ord\") value"
-      harnessModule `shouldSatisfy` T.isInfixOf "nominal ID binding preserves canonical representations: OrderId"
-      harnessModule `shouldSatisfy` T.isInfixOf "nominal ID boundary rejects wrong-prefix and normalized text: OrderId"
-      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligationsForService service)
-      let orderIdBindings = [obligation | obligation <- obligations, obligationMappedName obligation == "OrderId", obligationKind obligation == BindingValue]
-      map obligationIdDomainContract orderIdBindings `shouldBe` [Just "keiro-dsl/id-domain/typeid-v7/1"]
-      renderBindingObligations (specContext spec) obligations
-        `shouldSatisfy` T.isInfixOf "id-domain-contract: \"keiro-dsl/id-domain/typeid-v7/1\""
-
-    it "reports adoption by boundary, invalidates snapshots, and preserves replay compatibility" $ do
-      v2Text <- readTestText "test/fixtures/id-domain-migration-v3.keiro"
-      let oldText = T.replace "language keiro-dsl 3" "language keiro-dsl 2" v2Text
-      oldSource <- case parseSource "id-domain-migration-v2.keiro" oldText of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      newSource <- case parseSource "id-domain-migration-v3.keiro" v2Text of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      let oldService = checkedSource oldSource
-          newService = checkedSource newSource
-          changes = diffSources oldSource newSource
-          findings = [kindOfChange change | change <- changes, changeCode change == IdDomainContractChanged]
-      length findings `shouldBe` 1
-      forM_ findings $ \finding -> do
-        verdictFor PrivateHistoryRead (ckVector finding) `shouldBe` VCompatible
-        verdictFor OldBinaryReadNewEvents (ckVector finding) `shouldBe` VCompatible
-        verdictFor SnapshotHydration (ckVector finding) `shouldBe` VAdvisory
-        verdictFor PublicConsumer (ckVector finding) `shouldBe` VBreaking
-        verdictFor PersistedIdentity (ckVector finding) `shouldBe` VCompatible
-        verdictFor ConsumerBuild (ckVector finding) `shouldBe` VAdvisory
-        ckDetail finding `shouldSatisfy` T.isInfixOf "historical event replay retains its legacy decoder"
-        remediationFor (ckContext finding) (ckCode finding)
-          `shouldBe` RemedyDeploymentOrder RolloutProducerLast :| [RemedyStateCodecBump, RemedyRecompileConsumers, RemedyRunConformance]
-      [ckDetail finding | change <- changes, changeCode change == SourceLanguageDeclarationChanged, let finding = kindOfChange change]
-        `shouldSatisfy` all (T.isInfixOf "effective runtime semantics changed")
-      idDomainIdentitiesForService oldService `shouldBe` []
-      idDomainIdentitiesForService newService
-        `shouldSatisfy` any (T.isInfixOf "contract=keiro-dsl/id-domain/typeid-v7/1")
-      ReplayImpact.replayImpactServices oldService newService `shouldSatisfy` \case
-        ReplayImpact.ReplayAffected impacts ->
-          maybe False includeSnapshotStreams (Map.lookup "OrderBook" impacts)
-        ReplayImpact.ReplayNeutral -> False
-
-    it "keeps the raw constructor outside the compiled public module surface" $
-      withTempDirectory "keiro-dsl-id-domain-hidden-constructor" $ \out -> do
-        sourceText <- readTestText "test/fixtures/id-domain-migration-v3.keiro"
-        parsed <- case parseSource "id-domain-migration-v3.keiro" sourceText of
-          Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-          Right value -> pure value
-        let service = checkedSource parsed
-            spec = checkedSpec service
-            ctx = defaultContext (specContext spec)
-            modules = scaffoldServiceModules ctx service
-            attempt = out </> "Attempt.hs"
-            ghcOutput = out </> ".ghc"
-        result <- executeServiceScaffold out False "id-domain-migration-v3.keiro" (parsedSourceLanguage parsed) ctx service modules
-        result `shouldSatisfy` isRight
-        recordContents <- TIO.readFile (out </> recordFileName (specContext spec))
-        record <- case parseRecord recordContents of
-          Nothing -> expectationFailure "generated ID-domain scaffold record did not parse" >> fail "unreachable"
-          Just value -> pure value
-        recIdDomains record `shouldBe` idDomainIdentitiesForService service
-        recNominalEqualities record
-          `shouldSatisfy` any (T.isInfixOf "keiro-dsl/id-domain/typeid-v7/1")
-        createDirectoryIfMissing True ghcOutput
-        TIO.writeFile
-          attempt
-          ( T.unlines
-              [ "module Attempt where",
-                "import Generated.IdDomainMigration.Nominals (OrderId (..))",
-                "bad :: OrderId",
-                "bad = OrderId \"ord_LEGACY-NOT-TYPEID\""
-              ]
-          )
-        (exitCode, standardOutput, standardError) <-
-          readProcessWithExitCode
-            "cabal"
-            [ "exec",
-              "--",
-              "ghc",
-              "-XGHC2024",
-              "-XOverloadedStrings",
-              "-fno-code",
-              "-fforce-recomp",
-              "-outputdir",
-              ghcOutput,
-              "-i" <> out,
-              attempt
-            ]
-            ""
-        exitCode `shouldSatisfy` (/= ExitSuccess)
-        (standardOutput <> standardError) `shouldContain` "OrderId"
-
-    it "emits one enforced nominal owner for a version-3 workspace" $ do
-      manifest <- readTestText "test/fixtures/workspace-nominals/service.keiro-workspace"
-      shared <- readTestText "test/fixtures/workspace-nominals/domain/shared.keiro"
-      project <- readTestText "test/fixtures/workspace-nominals/domain/project.keiro"
-      artifact <- readTestText "test/fixtures/workspace-nominals/domain/project-artifact.keiro"
-      let v3 = T.replace "language keiro-dsl 2" "language keiro-dsl 3"
-          source =
-            memoryContentSource
-              ( Map.fromList
-                  [ ("service.keiro-workspace", manifest),
-                    ("domain/shared.keiro", v3 shared),
-                    ("domain/project.keiro", v3 project),
-                    ("domain/project-artifact.keiro", v3 artifact)
-                  ]
-              )
-      loaded <- loadWorkspace source "service.keiro-workspace"
-      workspace <- case loaded of
-        Left failure -> expectationFailure (show failure) >> fail "unreachable"
-        Right value -> pure value
-      plan <- case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
-        Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
-        Right value -> pure value
-      let paths = map (modulePath . fst) (wpModules plan)
-      length (filter (== "Generated/WorkspaceNominalProof/Nominals.hs") paths) `shouldBe` 1
-      length (filter (== "Generated/WorkspaceNominalProof/Nominals/Internal.hs") paths) `shouldBe` 1
-      forM_ [moduleText value | (value, _) <- wpModules plan, "/Domain.hs" `T.isSuffixOf` T.pack (modulePath value)] $ \domainText ->
-        domainText `shouldSatisfy` (not . T.isInfixOf "ProjectId (..)")
-      withTempDirectory "keiro-dsl-v3-workspace-record" $ \out -> do
-        emitted <- executeWorkspaceScaffold out False plan
-        emitted `shouldSatisfy` isRight
-        recordContents <- TIO.readFile (out </> workspaceRecordFileName (wsService workspace))
-        record <- case parseWorkspaceRecord recordContents of
-          Nothing -> expectationFailure "version-3 workspace record did not parse" >> fail "unreachable"
-          Just value -> pure value
-        wrIdDomains record `shouldBe` idDomainIdentitiesForService (wpCheckedService plan)
-        wrNominalEqualities record
-          `shouldSatisfy` any (T.isInfixOf "keiro-dsl/id-domain/typeid-v7/1")
-
-    it "emits an abstract public ID, an internal legacy seam, and exact equality" $ do
-      v2Source <- readTestText "test/fixtures/aggregate-scalar-expressions-v2.keiro"
-      let v3Source = T.replace "language keiro-dsl 2" "language keiro-dsl 3" v2Source
-      parsed <- case parseSource "aggregate-scalar-expressions-v3.keiro" v3Source of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      let service = checkedSource parsed
-          spec = checkedSpec service
-          modules = scaffoldServiceModules (defaultContext (specContext spec)) service
-          moduleAt path = case [value | value <- modules, modulePath value == path] of
-            [value] -> pure value
-            values -> expectationFailure ("expected one module at " <> path <> ", got " <> show (map modulePath values)) >> fail "unreachable"
-      validateService service `shouldBe` []
-      publicNominals <- moduleAt "Generated/AggregateScalarExpressions/Nominals.hs"
-      internalNominals <- moduleAt "Generated/AggregateScalarExpressions/Nominals/Internal.hs"
-      domainModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs"
-      codecModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs"
-      expressionsModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Expressions.hs"
-      moduleText publicNominals `shouldSatisfy` T.isInfixOf "parseRequestId"
-      moduleText publicNominals `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection RequestIdEqualityProjection"
-      moduleText publicNominals `shouldSatisfy` T.isInfixOf "idDomainTextPattern (typeIdV7Domain \"req\")"
-      moduleText publicNominals `shouldSatisfy` (not . T.isInfixOf "unsafeRequestIdFromLegacyText")
-      moduleText publicNominals `shouldSatisfy` (not . T.isInfixOf "newtype RequestId")
-      moduleText internalNominals `shouldSatisfy` T.isInfixOf "newtype RequestId = RequestId Text"
-      moduleText internalNominals `shouldSatisfy` T.isInfixOf "unsafeRequestIdFromLegacyText"
-      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "RequestId (..)")
-      moduleText codecModule `shouldSatisfy` T.isInfixOf "unsafeRequestIdFromLegacyText <$>"
-      moduleText expressionsModule `shouldSatisfy` T.isInfixOf "case parseRequestId"
-      firewallBreaches modules `shouldBe` []
-
-  describe "scalar expressions" $ do
-    it "parses, validates, and round-trips the authoritative v2 scalar fixture" $ do
-      source <- readTestText "test/fixtures/aggregate-scalar-expressions-v2.keiro"
-      parsed <- case parseSource "aggregate-scalar-expressions-v2.keiro" source of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      validateSpec (parsedSpec parsed) `shouldBe` []
-      parseSource "round-trip.keiro" (renderSource parsed) `shouldBe` Right parsed
-      case [aggregate | NAggregate aggregate <- specNodes (parsedSpec parsed)] of
-        [aggregate] -> case aggTransitions aggregate of
-          transition : holeTransition : [] -> do
-            tImplementation transition `shouldBe` GeneratedImplementation
-            tImplementation holeTransition `shouldBe` HoleImplementation
-            let environment = expressionEnvironment (parsedSpec parsed) aggregate transition
-            case lookup "reserved" (tWrites transition) >>= either (const Nothing) Just . resolveWriteExpr environment "reserved" of
-              Just resolved -> do
-                typedScalarType resolved `shouldBe` AggregateNatural
-                show (typedScalarNode resolved) `shouldContain` "TotalNaturalArithmetic"
-              Nothing -> expectationFailure "reserved write did not resolve"
-            let modules = scaffoldAggregate (defaultContext (specContext (parsedSpec parsed))) (parsedSpec parsed) aggregate
-                expressions = generatedTextEndingIn "Expressions.hs" modules
-                transducer = generatedTextEndingIn "Transducer.hs" modules
-                holes = holeTextEndingIn "Holes.hs" modules
-            map modulePath modules `shouldSatisfy` any (T.isSuffixOf "Expressions.hs" . T.pack)
-            map modulePath modules `shouldSatisfy` any (T.isSuffixOf "Transducer.hs" . T.pack)
-            expressions `shouldSatisfy` T.isInfixOf "K.tsub (d.requested) (B.reg @\"capacity\")"
-            transducer `shouldSatisfy` T.isInfixOf "B.requireGuard (Expressions.transition1OpenAdjustGuard d)"
-            transducer `shouldSatisfy` T.isInfixOf "B.slot @\"balance\" =: Expressions.transition1OpenAdjustWriteBalance d"
-            transducer `shouldSatisfy` T.isInfixOf "scalarAccountPredicateVerifications"
-            transducer `shouldSatisfy` T.isInfixOf "S.verifyPredicate predicate"
-            transducer `shouldSatisfy` T.isInfixOf "B.emit wireAdjusted (AdjustedTermFields"
-            transducer `shouldSatisfy` T.isInfixOf "balance = d.balance"
-            transducer `shouldSatisfy` (not . T.isInfixOf "transition1OpenAdjustOutput1Adjusted")
-            holes `shouldSatisfy` (not . T.isInfixOf "transition1OpenAdjustOutput1Adjusted")
-            holes `shouldSatisfy` (not . T.isInfixOf "transition2ReviewedCloseOutput1ClosedEvent")
-            holes `shouldSatisfy` T.isInfixOf "transition2ReviewedCloseHoleFoldVersion"
-            holes `shouldSatisfy` (not . T.isInfixOf "scalarAccountTransducer")
-            firewallBreaches modules `shouldBe` []
-          _ -> expectationFailure "expected one generated and one Hole scalar transition"
-        _ -> expectationFailure "expected one scalar aggregate"
-
-    it "rejects cross-command fields(Command) output before scaffolding" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context output-command-mismatch",
-                "aggregate Account",
-                "  regs",
-                "  states Open Closed!",
-                "  command OpenAccount { accountId:Text }",
-                "  command CloseAccount { accountId:Text }",
-                "  event AccountOpened = fields(OpenAccount)",
-                "  Open -- CloseAccount --> emit AccountOpened ; goto Closed"
-              ]
-      spec <- parseInlineSpec "<output-command-mismatch>" source
-      errorCodes spec `shouldContain` [EventOutputCommandMismatch]
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        [aggregate] -> case aggTransitions aggregate of
-          [transition] ->
-            eventOutputMapping spec aggregate transition 1 "AccountOpened"
-              `shouldBe` Left (OutputCommandMismatch "OpenAccount" "CloseAccount" "AccountOpened")
-          _ -> expectationFailure "expected one transition"
-        _ -> expectationFailure "expected one aggregate"
-
-    it "rejects Int arithmetic and mixed numeric operands before scaffolding" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-errors",
-                "aggregate Counter",
-                "  regs",
-                "    machine Int = 0",
-                "    exact Integer = 0",
-                "  states Open Closed!",
-                "  command Add { machine:Int exact:Integer }",
-                "  event Added = fields(Add)",
-                "  Open -- Add -->",
-                "    guard cmd.machine + 1 >= 0 && cmd.exact == cmd.machine",
-                "    emit Added",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<scalar-errors>" source
-      errorCodes spec `shouldContain` [AggregateExpressionOperatorUnsupported, AggregateExpressionOperandTypeMismatch]
-
-    it "rejects nominal type confusion and unqualified enum values at source checking" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context nominal-type-confusion",
-                "id OrderId prefix=ord",
-                "id UserId prefix=usr",
-                "enum OrderStatus { Draft=draft Submitted=submitted }",
-                "enum UserStatus { Active=active Disabled=disabled }",
-                "aggregate Account",
-                "  regs",
-                "    orderId OrderId = placeholder",
-                "    status OrderStatus = Draft",
-                "  states Open Closed!",
-                "  command Compare { orderId:OrderId userId:UserId status:OrderStatus userStatus:UserStatus label:Text }",
-                "  event Compared = fields(Compare)",
-                "  Open -- Compare -->",
-                "    guard cmd.orderId == cmd.userId",
-                "      && cmd.status == cmd.userStatus",
-                "      && cmd.orderId == cmd.label",
-                "      && cmd.status == Draft",
-                "    emit Compared",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<nominal-type-confusion>" source
-      let diagnostics = validateSpec spec
-      length [() | diagnostic <- diagnostics, code diagnostic == AggregateExpressionOperandTypeMismatch]
-        `shouldBe` 3
-      errorCodes spec `shouldContain` [AggregateExpressionRootUnknown]
-      T.unlines (map message diagnostics) `shouldSatisfy` T.isInfixOf "qualify"
-
-    it "rejects machine-Int arithmetic at both platform bounds" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-int-bounds",
-                "aggregate Counter",
-                "  regs",
-                "    machine Int = 0",
-                "  states Open Closed!",
-                "  command Set { machine:Int }",
-                "  event SetEvent = fields(Set)",
-                "  Open -- Set -->",
-                "    guard cmd.machine + 1 >= " <> T.pack (show (minBound :: Int)),
-                "      && cmd.machine - 1 <= " <> T.pack (show (maxBound :: Int)),
-                "    emit SetEvent",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<scalar-int-bounds>" source
-      length [() | diagnostic <- validateSpec spec, code diagnostic == AggregateExpressionOperatorUnsupported]
-        `shouldBe` 2
-
-    it "rejects predicate-valued Bool writes that Keiki cannot represent as scalar terms" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-bool-write",
-                "aggregate Flag",
-                "  regs",
-                "    active Bool = False",
-                "  states Open Closed!",
-                "  command Set { active:Bool }",
-                "  event SetEvent = fields(Set)",
-                "  Open -- Set -->",
-                "    write active := cmd.active == true",
-                "    emit SetEvent",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<scalar-bool-write>" source
-      errorCodes spec `shouldContain` [AggregateExpressionOperatorUnsupported]
-
-    it "requires explicit roots for a same-named register and command field" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-ambiguity",
-                "aggregate Counter",
-                "  regs",
-                "    amount Integer = 0",
-                "  states Open Closed!",
-                "  command Set { amount:Integer }",
-                "  event SetEvent = fields(Set)",
-                "  Open -- Set -->",
-                "    guard amount == 0",
-                "    emit SetEvent",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<scalar-ambiguity>" source
-      errorCodes spec `shouldContain` [AggregateExpressionRootAmbiguous]
-
-    it "enforces exclusive Hole ownership and preserves its canonical spelling" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-hole",
-                "aggregate Counter",
-                "  regs",
-                "    amount Integer = 0",
-                "  states Open Closed!",
-                "  command Set { amount:Integer }",
-                "  event SetEvent = fields(Set)",
-                "  Open -- Set -->",
-                "    implementation hole",
-                "    guard cmd.amount >= 0",
-                "    emit SetEvent",
-                "    goto Closed"
-              ]
-      parsed <- case parseSource "<scalar-hole>" source of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      errorCodes (parsedSpec parsed) `shouldContain` [AggregateTransitionOwnershipConflict]
-      renderSource parsed `shouldSatisfy` T.isInfixOf "implementation hole"
-
-    it "generates a stable per-transition Hole boundary and fold token" $ do
-      let source =
-            T.unlines
-              [ "language keiro-dsl 2",
-                "context scalar-hole",
-                "aggregate Counter",
-                "  regs",
-                "    amount Integer = 0",
-                "  states Open Closed!",
-                "  command Set { amount:Integer }",
-                "  event SetEvent = fields(Set)",
-                "  Open -- Set -->",
-                "    implementation hole",
-                "    emit SetEvent",
-                "    goto Closed"
-              ]
-      spec <- parseInlineSpec "<scalar-hole-valid>" source
-      aggregate <- case [value | NAggregate value <- specNodes spec] of
-        [value] -> pure value
-        _ -> expectationFailure "expected one Hole aggregate" >> fail "unreachable"
-      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
-          transducer = generatedTextEndingIn "Transducer.hs" modules
-          holes = holeTextEndingIn "Holes.hs" modules
-      errorCodes spec `shouldBe` []
-      transducer `shouldSatisfy` T.isInfixOf "Holes.transition1OpenSetHole d"
-      transducer `shouldSatisfy` T.isInfixOf "foldToken Holes.transition1OpenSetHoleFoldVersion"
-      holes `shouldSatisfy` T.isInfixOf "transition1OpenSetHole _d = B.requireGuard K.PTop"
-      holes `shouldSatisfy` T.isInfixOf "transition1OpenSetHoleFoldVersion = FoldVersion"
-      holes `shouldSatisfy` (not . T.isInfixOf "counterTransducer")
-
-    it "pins v1 and collection rejection at their stable boundaries" $ do
-      v1Source <- readTestText "test/fixtures/aggregate-scalar-expressions-v1-rejects.keiro"
-      case parseSource "v1.keiro" v1Source of
-        Left (SourceLanguageFailure diagnostic) -> sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
-        other -> expectationFailure ("expected v1 source-language refusal, got " <> show other)
-      collectionSource <- readTestText "test/fixtures/aggregate-collection-expressions-v2-rejects.keiro"
-      case parseSource "collections.keiro" collectionSource of
-        Left failure -> renderParseFailure failure `shouldSatisfy` T.isInfixOf "CollectionExpressionUnsupported"
-        Right _ -> expectationFailure "collection syntax unexpectedly parsed"
-
-    it "keeps arithmetic operands intact when complementing a scalar comparison" $ do
-      let left = EAdd noLoc (EPath noLoc CommandRoot ["balance"]) (ELiteral noLoc (LiteralIntegral 2))
-          right = ESubtract noLoc (EPath noLoc RegisterRoot ["balance"]) (ELiteral noLoc (LiteralIntegral 3))
-          predicate = ECmp OpLt left right
-      complementExpr predicate `shouldBe` ECmp OpGe left right
-      complementExpr (complementExpr predicate) `shouldBe` predicate
-
-    it "keeps the committed scalar-expression conformance tree fresh" $ do
-      spec <- specOf "test/fixtures/aggregate-scalar-expressions-v2.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-      forM_ [generatedModule | generatedModule <- modules, kind generatedModule == Generated] $ \generatedModule -> do
-        committed <- readTestText ("test/conformance-scalar-expressions/" <> modulePath generatedModule)
-        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generatedModule)
-
-  describe "behavior obligations" $ do
-    it "inventories every live-reachable cell, guarded edge, terminal rejection, and replay edge" $ do
-      spec <- specOf "test/fixtures/behavior-complete.keiro"
-      requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
-      length requirements `shouldBe` 14
-      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.LiveTransition] `shouldBe` 5
-      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.RequiredRejection] `shouldBe` 8
-      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.ReplayTransition] `shouldBe` 1
-      [Behavior.requirementSource requirement | requirement <- requirements, Behavior.requirementKind requirement == Behavior.RequiredRejection]
-        `shouldContain` ["Active", "Closed"]
-      length [() | requirement <- requirements, Behavior.requirementGuardCoverage requirement == Behavior.GuardTotal] `shouldBe` 4
-      length [() | requirement <- requirements, Behavior.requirementGuardCoverage requirement == Behavior.GuardUnknown] `shouldBe` 1
-      let report = Behavior.BehaviorObligationsReport "behavior-complete.keiro" Nothing requirements
-          encoded = Behavior.encodeBehaviorObligationsJson report
-      encoded `shouldSatisfy` T.isInfixOf "\"schema\":\"keiro-dsl/behavior-obligations/1\""
-      encoded `shouldSatisfy` T.isInfixOf "\"source\":\"Closed\""
-      encoded `shouldSatisfy` T.isInfixOf "\"kind\":\"replay-transition\""
-      encoded `shouldSatisfy` (not . T.isInfixOf "\"filled\"")
-      encoded `shouldSatisfy` (not . T.isInfixOf "\"missing\"")
-
-    it "keeps semantic keys stable across line movement and canonical pretty printing" $ do
-      source <- readTestText "test/fixtures/behavior-complete.keiro"
-      parsed <- case parseSource "behavior-complete.keiro" source of
-        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
-        Right value -> pure value
-      let original = parsedSpec parsed
-      moved <- parseInlineSpec "behavior-complete-moved.keiro" ("# line movement must not rename witnesses\n\n" <> source)
-      pretty <- parseInlineSpec "behavior-complete-pretty.keiro" (renderSource parsed)
-      let keys spec = fmap (map Behavior.requirementKey) (Behavior.deriveBehaviorRequirements spec)
-      keys moved `shouldBe` keys original
-      keys pretty `shouldBe` keys original
-
-    it "generates direct fields(Command) output and separate create-once pending witnesses" $ do
-      spec <- specOf "test/fixtures/behavior-complete.keiro"
-      aggregate <- case [value | NAggregate value <- specNodes spec] of
-        [value] -> pure value
-        _ -> expectationFailure "expected one behavior-complete aggregate" >> fail "unreachable"
-      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
-          transducer = generatedTextEndingIn "Transducer.hs" modules
-          codec = generatedTextEndingIn "Codec.hs" modules
-          contract = generatedTextEndingIn "BehaviorContract.hs" modules
-          behaviorHoles = case [moduleText value | value <- modules, T.isSuffixOf "BehaviorHoles.hs" (T.pack (modulePath value))] of
-            [value] -> value
-            values -> error ("expected one BehaviorHoles module, got " <> show (length values))
-          ordinaryHoles = [value | value <- modules, T.isSuffixOf "/Holes.hs" (T.pack (modulePath value)), not (T.isSuffixOf "BehaviorHoles.hs" (T.pack (modulePath value)))]
-      transducer `shouldSatisfy` T.isInfixOf "requestId = d.requestId"
-      transducer `shouldSatisfy` T.isInfixOf "observedAt = d.observedAt"
-      transducer `shouldSatisfy` T.isInfixOf "amount = d.amount"
-      transducer `shouldSatisfy` T.isInfixOf "details = d.details"
-      codec `shouldSatisfy` T.isInfixOf "display_label"
-      codec `shouldSatisfy` T.isInfixOf "optional_note"
-      transducer `shouldSatisfy` (not . T.isInfixOf "Output")
-      ordinaryHoles `shouldBe` []
-      obsoleteGeneratedOutputHooks spec `shouldContain` [("Journey", "transition1EmptyStartOutput1Started")]
-      contract `shouldSatisfy` T.isInfixOf "keiro/behavior-conformance/1"
-      contract `shouldSatisfy` T.isInfixOf "commandKind command == requirementCommandName requirement"
-      T.count "Pending (BehaviorKey " behaviorHoles `shouldBe` 14
-      behaviorHoles `shouldSatisfy` (not . T.isInfixOf "undefined")
-      behaviorHoles `shouldSatisfy` (not . T.isInfixOf "error")
-
-    it "rejects eventless state or register changes while accepting a true no-op" $ do
-      invalid <-
-        parseInlineSpec "<eventless-change>" $
-          T.unlines
-            [ "language keiro-dsl 2",
-              "context eventless-change",
-              "aggregate Counter",
-              "  regs",
-              "    count Natural = 0",
-              "  states Open Closed!",
-              "  command Tick { count:Natural }",
-              "  Open -- Tick --> write count := cmd.count ; goto Closed"
-            ]
-      errorCodes invalid `shouldContain` [AggregateEventlessStateChange]
-      valid <-
-        parseInlineSpec "<eventless-noop>" $
-          T.unlines
-            [ "language keiro-dsl 2",
-              "context eventless-noop",
-              "aggregate Counter",
-              "  regs",
-              "    count Natural = 0",
-              "  states Open",
-              "  command Tick { count:Natural }",
-              "  Open -- Tick --> goto Open"
-            ]
-      errorCodes valid `shouldBe` []
-
-    it "refuses duplicate semantic behavior identities before scaffolding" $ do
-      duplicate <-
-        parseInlineSpec "<duplicate-behavior>" $
-          T.unlines
-            [ "language keiro-dsl 2",
-              "context duplicate-behavior",
-              "aggregate Counter",
-              "  regs",
-              "  states Open",
-              "  command Tick { amount:Natural }",
-              "  event Ticked = fields(Tick)",
-              "  Open -- Tick --> emit Ticked ; goto Open",
-              "  Open -- Tick --> emit Ticked ; goto Open"
-            ]
-      let isBehaviorRefusal (BehaviorRefusal _) = True
-          isBehaviorRefusal _ = False
-      case planScaffold (defaultContext (specContext duplicate)) duplicate of
-        Left refusals -> refusals `shouldSatisfy` any isBehaviorRefusal
-        Right _ -> expectationFailure "duplicate behavior identity reached a scaffold write set"
-
-    it "round-trips additive single-file and workspace behavior rows with member ownership" $ do
-      spec <- specOf "test/fixtures/behavior-complete.keiro"
-      requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
-      version <- maybe (expectationFailure "language version 2 was not constructible" >> fail "unreachable") pure (languageVersion 2)
-      let rows = Behavior.behaviorRecordRows requirements
-          singleRecord =
-            ScaffoldRecord
-              { recSpecPath = "behavior-complete.keiro",
-                recModuleRoot = "",
-                recLayout = "prefixed",
-                recSourceLanguage = DeclaredLanguage version noLoc,
-                recLanguageContract = effectiveLanguageContract (DeclaredLanguage version noLoc),
-                recFiles = [],
-                recMappings = [],
-                recIdDomains = [],
-                recNominalEqualities = [],
-                recBindingObligations = [],
-                recBehaviorRequirements = rows
-              }
-      T.count "behavior " (renderRecord singleRecord) `shouldBe` 14
-      parseRecord (renderRecord singleRecord) `shouldBe` Just singleRecord
-
-      workspace <- shouldComposeWorkspace "test/fixtures/behavior-complete-workspace/service.keiro-workspace"
-      workspaceRequirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements (wsMergedSpec workspace))
-      let ownedRequirements =
-            map
-              (Behavior.attributeBehaviorOwner (fmap fst . nodeOwner (wsOwnership workspace) "aggregate"))
-              workspaceRequirements
-          ownedRows = Behavior.behaviorRecordRows ownedRequirements
-          workspaceRecord =
-            WorkspaceRecord
-              { wrService = wsService workspace,
-                wrManifest = "service.keiro-workspace",
-                wrContext = wsContext workspace,
-                wrModuleRoot = "",
-                wrLayout = "prefixed",
-                wrMembers = map wmPath (wsMembers workspace),
-                wrSourceLanguages = [WorkspaceSourceLanguageRow (wmPath member) (wmSourceLanguage member) | member <- wsMembers workspace],
-                wrLanguageContract = wsLanguageContract workspace,
-                wrModules = [],
-                wrMappings = [],
-                wrIdDomains = [],
-                wrNominalEqualities = [],
-                wrBindingObligations = [],
-                wrBehaviorRequirements = ownedRows,
-                wrAdopted = []
-              }
-      map Behavior.behaviorRecordOwner ownedRows `shouldSatisfy` all (== Just "journey.keiro")
-      T.count "behavior " (renderWorkspaceRecord workspaceRecord) `shouldBe` 14
-      parseWorkspaceRecord (renderWorkspaceRecord workspaceRecord) `shouldBe` Just workspaceRecord
-
-  describe "nominal consumer types" $ do
-    it "resolves every category through one checked registry and explains exact obligations" $ do
-      spec <- specOf "test/fixtures/nominal-scalars.keiro"
-      errorCodes spec `shouldBe` []
-      registry <- case resolveNominalTypes spec of
-        Left errors -> expectationFailure (show errors) >> fail "unreachable"
-        Right value -> pure value
-      Map.keys (nominalTypes registry)
-        `shouldBe` ["AccountNumber", "FeatureFlag", "ObservedAt", "OrderId", "OrderStatus", "RiskScore", "SequenceNumber"]
-      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
-      length obligations `shouldBe` 21
-      map obligationCategory obligations `shouldSatisfy` all (`elem` ["nominal-id", "nominal-enum", "nominal-scalar"])
-      length [() | obligation <- obligations, obligationEqualityContract obligation /= Nothing] `shouldBe` 2
-      renderBindingObligations (specContext spec) obligations `shouldSatisfy` T.isInfixOf "equality-contract:"
-      let signatures = map obligationSignature obligations
-      forM_
-        [ "orderIdBinding :: NominalBinding NominalConformance.Domain.OrderId (KindID \"ord\")",
-          "orderStatusBinding :: NominalBinding NominalConformance.Domain.OrderStatus Generated.NominalScalars.Nominal.Shape.OrderStatus.OrderStatusRepresentation",
-          "accountNumberBinding :: NominalBinding NominalConformance.Domain.AccountNumber Text",
-          "orderIdFixtures :: NominalFixtureCases NominalConformance.Domain.OrderId",
-          "initialAccountNumber :: NominalConformance.Domain.AccountNumber"
-        ]
-        (`shouldSatisfy` (`elem` signatures))
-      map obligationCanonicalType obligations `shouldSatisfy` all (/= Nothing)
-      let rendered = renderBindingObligations (specContext spec) obligations
-      rendered `shouldSatisfy` T.isInfixOf "nominal-id type OrderId"
-      rendered `shouldSatisfy` T.isInfixOf "canonical-type: \"nominal.OrderId.v1\""
-      case obligations of
-        firstObligation : _ ->
-          (Aeson.eitherDecode (Aeson.encode firstObligation) :: Either String BindingObligation)
-            `shouldBe` Right firstObligation
-        [] -> expectationFailure "expected nominal binding obligations"
-
-    it "allocates distinct stable diagnostics for incomplete or incompatible nominal declarations" $ do
-      missing <- errorCodesOf "test/fixtures/nominal-missing-facts.keiro"
-      missing `shouldBe` replicate 5 NominalMissingIngredient
-      errorCodesOf "test/fixtures/nominal-bad-qualified.keiro" `shouldReturn` [NominalInvalidQualifiedName]
-      errorCodesOf "test/fixtures/nominal-invalid-prefix.keiro" `shouldReturn` [NominalInvalidIdPrefix]
-      errorCodesOf "test/fixtures/nominal-unsupported-representation.keiro" `shouldReturn` [NominalUnsupportedRepresentation]
-      errorCodesOf "test/fixtures/nominal-missing-initial.keiro" `shouldReturn` [NominalMissingInitialValue]
-      errorCodesOf "test/fixtures/nominal-name-collision.keiro" `shouldReturn` [NominalNameCollision, NominalNameCollision]
-
-    it "keeps v1 rejection at the source-language boundary" $ do
-      source <- readTestText "test/fixtures/nominal-v1.keiro"
-      case parseSource "nominal-v1.keiro" source of
-        Left (SourceLanguageFailure diagnostic) -> sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
-        other -> expectationFailure ("expected source-language refusal, got " <> show other)
-
-    it "scaffolds consumer types, checked codecs, enum representation, projections, and deterministic manifests" $ do
-      spec <- specOf "test/fixtures/nominal-scalars.keiro"
-      let ctx = defaultContext (specContext spec)
-          modules = scaffoldModules ctx spec
-          moduleAt path = case [value | value <- modules, modulePath value == path] of
-            [value] -> pure value
-            values -> expectationFailure ("expected one module at " <> path <> ", got " <> show (map modulePath values)) >> fail "unreachable"
-      domainModule <- moduleAt "Generated/NominalScalars/NominalLedger/Domain.hs"
-      codecModule <- moduleAt "Generated/NominalScalars/NominalLedger/Codec.hs"
-      enumModule <- moduleAt "Generated/NominalScalars/Nominal/Shape/OrderStatus.hs"
-      projectionModule <- moduleAt "Generated/NominalScalars/NominalProjections.hs"
-      bindingModule <- moduleAt "NominalConformance/Bindings.hs"
-      map modulePath modules `shouldNotContain` ["NominalScalars/NominalLedger/Holes.hs"]
-      moduleText domainModule `shouldSatisfy` T.isInfixOf "NominalConformance.Domain.OrderId"
-      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "newtype OrderId")
-      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "data OrderStatus =")
-      moduleText codecModule `shouldSatisfy` T.isInfixOf "KindID.parseText @\"ord\""
-      moduleText codecModule `shouldSatisfy` T.isInfixOf "KindID.toText (nominalToRepresentation"
-      moduleText codecModule `shouldSatisfy` T.isInfixOf "nominalFromRepresentation"
-      forM_ ["coerce", "unsafe", "read ", "error "] $ \forbidden ->
-        moduleText codecModule `shouldSatisfy` (not . T.isInfixOf forbidden)
-      moduleText enumModule `shouldSatisfy` T.isInfixOf "data OrderStatusRepresentation = Draft | Submitted"
-      moduleText enumModule `shouldSatisfy` (not . T.isInfixOf "NominalConformance")
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "type FieldOwner AccountNumberNominalProjection = NominalConformance.Domain.AccountNumber"
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "projectFieldValue _ = nominalToRepresentation NominalConformance.Bindings.accountNumberBinding"
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection OrderIdEqualityProjection"
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "textProjectionDomain orderIdEqualityPattern"
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection OrderStatusEqualityProjection"
-      moduleText projectionModule `shouldSatisfy` T.isInfixOf "finiteProjectionDomain (\"draft\" :| [\"submitted\"])"
-      kind bindingModule `shouldBe` HoleStub
-      moduleText bindingModule `shouldSatisfy` T.isInfixOf "orderIdBinding :: NominalBinding NominalConformance.Domain.OrderId (KindID \"ord\")"
-      firewallBreaches modules `shouldBe` []
-      scaffoldModules ctx spec `shouldBe` modules
-      manifestDependencies spec `shouldContain` ["mmzk-typeid", "nominal-conformance"]
-
-    it "persists nominal provenance in a separate forward-compatible row kind" $ do
-      spec <- specOf "test/fixtures/nominal-scalars.keiro"
-      workspace <- shouldComposeWorkspace canonicalWorkspacePath
-      let plan = consumerPlan spec
-          record =
-            ScaffoldRecord
-              { recSpecPath = "nominal-scalars.keiro",
-                recModuleRoot = "",
-                recLayout = "prefixed",
-                recSourceLanguage = LegacyUnversioned,
-                recLanguageContract = effectiveLanguageContract LegacyUnversioned,
-                recFiles = [],
-                recMappings = consumerMappings plan,
-                recIdDomains = [],
-                recNominalEqualities = nominalEqualityIdentities spec,
-                recBindingObligations = [],
-                recBehaviorRequirements = []
-              }
-          encoded = renderRecord record
-          workspaceRecord =
-            (sampleWorkspaceRecord workspace)
-              { wrMappings = consumerMappings plan
-              }
-          workspaceEncoded = renderWorkspaceRecord workspaceRecord
-      consumerPackages plan `shouldBe` ["nominal-conformance"]
-      length [() | NominalMapping {} <- consumerMappings plan] `shouldBe` 7
-      T.count "nominal-mapping " encoded `shouldBe` 7
-      T.count "nominal-equality " encoded `shouldBe` 2
-      T.count "\nmapping " encoded `shouldBe` 0
-      parseRecord encoded `shouldBe` Just record
-      T.count "nominal-mapping " workspaceEncoded `shouldBe` 7
-      T.count "nominal-equality " workspaceEncoded `shouldSatisfy` (>= 2)
-      T.count "\nmapping " workspaceEncoded `shouldBe` 0
-      parseWorkspaceRecord workspaceEncoded `shouldBe` Just workspaceRecord
-
-    it "reports bound-ID decoder tightening and makes binding provenance replay-visible" $ do
-      current <- specOf "test/fixtures/nominal-scalars.keiro"
-      let unbound = current {specIds = [declaration {idBinding = Nothing} | declaration <- specIds current]}
-          adoption = diffSpecs unbound current
-          decoderFindings = [kindOfChange change | change <- adoption, changeCode change == NominalIdDecoderTightened]
-      map ckSubject decoderFindings `shouldContain` ["NominalLedger event NominalsRecorded .orderId"]
-      decoderFindings `shouldSatisfy` all ((== VAdvisory) . verdictFor PrivateHistoryRead . ckVector)
-      let bumped =
-            current
-              { specIds =
-                  [ declaration
-                      { idBinding = fmap (\binding -> binding {nominalBindingVersion = Just "2"}) (idBinding declaration)
-                      }
-                  | declaration <- specIds current
-                  ]
-              }
-          bindingChanges = diffSpecs current bumped
-      map changeCode bindingChanges `shouldContain` [NominalBindingChanged]
-      ReplayImpact.replayImpact current bumped `shouldSatisfy` \case
-        ReplayImpact.ReplayAffected impacts ->
-          maybe False (\impact -> Set.member "NominalsRecorded" (ReplayImpact.eventTypes impact) && includeSnapshotStreams impact) (Map.lookup "NominalLedger" impacts)
-        ReplayImpact.ReplayNeutral -> False
-      case [aggregate | NAggregate aggregate <- specNodes current] of
-        aggregate : _ -> do
-          aggregateFoldSurface current aggregate `shouldSatisfy` T.isInfixOf "nominal-equality-use:"
-          aggregateFoldSurface current aggregate `shouldNotBe` aggregateFoldSurface bumped aggregate
-        [] -> expectationFailure "expected nominal aggregate"
-
-  describe "historical codec comparison" $ do
-    it "treats object-key order as RFC 8785 parity" $ do
-      let historical = object ["z" .= (1 :: Int), "a" .= (2 :: Int)]
-          generated = object ["a" .= (2 :: Int), "z" .= (1 :: Int)]
-      classifyObservation (EncodeObservation "ordered-object" historical generated)
-        `shouldBe` Right JsonParity
-    it "classifies an omitted key versus explicit null as version work at that pointer" $ do
-      let historical = object []
-          generated = object ["description" .= Aeson.Null]
-      classifyObservation (EncodeObservation "absent-description" historical generated)
-        `shouldBe` Right (RequiresVersionWork (EncodedValueDifference (JsonPointer "/description") historical generated))
-    it "classifies generated rejection of a historical value as version work" $
-      classifyObservation
-        ( DecodeObservation
-            "legacy.json"
-            (object ["tag" .= ("legacy" :: T.Text)])
-            (DecodedShape (object ["tag" .= ("legacy" :: T.Text)]))
-            (DecodeFailed "unknown tag")
-        )
-        `shouldBe` Right (RequiresVersionWork (GeneratedDecodeRejected "unknown tag"))
-    it "treats historical-codec rejection as invalid input rather than parity" $
-      classifyObservation
-        ( DecodeObservation
-            "corrupt.json"
-            Aeson.Null
-            (DecodeFailed "not historical data")
-            (DecodeFailed "not generated data")
-        )
-        `shouldBe` Left (HistoricalCodecRejected "corrupt.json" "not historical data")
-    it "reports uncovered union arms separately by corpus origin" $ do
-      let canonical = DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
-          local = DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local_file")
-          report = compareReport comparisonProvenance [] [] [canonical, local] [ObservedBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local_file")]
-      crCoverageGaps report
-        `shouldBe` [CoverageGap HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")]
-      reportSucceeded report `shouldBe` False
-    it "derives optional, null, and union-arm observations from a generated branch schema" $ do
-      let schema =
-            BranchRecord
-              [ BranchField "description" True (BranchOptional BranchScalar),
-                BranchField "location" False (BranchUnion "tag" "contents" [BranchArm "local" (Just BranchScalar), BranchArm "canonical" Nothing])
-              ]
-          historical = object ["location" .= object ["tag" .= ("canonical" :: T.Text)]]
-      observedBranchesFor HistoricalGolden schema historical
-        `shouldBe` [ ObservedBranch HistoricalGolden (JsonPointer "/description") OptionalMissing,
-                     ObservedBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
-                   ]
-      let declared = declaredBranchesFor HistoricalGolden schema
-      forM_
-        [ DeclaredBranch HistoricalGolden (JsonPointer "/description") OptionalMissing,
-          DeclaredBranch HistoricalGolden (JsonPointer "/description") OptionalPresent,
-          DeclaredBranch HistoricalGolden (JsonPointer "/description") ExplicitNull,
-          DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local"),
-          DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
-        ]
-        (\branch -> declared `shouldContain` [branch])
-    it "round-trips the stable machine report" $ do
-      let observation = EncodeObservation "parity" (object ["a" .= (1 :: Int)]) (object ["a" .= (1 :: Int)])
-          report = compareReport comparisonProvenance [] [observation] [] []
-      Aeson.eitherDecode (Aeson.encode report) `shouldBe` Right report
-    it "atomically writes and replaces the machine report" $
-      withTempDirectory "keiro-dsl-codec-compare" $ \out -> do
-        let path = out </> "report.json"
-            firstReport = compareReport comparisonProvenance [] [] [] []
-            secondReport = compareReport comparisonProvenance [HistoricalGoldenUnreadable "bad.json" "bad JSON"] [] [] []
-        writeCompareReportAtomic path firstReport `shouldReturn` Right ()
-        Aeson.eitherDecodeFileStrict path `shouldReturn` Right firstReport
-        writeCompareReportAtomic path secondReport `shouldReturn` Right ()
-        Aeson.eitherDecodeFileStrict path `shouldReturn` Right secondReport
-
-  describe "historical codec comparison scaffold" $ do
-    it "emits an opt-in non-production runner without entering the ordinary module registry" $ do
-      spec <- specOf "test/fixtures/structural-conformance.keiro"
-      let ctx = defaultContext (specContext spec)
-          planned = codecComparisonModule ctx spec "ArtifactInfo"
-          ordinary = scaffoldModules ctx spec
-      case planned of
-        Left err -> expectationFailure (T.unpack err)
-        Right comparisonModule -> do
-          modulePath comparisonModule
-            `shouldBe` "Generated/StructuralConformance/Structural/CodecCompare/ArtifactInfo.hs"
-          moduleText comparisonModule `shouldSatisfy` T.isInfixOf codecComparisonBanner
-          moduleText comparisonModule `shouldSatisfy` T.isInfixOf "Generated.StructuralConformance.ArtifactCatalog.Codec qualified as GeneratedCodec"
-          moduleText comparisonModule `shouldSatisfy` T.isInfixOf "branchSchema = BranchRecord"
-          map modulePath ordinary `shouldNotContain` [modulePath comparisonModule]
-    it "refuses opaque selections rather than upgrading their claim" $ do
-      spec <- specOf "test/fixtures/structural-conformance.keiro"
-      codecComparisonModule (defaultContext (specContext spec)) spec "VendorGeometry"
-        `shouldSatisfy` either (T.isInfixOf "is opaque") (const False)
-
-  describe "structural/opaque coverage reporting" $ do
-    it "reports mapped private-event roots and consumer-json register boundaries without a percentage" $ do
-      spec <- specOf "test/fixtures/structural-conformance.keiro"
-      report <- shouldResolveCoverage "structural-conformance.keiro" spec
-      Coverage.privateEventPayloads (Coverage.coverageSummary report)
-        `shouldBe` Coverage.CoverageCounts 2 1 1 0
-      Coverage.snapshotRegisters (Coverage.coverageSummary report)
-        `shouldBe` Coverage.CoverageCounts 2 1 1 0
-      map Coverage.opaqueMappedType (Coverage.coverageOpaqueBoundaries report)
-        `shouldBe` ["VendorGeometry"]
-      map Coverage.snapshotEncoding (Coverage.coverageSnapshotBoundaries report)
-        `shouldBe` ["consumer-json-cache", "consumer-json-cache"]
-      map Coverage.snapshotInvalidation (Coverage.coverageSnapshotBoundaries report)
-        `shouldBe` ["tracked-by-mapped-wire-fingerprint", "tracked-by-mapped-wire-fingerprint"]
-      map Coverage.findingCode (Coverage.coverageFindings report)
-        `shouldBe` [CoverageOpaqueSurface]
-      map Coverage.findingSeverity (Coverage.coverageFindings report)
-        `shouldBe` [Warning]
-      case Aeson.toJSON report of
-        Aeson.Object values ->
-          forM_ ["spec", "roots", "opaqueBoundaries", "snapshotBoundaries", "unsupportedSurfaces"] $
-            \key -> KeyMap.member key values `shouldBe` True
-        value -> expectationFailure ("coverage report was not an object: " <> show value)
-    it "reports explicit Json leaves by their complete persisted path" $ do
-      spec <- withMetadataJson <$> specOf "test/fixtures/structural-conformance.keiro"
-      report <- shouldResolveCoverage "structural-conformance-json.keiro" spec
-      Coverage.jsonBoundaries (Coverage.privateEventPayloads (Coverage.coverageSummary report))
-        `shouldBe` 1
-      map Coverage.jsonPath (Coverage.coverageJsonBoundaries report)
-        `shouldBe` ["ArtifactCatalog event ArtifactRecorded .artifact : ArtifactInfo .metadata : ArtifactMetadata .note"]
-    it "keeps a zero-opaque spec advisory-free and makes rejection explicitly opt-in" $ do
-      original <- specOf "test/fixtures/structural-conformance.keiro"
-      clear <- shouldResolveCoverage "structural-only.keiro" (withoutVendorGeometry original)
-      Coverage.opaqueRoots (Coverage.privateEventPayloads (Coverage.coverageSummary clear)) `shouldBe` 0
-      Coverage.coverageOpaqueBoundaries clear `shouldBe` []
-      Coverage.coverageFindings clear `shouldBe` []
-      opaque <- shouldResolveCoverage "structural-conformance.keiro" original
-      Coverage.coverageSucceeded opaque `shouldBe` True
-      let gated = Coverage.failOnOpaque opaque
-      Coverage.coverageSucceeded gated `shouldBe` False
-      map Coverage.findingCode (Coverage.coverageFindings gated)
-        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueGateExceeded]
-      map Coverage.findingSeverity (Coverage.coverageFindings gated)
-        `shouldBe` [Warning, Error]
-    it "diffs named opaque boundaries and fails only an explicitly gated increase" $ do
-      newSpec <- specOf "test/fixtures/structural-conformance.keiro"
-      report <- case Coverage.coverageDiffReport "structural-conformance.keiro" "HEAD" (withoutVendorGeometry newSpec) newSpec of
-        Left err -> expectationFailure (show err) >> fail "unreachable"
-        Right value -> pure value
-      fmap Coverage.opaqueBoundaryDelta (Coverage.coverageDelta report) `shouldBe` Just 1
-      fmap (map Coverage.opaqueMappedType . Coverage.addedOpaqueBoundaries) (Coverage.coverageDelta report)
-        `shouldBe` Just ["VendorGeometry"]
-      map Coverage.findingCode (Coverage.coverageFindings report)
-        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueBoundaryAdded]
-      Coverage.coverageSucceeded report `shouldBe` True
-      let gated = Coverage.failOnOpaqueIncrease report
-      Coverage.coverageSucceeded gated `shouldBe` False
-      map Coverage.findingCode (Coverage.coverageFindings gated)
-        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueBoundaryAdded, CoverageOpaqueGateExceeded]
-    it "appends the six stable coverage and comparison registry codes" $
-      map
-        show
-        [ CoverageOpaqueSurface,
-          CoverageOpaqueBoundaryAdded,
-          CoverageOpaqueGateExceeded,
-          CodecCompareDifference,
-          CodecCompareCoverageGap,
-          CodecCompareInvalidInput
-        ]
-        `shouldBe` [ "CoverageOpaqueSurface",
-                     "CoverageOpaqueBoundaryAdded",
-                     "CoverageOpaqueGateExceeded",
-                     "CodecCompareDifference",
-                     "CodecCompareCoverageGap",
-                     "CodecCompareInvalidInput"
-                   ]
-
-  describe "parse . pretty round-trip" $
-    do
-      it "re-parses any generated spec to an equal AST (modulo source locations)" $
-        checkCoverage $
-          forAll genSpec $ \s ->
-            let families = map nodeTag (specNodes s)
-                roundTrip = parseSpec "<gen>" (renderSpec s) === Right s
-             in cover 5 (not (null (specMapped s))) "mapped" $
-                  foldr (\family -> cover 1 (family `elem` families) family) roundTrip allNodeTags
-      it "round-trips an aggregate with no states" $
-        parseSpec "<empty-states>" (renderSpec emptyStatesSpec) `shouldBe` Right emptyStatesSpec
-      it "separates transition emit clauses from following nodes" $ do
-        spec <- parseInlineSpec "<cross-family-boundaries>" crossFamilyBoundarySpec
-        case specNodes spec of
-          [NAggregate first, NEmit _, NAggregate second, NPgmqDispatch _] -> do
-            concatMap tEmits (aggTransitions first) `shouldBe` ["Changed"]
-            aggStates second `shouldBe` []
-          nodes -> expectationFailure ("unexpected node sequence: " <> show (map nodeTag nodes))
-
-  describe "mapped types (EP-149)" $ do
-    it "round-trips the canonical structural and opaque consumer fixture" $ do
-      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
-      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
-      parseSpec "<consumer-types-round-trip>" (renderSpec spec) `shouldBe` Right spec
-      length (specMapped spec) `shouldBe` 4
-    it "preserves every missing-value policy, nested type expression, and unit union arm" $ do
-      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
-      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
-      let fields = [field | MappedStructural {msShape = ShapeRecord _ _ recordFields} <- specMapped spec, field <- recordFields]
-          arms = [arm | MappedStructural {msShape = ShapeUnion _ unionArms} <- specMapped spec, arm <- unionArms]
-      [value | field <- fields, Just value <- [wfOnMissing field]]
-        `shouldBe` [OmCtor "Guide", OmNull, OmInt 0, OmBool False, OmEmptyList, OmEmptyMap]
-      [wfType field | field <- fields, wfHaskell field == "labels"]
-        `shouldBe` [TList (TOptional TText)]
-      [waCtor arm | arm <- arms, waPayload arm == Nothing]
-        `shouldBe` ["Unknown"]
-    it "rejects every mapped validation fixture with its stable diagnostic code" $ do
-      let cases =
-            [ ("mapped-unresolved.keiro", MappedUnresolvedName),
-              ("mapped-ambiguous.keiro", MappedAmbiguousName),
-              ("mapped-dup-fieldname.keiro", MappedDuplicateFieldName),
-              ("mapped-dup-wirekey.keiro", MappedDuplicateWireKey),
-              ("mapped-dup-armname.keiro", MappedDuplicateArmName),
-              ("mapped-dup-tag.keiro", MappedDuplicateWireTag),
-              ("mapped-recursive.keiro", MappedRecursiveType),
-              ("mapped-recursive-mutual.keiro", MappedRecursiveType),
-              ("mapped-bad-encoding.keiro", MappedUnsupportedEncoding),
-              ("mapped-union-key-collision.keiro", MappedUnsupportedEncoding),
-              ("mapped-optional-json.keiro", MappedNonInjectiveNullability),
-              ("mapped-optional-optional.keiro", MappedNonInjectiveNullability),
-              ("mapped-optional-opaque.keiro", MappedNonInjectiveNullability),
-              ("mapped-missing-binding.keiro", MappedMissingIngredient),
-              ("mapped-missing-binding-version.keiro", MappedMissingIngredient),
-              ("mapped-missing-canonical.keiro", MappedMissingIngredient),
-              ("mapped-missing-fixture.keiro", MappedMissingIngredient),
-              ("mapped-missing-initial.keiro", MappedMissingInitialValue),
-              ("mapped-bad-haskell-name.keiro", MappedInvalidHaskellName),
-              ("mapped-empty-identity.keiro", MappedInvalidIdentity),
-              ("mapped-import-conflict.keiro", MappedImportConflict),
-              ("mapped-illtyped-default.keiro", MappedDefaultIllTyped),
-              ("mapped-guard.keiro", AggregateGuardCapabilityUnsupported)
-            ]
-      forM_ cases $ \(fixture, expected) ->
-        errorCodesOf ("test/fixtures/" <> fixture) `shouldReturn` [expected]
-    it "keeps Time and Natural in Keiki's curated comparison set" $ do
-      errorCodesOf "test/fixtures/mapped-guard-time.keiro" `shouldReturn` []
-      errorCodesOf "test/fixtures/mapped-guard-natural.keiro" `shouldReturn` []
-    it "rejects required defaults, missing optional policies, Int overflow, and negative Natural defaults" $ do
-      let invalidFields =
-            [ WireField "requiredDefault" "requiredDefault" TText PRequired (Just (OmText "x")) noLoc,
-              WireField "missingPolicy" "missingPolicy" TText POptional Nothing noLoc,
-              WireField "overflow" "overflow" TInt POptional (Just (OmInt (toInteger (maxBound :: Int) + 1))) noLoc,
-              WireField "negativeNatural" "negativeNatural" TNatural POptional (Just (OmInt (-1))) noLoc
-            ]
-          declaration = completeStructural "Defaults" (ShapeRecord "Defaults" RejectUnknown invalidFields)
-      errorCodes (mappedSpec [declaration])
-        `shouldBe` [MappedDefaultIllTyped, MappedMissingIngredient, MappedDefaultIllTyped, MappedDefaultIllTyped]
-
-  describe "aggregate type capabilities" $ do
-    it "enumerates the policy for every resolved type and use site" $ do
-      let resolvedTypes =
-            [ AggregateText,
-              AggregateInt,
-              AggregateBool,
-              AggregateTime,
-              AggregateNatural,
-              AggregateNominal (ResolvedNominalType "EntityId" (IdRepresentation "ent") GeneratedNominal noLoc),
-              AggregateNominal (ResolvedNominalType "Status" (EnumRepresentation (("Active", "active") :| [])) GeneratedNominal noLoc),
-              AggregateNominal (ResolvedNominalType "Amount" (ScalarRepresentation NominalInt) (consumerNominalFor "Amount") noLoc),
-              AggregateNominal (ResolvedNominalType "Label" (ScalarRepresentation NominalText) (consumerNominalFor "Label") noLoc),
-              AggregateVertex "EntityVertex",
-              AggregateMapped (MappedKey "ConsumerValue")
-            ]
-          useSites = [minBound .. maxBound]
-          expected useSite resolvedType = case useSite of
-            OrderingGuardUse -> case resolvedType of
-              AggregateInt -> SolverVisible
-              AggregateTime -> SolverVisible
-              AggregateNatural -> SolverVisible
-              AggregateNominal nominal -> case resolvedNominalRepresentation nominal of
-                ScalarRepresentation NominalInt -> SolverVisible
-                ScalarRepresentation NominalNatural -> SolverVisible
-                ScalarRepresentation NominalTime -> SolverVisible
-                _ -> Unsupported
-              _ -> Unsupported
-            EqualityGuardUse -> case resolvedType of
-              AggregateMapped {} -> Unsupported
-              AggregateNominal {} -> SolverVisible
-              AggregateVertex {} -> OpaqueOnly
-              _ -> SolverVisible
-            _ -> case resolvedType of
-              AggregateNominal nominal -> case resolvedNominalRepresentation nominal of
-                ScalarRepresentation {} -> SolverVisible
-                _ -> OpaqueOnly
-              AggregateVertex {} -> OpaqueOnly
-              AggregateMapped {} -> OpaqueOnly
-              _ -> SolverVisible
-          actual =
-            [ (useSite, resolvedType, aggregateCapability useSite resolvedType)
-            | useSite <- useSites,
-              resolvedType <- resolvedTypes
-            ]
-          wanted =
-            [ (useSite, resolvedType, expected useSite resolvedType)
-            | useSite <- useSites,
-              resolvedType <- resolvedTypes
-            ]
-      actual `shouldBe` wanted
-    it "lowers direct Time and Natural through every generated aggregate boundary" $ do
-      spec <- specOf "test/fixtures/aggregate-scalars.keiro"
-      errorCodes spec `shouldBe` []
-      let aggregate = onlyAggregate spec
-          generated =
-            [ moduleText generatedModule
-            | generatedModule <- scaffoldAggregate (defaultContext (specContext spec)) spec aggregate,
-              Keiro.Dsl.Scaffold.kind generatedModule == Generated
-            ]
-          domain = generatedTextEndingIn "Domain.hs" (scaffoldAggregate (defaultContext (specContext spec)) spec aggregate)
-      domain `shouldSatisfy` T.isInfixOf "observedAt :: !UTCTime"
-      domain `shouldSatisfy` T.isInfixOf "revision :: !Natural"
-      domain `shouldSatisfy` T.isInfixOf "UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)"
-      domain `shouldSatisfy` T.isInfixOf "import Data.Time.Calendar (fromGregorian)"
-      domain `shouldSatisfy` T.isInfixOf "import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)"
-      domain `shouldSatisfy` T.isInfixOf "import Numeric.Natural (Natural)"
-      manifestDependencies spec `shouldContain` ["time"]
-      manifestDependencies spec `shouldNotContain` ["keiki-codec-json"]
-      generated `shouldSatisfy` all (not . T.isInfixOf "error")
-      generated `shouldSatisfy` all (not . T.isInfixOf "getCurrentTime")
-      generated `shouldSatisfy` all (not . T.isInfixOf "iso8601ParseM")
-    it "canonicalizes Time and UTCTime across pretty, diff, and fold identity" $ do
-      source <- readTestText "test/fixtures/aggregate-scalars.keiro"
-      canonical <- parseInlineSpec "<time>" source
-      alias <- parseInlineSpec "<utctime>" (T.replace ":Time" ":UTCTime" (T.replace " Time =" " UTCTime =" source))
-      renderSpec alias `shouldBe` renderSpec canonical
-      diffSpecs canonical alias `shouldBe` []
-      aggregateFoldFingerprint canonical (onlyAggregate canonical)
-        `shouldBe` aggregateFoldFingerprint alias (onlyAggregate alias)
-      aggregateFoldSurface canonical (onlyAggregate canonical)
-        `shouldBe` aggregateFoldSurface alias (onlyAggregate alias)
-    it "keeps the committed scalar conformance generated tree fresh" $ do
-      spec <- specOf "test/fixtures/aggregate-scalars.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-      forM_ [generatedModule | generatedModule <- modules, Keiro.Dsl.Scaffold.kind generatedModule == Generated] $ \generatedModule -> do
-        committed <- readTestText ("test/conformance-aggregate-scalars/" <> modulePath generatedModule)
-        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generatedModule)
-    it "never sends a clean scalar aggregate to a type scaffold refusal" $
-      property $
-        forAll (elements scalarRegisterCases) $ \(typeName, initialValue) ->
-          case parseSpec "<clean-scalar>" (cleanScalarAggregateSpec typeName initialValue) of
-            Left parseError -> counterexample (T.unpack parseError) False
-            Right spec ->
-              let diagnostics = [diagnostic | diagnostic <- validateSpec spec, severity diagnostic == Error]
-                  modules = scaffoldModules (defaultContext (specContext spec)) spec
-               in counterexample
-                    (show diagnostics <> "\n" <> show (scaffoldRefusals spec))
-                    ( null diagnostics
-                        && null (scaffoldRefusals spec)
-                        && all (not . T.null . moduleText) modules
-                    )
-
-  describe "aggregate scalar diagnostics" $ do
-    it "reports unsupported shapes, invalid initials, and mismatched guards at stable lines" $ do
-      diagnostics <- diagnosticsOf "test/fixtures/aggregate-scalars-unsupported.keiro"
-      [(code diagnostic, line diagnostic) | diagnostic <- diagnostics, severity diagnostic == Error]
-        `shouldBe` [ (AggregateRegisterInitialInvalid, 6),
-                     (AggregateRegisterInitialInvalid, 7),
-                     (AggregateTypeUnsupportedAtUse, 10),
-                     (AggregateGuardTypeMismatch, 13)
-                   ]
-      map message diagnostics `shouldSatisfy` any (T.isInfixOf "non-negative integral literals")
-      map message diagnostics `shouldSatisfy` any (T.isInfixOf "ISO-8601 UTC timestamps")
-      map message diagnostics `shouldSatisfy` any (T.isInfixOf "mapped structural declaration")
-    it "rejects aggregate arithmetic at the operator" $ do
-      source <- readTestText "test/fixtures/aggregate-scalars-arithmetic.keiro"
-      err <- parseErrorOf "test/fixtures/aggregate-scalars-arithmetic.keiro" source
-      err `shouldSatisfy` T.isInfixOf "aggregate arithmetic operator '+' is unsupported"
-      err `shouldSatisfy` T.isInfixOf "aggregate-scalars-arithmetic.keiro:12:39:"
-    it "covers unknown, container, fractional, out-of-range, and ordering failures" $ do
-      diagnostics <- diagnosticsOf "test/fixtures/aggregate-scalars-invalid-capabilities.keiro"
-      [(code diagnostic, line diagnostic) | diagnostic <- diagnostics, severity diagnostic == Error]
-        `shouldBe` [ (AggregateRegisterInitialInvalid, 6),
-                     (AggregateRegisterInitialInvalid, 7),
-                     (AggregateTypeUnknown, 10),
-                     (AggregateTypeUnsupportedAtUse, 10),
-                     (AggregateTypeUnsupportedAtUse, 10),
-                     (AggregateTypeUnsupportedAtUse, 10),
-                     (AggregateGuardCapabilityUnsupported, 13)
-                   ]
-    it "keeps one-member workspace diagnostics identical to the single file" $ do
-      direct <- diagnosticsOf "test/fixtures/aggregate-scalars-unsupported.keiro"
-      composed <- shouldComposeWorkspace "test/fixtures/aggregate-scalars-workspace/service.keiro-workspace"
-      let directErrors =
-            [(code diagnostic, line diagnostic, message diagnostic) | diagnostic <- direct, severity diagnostic == Error]
-          workspaceErrors =
-            [ (wdCode diagnostic, wlLine (NE.head (wdLocations diagnostic)), wdMessage diagnostic)
-            | diagnostic <- checkWorkspace composed,
-              wdSeverity diagnostic == Error
-            ]
-      workspaceErrors `shouldBe` directErrors
-
-  describe "mapped type graph (EP-149)" $ do
-    it "resolves checked declarations, transitive reachability, and every aggregate root path" $ do
-      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
-      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
-      graph <- shouldResolveTypeGraph spec
-      Map.size (tgDeclarations graph) `shouldBe` 4
-      Map.lookup (MappedKey "ArtifactInfo") (tgReachability graph)
-        `shouldBe` Just (Set.fromList [MappedKey "ArtifactKind", MappedKey "ArtifactLocation"])
-      map renderUsePath (usePaths graph "ArtifactLocation")
-        `shouldBe` [ "Catalog command ObserveArtifact .artifact : ArtifactInfo .location : ArtifactLocation",
-                     "Catalog event ArtifactObserved .artifact : ArtifactInfo .location : ArtifactLocation",
-                     "Catalog register currentArtifact : ArtifactInfo .location : ArtifactLocation"
-                   ]
-    it "resolves every builtin through the complete expression algebra" $ do
-      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
-      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
-      graph <- shouldResolveTypeGraph spec
-      case Map.lookup (MappedKey "ArtifactInfo") (tgDeclarations graph) of
-        Just (ResolvedStructural _ (RRecord _ _ fields)) ->
-          Set.fromList (concatMap (foldTypeExpr expressionTags . rwfType) fields)
-            `shouldBe` Set.fromList ["text", "int", "bool", "natural", "time", "json", "optional", "list", "map", "ref:ArtifactKind", "ref:ArtifactLocation"]
-        declaration -> expectationFailure ("unexpected ArtifactInfo declaration: " <> show declaration)
-    it "rejects direct, mutual, wrapped, and union-arm recursion" $ do
-      let direct = mappedSpec [completeStructural "A" (recordShape [TRef "A"])]
-          mutual = mappedSpec [completeStructural "A" (recordShape [TRef "B"]), completeStructural "B" (recordShape [TRef "A"])]
-          wrapped = mappedSpec [completeStructural "A" (recordShape [TList (TOptional (TRef "A"))])]
-          throughArm = mappedSpec [completeStructural "A" (ShapeUnion (TaggedObject "tag" "contents" RejectUnknown) [WireArm "Again" "again" (Just (TRef "A")) noLoc])]
-      map (hasTypeGraphError isRecursive . resolveTypeGraph) [direct, mutual, wrapped, throughArm]
-        `shouldBe` replicate 4 True
-    it "keeps existing ids and enums outside the mapped-reference namespace" $ do
-      let spec =
-            (mappedSpec [completeStructural "A" (recordShape [TRef "ExistingId"])])
-              { specIds = [IdDecl "ExistingId" "id" Nothing noLoc]
-              }
-      resolveTypeGraph spec `shouldSatisfy` hasTypeGraphError isUnresolved
-    it "fingerprints wire identity while ignoring Haskell selector names" $ do
-      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
-      base <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
-      baseGraph <- shouldResolveTypeGraph base
-      haskellRenameGraph <- shouldResolveTypeGraph (mapArtifactField (\field -> field {wfHaskell = "renamedKey"}) base)
-      wireRenameGraph <- shouldResolveTypeGraph (mapArtifactField (\field -> field {wfKey = "renamed_key"}) base)
-      wireFingerprint haskellRenameGraph "ArtifactInfo" `shouldBe` wireFingerprint baseGraph "ArtifactInfo"
-      wireFingerprint wireRenameGraph "ArtifactInfo" `shouldNotBe` wireFingerprint baseGraph "ArtifactInfo"
-
-  describe "string literal integrity" $ do
-    it "parses an escaped emit-map value as exactly one row" $ do
-      let src =
-            T.unlines
-              [ "context svc",
-                "",
-                "emit e {",
-                "  contract c",
-                "  topic events",
-                "  source \"svc\"",
-                "  key thingId",
-                "  map status {",
-                "    \"a\\\" => Wat \\\"b\" => ThingAccepted",
-                "    _ => skip",
-                "  }",
-                "  messageId derive hole",
-                "  idempotencyKey derive hole",
-                "}"
-              ]
-      case parseSpec "<escaped-map>" src of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> case [row | NEmit e <- specNodes spec, row <- emMap e] of
-          [row] -> do
-            emrValue row `shouldBe` "a\" => Wat \"b"
-            emrEvent row `shouldBe` "ThingAccepted"
-          rows -> expectationFailure ("expected one emit-map row, got " <> show (length rows))
-    it "rejects a raw newline inside a quoted string" $ do
-      let src = "context svc\n\ncontract c {\n  schemaVersion 1\n  discriminator kind\n  topic events \"first\nsecond\"\n}\n"
-      parseSpec "<raw-newline>" src `shouldSatisfy` leftContains "unescaped newline"
-    it "rejects an unknown escape sequence" $ do
-      let src = "context svc\n\ncontract c {\n  schemaVersion 1\n  discriminator kind\n  topic events \"bad\\q\"\n}\n"
-      parseSpec "<unknown-escape>" src `shouldSatisfy` leftContains "unknown escape"
-    it "round-trips adversarial text through topics, emit maps, and quoted bindings" $
-      property $
-        forAll genAdversarialText $ \t ->
-          let spec = escapedSpec t
-              rendered = renderSpec spec
-           in counterexample (T.unpack rendered) (parseSpec "<escaped-round-trip>" rendered === Right spec)
-
-  describe "partial status maps" $ do
-    it "suppresses totality only when the partial marker is present" $ do
-      partial <- parseInlineSpec "<partial-status-map>" (statusMapSpec " partial")
-      totalSpec <- parseInlineSpec "<total-status-map>" (statusMapSpec "")
-      map code (validateSpec partial) `shouldNotContain` [StatusMapNotTotal]
-      map code (validateSpec totalSpec) `shouldContain` [StatusMapNotTotal]
-      parseSpec "<partial-round-trip>" (renderSpec partial) `shouldBe` Right partial
-
-  describe "positioned parser diagnostics" $ do
-    it "rejects a duplicate goto at the second clause" $ do
-      err <- parseErrorOf "<duplicate-goto>" duplicateGotoSpec
-      err `shouldSatisfy` T.isInfixOf "duplicate goto"
-      err `shouldSatisfy` T.isInfixOf "<duplicate-goto>:10:"
-    it "rejects duplicate wire and projection blocks at their second occurrences" $ do
-      wireErr <- parseErrorOf "<duplicate-wire>" duplicateWireSpec
-      wireErr `shouldSatisfy` T.isInfixOf "duplicate wire block"
-      wireErr `shouldSatisfy` T.isInfixOf "<duplicate-wire>:8:"
-      projectionErr <- parseErrorOf "<duplicate-projection>" duplicateProjectionSpec
-      projectionErr `shouldSatisfy` T.isInfixOf "duplicate projection block"
-      projectionErr `shouldSatisfy` T.isInfixOf "<duplicate-projection>:9:"
-    it "anchors a missing goto on the transition line" $ do
-      err <- parseErrorOf "<missing-goto>" missingGotoSpec
-      err `shouldSatisfy` T.isInfixOf "missing a goto clause"
-      err `shouldSatisfy` T.isInfixOf "<missing-goto>:8:"
-    it "stops before a misplaced dispatch-id and expects schedule at its start" $ do
-      let src = misplacedDispatchIdSpec
-          expectedPosition =
-            "<misplaced-dispatch-id>:"
-              <> T.pack (show (lineNumberContaining "dispatch-id" src))
-              <> ":5:"
-      err <- parseErrorOf "<misplaced-dispatch-id>" src
-      err `shouldSatisfy` T.isInfixOf "schedule"
-      err `shouldSatisfy` T.isInfixOf expectedPosition
-    it "keeps a malformed register declaration's equals error" $ do
-      err <- parseErrorOf "<malformed-register>" malformedRegisterSpec
-      err `shouldSatisfy` T.isInfixOf "expecting '='"
-
-  describe "bounded decimal literals" $ do
-    forM_ decimalOverflowSpecs $ \(site, src) ->
-      it ("rejects overflow at " <> site) $ do
-        err <- parseErrorOf ("<overflow-" <> site <> ">") src
-        err `shouldSatisfy` T.isInfixOf ("decimal literal " <> decimalOverflow <> " is out of range")
-    it "accepts maxBound without changing its value" $ do
-      spec <- parseInlineSpec "<max-bound>" (wireDecimalSpec (T.pack (show (maxBound :: Int))))
-      [wireSchemaVersion wire | NAggregate aggregate <- specNodes spec, Just wire <- [aggWire aggregate]]
-        `shouldBe` [maxBound]
-
-  describe "identifier hygiene" $ do
-    it "reports constructor shape and Haskell keywords at their owning declarations" $ do
-      spec <- parseInlineSpec "<identifier-hygiene>" identifierHygieneSpec
-      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic `elem` [IdentNotConstructorSafe, IdentHaskellKeyword]]
-        `shouldContain` [(IdentNotConstructorSafe, 3), (IdentHaskellKeyword, 7)]
-    it "rejects generated vertex constructors that collide with event constructors" $ do
-      spec <- parseInlineSpec "<vertex-collision>" vertexCollisionSpec
-      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic == VertexCtorCollision]
-        `shouldBe` [(VertexCtorCollision, 3)]
-    it "rejects underscore-leading names whose title-casing cannot make a module segment" $ do
-      spec <- parseInlineSpec "<underscore-node>" underscoreNodeSpec
-      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic == IdentNotConstructorSafe]
-        `shouldBe` [(IdentNotConstructorSafe, 3)]
-    it "rejects non-ASCII identifier characters in the parser" $
-      parseSpec "<unicode-identifier>" unicodeIdentifierSpec `shouldSatisfy` leftContains "unexpected"
-
-  describe "canonical reservation.keiro" $
-    it "parses into the expected aggregate shape" $ do
-      input <- readTestText "test/fixtures/reservation.keiro"
-      case parseSpec "test/fixtures/reservation.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> do
-          specContext spec `shouldBe` "hospital-capacity"
-          length (specIds spec) `shouldBe` 3
-          length (specEnums spec) `shouldBe` 3
-          length (specRules spec) `shouldBe` 1
-          case specNodes spec of
-            [NAggregate a] -> do
-              aggName a `shouldBe` "Reservation"
-              length (aggStates a) `shouldBe` 6
-              length (aggCommands a) `shouldBe` 2
-              length (aggEvents a) `shouldBe` 2
-              length (aggTransitions a) `shouldBe` 2
-              map stTerminal (aggStates a) `shouldBe` [False, False, False, True, True, True]
-            other -> expectationFailure ("expected one aggregate node, got " <> show (length other))
-
-  describe "validator" $ do
-    it "accepts the canonical reservation.keiro" $ do
-      codes <- errorCodesOf "test/fixtures/reservation.keiro"
-      codes `shouldBe` []
-    it "rejects a missing status-map as StatusMapNotTotal" $ do
-      codes <- diagnosticCodesOf "test/fixtures/reservation-no-statusmap.keiro"
-      codes `shouldContain` [StatusMapNotTotal]
-    it "rejects an undeclared command as UndeclaredCommand" $ do
-      codes <- diagnosticCodesOf "test/fixtures/reservation-bad-command.keiro"
-      codes `shouldContain` [UndeclaredCommand]
-    it "rejects a wall-clock guard atom as ClockSampled" $ do
-      codes <- diagnosticCodesOf "test/fixtures/reservation-clock.keiro"
-      codes `shouldContain` [ClockSampled]
-    it "accepts a v2 event with a contiguous upcaster hole" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-v2.keiro"
-      codes `shouldBe` []
-    it "rejects a v2 event with no upcaster as EvtVersionMissingUpcaster" $ do
-      codes <- diagnosticCodesOf "test/fixtures/reservation-v2-noupcast.keiro"
-      codes `shouldContain` [EvtVersionMissingUpcaster]
-    it "accepts shared upcaster sources for different event kinds" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-dup-upcast-source.keiro"
-      codes `shouldNotContain` [DuplicateUpcasterSource]
-    it "rejects a gap in the aggregate-global upcaster chain" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-chain-gap.keiro"
-      codes `shouldContain` [UpcasterChainGap]
-    it "warns while a retiring event keeps its live emitting transition" $ do
-      diagnostics <- diagnosticsOf "test/fixtures/reservation-retiring.keiro"
-      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
-      [code d | d <- diagnostics, severity d == Warning]
-        `shouldContain` [EventRetirementInProgress]
-    it "rejects a retiring event after its live emitting transition disappears" $ do
-      source <- readTestText "test/fixtures/reservation-retiring.keiro"
-      spec <- parseInlineSpec "<retiring-without-emitter>" (T.replace " ; emit TransferReservationConfirmed" "" source)
-      [code d | d <- validateSpec spec, severity d == Error]
-        `shouldContain` [EventRetirementInProgress]
-    it "warns when a deprecated event has no replay-only emitting transition" $ do
-      diagnostics <- diagnosticsOf "test/fixtures/reservation-deprecated.keiro"
-      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
-      [code d | d <- diagnostics, severity d == Warning]
-        `shouldContain` [DeprecatedEventReplayHazard]
-    it "recognises deprecated plus replay-only as the replay-safe cutover" $ do
-      diagnostics <- diagnosticsOf "test/fixtures/reservation-deprecated-replay-only.keiro"
-      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
-      [code d | d <- diagnostics, severity d == Warning]
-        `shouldContain` [EventRetirementInProgress]
-      [code d | d <- diagnostics] `shouldNotContain` [DeprecatedEventReplayHazard]
-    it "requires exact, unique status-map event keys" $ do
-      dangling <- errorCodesOf "test/fixtures/statusmap-dangling.keiro"
-      mapM_ (\expected -> dangling `shouldContain` [expected]) [StatusMapDanglingKey, StatusMapNotTotal]
-      duplicate <- errorCodesOf "test/fixtures/statusmap-dup-key.keiro"
-      duplicate `shouldContain` [StatusMapDuplicateKey]
-    it "rejects duplicate spec and aggregate names" $ do
-      codes <- errorCodesOf "test/fixtures/duplicate-names.keiro"
-      mapM_
-        (\expected -> codes `shouldContain` [expected])
-        [ DuplicateNodeName,
-          DuplicateEnumCtor,
-          DuplicateEnumWire,
-          DuplicateIdPrefix,
-          DuplicateCommandName,
-          DuplicateEventName
-        ]
-    it "rejects aggregate-local references that do not resolve" $ do
-      codes <- errorCodesOf "test/fixtures/aggregate-bad-refs.keiro"
-      codes `shouldContain` [RegisterInitialOutOfScope, UndeclaredCommand, WriteTargetNotRegister]
-    it "anchors UnreachableState on the state row" $ do
-      let src =
-            T.unlines
-              [ "context repro",
-                "",
-                "aggregate Thing",
-                "  regs",
-                "  states",
-                "    Initial",
-                "    Unreachable"
-              ]
-      case parseSpec "<unreachable-row>" src of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec ->
-          [line d | d <- validateSpec spec, code d == UnreachableState]
-            `shouldBe` [7]
-    it "accepts a replay-only twin with a live sibling (plan 143)" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-guard-tightened-twin.keiro"
-      codes `shouldBe` []
-    it "rejects a replay-only transition that emits nothing" $ do
-      case parseSpec "<replay-only-no-emit>" (replayOnlySpecWith ["    write reservationState := Held", "    goto  Held"]) of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec ->
-          [code d | d <- validateSpec spec, severity d == Error]
-            `shouldContain` [ReplayOnlyEmitsNothing]
-    it "warns when a replay-only transition has no live sibling" $ do
-      case parseSpec "<replay-only-orphan>" (replayOnlySpecWith ["    emit  TransferReservationCreated", "    goto  Held"]) of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> do
-          [code d | d <- validateSpec spec, severity d == Warning]
-            `shouldContain` [ReplayOnlyCommandStillLive]
-          [code d | d <- validateSpec spec, severity d == Error]
-            `shouldNotContain` [ReplayOnlyCommandStillLive]
-
-  describe "complementExpr (plan 143)" $ do
-    it "applies De Morgan over and/or and flips comparison operators" $ do
-      let a = EAtom (AName "a")
-          b = EAtom (AName "b")
-      complementExpr (EAnd a b)
-        `shouldBe` EOr (ECmp OpEq a (EAtom (ABool False))) (ECmp OpEq b (EAtom (ABool False)))
-      complementExpr (ECmp OpLt a b) `shouldBe` ECmp OpGe a b
-      complementExpr (ECmp OpEq a b) `shouldBe` ECmp OpNeq a b
-      complementExpr (ECmp OpLe a b) `shouldBe` ECmp OpGt a b
-      complementExpr (ECmp OpGt a b) `shouldBe` ECmp OpLe a b
-      complementExpr (ECmp OpGe a b) `shouldBe` ECmp OpLt a b
-      complementExpr (ECmp OpNeq a b) `shouldBe` ECmp OpEq a b
-    it "flips boolean literals and grounds bare names as == false" $ do
-      complementExpr (EAtom (ABool True)) `shouldBe` EAtom (ABool False)
-      complementExpr (EAtom (AName "open"))
-        `shouldBe` ECmp OpEq (EAtom (AName "open")) (EAtom (ABool False))
-    it "stays inside the grammar: the complement of any guard re-parses" $
-      property $
-        forAll genExpr $ \e ->
-          let twin =
-                replayOnlySpecWith
-                  [ "    guard " <> renderExprText (complementExpr e),
-                    "    emit  TransferReservationCreated",
-                    "    goto  Held"
-                  ]
-           in case parseSpec "<complement>" twin of
-                Left err -> counterexample (T.unpack err) False
-                Right spec ->
-                  [tGuard t | NAggregate a <- specNodes spec, t <- aggTransitions a]
-                    === [Just (complementExpr e)]
-
-  describe "evolution parsing" $ do
-    it "parses event version and upcaster from reservation-v2.keiro" $ do
-      input <- readTestText "test/fixtures/reservation-v2.keiro"
-      case parseSpec "test/fixtures/reservation-v2.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> case [e | NAggregate a <- specNodes spec, e <- aggEvents a, evName e == "TransferReservationCreated"] of
-          (e : _) -> do
-            evVersion e `shouldBe` 2
-            evUpcastFrom e `shouldBe` Just (1, Hole)
-          [] -> expectationFailure "TransferReservationCreated not found"
-    it "round-trips the retiring marker" $ do
-      spec <- specOf "test/fixtures/reservation-retiring.keiro"
-      parseSpec "<retiring-round-trip>" (renderSpec spec) `shouldBe` Right spec
-      [evRetiring event | NAggregate aggregate <- specNodes spec, event <- aggEvents aggregate, evName event == "TransferReservationConfirmed"]
-        `shouldBe` [True]
-    it "rejects an event marked both retiring and deprecated" $ do
-      source <- readTestText "test/fixtures/reservation-retiring.keiro"
-      let conflicting = T.replace "retiring event TransferReservationConfirmed" "retiring deprecated event TransferReservationConfirmed" source
-      parseSpec "<conflicting-retirement-markers>" conflicting `shouldSatisfy` isLeft
-
-  describe "aggregate snapshots (EP-109)" $ do
-    it "parses, validates, and round-trips a snapshot policy with codec fixture" $ do
-      spec <- specOf "test/fixtures/reservation-snapshot.keiro"
-      errorCodesOf "test/fixtures/reservation-snapshot.keiro" `shouldReturn` []
-      parseSpec "<snapshot-round-trip>" (renderSpec spec) `shouldBe` Right spec
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        [aggregate] -> aggSnapshot aggregate `shouldBe` Just (SnapshotSpec (SnapEvery 100) 1 "7eb3a94f62f947231375d44083e2a1c8029d91ffe0329107d55092ed3430efcc" noLoc)
-        aggregates -> expectationFailure ("expected one snapshot aggregate, got " <> show (length aggregates))
-    it "rejects disabled intervals and invalid codec fixtures" $ do
-      source <- readTestText "test/fixtures/reservation-snapshot.keiro"
-      interval <- parseInlineSpec "<snapshot-zero>" (T.replace "snapshot every 100" "snapshot every 0" source)
-      map code (validateSpec interval) `shouldContain` [SnapshotIntervalInvalid]
-      version <- parseInlineSpec "<snapshot-version-zero>" (T.replace "state-codec version=1" "state-codec version=0" source)
-      map code (validateSpec version) `shouldContain` [SnapshotCodecFixtureInvalid]
-      emptyHash <- parseInlineSpec "<snapshot-empty-hash>" (T.replace "shape-hash=\"7eb3a94f62f947231375d44083e2a1c8029d91ffe0329107d55092ed3430efcc\"" "shape-hash=\"\"" source)
-      map code (validateSpec emptyHash) `shouldContain` [SnapshotCodecFixtureInvalid]
-    it "conditionally lowers JSON instances and the live defaultStateCodec" $ do
-      snapshot <- specOf "test/fixtures/reservation-snapshot.keiro"
-      ordinary <- specOf "test/fixtures/reservation.keiro"
-      case ([aggregate | NAggregate aggregate <- specNodes snapshot], [aggregate | NAggregate aggregate <- specNodes ordinary]) of
-        ([snapshotAggregate], [ordinaryAggregate]) -> do
-          let snapshotModules = scaffoldAggregate (defaultContext (specContext snapshot)) snapshot snapshotAggregate
-              ordinaryModules = scaffoldAggregate (defaultContext (specContext ordinary)) ordinary ordinaryAggregate
-              snapshotDomain = generatedTextEndingIn "Domain.hs" snapshotModules
-              snapshotStream = generatedTextEndingIn "EventStream.hs" snapshotModules
-              ordinaryDomain = generatedTextEndingIn "Domain.hs" ordinaryModules
-              ordinaryStream = generatedTextEndingIn "EventStream.hs" ordinaryModules
-          snapshotDomain `shouldSatisfy` T.isInfixOf "deriving anyclass (ToJSON, FromJSON)"
-          snapshotStream `shouldSatisfy` T.isInfixOf "snapshotPolicy = Every 100"
-          snapshotStream `shouldSatisfy` T.isInfixOf "stateCodec = Just (withFoldFingerprint"
-          snapshotStream `shouldSatisfy` T.isInfixOf "Spec-visible fold changes invalidate old"
-          snapshotStream `shouldSatisfy` T.isInfixOf "module are invisible here"
-          snapshotStream `shouldSatisfy` T.isInfixOf "reservationSnapshotFixture = (1, \"7eb3a94f62f947231375d44083e2a1c8029d91ffe0329107d55092ed3430efcc\")"
-          ordinaryDomain `shouldNotSatisfy` T.isInfixOf "DeriveAnyClass"
-          ordinaryStream `shouldSatisfy` T.isInfixOf "snapshotPolicy = Never"
-          ordinaryStream `shouldSatisfy` T.isInfixOf "stateCodec = Nothing"
-          ordinaryStream `shouldSatisfy` T.isInfixOf "reservationCategory = Stream.categoryUnsafe \"reservation\""
-          firewallBreaches snapshotModules `shouldBe` []
-        _ -> expectationFailure "expected one aggregate in each snapshot test spec"
-
-  describe "aggregate fold fingerprints (plan 138)" $ do
-    it "is deterministic across repeated parses and formatting-only changes" $ do
-      source <- readTestText "test/fixtures/reservation.keiro"
-      first <- parseInlineSpec "<first>" source
-      second <- parseInlineSpec "<second>" ("\n\n" <> renderSpec first <> "\n")
-      aggregateFoldFingerprint first (onlyAggregate first)
-        `shouldBe` aggregateFoldFingerprint second (onlyAggregate second)
-    it "changes for transition writes, guards, and referenced rule bodies" $ do
-      base <- specOf "test/fixtures/reservation.keiro"
-      writeChanged <- specOf "test/fixtures/reservation-foldchange.keiro"
-      guardChanged <- specOf "test/fixtures/reservation-guard-tightened.keiro"
-      source <- readTestText "test/fixtures/reservation.keiro"
-      ruleChanged <- parseInlineSpec "<rule-change>" (T.replace "RedTag => true" "RedTag => false" source)
-      let baseFingerprint = aggregateFoldFingerprint base (onlyAggregate base)
-      aggregateFoldFingerprint writeChanged (onlyAggregate writeChanged) `shouldNotBe` baseFingerprint
-      aggregateFoldFingerprint guardChanged (onlyAggregate guardChanged) `shouldNotBe` baseFingerprint
-      aggregateFoldFingerprint ruleChanged (onlyAggregate ruleChanged) `shouldNotBe` baseFingerprint
-    it "ignores wire and projection changes" $ do
-      base <- specOf "test/fixtures/reservation.keiro"
-      wireChanged <- specOf "test/fixtures/reservation-wire.keiro"
-      source <- readTestText "test/fixtures/reservation.keiro"
-      projectionChanged <- parseInlineSpec "<projection-change>" (T.replace "projection transfer_decisions" "projection renamed_projection" source)
-      let surface = aggregateFoldSurface base (onlyAggregate base)
-      aggregateFoldSurface wireChanged (onlyAggregate wireChanged) `shouldBe` surface
-      aggregateFoldSurface projectionChanged (onlyAggregate projectionChanged) `shouldBe` surface
-    it "invalidates mapped-register snapshots when binding or wire identity changes" $ do
-      base <- specOf "test/fixtures/consumer-types.keiro"
-      bindingChanged <- specOf "test/fixtures/consumer-types-binding-change.keiro"
-      wireChanged <- specOf "test/fixtures/consumer-types-wirekey.keiro"
-      let baseFingerprint = aggregateFoldFingerprint base (onlyAggregate base)
-      aggregateFoldFingerprint bindingChanged (onlyAggregate bindingChanged) `shouldNotBe` baseFingerprint
-      aggregateFoldFingerprint wireChanged (onlyAggregate wireChanged) `shouldNotBe` baseFingerprint
-
-  describe "process/timer (EP-3)" $ do
-    it "parses the hospital-surge process + nested timer" $ do
-      input <- readTestText "test/fixtures/hospital-surge.keiro"
-      case parseSpec "test/fixtures/hospital-surge.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> case [p | NProcess p <- specNodes spec] of
-          (p : _) -> do
-            procId p `shouldBe` "HospitalSurge"
-            procName p `shouldBe` "hospital-surge"
-            procRejected p `shouldBe` PolHalt
-            procPoison p `shouldBe` PolHalt
-            sagaCategory (procSaga p) `shouldBe` "hospitalSurge"
-            tmName (procTimer p) `shouldBe` "surgeFollowUp"
-            onReject (fireDisposition (tmFire (procTimer p))) `shouldBe` OFired
-            onAmbiguous (fireDisposition (tmFire (procTimer p))) `shouldBe` ORetry
-            tmMaxAttempts (procTimer p) `shouldBe` 5
-          [] -> expectationFailure "no process node parsed"
-    it "round-trips the hospital-surge spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/hospital-surge.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the hospital-surge spec (no errors; benign-inversion warnings only)" $ do
-      codes <- errorCodesOf "test/fixtures/hospital-surge.keiro"
-      codes `shouldBe` []
-    it "rejects illegal saga categories and no longer parses the raw stream-prefix clause" $ do
-      spec <- specOf "test/fixtures/hospital-surge.keiro"
-      mapM_
-        (\categoryName -> processErrorCodes (\process -> process {procSaga = (procSaga process) {sagaCategory = categoryName}}) spec `shouldContain` [SagaCategoryIllegal])
-        ["", "$all", "hospital-surge", "hospital surge", "wf:surge"]
-      source <- readTestText "test/fixtures/hospital-surge.keiro"
-      parseSpec "<legacy-saga>" (T.replace "saga Surge category \"hospitalSurge\"" "saga Surge stream=\"hospital-surge-\" <> correlationId" source)
-        `shouldSatisfy` isLeft
-    it "rejects a wall-clock fireAt as ProcessFireAtNotInjected" $ do
-      codes <- errorCodesOf "test/fixtures/hospital-surge-clock.keiro"
-      codes `shouldContain` [ProcessFireAtNotInjected]
-    it "reports one ProcessFireAtNotInjected for a wholly unknown fireAt field" $ do
-      codes <- errorCodesOf "test/fixtures/hospital-surge-clock.keiro"
-      length (filter (== ProcessFireAtNotInjected) codes) `shouldBe` 1
-    it "rejects a user-supplied dispatch id as ProcessDispatchIdSupplied" $ do
-      codes <- errorCodesOf "test/fixtures/hospital-surge-dispatchid.keiro"
-      codes `shouldContain` [ProcessDispatchIdSupplied]
-    it "rejects an unresolved saga reference as ProcessUnresolvedRef" $ do
-      codes <- errorCodesOf "test/fixtures/hospital-surge-badref.keiro"
-      codes `shouldContain` [ProcessUnresolvedRef]
-    it "rejects unresolved process commands, projections, schedules, and advance ids" $ do
-      codes <- errorCodesOf "test/fixtures/process-ghost-refs.keiro"
-      length (filter (== ProcessUnresolvedRef) codes) `shouldBe` 5
-      codes `shouldContain` [ProcessDispatchIdSupplied]
-
-  describe "router (EP-108)" $ do
-    it "parses the incident-paging router shape" $ do
-      input <- readTestText "test/fixtures/incident-paging/incident-paging.keiro"
-      case parseSpec "test/fixtures/incident-paging/incident-paging.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> case [router | NRouter router <- specNodes spec] of
-          [router] -> do
-            rtId router `shouldBe` "PagingRouter"
-            rtName router `shouldBe` "jitsurei-paging"
-            corrField (rtKey router) `shouldBe` "incidentId"
-            rvSource (rtResolve router) `shouldBe` ResolveReadModel "service_oncall"
-            rvRow (rtResolve router) `shouldBe` ["responderId"]
-            rdCommand (rtDispatch router) `shouldBe` "SendPage"
-            rtRejected router `shouldBe` PolDeadLetter
-            rtPoison router `shouldBe` PolHalt
-          routers -> expectationFailure ("expected one router, got " <> show (length routers))
-    it "round-trips the incident-paging spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/incident-paging/incident-paging.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the incident-paging router with warnings only" $ do
-      codes <- errorCodesOf "test/fixtures/incident-paging/incident-paging.keiro"
-      codes `shouldBe` []
-      diagnostics <- diagnosticCodesOf "test/fixtures/incident-paging/incident-paging.keiro"
-      diagnostics `shouldContain` [PolicyDeadLetterUnused, AmbiguousFollowsRejectedPolicy]
-    it "rejects unresolved targets, keys, commands, and binding scopes" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      routerErrorCodes (\router -> router {rtTarget = "Pge"}) spec `shouldContain` [RouterUnresolvedRef]
-      routerErrorCodes (\router -> router {rtKey = (rtKey router) {corrField = "incidntId"}}) spec `shouldContain` [RouterKeyFieldUnknown]
-      routerErrorCodes (\router -> router {rtDispatch = (rtDispatch router) {rdCommand = "SendPag"}}) spec `shouldContain` [RouterCommandUnknown]
-      routerErrorCodes
-        ( \router ->
-            let dispatch = rtDispatch router
-             in router {rtDispatch = dispatch {rdFields = [FieldBinding "responderId" (Just "resolved.responder")]}}
-        )
-        spec
-        `shouldContain` [RouterBindingUnscoped]
-    it "rejects unresolved read models and contradictory rejection policies" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      let withoutReadModel = removeReadModel "service_oncall" spec
-      errorCodes withoutReadModel `shouldContain` [RouterUnresolvedRef]
-      routerErrorCodes
-        ( \router ->
-            let dispatch = rtDispatch router
-                disposition = rdDisposition dispatch
-             in router
-                  { rtRejected = PolHalt,
-                    rtDispatch = dispatch {rdDisposition = disposition {onFailed = DDeadLetter "page rejected"}}
-                  }
-        )
-        spec
-        `shouldContain` [PolicyContradiction]
-    it "rejects on-ambiguous Fired for process timers" $ do
-      spec <- specOf "test/fixtures/hospital-surge.keiro"
-      let changed =
-            spec
-              { specNodes =
-                  [ case node of
-                      NProcess process ->
-                        let timer = procTimer process
-                            fire = tmFire timer
-                            disposition = fireDisposition fire
-                         in NProcess process {procTimer = timer {tmFire = fire {fireDisposition = disposition {onAmbiguous = OFired}}}}
-                      _ -> node
-                  | node <- specNodes spec
-                  ]
-              }
-      errorCodes changed `shouldContain` [AmbiguousMarkedBenign]
-    it "requires explicit policy and ambiguity clauses in the grammar" $ do
-      source <- readTestText "test/fixtures/hospital-surge.keiro"
-      parseSpec "<missing-poison>" (T.replace "  poison => halt\n" "" source) `shouldSatisfy` isLeft
-      parseSpec "<missing-ambiguous>" (T.replace " ; on-ambiguous Retry" "" source) `shouldSatisfy` isLeft
-    it "scaffolds firewall-clean router wiring, policies, and typed-hole guidance" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      case [router | NRouter router <- specNodes spec] of
-        [router] -> do
-          let ctx = defaultContext (specContext spec)
-              modules = scaffoldRouter ctx router
-              generated = [m | m <- modules, kind m == Generated]
-              holes = [m | m <- modules, kind m == HoleStub]
-          firewallBreaches generated `shouldBe` []
-          case (generated, holes) of
-            ([generatedModule], [holeModule]) -> do
-              moduleText generatedModule `shouldSatisfy` T.isInfixOf "pagingRouterWorkerOptions"
-              moduleText generatedModule `shouldSatisfy` T.isInfixOf "rejectedCommandPolicy = RejectedDeadLetter"
-              moduleText holeModule `shouldSatisfy` T.isInfixOf "UNION of resolved target identities"
-              moduleText holeModule `shouldSatisfy` T.isInfixOf "confirmBenignDuplicate"
-            _ -> expectationFailure "expected one generated router module and one router hole module"
-        routers -> expectationFailure ("expected one router, got " <> show (length routers))
-    it "requires a caller callback for non-halting poison policies" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      case [router | NRouter router <- specNodes spec] of
-        [router] -> do
-          let ctx = defaultContext (specContext spec)
-              generatedFor choice = [moduleText m | m <- scaffoldRouter ctx router {rtPoison = choice}, kind m == Generated]
-          mapM_
-            ( \(choice, constructor) -> case generatedFor choice of
-                [generatedModule] -> do
-                  generatedModule `shouldSatisfy` T.isInfixOf "(Envelope msg -> Eff es ()) -> WorkerOptions es msg"
-                  generatedModule `shouldSatisfy` T.isInfixOf (constructor <> " poisonCallback")
-                _ -> expectationFailure "expected one generated router module"
-            )
-            [(PolDeadLetter, "PoisonDeadLetter"), (PolSkip, "PoisonSkip")]
-          case [moduleText m | m <- scaffoldRouter ctx router {rtRejected = PolSkip}, kind m == Generated] of
-            [generatedModule] -> generatedModule `shouldSatisfy` T.isInfixOf "rejectedCommandPolicy = RejectedSkip"
-            _ -> expectationFailure "expected one generated router module"
-        routers -> expectationFailure ("expected one router, got " <> show (length routers))
-    it "emits router harness facts that pin policy and target-keyed identity" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      case [router | NRouter router <- specNodes spec] of
-        [router] -> case harnessRouter (defaultContext (specContext spec)) router of
-          [facts] -> do
-            moduleText facts `shouldSatisfy` T.isInfixOf "(\"rejectedPolicy\", \"deadLetter\")"
-            moduleText facts `shouldSatisfy` T.isInfixOf "targetStreamName, occurrence"
-          modules -> expectationFailure ("expected one router harness, got " <> show (length modules))
-        routers -> expectationFailure ("expected one router, got " <> show (length routers))
-    it "rejects invalid timer ceilings and target field bindings" $ do
-      codes <- errorCodesOf "test/fixtures/process-bad-timer.keiro"
-      mapM_
-        (\expected -> codes `shouldContain` [expected])
-        [ProcessTimerCeilingInvalid, ProcessFieldBindingUnresolved]
-    it "accepts resolved process projection references" $ do
-      codes <- errorCodesOf "test/fixtures/surge-service.keiro"
-      codes `shouldBe` []
-    it "scaffolds the process: Generated wiring is firewall-clean + a HoleStub" $ do
-      mods <- scaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
-      let gens = [m | m <- mods, kind m == Generated]
-          holes = [m | m <- mods, kind m == HoleStub]
-      length holes `shouldBe` 1
-      firewallBreaches gens `shouldBe` []
-      case gens of
-        [generatedModule] -> do
-          -- the worker uses the spec's ceiling, never the dangerous default
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "max-attempts = 5"
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "hospitalSurgeProcessWorkerOptions"
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "hospitalSurgeCategory = Stream.categoryUnsafe \"hospitalSurge\""
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "confirmBenignDuplicate"
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "StreamName -> EventId -> CommandError -> Eff es Bool"
-          moduleText generatedModule `shouldSatisfy` T.isInfixOf "Left (CommandAmbiguous _)"
-          case holes of
-            [holeModule] -> moduleText holeModule `shouldSatisfy` T.isInfixOf "entityStream hospitalSurgeCategory"
-            _ -> expectationFailure "expected one process hole module"
-        _ -> expectationFailure "expected one generated process module"
-    it "process scaffold is deterministic" $ do
-      a <- scaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
-      b <- scaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
-      map moduleText a `shouldBe` map moduleText b
-
-  describe "contract (EP-4)" $ do
-    it "parses the emergency contract (topics + events-on-topic + typed fields)" $ do
-      input <- readTestText "test/fixtures/contract.keiro"
-      case parseSpec "test/fixtures/contract.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> case [c | NContract c <- specNodes spec] of
-          (c : _) -> do
-            ctrName c `shouldBe` "emergency"
-            ctrDiscriminator c `shouldBe` "messageType"
-            map fst (ctrTopics c) `shouldBe` ["incidentEvents", "hospitalEvents"]
-            map ceName (ctrEvents c) `shouldBe` ["IncidentTransferNeedDeclared", "TransferReservationAccepted"]
-          [] -> expectationFailure "no contract node parsed"
-    it "round-trips the contract spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/contract.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "round-trips the intake (inbox) spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/intake.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the intake spec (complete disposition, no inversions)" $ do
-      codes <- errorCodesOf "test/fixtures/intake.keiro"
-      codes `shouldBe` []
-    it "lowers explicit dedupe-only persistence and defaults omission to full-envelope" $ do
-      spec <- specOf "test/fixtures/intake.keiro"
-      ordinary <- specOf "test/fixtures/intake-decode.keiro"
-      case ([intake | NIntake intake <- specNodes spec], [intake | NIntake intake <- specNodes ordinary]) of
-        ([intake], [defaultIntake]) -> do
-          inkPersist intake `shouldBe` InkPersistDedupeOnly
-          inkPersist defaultIntake `shouldBe` InkPersistFull
-          renderSpec spec `shouldSatisfy` T.isInfixOf "persist = dedupe-only"
-          renderSpec ordinary `shouldNotSatisfy` T.isInfixOf "persist ="
-          let inbox = generatedTextEndingIn "Inbox.hs" (scaffoldIntake (defaultContext (specContext spec)) intake)
-          inbox `shouldSatisfy` T.isInfixOf "inboxPersistence = PersistDedupeOnly"
-        (intakes, defaultIntakes) ->
-          expectationFailure ("expected one intake in each fixture, got " <> show (length intakes, length defaultIntakes))
-    it "rejects duplicate => retry (inversion 1)" $ do
-      codes <- errorCodesOf "test/fixtures/intake-dup-retry.keiro"
-      codes `shouldContain` [DispositionDuplicateRetry]
-    it "rejects previouslyFailed => retry (inversion 2)" $ do
-      codes <- errorCodesOf "test/fixtures/intake-pf-retry.keiro"
-      codes `shouldContain` [DispositionPreviouslyFailedRetry]
-    it "rejects an incomplete disposition table" $ do
-      codes <- errorCodesOf "test/fixtures/intake-incomplete.keiro"
-      codes `shouldContain` [DispositionIncomplete]
-    it "rejects a shadowing duplicate intake disposition row" $ do
-      codes <- errorCodesOf "test/fixtures/intake-dup-row.keiro"
-      codes `shouldContain` [DispositionDuplicateOutcome]
-    it "rejects intake events declared on another topic" $ do
-      codes <- errorCodesOf "test/fixtures/intake-topic-mismatch.keiro"
-      codes `shouldContain` [TopicAffinityMismatch]
-    it "round-trips the emit/publisher spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/emit.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the emit/publisher spec (skip present, coupling resolves)" $ do
-      codes <- errorCodesOf "test/fixtures/emit.keiro"
-      codes `shouldBe` []
-    it "rejects a missing _ => skip catch-all as EmitSkipMissing" $ do
-      codes <- errorCodesOf "test/fixtures/emit-noskip.keiro"
-      codes `shouldContain` [EmitSkipMissing]
-    it "rejects mapping to an undeclared contract event as EmitUnresolvedContract" $ do
-      codes <- errorCodesOf "test/fixtures/emit-badevent.keiro"
-      codes `shouldContain` [EmitUnresolvedContract]
-    it "rejects emit events declared on another topic" $ do
-      codes <- errorCodesOf "test/fixtures/emit-topic-mismatch.keiro"
-      codes `shouldContain` [TopicAffinityMismatch]
-
-  describe "pgmq workqueue/dispatch (EP-5)" $ do
-    it "round-trips the reservation-work spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/reservation-work.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the reservation-work spec (physical matches, no inversions)" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-work.keiro"
-      codes `shouldBe` []
-    it "rejects a divergent captured physical name as WqPhysicalDivergence" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-work-divergent.keiro"
-      codes `shouldContain` [WqPhysicalDivergence]
-    it "rejects storeFailure => deadLetter as WqStoreFailureNotRetry" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-work-sf-deadletter.keiro"
-      codes `shouldContain` [WqStoreFailureNotRetry]
-    it "rejects decodeFailure => retry as WqDecodeFailureNotDeadLetter" $ do
-      codes <- errorCodesOf "test/fixtures/reservation-work-df-retry.keiro"
-      codes `shouldContain` [WqDecodeFailureNotDeadLetter]
-    it "requires complete, unique workqueue disposition rows" $ do
-      incomplete <- errorCodesOf "test/fixtures/workqueue-incomplete.keiro"
-      incomplete `shouldContain` [WqDispositionIncomplete]
-      duplicateSpec <- specOf "test/fixtures/workqueue-dup-row.keiro"
-      let duplicateDiagnostics = [d | d <- validateSpec duplicateSpec, code d == DispositionDuplicateOutcome]
-      map line duplicateDiagnostics `shouldBe` [18]
-    it "checks the captured queueRef dlq and table fixtures" $ do
-      dlqCodes <- errorCodesOf "test/fixtures/workqueue-dlq-divergent.keiro"
-      dlqCodes `shouldContain` [WqDlqDivergence]
-      tableCodes <- errorCodesOf "test/fixtures/workqueue-table-divergent.keiro"
-      tableCodes `shouldContain` [WqTableDivergence]
-    it "matches queueRef for upper-case, punctuation, and hashed logical names" $ do
-      upper <- errorCodesOf "test/fixtures/workqueue-uppercase-logical.keiro"
-      upper `shouldBe` []
-      hashed <- errorCodesOf "test/fixtures/workqueue-hashed-logical.keiro"
-      hashed `shouldBe` []
-      derivedQueueTrio "hospital_capacity.reservation_work.per_hospital_fifo_lane_assignments"
-        `shouldBe` ( "hospital_capacity_reservat_757040df00976c33",
-                     "hospital_capacity_reservat_757040df00976c33_dlq",
-                     "pgmq.q_hospital_capacity_reservat_757040df00976c33"
-                   )
-    it "resolves dispatch dedup queues and payload wire fields" $ do
-      ghost <- errorCodesOf "test/fixtures/dispatch-dedup-ghost-queue.keiro"
-      ghost `shouldContain` [DispatchDedupQueueUnresolved]
-      field <- errorCodesOf "test/fixtures/dispatch-dedup-bad-field.keiro"
-      field `shouldContain` [DispatchDedupFieldUnresolved]
-    it "requires a resolvable group key exactly when ordering is FIFO" $ do
-      noKey <- errorCodesOf "test/fixtures/reservation-work-fifo-nokey.keiro"
-      noKey `shouldContain` [WqGroupKeyMissing]
-      unordered <- errorCodesOf "test/fixtures/reservation-work-key-unordered.keiro"
-      unordered `shouldContain` [WqGroupKeyWithoutFifo]
-      source <- readTestText "test/fixtures/reservation-work.keiro"
-      unresolved <- parseInlineSpec "<unresolved-group-key>" (T.replace "group key from reservationId" "group key from missingId" source)
-      map code (validateSpec unresolved) `shouldContain` [WqGroupKeyUnresolved]
-    it "warns on unlogged storage and rejects empty partition settings" $ do
-      warningCodes <- diagnosticCodesOf "test/fixtures/reservation-work-unlogged.keiro"
-      warningCodes `shouldContain` [WqUnloggedDurability]
-      partitionCodes <- errorCodesOf "test/fixtures/reservation-work-partitioned-empty.keiro"
-      partitionCodes `shouldContain` [WqPartitionSpecEmpty]
-    it "lowers ordering, provisioning, and raw group-key projection" $ do
-      spec <- specOf "test/fixtures/reservation-work.keiro"
-      case [workqueue | NWorkqueue workqueue <- specNodes spec] of
-        workqueue : _ -> do
-          let modules = scaffoldWorkqueue (defaultContext (specContext spec)) workqueue
-              queue = generatedTextEndingIn "Queue.hs" modules
-              policy = generatedTextEndingIn "QueuePolicy.hs" modules
-          queue `shouldSatisfy` T.isInfixOf "groupKeyFor payload = payload.reservationId"
-          policy `shouldSatisfy` T.isInfixOf "jobOrdering = FifoThroughput"
-          policy `shouldSatisfy` T.isInfixOf "withFifoIndexProvision (standardProvision)"
-          firewallBreaches modules `shouldBe` []
-        [] -> expectationFailure "reservation-work fixture has no workqueue"
-
-  describe "readmodel (EP-107)" $ do
-    it "parses and round-trips first-class read models" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      case [readModel | NReadModel readModel <- specNodes spec] of
-        [subscriptionModel, inlineModel] -> do
-          rmName subscriptionModel `shouldBe` "transfer_decisions"
-          rmColumns subscriptionModel
-            `shouldBe` [ RmColumn "reservation_id" "text" True,
-                         RmColumn "hospital_id" "text" True,
-                         RmColumn "status" "text" True,
-                         RmColumn "decided_at" "timestamptz" False
-                       ]
-          rmScope subscriptionModel `shouldBe` Just (RmCategory "reservation")
-          rmFeed subscriptionModel `shouldBe` RmSubscription
-          rmSubscription subscriptionModel `shouldBe` Just "hospital-capacity-transfer-decisions-sub"
-          rmName inlineModel `shouldBe` "subscriptions"
-          rmScope inlineModel `shouldBe` Nothing
-          rmFeed inlineModel `shouldBe` RmInline
-        nodes -> expectationFailure ("expected two readmodel nodes, got " <> show (length nodes))
-      parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts an aggregate projection without a consistency clause" $ do
-      spec <- parseInlineSpec "<projection-without-consistency>" projectionWithoutConsistencySpec
-      case [projection | NAggregate aggregate <- specNodes spec, Just projection <- [aggProjection aggregate]] of
-        [projection] -> projConsistency projection `shouldBe` Nothing
-        projections -> expectationFailure ("expected one projection, got " <> show (length projections))
-    it "pins the canonical UTF-8 shape digest and runtime identities" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      case [readModel | NReadModel readModel <- specNodes spec] of
-        (subscriptionModel : inlineModel : _) -> do
-          canonicalShape subscriptionModel
-            `shouldBe` "transfer_decisions|reservation_id:text:req|hospital_id:text:req|status:text:req|decided_at:timestamptz:null"
-          deriveShapeHash subscriptionModel `shouldBe` "fnv1a:3717f6d9e3c44bd6"
-          deriveShapeHash inlineModel `shouldBe` "fnv1a:f54d9bb2f40a6738"
-          registryNameFor (specContext spec) subscriptionModel `shouldBe` "hospital-capacity-transfer-decisions"
-          subscriptionNameFor (specContext spec) subscriptionModel `shouldBe` "hospital-capacity-transfer-decisions-sub"
-          subscriptionNameFor "billing" inlineModel `shouldBe` "billing-subscriptions-sub"
-        nodes -> expectationFailure ("expected readmodel nodes, got " <> show (length nodes))
-    it "accepts the positive readmodel fixture with all references resolved" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      validateSpec spec `shouldBe` []
-    it "rejects shape drift and unknown SQL column types" $ do
-      codes <- errorCodesOf "test/fixtures/readmodel-shape-drift.keiro"
-      codes `shouldContain` [RmShapeHashDrift, RmUnknownColumnType]
-    it "rejects Strong on inline and standalone projections" $ do
-      inlineCodes <- errorCodesOf "test/fixtures/readmodel-strong-inline.keiro"
-      inlineCodes `shouldContain` [RmStrongInlineOnly]
-      standalone <- specOf "test/fixtures/readmodel-strong-standalone.keiro"
-      let diagnostics = validateSpec standalone
-      map code diagnostics `shouldContain` [RmStrongInlineOnly, RmProjectionWithoutNode]
-      [severity diagnostic | diagnostic <- diagnostics, code diagnostic == RmProjectionWithoutNode]
-        `shouldBe` [Warning]
-    it "rejects scope without Strong and an unreferenced inline feed" $ do
-      scopeCodes <- errorCodesOf "test/fixtures/readmodel-scope-eventual.keiro"
-      scopeCodes `shouldContain` [RmScopeWithoutStrong]
-      inlineCodes <- errorCodesOf "test/fixtures/readmodel-inline-unreferenced.keiro"
-      inlineCodes `shouldContain` [RmInlineFeedUnreferenced]
-    it "rejects projection consistency conflicts" $ do
-      codes <- errorCodesOf "test/fixtures/readmodel-consistency-conflict.keiro"
-      codes `shouldContain` [RmConsistencyConflict]
-    it "resolves query read models and validates query consistency" $ do
-      codes <- errorCodesOf "test/fixtures/readmodel-query-unresolved.keiro"
-      codes `shouldContain` [QueryUnresolvedReadModel, QueryConsistencyInvalid]
-    it "resolves dispatch read models and declared dedup columns" $ do
-      codes <- errorCodesOf "test/fixtures/readmodel-dispatch-unresolved.keiro"
-      codes `shouldContain` [DispatchReadModelUnresolved, DispatchReadModelFieldUnknown]
-    it "scaffolds runtime records, rebuild helpers, async wiring, and typed holes" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      let ctx = defaultContext (specContext spec)
-          readModels = [readModel | NReadModel readModel <- specNodes spec]
-          modules = concatMap (scaffoldReadModel ctx) readModels
-          transfer = generatedTextEndingIn "Transfer_decisions/ReadModel.hs" modules
-          inline = generatedTextEndingIn "Subscriptions/ReadModel.hs" modules
-          transferHoles = [moduleText m | m <- modules, "Transfer_decisions/ReadModelHoles.hs" `T.isSuffixOf` T.pack (modulePath m)]
-      length modules `shouldBe` 6
-      length [m | m <- modules, kind m == Generated] `shouldBe` 4
-      length [m | m <- modules, kind m == HoleStub] `shouldBe` 2
-      firewallBreaches modules `shouldBe` []
-      transfer `shouldSatisfy` T.isInfixOf "registerTransferDecisions"
-      transfer `shouldSatisfy` T.isInfixOf "Rebuild.startRebuild transferDecisionsReadModel [\"hospital-capacity-transfer-decisions-async\"]"
-      transfer `shouldSatisfy` T.isInfixOf "strongScope = CategoryHead \"reservation\""
-      transfer `shouldSatisfy` T.isInfixOf "transferDecisionsAsyncProjection"
-      inline `shouldSatisfy` T.isInfixOf "Rebuild.startRebuild subscriptionsReadModel []"
-      inline `shouldNotSatisfy` T.isInfixOf "AsyncProjection"
-      transferHoles `shouldSatisfy` any (T.isInfixOf "RecordedEvent -> Tx.Transaction ()")
-    it "threads qualified table and column guidance into aggregate projection holes" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        [aggregate] -> do
-          let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
-              holes = [moduleText m | m <- modules, kind m == HoleStub]
-              projection = generatedTextEndingIn "Projection.hs" modules
-          holes `shouldSatisfy` any (T.isInfixOf "subscriptionsQualifiedTable")
-          holes `shouldSatisfy` any (T.isInfixOf "Table: \"billing\".\"subscriptions\"")
-          projection `shouldSatisfy` T.isInfixOf "ReadModelTable.subscriptionsQualifiedTable"
-        aggregates -> expectationFailure ("expected one aggregate, got " <> show (length aggregates))
-    it "emits runtime-free derivation facts for each read model" $ do
-      spec <- specOf "test/fixtures/readmodel.keiro"
-      case [readModel | NReadModel readModel <- specNodes spec] of
-        (subscriptionModel : _) -> do
-          let modules = harnessReadModel (defaultContext (specContext spec)) subscriptionModel
-              harnessText = generatedTextEndingIn "ReadModelHarness.hs" modules
-          length modules `shouldBe` 1
-          firewallBreaches modules `shouldBe` []
-          harnessText `shouldSatisfy` T.isInfixOf "(\"shapeHash\", \"fnv1a:3717f6d9e3c44bd6\", \"fnv1a:3717f6d9e3c44bd6\")"
-          harnessText `shouldSatisfy` T.isInfixOf "(\"strongScope\", \"CategoryHead reservation\", \"CategoryHead reservation\")"
-          harnessText `shouldSatisfy` T.isInfixOf "runReadModelFacts"
-        nodes -> expectationFailure ("expected readmodel nodes, got " <> show (length nodes))
-
-  describe "workflow/operation (EP-6)" $ do
-    it "round-trips the workflow spec through parse . pretty" $ do
-      input <- readTestText "test/fixtures/workflow.keiro"
-      case parseSpec "in" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
-    it "accepts the workflow spec (await<->signal matches, run resolves)" $ do
-      codes <- errorCodesOf "test/fixtures/workflow.keiro"
-      codes `shouldBe` []
-    it "rejects a signal label with no matching await as AwaitSignalMismatch" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-signal-mismatch.keiro"
-      codes `shouldContain` [AwaitSignalMismatch]
-    it "rejects duplicate workflow labels" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-dup-label.keiro"
-      codes `shouldContain` [WorkflowDuplicateLabel]
-    it "rejects unresolved workflow id and sleep fields" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-unresolved-fields.keiro"
-      codes `shouldContain` [WorkflowIdFieldUnresolved, WorkflowSleepDelayUnresolved]
-    it "validates rule domains, totality, case constructors, and bodies" $ do
-      unresolved <- errorCodesOf "test/fixtures/rule-bad-domain.keiro"
-      unresolved `shouldBe` [RuleDomainUnresolved]
-      codes <- errorCodesOf "test/fixtures/rule-not-total.keiro"
-      mapM_
-        (\expected -> codes `shouldContain` [expected])
-        [RuleNotTotal, RuleCaseUnknownCtor, ClockSampled, GuardAtomOutOfScope]
-    it "rejects unresolved command operation references" $ do
-      codes <- errorCodesOf "test/fixtures/operation-ghost-aggregate.keiro"
-      codes `shouldContain` [OperationUnresolvedRef]
-    it "rejects a signal value type that differs from its await" $ do
-      codes <- errorCodesOf "test/fixtures/operation-signal-value.keiro"
-      codes `shouldContain` [AwaitSignalValueMismatch]
-    it "round-trips guarded patches and terminal continueAsNew" $ do
-      input <- readTestText "test/fixtures/workflow-evolution.keiro"
-      case parseSpec "workflow-evolution" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> do
-          parseSpec "workflow-evolution" (renderSpec spec) `shouldBe` Right spec
-          errorCodes spec `shouldBe` []
-    it "rejects duplicate patch ids anywhere in the workflow body" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-patch-dup.keiro"
-      codes `shouldBe` [WorkflowPatchDuplicate]
-    it "rejects non-terminal and nested continueAsNew" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-can-mid.keiro"
-      codes `shouldBe` [WorkflowContinueAsNewNotTerminal, WorkflowContinueAsNewNotTerminal]
-    it "rejects a colon in a patch id with a workflow diagnostic" $ do
-      codes <- errorCodesOf "test/fixtures/workflow-patch-colon.keiro"
-      codes `shouldBe` [WorkflowPatchIdInvalid]
-    it "lowers patch facts and live runtime declarations" $ do
-      spec <- specOf "test/fixtures/workflow-evolution.keiro"
-      case [workflow | NWorkflow workflow <- specNodes spec] of
-        [workflow] -> do
-          let modules = harnessWorkflow (defaultContext (specContext spec)) workflow
-              facts = generatedTextEndingIn "WorkflowFacts.hs" modules
-              runtime = generatedTextEndingIn "WorkflowRuntime.hs" modules
-          facts `shouldSatisfy` T.isInfixOf "patch:fraud-check-v2(step:fraud-check)"
-          facts `shouldSatisfy` T.isInfixOf "continueAsNew:RolloverSeed"
-          facts `shouldSatisfy` T.isInfixOf "(\"patches\", \"fraud-check-v2\")"
-          runtime `shouldSatisfy` T.isInfixOf "declaredPatches = Set.fromList [PatchId \"fraud-check-v2\"]"
-          runtime `shouldSatisfy` T.isInfixOf "opts{activePatches = declaredPatches}"
-        workflows -> expectationFailure ("expected one workflow, got " <> show (length workflows))
-
-  describe "replay impact" $ do
-    it "treats new events and transitions as replay-neutral" $ do
-      old <- specOf "test/fixtures/reservation.keiro"
-      let aggregate = onlyAggregate old
-      case (aggEvents aggregate, aggTransitions aggregate) of
-        (event : _, transition : _) -> do
-          let newEvent =
-                event
-                  { evName = "ReservationReviewed",
-                    evLoc = noLoc
-                  }
-              newTransition =
-                transition
-                  { tEmits = ["ReservationReviewed"],
-                    tLoc = noLoc
-                  }
-              new =
-                modifyAggregate
-                  "Reservation"
-                  ( \candidate ->
-                      candidate
-                        { aggEvents = aggEvents candidate <> [newEvent],
-                          aggTransitions = aggTransitions candidate <> [newTransition]
-                        }
-                  )
-                  old
-          ReplayImpact.replayImpact old new `shouldBe` ReplayNeutral
-        _ -> expectationFailure "reservation fixture must contain an event and transition"
-
-    it "narrows a guard edit to that transition's event types" $ do
-      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened.keiro"
-      impact
-        `shouldBe` ReplayAffected
-          ( Map.singleton
-              "Reservation"
-              AggregateImpact
-                { eventTypes = Set.singleton "TransferReservationCreated",
-                  includeSnapshotStreams = True
-                }
-          )
-
-    it "proves a syntactic guard loosening replay-neutral" $ do
-      old <- specOf "test/fixtures/reservation.keiro"
-      let loosened =
-            modifyAggregate
-              "Reservation"
-              ( \aggregate ->
-                  aggregate
-                    { aggTransitions =
-                        [ transition {tGuard = Nothing}
-                        | transition <- aggTransitions aggregate
-                        ]
-                    }
-              )
-              old
-      ReplayImpact.replayImpact old loosened `shouldBe` ReplayNeutral
-
-    it "marks every existing event when the aggregate wire convention changes" $ do
-      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-wire.keiro"
-      case impact of
-        ReplayAffected aggregates ->
-          ReplayImpact.eventTypes <$> Map.lookup "Reservation" aggregates
-            `shouldBe` Just (Set.fromList ["TransferReservationCreated", "TransferReservationConfirmed"])
-        ReplayNeutral -> expectationFailure "expected a wire-clause replay impact"
-
-    it "includes snapshot streams when a write expression changes" $ do
-      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-foldchange.keiro"
-      case impact of
-        ReplayAffected aggregates ->
-          includeSnapshotStreams <$> Map.lookup "Reservation" aggregates
-            `shouldBe` Just True
-        ReplayNeutral -> expectationFailure "expected a fold replay impact"
-
-    it "detects codec evolution and ignores formatting-only rewrites" $ do
-      changed <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v2.keiro"
-      changed `shouldSatisfy` (/= ReplayNeutral)
-      old <- specOf "test/fixtures/reservation.keiro"
-      formatted <- parseInlineSpec "<formatted>" (renderSpec old)
-      ReplayImpact.replayImpact old formatted `shouldBe` ReplayNeutral
-
-    it "names mapped nested event and snapshot roots while ignoring Haskell-only changes" $ do
-      nested <- replayImpactFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-nested-propagation.keiro"
-      case nested of
-        ReplayAffected aggregates ->
-          Map.lookup "Catalog" aggregates
-            `shouldBe` Just AggregateImpact {eventTypes = Set.singleton "ArtifactObserved", includeSnapshotStreams = True}
-        ReplayNeutral -> expectationFailure "expected nested mapped wire change to affect replay"
-      sourceOnly <- replayImpactFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-haskell-rename.keiro"
-      sourceOnly `shouldBe` ReplayNeutral
-
-    it "generates one context target for every aggregate, including the process saga" $ do
-      spec <- specOf "test/fixtures/surge-service.keiro"
-      case scaffoldReplayAudit (defaultContext (specContext spec)) spec of
-        [assembly] -> do
-          modulePath assembly `shouldBe` "Generated/SurgeDemo/ReplayAudit.hs"
-          moduleText assembly `shouldSatisfy` T.isInfixOf "Hospital.hospitalEventStream"
-          moduleText assembly `shouldSatisfy` T.isInfixOf "Surge.surgeEventStream"
-          T.count "      AuditTarget" (moduleText assembly) `shouldBe` 2
-        assemblies -> expectationFailure ("expected one replay-audit assembly, got " <> show (length assemblies))
-
-  describe "diff (evolution classification)" $ do
-    it "covers every node family exactly once and explains exclusions" $ do
-      sort (map fst familyRegistry) `shouldBe` ([minBound .. maxBound] :: [NodeFamily])
-      [reason | (_, OutOfDiffScope reason) <- familyRegistry, T.null reason] `shouldBe` []
-    it "derives every exercised headline from its vector under the default gate" $ do
-      changes <-
-        concat
-          <$> mapM
-            (uncurry diffFixtures)
-            [ ("test/fixtures/reservation.keiro", "test/fixtures/reservation-fieldadd.keiro"),
-              ("test/fixtures/reservation.keiro", "test/fixtures/reservation-v2.keiro"),
-              ("test/fixtures/reservation.keiro", "test/fixtures/reservation-enumadd.keiro"),
-              ("test/fixtures/contract.keiro", "test/fixtures/contract-fieldadd.keiro"),
-              ("test/fixtures/reservation-work.keiro", "test/fixtures/reservation-work-rename.keiro")
-            ]
-      forM_ changes $ \change ->
-        do
-          deriveLabel defaultGate (ckVector (kindOfChange change))
-            `shouldBe` labelOfChange change
-          gatedBreaking defaultGate change `shouldBe` isBreaking change
-    it "never removes a breaking result when the gate grows" $
-      property $
-        forAll genCompatibilityVector $ \compatibility ->
-          forAll genSurfaceSet $ \gate ->
-            forAll genSurfaceSet $ \extra ->
-              deriveLabel gate compatibility
-                == LabelBreaking
-                  ==> deriveLabel (gate <> extra) compatibility
-                == LabelBreaking
-    it "renders the consumer-neutral matrix with separate private, snapshot, and public surfaces" $ do
-      changes <- diffFixtures "test/fixtures/compatibility-vector-old.keiro" "test/fixtures/compatibility-vector-new.keiro"
-      golden <- readTestText "test/fixtures/compatibility-vector.diff.golden"
-      let rendered = T.intercalate "\n" (map renderFinding changes)
-          explained = T.intercalate "\n" (map renderExplainBlock changes)
-          reportJson = T.pack (show (Aeson.toJSON (diffReport defaultGate changes)))
-      T.stripEnd rendered `shouldBe` T.stripEnd golden
-      rendered `shouldSatisfy` T.isInfixOf "Reservation.event.TransferReservationCreated.patientAcuity"
-      rendered `shouldSatisfy` T.isInfixOf "old-binary-read-new-events=breaking"
-      rendered `shouldSatisfy` T.isInfixOf "snapshot-hydration=advisory"
-      rendered `shouldSatisfy` T.isInfixOf "public-consumer=breaking"
-      explained `shouldSatisfy` T.isInfixOf "invalidate and rebuild snapshots"
-      reportJson `shouldSatisfy` T.isInfixOf "keiro-dsl/diff-report/1"
-      reportJson `shouldSatisfy` T.isInfixOf "Reservation.event.TransferReservationCreated.patientAcuity"
-      let eventEnumFindings =
-            [ change
-            | change@(Advisory kind) <- changes,
-              ckCode kind == EnumCtorAdded,
-              verdictFor OldBinaryReadNewEvents (ckVector kind) == VBreaking
-            ]
-      eventEnumFindings `shouldSatisfy` all (not . gatedBreaking defaultGate)
-      eventEnumFindings `shouldSatisfy` all (gatedBreaking (gateWith [OldBinaryReadNewEvents]))
-      forM_ changes $ \change ->
-        remediationFor (ckContext (kindOfChange change)) (ckCode (kindOfChange change))
-          `shouldSatisfy` (not . null)
-    it "rejects unknown --gate values with the valid surface list" $ do
-      parseSurfaceName "mystery-surface"
-        `shouldSatisfy` either (T.isInfixOf "old-binary-read-new-events" . T.pack) (const False)
-    it "covers the mapped evolution matrix with stable codes and non-empty remedies" $ do
-      let cases =
-            [ ("consumer-types-fieldadd-default.keiro", MappedFieldAddedWithDefault),
-              ("consumer-types-fieldadd-nodefault.keiro", MappedFieldAddedNoDefault),
-              ("consumer-types-fieldremove.keiro", MappedFieldRemoved),
-              ("consumer-types-wirekey.keiro", MappedWireKeyChanged),
-              ("consumer-types-haskell-rename.keiro", MappedHaskellSourceChanged),
-              ("consumer-types-binding-change.keiro", MappedBindingChanged),
-              ("consumer-types-fixtures-change.keiro", MappedFixturesChanged),
-              ("consumer-types-initial-change.keiro", MappedInitialChanged),
-              ("consumer-types-armadd.keiro", MappedArmAdded),
-              ("consumer-types-tagchange.keiro", MappedArmTagChanged),
-              ("consumer-types-enumadd.keiro", MappedEnumValueAdded),
-              ("consumer-types-enumremove.keiro", MappedEnumValueRemoved),
-              ("consumer-types-enumspelling.keiro", MappedEnumSpellingChanged),
-              ("consumer-types-encoding.keiro", MappedUnionEncodingChanged),
-              ("consumer-types-opaque-version.keiro", MappedOpaqueCodecChanged),
-              ("consumer-types-mode-cross.keiro", MappedModeCrossed),
-              ("consumer-types-nested-propagation.keiro", MappedArmTagChanged)
-            ]
-      forM_ cases $ \(fixture, expectedCode) -> do
-        changes <- diffFixtures "test/fixtures/consumer-types.keiro" ("test/fixtures/" <> fixture)
-        map (ckCode . kindOfChange) changes `shouldContain` [expectedCode]
-        forM_ changes $ \change ->
-          remediationFor (ckContext (kindOfChange change)) (ckCode (kindOfChange change))
-            `shouldSatisfy` (not . null)
-    it "separates mapped event migration, snapshot invalidation, and directional rollout" $ do
-      breakingAdd <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-fieldadd-nodefault.keiro"
-      let noDefault = [change | change <- breakingAdd, ckCode (kindOfChange change) == MappedFieldAddedNoDefault]
-      [ckFacet kind | Breaking kind <- noDefault] `shouldContain` ["mapped-event"]
-      [ckFacet kind | Advisory kind <- noDefault] `shouldContain` ["mapped-register"]
-      defaulted <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-fieldadd-default.keiro"
-      [change | change <- defaulted, isBreaking change] `shouldBe` []
-      let eventDefaults = [kind | Advisory kind <- defaulted, ckCode kind == MappedFieldAddedWithDefault, ckFacet kind == "mapped-event"]
-      eventDefaults `shouldSatisfy` any ((== VBreaking) . verdictFor OldBinaryReadNewEvents . ckVector)
-      armAdded <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-armadd.keiro"
-      [change | change <- armAdded, isBreaking change] `shouldBe` []
-      [kind | Advisory kind <- armAdded, ckCode kind == MappedArmAdded, ckFacet kind == "mapped-event"]
-        `shouldSatisfy` any ((== VBreaking) . verdictFor OldBinaryReadNewEvents . ckVector)
-    it "propagates a nested mapped leaf to complete command, event, and register paths" $ do
-      changes <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-nested-propagation.keiro"
-      let subjects =
-            [ ckSubject kind
-            | change <- changes,
-              let kind = kindOfChange change,
-              ckCode kind == MappedArmTagChanged
-            ]
-      subjects
-        `shouldContain` [ "Catalog command ObserveArtifact .artifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]",
-                          "Catalog event ArtifactObserved .artifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]",
-                          "Catalog register currentArtifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]"
-                        ]
-    it "classifies every remaining mapped field and declaration evolution row" $ do
-      base <- specOf "test/fixtures/consumer-types.keiro"
-      let mutationCodes =
-            [ (mapArtifactNamedField "key" (\field -> field {wfType = TInt}) base, MappedFieldTypeChanged),
-              (mapArtifactNamedField "key" (\field -> field {wfPresence = POptional, wfOnMissing = Just (OmText "")}) base, MappedPresenceChanged),
-              (mapArtifactNamedField "key" (\field -> field {wfType = TOptional TText}) base, MappedNullabilityChanged),
-              (mapArtifactNamedField "description" (\field -> field {wfOnMissing = Nothing}) base, MappedDefaultRemoved),
-              (mapArtifactNamedField "count" (\field -> field {wfOnMissing = Just (OmInt 1)}) base, MappedDefaultChanged),
-              (mapMappedStructural "ArtifactInfo" renameMappedRecordConstructor base, MappedRecordConstructorChanged),
-              (mapMappedStructural "ArtifactInfo" changeMappedCanonical base, MappedCanonicalTypeChanged)
-            ]
-      forM_ mutationCodes $ \(candidate, expectedCode) ->
-        map (ckCode . kindOfChange) (diffSpecs base candidate) `shouldContain` [expectedCode]
-      let declarationA = completeStructural "A" (recordShape [TText])
-          declarationB = completeStructural "B" (recordShape [TInt])
-          onlyA = mappedSpec [declarationA]
-          withB = mappedSpec [declarationA, declarationB]
-      map (ckCode . kindOfChange) (diffSpecs onlyA withB) `shouldContain` [MappedDeclAdded]
-      map (ckCode . kindOfChange) (diffSpecs withB onlyA) `shouldContain` [MappedDeclRemoved]
-      diffSpecs base (mapArtifactNamedField "key" (\field -> field {wfHaskell = "renamedKey"}) base)
-        `shouldBe` []
-    it "visits every mapped wire mutation and reports every complete root path" $ do
-      base <- specOf "test/fixtures/consumer-types.keiro"
-      let mutations = mappedWireMutations base
-      mutations `shouldSatisfy` (not . null)
-      visited <- fmap Set.unions . forM mutations $ \mutation -> do
-        let changes =
-              [ change
-              | change <- diffSpecs base (mmCandidate mutation),
-                ckCode (kindOfChange change) == mmCode mutation
-              ]
-            actualSubjects = Set.fromList (map (ckSubject . kindOfChange) changes)
-        changes `shouldSatisfy` any (not . isAdditiveChange)
-        actualSubjects `shouldBe` mmExpectedSubjects mutation
-        pure actualSubjects
-      visited `shouldBe` Set.unions (map mmExpectedSubjects mutations)
-    it "reports the exact ingredient code when every required mapped fact is deleted" $ do
-      base <- specOf "test/fixtures/consumer-types.keiro"
-      forM_ (mappedIngredientMutations base) $ \(candidate, expectedCode) ->
-        errorCodes candidate `shouldContain` [expectedCode]
-    it "classifies a field added without a version bump as BREAKING" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldadd.keiro"
-      any isBreaking cs `shouldBe` True
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldAddedWithoutBump]
-    it "classifies the same field wrapped as v2 + upcaster as ADDITIVE" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v2.keiro"
-      any isBreaking cs `shouldBe` False
-      [ck | Additive ck <- cs] `shouldSatisfy` any ((== "TransferReservationCreated") . ckSubject)
-    it "reports no breaking change when the spec is unchanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation.keiro"
-      any isBreaking cs `shouldBe` False
-    it "classifies a direct event field type change as EvtFieldTypeChanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldtype.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldTypeChanged]
-    it "resolves fields(Command) before comparing event field types" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-cmdfieldtype.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldTypeChanged]
-    it "uses EvtFieldRemovedSameVersion for an unchanged-version removal" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldremove.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldRemovedSameVersion]
-    it "uses EvtVersionDecreased for a version decrease" $ do
-      cs <- diffFixtures "test/fixtures/reservation-v2.keiro" "test/fixtures/reservation.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtVersionDecreased]
-    it "rejects a v1 to v3 jump whose only upcaster starts at v2" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v3-dangling.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EvtVersionMissingUpcaster]
-    it "classifies a vanished historical upcaster rung as UpcasterChainGap" $ do
-      cs <- diffFixtures "test/fixtures/reservation-v2.keiro" "test/fixtures/reservation-chain-gap.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [UpcasterChainGap]
-    it "classifies an enum constructor removal as EnumCtorRemoved" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumdrop.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EnumCtorRemoved]
-    it "classifies an enum wire-spelling change as EnumWireSpellingChanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumwire.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [EnumWireSpellingChanged]
-    it "classifies an enum constructor addition per use site as advisory" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumadd.keiro"
-      any isBreaking cs `shouldBe` False
-      let enumFindings = [k | Advisory k <- cs, ckCode k == EnumCtorAdded]
-      [ckSubject k | k <- enumFindings] `shouldContain` ["BlackTag"]
-      [verdictFor SnapshotHydration (ckVector k) | k <- enumFindings]
-        `shouldContain` [VAdvisory]
-    it "classifies an effective wire convention change as WireSpecChanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-wire.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WireSpecChanged]
-    it "advises when the aggregate fold surface changes" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-foldchange.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [AggFoldSurfaceChanged]
-    it "advises on hazardous deprecation and reports un-deprecation" $ do
-      deprecated <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-deprecated.keiro"
-      any isBreaking deprecated `shouldBe` False
-      [ckCode k | Advisory k <- deprecated] `shouldContain` [DeprecatedEventReplayHazard]
-      restored <- diffFixtures "test/fixtures/reservation-deprecated.keiro" "test/fixtures/reservation.keiro"
-      any isAdvisory restored `shouldBe` True
-      [ckCode k | Advisory k <- restored] `shouldContain` [EventUndeprecated]
-    it "recognises replay-only deprecation as a replay-safe retirement cutover" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-deprecated-replay-only.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [EventRetirementInProgress]
-      [ckCode k | Advisory k <- cs] `shouldNotContain` [DeprecatedEventReplayHazard]
-    it "advises when event retirement starts" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-retiring.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [EventRetirementInProgress]
-    it "does not recommend decode-only deprecation for an event removal" $ do
-      old <- specOf "test/fixtures/reservation.keiro"
-      let new =
-            old
-              { specNodes =
-                  [ case node of
-                      NAggregate aggregate ->
-                        NAggregate
-                          aggregate
-                            { aggEvents =
-                                [ event
-                                | event <- aggEvents aggregate,
-                                  evName event /= "TransferReservationConfirmed"
-                                ]
-                            }
-                      _ -> node
-                  | node <- specNodes old
-                  ]
-              }
-          removals = [change | change@(Breaking kind) <- diffSpecs old new, ckCode kind == EvtRemovedNotDeprecated]
-      removals `shouldSatisfy` (not . null)
-      [ckDetail kind | Breaking kind <- removals]
-        `shouldSatisfy` all (not . T.isInfixOf "so old payloads still decode")
-    it "prints a paste-ready replay-only twin when a guard tightens (plan 143)" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened.keiro"
-      any isBreaking cs `shouldBe` False
-      let advisories = [k | Advisory k <- cs, ckCode k == AggGuardTightened]
-      map ckSubject advisories `shouldBe` ["Unrequested -- RequestTransferReservation"]
-      detail <- case advisories of
-        [k] -> pure (ckDetail k)
-        other -> expectationFailure ("expected one advisory, got " <> show other) >> pure ""
-      detail `shouldSatisfy` T.isInfixOf "replay-only Unrequested -- RequestTransferReservation"
-      -- The printed twin is paste-ready: appended to the new spec it
-      -- parses, validates without errors, and silences the advisory.
-      tightened <- readTestText "test/fixtures/reservation-guard-tightened.keiro"
-      let twinText = snd (T.breakOnEnd "\n\n" detail)
-          pasted = tightened <> "\n" <> twinText <> "\n"
-      case parseSpec "<pasted-twin>" pasted of
-        Left err -> expectationFailure (T.unpack err)
-        Right pastedSpec -> do
-          [code d | d <- validateSpec pastedSpec, severity d == Error] `shouldBe` []
-          base <- specOf "test/fixtures/reservation.keiro"
-          [k | Advisory k <- diffSpecs base pastedSpec, ckCode k == AggGuardTightened]
-            `shouldBe` []
-    it "omits the twin advisory when the twin is already present (plan 143)" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened-twin.keiro"
-      [k | Advisory k <- cs, ckCode k == AggGuardTightened] `shouldBe` []
-    it "classifies a removed contract event as ContractEventRemoved" $ do
-      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-eventdrop.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [ContractEventRemoved]
-    it "classifies contract field type changes and unversioned additions as ContractFieldChanged" $ do
-      changed <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-fieldtype.keiro"
-      [ckCode k | Breaking k <- changed] `shouldContain` [ContractFieldChanged]
-      added <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-fieldadd.keiro"
-      [ckCode k | Breaking k <- added] `shouldContain` [ContractFieldChanged]
-    it "reports a field addition with a contract version bump as an advisory" $ do
-      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-bump-fieldadd.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [ContractSchemaVersionBumped]
-    it "classifies a contract schema version decrease separately" $ do
-      cs <- diffFixtures "test/fixtures/contract-bump-fieldadd.keiro" "test/fixtures/contract.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [ContractSchemaVersionDecreased]
-    it "classifies contract topic and discriminator changes separately" $ do
-      topic <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-topic.keiro"
-      [ckCode k | Breaking k <- topic] `shouldContain` [ContractTopicChanged]
-      discriminator <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-discriminator.keiro"
-      [ckCode k | Breaking k <- discriminator] `shouldContain` [ContractDiscriminatorChanged]
-    it "classifies a new contract event as additive" $ do
-      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-eventadd.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckSubject k | Additive k <- cs] `shouldContain` ["IncidentTransferNeedCancelled"]
-    it "classifies workqueue wire names, types, and required additions as WqPayloadFieldChanged" $ do
-      wire <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-wirename.keiro"
-      [ckCode k | Breaking k <- wire] `shouldContain` [WqPayloadFieldChanged]
-      fieldTypeChange <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-fieldtype.keiro"
-      [ckCode k | Breaking k <- fieldTypeChange] `shouldContain` [WqPayloadFieldChanged]
-      required <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-reqfield.keiro"
-      [ckCode k | Breaking k <- required] `shouldContain` [WqPayloadFieldChanged]
-    it "classifies a new optional workqueue payload field as additive" $ do
-      cs <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-optfield.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckSubject k | Additive k <- cs] `shouldContain` ["note"]
-    it "classifies workqueue ordering changes as breaking delivery-contract changes" $ do
-      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-ordering-change.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WqOrderingChanged]
-      [ckDetail k | Breaking k <- cs, ckCode k == WqOrderingChanged]
-        `shouldSatisfy` any (T.isInfixOf "delivery-order contract")
-    it "classifies workqueue provision changes as operational migrations" $ do
-      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-provision-change.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WqProvisionChanged]
-      [ckDetail k | Breaking k <- cs, ckCode k == WqProvisionChanged]
-        `shouldSatisfy` any (T.isInfixOf "migrate the existing queue operationally")
-    it "classifies workqueue group-key changes as breaking repartitioning" $ do
-      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-group-key-change.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WqGroupKeyChanged]
-      [ckDetail k | Breaking k <- cs, ckCode k == WqGroupKeyChanged]
-        `shouldSatisfy` any (T.isInfixOf "re-partitioned")
-    it "classifies a process input type change as ProcessInputChanged" $ do
-      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-inputtype.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [ProcessInputChanged]
-    it "classifies workflow input and output changes as WorkflowShapeChanged" $ do
-      input <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-inputfield.keiro"
-      [ckCode k | Breaking k <- input] `shouldContain` [WorkflowShapeChanged]
-      output <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-output.keiro"
-      [ckCode k | Breaking k <- output] `shouldContain` [WorkflowShapeChanged]
-    it "classifies workflow relabeling and appends as WorkflowBodyChanged" $ do
-      relabeled <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-body.keiro"
-      [ckCode k | Breaking k <- relabeled] `shouldContain` [WorkflowBodyChanged]
-      appended <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-stepadd.keiro"
-      [ckCode k | Breaking k <- appended] `shouldContain` [WorkflowBodyChanged]
-      [ckDetail k | Breaking k <- appended, ckCode k == WorkflowBodyChanged]
-        `shouldSatisfy` any (T.isInfixOf "new patch guard")
-    it "classifies a body addition wholly guarded by a new patch as additive" $ do
-      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-evolution-diff.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckSubject k | Additive k <- cs, ckFacet k == "workflow-patch"] `shouldContain` ["fraud-check-v2"]
-      [ckSubject k | Additive k <- cs, ckFacet k == "workflow-continue-as-new"] `shouldContain` ["RolloverSeed"]
-    it "classifies removing an existing patch as breaking" $ do
-      cs <- diffFixtures "test/fixtures/workflow-evolution-diff.keiro" "test/fixtures/workflow-continue.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WorkflowPatchRemoved]
-      [ckDetail k | Breaking k <- cs, ckCode k == WorkflowPatchRemoved]
-        `shouldSatisfy` any (T.isInfixOf "cannot prove")
-    it "classifies terminal continueAsNew append as additive and seed drift as breaking" $ do
-      appended <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-continue.keiro"
-      any isBreaking appended `shouldBe` False
-      [ckFacet k | Additive k <- appended] `shouldContain` ["workflow-continue-as-new"]
-      changed <- diffFixtures "test/fixtures/workflow-continue.keiro" "test/fixtures/workflow-continue-seed-v2.keiro"
-      [ckCode k | Breaking k <- changed] `shouldContain` [WorkflowContinueSeedChanged]
-      [ckDetail k | Breaking k <- changed, ckCode k == WorkflowContinueSeedChanged]
-        `shouldSatisfy` any (T.isInfixOf "restoreSeed")
-    it "classifies a workflow stable-name change as WorkflowStableNameChanged" $ do
-      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-rename.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [WorkflowStableNameChanged]
-    it "classifies workflow id-derivation changes as DerivedIdentityChanged" $ do
-      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-idfield.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [DerivedIdentityChanged]
-    it "classifies an id prefix change as IdPrefixChanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-idprefix.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [IdPrefixChanged]
-    it "classifies intake dedupe key and policy changes as DedupeIdentityChanged" $ do
-      policy <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-dedupepolicy.keiro"
-      [ckCode k | Breaking k <- policy] `shouldContain` [DedupeIdentityChanged]
-      key <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-dedupekey.keiro"
-      [ckCode k | Breaking k <- key] `shouldContain` [DedupeIdentityChanged]
-    it "reports intake decode-posture changes as warnings" $ do
-      cs <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-decode.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [DecodePostureChanged]
-      [ckCode k | Advisory k <- cs] `shouldContain` [IntakePersistenceChanged]
-    it "classifies process and timer derivation changes as DerivedIdentityChanged" $ do
-      processName <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-procname.keiro"
-      [ckCode k | Breaking k <- processName] `shouldContain` [DerivedIdentityChanged]
-      timerId <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-timerid.keiro"
-      [ckCode k | Breaking k <- timerId] `shouldContain` [DerivedIdentityChanged]
-      base <- specOf "test/fixtures/hospital-surge.keiro"
-      let categoryChange = diffSpecs base (modifyProcess "HospitalSurge" (\process -> process {procSaga = (procSaga process) {sagaCategory = "hospitalSurgeV2"}}) base)
-      [ckCode k | Breaking k <- categoryChange] `shouldContain` [DerivedIdentityChanged]
-    it "classifies router stable names, keys, and targets as identity-bearing" $ do
-      base <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      let stableName = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtName = "paging-v2"}) base)
-          keyDerivation = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtKey = (rtKey router) {corrVia = "otherIdText"}}) base)
-          target = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtTarget = "OtherPage"}) base)
-      [ckCode k | Breaking k <- stableName] `shouldContain` [RouterStableNameChanged]
-      [ckCode k | Breaking k <- keyDerivation] `shouldContain` [DerivedIdentityChanged]
-      [ckCode k | Breaking k <- target] `shouldContain` [DerivedIdentityChanged]
-    it "advises on router dispatch-surface changes without making them breaking" $ do
-      cs <- diffFixtures "test/fixtures/incident-paging/incident-paging.keiro" "test/fixtures/incident-paging/incident-paging-dispatch.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldBe` [RouterDecideSurfaceChanged]
-    it "advises on process dispatch-surface changes without making them breaking" $ do
-      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-handle.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldBe` [ProcessDecideSurfaceChanged]
-    it "advises on unversioned timer payload changes without making them breaking" $ do
-      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-payload.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldBe` [ProcessTimerPayloadChanged]
-    it "ignores formatting-only process and timer surface rewrites" $ do
-      original <- specOf "test/fixtures/hospital-surge.keiro"
-      formatted <- parseInlineSpec "<formatted-process>" (renderSpec original)
-      diffSpecs original formatted `shouldBe` []
-    it "reports a timer window change as a warning" $ do
-      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-window.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [TimerWindowChanged]
-    it "reports emit-map changes as warnings and derive changes as breaking" $ do
-      mapping <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-mapchange.keiro"
-      any isBreaking mapping `shouldBe` False
-      [ckCode k | Advisory k <- mapping] `shouldContain` [EmitMappingChanged]
-      derive <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-derive.keiro"
-      [ckCode k | Breaking k <- derive] `shouldContain` [DerivedIdentityChanged]
-    it "classifies publisher outbox identity and ordering independently" $ do
-      outbox <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-outboxfield.keiro"
-      [ckCode k | Breaking k <- outbox] `shouldContain` [DerivedIdentityChanged]
-      ordering <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-ordering.keiro"
-      any isBreaking ordering `shouldBe` False
-      [ckCode k | Advisory k <- ordering] `shouldContain` [PublisherPolicyChanged]
-    it "classifies workqueue names as QueueIdentityChanged" $ do
-      cs <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-rename.keiro"
-      [ckCode k | Breaking k <- cs] `shouldContain` [QueueIdentityChanged]
-    it "classifies pgmq dispatch dedupe and retargeting independently" $ do
-      dedupe <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-dedupkey.keiro"
-      [ckCode k | Breaking k <- dedupe] `shouldContain` [DedupeIdentityChanged]
-      retarget <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-retarget.keiro"
-      any isBreaking retarget `shouldBe` False
-      [ckCode k | Advisory k <- retarget] `shouldContain` [DispatchRetargeted]
-    it "reports aggregate projection changes as warnings" $ do
-      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-projection.keiro"
-      any isBreaking cs `shouldBe` False
-      [ckCode k | Advisory k <- cs] `shouldContain` [ProjectionChanged]
-    it "classifies read-model version and unversioned shape changes" $ do
-      base <- specOf "test/fixtures/readmodel-runtime.keiro"
-      let versionTwo = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmVersion = 2}) base
-          changedShape = modifyReadModel "transfer_decisions" changeReadModelShape base
-          bumpedShape = modifyReadModel "transfer_decisions" (\readModel -> (changeReadModelShape readModel) {rmVersion = 2}) base
-          decreased = diffSpecs versionTwo base
-          unversioned = diffSpecs base changedShape
-          bumped = diffSpecs base bumpedShape
-      [ckCode k | Breaking k <- decreased] `shouldContain` [ReadModelVersionDecreased]
-      [ckCode k | Breaking k <- unversioned] `shouldContain` [ReadModelShapeChangedWithoutBump]
-      any isBreaking bumped `shouldBe` False
-      [ckFacet k | Additive k <- bumped] `shouldContain` ["read-model-version"]
-    it "classifies read-model registry, table, subscription, and removal identities" $ do
-      base <- specOf "test/fixtures/readmodel-runtime.keiro"
-      let tableChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmTable = "transfer_decisions_v2"}) base
-          subscriptionChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmSubscription = Just "transfer-decisions-v2"}) base
-          renamed = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmName = "reservation_decisions"}) base
-          removed = removeReadModel "transfer_decisions" base
-      mapM_
-        (\changes -> [ckCode k | Breaking k <- changes] `shouldContain` [DerivedIdentityChanged])
-        [diffSpecs base tableChanged, diffSpecs base subscriptionChanged, diffSpecs base renamed, diffSpecs base removed]
-    it "classifies read-model feed flips and consistency/scope weakening as breaking" $ do
-      base <- specOf "test/fixtures/readmodel-runtime.keiro"
-      let feedChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmFeed = RmInline}) base
-          consistencyWeakened = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmConsistency = Eventual}) base
-          entireLog = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmScope = Just RmEntireLog}) base
-      [ckCode k | Breaking k <- diffSpecs base feedChanged] `shouldContain` [ReadModelFeedChanged]
-      [ckCode k | Breaking k <- diffSpecs base consistencyWeakened] `shouldContain` [ReadModelConsistencyWeakened]
-      [ckCode k | Breaking k <- diffSpecs entireLog base] `shouldContain` [ReadModelConsistencyWeakened]
-    it "classifies Eventual to Strong read-model consistency as additive" $ do
-      strong <- specOf "test/fixtures/readmodel-runtime.keiro"
-      let eventual = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmConsistency = Eventual}) strong
-          changes = diffSpecs eventual strong
-      any isBreaking changes `shouldBe` False
-      [ckFacet k | Additive k <- changes] `shouldContain` ["read-model-consistency"]
-
-  describe "module placement (M1)" $ do
-    it "GeneratedPrefix is today's namespace (Generated.<Ctx>.<Node>, holes at <Ctx>.<Node>)" $ do
-      let ctx = defaultContext "hospital-capacity"
-      genPrefixFor ctx "Reservation" `shouldBe` "Generated.HospitalCapacity.Reservation"
-      holePrefixFor ctx "Reservation" `shouldBe` "HospitalCapacity.Reservation"
-    it "module-root prefixes both layers" $ do
-      let ctx = (defaultContext "hospital-capacity") {moduleRoot = "Acme"}
-      genPrefixFor ctx "Reservation" `shouldBe` "Acme.Generated.HospitalCapacity.Reservation"
-      holePrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation"
-    it "CollocatedLeaf places the generated layer under the domain leaf" $ do
-      let ctx = (defaultContext "hospital-capacity") {moduleRoot = "Acme", placement = CollocatedLeaf}
-      genPrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation.Generated"
-      holePrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation"
-    it "parses and preserves the module/layout clauses through parse . pretty" $ do
-      let src = "context hospital-capacity\nmodule Acme.Services\nlayout collocated\n\naggregate Reservation\n  regs\n  states Open\n"
-      case parseSpec "<m1>" src of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> do
-          specModuleRoot spec `shouldBe` Just "Acme.Services"
-          specLayout spec `shouldBe` Just CollocatedLeaf
-          parseSpec "<m1>" (renderSpec spec) `shouldBe` Right spec
-    it "a spec without the clauses leaves placement at the default" $ do
-      input <- readTestText "test/fixtures/reservation.keiro"
-      case parseSpec "test/fixtures/reservation.keiro" input of
-        Left err -> expectationFailure (T.unpack err)
-        Right spec -> do
-          specModuleRoot spec `shouldBe` Nothing
-          specLayout spec `shouldBe` Nothing
-
-  describe "structural scaffold" $ do
-    it "emits one private shape module per structural declaration and one context facade" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-          paths = map modulePath modules
-      paths
-        `shouldContain` [ "Generated/ConsumerDemo/Structural/Shape/ArtifactInfo.hs",
-                          "Generated/ConsumerDemo/Structural/Shape/ArtifactKind.hs",
-                          "Generated/ConsumerDemo/Structural/Shape/ArtifactLocation.hs",
-                          "Generated/ConsumerDemo/StructuralProjections.hs"
-                        ]
-      paths `shouldNotContain` ["Generated/ConsumerDemo/Structural/Shape/VendorGeometry.hs"]
-      firewallBreaches modules `shouldBe` []
-    it "emits one create-once binding skeleton per owning module and derives Generic for private shapes" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-          skeletons = [moduleValue | moduleValue <- modules, kind moduleValue == HoleStub, modulePath moduleValue == "Example/Artifact/KeiroBindings.hs"]
-          shape = generatedTextEndingIn "Structural/Shape/ArtifactInfo.hs" modules
-      case skeletons of
-        [skeleton] -> do
-          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactInfoBinding :: StructuralBinding"
-          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactKindBinding :: StructuralBinding"
-          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactLocationBinding :: StructuralBinding"
-          moduleText skeleton `shouldSatisfy` T.isInfixOf "HOLE: fill ArtifactInfo bindingToShape.key"
-        _ -> expectationFailure ("expected exactly one shared binding skeleton, got " <> show (map modulePath skeletons))
-      shape `shouldSatisfy` T.isInfixOf "deriving stock (Eq, Generic, Show)"
-      shape `shouldSatisfy` T.isInfixOf "import GHC.Generics (Generic)"
-    it "never overwrites an existing binding skeleton" $
-      withTempDirectory "keiro-dsl-binding-create-once" $ \out -> do
-        spec <- specOf "test/fixtures/consumer-types.keiro"
-        let ctx = defaultContext (specContext spec)
-            bindingPath = out </> "Example/Artifact/KeiroBindings.hs"
-        _ <- executePlannedScaffold out "consumer-types.keiro" ctx spec
-        TIO.writeFile bindingPath "hand-owned binding\n"
-        second <- executePlannedScaffold out "consumer-types.keiro" ctx spec
-        TIO.readFile bindingPath `shouldReturn` "hand-owned binding\n"
-        reportDispositions second
-          `shouldSatisfy` any (\(moduleValue, disposition) -> modulePath moduleValue == "Example/Artifact/KeiroBindings.hs" && disposition == Skipped)
-    it "fresh binding skeletons compile at the application boundary" $
-      withTempDirectory "keiro-dsl-binding-compiles" $ \out -> do
-        spec <- specOf "test/fixtures/structural-conformance.keiro"
-        let ctx = defaultContext (specContext spec)
-            bindingSource = out </> "Conformance/Structural/Bindings.hs"
-            ghcOutput = out </> ".ghc"
-        _ <- executePlannedScaffold out "structural-conformance.keiro" ctx spec
-        createDirectoryIfMissing True ghcOutput
-        (exitCode, standardOutput, standardError) <-
-          readProcessWithExitCode
-            "cabal"
-            [ "exec",
-              "--",
-              "ghc",
-              "-XGHC2024",
-              "-XOverloadedStrings",
-              "-fno-code",
-              "-fforce-recomp",
-              "-outputdir",
-              ghcOutput,
-              "-i" <> out,
-              "-itest/conformance-structural",
-              "-i../keiro-core/src",
-              bindingSource
-            ]
-            ""
-        unless (exitCode == ExitSuccess) $
-          expectationFailure (standardOutput <> standardError)
-    it "keeps consumer types in Domain while the generated Codec owns keys, tags, and defaults" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-          domain = generatedTextEndingIn "Catalog/Domain.hs" modules
-          codec = generatedTextEndingIn "Catalog/Codec.hs" modules
-      domain `shouldSatisfy` T.isInfixOf "Example.Artifact.Domain.ArtifactInfo"
-      domain `shouldSatisfy` T.isInfixOf "Vendor.Geometry.Geometry"
-      domain `shouldSatisfy` T.isInfixOf "Example.Artifact.KeiroBindings.emptyArtifactInfo"
-      codec `shouldSatisfy` T.isInfixOf "\"location\" .= encodeArtifactLocationShape"
-      codec `shouldSatisfy` T.isInfixOf "\"local_file\""
-      codec `shouldSatisfy` T.isInfixOf "Nothing -> pure Generated.ConsumerDemo.Structural.Shape.ArtifactKind.Guide"
-      codec `shouldSatisfy` T.isInfixOf "rejectUnknownFields \"ArtifactInfo\""
-      codec `shouldSatisfy` T.isInfixOf "toJSON payload.geometry"
-      codec `shouldSatisfy` (not . T.isInfixOf "vendor.geometry.json")
-    it "generates shape-only nested types and schema-derived Keiki witnesses" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-          shape = generatedTextEndingIn "Structural/Shape/ArtifactInfo.hs" modules
-          facade = generatedTextEndingIn "StructuralProjections.hs" modules
-      shape `shouldSatisfy` T.isInfixOf "data ArtifactInfoShape = ArtifactInfo"
-      shape `shouldSatisfy` T.isInfixOf "ArtifactKind.ArtifactKindShape"
-      shape `shouldSatisfy` (not . T.isInfixOf "KeiroBindings")
-      facade `shouldSatisfy` T.isInfixOf "type FieldName"
-      facade `shouldSatisfy` T.isInfixOf "= \"/key\""
-      facade `shouldSatisfy` T.isInfixOf "fieldShapeId _ = \"example.artifact.ArtifactInfo.v1\""
-      facade `shouldSatisfy` T.isInfixOf "bindingToShape Example.Artifact.KeiroBindings.artifactInfoBinding owner"
-
-  describe "structural manifest" $ do
-    it "lists consumer packages and every domain, binding, fixture, and initial module" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-          manifest = renderManifest "consumer-types.keiro" modules spec
-      mapM_ (\packageName -> manifestDependencies spec `shouldContain` [packageName]) ["artifact-domain", "vendor-geometry"]
-      manifest `shouldSatisfy` T.isInfixOf "consumer-packages:\n    artifact-domain\n    vendor-geometry"
-      mapM_
-        (\moduleName -> manifest `shouldSatisfy` T.isInfixOf moduleName)
-        [ "Example.Artifact.Domain",
-          "Example.Artifact.KeiroBindings",
-          "Vendor.Geometry",
-          "Vendor.Geometry.KeiroBindings"
-        ]
-
-  describe "structural scaffold record" $ do
-    it "round-trips canonical mapping rows and reports binding drift on the next run" $
-      withTempDirectory "keiro-dsl-mapping-record" $ \out -> do
-        spec <- specOf "test/fixtures/consumer-types.keiro"
-        let ctx = defaultContext (specContext spec)
-        first <- executePlannedScaffold out "consumer-types.keiro" ctx spec
-        length (consumerMappings (reportConsumerPlan first)) `shouldBe` 4
-        recordText <- TIO.readFile (out </> recordFileName (specContext spec))
-        let mappingRows = filter (T.isPrefixOf "mapping ") (T.lines recordText)
-            bindingRows = filter (T.isPrefixOf "binding ") (T.lines recordText)
-        length mappingRows `shouldBe` 4
-        bindingRows `shouldSatisfy` (not . null)
-        fmap recMappings (parseRecord recordText) `shouldSatisfy` maybe False ((== 4) . length)
-        fmap recBindingObligations (parseRecord recordText) `shouldSatisfy` maybe False ((== length bindingRows) . length)
-        let bumped = spec {specMapped = map bumpArtifactBindingVersion (specMapped spec)}
-        second <- executePlannedScaffold out "consumer-types.keiro" ctx bumped
-        reportMappingDrift second
-          `shouldSatisfy` any (\drift -> driftSpecName drift == "ArtifactInfo" && driftPrevious drift /= driftCurrent drift)
-        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "mapping drift:")
-        case mappingRows of
-          row : _ -> parseRecord (recordText <> row <> "\n") `shouldBe` Nothing
-          [] -> expectationFailure "expected mapping rows"
-        case bindingRows of
-          row : _ -> parseRecord (recordText <> row <> "\n") `shouldBe` Nothing
-          [] -> expectationFailure "expected binding rows"
-    it "reports exactly the newly added binding field without rewriting the shared skeleton" $
-      withTempDirectory "keiro-dsl-binding-drift" $ \out -> do
-        spec <- specOf "test/fixtures/consumer-types.keiro"
-        let ctx = defaultContext (specContext spec)
-        _ <- executePlannedScaffold out "consumer-types.keiro" ctx spec
-        let extended = spec {specMapped = map addArtifactSummaryField (specMapped spec)}
-        second <- executePlannedScaffold out "consumer-types.keiro" ctx extended
-        reportNewHoles second
-          `shouldBe` [ BindingHole
-                         { holeMappedName = "ArtifactInfo",
-                           holeModule = "Example.Artifact.KeiroBindings",
-                           holeSymbol = "artifactInfoBinding",
-                           holeKind = BindingValue,
-                           holePath = Just "summary",
-                           holeSignature = "artifactInfoBinding.summary :: Text"
-                         }
-                     ]
-        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "artifactInfoBinding.summary :: Text")
-    it "rejects malformed known mapping JSON while ignoring unrelated future rows" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      withTempDirectory "keiro-dsl-mapping-malformed" $ \out -> do
-        report <- executePlannedScaffold out "consumer-types.keiro" (defaultContext (specContext spec)) spec
-        recordText <- TIO.readFile (reportRecordPath report)
-        parseRecord (recordText <> "mapping {not-json}\n") `shouldBe` Nothing
-        parseRecord (recordText <> "future-row retained\n") `shouldBe` parseRecord recordText
-
-  describe "structural import plan" $ do
-    it "reports the successful dependency plan in the scaffold report" $
-      withTempDirectory "keiro-dsl-dependency-plan" $ \out -> do
-        spec <- specOf "test/fixtures/consumer-types.keiro"
-        report <- executePlannedScaffold out "consumer-types.keiro" (defaultContext (specContext spec)) spec
-        renderScaffoldReport report
-          `shouldSatisfy` any (T.isInfixOf "dependency plan: consumer packages [artifact-domain, vendor-geometry]")
-    it "refuses a binding module inside the generated namespace with the exact cycle" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let cyclic = spec {specMapped = map moveArtifactBindingIntoGenerated (specMapped spec)}
-      case planScaffold (defaultContext (specContext cyclic)) cyclic of
-        Left refusals -> do
-          refusals `shouldSatisfy` any isImportCycle
-          renderRefusals refusals `shouldSatisfy` any (T.isInfixOf "Generated.ConsumerDemo.Bindings")
-        Right _ -> expectationFailure "expected an import-cycle refusal"
-    it "refuses missing mapped register initials but permits command/event-only use" $ do
-      missing <- specOf "test/fixtures/mapped-missing-initial.keiro"
-      planScaffold (defaultContext (specContext missing)) missing `shouldSatisfy` isLoweringRefusal
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let commandOnly = removeMappedRegisterRequirements spec
-      planScaffold (defaultContext (specContext commandOnly)) commandOnly `shouldSatisfy` isRight
-
-  describe "binding explanations" $ do
-    it "lists binding, fixture, and use-site-scoped initial obligations deterministically" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
-      length obligations `shouldBe` 7
-      obligations
-        `shouldSatisfy` any
-          ( \obligation ->
-              obligationKind obligation == BindingValue
-                && obligationSymbol obligation == "artifactInfoBinding"
-                && obligationBindingVersion obligation == Just "1"
-          )
-      obligations
-        `shouldSatisfy` any
-          ( \obligation ->
-              obligationKind obligation == InitialValue
-                && obligationSymbol obligation == "emptyArtifactInfo"
-                && any (T.isInfixOf "Catalog register currentArtifact") (obligationUseSites obligation)
-          )
-      let rendered = renderBindingObligations (specContext spec) obligations
-      rendered `shouldSatisfy` T.isInfixOf "binding obligations for context consumer-demo"
-      rendered `shouldSatisfy` T.isInfixOf "artifactInfoBinding :: StructuralBinding Example.Artifact.Domain.ArtifactInfo ArtifactInfoShape"
-      rendered `shouldSatisfy` T.isInfixOf "provenance: binding-version \"1\""
-    it "states explicitly when a spec has no structural obligations" $ do
-      spec <- specOf "test/fixtures/reservation.keiro"
-      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
-      renderBindingObligations (specContext spec) obligations
-        `shouldBe` "no binding obligations for context hospital-capacity"
-
-  describe "exact generic structural bindings" $ do
-    forM_
-      [ ("renamed-field", "selector mismatch"),
-        ("reordered-field", "selector mismatch"),
-        ("arity-mismatch", "no exact nominal correspondence"),
-        ("incompatible-type", "no exact nominal correspondence")
-      ]
-      $ \(fixture, diagnostic) ->
-        it ("rejects " <> fixture <> " and directs the author to the scaffolded module") $
-          expectGenericCompileFailure fixture diagnostic
-
-  describe "structural harness" $ do
-    it "emits every structural, wire-policy, projection, and replay assertion family" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let aggregate = onlyAggregate spec
-          ctx = defaultContext (specContext spec)
-          harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx spec aggregate)
-      mapM_
-        (\needle -> harness `shouldSatisfy` T.isInfixOf needle)
-        [ "binding domain round-trip: example.artifact.ArtifactInfo.v1/",
-          "binding shape round-trip: example.artifact.ArtifactInfo.v1/",
-          "mapped codec round-trip: ArtifactObserved/artifact/",
-          "fixture coverage: example.artifact.ArtifactLocation.v1",
-          "wire policy missing default: example.artifact.ArtifactInfo.v1/description",
-          "wire policy explicit null: example.artifact.ArtifactInfo.v1/description",
-          "wire policy unknown fields: example.artifact.ArtifactInfo.v1",
-          "wire union arm: example.artifact.ArtifactLocation.v1/local_file",
-          "canonical identity: example.artifact.ArtifactInfo.v1",
-          "projection witness agreement: example.artifact.ArtifactInfo.v1/key",
-          "forward/replay equality: ObserveArtifact from CatalogEmpty -- ",
-          "register currentArtifact"
-        ]
-    it "keeps opaque assertions at the declared codec boundary" $ do
-      spec <- specOf "test/fixtures/consumer-types.keiro"
-      let aggregate = onlyAggregate spec
-          ctx = defaultContext (specContext spec)
-          modules = scaffoldAggregate ctx spec aggregate <> harnessFor ctx spec aggregate
-          harness = generatedTextEndingIn "Harness.hs" modules
-          codec = generatedTextEndingIn "Codec.hs" modules
-      harness `shouldSatisfy` T.isInfixOf "opaque codec round-trip: vendor.geometry.json@3/"
-      harness `shouldNotSatisfy` T.isInfixOf "wire policy unknown fields: vendor.geometry.json"
-      harness `shouldNotSatisfy` T.isInfixOf "fixture coverage: vendor.geometry"
-      codec `shouldNotSatisfy` T.isInfixOf "encodeVendorGeometryShape"
-
-  describe "manifest (M2)" $ do
-    it "lists exactly the modules the scaffolder produced" $ do
-      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      spec <- specOf "test/fixtures/reservation.keiro"
-      let manifest = renderManifest "reservation.keiro" mods spec
-          expectedNames = sort (map (moduleNameOf . modulePath) mods)
-      -- every produced module name appears in the manifest…
-      mapM_ (\m -> (m `T.isInfixOf` manifest) `shouldBe` True) expectedNames
-      -- …and the module list is exactly the scaffolder's output set.
-      expectedNames
-        `shouldBe` sort
-          [ "Generated.HospitalCapacity.Reservation.Codec",
-            "Generated.HospitalCapacity.Reservation.Domain",
-            "Generated.HospitalCapacity.Reservation.EventStream",
-            "Generated.HospitalCapacity.Reservation.Harness",
-            "Generated.HospitalCapacity.Reservation.Projection",
-            "HospitalCapacity.Reservation.Holes"
-          ]
-    it "derives the dependency set from the node kinds present (aggregate)" $ do
-      spec <- specOf "test/fixtures/reservation.keiro"
-      manifestDependencies spec `shouldBe` ["aeson", "base", "keiki", "keiro", "text"]
-    it "derives the process dependency set, including worker-policy runtime imports" $ do
-      spec <- specOf "test/fixtures/hospital-surge.keiro"
-      let dependencies = manifestDependencies spec
-      mapM_ (\dependency -> dependencies `shouldContain` [dependency]) ["time", "uuid", "shibuya-core", "keiki", "keiro"]
-    it "uses the registered shibuya-core package name for router scaffolds" $ do
-      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
-      let dependencies = manifestDependencies spec
-      mapM_ (\dependency -> dependencies `shouldContain` [dependency]) ["effectful-core", "keiro", "shibuya-core"]
-      dependencies `shouldNotContain` ["shibuya"]
-
-  describe "new <kind> skeletons (M5)" $ do
-    it "every skeleton parses and validates with zero error diagnostics" $
-      mapM_ assertSkeletonValid skeletonKinds
-    it "every skeleton passes the scaffold refusal gates" $
-      mapM_ assertSkeletonScaffoldable skeletonKinds
-    it "fresh skeleton scaffolds match the committed compiling modules" $
-      mapM_ (uncurry assertSkeletonMatchesCommitted) skeletonModuleRoots
-    it "rejects an unknown kind with a helpful message" $
-      case skeletonFor "bogus" of
-        Left msg -> ("Valid kinds:" `T.isInfixOf` msg) `shouldBe` True
-        Right _ -> expectationFailure "expected an error for an unknown kind"
-
-  describe "firewall self-check (M3)" $ do
-    it "flags a forbidden operator in a Generated module" $ do
-      let m = ScaffoldModule {modulePath = "Gen/Foo.hs", moduleText = "x = a ./= b", kind = Generated, origin = "test"}
-      firewallBreaches [m] `shouldBe` [("Gen/Foo.hs", "./=", 1)]
-    it "ignores forbidden operators in a HoleStub module (holes own them)" $ do
-      let m = ScaffoldModule {modulePath = "Foo/Holes.hs", moduleText = "x = lit 1 .== y", kind = HoleStub, origin = "test"}
-      firewallBreaches [m] `shouldBe` []
-    it "matches `lit` as a word, not a substring of quality/split" $ do
-      let clean = ScaffoldModule {modulePath = "Gen/Q.hs", moduleText = "quality = split facility", kind = Generated, origin = "test"}
-          dirty = ScaffoldModule {modulePath = "Gen/L.hs", moduleText = "v = lit foo", kind = Generated, origin = "test"}
-      firewallBreaches [clean] `shouldBe` []
-      firewallBreaches [dirty] `shouldBe` [("Gen/L.hs", "lit", 1)]
-    it "skips strings and comments and maximal-munches symbolic tokens" $ do
-      let clean = syntheticGenerated "Gen/Clean.hs" "wire = \"lit .== B.slot\"\n-- x =: y\nx = a .<= b"
-          dirty = syntheticGenerated "Gen/Dirty.hs" "x = a .< b\ny = c =: d"
-      firewallBreaches [clean] `shouldBe` [("Gen/Clean.hs", ".<=", 3)]
-      firewallBreaches [dirty] `shouldBe` [("Gen/Dirty.hs", ".<", 1), ("Gen/Dirty.hs", "=:", 2)]
-    it "guards keiki imports while allowing the generated Core allowlist" $ do
-      let forbidden = syntheticGenerated "Gen/Builder.hs" "import Keiki.Builder"
-          restricted = syntheticGenerated "Gen/CoreBad.hs" "import Keiki.Core (lit)"
-          allowed = syntheticGenerated "Gen/CoreGood.hs" "import Keiki.Core (RegFile (..), HsPred, step)"
-      firewallBreaches [forbidden] `shouldBe` [("Gen/Builder.hs", "import:Keiki.Builder", 1)]
-      firewallBreaches [restricted] `shouldBe` [("Gen/CoreBad.hs", "import:Keiki.Core", 1)]
-      firewallBreaches [allowed] `shouldBe` []
-    it "exempts only the authoritative generated expression/transducer module paths" $ do
-      let expressions = syntheticGenerated "Gen/Aggregate/Expressions.hs" "import Keiki.Core qualified as K\nx = K.lit 1"
-          transducer = syntheticGenerated "Gen/Aggregate/Transducer.hs" "import Keiki.Builder qualified as B\nx = B.slot"
-          ordinary = syntheticGenerated "Gen/Aggregate/Projection.hs" "import Keiki.Builder qualified as B"
-      firewallBreaches [expressions, transducer] `shouldBe` []
-      firewallBreaches [ordinary] `shouldBe` [("Gen/Aggregate/Projection.hs", "import:Keiki.Builder", 1)]
-    it "finds no breach in real scaffolder output (aggregate + process fixtures)" $ do
-      aggMods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      procMods <- scaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
-      firewallBreaches (aggMods <> procMods) `shouldBe` []
-
-  describe "scaffold gates" $ do
-    it "refuses duplicate and case-folded module paths with both origins" $ do
-      spec <- specOf "test/fixtures/reservation.keiro"
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        aggregate : _ -> do
-          let duplicate = spec {specNodes = [NAggregate aggregate, NAggregate aggregate]}
-              caseVariant = spec {specNodes = [NAggregate aggregate, NAggregate aggregate {aggName = T.toUpper (aggName aggregate)}]}
-          planScaffold (defaultContext (specContext spec)) duplicate `shouldSatisfy` hasPathCollisionWithTwoOrigins
-          planScaffold (defaultContext (specContext spec)) caseVariant `shouldSatisfy` hasPathCollisionWithTwoOrigins
-        [] -> expectationFailure "reservation fixture has no aggregate"
-    it "refuses a bannerless Generated target without changing its bytes" $
-      withTempDirectory "keiro-dsl-banner" $ \out -> do
-        spec <- specOf "test/fixtures/reservation.keiro"
-        let ctx = defaultContext (specContext spec)
-        case planScaffold ctx spec of
-          Left refusals -> expectationFailure ("unexpected planning refusal: " <> show refusals)
-          Right modules -> case [m | m <- modules, kind m == Generated] of
-            generated : _ -> do
-              let target = out </> modulePath generated
-              createDirectoryIfMissing True (takeDirectory target)
-              TIO.writeFile target "hand owned\n"
-              result <- executeScaffold out False "test/fixtures/reservation.keiro" ctx spec modules
-              result `shouldSatisfy` isMissingBannerRefusal
-              TIO.readFile target `shouldReturn` "hand owned\n"
-              forced <- executeScaffold out True "test/fixtures/reservation.keiro" ctx spec modules
-              forced `shouldSatisfy` isSuccessfulScaffold
-              TIO.readFile target `shouldReturn` moduleText generated
-            [] -> expectationFailure "reservation scaffold has no Generated module"
-    it "reports renamed-node modules as stale without deleting them" $
-      withTempDirectory "keiro-dsl-stale-rename" $ \out -> do
-        spec <- parseInlineSpec "<stale-rename>" loweringAggregateSpec
-        first <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext spec)) spec
-        let renamed = spec {specNodes = map renameCounter (specNodes spec)}
-        second <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext renamed)) renamed
-        let oldDomain = onlyPathEndingIn "Counter/Domain.hs" (map fst (reportDispositions first))
-            oldHoles = onlyPathEndingIn "Counter/Holes.hs" (map fst (reportDispositions first))
-        reportStale second `shouldSatisfy` \stale -> StaleModule Generated oldDomain `elem` stale && StaleModule HoleStub oldHoles `elem` stale
-        doesFileExist (out </> oldDomain) `shouldReturn` True
-        doesFileExist (out </> oldHoles) `shouldReturn` True
-    it "reports the entire old tree across a module-root flip" $
-      withTempDirectory "keiro-dsl-stale-root" $ \out -> do
-        spec <- parseInlineSpec "<stale-root>" loweringAggregateSpec
-        let initialCtx = defaultContext (specContext spec)
-            rootedCtx = initialCtx {moduleRoot = "Acme"}
-        first <- executePlannedScaffold out "counter.keiro" initialCtx spec
-        second <- executePlannedScaffold out "moved-counter.keiro" rootedCtx spec
-        reportStale second
-          `shouldMatchList` [StaleModule (kind m) (modulePath m) | (m, _) <- reportDispositions first]
-        forM_ (reportStale second) $ \stale -> doesFileExist (out </> stalePath stale) `shouldReturn` True
-        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "previous scaffold record used spec counter.keiro")
-    it "reports moved generated modules across a layout flip" $
-      withTempDirectory "keiro-dsl-stale-layout" $ \out -> do
-        spec <- parseInlineSpec "<stale-layout>" loweringAggregateSpec
-        let initialCtx = defaultContext (specContext spec)
-            collocatedCtx = initialCtx {placement = CollocatedLeaf}
-        first <- executePlannedScaffold out "counter.keiro" initialCtx spec
-        second <- executePlannedScaffold out "counter.keiro" collocatedCtx spec
-        let oldGenerated = [StaleModule Generated (modulePath m) | (m, _) <- reportDispositions first, kind m == Generated]
-        reportStale second `shouldSatisfy` all (`elem` oldGenerated)
-        length (reportStale second) `shouldBe` length oldGenerated
-    it "writes a parseable record and no stale section for a fresh output" $
-      withTempDirectory "keiro-dsl-record" $ \out -> do
-        spec <- parseInlineSpec "<fresh-record>" loweringAggregateSpec
-        let ctx = defaultContext (specContext spec)
-        report <- executePlannedScaffold out "counter.keiro" ctx spec
-        reportStale report `shouldBe` []
-        renderScaffoldReport report `shouldSatisfy` all (not . T.isPrefixOf "stale:")
-        contents <- TIO.readFile (out </> recordFileName (specContext spec))
-        requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
-        let expected =
-              ScaffoldRecord
-                { recSpecPath = "counter.keiro",
-                  recModuleRoot = "",
-                  recLayout = "prefixed",
-                  recSourceLanguage = LegacyUnversioned,
-                  recLanguageContract = effectiveLanguageContract LegacyUnversioned,
-                  recFiles = [(kind m, modulePath m) | (m, _) <- reportDispositions report],
-                  recMappings = [],
-                  recIdDomains = [],
-                  recNominalEqualities = [],
-                  recBindingObligations = [],
-                  recBehaviorRequirements = Behavior.behaviorRecordRows requirements
-                }
-            sourceRows = filter ("source-language " `T.isPrefixOf`) (T.lines contents)
-            withoutSourceRows = T.unlines (filter (not . T.isPrefixOf "source-language ") (T.lines contents))
-            semanticRows = filter ("semantic-contract " `T.isPrefixOf`) (T.lines contents)
-            withoutSemanticRows = T.unlines (filter (not . T.isPrefixOf "semantic-contract ") (T.lines contents))
-        parseRecord contents `shouldBe` Just expected
-        parseRecord withoutSourceRows `shouldBe` Just expected
-        parseRecord withoutSemanticRows `shouldBe` Just expected
-        case sourceRows of
-          [sourceRow] -> do
-            parseRecord (T.replace sourceRow (sourceRow <> "\n" <> sourceRow) contents) `shouldBe` Nothing
-            parseRecord (T.replace sourceRow "source-language {malformed}" contents) `shouldBe` Nothing
-          _ -> expectationFailure "expected exactly one source-language row"
-        case semanticRows of
-          [semanticRow] -> do
-            parseRecord (T.replace semanticRow (semanticRow <> "\n" <> semanticRow) contents) `shouldBe` Nothing
-            parseRecord (T.replace semanticRow "semantic-contract {malformed}" contents) `shouldBe` Nothing
-            parseRecord (T.replace "\"languageVersion\":1" "\"languageVersion\":2" contents) `shouldBe` Nothing
-          _ -> expectationFailure "expected exactly one semantic-contract row"
-        parseRecord (T.replace "spec: " "future-field: retained\nspec: " contents) `shouldBe` parseRecord contents
-        parseRecord (T.replace "record v1" "record v2" contents) `shouldBe` Nothing
-    it "records declared provenance and reports a header-only scaffold drift" $
-      withTempDirectory "keiro-dsl-language-drift" $ \out -> do
-        spec <- parseInlineSpec "<language-drift>" loweringAggregateSpec
-        let ctx = defaultContext (specContext spec)
-        modules <- case planScaffold ctx spec of
-          Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
-          Right planned -> pure planned
-        _ <- executePlannedScaffold out "counter.keiro" ctx spec
-        case languageVersion 1 of
-          Nothing -> expectationFailure "version 1 was not constructible"
-          Just version -> do
-            let declared = DeclaredLanguage version noLoc
-            result <- executeScaffoldWithLanguage out False "counter.keiro" declared ctx spec modules
-            case result of
-              Left refusals -> expectationFailure (show refusals)
-              Right report -> do
-                reportSourceLanguageDrift report
-                  `shouldBe` Just (SourceLanguageDrift LegacyUnversioned declared)
-                contents <- TIO.readFile (reportRecordPath report)
-                recSourceLanguage <$> parseRecord contents `shouldBe` Just declared
-
-  describe "faithful scaffold lowering" $ do
-    it "escapes a trailing-backslash payload literal exactly once" $ do
-      spec <- specOf "test/fixtures/hospital-surge.keiro"
-      case [process | NProcess process <- specNodes spec] of
-        process : _ -> do
-          let timer = (procTimer process) {tmPayload = [FieldBinding "kind" (Just "\"follow-up\\\"")]}
-              modules = scaffoldProcess (defaultContext (specContext spec)) process {procTimer = timer}
-          generatedTextEndingIn "Process.hs" modules
-            `shouldSatisfy` T.isInfixOf "\"kind\" .= (\"follow-up\\\\\" :: Value)"
-        [] -> expectationFailure "hospital-surge fixture has no process"
-    it "preserves quoted Text register initials and refuses unsafe register shapes" $ do
-      spec <- parseInlineSpec "<register-initials>" loweringAggregateSpec
-      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec =<< [aggregate | NAggregate aggregate <- specNodes spec]
-          domain = generatedTextEndingIn "Domain.hs" modules
-      domain `shouldSatisfy` T.isInfixOf "RCons (Proxy @\"note\") \"hello world\""
-      scaffoldRefusals spec `shouldBe` []
-      bare <- parseInlineSpec "<bare-text-initial>" (T.replace "\"hello world\"" "hello" loweringAggregateSpec)
-      scaffoldRefusals bare `shouldSatisfy` any (T.isInfixOf "RegTextInitialNotQuoted")
-      unsupported <- parseInlineSpec "<unsupported-field>" (T.replace "count:Int" "count:Json" loweringAggregateSpec)
-      scaffoldRefusals unsupported `shouldSatisfy` any (T.isInfixOf "FieldTypeUnrepresentable")
-    it "lowers seconds, minutes, hours, and both backoff constructors faithfully" $ do
-      windowSeconds "90s" `shouldBe` Right 90
-      windowSeconds "5m" `shouldBe` Right 300
-      windowSeconds "2h" `shouldBe` Right 7200
-      emitSource <- readTestText "test/fixtures/emit.keiro"
-      let exponentialSource = T.replace "backoff constant 2s" "backoff exponential 2s max=60s multiplier=2.0" emitSource
-      exponential <- parseInlineSpec "<exponential-backoff>" exponentialSource
-      case [publisher | NPublisher publisher <- specNodes exponential] of
-        publisher : _ -> do
-          let generated = generatedTextEndingIn "Publisher.hs" (scaffoldPublisher (defaultContext (specContext exponential)) publisher)
-          generated `shouldSatisfy` T.isInfixOf "ExponentialBackoff ExponentialBackoffOptions { initial = 2, maxDelay = 60, multiplier = 2.0 }"
-          parseSpec "<exponential-round-trip>" (renderSpec exponential) `shouldBe` Right exponential
-        [] -> expectationFailure "emit fixture has no publisher"
-      constant <- parseInlineSpec "<constant-backoff>" (T.replace "backoff constant 2s" "backoff constant 2m" emitSource)
-      case [publisher | NPublisher publisher <- specNodes constant] of
-        publisher : _ -> generatedTextEndingIn "Publisher.hs" (scaffoldPublisher (defaultContext (specContext constant)) publisher) `shouldSatisfy` T.isInfixOf "ConstantBackoff 120"
-        [] -> expectationFailure "emit fixture has no publisher"
-    it "refuses incomplete exponential backoff and rejects unknown window units" $ do
-      emitSource <- readTestText "test/fixtures/emit.keiro"
-      incomplete <- parseInlineSpec "<incomplete-backoff>" (T.replace "backoff constant 2s" "backoff exponential 2s" emitSource)
-      scaffoldRefusals incomplete `shouldSatisfy` any (T.isInfixOf "BackoffExponentialIncomplete")
-      parseSpec "<bad-window>" (T.replace "backoff constant 2s" "backoff constant 2x" emitSource)
-        `shouldSatisfy` leftContains "time unit: s, m, or h"
-    it "lowers workqueue retry windows in minutes to seconds" $ do
-      queueSource <- readTestText "test/fixtures/reservation-work.keiro"
-      queueSpec <- parseInlineSpec "<minute-queue>" (T.replace "5s" "5m" queueSource)
-      case [workqueue | NWorkqueue workqueue <- specNodes queueSpec] of
-        workqueue : _ -> do
-          let policy = generatedTextEndingIn "QueuePolicy.hs" (scaffoldWorkqueue (defaultContext (specContext queueSpec)) workqueue)
-          policy `shouldSatisfy` T.isInfixOf "defaultRetryDelay = RetryDelay 300"
-          policy `shouldSatisfy` T.isInfixOf "Retry (RetryDelay 300)"
-        [] -> expectationFailure "queue fixture has no workqueue"
-    it "uses exact status-map keys and emits total Int harness samples" $ do
-      statusSpec <- parseInlineSpec "<exact-status>" exactStatusSpec
-      case [aggregate | NAggregate aggregate <- specNodes statusSpec] of
-        aggregate : _ -> do
-          let ctx = defaultContext (specContext statusSpec)
-              projection = generatedTextEndingIn "Projection.hs" (scaffoldAggregate ctx statusSpec aggregate)
-              harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx statusSpec aggregate)
-          projection `shouldSatisfy` T.isInfixOf "ReservationUnHeld {} -> Just \"available\""
-          harness `shouldSatisfy` T.isInfixOf "CountBumpedData 0"
-          harness `shouldNotSatisfy` T.isInfixOf "sample: unsupported"
-        [] -> expectationFailure "exact-status spec has no aggregate"
-
-  describe "scaffold" $ do
-    it "synthesizes the exact old wire shape and embeds it in the harness" $ do
-      oldSpec <- specOf "test/fixtures/reservation.keiro"
-      newSpec <- specOf "test/fixtures/reservation-v2.keiro"
-      case goldensForDiff oldSpec newSpec of
-        [golden] -> do
-          goldenRelativePath golden
-            `shouldBe` "hospital-capacity/Reservation/TransferReservationCreated.v1.json"
-          goldenJson golden
-            `shouldBe` "{\"commandId\":\"cmd_01hzy3v7q2e8kaw2m5x0d41n9c\",\"divertStatus\":\"open\",\"hospitalId\":\"hosp_01hzy3v7q2e8kaw2m5x0d41n9c\",\"kind\":\"TransferReservationCreated\",\"lifeCriticalOverride\":true,\"patientAcuity\":\"red\",\"reservationId\":\"rsv_01hzy3v7q2e8kaw2m5x0d41n9c\"}\n"
-          goldenEvidence golden `shouldBe` SynthesizedWeakStandIn
-          let aggregate = onlyAggregate newSpec
-              modules =
-                harnessForWithGoldens
-                  [golden]
-                  (defaultContext (specContext newSpec))
-                  newSpec
-                  aggregate
-              harness = generatedTextEndingIn "Harness.hs" modules
-          harness `shouldSatisfy` T.isInfixOf "golden TransferReservationCreated.v1 decodes"
-          harness `shouldSatisfy` T.isInfixOf "\\\"reservationId\\\":\\\"rsv_"
-          harness `shouldSatisfy` (not . T.isInfixOf "current-shape stand-in")
-        goldens -> expectationFailure ("expected one synthesized golden, got " <> show goldens)
-    it "synthesizes complete nested mapped old shapes deterministically and never overwrites captured evidence" $ do
-      oldSpec <- specOf "test/fixtures/consumer-types.keiro"
-      newSpec <- specOf "test/fixtures/consumer-types-v2.keiro"
-      case goldensForDiff oldSpec newSpec of
-        [golden] -> do
-          goldenEvidence golden `shouldBe` SynthesizedWeakStandIn
-          goldenJson golden `shouldSatisfy` T.isInfixOf "\"artifact\":{"
-          goldenJson golden `shouldSatisfy` T.isInfixOf "\"location\":{\"contents\":\"sample\",\"tag\":\"local_file\"}"
-          goldenJson golden `shouldSatisfy` T.isInfixOf "\"labels\":[\"sample\"]"
-          goldenJson golden `shouldSatisfy` T.isInfixOf "\"revision\":1"
-          goldenJson golden `shouldSatisfy` T.isInfixOf "\"observedAt\":\"2026-01-01T00:00:00Z\""
-          goldensForDiff oldSpec newSpec `shouldBe` [golden]
-          withTempDirectory "keiro-golden-preserve" $ \root -> do
-            let target = root </> goldenRelativePath golden
-            createDirectoryIfMissing True (takeDirectory target)
-            TIO.writeFile target "hand captured\n"
-            emitGoldenPayloads root oldSpec newSpec `shouldReturn` []
-            TIO.readFile target `shouldReturn` "hand captured\n"
-          withTempDirectory "keiro-golden-write" $ \root -> do
-            let target = root </> goldenRelativePath golden
-            emitGoldenPayloads root oldSpec newSpec `shouldReturn` [target]
-            TIO.readFile target `shouldReturn` goldenJson golden
-        goldens -> expectationFailure ("expected one nested synthesized golden, got " <> show goldens)
-    it "dispatches shared-version upcasters by wire event type and passes foreign kinds through" $ do
-      source <- readTestText "test/fixtures/reservation-dup-upcast-source.keiro"
-      spec <- parseInlineSpec "<shared-upcaster-source>" source
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        [aggregate] -> do
-          let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
-              codec = generatedTextEndingIn "Codec.hs" modules
-              holes = case [moduleText m | m <- modules, "Holes.hs" `T.isSuffixOf` T.pack (modulePath m)] of
-                [text] -> text
-                _ -> ""
-          codec `shouldSatisfy` T.isInfixOf "upcasters = [(1, upcastRungV1)]"
-          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 (EventType \"TransferReservationCreated\") value = upcastTransferReservationCreatedV1 value"
-          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 (EventType \"TransferReservationConfirmed\") value = upcastTransferReservationConfirmedV1 value"
-          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 _ value = Right value"
-          holes `shouldSatisfy` T.isInfixOf "receives ONLY TransferReservationCreated payloads"
-        _ -> expectationFailure "expected exactly one aggregate"
-    it "keeps foreign payloads byte-for-byte and invokes both same-rung event upcasters" $ do
-      let payloadA = object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (2 :: Int)]
-          payloadB = object ["kind" .= ("AmountRenamed" :: T.Text), "amount" .= (3 :: Int)]
-          foreignPayload = object ["kind" .= ("AmountObserved" :: T.Text), "amount" .= (7 :: Int)]
-          upcastA _ = Right (object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (200 :: Int)])
-          upcastB _ = Right (object ["kind" .= ("AmountRenamed" :: T.Text), "amountInCents" .= (300 :: Int)])
-          rung (EventType "AmountScaled") = upcastA
-          rung (EventType "AmountRenamed") = upcastB
-          rung _ = Right
-          codec =
-            Codec
-              { eventTypes = EventType "AmountScaled" :| [EventType "AmountRenamed", EventType "AmountObserved"],
-                eventType = const (EventType "AmountObserved"),
-                schemaVersion = 2,
-                encode = id,
-                decode = \_ -> Right,
-                upcasters = [(1, rung)]
-              } ::
-              Codec Value
-      decodeRaw codec (EventType "AmountObserved") 1 foreignPayload `shouldBe` Right foreignPayload
-      decodeRaw codec (EventType "AmountScaled") 1 payloadA
-        `shouldBe` Right (object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (200 :: Int)])
-      decodeRaw codec (EventType "AmountRenamed") 1 payloadB
-        `shouldBe` Right (object ["kind" .= ("AmountRenamed" :: T.Text), "amountInCents" .= (300 :: Int)])
-    it "never emits a keiki symbolic operator into a Generated module (firewall)" $ do
-      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      firewallBreaches mods `shouldBe` []
-    it "marks the Holes module HoleStub and the rest Generated" $ do
-      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      let holes = [m | m <- mods, "Holes.hs" `T.isSuffixOf` T.pack (modulePath m)]
-      map kind holes `shouldBe` [HoleStub]
-      -- Domain, Codec, EventStream, Projection, Harness.
-      length [m | m <- mods, kind m == Generated] `shouldBe` 5
-    it "is deterministic (re-scaffolding yields byte-identical text)" $ do
-      a <- scaffoldFixture "test/fixtures/reservation.keiro"
-      b <- scaffoldFixture "test/fixtures/reservation.keiro"
-      map moduleText a `shouldBe` map moduleText b
-    it "keeps retiring as validator-only metadata in generated modules" $ do
-      ordinary <- scaffoldFixture "test/fixtures/reservation.keiro"
-      retiring <- scaffoldFixture "test/fixtures/reservation-retiring.keiro"
-      map (\m -> (modulePath m, kind m, moduleText m)) retiring
-        `shouldBe` map (\m -> (modulePath m, kind m, moduleText m)) ordinary
-    it "matches the committed compiling Generated conformance modules (modulo whitespace)" $ do
-      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      mapM_ assertMatchesCommitted [m | m <- mods, kind m == Generated]
-    it "matches every committed new-surface Generated module (modulo formatting)" $ do
-      spec <- specOf "test/fixtures/transfer-routing.keiro"
-      let modules = scaffoldModules (defaultContext (specContext spec)) spec
-      forM_ [m | m <- modules, kind m == Generated] $ \m -> do
-        committed <- readTestText ("test/conformance-newsurface/" <> modulePath m)
-        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
-    it "scaffolds the register-free OrderStream smoke target without error" $ do
-      mods <- scaffoldFixture "test/fixtures/order.keiro"
-      -- 5 Generated (Domain/Codec/EventStream/Projection/Harness) + 1 Holes.
-      length mods `shouldBe` 6
-      firewallBreaches mods `shouldBe` []
-      let harness = generatedTextEndingIn "Harness.hs" mods
-      harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: PlaceOrder from OrderNotStarted -- \""
-      harness `shouldSatisfy` T.isInfixOf "prefix <> \"final vertex\""
-      harness `shouldNotSatisfy` T.isInfixOf "prefix <> \"register "
-    it "emits forward/replay checks with field-distinct Text samples" $ do
-      spec <- parseInlineSpec "<forward-replay-samples>" (T.replace "command Bump { count:Int }" "command Bump { count:Int noteText:Text echo:Text }" loweringAggregateSpec)
-      case [aggregate | NAggregate aggregate <- specNodes spec] of
-        aggregate : _ -> do
-          let ctx = defaultContext (specContext spec)
-              harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx spec aggregate)
-          harness `shouldSatisfy` T.isInfixOf "\"sample-noteText\" \"sample-echo\""
-          harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: Bump from CounterPending -- \""
-          harness `shouldSatisfy` T.isInfixOf "prefix <> \"register note\""
-        [] -> expectationFailure "forward/replay sample spec has no aggregate"
-    it "emits the canonical reservation register checks" $ do
-      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      let harness = generatedTextEndingIn "Harness.hs" mods
-      harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: RequestTransferReservation from ReservationUnrequested -- \""
-      harness `shouldSatisfy` T.isInfixOf "prefix <> \"register reservationState\""
-    it "lowers a replay-only transition to B.replayOnly in the holes skeleton (plan 143)" $ do
-      twinMods <- scaffoldFixture "test/fixtures/reservation-guard-tightened-twin.keiro"
-      let twinHoles = [moduleText m | m <- twinMods, kind m == HoleStub]
-      twinHoles `shouldSatisfy` any (T.isInfixOf "B.replayOnly")
-      let twinHarness = generatedTextEndingIn "Harness.hs" twinMods
-      T.count "forwardReplayRequestTransferReservation ::" twinHarness `shouldBe` 1
-      plainMods <- scaffoldFixture "test/fixtures/reservation.keiro"
-      let plainHoles = [moduleText m | m <- plainMods, kind m == HoleStub]
-      plainHoles `shouldSatisfy` all (not . T.isInfixOf "B.replayOnly")
-
-  describe "service workspace (EP-153)" $ do
-    describe "manifest grammar" $ do
-      it "round-trips the canonical fixture manifest byte-for-byte" $ do
-        source <- readTestText canonicalWorkspacePath
-        manifest <- shouldParseManifest canonicalWorkspacePath source
-        wmfService manifest `shouldBe` "demo-project"
-        wmfModuleRoot manifest `shouldBe` Just "Demo.Modules.Project"
-        wmfLayout manifest `shouldBe` Just CollocatedLeaf
-        map wmrPath (NE.toList (wmfMembers manifest))
-          `shouldBe` [ "domain/project-artifact.keiro",
-                       "domain/project.keiro",
-                       "domain/shared.keiro"
-                     ]
-        renderWorkspaceManifest manifest
-          `shouldBe` T.intercalate
-            "\n"
-            [ "service demo-project",
-              "module Demo.Modules.Project",
-              "layout collocated",
-              "spec domain/project-artifact.keiro",
-              "spec domain/project.keiro",
-              "spec domain/shared.keiro"
-            ]
-      it "treats membership as a set: source order changes neither the AST nor the bytes" $ do
-        canonical <- readTestText canonicalWorkspacePath >>= shouldParseManifest canonicalWorkspacePath
-        reordered <-
-          shouldParseManifest "<reordered>" $
-            T.unlines
-              [ "service demo-project",
-                "layout collocated",
-                "spec domain/shared.keiro",
-                "module Demo.Modules.Project",
-                "spec domain/project.keiro",
-                "spec ./domain/project-artifact.keiro"
-              ]
-        reordered `shouldBe` canonical
-        renderWorkspaceManifest reordered `shouldBe` renderWorkspaceManifest canonical
-      it "satisfies parse . render == id and render . parse . render == render" $
-        property $
-          forAll genWorkspaceManifest $ \manifest ->
-            let rendered = renderWorkspaceManifest manifest
-             in case parseWorkspaceManifest "<generated>" rendered of
-                  Left err -> counterexample (T.unpack err) False
-                  Right reparsed ->
-                    counterexample (T.unpack rendered) $
-                      reparsed == manifest && renderWorkspaceManifest reparsed == rendered
-      it "recognizes a workspace manifest by extension, case-insensitively" $ do
-        map
-          isWorkspacePath
-          [ "service.keiro-workspace",
-            "a/b/Service.KEIRO-Workspace",
-            "service.keiro",
-            ".keiro-workspace",
-            "keiro-workspace"
-          ]
-          `shouldBe` [True, True, False, False, False]
-    describe "manifest refusals" $ do
-      let rejects description source expected =
-            it description $ case parseWorkspaceManifest "<manifest>" source of
-              Right _ -> expectationFailure ("expected a refusal, got a manifest for:\n" <> T.unpack source)
-              Left err -> T.unpack err `shouldContain` expected
-      rejects
-        "an empty manifest"
-        "# only a comment\n"
-        "must begin with a 'service <name>' clause"
-      rejects
-        "a manifest with no service clause"
-        "spec domain/a.keiro\n"
-        "first clause of a workspace manifest must be 'service <name>'"
-      rejects
-        "a manifest whose first clause is not service"
-        "module Demo\nservice demo\nspec domain/a.keiro\n"
-        "first clause of a workspace manifest must be 'service <name>'"
-      rejects
-        "a duplicate service clause"
-        "service demo\nservice demo\nspec domain/a.keiro\n"
-        "duplicate 'service' clause"
-      rejects
-        "a duplicate module clause"
-        "service demo\nmodule Demo\nmodule Demo\nspec domain/a.keiro\n"
-        "duplicate 'module' clause"
-      rejects
-        "a duplicate layout clause"
-        "service demo\nlayout prefixed\nlayout prefixed\nspec domain/a.keiro\n"
-        "duplicate 'layout' clause"
-      rejects
-        "a manifest with no members"
-        "service demo\nmodule Demo\n"
-        "must list at least one 'spec <path>.keiro' member"
-      rejects
-        "the same member listed twice"
-        "service demo\nspec domain/a.keiro\nspec ./domain/a.keiro\n"
-        "duplicate workspace member 'domain/a.keiro'"
-      rejects
-        "two members that differ only by case"
-        "service demo\nspec domain/a.keiro\nspec domain/A.keiro\n"
-        "differ only by case"
-      rejects
-        "an absolute member path"
-        "service demo\nspec /etc/a.keiro\n"
-        "must be relative, not absolute"
-      rejects
-        "a member path escaping the manifest directory"
-        "service demo\nspec ../escape.keiro\n"
-        "must not contain '..' segments"
-      rejects
-        "a member that is not a .keiro spec"
-        "service demo\nspec domain/a.txt\n"
-        "must name a .keiro spec"
-      rejects
-        "a manifest listing another manifest"
-        "service demo\nspec domain/other.keiro-workspace\n"
-        "must name a .keiro spec"
-    describe "line relocation" $ do
-      it "shifts every location the AST carries, and only the locations" $ do
-        spec <- specOf "test/fixtures/reservation.keiro"
-        let shifted = relocateLocs (+ 1000) spec
-        collectLocs spec `shouldSatisfy` (not . null)
-        collectLocs shifted `shouldBe` map (+ 1000) (collectLocs spec)
-        -- Loc's Eq deliberately ignores the line, so relocation cannot
-        -- change any equality-based behavior anywhere downstream.
-        shifted `shouldBe` spec
-      it "leaves the placeholder location alone so it never lands inside a member range" $ do
-        spec <- specOf "test/fixtures/reservation.keiro"
-        let blanked = relocateLocs (const 0) spec
-            reshifted = relocateLocs (\n -> if n <= 0 then n else n + 500) blanked
-        collectLocs reshifted `shouldBe` map (const 0) (collectLocs spec)
-    describe "composition" $ do
-      it "resolves cross-file ids, enums, mapped types, and read-model feeds" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        wsService workspace `shouldBe` "demo-project"
-        wsContext workspace `shouldBe` "demo-project"
-        wsModuleRoot workspace `shouldBe` Just "Demo.Modules.Project"
-        wsLayout workspace `shouldBe` Just CollocatedLeaf
-        map wmPath (wsMembers workspace)
-          `shouldBe` [ "domain/project-artifact.keiro",
-                       "domain/project.keiro",
-                       "domain/shared.keiro"
-                     ]
-        -- Every member is individually incomplete; together they check.
-        checkWorkspace workspace `shouldBe` []
-      it "records which member owns each shared declaration and node" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let ownership = wsOwnership workspace
-        fmap fst (declarationOwner ownership "id" "ProjectId")
-          `shouldBe` Just "domain/shared.keiro"
-        fmap fst (declarationOwner ownership "enum" "ProjectPhase")
-          `shouldBe` Just "domain/shared.keiro"
-        fmap fst (declarationOwner ownership "rule" "phaseIsTerminal")
-          `shouldBe` Just "domain/shared.keiro"
-        fmap fst (declarationOwner ownership "mapped" "ProjectSummary")
-          `shouldBe` Just "domain/shared.keiro"
-        fmap fst (nodeOwner ownership "aggregate" "Project")
-          `shouldBe` Just "domain/project.keiro"
-        fmap fst (nodeOwner ownership "aggregate" "ProjectArtifact")
-          `shouldBe` Just "domain/project-artifact.keiro"
-        fmap fst (nodeOwner ownership "readmodel" "project_activity")
-          `shouldBe` Just "domain/project-artifact.keiro"
-      it "maps every merged line back to the member that wrote it" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let bases = [(wmPath m, wmLineBase m, wmLineCount m) | m <- wsMembers workspace]
-        -- Ranges are disjoint and contiguous from zero.
-        map (\(_, base, _) -> base) bases `shouldBe` scanl (+) 0 (init [c | (_, _, c) <- bases])
-        sequence_
-          [ resolveWorkspaceLine workspace (base + offset) `shouldBe` Just (path, offset)
-          | (path, base, memberLines) <- bases,
-            offset <- [1, memberLines]
-          ]
-        resolveWorkspaceLine workspace 0 `shouldBe` Nothing
-      it "is insensitive to the order members are listed in" $ do
-        canonical <- shouldComposeWorkspace canonicalWorkspacePath
-        reordered <- shouldComposeWorkspace reorderedWorkspacePath
-        reordered {wsManifestPath = wsManifestPath canonical} `shouldBe` canonical
-      it "checks a single .keiro file as a one-member workspace, diagnostic for diagnostic" $ do
-        let fixtures =
-              [ "test/fixtures/reservation.keiro",
-                "test/fixtures/consumer-types.keiro",
-                "test/fixtures/aggregate-bad-refs.keiro",
-                "test/fixtures/readmodel.keiro"
-              ]
-        forM_ fixtures $ \path -> do
-          spec <- specOf path
-          let workspace = oneMemberWorkspace path spec
-              viaWorkspace = map (renderWorkspaceDiagnostic path) (checkWorkspace workspace)
-              direct = map (renderDiagnostic path) (validateSpec spec)
-          viaWorkspace `shouldBe` direct
-        -- At least one of those fixtures must actually produce errors,
-        -- or the equivalence claim is vacuous.
-        badRefs <- specOf "test/fixtures/aggregate-bad-refs.keiro"
-        checkWorkspace (oneMemberWorkspace "test/fixtures/aggregate-bad-refs.keiro" badRefs)
-          `shouldSatisfy` any ((== Error) . wdSeverity)
-    describe "composition refusals" $ do
-      let refusesWith path expectedCode expectedFiles = do
-            diagnostics <- shouldRefuseWorkspace path
-            map wdCode (NE.toList diagnostics) `shouldContain` [expectedCode]
-            let cited =
-                  [ wlFile location
-                  | diagnostic <- NE.toList diagnostics,
-                    wdCode diagnostic == expectedCode,
-                    location <- NE.toList (wdLocations diagnostic)
-                  ]
-            sort (nubOrd cited) `shouldBe` sort expectedFiles
-      it "refuses members that declare different contexts, citing every context clause" $
-        refusesWith
-          "test/fixtures/workspace-context-mismatch/service.keiro-workspace"
-          WorkspaceContextMismatch
-          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
-      it "refuses a member layout clause that contradicts the manifest authority" $
-        refusesWith
-          "test/fixtures/workspace-authority-conflict/service.keiro-workspace"
-          WorkspaceAuthorityConflict
-          [WorkspaceManifestFile, WorkspaceMemberFile "domain/b.keiro"]
-      it "refuses a textually identical shared declaration owned by two members" $
-        refusesWith
-          "test/fixtures/workspace-dup-decl/service.keiro-workspace"
-          WorkspaceDuplicateDeclaration
-          [WorkspaceMemberFile "domain/project.keiro", WorkspaceMemberFile "domain/shared.keiro"]
-      it "refuses one aggregate defined in two members" $
-        refusesWith
-          "test/fixtures/workspace-dup-node/service.keiro-workspace"
-          WorkspaceDuplicateNodeName
-          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
-      it "refuses generated paths that collide across members under case folding" $
-        refusesWith
-          "test/fixtures/workspace-path-collision/service.keiro-workspace"
-          WorkspacePathCollision
-          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
-      it "reports a listed member that is missing from disk" $
-        refusesWith
-          "test/fixtures/workspace-missing-member/service.keiro-workspace"
-          WorkspaceMemberUnreadable
-          [WorkspaceManifestFile]
-      it "reports a member that does not parse" $
-        refusesWith
-          "test/fixtures/workspace-member-parse-failed/service.keiro-workspace"
-          WorkspaceMemberParseFailed
-          [WorkspaceManifestFile]
-      it "surfaces a cross-file unresolved reference through the merged validator" $ do
-        workspace <- shouldComposeWorkspace "test/fixtures/workspace-unresolved/service.keiro-workspace"
-        let errors = [d | d <- checkWorkspace workspace, wdSeverity d == Error]
-        map wdCode errors `shouldContain` [GuardAtomOutOfScope]
-        [wlFile location | d <- errors, location <- NE.toList (wdLocations d)]
-          `shouldContain` [WorkspaceMemberFile "domain/project.keiro"]
-    describe "multi-file diagnostic rendering" $ do
-      it "puts the primary location in the established shape and every other file on a note line" $ do
-        diagnostics <- shouldRefuseWorkspace "test/fixtures/workspace-dup-decl/service.keiro-workspace"
-        let manifest = "keiro-dsl/test/fixtures/workspace-dup-decl/service.keiro-workspace"
-        map (renderWorkspaceDiagnostic manifest) (NE.toList diagnostics)
-          `shouldBe` [ T.intercalate
-                         "\n"
-                         [ "keiro-dsl/test/fixtures/workspace-dup-decl/domain/project.keiro:4: error[WorkspaceDuplicateDeclaration]: duplicate declaration 'ProjectId': a shared declaration has exactly one owning member (identical duplicates do not merge)",
-                           "  keiro-dsl/test/fixtures/workspace-dup-decl/domain/shared.keiro:4: note: also declared here, as id 'ProjectId'"
-                         ]
-                     ]
-    describe "whole-service check through the CLI" $ do
-      it "prints OK and exits zero for the composed fixture workspace" $ do
-        (exitCode, out, err) <- runKeiroDsl ["check", canonicalWorkspacePath]
-        unless (exitCode == ExitSuccess) (expectationFailure (out <> err))
-        lines out `shouldBe` ["OK"]
-      it "exits non-zero and names every involved file for a cross-file refusal" $ do
-        (exitCode, _, err) <-
-          runKeiroDsl ["check", "test/fixtures/workspace-dup-decl/service.keiro-workspace"]
-        exitCode `shouldBe` ExitFailure 1
-        err `shouldContain` "error[WorkspaceDuplicateDeclaration]"
-        err `shouldContain` "workspace-dup-decl/domain/project.keiro:4"
-        err `shouldContain` "workspace-dup-decl/domain/shared.keiro:4"
-      it "attributes a merged-graph validation error to the member that wrote it" $ do
-        (exitCode, _, err) <-
-          runKeiroDsl ["check", "test/fixtures/workspace-unresolved/service.keiro-workspace"]
-        exitCode `shouldBe` ExitFailure 1
-        err `shouldContain` "workspace-unresolved/domain/project.keiro:13: error[GuardAtomOutOfScope]"
-      it "produces byte-identical output for a manifest whose members are listed in reverse" $ do
-        (canonicalCode, canonicalOut, _) <- runKeiroDsl ["check", canonicalWorkspacePath, "--emit"]
-        (reorderedCode, reorderedOut, _) <- runKeiroDsl ["check", reorderedWorkspacePath, "--emit"]
-        canonicalCode `shouldBe` ExitSuccess
-        reorderedCode `shouldBe` ExitSuccess
-        reorderedOut `shouldBe` canonicalOut
-        (_, canonicalParse, _) <- runKeiroDsl ["parse", canonicalWorkspacePath]
-        (_, reorderedParse, _) <- runKeiroDsl ["parse", reorderedWorkspacePath]
-        reorderedParse `shouldBe` canonicalParse
-      it "keeps the single-file path working, byte for byte" $ do
-        (exitCode, out, err) <- runKeiroDsl ["check", "test/fixtures/reservation.keiro"]
-        unless (exitCode == ExitSuccess) (expectationFailure (out <> err))
-        lines out `shouldBe` ["OK"]
-      it "explains bindings and reports coverage against the merged graph" $ do
-        (bindingsCode, bindingsOut, _) <-
-          runKeiroDsl ["check", canonicalWorkspacePath, "--explain-bindings"]
-        bindingsCode `shouldBe` ExitSuccess
-        bindingsOut `shouldContain` "binding obligations for context demo-project"
-        -- The obligation's use sites span both aggregate members, which
-        -- is only possible because the graph was resolved once, merged.
-        bindingsOut `shouldContain` "Project register summary : ProjectSummary"
-        bindingsOut `shouldContain` "ProjectArtifact command RecordArtifact .artifactSummary : ProjectSummary"
-        withTempDirectory "keiro-dsl-workspace-coverage" $ \out -> do
-          let reportPath = out </> "coverage.json"
-          (coverageCode, coverageOut, _) <-
-            runKeiroDsl ["check", canonicalWorkspacePath, "--coverage-report", reportPath]
-          coverageCode `shouldBe` ExitSuccess
-          coverageOut `shouldContain` "structural/opaque boundaries (reporting only)"
-          report <- Aeson.eitherDecodeFileStrict reportPath
-          case report of
-            Left err -> expectationFailure err
-            Right value -> coverageSpecPath value `shouldBe` Just (T.pack canonicalWorkspacePath)
-
-  describe "workspace diff revision loading (EP-155 M1)" $ do
-    it "composes added, removed, and renamed members through an in-memory content source" $ do
-      project <- readTestText "test/fixtures/workspace/domain/project.keiro"
-      artifact <- readTestText "test/fixtures/workspace/domain/project-artifact.keiro"
-      shared <- readTestText "test/fixtures/workspace/domain/shared.keiro"
-      let extra = "context demo-project\n\nid ExtraId prefix=extra\n"
-          manifest members =
-            T.unlines
-              ( ["service demo-project", "module Demo.Modules.Project", "layout collocated"]
-                  <> ["spec " <> T.pack member | member <- members]
-              )
-          baseFiles =
-            Map.fromList
-              [ ("domain/project.keiro", project),
-                ("domain/project-artifact.keiro", artifact),
-                ("domain/shared.keiro", shared)
-              ]
-          loadFrom members files =
-            loadWorkspace
-              (memoryContentSource (Map.insert "service.keiro-workspace" (manifest members) files))
-              "service.keiro-workspace"
-          baseMembers = ["domain/project.keiro", "domain/project-artifact.keiro", "domain/shared.keiro"]
-          expectLoaded result = case result of
-            Left failure -> expectationFailure (show failure) >> error "unreachable"
-            Right workspace -> pure workspace
-
-      oldAdded <- loadFrom baseMembers baseFiles >>= expectLoaded
-      newAdded <-
-        loadFrom
-          (baseMembers <> ["domain/extra.keiro"])
-          (Map.insert "domain/extra.keiro" extra baseFiles)
-          >>= expectLoaded
-      map changeCode (diffSpecs (wsMergedSpec oldAdded) (wsMergedSpec newAdded))
-        `shouldContain` [DeclarationAdded]
-
-      oldRemoved <- loadFrom baseMembers baseFiles >>= expectLoaded
-      newRemoved <-
-        loadFrom
-          ["domain/project.keiro", "domain/shared.keiro"]
-          (Map.delete "domain/project-artifact.keiro" baseFiles)
-          >>= expectLoaded
-      map changeCode (diffSpecs (wsMergedSpec oldRemoved) (wsMergedSpec newRemoved))
-        `shouldContain` [EvtRemovedNotDeprecated]
-
-      oldRenamed <- loadFrom baseMembers baseFiles >>= expectLoaded
-      let renamedMembers = ["domain/project-renamed.keiro", "domain/project-artifact.keiro", "domain/shared.keiro"]
-          renamedFiles = Map.insert "domain/project-renamed.keiro" project (Map.delete "domain/project.keiro" baseFiles)
-      newRenamed <- loadFrom renamedMembers renamedFiles >>= expectLoaded
-      diffSpecs (wsMergedSpec oldRenamed) (wsMergedSpec newRenamed) `shouldBe` []
-
-  describe "workspace diff ownership and unified reports (EP-155 M2)" $ do
-    it "classifies shared declarations at use sites across every member with owned citations" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
-      let changes = diffWorkspaces old new
-          enumChanges = filter ((== EnumCtorAdded) . changeCode . wcChange) changes
-          mappedChanges = filter ((== MappedFieldTypeChanged) . changeCode . wcChange) changes
-          citedFiles workspaceChanges =
-            [ osFile site
-            | change <- workspaceChanges,
-              (_, Just site) <- wcUseSites change
-            ]
-      enumChanges `shouldSatisfy` (not . null)
-      mappedChanges `shouldSatisfy` (not . null)
-      let enumWireChanges =
-            [ change
-            | workspaceChange <- enumChanges,
-              let change = wcChange workspaceChange,
-              OldBinaryReadNewEvents `elem` breakingSurfaces change
-            ]
-      enumWireChanges `shouldSatisfy` (not . null)
-      enumWireChanges `shouldSatisfy` all (not . gatedBreaking defaultGate)
-      enumWireChanges `shouldSatisfy` all (gatedBreaking (gateWith [OldBinaryReadNewEvents]))
-      map (fmap osFile . wcDeclarationSite) (enumChanges <> mappedChanges)
-        `shouldSatisfy` all (== Just "domain/shared.keiro")
-      citedFiles enumChanges `shouldContain` ["domain/order.keiro", "domain/shipment.keiro"]
-      citedFiles mappedChanges `shouldContain` ["domain/order.keiro", "domain/shipment.keiro"]
-      let rendered = T.intercalate "\n" (map renderWorkspaceFinding (enumChanges <> mappedChanges))
-      rendered `shouldSatisfy` T.isInfixOf "    declared: domain/shared.keiro:4"
-      rendered `shouldSatisfy` T.isInfixOf "    use-site: Order"
-      rendered `shouldSatisfy` T.isInfixOf "(domain/order.keiro:"
-      rendered `shouldSatisfy` T.isInfixOf "(domain/shipment.keiro:"
-      golden <- readTestText "test/fixtures/workspace-diff-new/workspace.diff.golden"
-      T.unlines (map renderWorkspaceFinding changes) `shouldBe` golden
-
-    it "emits one additive version-1 report with workspace provenance" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
-      let changes = diffWorkspaces old new
-          meta =
-            WorkspaceMeta
-              { wmIdentity = wsService new,
-                wmManifest = "service.keiro-workspace",
-                wmSince = "HEAD",
-                wmMembersOld = map wmPath (wsMembers old),
-                wmMembersNew = map wmPath (wsMembers new),
-                wmAdoptionBaseline = False
-              }
-      case Aeson.toJSON (workspaceDiffReport meta defaultGate changes) of
-        Aeson.Object report -> do
-          KeyMap.lookup "schema" report `shouldBe` Just (Aeson.String "keiro-dsl/diff-report/1")
-          case KeyMap.lookup "workspace" report of
-            Just (Aeson.Object workspace) -> do
-              KeyMap.lookup "identity" workspace `shouldBe` Just (Aeson.String "workspace-diff")
-              KeyMap.lookup "adoptionBaseline" workspace `shouldBe` Just (Aeson.Bool False)
-            other -> expectationFailure ("missing workspace report metadata: " <> show other)
-          case KeyMap.lookup "findings" report of
-            Just (Aeson.Array findings) -> do
-              findings `shouldSatisfy` (not . null)
-              let objects = [finding | Aeson.Object finding <- toList findings]
-              objects `shouldSatisfy` any (KeyMap.member "declaration")
-              objects `shouldSatisfy` any (KeyMap.member "useSites")
-            other -> expectationFailure ("missing workspace findings: " <> show other)
-        other -> expectationFailure ("workspace report was not an object: " <> show other)
-
-    it "computes one replay-impact value over both aggregates" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
-      case ReplayImpact.replayImpact (wsMergedSpec old) (wsMergedSpec new) of
-        ReplayAffected affected -> Map.keysSet affected `shouldBe` Set.fromList ["Order", "Shipment"]
-        ReplayNeutral -> expectationFailure "shared mapped evolution unexpectedly reported replay-neutral"
-
-  describe "workspace ownership and authority changes (EP-155 M3)" $ do
-    it "reports an unchanged aggregate move once without wire evolution" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      moved <- shouldComposeWorkspace "test/fixtures/workspace-diff-moved/service.keiro-workspace"
-      let changes = diffWorkspaces old moved
-      map (changeCode . wcChange) changes `shouldBe` [OwnershipMoved]
-      forM_ changes $ \workspaceMove -> do
-        let move = wcChange workspaceMove
-        move `shouldSatisfy` isAdvisory
-        move `shouldSatisfy` (not . gatedBreaking defaultGate)
-        move `shouldSatisfy` (not . gatedBreaking (gateWith [minBound .. maxBound]))
-        deriveLabel defaultGate (ckVector (workspaceChangeKind move)) `shouldBe` LabelAdvisory
-        remediationFor (ckContext (workspaceChangeKind move)) OwnershipMoved
-          `shouldBe` (RemedyRescaffoldWorkspace :| [])
-        renderWorkspaceFinding workspaceMove
-          `shouldSatisfy` T.isInfixOf "declaration moved domain/shipment.keiro -> domain/order.keiro"
-
-    it "treats a member rename as the same owner-map change" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      let ownership = wsOwnership old
-          renamed =
-            old
-              { wsOwnership =
-                  ownership
-                    { oiNodes =
-                        Map.adjust
-                          (\(_, loc) -> ("domain/shipping.keiro", loc))
-                          ("aggregate", "Shipment")
-                          (oiNodes ownership)
-                    }
-              }
-          moves = filter ((== OwnershipMoved) . changeCode . wcChange) (diffWorkspaces old renamed)
-      length moves `shouldBe` 1
-      forM_ moves $ \move ->
-        renderWorkspaceFinding move `shouldSatisfy` T.isInfixOf "domain/shipment.keiro -> domain/shipping.keiro"
-
-    it "reports ownership motion beside an independently classified wire edit" $ do
-      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
-      edited <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
-      let ownership = wsOwnership edited
-          movedAndEdited =
-            edited
-              { wsOwnership =
-                  ownership
-                    { oiNodes =
-                        Map.adjust
-                          (\(_, loc) -> ("domain/order.keiro", loc))
-                          ("aggregate", "Shipment")
-                          (oiNodes ownership)
-                    }
-              }
-          codes = map (changeCode . wcChange) (diffWorkspaces old movedAndEdited)
-      codes `shouldContain` [OwnershipMoved]
-      codes `shouldContain` [MappedFieldTypeChanged]
-
-    it "reports context authority separately from derived read-model identity breaks" $ do
-      old <- shouldComposeWorkspace canonicalWorkspacePath
-      let newContext = "demo-project-renamed"
-          renamed =
-            old
-              { wsContext = newContext,
-                wsMergedSpec = (wsMergedSpec old) {specContext = newContext}
-              }
-          changes = diffWorkspaces old renamed
-          codes = map (changeCode . wcChange) changes
-      codes `shouldContain` [WorkspaceAuthorityChanged]
-      codes `shouldContain` [DerivedIdentityChanged]
-      map wcChange changes `shouldSatisfy` any (gatedBreaking defaultGate)
-
-    it "keeps service, module-root, and layout authority advisories non-blocking" $ do
-      old <- shouldComposeWorkspace canonicalWorkspacePath
-      let changed =
-            old
-              { wsService = "demo-project-renamed",
-                wsModuleRoot = Just "Demo.Modules.Renamed",
-                wsLayout = Just GeneratedPrefix
-              }
-          authority = filter ((== WorkspaceAuthorityChanged) . changeCode . wcChange) (diffWorkspaces old changed)
-      length authority `shouldBe` 3
-      forM_ (map wcChange authority) $ \change -> do
-        deriveLabel defaultGate (ckVector (workspaceChangeKind change)) `shouldBe` LabelAdvisory
-        change `shouldSatisfy` (not . gatedBreaking (gateWith [minBound .. maxBound]))
-        remediationFor (ckContext (workspaceChangeKind change)) WorkspaceAuthorityChanged
-          `shouldBe` (RemedyRescaffoldWorkspace :| [RemedyRecompileConsumers])
-
-  describe "workspace scaffold (EP-154)" $ do
-    describe "workspace record" $ do
-      it "round-trips modules, owners, members, mappings, obligations, and adoptions" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let record = sampleWorkspaceRecord workspace
-            rendered = renderWorkspaceRecord record
-        parseWorkspaceRecord rendered `shouldBe` Just record
-        -- The header pins the schema: a v1 context-keyed record and a
-        -- workspace record can never be read as each other.
-        T.lines rendered `shouldSatisfy` \case
-          header : _ -> header == "keiro-dsl workspace scaffold record v1"
-          [] -> False
-        parseRecord rendered `shouldBe` Nothing
-        parseWorkspaceRecord (T.replace "record v1" "record v2" rendered) `shouldBe` Nothing
-      it "ignores unknown rows and unknown JSON keys, and keeps context-level rows ownerless" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let record = sampleWorkspaceRecord workspace
-            rendered = renderWorkspaceRecord record
-        parseWorkspaceRecord (T.replace "service: " "future-row: retained\nservice: " rendered)
-          `shouldBe` Just record
-        parseWorkspaceRecord (T.replace "\"kind\":\"generated\"" "\"kind\":\"generated\",\"future\":1" rendered)
-          `shouldBe` Just record
-        [row | row <- wrModules record, wrmOwner row == Nothing]
-          `shouldSatisfy` (not . null)
-      it "derives absent language rows and rejects partial, duplicate, malformed, or inconsistent contracts" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let record = sampleWorkspaceRecord workspace
-            rendered = renderWorkspaceRecord record
-            sourceRows = filter ("source-language " `T.isPrefixOf`) (T.lines rendered)
-            withoutSourceRows = T.unlines (filter (not . T.isPrefixOf "source-language ") (T.lines rendered))
-            semanticRows = filter ("semantic-contract " `T.isPrefixOf`) (T.lines rendered)
-            withoutSemanticRows = T.unlines (filter (not . T.isPrefixOf "semantic-contract ") (T.lines rendered))
-            legacyRows = [WorkspaceSourceLanguageRow path LegacyUnversioned | path <- wrMembers record]
-        parseWorkspaceRecord withoutSourceRows
-          `shouldBe` Just record {wrSourceLanguages = legacyRows}
-        case sourceRows of
-          firstRow : secondRow : _ -> do
-            parseWorkspaceRecord (T.unlines (filter (/= secondRow) (T.lines rendered))) `shouldBe` Nothing
-            parseWorkspaceRecord (T.replace firstRow (firstRow <> "\n" <> firstRow) rendered) `shouldBe` Nothing
-            parseWorkspaceRecord (T.replace firstRow "source-language {malformed}" rendered) `shouldBe` Nothing
-          _ -> expectationFailure "expected multiple workspace source-language rows"
-        parseWorkspaceRecord withoutSemanticRows `shouldBe` Just record
-        case semanticRows of
-          [semanticRow] -> do
-            parseWorkspaceRecord (T.replace semanticRow (semanticRow <> "\n" <> semanticRow) rendered) `shouldBe` Nothing
-            parseWorkspaceRecord (T.replace semanticRow "semantic-contract {malformed}" rendered) `shouldBe` Nothing
-            parseWorkspaceRecord (T.replace "\"languageVersion\":1" "\"languageVersion\":2" rendered) `shouldBe` Nothing
-          _ -> expectationFailure "expected one workspace semantic-contract row"
-      it "rejects unsafe module, owner, member, and adoption paths" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let rendered = renderWorkspaceRecord (sampleWorkspaceRecord workspace)
-            corrupt from to = parseWorkspaceRecord (T.replace from to rendered)
-        corrupt "member domain/shared.keiro" "member /etc/passwd" `shouldBe` Nothing
-        corrupt "member domain/shared.keiro" "member ../escape.keiro" `shouldBe` Nothing
-        corrupt "\"owner\":\"domain/shared.keiro\"" "\"owner\":\"../shared.keiro\"" `shouldBe` Nothing
-        corrupt "\"path\":\"claimed/One.hs\"" "\"path\":\"/tmp/One.hs\"" `shouldBe` Nothing
-      it "keys history by service in a slot no context name can reach" $ do
-        -- A context name is lexed as letters/digits/_/- and can never
-        -- contain a dot, so the workspace slot cannot alias a legacy
-        -- record even when the service is named after its context.
-        workspaceRecordFileName "demo-project"
-          `shouldBe` "keiro-dsl-scaffold-record.workspace.demo-project.txt"
-        workspaceManifestFileName "demo-project"
-          `shouldBe` "keiro-dsl-manifest.workspace.demo-project.txt"
-        workspaceRecordFileName "demo-project" `shouldNotBe` recordFileName "demo-project"
-        map
-          (T.isInfixOf "." . T.pack)
-          [ workspaceRecordFileName "demo-project",
-            recordFileName "demo-project"
-          ]
-          `shouldBe` [True, True]
-        supersededByLine "demo-project"
-          `shouldBe` "superseded-by: keiro-dsl-scaffold-record.workspace.demo-project.txt"
-
-    describe "workspace plan" $ do
-      it "emits the context-level facade and replay-audit exactly once from the merged graph" $ do
-        plan <- shouldPlanWorkspace canonicalWorkspacePath
-        let modules = map fst (wpModules plan)
-            facades = [m | m <- modules, "StructuralProjections.hs" `isSuffixOfPath` m]
-            audits = [m | m <- modules, "ReplayAudit.hs" `isSuffixOfPath` m]
-            shapes = [m | m <- modules, "Structural/Shape/ProjectSummary.hs" `isSuffixOfPath` m]
-        length facades `shouldBe` 1
-        length audits `shouldBe` 1
-        length shapes `shouldBe` 1
-        -- The audit assembles aggregates owned by two different member
-        -- files, which is only possible from one merged graph.
-        forM_ audits $ \audit -> do
-          moduleText audit `shouldSatisfy` T.isInfixOf "Project.projectEventStream"
-          moduleText audit `shouldSatisfy` T.isInfixOf "ProjectArtifact.projectArtifactEventStream"
-      it "gives every generated ID and enum one context owner and imports only aggregate uses" $ do
-        plan <- shouldPlanWorkspace canonicalWorkspacePath
-        let ctx = wpContext plan
-            modules = map fst (wpModules plan)
-            nominalModules = [m | m <- modules, modulePath m == T.unpack (T.replace "." "/" (generatedNominalModule ctx) <> ".hs")]
-            domainFor suffix = case [m | m <- modules, suffix `isSuffixOfPath` m] of
-              [m] -> pure m
-              found -> expectationFailure ("expected one domain ending in " <> suffix <> ", got " <> show (map modulePath found)) >> fail "unreachable"
-        ownerModule <- case nominalModules of
-          [m] -> pure m
-          found -> expectationFailure ("expected one generated nominal owner, got " <> show (map modulePath found)) >> fail "unreachable"
-        let nominalText = moduleText ownerModule
-        T.count "newtype ProjectId" nominalText `shouldBe` 1
-        T.count "data ProjectPhase =" nominalText `shouldBe` 1
-        T.count "data WorkspaceVisibility =" nominalText `shouldBe` 1
-        projectDomain <- domainFor "Project/Generated/Domain.hs"
-        artifactDomain <- domainFor "ProjectArtifact/Generated/Domain.hs"
-        forM_ [projectDomain, artifactDomain] $ \domain -> do
-          moduleText domain `shouldSatisfy` (not . T.isInfixOf "newtype ProjectId")
-          moduleText domain `shouldSatisfy` (not . T.isInfixOf "data ProjectPhase")
-          moduleText domain `shouldSatisfy` T.isInfixOf (generatedNominalModule ctx <> " (ProjectId (..), ProjectPhase (..))")
-          moduleText domain `shouldSatisfy` (not . T.isInfixOf "WorkspaceVisibility")
-        singleFileModules <- case planScaffold ctx (wsMergedSpec (wpWorkspace plan)) of
-          Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
-          Right values -> pure values
-        let withoutOrigin m = (modulePath m, moduleText m, kind m)
-        map withoutOrigin singleFileModules `shouldBe` map withoutOrigin modules
-        owners <- case planNominalGeneration ctx (wsMergedSpec (wpWorkspace plan)) of
-          Left errors -> expectationFailure (show errors) >> fail "unreachable"
-          Right values -> pure values
-        map (resolvedNominalName . nominalDeclaration) owners
-          `shouldBe` ["ProjectId", "ProjectPhase", "WorkspaceVisibility"]
-        case [owner | owner <- owners, resolvedNominalName (nominalDeclaration owner) == "ProjectId"] of
-          [owner] -> do
-            nominalModule owner `shouldBe` generatedNominalModule ctx
-            Set.fromList [NominalUseSite "Project" RegisterUse, NominalUseSite "ProjectArtifact" EventFieldUse]
-              `shouldSatisfy` (`Set.isSubsetOf` nominalUseSites owner)
-          found -> expectationFailure ("expected one ProjectId owner, got " <> show (length found))
-      it "attributes every module to its owning member and leaves shared ones context-level" $ do
-        plan <- shouldPlanWorkspace canonicalWorkspacePath
-        let memberPaths = map wmPath (wsMembers (wpWorkspace plan))
-            ownerOf suffix =
-              case [provenance | (m, provenance) <- wpModules plan, suffix `isSuffixOfPath` m] of
-                [provenance] -> Just provenance
-                _ -> Nothing
-        ownerOf "StructuralProjections.hs" `shouldBe` Just ContextLevel
-        ownerOf "Generated/Nominals.hs" `shouldBe` Just ContextLevel
-        ownerOf "ReplayAudit.hs" `shouldBe` Just ContextLevel
-        ownerOf "Structural/Shape/ProjectSummary.hs"
-          `shouldBe` Just (MemberOwned "domain/shared.keiro")
-        ownerOf "Project/Generated/Domain.hs"
-          `shouldBe` Just (MemberOwned "domain/project.keiro")
-        ownerOf "ProjectArtifact/Generated/Domain.hs"
-          `shouldBe` Just (MemberOwned "domain/project-artifact.keiro")
-        ownerOf "Project_activity/Generated/ReadModel.hs"
-          `shouldBe` Just (MemberOwned "domain/project-artifact.keiro")
-        -- No module may claim an owner that is not a member of the
-        -- workspace: the record's owner column has to stay resolvable.
-        map (provenanceOwner . snd) (wpModules plan)
-          `shouldSatisfy` all (maybe True (`elem` memberPaths))
-      it "keeps the compiled workspace nominal conformance tree byte-current" $ do
-        workspace <- shouldComposeWorkspace "test/fixtures/workspace-nominals/service.keiro-workspace"
-        plan <- shouldPlanWorkspaceSpec workspace
-        let compiledPaths =
-              [ "Generated/WorkspaceNominalProof/Nominals.hs",
-                "Generated/WorkspaceNominalProof/Project/Domain.hs",
-                "Generated/WorkspaceNominalProof/Project/Codec.hs",
-                "Generated/WorkspaceNominalProof/Project/Expressions.hs",
-                "Generated/WorkspaceNominalProof/Project/Transducer.hs",
-                "Generated/WorkspaceNominalProof/Project/BehaviorContract.hs",
-                "Generated/WorkspaceNominalProof/Project/EventStream.hs",
-                "Generated/WorkspaceNominalProof/Project/Harness.hs",
-                "Generated/WorkspaceNominalProof/Project/Projection.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Expressions.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs",
-                "Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs",
-                "Generated/WorkspaceNominalProof/ReplayAudit.hs"
-              ]
-        map fst (wpModules plan) `shouldSatisfy` all (not . isSuffixOfPath "/Holes.hs")
-        forM_ compiledPaths $ \path ->
-          case [m | (m, _) <- wpModules plan, modulePath m == path] of
-            [generated] -> do
-              committed <- readTestText ("test/conformance-workspace-nominals/" <> path)
-              normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generated)
-            found -> expectationFailure ("expected one generated module at " <> path <> ", got " <> show (map modulePath found))
-      it "plans a one-member workspace byte-identically to the single-file path" $ do
-        let fixtures =
-              [ "test/fixtures/reservation.keiro",
-                "test/fixtures/consumer-types.keiro",
-                "test/fixtures/readmodel.keiro",
-                "test/fixtures/hospital-surge.keiro"
-              ]
-        -- Modules and refusals both: hospital-surge refuses on both
-        -- paths, which proves the gates agree as well as the emitters.
-        forM_ fixtures $ \path -> do
-          spec <- specOf path
-          let ctx = defaultContext (specContext spec)
-              workspace = oneMemberWorkspace path spec
-          fmap (map fst . wpModules) (planWorkspaceScaffold "goldens" ctx workspace)
-            `shouldBe` planScaffold ctx spec
-        -- The equality is not vacuous: at least one fixture plans, and
-        -- its per-node modules are attributed to the single member.
-        spec <- specOf "test/fixtures/reservation.keiro"
-        let workspace = oneMemberWorkspace "test/fixtures/reservation.keiro" spec
-        case planWorkspaceScaffold "goldens" (defaultContext (specContext spec)) workspace of
-          Left refusals -> expectationFailure ("reservation should plan: " <> show refusals)
-          Right plan -> do
-            wpModules plan `shouldSatisfy` (not . null)
-            map snd (wpModules plan)
-              `shouldSatisfy` all (`elem` [ContextLevel, MemberOwned "reservation.keiro"])
-            map snd (wpModules plan)
-              `shouldSatisfy` elem (MemberOwned "reservation.keiro")
-      it "computes obligations from the complete merged graph, spanning members" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        case bindingObligations (wsMergedSpec workspace) of
-          Left graphErrors -> expectationFailure ("merged graph did not resolve: " <> show graphErrors)
-          Right obligations ->
-            case [o | o <- obligations, obligationMappedName o == "ProjectSummary", obligationKind o == BindingValue] of
-              [obligation] -> do
-                obligationUseSites obligation
-                  `shouldSatisfy` any (T.isInfixOf "Project register summary")
-                obligationUseSites obligation
-                  `shouldSatisfy` any (T.isInfixOf "ProjectArtifact command RecordArtifact")
-              found -> expectationFailure ("expected one ProjectSummary binding obligation, got " <> show (length found))
-      it "refuses a case-folded path collision across members, naming both files" $ do
-        workspace <- shouldComposeWorkspace canonicalWorkspacePath
-        let collided = withCaseVariantAggregate workspace
-        case planWorkspaceScaffold "goldens" (workspaceContext collided) collided of
-          Right _ -> expectationFailure "expected a cross-member path collision refusal"
-          Left refusals -> do
-            let origins = concat [os | PathCollision _ os <- refusals]
-            origins `shouldSatisfy` any (T.isInfixOf "domain/project.keiro: ")
-            origins `shouldSatisfy` any (T.isInfixOf "domain/project-artifact.keiro: ")
-      it "refuses golden fixtures stranded beside a member instead of under the workspace root" $
-        withTempDirectory "keiro-dsl-workspace-goldens" $ \root -> do
-          workspace <- writeGoldenWorkspace root
-          let workspaceGoldens = root </> "golden-payloads"
-              fixture = "hospital-capacity/Reservation/TransferReservationCreated.v1.json"
-              beside = root </> "domain/golden-payloads" </> fixture
-          goldenRootDivergence workspaceGoldens workspace `shouldReturn` []
-          createDirectoryIfMissing True (takeDirectory beside)
-          TIO.writeFile beside "{}\n"
-          refusals <- goldenRootDivergence workspaceGoldens workspace
-          refusals `shouldBe` [GoldenRootDivergence workspaceGoldens [beside]]
-          renderRefusals refusals
-            `shouldSatisfy` any (T.isInfixOf "one golden root per workspace")
-          -- The same fixture under the workspace root is no divergence.
-          let atRoot = workspaceGoldens </> fixture
-          createDirectoryIfMissing True (takeDirectory atRoot)
-          TIO.writeFile atRoot "{}\n"
-          goldenRootDivergence workspaceGoldens workspace `shouldReturn` []
-
-    describe "workspace scaffold" $ do
-      it "writes workspace-keyed history and no context-keyed file at all" $
-        withWorkspaceFixture "keiro-dsl-workspace-history" id $ \_ out workspace -> do
-          report <- executePlannedWorkspaceScaffold out workspace
-          wsrRecordPath report
-            `shouldBe` out </> "keiro-dsl-scaffold-record.workspace.demo-project.txt"
-          wsrBuildManifestPath report
-            `shouldBe` out </> "keiro-dsl-manifest.workspace.demo-project.txt"
-          doesFileExist (out </> recordFileName "demo-project") `shouldReturn` False
-          doesFileExist (out </> "keiro-dsl-manifest.demo-project.txt") `shouldReturn` False
-          contents <- TIO.readFile (wsrRecordPath report)
-          case parseWorkspaceRecord contents of
-            Nothing -> expectationFailure ("workspace record did not parse:\n" <> T.unpack contents)
-            Just record -> do
-              wrService record `shouldBe` "demo-project"
-              wrManifest record `shouldBe` "service.keiro-workspace"
-              wrMembers record
-                `shouldBe` [ "domain/project-artifact.keiro",
-                             "domain/project.keiro",
-                             "domain/shared.keiro"
-                           ]
-              -- Context-level modules are ownerless; everything
-              -- else names the member that produced it.
-              [wrmPath row | row <- wrModules record, wrmOwner row == Nothing]
-                `shouldSatisfy` \ownerless ->
-                  length ownerless == 3
-                    && any (T.isSuffixOf "StructuralProjections.hs" . T.pack) ownerless
-                    && any (T.isSuffixOf "Nominals.hs" . T.pack) ownerless
-                    && any (T.isSuffixOf "ReplayAudit.hs" . T.pack) ownerless
-              [ wrmOwner row
-                | row <- wrModules record,
-                  "Project/Generated/Domain.hs" `T.isSuffixOf` T.pack (wrmPath row)
-                ]
-                `shouldBe` [Just "domain/project.keiro"]
-      it "is idempotent: an unchanged second run rewrites nothing and reports nothing" $
-        withWorkspaceFixture "keiro-dsl-workspace-idempotent" id $ \_ out workspace -> do
-          first <- executePlannedWorkspaceScaffold out workspace
-          before <- treeSnapshot out
-          second <- executePlannedWorkspaceScaffold out workspace
-          after <- treeSnapshot out
-          after `shouldBe` before
-          map thd3 (wsrDispositions second)
-            `shouldSatisfy` all (`elem` [Unchanged, Skipped])
-          wsrStale second `shouldBe` []
-          wsrOwnershipMoves second `shouldBe` []
-          wsrMappingDrift second `shouldBe` []
-          wsrNewHoles second `shouldBe` []
-          -- The first run had to write; the claim is not vacuous.
-          map thd3 (wsrDispositions first) `shouldSatisfy` any (== Overwritten)
-          renderWorkspaceScaffoldReport second
-            `shouldSatisfy` all (not . T.isPrefixOf "stale:")
-      it "produces byte-identical output for members listed in reverse order" $
-        withWorkspaceFixture "keiro-dsl-workspace-order-a" id $ \_ outA workspaceA ->
-          withWorkspaceFixture "keiro-dsl-workspace-order-b" reverse $ \_ outB workspaceB -> do
-            _ <- executePlannedWorkspaceScaffold outA workspaceA
-            _ <- executePlannedWorkspaceScaffold outB workspaceB
-            treeB <- treeSnapshot outB
-            treeA <- treeSnapshot outA
-            treeB `shouldBe` treeA
-            map fst treeA `shouldSatisfy` elem "keiro-dsl-scaffold-record.workspace.demo-project.txt"
-      it "reports stale files only for the member that changed" $
-        withWorkspaceFixture "keiro-dsl-workspace-stale" id $ \root out workspace -> do
-          first <- executePlannedWorkspaceScaffold out workspace
-          let siblingPaths =
-                [ modulePath m
-                | (m, provenance, _) <- wsrDispositions first,
-                  provenance == MemberOwned "domain/project-artifact.keiro"
-                ]
-          siblingsBefore <- traverse (TIO.readFile . (out </>)) siblingPaths
-          renamed <- renameMemberAggregate root "domain/project.keiro" "Project" "Ledger"
-          second <- executePlannedWorkspaceScaffold out renamed
-          let stalePaths = map stalePath (wsrStale second)
-          stalePaths `shouldSatisfy` (not . null)
-          stalePaths `shouldSatisfy` all (T.isInfixOf "/Project/" . T.pack)
-          -- Nothing the sibling member owns is stale, and nothing it
-          -- owns changed on disk: no cross-member false positives.
-          stalePaths `shouldSatisfy` all (`notElem` siblingPaths)
-          siblingsAfter <- traverse (TIO.readFile . (out </>)) siblingPaths
-          siblingsAfter `shouldBe` siblingsBefore
-          forM_ stalePaths $ \path -> doesFileExist (out </> path) `shouldReturn` True
-          renderWorkspaceScaffoldReport second
-            `shouldSatisfy` any (T.isInfixOf "keiro-dsl never deletes files.")
-      it "reports an aggregate moved between members as an ownership move, not stale churn" $
-        withWorkspaceFixture "keiro-dsl-workspace-move" id $ \root out workspace -> do
-          _ <- executePlannedWorkspaceScaffold out workspace
-          before <- treeSnapshot out
-          moved <- moveArtifactAggregate root
-          second <- executePlannedWorkspaceScaffold out moved
-          wsrStale second `shouldBe` []
-          let moves = wsrOwnershipMoves second
-          moves `shouldSatisfy` (not . null)
-          moves
-            `shouldSatisfy` all
-              ( \move ->
-                  omPrevious move == Just "domain/project-artifact.keiro"
-                    && omCurrent move == Just "domain/project.keiro"
-              )
-          map omPath moves
-            `shouldSatisfy` any (T.isInfixOf "ProjectArtifact" . T.pack)
-          -- An ownership move is not a content change: every module's
-          -- bytes, and the build manifest, are untouched.
-          map thd3 (wsrDispositions second)
-            `shouldSatisfy` all (`elem` [Unchanged, Skipped])
-          after <- treeSnapshot out
-          map fst after `shouldBe` map fst before
-          [(path, text) | (path, text) <- after, not ("scaffold-record" `T.isInfixOf` T.pack path)]
-            `shouldBe` [(path, text) | (path, text) <- before, not ("scaffold-record" `T.isInfixOf` T.pack path)]
-          renderWorkspaceScaffoldReport second
-            `shouldSatisfy` any (T.isInfixOf "changed owning member")
-      it "leaves the tree, record, and manifest untouched when any member refuses" $
-        withWorkspaceFixture "keiro-dsl-workspace-atomic" id $ \_ out workspace -> do
-          _ <- executePlannedWorkspaceScaffold out workspace
-          before <- treeSnapshot out
-          let broken = withCaseVariantAggregate workspace
-          case planWorkspaceScaffold "goldens" (workspaceContext broken) broken of
-            Right _ -> expectationFailure "expected the broken workspace to refuse"
-            Left refusals -> refusals `shouldSatisfy` any isPathCollision
-          treeSnapshot out `shouldReturn` before
-          -- A fresh output directory is never even created.
-          withTempDirectory "keiro-dsl-workspace-atomic-fresh" $ \fresh -> do
-            let target = fresh </> "out"
-            case planWorkspaceScaffold "goldens" (workspaceContext broken) broken of
-              Right _ -> expectationFailure "expected the broken workspace to refuse"
-              Left _ -> doesDirectoryExist target `shouldReturn` False
-      it "leaves prior workspace output byte-identical for parse, validation, and collision failures" $
-        withWorkspaceFixture "keiro-dsl-workspace-atomic-cli" id $ \root out workspace -> do
-          _ <- executePlannedWorkspaceScaffold out workspace
-          before <- treeSnapshot out
-          let member = root </> "domain/project-artifact.keiro"
-              manifest = root </> "service.keiro-workspace"
-          original <- TIO.readFile member
-          let failures =
-                [ ("parse", "context demo-project\naggregate !!!\n"),
-                  ("validation", T.replace "ProjectId" "MissingProjectId" original),
-                  ("collision", T.replace "aggregate ProjectArtifact" "aggregate PROJECT" original)
-                ]
-          forM_ failures $ \(failureKind, brokenSource) -> do
-            TIO.writeFile member brokenSource
-            (exitCode, stdoutText, stderrText) <-
-              runKeiroDsl ["scaffold", manifest, "--out", out]
-            unless (exitCode == ExitFailure 1) $
-              expectationFailure
-                (failureKind <> " failure unexpectedly scaffolded:\n" <> stdoutText <> stderrText)
-            treeSnapshot out `shouldReturn` before
-            TIO.writeFile member original
-      it "refuses the whole workspace for one bannerless Generated target, changing nothing" $
-        withWorkspaceFixture "keiro-dsl-workspace-banner" id $ \_ out workspace -> do
-          plan <- shouldPlanWorkspaceSpec workspace
-          let generated = [m | (m, _) <- wpModules plan, kind m == Generated]
-          case generated of
-            [] -> expectationFailure "workspace fixture has no Generated module"
-            target : _ -> do
-              let path = out </> modulePath target
-              createDirectoryIfMissing True (takeDirectory path)
-              TIO.writeFile path "hand owned\n"
-              before <- treeSnapshot out
-              refused <- executeWorkspaceScaffold out False plan
-              refused `shouldSatisfy` isMissingBannerRefusal
-              treeSnapshot out `shouldReturn` before
-              forced <- executeWorkspaceScaffold out True plan
-              forced `shouldSatisfy` isSuccessfulScaffold
-              TIO.readFile path `shouldReturn` moduleText target
-      it "scaffolds a whole workspace through the CLI" $
-        withTempDirectory "keiro-dsl-workspace-cli" $ \out -> do
-          (exitCode, stdoutText, stderrText) <-
-            runKeiroDsl ["scaffold", canonicalWorkspacePath, "--out", out]
-          unless (exitCode == ExitSuccess) (expectationFailure (stdoutText <> stderrText))
-          stderrText `shouldContain` "workspace: demo-project"
-          doesFileExist (out </> "keiro-dsl-scaffold-record.workspace.demo-project.txt")
-            `shouldReturn` True
-          tree <- treeSnapshot out
-          length [path | (path, _) <- tree, "StructuralProjections.hs" `T.isSuffixOf` T.pack path]
-            `shouldBe` 1
-          length [path | (path, _) <- tree, "ReplayAudit.hs" `T.isSuffixOf` T.pack path]
-            `shouldBe` 1
-          (secondCode, _, secondErr) <-
-            runKeiroDsl ["scaffold", canonicalWorkspacePath, "--out", out]
-          secondCode `shouldBe` ExitSuccess
-          secondErr `shouldSatisfy` (not . isInfixOfString "(overwritten)")
-          treeSnapshot out `shouldReturn` tree
-
-    describe "workspace adoption" $ do
-      it "replaces embedded 0.6 nominal declarations only in generated files" $
-        withWorkspaceFixture "keiro-dsl-workspace-nominal-adopt" id $ \_ out workspace -> do
-          plan <- shouldPlanWorkspaceSpec workspace
-          let pathEndingIn suffix selectedKind =
-                case [modulePath m | (m, _) <- wpModules plan, kind m == selectedKind, suffix `isSuffixOfPath` m] of
-                  [path] -> pure path
-                  found -> expectationFailure ("expected one path ending in " <> suffix <> ", got " <> show found) >> fail "unreachable"
-          domainPath <- pathEndingIn "Project/Generated/Domain.hs" Generated
-          nominalPath <- pathEndingIn "Generated/Nominals.hs" Generated
-          holePath <- pathEndingIn "Project/Holes.hs" HoleStub
-          writeFileWithParents
-            (out </> domainPath)
-            "-- @generated by keiro-dsl; legacy 0.6 fixture\nmodule LegacyDomain where\nnewtype ProjectId = ProjectId String\ndata ProjectPhase = Draft | Active\n"
-          writeFileWithParents (out </> holePath) "-- hand-owned 0.6 implementation\n"
-
-          report <- executePlannedWorkspaceScaffold out workspace
-          wsrStale report `shouldBe` []
-          [disposition | (m, _, disposition) <- wsrDispositions report, modulePath m == domainPath]
-            `shouldBe` [Overwritten]
-          [disposition | (m, _, disposition) <- wsrDispositions report, modulePath m == nominalPath]
-            `shouldBe` [Overwritten]
-          newDomain <- TIO.readFile (out </> domainPath)
-          newDomain `shouldSatisfy` (not . T.isInfixOf "newtype ProjectId")
-          newDomain `shouldSatisfy` T.isInfixOf "Generated.Nominals (ProjectId (..), ProjectPhase (..))"
-          newNominals <- TIO.readFile (out </> nominalPath)
-          T.count "newtype ProjectId" newNominals `shouldBe` 1
-          T.count "data ProjectPhase =" newNominals `shouldBe` 1
-          TIO.readFile (out </> holePath) `shouldReturn` "-- hand-owned 0.6 implementation\n"
-      it "adopts an overwritten same-context record pair by record and by banner" $
-        withInlineWorkspace "keiro-dsl-workspace-adopt" adoptionMembers $ \_ out workspace -> do
-          -- Reproduce today's defect first: two same-context specs
-          -- scaffolded independently into one directory, the second
-          -- replacing the first's record and calling its files stale.
-          specA <- parseInlineSpec "domain/a.keiro" adoptionMemberA
-          specB <- parseInlineSpec "domain/b.keiro" adoptionMemberB
-          let ctx = defaultContext "adoption-demo"
-          legacyA <- executePlannedScaffold out "domain/a.keiro" ctx specA
-          legacyB <- executePlannedScaffold out "domain/b.keiro" ctx specB
-          reportStale legacyB `shouldSatisfy` (not . null)
-          legacyBefore <- TIO.readFile (out </> recordFileName "adoption-demo")
-
-          report <- executePlannedWorkspaceScaffold out workspace
-          wsrStale report `shouldBe` []
-          case wsrMigration report of
-            Nothing -> expectationFailure "expected the first workspace run to adopt"
-            Just migration -> do
-              let generatedOf run = sort [modulePath m | (m, _) <- reportDispositions run, kind m == Generated]
-                  claimedBy evidence = sort [cfPath entry | entry <- mrClaimed migration, cfEvidence entry == evidence]
-              -- The surviving record attributes B's files; A's
-              -- files survived only as banners, which is exactly
-              -- the orphan case the overwrite created.
-              claimedBy ClaimedFromRecord `shouldBe` generatedOf legacyB
-              claimedBy ClaimedFromBanner `shouldBe` sort (generatedOf legacyA \\ generatedOf legacyB)
-              claimedBy ClaimedFromBanner `shouldSatisfy` (not . null)
-              mrLikelyStale migration `shouldBe` []
-              mrLegacyRecord migration
-                `shouldBe` Just (recordFileName "adoption-demo", "domain/b.keiro")
-              -- Provenance is persisted, not merely printed.
-              recorded <- parseWorkspaceRecord <$> TIO.readFile (wsrRecordPath report)
-              fmap (sort . map adPath . wrAdopted) recorded
-                `shouldBe` Just (sort (map cfPath (mrClaimed migration)))
-              fmap (sort . nubOrd . map adEvidence . wrAdopted) recorded
-                `shouldBe` Just ["banner", "record"]
-              persisted <- TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
-              persisted `shouldBe` T.unlines (renderMigrationReport migration)
-              renderWorkspaceScaffoldReport report
-                `shouldSatisfy` any (T.isInfixOf "adopting pre-workspace scaffold output")
-
-          -- The legacy record gained one line and nothing else: it
-          -- still parses to the same value for an old binary.
-          legacyAfter <- TIO.readFile (out </> recordFileName "adoption-demo")
-          T.lines legacyAfter `shouldSatisfy` elem (supersededByLine "adoption-demo")
-          parseRecord legacyAfter `shouldBe` parseRecord legacyBefore
-          T.lines legacyAfter
-            `shouldBe` T.lines legacyBefore <> [supersededByLine "adoption-demo"]
-
-          -- Adoption is not a content change: the generated tree is
-          -- what a fresh workspace scaffold of the same members emits.
-          withInlineWorkspace "keiro-dsl-workspace-adopt-fresh" adoptionMembers $ \_ fresh freshWorkspace -> do
-            freshReport <- executePlannedWorkspaceScaffold fresh freshWorkspace
-            wsrMigration freshReport `shouldBe` Nothing
-            adoptedTree <- treeSnapshot out
-            freshTree <- treeSnapshot fresh
-            haskellOnly adoptedTree `shouldBe` haskellOnly freshTree
-      it "lists hand-written files as unclaimed and leaves their bytes alone" $
-        withInlineWorkspace "keiro-dsl-workspace-unclaimed" adoptionMembers $ \_ out workspace -> do
-          plan <- shouldPlanWorkspaceSpec workspace
-          case [modulePath m | (m, _) <- wpModules plan, kind m == HoleStub] of
-            [] -> expectationFailure "adoption fixture emits no hole module"
-            holePath : _ -> do
-              writeFileWithParents (out </> holePath) "-- hand filled\n"
-              writeFileWithParents (out </> "Notes.hs") "module Notes where\n"
-              report <- executePlannedWorkspaceScaffold out workspace
-              case wsrMigration report of
-                Nothing -> expectationFailure "expected a report for a directory holding hand-written files"
-                Just migration -> do
-                  mrLegacyRecord migration `shouldBe` Nothing
-                  mrClaimed migration `shouldBe` []
-                  mrUnclaimed migration `shouldBe` sort [holePath, "Notes.hs"]
-              TIO.readFile (out </> holePath) `shouldReturn` "-- hand filled\n"
-              TIO.readFile (out </> "Notes.hs") `shouldReturn` "module Notes where\n"
-      it "never claims a bannerless file at a planned Generated path" $
-        withInlineWorkspace "keiro-dsl-workspace-unattributable" adoptionMembers $ \_ out workspace -> do
-          plan <- shouldPlanWorkspaceSpec workspace
-          case [modulePath m | (m, _) <- wpModules plan, kind m == Generated] of
-            [] -> expectationFailure "adoption fixture emits no Generated module"
-            target : _ -> do
-              writeFileWithParents (out </> target) "hand owned\n"
-              refused <- executeWorkspaceScaffold out False plan
-              refused `shouldSatisfy` isMissingBannerRefusal
-              TIO.readFile (out </> target) `shouldReturn` "hand owned\n"
-              doesFileExist (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
-                `shouldReturn` False
-      it "adopts at most once, and the second run is an ordinary idempotent run" $
-        withInlineWorkspace "keiro-dsl-workspace-adopt-once" adoptionMembers $ \_ out workspace -> do
-          specA <- parseInlineSpec "domain/a.keiro" adoptionMemberA
-          _ <- executePlannedScaffold out "domain/a.keiro" (defaultContext "adoption-demo") specA
-          first <- executePlannedWorkspaceScaffold out workspace
-          wsrMigration first `shouldSatisfy` \case Just _ -> True; Nothing -> False
-          before <- treeSnapshot out
-          reportBefore <- TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
-          legacyBefore <- TIO.readFile (out </> recordFileName "adoption-demo")
-
-          second <- executePlannedWorkspaceScaffold out workspace
-          wsrMigration second `shouldBe` Nothing
-          wsrStale second `shouldBe` []
-          map thd3 (wsrDispositions second) `shouldSatisfy` all (`elem` [Unchanged, Skipped])
-          treeSnapshot out `shouldReturn` before
-          TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
-            `shouldReturn` reportBefore
-          legacyAfter <- TIO.readFile (out </> recordFileName "adoption-demo")
-          legacyAfter `shouldBe` legacyBefore
-          length (filter (== supersededByLine "adoption-demo") (T.lines legacyAfter))
-            `shouldBe` 1
-
-comparisonProvenance :: CompareProvenance
-comparisonProvenance =
-  CompareProvenance
-    { cpHistoricalCodecIdentity = "example.historical",
-      cpHistoricalCodecVersion = "legacy-v1",
-      cpCanonicalType = CanonicalTypeId "example.Artifact.v1",
-      cpBindingSymbol = QualifiedValueName "Example.Bindings.artifactBinding",
-      cpBindingVersion = BindingVersion "1",
-      cpWireFingerprint = "deadbeef"
-    }
-
-syntheticGenerated :: FilePath -> T.Text -> ScaffoldModule
-syntheticGenerated path contents =
-  ScaffoldModule {modulePath = path, moduleText = contents, kind = Generated, origin = "test"}
-
-generatedTextEndingIn :: T.Text -> [ScaffoldModule] -> T.Text
-generatedTextEndingIn suffix modules = case [moduleText m | m <- modules, kind m == Generated, suffix `T.isSuffixOf` T.pack (modulePath m)] of
-  contents : _ -> contents
-  [] -> ""
-
-holeTextEndingIn :: T.Text -> [ScaffoldModule] -> T.Text
-holeTextEndingIn suffix modules = case [moduleText m | m <- modules, kind m == HoleStub, suffix `T.isSuffixOf` T.pack (modulePath m), not ("BehaviorHoles.hs" `T.isSuffixOf` T.pack (modulePath m))] of
-  contents : _ -> contents
-  [] -> ""
-
-onlyAggregate :: Spec -> Aggregate
-onlyAggregate spec = case [aggregate | NAggregate aggregate <- specNodes spec] of
-  [aggregate] -> aggregate
-  aggregates -> error ("expected one aggregate, got " <> show (length aggregates))
-
-loweringAggregateSpec :: T.Text
-loweringAggregateSpec =
-  T.unlines
-    [ "context samples",
-      "",
-      "aggregate Counter",
-      "  regs",
-      "    note Text = \"hello world\"",
-      "    count Int = 0",
-      "    state CounterVertex = Pending",
-      "  states Pending Done!",
-      "  command Bump { count:Int }",
-      "  event CountBumped { count:Int }",
-      "  Pending -- Bump --> emit CountBumped ; goto Done"
-    ]
-
-scalarRegisterCases :: [(T.Text, T.Text)]
-scalarRegisterCases =
-  [ ("Text", "\"sample\""),
-    ("Int", "0"),
-    ("Bool", "False"),
-    ("Time", "\"2026-01-02T03:04:05.123456789012Z\""),
-    ("Natural", "0")
-  ]
-
-cleanScalarAggregateSpec :: T.Text -> T.Text -> T.Text
-cleanScalarAggregateSpec typeName initialValue =
-  T.unlines
-    [ "context clean-scalar",
-      "",
-      "aggregate Scalar",
-      "  regs",
-      "    value " <> typeName <> " = " <> initialValue,
-      "  states Empty Done!",
-      "  command Set { value:" <> typeName <> " }",
-      "  event SetDone { value:" <> typeName <> " }",
-      "  Empty -- Set --> write value := value ; emit SetDone ; goto Done"
-    ]
-
-exactStatusSpec :: T.Text
-exactStatusSpec =
-  T.unlines
-    [ "context samples",
-      "",
-      "aggregate Reservation",
-      "  regs",
-      "    state ReservationVertex = Open",
-      "  states Open Closed!",
-      "  command Bump { count:Int }",
-      "  event ReservationHeld { count:Int }",
-      "  event ReservationUnHeld { count:Int }",
-      "  event CountBumped { count:Int }",
-      "  Open -- Bump --> emit CountBumped ; goto Closed",
-      "  projection reservation_status consistency=Eventual key=count",
-      "    status-map { ReservationHeld=>held ReservationUnHeld=>available CountBumped=>bumped }"
-    ]
-
-hasPathCollisionWithTwoOrigins :: Either [Refusal] [ScaffoldModule] -> Bool
-hasPathCollisionWithTwoOrigins = \case
-  Left refusals -> any hasTwo refusals
-  Right _ -> False
-  where
-    hasTwo (PathCollision _ origins) = length origins == 2
-    hasTwo _ = False
-
-isMissingBannerRefusal :: Either [Refusal] a -> Bool
-isMissingBannerRefusal = \case
-  Left [MissingGeneratedBanner paths] -> not (null paths)
-  _ -> False
-
-isSuccessfulScaffold :: Either [Refusal] a -> Bool
-isSuccessfulScaffold = \case
-  Right _ -> True
-  Left _ -> False
-
-executePlannedScaffold :: FilePath -> FilePath -> Context -> Spec -> IO ScaffoldReport
-executePlannedScaffold out specPath ctx spec = case planScaffold ctx spec of
-  Left refusals -> expectationFailure ("unexpected scaffold refusal: " <> show refusals) >> error "unreachable"
-  Right modules -> do
-    result <- executeScaffold out False specPath ctx spec modules
-    case result of
-      Left refusals -> expectationFailure ("unexpected execution refusal: " <> show refusals) >> error "unreachable"
-      Right report -> pure report
-
-renameCounter :: Node -> Node
-renameCounter (NAggregate aggregate) =
-  NAggregate
-    aggregate
-      { aggName = "Widget",
-        aggRegs = [reg {regType = if regType reg == TRef "CounterVertex" then TRef "WidgetVertex" else regType reg} | reg <- aggRegs aggregate]
-      }
-renameCounter node = node
-
-onlyPathEndingIn :: FilePath -> [ScaffoldModule] -> FilePath
-onlyPathEndingIn suffix modules = case [modulePath m | m <- modules, T.pack suffix `T.isSuffixOf` T.pack (modulePath m)] of
-  [path] -> path
-  paths -> error ("expected one path ending in " <> suffix <> ", got " <> show paths)
-
-withTempDirectory :: String -> (FilePath -> IO a) -> IO a
-withTempDirectory template = bracket acquire removePathForcibly
-  where
-    acquire = do
-      base <- getTemporaryDirectory
-      (path, handle) <- openTempFile base template
-      hClose handle
-      removeFile path
-      createDirectory path
-      pure path
-
--- | Parse a fixture and return the validator's diagnostic codes (failing the
--- test on a parse error).
-diagnosticCodesOf :: FilePath -> IO [DiagnosticCode]
-diagnosticCodesOf path = do
-  map code <$> diagnosticsOf path
-
--- | Parse a fixture and return all validator diagnostics.
-diagnosticsOf :: FilePath -> IO [Diagnostic]
-diagnosticsOf path = do
-  input <- readTestText path
-  case parseSpec path input of
-    Left err -> expectationFailure (T.unpack err) >> pure []
-    Right spec -> pure (validateSpec spec)
-
--- | Like 'diagnosticCodesOf' but only the Error-severity codes (warnings, e.g.
--- the benign-inversion notices, are excluded).
-errorCodesOf :: FilePath -> IO [DiagnosticCode]
-errorCodesOf path = do
-  diagnostics <- diagnosticsOf path
-  pure [code d | d <- diagnostics, severity d == Error]
-
--- | Parse two fixtures and diff them (old, new).
--- | Plan 143: render an Expr in concrete guard syntax by printing a dummy
--- transition through the real pretty-printer and slicing its guard clause,
--- so the test exercises the exact printer the diff advisory uses.
-renderExprText :: Expr -> T.Text
-renderExprText e =
-  case [T.strip l | l <- T.lines rendered, "guard " `T.isPrefixOf` T.strip l] of
-    [guardLine] -> T.strip (T.drop (T.length "guard ") guardLine)
-    _ -> error ("renderExprText: unexpected printer output: " <> T.unpack rendered)
-  where
-    rendered =
-      renderTransition
-        Transition
-          { tSource = "S",
-            tCommand = "C",
-            tImplementation = LegacyHoleImplementation,
-            tGuard = Just e,
-            tWrites = [],
-            tEmits = [],
-            tGoto = "S",
-            tMode = TmLive,
-            tLoc = noLoc
-          }
-
--- | Plan 143: a minimal spec whose only transition is replay-only, with the
--- supplied clause lines spliced into its body.
-replayOnlySpecWith :: [T.Text] -> T.Text
-replayOnlySpecWith clauseLines =
-  T.unlines $
-    [ "context hospital-capacity",
-      "",
-      "id TransferReservationId prefix=rsv",
-      "",
-      "aggregate Reservation",
-      "  regs",
-      "    reservationId    TransferReservationId = placeholder",
-      "    reservationState ReservationVertex     = Unrequested",
-      "  states Unrequested Held",
-      "",
-      "  command RequestTransferReservation { reservationId }",
-      "",
-      "  event TransferReservationCreated = fields(RequestTransferReservation)",
-      "",
-      "  replay-only Unrequested -- RequestTransferReservation -->"
-    ]
-      ++ clauseLines
-
-diffFixtures :: FilePath -> FilePath -> IO [Change]
-diffFixtures oldP newP = do
-  old <- readTestText oldP
-  new <- readTestText newP
-  case (,) <$> parseSpec oldP old <*> parseSpec newP new of
-    Left err -> expectationFailure (T.unpack err) >> pure []
-    Right (o, n) -> pure (diffSpecs o n)
-
-kindOfChange :: Change -> ChangeKind
-kindOfChange (Additive kind) = kind
-kindOfChange (Advisory kind) = kind
-kindOfChange (Breaking kind) = kind
-
-labelOfChange :: Change -> Label
-labelOfChange Additive {} = LabelAdditive
-labelOfChange Advisory {} = LabelAdvisory
-labelOfChange Breaking {} = LabelBreaking
-
-genSurfaceSet :: Gen (Set.Set CompatibilitySurface)
-genSurfaceSet = Set.fromList <$> listOf (elements [minBound .. maxBound])
-
-genCompatibilityVector :: Gen CompatibilityVector
-genCompatibilityVector =
-  CompatibilityVector
-    <$> genVerdict
-    <*> genVerdict
-    <*> genVerdict
-    <*> genVerdict
-    <*> genVerdict
-    <*> genVerdict
-    <*> (Set.fromList <$> listOf (elements rolloutConstraints))
-  where
-    genVerdict = elements [VCompatible, VAdvisory, VBreaking, VNotApplicable]
-    rolloutConstraints =
-      [ RolloutStopTheWorld,
-        RolloutWorkersFirst,
-        RolloutDrainRequired,
-        RolloutProducerLast
-      ]
-
-replayImpactFixtures :: FilePath -> FilePath -> IO ReplayImpact
-replayImpactFixtures oldPath newPath = do
-  old <- specOf oldPath
-  new <- specOf newPath
-  pure (ReplayImpact.replayImpact old new)
-
-modifyAggregate :: Name -> (Aggregate -> Aggregate) -> Spec -> Spec
-modifyAggregate target update spec =
-  spec
-    { specNodes =
-        [ case node of
-            NAggregate aggregate | aggName aggregate == target -> NAggregate (update aggregate)
-            _ -> node
-        | node <- specNodes spec
-        ]
-    }
-
-modifyReadModel :: Name -> (ReadModelNode -> ReadModelNode) -> Spec -> Spec
-modifyReadModel target update spec =
-  spec
-    { specNodes =
-        [ case node of
-            NReadModel readModel | rmName readModel == target -> NReadModel (update readModel)
-            _ -> node
-        | node <- specNodes spec
-        ]
-    }
-
-removeReadModel :: Name -> Spec -> Spec
-removeReadModel target spec =
-  spec {specNodes = [node | node <- specNodes spec, not (isTarget node)]}
-  where
-    isTarget (NReadModel readModel) = rmName readModel == target
-    isTarget _ = False
-
-modifyRouter :: Name -> (RouterNode -> RouterNode) -> Spec -> Spec
-modifyRouter target update spec =
-  spec
-    { specNodes =
-        [ case node of
-            NRouter router | rtId router == target -> NRouter (update router)
-            _ -> node
-        | node <- specNodes spec
-        ]
-    }
-
-routerErrorCodes :: (RouterNode -> RouterNode) -> Spec -> [DiagnosticCode]
-routerErrorCodes update = errorCodes . modifyRouter "PagingRouter" update
-
-modifyProcess :: Name -> (ProcessNode -> ProcessNode) -> Spec -> Spec
-modifyProcess target update spec =
-  spec
-    { specNodes =
-        [ case node of
-            NProcess process | procId process == target -> NProcess (update process)
-            _ -> node
-        | node <- specNodes spec
-        ]
-    }
-
-processErrorCodes :: (ProcessNode -> ProcessNode) -> Spec -> [DiagnosticCode]
-processErrorCodes update = errorCodes . modifyProcess "HospitalSurge" update
-
-errorCodes :: Spec -> [DiagnosticCode]
-errorCodes spec = [code diagnostic | diagnostic <- validateSpec spec, severity diagnostic == Error]
-
-changeReadModelShape :: ReadModelNode -> ReadModelNode
-changeReadModelShape readModel =
-  readModel
-    { rmColumns = rmColumns readModel <> [RmColumn "reviewed_by" "text" False],
-      rmShape = "fnv1a:0000000000000000"
-    }
-
--- | Assert a @new \<kind\>@ skeleton parses and validates with zero
--- error-severity diagnostics.
-assertSkeletonValid :: T.Text -> IO ()
-assertSkeletonValid kind = case skeletonFor kind of
-  Left err -> expectationFailure (T.unpack ("skeleton for " <> kind <> ": " <> err))
-  Right src -> case parseSpec ("new:" <> T.unpack kind) src of
-    Left perr -> expectationFailure (T.unpack ("skeleton for " <> kind <> " failed to parse: " <> perr))
-    Right spec ->
-      [code d | d <- validateSpec spec, severity d == Error]
-        `shouldBe` ([] :: [DiagnosticCode])
-
-assertSkeletonScaffoldable :: T.Text -> IO ()
-assertSkeletonScaffoldable kind = case skeletonFor kind of
-  Left err -> expectationFailure (T.unpack ("skeleton for " <> kind <> ": " <> err))
-  Right src -> case parseSpec ("new:" <> T.unpack kind) src of
-    Left perr -> expectationFailure (T.unpack perr)
-    Right spec -> planScaffold (defaultContext (specContext spec)) spec `shouldSatisfy` isSuccessfulScaffold
-
-skeletonModuleRoots :: [(T.Text, T.Text)]
-skeletonModuleRoots =
-  [ ("aggregate", "SkelAggregate"),
-    ("process", "SkelProcess"),
-    ("router", "SkelRouter"),
-    ("contract", "SkelContract"),
-    ("intake", "SkelIntake"),
-    ("emit", "SkelEmit"),
-    ("workqueue", "SkelQueue"),
-    ("workflow", "SkelWorkflow")
-  ]
-
-assertSkeletonMatchesCommitted :: T.Text -> T.Text -> IO ()
-assertSkeletonMatchesCommitted kind root = case skeletonFor kind of
-  Left err -> expectationFailure (T.unpack err)
-  Right source -> case parseSpec ("new:" <> T.unpack kind) source of
-    Left err -> expectationFailure (T.unpack err)
-    Right spec -> do
-      let ctx = (defaultContext (specContext spec)) {moduleRoot = root}
-      forM_ [m | m <- scaffoldModules ctx spec, kindOf m == Generated] $ \m -> do
-        committed <- readTestText ("test/conformance-skeletons/" <> modulePath m)
-        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
-  where
-    kindOf = Keiro.Dsl.Scaffold.kind
-
-bumpArtifactBindingVersion :: MappedDecl -> MappedDecl
-bumpArtifactBindingVersion declaration@MappedStructural {msName = "ArtifactInfo"} =
-  declaration {msBindingVersion = Just "2"}
-bumpArtifactBindingVersion declaration = declaration
-
-addArtifactSummaryField :: MappedDecl -> MappedDecl
-addArtifactSummaryField declaration@MappedStructural {msName = "ArtifactInfo", msShape = ShapeRecord constructor unknownFields fields} =
-  declaration
-    { msShape =
-        ShapeRecord
-          constructor
-          unknownFields
-          ( fields
-              <> [ WireField
-                     { wfHaskell = "summary",
-                       wfKey = "summary",
-                       wfType = TText,
-                       wfPresence = PRequired,
-                       wfOnMissing = Nothing,
-                       wfLoc = Loc 0
-                     }
-                 ]
-          )
-    }
-addArtifactSummaryField declaration = declaration
-
-expectGenericCompileFailure :: FilePath -> String -> Expectation
-expectGenericCompileFailure fixture expectedDiagnostic = do
-  let fixtureDir = "../keiro-core/test/compile-fail" </> fixture
-      fixtureSource = fixtureDir </> "Fixture.hs"
-  (exitCode, standardOutput, standardError) <-
-    readProcessWithExitCode
-      "cabal"
-      [ "exec",
-        "--",
-        "ghc",
-        "-XGHC2024",
-        "-fno-code",
-        "-fforce-recomp",
-        "-i../keiro-core/src",
-        "-i" <> fixtureDir,
-        fixtureSource
-      ]
-      ""
-  exitCode `shouldSatisfy` (/= ExitSuccess)
-  let compilerOutput = standardOutput <> standardError
-  compilerOutput `shouldContain` expectedDiagnostic
-  compilerOutput `shouldContain` "Run keiro-dsl scaffold and fill the binding by hand at this error location in the scaffolded module."
-  compilerOutput `shouldContain` fixtureSource
-
-moveArtifactBindingIntoGenerated :: MappedDecl -> MappedDecl
-moveArtifactBindingIntoGenerated declaration@MappedStructural {msName = "ArtifactInfo"} =
-  declaration {msBinding = Just "Generated.ConsumerDemo.Bindings.artifactInfoBinding"}
-moveArtifactBindingIntoGenerated declaration = declaration
-
-removeMappedRegisterRequirements :: Spec -> Spec
-removeMappedRegisterRequirements spec =
-  spec
-    { specMapped = map removeInitial (specMapped spec),
-      specNodes = map removeRegisters (specNodes spec)
-    }
-  where
-    removeInitial declaration@MappedStructural {} = declaration {msInitial = Nothing}
-    removeInitial declaration@MappedOpaque {} = declaration {moInitial = Nothing}
-    removeRegisters (NAggregate aggregate) =
-      NAggregate
-        aggregate
-          { aggRegs = [],
-            aggTransitions = [transition {tWrites = []} | transition <- aggTransitions aggregate]
-          }
-    removeRegisters node = node
-
-isImportCycle :: Refusal -> Bool
-isImportCycle ImportCycle {} = True
-isImportCycle _ = False
-
-isLoweringRefusal :: Either [Refusal] modules -> Bool
-isLoweringRefusal (Left refusals) = any isLowering refusals
-  where
-    isLowering LoweringRefusal {} = True
-    isLowering _ = False
-isLoweringRefusal (Right _) = False
-
--- | The canonical positive workspace fixture: three members under one context.
-canonicalWorkspacePath :: FilePath
-canonicalWorkspacePath = "test/fixtures/workspace/service.keiro-workspace"
-
--- | Deterministic workspace source used to model git blobs without invoking git.
-memoryContentSource :: Map.Map FilePath T.Text -> ContentSource
-memoryContentSource files =
-  ContentSource
-    { csRead = \path ->
-        pure $ maybe (Left ("missing in-memory content: " <> T.pack path)) Right (Map.lookup path files)
-    }
-
-changeCode :: Change -> DiagnosticCode
-changeCode (Additive kind) = ckCode kind
-changeCode (Advisory kind) = ckCode kind
-changeCode (Breaking kind) = ckCode kind
-
-breakingSurfaces :: Change -> [CompatibilitySurface]
-breakingSurfaces change =
-  [ surface
-  | surface <- [minBound .. maxBound],
-    verdictFor surface (ckVector kind) == VBreaking
-  ]
-  where
-    kind = case change of
-      Additive value -> value
-      Advisory value -> value
-      Breaking value -> value
-
-workspaceChangeKind :: Change -> ChangeKind
-workspaceChangeKind (Additive kind) = kind
-workspaceChangeKind (Advisory kind) = kind
-workspaceChangeKind (Breaking kind) = kind
-
--- | The same members as 'canonicalWorkspacePath', listed in reverse order.
-reorderedWorkspacePath :: FilePath
-reorderedWorkspacePath = "test/fixtures/workspace/service-reordered.keiro-workspace"
-
--- | Load and compose a workspace fixture, failing the test on a refusal. The
--- fixture path is package-relative; the loader is rooted at the manifest's own
--- directory, exactly as the CLI roots it.
-shouldComposeWorkspace :: FilePath -> IO WorkspaceSpec
-shouldComposeWorkspace path = do
-  resolved <- resolveTestPath path
-  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) resolved
-  case loaded of
-    Left failure ->
-      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure resolved failure)))
-        >> error "unreachable"
-    Right workspace -> pure workspace {wsManifestPath = path}
-
--- | The 'Context' a workspace scaffolds under, with no CLI overrides: the
--- members' unanimous context name, the manifest's module-root and layout
--- authority, and the built-in defaults where the manifest is silent.
-workspaceContext :: WorkspaceSpec -> Context
-workspaceContext workspace =
-  Context
-    { contextName = wsContext workspace,
-      moduleRoot = maybe "" id (wsModuleRoot workspace),
-      placement = maybe GeneratedPrefix id (wsLayout workspace)
-    }
-
--- | Compose and plan a workspace fixture, failing the test on any refusal.
-shouldPlanWorkspace :: FilePath -> IO WorkspacePlan
-shouldPlanWorkspace path = do
-  workspace <- shouldComposeWorkspace path
-  case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
-    Left refusals -> expectationFailure ("unexpected workspace plan refusal: " <> show refusals) >> error "unreachable"
-    Right plan -> pure plan
-
--- | Does a scaffolded module's path end in this suffix?
-isSuffixOfPath :: FilePath -> ScaffoldModule -> Bool
-isSuffixOfPath suffix m = T.pack suffix `T.isSuffixOf` T.pack (modulePath m)
-
--- | A workspace record built from real composed data plus two synthetic
--- adoption rows, so the round-trip test exercises every row kind including the
--- JSON encodings shared with the v1 record.
-sampleWorkspaceRecord :: WorkspaceSpec -> WorkspaceRecord
-sampleWorkspaceRecord workspace =
-  WorkspaceRecord
-    { wrService = wsService workspace,
-      wrManifest = "service.keiro-workspace",
-      wrContext = wsContext workspace,
-      wrModuleRoot = maybe "" id (wsModuleRoot workspace),
-      wrLayout = "collocated",
-      wrMembers = map wmPath (wsMembers workspace),
-      wrSourceLanguages =
-        [ WorkspaceSourceLanguageRow (wmPath member) (wmSourceLanguage member)
-        | member <- wsMembers workspace
-        ],
-      wrLanguageContract = wsLanguageContract workspace,
-      wrModules =
-        [ WorkspaceModuleRow Generated "Demo/Generated/StructuralProjections.hs" Nothing,
-          WorkspaceModuleRow Generated "Demo/Project/Generated/Domain.hs" (Just "domain/project.keiro"),
-          WorkspaceModuleRow HoleStub "Demo/Project/Holes.hs" (Just "domain/shared.keiro")
-        ],
-      wrMappings = consumerMappings (consumerPlan (wsMergedSpec workspace)),
-      wrIdDomains = [],
-      wrNominalEqualities = nominalEqualityIdentities (wsMergedSpec workspace),
-      wrBindingObligations = either (const []) id (bindingHoles (wsMergedSpec workspace)),
-      wrBehaviorRequirements = [],
-      wrAdopted =
-        [ AdoptedRow "claimed/One.hs" "record" (Just "keiro-dsl-scaffold-record.demo-project.txt") (Just "project.keiro"),
-          AdoptedRow "claimed/Two.hs" "banner" Nothing Nothing
-        ]
-    }
-
--- | The canonical workspace with a case-variant copy of one member's aggregate
--- grafted onto another member. Composition refuses this shape (EP-153 catches it
--- at the earliest boundary), so the planner's own cross-member collision gate can
--- only be exercised by constructing the graph directly — which is exactly what
--- this does, mirroring the single-file @caseVariant@ construction.
-withCaseVariantAggregate :: WorkspaceSpec -> WorkspaceSpec
-withCaseVariantAggregate workspace = case [aggregate | NAggregate aggregate <- specNodes merged, aggName aggregate == "Project"] of
-  [] -> error "canonical workspace fixture has no Project aggregate"
-  aggregate : _ ->
-    let shouted = aggregate {aggName = T.toUpper (aggName aggregate)}
-        ownership = wsOwnership workspace
-     in workspace
-          { wsMergedSpec = merged {specNodes = specNodes merged <> [NAggregate shouted]},
-            wsOwnership =
-              ownership
-                { oiNodes =
-                    Map.insert
-                      ("aggregate", aggName shouted)
-                      ("domain/project-artifact.keiro", Loc 1)
-                      (oiNodes ownership)
-                }
-          }
-  where
-    merged = wsMergedSpec workspace
-
--- | Write a one-member workspace whose member declares an upcaster, so its
--- golden payload fixture has a canonical location. Returns the composed
--- workspace; the caller decides where the fixture lives.
-writeGoldenWorkspace :: FilePath -> IO WorkspaceSpec
-writeGoldenWorkspace root = do
-  source <- readTestText "test/fixtures/reservation-v2.keiro"
-  createDirectoryIfMissing True (root </> "domain")
-  TIO.writeFile (root </> "domain/reservation.keiro") source
-  let manifestPath = root </> "service.keiro-workspace"
-  TIO.writeFile manifestPath "service gold-demo\nspec domain/reservation.keiro\n"
-  loaded <- loadWorkspace (fileContentSource root) manifestPath
-  case loaded of
-    Left failure ->
-      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure manifestPath failure)))
-        >> error "unreachable"
-    Right workspace -> pure workspace
-
--- | Materialize the canonical fixture workspace in a fresh temporary directory
--- and hand the callback its root, a sibling output directory, and the composed
--- workspace. Working on a copy is what lets a test edit a member and re-scaffold.
---
--- The manifest's @spec@ lines are passed through the given function first, so a
--- caller can list the same members in a different order; the manifest __file
--- name__ stays the same, which is what makes two runs comparable byte for byte.
-withWorkspaceFixture ::
-  String ->
-  ([FilePath] -> [FilePath]) ->
-  (FilePath -> FilePath -> WorkspaceSpec -> IO a) ->
-  IO a
-withWorkspaceFixture template orderMembers act =
-  withTempDirectory template $ \base -> do
-    let root = base </> "workspace"
-        out = base </> "out"
-        members =
-          [ "domain/project-artifact.keiro",
-            "domain/project.keiro",
-            "domain/shared.keiro"
-          ]
-    createDirectoryIfMissing True (root </> "domain")
-    forM_ members $ \relative -> do
-      source <- readTestText ("test/fixtures/workspace" </> relative)
-      TIO.writeFile (root </> relative) source
-    TIO.writeFile
-      (root </> "service.keiro-workspace")
-      ( T.unlines
-          ( ["service demo-project", "module Demo.Modules.Project", "layout collocated"]
-              <> ["spec " <> T.pack relative | relative <- orderMembers members]
-          )
-      )
-    workspace <- loadTempWorkspace root
-    act root out workspace
-
--- | Materialize an inline workspace — a manifest plus literal member sources —
--- in a fresh temporary directory, and hand the callback its root, a sibling output
--- directory, and the composed workspace.
-withInlineWorkspace ::
-  String ->
-  (T.Text, [(FilePath, T.Text)]) ->
-  (FilePath -> FilePath -> WorkspaceSpec -> IO a) ->
-  IO a
-withInlineWorkspace template (service, members) act =
-  withTempDirectory template $ \base -> do
-    let root = base </> "workspace"
-        out = base </> "out"
-    forM_ members $ \(relative, source) -> writeFileWithParents (root </> relative) source
-    TIO.writeFile
-      (root </> "service.keiro-workspace")
-      ( T.unlines
-          (("service " <> service) : ["spec " <> T.pack relative | (relative, _) <- members])
-      )
-    workspace <- loadTempWorkspace root
-    act root out workspace
-
--- | Two independently valid members under one context. Each is a complete spec
--- that the pre-workspace single-file scaffolder accepts, which is what lets a test
--- reproduce the overwritten-record defect before adopting.
-adoptionMembers :: (T.Text, [(FilePath, T.Text)])
-adoptionMembers = ("adoption-demo", [("domain/a.keiro", adoptionMemberA), ("domain/b.keiro", adoptionMemberB)])
-
-adoptionMemberA :: T.Text
-adoptionMemberA =
-  T.unlines
-    [ "context adoption-demo",
-      "",
-      "aggregate Counter",
-      "  regs",
-      "    count Int = 0",
-      "    state CounterVertex = Pending",
-      "  states Pending Done!",
-      "  command Bump { count:Int }",
-      "  event CountBumped { count:Int }",
-      "  Pending -- Bump --> emit CountBumped ; goto Done"
-    ]
-
-adoptionMemberB :: T.Text
-adoptionMemberB =
-  T.unlines
-    [ "context adoption-demo",
-      "",
-      "aggregate Widget",
-      "  regs",
-      "    size Int = 0",
-      "    state WidgetVertex = Draft",
-      "  states Draft Shipped!",
-      "  command Ship { size:Int }",
-      "  event WidgetShipped { size:Int }",
-      "  Draft -- Ship --> emit WidgetShipped ; goto Shipped"
-    ]
-
-writeFileWithParents :: FilePath -> T.Text -> IO ()
-writeFileWithParents path contents = do
-  createDirectoryIfMissing True (takeDirectory path)
-  TIO.writeFile path contents
-
--- | Only the Haskell sources of a tree snapshot, dropping bookkeeping files.
-haskellOnly :: [(FilePath, T.Text)] -> [(FilePath, T.Text)]
-haskellOnly entries = [entry | entry@(path, _) <- entries, ".hs" `T.isSuffixOf` T.pack path]
-
--- | Compose a workspace that a test just wrote to disk.
-loadTempWorkspace :: FilePath -> IO WorkspaceSpec
-loadTempWorkspace root = do
-  let manifestPath = root </> "service.keiro-workspace"
-  loaded <- loadWorkspace (fileContentSource root) manifestPath
-  case loaded of
-    Left failure ->
-      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure manifestPath failure)))
-        >> error "unreachable"
-    Right workspace -> pure workspace
-
--- | Plan an already-composed workspace, failing the test on a refusal.
-shouldPlanWorkspaceSpec :: WorkspaceSpec -> IO WorkspacePlan
-shouldPlanWorkspaceSpec workspace =
-  case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
-    Left refusals -> expectationFailure ("unexpected workspace plan refusal: " <> show refusals) >> error "unreachable"
-    Right plan -> pure plan
-
--- | Plan then execute a whole-workspace scaffold, failing loudly on either.
-executePlannedWorkspaceScaffold :: FilePath -> WorkspaceSpec -> IO WorkspaceScaffoldReport
-executePlannedWorkspaceScaffold out workspace = do
-  plan <- shouldPlanWorkspaceSpec workspace
-  result <- executeWorkspaceScaffold out False plan
-  case result of
-    Left refusals -> expectationFailure ("unexpected workspace execution refusal: " <> show refusals) >> error "unreachable"
-    Right report -> pure report
-
--- | Rename one member's aggregate in place and recompose. Only the
--- @aggregate \<Name\>@ header is rewritten, so declarations that merely share the
--- prefix (@ProjectId@, @ProjectSummary@) are untouched.
-renameMemberAggregate :: FilePath -> FilePath -> T.Text -> T.Text -> IO WorkspaceSpec
-renameMemberAggregate root member from to = do
-  source <- TIO.readFile (root </> member)
-  TIO.writeFile (root </> member) (T.replace ("aggregate " <> from <> "\n") ("aggregate " <> to <> "\n") source)
-  loadTempWorkspace root
-
--- | Move the @ProjectArtifact@ aggregate from the artifact member into the
--- project member, and recompose.
---
--- It is prepended, so the merged spec's node order — and therefore every emitted
--- byte, including the replay-audit assembly's aggregate list — is exactly what it
--- was. That isolates the change to ownership, which is the point of the test.
-moveArtifactAggregate :: FilePath -> IO WorkspaceSpec
-moveArtifactAggregate root = do
-  artifact <- TIO.readFile (root </> "domain/project-artifact.keiro")
-  project <- TIO.readFile (root </> "domain/project.keiro")
-  case T.breakOn "aggregate ProjectArtifact" artifact of
-    (kept, moved) | not (T.null moved) -> do
-      TIO.writeFile (root </> "domain/project-artifact.keiro") kept
-      TIO.writeFile
-        (root </> "domain/project.keiro")
-        (T.replace "aggregate Project\n" (moved <> "\naggregate Project\n") project)
-      loadTempWorkspace root
-    _ -> expectationFailure "artifact member has no ProjectArtifact aggregate" >> error "unreachable"
-
--- | Every regular file under a directory, as @(relative path, contents)@ sorted
--- by path — the comparison unit for "byte-identical output".
-treeSnapshot :: FilePath -> IO [(FilePath, T.Text)]
-treeSnapshot root = do
-  exists <- doesDirectoryExist root
-  if not exists then pure [] else sort <$> walk ""
-  where
-    walk relative = do
-      entries <- listDirectory (root </> relative)
-      fmap concat . forM (sort entries) $ \entry -> do
-        let child = if null relative then entry else relative </> entry
-        isDirectory <- doesDirectoryExist (root </> child)
-        if isDirectory
-          then walk child
-          else do
-            contents <- TIO.readFile (root </> child)
-            pure [(child, contents)]
-
-thd3 :: (a, b, c) -> c
-thd3 (_, _, value) = value
-
-isPathCollision :: Refusal -> Bool
-isPathCollision PathCollision {} = True
-isPathCollision _ = False
-
-isInfixOfString :: String -> String -> Bool
-isInfixOfString needle haystack = T.isInfixOf (T.pack needle) (T.pack haystack)
-
--- | Load a workspace fixture expecting a compose refusal, and return it.
-shouldRefuseWorkspace :: FilePath -> IO (NonEmpty WorkspaceDiagnostic)
-shouldRefuseWorkspace path = do
-  resolved <- resolveTestPath path
-  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) resolved
-  case loaded of
-    Left (WorkspaceRefused diagnostics) -> pure diagnostics
-    Left other ->
-      expectationFailure
-        ("expected compose refusals, got:\n" <> T.unpack (T.intercalate "\n" (renderWorkspaceFailure resolved other)))
-        >> error "unreachable"
-    Right _ -> expectationFailure ("expected " <> path <> " to be refused") >> error "unreachable"
-
--- | Invoke the built @keiro-dsl@ executable. Fixture paths are resolved first,
--- so the test works whether it runs from the package directory or the repository
--- root.
-runKeiroDsl :: [String] -> IO (ExitCode, String, String)
-runKeiroDsl arguments = do
-  resolved <- traverse resolveArgument arguments
-  readProcessWithExitCode "cabal" (["run", "-v0", "keiro-dsl", "--"] <> resolved) ""
-  where
-    resolveArgument argument
-      | "test/fixtures/" `isPrefixOfString` argument = resolveTestPath argument
-      | otherwise = pure argument
-    isPrefixOfString prefix value = take (length prefix) value == prefix
-
--- | The @spec@ field of a coverage report, i.e. what the report says it covers.
-coverageSpecPath :: Value -> Maybe T.Text
-coverageSpecPath value = case value of
-  Aeson.Object fields -> case KeyMap.lookup "spec" fields of
-    Just (Aeson.String path) -> Just path
-    _ -> Nothing
-  _ -> Nothing
-
--- | Order-preserving deduplication for comparing cited file sets.
-nubOrd :: (Eq a) => [a] -> [a]
-nubOrd = go []
-  where
-    go seen [] = reverse seen
-    go seen (x : xs) = if x `elem` seen then go seen xs else go (x : seen) xs
-
--- | Parse a workspace manifest, failing the test on a refusal.
-shouldParseManifest :: FilePath -> T.Text -> IO WorkspaceManifest
-shouldParseManifest path source = case parseWorkspaceManifest path source of
-  Left err -> expectationFailure (T.unpack err) >> error "unreachable"
-  Right manifest -> pure manifest
-
--- | Generate a canonical workspace manifest. Members are drawn from a pool of
--- paths that are distinct even under case folding and are held sorted, which is
--- the invariant every parsed manifest satisfies.
-genWorkspaceManifest :: Gen WorkspaceManifest
-genWorkspaceManifest = do
-  service <- elements ["demo-project", "mori", "kotei", "a1", "svc-2"]
-  moduleRoot <- elements [Nothing, Just "Demo", Just "Demo.Modules.Project"]
-  layout <- elements [Nothing, Just GeneratedPrefix, Just CollocatedLeaf]
-  chosen <-
-    sublistOf
-      [ "a.keiro",
-        "d-e_f.keiro",
-        "domain/b.keiro",
-        "domain/sub/c.keiro",
-        "x1.keiro"
-      ]
-      `suchThat` (not . null)
-  pure
-    WorkspaceManifest
-      { wmfService = service,
-        wmfServiceLoc = Loc 1,
-        wmfModuleRoot = moduleRoot,
-        wmfModuleRootLoc = Loc 2,
-        wmfLayout = layout,
-        wmfLayoutLoc = Loc 3,
-        wmfMembers = NE.fromList [WorkspaceMemberRef path (Loc 4) | path <- sort chosen]
-      }
-
--- | Parse a fixture into a 'Spec', failing the test on a parse error.
-specOf :: FilePath -> IO Spec
-specOf path = do
-  input <- readTestText path
-  case parseSpec path input of
-    Left err -> expectationFailure (T.unpack err) >> error "unreachable"
-    Right spec -> pure spec
-
--- | Parse a fixture and scaffold every aggregate in it.
-scaffoldFixture :: FilePath -> IO [ScaffoldModule]
-scaffoldFixture path = do
-  input <- readTestText path
-  case parseSpec path input of
-    Left err -> expectationFailure (T.unpack err) >> pure []
-    Right spec ->
-      pure $
-        concat
-          [ scaffoldAggregate (ctx spec) spec agg <> harnessFor (ctx spec) spec agg
-          | NAggregate agg <- specNodes spec
-          ]
-  where
-    ctx spec = defaultContext (specContext spec)
-
-scaffoldProcessFixture :: FilePath -> IO [ScaffoldModule]
-scaffoldProcessFixture path = do
-  input <- readTestText path
-  case parseSpec path input of
-    Left err -> expectationFailure (T.unpack err) >> pure []
-    Right spec ->
-      pure $ concat [scaffoldProcess (ctx spec) p | NProcess p <- specNodes spec]
-  where
-    ctx spec = defaultContext (specContext spec)
-
--- | Assert a freshly-scaffolded Generated module matches its committed copy
--- under test/conformance/ (whitespace-normalized). The committed copies are the
--- ones the keiro-dsl-conformance suite compiles, so this pins the live scaffolder
--- to known-compiling output.
-assertMatchesCommitted :: ScaffoldModule -> IO ()
-assertMatchesCommitted m = do
-  let committedPath = "test/conformance/" <> modulePath m
-  committed <- readTestText committedPath
-  normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
-
-normalizeGenerated :: T.Text -> (T.Text, [T.Text])
-normalizeGenerated text =
-  let (imports, body) = partition isImport (T.lines text)
-   in (normalizeBody body, sort (map normalizeImport imports))
-  where
-    -- Compare the deterministic body exactly as before and imports as a sorted,
-    -- whitespace-normalized list. Sorting tolerates formatter reordering while
-    -- additions, removals, and renamed imports now fail the pin.
-    normalizeBody =
-      T.replace " , )" " )"
-        . T.unwords
-        . T.words
-        . T.replace "}" " } "
-        . T.replace "{" " { "
-        . T.replace "]" " ] "
-        . T.replace "[" " [ "
-        . T.replace "," " , "
-        . T.unlines
-    normalizeImport line =
-      let reordered = case T.words line of
-            "import" : "qualified" : moduleName : rest -> T.unwords ("import" : moduleName : "qualified" : rest)
-            wordsInImport -> T.unwords wordsInImport
-          (prefix, explicit) = T.breakOn " (" reordered
-       in if T.null explicit
-            then prefix
-            else
-              let members =
-                    sort
-                      . map (T.replace " (" "(" . T.unwords . T.words)
-                      . T.splitOn ","
-                      . T.dropEnd 1
-                      $ T.drop 2 explicit
-               in prefix <> " (" <> T.intercalate "," members <> ")"
-    isImport line = case T.words line of
-      "import" : _ -> True
-      _ -> False
-
--- | Locate and read a test fixture or committed conformance source regardless
--- of whether the suite was launched from the package directory or repo root.
-readTestText :: FilePath -> IO T.Text
-readTestText path = resolveTestPath path >>= TIO.readFile
-
--- | Locate a repo file regardless of the test process's current directory.
-resolveTestPath :: FilePath -> IO FilePath
-resolveTestPath rel = do
-  override <- lookupEnv "KEIRO_DSL_TEST_ROOT"
-  let candidates = [rel, "keiro-dsl" </> rel] <> maybe [] (\root -> [root </> rel]) override
-  existing <- filterM doesFileExist candidates
-  case existing of
-    path : _ -> pure path
-    [] ->
-      fail $
-        "unable to locate keiro-dsl test file "
-          <> show rel
-          <> "; tried "
-          <> show candidates
-
-leftContains :: T.Text -> Either T.Text a -> Bool
-leftContains needle = \case
-  Left err -> needle `T.isInfixOf` err
-  Right _ -> False
-
-parseInlineSpec :: FilePath -> T.Text -> IO Spec
-parseInlineSpec sourceName src = case parseSpec sourceName src of
-  Left err -> expectationFailure (T.unpack err) >> error "unreachable"
-  Right spec -> pure spec
+-- | Test driver for keiro-dsl. EP-1 milestone 1 tests: the @parse . pretty@
+-- round-trip property over generated specs, and a unit test pinning the shape
+-- of the canonical Reservation fixture.
+module Main (main) where
+
+import Control.Exception (bracket)
+import Control.Monad (filterM, forM, forM_, unless)
+import Data.Aeson (Value, object, (.=))
+import Data.Aeson qualified as Aeson
+import Data.Aeson.KeyMap qualified as KeyMap
+import Data.Aeson.Types (parseEither)
+import Data.Either (isLeft, isRight)
+import Data.Foldable (toList)
+import Data.KindID qualified as KindID
+import Data.List (partition, permutations, sort, (\\))
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.List.NonEmpty qualified as NE
+import Data.Map.Strict qualified as Map
+import Data.Set qualified as Set
+import Data.Text qualified as T
+import Data.Text.IO qualified as TIO
+import Data.Text.Lazy qualified as LazyText
+import Data.Text.Lazy.Encoding qualified as LazyTextEncoding
+import Data.Version (showVersion)
+import Keiki.ProjectionDomain (matchesTextPattern)
+import Keiro.Codec (Codec (..), EventType (..), decodeRaw)
+import Keiro.Codec.IdDomain (IdDomainFailure (..), idDomainSampleText, idDomainTextPattern, parseKindIdV7Text, parseKindIdV7Value, typeIdV7Domain, validateIdDomainText)
+import Keiro.Dsl.AggregateType
+import Keiro.Dsl.BehaviorCoverage qualified as Behavior
+import Keiro.Dsl.CanonicalEncoding (foldFingerprint128)
+import Keiro.Dsl.CodecCompare
+import Keiro.Dsl.ConformanceBaseline (conformanceBaselineSpec)
+import Keiro.Dsl.Coverage qualified as Coverage
+import Keiro.Dsl.Diff (Change (..), ChangeKind (..), CompatibilitySurface (..), CompatibilityVector (..), FamilyDiff (..), Label (..), NodeFamily, RolloutConstraint (..), SurfaceVerdict (..), defaultGate, deriveLabel, familyRegistry, gateWith, gatedBreaking, isAdvisory, isBreaking, verdictFor)
+import Keiro.Dsl.Diff qualified as CheckedDiff
+import Keiro.Dsl.DiffReport (Remedy (..), diffReport, parseSurfaceName, remediationFor, renderExplainBlock, renderFinding)
+import Keiro.Dsl.EventOutput
+import Keiro.Dsl.ExplainBindings (BindingHole (..), BindingObligation (..), BindingObligationKind (..), bindingHoles, bindingObligations, bindingObligationsForService, renderBindingObligations)
+import Keiro.Dsl.Expression
+import Keiro.Dsl.FoldFingerprint (FoldSurfaceError (..))
+import Keiro.Dsl.FoldFingerprint qualified as CheckedFold
+import Keiro.Dsl.FrontendCompatibility (frontendCompatibilitySpec)
+import Keiro.Dsl.FrontendProfiles (frontendProfilesSpec)
+import Keiro.Dsl.FrontendSurface (frontendSurfaceSpec)
+import Keiro.Dsl.Goldens (GoldenEvidence (..), GoldenPayload (..), emitGoldenPayloads, goldenRelativePath, goldensForDiff)
+import Keiro.Dsl.Grammar
+import Keiro.Dsl.Harness (harnessFor, harnessForWithGoldens, harnessReadModel, harnessRouter, harnessWorkflow)
+import Keiro.Dsl.IdDomain (IdDomainContract (..), contractIdDomainContractFor, idDomainContractFor, idDomainIdentitiesForService)
+import Keiro.Dsl.LanguageVersion
+import Keiro.Dsl.Manifest (manifestDependencies, manifestDependenciesForService, moduleNameOf, renderManifest, renderManifestForService)
+import Keiro.Dsl.MappedConsumer (ConsumerPlan (..), MappingIdentity (..), consumerPlan)
+import Keiro.Dsl.NominalType hiding (NominalInvalidHaskellSource, NominalInvalidIdPrefix, NominalInvalidIdentity, NominalMissingIngredient)
+import Keiro.Dsl.Parser (parseSource, parseSpec)
+import Keiro.Dsl.PrettyPrint (renderSource, renderSpec, renderTransition)
+import Keiro.Dsl.ReadModelShape (canonicalShape, deriveShapeHash, registryNameFor, subscriptionNameFor)
+import Keiro.Dsl.ReplayImpact (AggregateImpact (..), ReplayImpact (..))
+import Keiro.Dsl.ReplayImpact qualified as ReplayImpact
+import Keiro.Dsl.Scaffold (Context (..), ModuleKind (..), NominalGenerationOwner (..), NominalUseSite (..), ScaffoldModule (..), StructuralProjection (..), codecComparisonBanner, codecComparisonModule, defaultContext, firewallBreaches, genPrefixFor, generatedBanner, generatedBannerFor, generatedNominalModule, holePrefixFor, isGeneratedBannerLine, obsoleteGeneratedOutputHooks, planNominalGeneration, projectionSpecs, scaffoldAggregate, scaffoldContract, scaffoldContractForService, scaffoldIntake, scaffoldProcess, scaffoldPublisher, scaffoldReadModel, scaffoldRefusals, scaffoldReplayAudit, scaffoldRouter, scaffoldWorkqueue, windowSeconds)
+import Keiro.Dsl.ScaffoldRecord (ScaffoldRecord (..), parseRecord, recordFileName, renderRecord)
+import Keiro.Dsl.ScaffoldRun (MappingDrift (..), Refusal (..), ScaffoldReport (..), SourceLanguageDrift (..), StaleGeneratedEvidence (..), StaleModule (..), WriteDisposition (..), executeScaffold, executeScaffoldWithLanguage, executeServiceScaffold, planScaffold, planServiceScaffold, renderRefusals, renderScaffoldReport, scaffoldModules, scaffoldServiceModules)
+import Keiro.Dsl.SemanticContract
+import Keiro.Dsl.Skeleton (skeletonFor, skeletonKinds)
+import Keiro.Dsl.TypeGraph
+import Keiro.Dsl.Validate (Diagnostic (..), DiagnosticCode (..), Severity (..), derivedQueueTrio, renderDiagnostic, validateService, validateSpec)
+import Keiro.Dsl.Workspace
+import Keiro.Dsl.WorkspaceAdoption
+import Keiro.Dsl.WorkspaceDiff hiding (diffWorkspaces)
+import Keiro.Dsl.WorkspaceDiff qualified as CheckedWorkspaceDiff
+import Keiro.Dsl.WorkspaceRecord
+import Keiro.Dsl.WorkspaceScaffold
+import Paths_keiro_dsl qualified as Package
+import System.Directory (createDirectory, createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getTemporaryDirectory, listDirectory, removeFile, removePathForcibly)
+import System.Environment (lookupEnv)
+import System.Exit (ExitCode (..))
+import System.FilePath (takeDirectory, takeExtension, takeFileName, (</>))
+import System.IO (hClose, openTempFile)
+import System.Process (readProcessWithExitCode)
+import Test.Hspec hiding (Spec)
+import Test.QuickCheck
+
+resolvedFold :: Either FoldSurfaceError value -> value
+resolvedFold = either (error . ("unexpected fold-surface failure in checked fixture: " <>) . show) id
+
+aggregateFoldFingerprintForService :: CheckedService -> Aggregate -> T.Text
+aggregateFoldFingerprintForService service aggregate = resolvedFold (CheckedFold.aggregateFoldFingerprintForService service aggregate)
+
+aggregateFoldSurfaceForService :: CheckedService -> Aggregate -> T.Text
+aggregateFoldSurfaceForService service aggregate = resolvedFold (CheckedFold.aggregateFoldSurfaceForService service aggregate)
+
+aggregateFoldFingerprint :: Spec -> Aggregate -> T.Text
+aggregateFoldFingerprint spec aggregate = aggregateFoldFingerprintForService (stableCheckedService spec) aggregate
+
+aggregateFoldSurface :: Spec -> Aggregate -> T.Text
+aggregateFoldSurface spec aggregate = aggregateFoldSurfaceForService (stableCheckedService spec) aggregate
+
+legacyAggregateFoldFingerprint :: Spec -> Aggregate -> T.Text
+legacyAggregateFoldFingerprint spec aggregate = aggregateFoldFingerprintForService (legacyCheckedService spec) aggregate
+
+legacyAggregateFoldSurface :: Spec -> Aggregate -> T.Text
+legacyAggregateFoldSurface spec aggregate = aggregateFoldSurfaceForService (legacyCheckedService spec) aggregate
+
+diffServices :: CheckedService -> CheckedService -> [Change]
+diffServices old new = resolvedFold (CheckedDiff.diffServices old new)
+
+diffSources :: ParsedSource -> ParsedSource -> [Change]
+diffSources old new = resolvedFold (CheckedDiff.diffSources old new)
+
+diffSpecs :: Spec -> Spec -> [Change]
+diffSpecs old new = diffServices (stableCheckedService old) (stableCheckedService new)
+
+legacyDiffSpecs :: Spec -> Spec -> [Change]
+legacyDiffSpecs old new = diffServices (legacyCheckedService old) (legacyCheckedService new)
+
+diffWorkspaces :: WorkspaceSpec -> WorkspaceSpec -> [WorkspaceChange]
+diffWorkspaces old new = resolvedFold (CheckedWorkspaceDiff.diffWorkspaces old new)
+
+replayImpactSpecs :: Spec -> Spec -> ReplayImpact
+replayImpactSpecs old new =
+  resolvedFold (ReplayImpact.replayImpactServices (stableCheckedService old) (stableCheckedService new))
+
+legacyReplayImpactSpecs :: Spec -> Spec -> ReplayImpact
+legacyReplayImpactSpecs old new =
+  resolvedFold (ReplayImpact.replayImpactServices (legacyCheckedService old) (legacyCheckedService new))
+
+nominalEqualityIdentities :: Spec -> [T.Text]
+nominalEqualityIdentities = nominalEqualityIdentitiesForService . stableCheckedService
+
+stableCheckedService :: Spec -> CheckedService
+stableCheckedService = checkedService stableSourceLanguage
+
+stableSourceLanguage :: SourceLanguage
+stableSourceLanguage =
+  DeclaredLanguage
+    { declaredLanguageVersion = currentStableLanguageVersion,
+      languageVersionLoc = noLoc
+    }
+
+main :: IO ()
+main = hspec $ do
+  conformanceBaselineSpec
+  frontendCompatibilitySpec
+  frontendSurfaceSpec
+  frontendProfilesSpec
+
+  describe "language support" $ do
+    it "serializes support from the registered version and decodes older records" $ do
+      v1Contract <- maybe (expectationFailure "missing v1 contract" >> fail "unreachable") pure (effectiveLanguageContractForVersion =<< languageVersion 1)
+      v4Contract <- maybe (expectationFailure "missing v4 contract" >> fail "unreachable") pure (effectiveLanguageContractForVersion =<< languageVersion 4)
+      effectiveLanguageSupport v1Contract `shouldBe` CompatibilityOnly
+      effectiveLanguageSupport v4Contract `shouldBe` Stable
+      Aeson.toJSON v4Contract
+        `shouldBe` object
+          [ "languageVersion" .= (4 :: Int),
+            "runtimeSemantics" .= ("keiro-dsl/runtime-semantics/3" :: T.Text),
+            "languageSupport" .= ("stable" :: T.Text)
+          ]
+      Aeson.eitherDecode "{\"languageVersion\":1,\"runtimeSemantics\":\"keiro-dsl/runtime-semantics/1\"}"
+        `shouldBe` Right v1Contract
+
+    it "reports stable and compatibility-only support through source inspection" $ do
+      (stableCode, stableOut, stableErr) <- runKeiroDsl ["inspect", "test/fixtures/contract-v4.keiro", "--format=json"]
+      stableCode `shouldBe` ExitSuccess
+      stableErr `shouldBe` ""
+      stableOut `shouldContain` "\"languageVersion\":4"
+      stableOut `shouldContain` "\"languageSupport\":\"stable\""
+      (compatibilityCode, compatibilityOut, compatibilityErr) <- runKeiroDsl ["inspect", "test/fixtures/language-v1.keiro", "--format=json"]
+      compatibilityCode `shouldBe` ExitSuccess
+      compatibilityErr `shouldBe` ""
+      compatibilityOut `shouldContain` "\"languageVersion\":1"
+      compatibilityOut `shouldContain` "\"languageSupport\":\"compatibility-only\""
+
+  describe "runtime capability and fold identity baseline (plan 181)" $ do
+    it "pins the fold-only FNV-1a-128 UTF-8 encoding" $ do
+      foldFingerprint128 "" `shouldBe` "6c62272e07bb014262b821756295c58d"
+      foldFingerprint128 "雪" `shouldBe` "a68afaae758b5822836dbc787bb233bd"
+
+    it "pins complete fold surfaces and fingerprints across representative aggregates" $ do
+      scalar <- checkedServiceOf "test/fixtures/aggregate-scalar-expressions-v2.keiro"
+      nominal <- checkedServiceOf "test/fixtures/nominal-scalars.keiro"
+      idDomain <- checkedServiceOf "test/fixtures/id-domain-migration-v3.keiro"
+      behavior <- checkedServiceOf "test/fixtures/behavior-complete.keiro"
+      workspace <- shouldComposeWorkspace "test/fixtures/workspace-nominals/service.keiro-workspace"
+      let actual =
+            T.intercalate
+              "\n\n"
+              [ renderFoldBaseline "aggregate-scalar-expressions-v2" scalar,
+                renderFoldBaseline "nominal-scalars" nominal,
+                renderFoldBaseline "id-domain-migration-v3" idDomain,
+                renderFoldBaseline "behavior-complete" behavior,
+                renderFoldBaseline "workspace-nominals" (checkedWorkspace workspace)
+              ]
+      golden <- readTestText "test/fixtures/fold-identity-baseline.golden"
+      T.stripEnd actual `shouldBe` T.stripEnd golden
+
+    it "pins all four runtime gates and fingerprint segment projections" $ do
+      nominalSpec <- specOf "test/fixtures/id-domain-migration-v3.keiro"
+      nominalRegistry <- case resolveNominalTypes nominalSpec of
+        Left errors -> expectationFailure (show errors) >> fail "unreachable"
+        Right value -> pure value
+      nominal <- case lookupNominalType "OrderId" nominalRegistry of
+        Nothing -> expectationFailure "missing OrderId nominal" >> fail "unreachable"
+        Just value -> pure value
+      strictSpec <-
+        parseInlineSpec
+          "<strict-profile>"
+          ( T.unlines
+              [ "context strict-profile",
+                "aggregate DuplicateRegister",
+                "  regs",
+                "    value Int = 0",
+                "    value Int = 0",
+                "  states Open"
+              ]
+          )
+      rows <- forM [1 .. 4 :: Int] $ \number -> do
+        contract <- case languageVersion (fromIntegral number) >>= effectiveLanguageContractForVersion of
+          Nothing -> expectationFailure ("missing released language contract " <> show number) >> fail "unreachable"
+          Just value -> pure value
+        let hasAggregateIdDomain = maybe False (const True) (idDomainContractFor contract "ord")
+            hasContractIdDomain = maybe False (const True) (contractIdDomainContractFor contract "ord")
+            nominalContract = equalityContractVersion <$> nominalEqualityContractForService contract nominal
+            strictService = CheckedService contract strictSpec
+            hasStrictValidation = any ((== AggregateDuplicateRegister) . code) (validateService strictService)
+        pure
+          ( number,
+            effectiveRuntimeSemantics contract,
+            runtimeSemanticsFingerprintSegments contract,
+            hasAggregateIdDomain,
+            hasContractIdDomain,
+            nominalContract,
+            hasStrictValidation
+          )
+      rows
+        `shouldBe` [ (1, "keiro-dsl/runtime-semantics/1", [], False, False, Just "keiro-dsl/nominal-equality/1", False),
+                     (2, "keiro-dsl/runtime-semantics/1", [], False, False, Just "keiro-dsl/nominal-equality/1", False),
+                     (3, "keiro-dsl/runtime-semantics/2", ["semantic-contract:keiro-dsl/runtime-semantics/2"], True, False, Just "keiro-dsl/nominal-equality/2", False),
+                     (4, "keiro-dsl/runtime-semantics/3", ["semantic-contract:keiro-dsl/runtime-semantics/2"], True, True, Just "keiro-dsl/nominal-equality/2", True)
+                   ]
+
+    it "explains a serialized runtime-profile mismatch" $ do
+      case (Aeson.eitherDecode "{\"languageVersion\":4,\"runtimeSemantics\":\"keiro-dsl/runtime-semantics/2\"}" :: Either String EffectiveLanguageContract) of
+        Left message -> do
+          message `shouldContain` "runtimeSemantics does not match language version 4"
+          message `shouldContain` "keiro-dsl/runtime-semantics/3"
+          message `shouldContain` "keiro-dsl/runtime-semantics/2"
+        Right _ -> expectationFailure "expected a runtime-profile mismatch"
+
+    it "reports every formerly silent fold-resolution failure and propagates it" $ do
+      baseService <- checkedServiceOf "test/fixtures/id-domain-migration-v3.keiro"
+      recursiveMapped <- specOf "test/fixtures/mapped-recursive.keiro"
+      brokenNominal <- specOf "test/fixtures/nominal-missing-facts.keiro"
+      missingInitial <- specOf "test/fixtures/mapped-missing-initial.keiro"
+      let contract = checkedLanguageContract baseService
+          baseSpec = checkedSpec baseService
+          baseAggregate = onlyAggregate baseSpec
+          withAggregate transform =
+            baseSpec
+              { specNodes =
+                  [ NAggregate (transform aggregate)
+                  | NAggregate aggregate <- specNodes baseSpec
+                  ]
+              }
+          replaceFirstTransition transform aggregate =
+            aggregate
+              { aggTransitions = case aggTransitions aggregate of
+                  transition : rest -> transform transition : rest
+                  [] -> []
+              }
+          guardSpec = withAggregate (replaceFirstTransition (\transition -> transition {tGuard = Just (EAtom (AName "missingGuardRoot"))}))
+          outputSpec = withAggregate (replaceFirstTransition (\transition -> transition {tEmits = ["MissingEvent"]}))
+          typeGraphSpec = baseSpec {specMapped = specMapped recursiveMapped}
+          nominalSpec = baseSpec {specNominalScalars = specNominalScalars brokenNominal}
+          cases =
+            [ (CheckedService contract typeGraphSpec, baseAggregate, \case FoldTypeGraphResolutionFailed {} -> True; _ -> False),
+              (CheckedService contract nominalSpec, baseAggregate, \case FoldNominalResolutionFailed {} -> True; _ -> False),
+              (CheckedService contract missingInitial, onlyAggregate missingInitial, \case FoldRegisterInitialResolutionFailed {} -> True; _ -> False),
+              (CheckedService contract guardSpec, onlyAggregate guardSpec, \case FoldGuardResolutionFailed {} -> True; _ -> False),
+              (CheckedService contract outputSpec, onlyAggregate outputSpec, \case FoldEventOutputResolutionFailed {} -> True; _ -> False)
+            ]
+      forM_ cases $ \(service, aggregate, matches) -> do
+        CheckedFold.aggregateFoldSurfaceForService service aggregate
+          `shouldSatisfy` either matches (const False)
+        CheckedFold.aggregateFoldFingerprintForService service aggregate
+          `shouldSatisfy` either matches (const False)
+      let brokenService = CheckedService contract guardSpec
+      CheckedDiff.diffServices brokenService baseService `shouldSatisfy` isLeft
+      ReplayImpact.replayImpactServices brokenService baseService `shouldSatisfy` isLeft
+      planServiceScaffold (defaultContext (specContext guardSpec)) brokenService
+        `shouldSatisfy` \case
+          Left refusals -> any (\case FoldSurfaceRefusal {} -> True; _ -> False) refusals
+          Right _ -> False
+
+    it "pins representative diff and replay-impact rendering" $ do
+      old <- parsedSourceOf "test/fixtures/reservation.keiro"
+      new <- parsedSourceOf "test/fixtures/reservation-guard-tightened.keiro"
+      let changes = diffSources old new
+          impact = resolvedFold (ReplayImpact.replayImpactServices (checkedSource old) (checkedSource new))
+          actual =
+            T.intercalate
+              "\n"
+              ( "diff:"
+                  : map renderFinding changes
+                    <> ["replay:", ReplayImpact.renderReplayImpact impact]
+              )
+      golden <- readTestText "test/fixtures/fold-identity-diff-replay.golden"
+      T.stripEnd actual `shouldBe` T.stripEnd golden
+
+    it "pins unrelated public 64-bit identities outside the fold digest" $ do
+      readModelSpec <- specOf "test/fixtures/readmodel.keiro"
+      wireSpec <- specOf "test/fixtures/consumer-types.keiro"
+      behaviorSpec <- specOf "test/fixtures/behavior-complete.keiro"
+      readModel <- case [value | NReadModel value <- specNodes readModelSpec] of
+        value : _ -> pure value
+        [] -> expectationFailure "missing read-model fixture" >> fail "unreachable"
+      graph <- shouldResolveTypeGraph wireSpec
+      behaviorKey <- case Behavior.deriveBehaviorRequirements behaviorSpec of
+        Right (requirement : _) -> pure (Behavior.unBehaviorKey (Behavior.requirementKey requirement))
+        result -> expectationFailure ("missing behavior requirement: " <> show result) >> fail "unreachable"
+      deriveShapeHash readModel `shouldBe` "fnv1a:3717f6d9e3c44bd6"
+      wireFingerprint graph "ArtifactInfo" `shouldBe` "2bd99b3e57bcde9b"
+      behaviorKey `shouldBe` "behavior-v1-2e1fd6b9580e1a3d"
+
+  describe "source language version" $ do
+    let legacy = "context hospital-capacity\n"
+        declared = "# leading comment\n\nlanguage keiro-dsl 1\ncontext hospital-capacity\n"
+        failureCode source = case parseSource "source.keiro" source of
+          Left (SourceLanguageFailure diagnostic) -> Just (sourceLanguageErrorCode diagnostic)
+          _ -> Nothing
+        parseRight name source = case parseSource name source of
+          Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+          Right value -> pure value
+        declaredVersionOf DeclaredLanguage {declaredLanguageVersion = version} = Just version
+        declaredVersionOf LegacyUnversioned = Nothing
+        orderedSubstrings needles haystack = go (map T.pack needles) (T.pack haystack)
+          where
+            go [] _ = True
+            go (needle : rest) remaining =
+              let (_, suffix) = T.breakOn needle remaining
+               in not (T.null suffix) && go rest (T.drop (T.length needle) suffix)
+
+    it "selects declared v1 after comments while preserving semantic equality" $ do
+      legacySource <- parseRight "legacy.keiro" legacy
+      declaredSource <- parseRight "declared.keiro" declared
+      parsedSpec legacySource `shouldBe` parsedSpec declaredSource
+      parsedSourceLanguage legacySource `shouldBe` LegacyUnversioned
+      declaredVersionOf (parsedSourceLanguage declaredSource) `shouldBe` languageVersion 1
+      effectiveLanguageVersion (parsedSourceLanguage legacySource)
+        `shouldBe` effectiveLanguageVersion (parsedSourceLanguage declaredSource)
+
+    it "threads paired released versions through one checked semantic boundary" $ do
+      let body = T.unlines ["context semantic-pair", "aggregate Counter", "  regs", "  states Open"]
+          v1Text = "language keiro-dsl 1\n" <> body
+          v2Text = "language keiro-dsl 2\n" <> body
+      v1Source <- parseRight "reservation-v1.keiro" v1Text
+      v2Source <- parseRight "reservation-v2.keiro" v2Text
+      let v1Service = checkedSource v1Source
+          v2Service = checkedSource v2Source
+          v1Spec = checkedSpec v1Service
+          v2Spec = checkedSpec v2Service
+          ctx = defaultContext (specContext v1Spec)
+          aggregates spec = [aggregate | NAggregate aggregate <- specNodes spec]
+      v1Spec `shouldBe` v2Spec
+      Just (effectiveContractLanguageVersion (checkedLanguageContract v1Service)) `shouldBe` languageVersion 1
+      Just (effectiveContractLanguageVersion (checkedLanguageContract v2Service)) `shouldBe` languageVersion 2
+      effectiveRuntimeSemantics (checkedLanguageContract v1Service)
+        `shouldBe` effectiveRuntimeSemantics (checkedLanguageContract v2Service)
+      validateService v1Service `shouldBe` validateService v2Service
+      scaffoldServiceModules ctx v1Service `shouldBe` scaffoldServiceModules ctx v2Service
+      case (aggregates v1Spec, aggregates v2Spec) of
+        ([v1Aggregate], [v2Aggregate]) -> do
+          aggregateFoldSurfaceForService v1Service v1Aggregate
+            `shouldBe` aggregateFoldSurfaceForService v2Service v2Aggregate
+          aggregateFoldFingerprintForService v1Service v1Aggregate
+            `shouldBe` aggregateFoldFingerprintForService v2Service v2Aggregate
+        other -> expectationFailure ("expected one aggregate per paired source, got " <> show (fmap length other))
+      diffServices v1Service v2Service `shouldBe` []
+      resolvedFold (ReplayImpact.replayImpactServices v1Service v2Service) `shouldBe` ReplayNeutral
+
+    it "retains one effective contract for same-version workspaces and refuses mixed versions" $ do
+      let manifest = "service semantic-workspace\nspec domain/a.keiro\nspec domain/b.keiro\n"
+          v1Body = "language keiro-dsl 1\ncontext semantic-workspace\n"
+          v2Body = "language keiro-dsl 2\ncontext semantic-workspace\n"
+          sourceWith b =
+            memoryContentSource
+              ( Map.fromList
+                  [ ("service.keiro-workspace", manifest),
+                    ("domain/a.keiro", b),
+                    ("domain/b.keiro", b)
+                  ]
+              )
+          mixedSource =
+            memoryContentSource
+              ( Map.fromList
+                  [ ("service.keiro-workspace", manifest),
+                    ("domain/a.keiro", v1Body),
+                    ("domain/b.keiro", v2Body)
+                  ]
+              )
+      sameVersion <- loadWorkspace (sourceWith v2Body) "service.keiro-workspace"
+      case sameVersion of
+        Right workspace -> do
+          Just (effectiveContractLanguageVersion (checkedLanguageContract (checkedWorkspace workspace))) `shouldBe` languageVersion 2
+          validateService (checkedWorkspace workspace) `shouldBe` []
+        Left failure -> expectationFailure (show failure)
+      mixed <- loadWorkspace mixedSource "service.keiro-workspace"
+      case mixed of
+        Left (WorkspaceRefused diagnostics) ->
+          map wdCode (NE.toList diagnostics) `shouldContain` [WorkspaceLanguageVersionMismatch]
+        other -> expectationFailure ("expected a mixed-version refusal, got " <> show other)
+
+    it "refuses a source/service contract mismatch before creating the output directory" $ do
+      v1Version <- maybe (expectationFailure "version 1 missing" >> fail "unreachable") pure (languageVersion 1)
+      parsed <- parseRight "semantic-v2.keiro" "language keiro-dsl 2\ncontext semantic-refusal\n"
+      let service = checkedSource parsed
+          ctx = defaultContext "semantic-refusal"
+      modules <- case planServiceScaffold ctx service of
+        Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
+        Right planned -> pure planned
+      withTempDirectory "keiro-dsl-semantic-refusal" $ \root -> do
+        let out = root </> "not-created"
+        result <- executeServiceScaffold out False "semantic-v2.keiro" (DeclaredLanguage v1Version noLoc) ctx service modules
+        result `shouldBe` Left [SemanticContractMismatch "source provenance and checked service selected different effective language contracts"]
+        doesDirectoryExist out `shouldReturn` False
+
+    it "retains explicit declarations in source rendering and leaves legacy unversioned" $ do
+      legacySource <- parseRight "legacy.keiro" legacy
+      declaredSource <- parseRight "declared.keiro" declared
+      renderSource legacySource `shouldBe` "context hospital-capacity\n"
+      renderSource declaredSource `shouldBe` "language keiro-dsl 1\ncontext hospital-capacity\n"
+      parseSpec "declared.keiro" declared `shouldBe` Right (parsedSpec declaredSource)
+
+    it "classifies invalid, unsupported, duplicate, and misplaced preambles" $ do
+      failureCode "language keiro-dsl 0\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
+      failureCode "language keiro-dsl nope\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
+      failureCode "language keiro-dsl -1\ncontext hospital-capacity\n" `shouldBe` Just InvalidLanguageVersion
+      failureCode "language keiro-dsl 5\ncontext hospital-capacity\n" `shouldBe` Just UnsupportedLanguageVersion
+      failureCode "language keiro-dsl 1\nlanguage keiro-dsl 1\ncontext hospital-capacity\n" `shouldBe` Just DuplicateLanguagePreamble
+      failureCode "context hospital-capacity\nlanguage keiro-dsl 1\n" `shouldBe` Just MisplacedLanguagePreamble
+
+    it "treats language and successor spellings as data in nested grammar positions" $ do
+      forM_ ["language-identifier-v1.keiro", "language-identifier-v2.keiro"] $ \fixture -> do
+        source <- readTestText ("test/fixtures/" <> fixture)
+        parsed <- parseRight fixture source
+        validateSpec (parsedSpec parsed) `shouldBe` []
+      v1 <- readTestText "test/fixtures/language-identifier-v1.keiro"
+      let manifest = "service language-collisions\nspec domain/collisions.keiro\n"
+          workspaceSource = memoryContentSource (Map.fromList [("service.keiro-workspace", manifest), ("domain/collisions.keiro", v1)])
+      loaded <- loadWorkspace workspaceSource "service.keiro-workspace"
+      loaded `shouldSatisfy` isRight
+
+    it "keeps duplicate and misplaced preamble diagnostics on their grammar lines" $ do
+      let sourceFailureAt expectedCode expectedLine source =
+            case parseSource "located.keiro" source of
+              Left (SourceLanguageFailure diagnostic) -> do
+                sourceLanguageErrorCode diagnostic `shouldBe` expectedCode
+                unLoc (sourceLanguageLoc diagnostic) `shouldBe` expectedLine
+              other -> expectationFailure ("expected located source-language failure, got " <> show other)
+      sourceFailureAt DuplicateLanguagePreamble 2 "language keiro-dsl 1\nlanguage keiro-dsl 1\ncontext located\n"
+      sourceFailureAt MisplacedLanguagePreamble 3 "context located\nid language prefix=lang\nlanguage keiro-dsl 1\n"
+
+    it "rejects a future version before parsing an invalid v1 body" $
+      case parseSource "future.keiro" "language keiro-dsl 5\nthis is not a v2 body\n" of
+        Left failure@(SourceLanguageFailure diagnostic) -> do
+          sourceLanguageErrorCode diagnostic `shouldBe` UnsupportedLanguageVersion
+          renderParseFailure failure `shouldSatisfy` T.isInfixOf "supported versions: 1, 2, 3, 4"
+          renderParseFailure failure `shouldNotSatisfy` T.isInfixOf "expecting `context`"
+        other -> expectationFailure ("expected source-language failure, got " <> show other)
+
+    it "accepts and canonically round-trips nominal declarations only in v2" $ do
+      let nominalSource =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context orders",
+                "id OrderId prefix=ord using {",
+                "  haskell package=orders-domain module=Orders.Id type=OrderId",
+                "  binding = \"Orders.KeiroBindings.orderIdBinding\"",
+                "  binding-version = \"1\"",
+                "  canonical-type = \"orders.OrderId.v1\"",
+                "  fixtures = \"Orders.KeiroBindings.orderIdFixtures\"",
+                "}",
+                "enum OrderStatus { Draft=draft Submitted=submitted } using {",
+                "  haskell package=orders-domain module=Orders.Order type=OrderStatus",
+                "  binding = \"Orders.KeiroBindings.orderStatusBinding\"",
+                "  binding-version = \"1\"",
+                "  canonical-type = \"orders.OrderStatus.v1\"",
+                "  fixtures = \"Orders.KeiroBindings.orderStatusFixtures\"",
+                "}",
+                "mapped nominal AccountNumber : Text {",
+                "  haskell package=orders-domain module=Orders.Account type=AccountNumber",
+                "  binding = \"Orders.KeiroBindings.accountNumberBinding\"",
+                "  binding-version = \"1\"",
+                "  canonical-type = \"orders.AccountNumber.v1\"",
+                "  fixtures = \"Orders.KeiroBindings.accountNumberFixtures\"",
+                "  initial = \"Orders.KeiroBindings.initialAccountNumber\"",
+                "}"
+              ]
+      parsed <- parseRight "nominal.keiro" nominalSource
+      length (specIds (parsedSpec parsed)) `shouldBe` 1
+      length (specEnums (parsedSpec parsed)) `shouldBe` 1
+      length (specNominalScalars (parsedSpec parsed)) `shouldBe` 1
+      parseSource "nominal-round-trip.keiro" (renderSource parsed) `shouldBe` Right parsed
+
+    it "reports successor nominal syntax as one language-version diagnostic under v1 and legacy" $ do
+      let body = "context orders\nmapped nominal AccountNumber : Text {}\n"
+      failureCode ("language keiro-dsl 1\n" <> body) `shouldBe` Just LanguageFeatureRequiresVersion
+      failureCode body `shouldBe` Just LanguageFeatureRequiresVersion
+
+    it "attributes every successor feature gate to its owning grammar production" $ do
+      let featureFailureAt expectedLine source =
+            case parseSource "feature.keiro" source of
+              Left (SourceLanguageFailure diagnostic) -> do
+                sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
+                unLoc (sourceLanguageLoc diagnostic) `shouldBe` expectedLine
+              other -> expectationFailure ("expected a located feature gate, got " <> show other)
+          aggregateWith clause =
+            T.unlines
+              [ "language keiro-dsl 1",
+                "context feature-gates",
+                "aggregate Account",
+                "  regs",
+                "    balance Text = \"0\"",
+                "  states Open",
+                "  command Adjust { amount:Text }",
+                "  event Adjusted = fields(Adjust)",
+                "  Open -- Adjust --> " <> clause <> " ; emit Adjusted ; goto Open"
+              ]
+      featureFailureAt 3 (T.unlines ["language keiro-dsl 1", "context feature-gates", "id AccountId prefix=acct using {"])
+      featureFailureAt 3 (T.unlines ["language keiro-dsl 1", "context feature-gates", "mapped nominal AccountNumber : Text {}"])
+      featureFailureAt 5 (T.unlines ["language keiro-dsl 1", "context feature-gates", "aggregate Account", "  regs", "    balance Integer = 0", "  states Open"])
+      featureFailureAt 9 (aggregateWith "guard reg.balance == cmd.amount")
+      featureFailureAt 9 (aggregateWith "implementation hole")
+
+    it "reports a declaration-only rewrite without semantic, generated, fold, or replay impact" $ do
+      fixture <- readTestText "test/fixtures/language-v1.keiro"
+      let legacyFixture = T.unlines (drop 1 (T.lines fixture))
+      legacySource <- parseRight "legacy.keiro" legacyFixture
+      declaredSource <- parseRight "declared.keiro" fixture
+      let oldSpec = parsedSpec legacySource
+          newSpec = parsedSpec declaredSource
+          changes = diffSources legacySource declaredSource
+          vectors = [ckVector kind | change <- changes, let kind = workspaceChangeKind change]
+      map changeCode changes `shouldBe` [SourceLanguageDeclarationChanged]
+      legacyDiffSpecs oldSpec newSpec `shouldBe` []
+      vectors `shouldSatisfy` all (\compatibility -> all ((== VCompatible) . (`verdictFor` compatibility)) [minBound .. maxBound])
+      case changes of
+        [change] ->
+          remediationFor (ckContext (workspaceChangeKind change)) SourceLanguageDeclarationChanged
+            `shouldBe` (RemedyNoSemanticAction :| [])
+        _ -> expectationFailure "expected one source-language change"
+      let legacyGeneratedSurface spec =
+            [ (modulePath scaffoldModule, moduleText scaffoldModule, kind scaffoldModule)
+            | scaffoldModule <- scaffoldModules (defaultContext (specContext spec)) spec
+            ]
+          legacyFoldFingerprint spec aggregate = aggregateFoldFingerprintForService (legacyCheckedService spec) aggregate
+      legacyGeneratedSurface oldSpec `shouldBe` legacyGeneratedSurface newSpec
+      [legacyFoldFingerprint oldSpec aggregate | NAggregate aggregate <- specNodes oldSpec]
+        `shouldBe` [legacyFoldFingerprint newSpec aggregate | NAggregate aggregate <- specNodes newSpec]
+      legacyReplayImpactSpecs oldSpec newSpec `shouldBe` ReplayNeutral
+
+    it "exposes stable JSON inspection for a source and canonically ordered workspace members" $ do
+      (sourceCode, sourceOut, sourceErr) <- runKeiroDsl ["inspect", "test/fixtures/reservation.keiro", "--format=json"]
+      sourceCode `shouldBe` ExitSuccess
+      sourceErr `shouldBe` ""
+      sourceOut `shouldContain` "\"schema\":\"keiro-dsl/source-inspection/1\""
+      sourceOut `shouldContain` "\"kind\":\"source\""
+      sourceOut `shouldContain` "\"sourceForm\":\"declared\""
+      sourceOut `shouldContain` "\"declaredLanguageVersion\":4"
+      sourceOut `shouldContain` "\"effectiveLanguageVersion\":4"
+      sourceOut `shouldContain` "\"effectiveSemanticContract\":{"
+      sourceOut `shouldContain` "\"runtimeSemantics\":\"keiro-dsl/runtime-semantics/3\""
+      sourceOut `shouldContain` "\"languageSupport\":\"stable\""
+      (workspaceCode, workspaceOut, workspaceErr) <- runKeiroDsl ["inspect", canonicalWorkspacePath, "--format=json"]
+      workspaceCode `shouldBe` ExitSuccess
+      workspaceErr `shouldBe` ""
+      workspaceOut `shouldContain` "\"kind\":\"workspace\""
+      workspaceOut `shouldContain` "\"service\":\"demo-project\""
+      workspaceOut `shouldContain` "\"effectiveSemanticContract\":{"
+      workspaceOut `shouldContain` "\"languageSupport\":\"stable\""
+      workspaceOut `shouldSatisfy` orderedSubstrings ["domain/project-artifact.keiro", "domain/project.keiro", "domain/shared.keiro"]
+
+    it "keeps only the named source-version compatibility fixtures outside stable v4" $ do
+      fixtureTree <- treeSnapshot "test/fixtures"
+      let outsideStableV4 =
+            sort
+              [ path
+              | (path, contents) <- fixtureTree,
+                takeExtension path == ".keiro",
+                "language keiro-dsl 4" `notElem` T.lines contents
+              ]
+      outsideStableV4
+        `shouldBe` sort
+          [ "aggregate-collection-expressions-v2-rejects.keiro",
+            "aggregate-scalar-expressions-v1-rejects.keiro",
+            "contract-v1-compat.keiro",
+            "id-domain-migration-v3.keiro",
+            "language-duplicate.keiro",
+            "language-future.keiro",
+            "language-identifier-v1.keiro",
+            "language-identifier-v2.keiro",
+            "language-legacy.keiro",
+            "language-malformed.keiro",
+            "language-misplaced.keiro",
+            "language-v1.keiro",
+            "language-zero.keiro",
+            "nominal-v1.keiro"
+          ]
+
+    it "checks v1, rejects a future contract once, and inspects legacy explicitly" $ do
+      (v1Code, v1Out, v1Err) <- runKeiroDsl ["check", "test/fixtures/language-v1.keiro"]
+      v1Code `shouldBe` ExitSuccess
+      v1Out `shouldBe` "OK\n"
+      v1Err `shouldBe` ""
+      (futureCode, _, futureErr) <- runKeiroDsl ["check", "test/fixtures/language-future.keiro"]
+      futureCode `shouldBe` ExitFailure 1
+      T.count "UnsupportedLanguageVersion" (T.pack futureErr) `shouldBe` 1
+      futureErr `shouldContain` "supported versions: 1, 2, 3, 4"
+      futureErr `shouldNotContain` "expecting `context`"
+      (legacyCode, legacyOut, legacyErr) <- runKeiroDsl ["inspect", "test/fixtures/language-legacy.keiro", "--format=json"]
+      legacyCode `shouldBe` ExitSuccess
+      legacyErr `shouldBe` ""
+      legacyOut `shouldContain` "\"sourceForm\":\"legacy-unversioned\""
+      legacyOut `shouldContain` "\"declaredLanguageVersion\":null"
+      legacyOut `shouldContain` "\"effectiveLanguageVersion\":1"
+      legacyOut `shouldContain` "\"languageSupport\":\"compatibility-only\""
+
+    it "checks and scaffolds contextual language identifiers through the CLI" $ do
+      forM_ ["language-identifier-v1.keiro", "language-identifier-v2.keiro"] $ \fixture -> do
+        let sourcePath = "test/fixtures/" <> fixture
+        (checkCode, checkOut, checkErr) <- runKeiroDsl ["check", sourcePath]
+        checkCode `shouldBe` ExitSuccess
+        checkOut `shouldBe` "OK\n"
+        checkErr `shouldBe` ""
+        withTempDirectory ("keiro-dsl-" <> fixture) $ \out -> do
+          (scaffoldCode, _, scaffoldErr) <- runKeiroDsl ["scaffold", sourcePath, "--out", out]
+          scaffoldCode `shouldBe` ExitSuccess
+          scaffoldErr `shouldContain` "firewall: OK"
+
+    it "preserves a workspace member's source-selection code beneath outer attribution" $ do
+      let manifest = "service demo\nspec domain/future.keiro\n"
+          futureSource = "language keiro-dsl 5\nthis body must not parse\n"
+          source = memoryContentSource (Map.fromList [("service.keiro-workspace", manifest), ("domain/future.keiro", futureSource)])
+      loaded <- loadWorkspace source "service.keiro-workspace"
+      case loaded of
+        Left (WorkspaceRefused (diagnostic :| [])) -> do
+          wdCode diagnostic `shouldBe` WorkspaceMemberParseFailed
+          sourceLanguageErrorCode <$> wdSourceLanguageCause diagnostic
+            `shouldBe` Just UnsupportedLanguageVersion
+          renderWorkspaceDiagnostic "service.keiro-workspace" diagnostic
+            `shouldSatisfy` T.isInfixOf "UnsupportedLanguageVersion"
+        other -> expectationFailure ("expected one attributed source-language refusal, got " <> show other)
+
+    it "attributes a workspace provenance-only diff to the changed member" $ do
+      workspace <- shouldComposeWorkspace canonicalWorkspacePath
+      case wsMembers workspace of
+        firstMember : remaining -> do
+          let changedMember = firstMember {wmSourceLanguage = LegacyUnversioned}
+              changedWorkspace = workspace {wsMembers = changedMember : remaining}
+              changes = diffWorkspaces workspace changedWorkspace
+          map (changeCode . wcChange) changes `shouldBe` [SourceLanguageDeclarationChanged]
+          map (fmap osFile . wcDeclarationSite) changes `shouldBe` [Just (wmPath firstMember)]
+          map wcChange changes `shouldSatisfy` all (not . gatedBreaking (gateWith [minBound .. maxBound]))
+        _ -> expectationFailure "canonical workspace had no member"
+
+  describe "ID domain" $ do
+    let parseRight name source = case parseSource name source of
+          Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+          Right value -> pure value
+
+    it "registers language 3 as the first enforced runtime contract" $ do
+      parsed <- case parseSource "id-domain-v3.keiro" "language keiro-dsl 3\ncontext id-domain\n" of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let contract = checkedLanguageContract (checkedSource parsed)
+      effectiveRuntimeSemantics contract `shouldBe` "keiro-dsl/runtime-semantics/2"
+      effectiveContractLanguageVersion contract `shouldBe` maybe (error "missing v3") id (languageVersion 3)
+      idDomainContractFor contract "req" `shouldSatisfy` (/= Nothing)
+
+    it "registers language 4 as contract admission semantics without changing aggregate ID admission" $ do
+      v3 <- parseRight "id-domain-v3.keiro" "language keiro-dsl 3\ncontext id-domain\n"
+      v4 <- parseRight "id-domain-v4.keiro" "language keiro-dsl 4\ncontext id-domain\n"
+      let v3Contract = checkedLanguageContract (checkedSource v3)
+          v4Contract = checkedLanguageContract (checkedSource v4)
+      effectiveRuntimeSemantics v4Contract `shouldBe` "keiro-dsl/runtime-semantics/3"
+      effectiveContractLanguageVersion v4Contract `shouldBe` maybe (error "missing v4") id (languageVersion 4)
+      idDomainContractFor v4Contract "req" `shouldBe` idDomainContractFor v3Contract "req"
+      contractIdDomainContractFor v3Contract "req" `shouldBe` Nothing
+      contractIdDomainContractFor v4Contract "req" `shouldBe` Just (typeIdV7Domain "req")
+
+    it "constructs typed KindIDs only after the frozen four-way admission policy" $ do
+      let valid = "req_01h455vb4pex5vsknk084sn02q"
+          uppercase = "req_01H455VB4PEX5VSKNK084SN02Q"
+          nonV7 = "req_00041061050r3gg28a1c60t3gf"
+      (KindID.toText @"req" <$> parseKindIdV7Text @"req" valid) `shouldBe` Right valid
+      parseKindIdV7Text @"req" "req-1" `shouldSatisfy` \case
+        Left IdDomainMalformed {} -> True
+        _ -> False
+      parseKindIdV7Text @"req" "other_01h455vb4pex5vsknk084sn02q" `shouldSatisfy` \case
+        Left (IdDomainWrongPrefix "req" "other") -> True
+        _ -> False
+      parseKindIdV7Text @"req" uppercase `shouldBe` Left IdDomainNonCanonical
+      parseKindIdV7Text @"req" nonV7 `shouldSatisfy` \case
+        Left IdDomainNotUuidV7 {} -> True
+        _ -> False
+      parseEither (parseKindIdV7Value @"req") (Aeson.String uppercase)
+        `shouldSatisfy` \case
+          Left problem -> "not canonical lowercase" `T.isInfixOf` T.pack problem
+          Right _ -> False
+
+    it "validates contract TypeID prefixes only at the language-4 boundary" $ do
+      let source versionNumber =
+            T.unlines
+              [ "language keiro-dsl " <> T.pack (show versionNumber),
+                "context invalid-contract-prefix",
+                "contract emergency {",
+                "  schemaVersion 1",
+                "  discriminator messageType",
+                "  topic incidentEvents \"emergency.incident.events\"",
+                "  event IncidentDeclared on incidentEvents {",
+                "    incidentId: typeid \"Bad\"",
+                "  }",
+                "}"
+              ]
+      v3 <- parseRight "contract-prefix-v3.keiro" (source (3 :: Int))
+      v4 <- parseRight "contract-prefix-v4.keiro" (source (4 :: Int))
+      validateService (checkedSource v3) `shouldBe` []
+      case validateService (checkedSource v4) of
+        [diagnostic] -> do
+          code diagnostic `shouldBe` ContractInvalidTypeIdPrefix
+          line diagnostic `shouldBe` 3
+          message diagnostic `shouldSatisfy` T.isInfixOf "contract 'emergency' event 'IncidentDeclared' field 'incidentId'"
+          message diagnostic `shouldSatisfy` T.isInfixOf "invalid TypeID prefix 'Bad'"
+        diagnostics -> expectationFailure ("expected one invalid contract prefix diagnostic, got " <> show diagnostics)
+
+    it "keeps version-3 and version-4 aggregate fold and replay semantics equal" $ do
+      v3Text <- readTestText "test/fixtures/id-domain-migration-v3.keiro"
+      v3 <- parseRight "fold-v3.keiro" v3Text
+      v4 <- parseRight "fold-v4.keiro" (T.replace "language keiro-dsl 3" "language keiro-dsl 4" v3Text)
+      let v3Service = checkedSource v3
+          v4Service = checkedSource v4
+          fingerprints service =
+            [ aggregateFoldFingerprintForService service aggregate
+            | NAggregate aggregate <- specNodes (checkedSpec service)
+            ]
+      fingerprints v4Service `shouldBe` fingerprints v3Service
+      diffServices v3Service v4Service `shouldBe` []
+      resolvedFold (ReplayImpact.replayImpactServices v3Service v4Service) `shouldBe` ReplayNeutral
+
+    it "keeps runtime validation and the exact Keiki text image in agreement" $ do
+      let contract = typeIdV7Domain "req"
+          sample = idDomainSampleText contract
+          suffix = T.drop (T.length "req_") sample
+          replaceAt position replacement value =
+            T.take position value <> T.singleton replacement <> T.drop (position + 1) value
+          accepted =
+            [ sample,
+              replaceAt (T.length "req_" + 10) 'f' sample,
+              replaceAt (T.length "req_" + 13) 'v' sample
+            ]
+          rejected =
+            [ "",
+              "req_",
+              "other_" <> suffix,
+              "req__" <> suffix,
+              T.dropEnd 1 sample,
+              sample <> "0",
+              T.toUpper sample,
+              replaceAt (T.length "req_" + 0) '8' sample,
+              replaceAt (T.length "req_" + 10) 'd' sample,
+              replaceAt (T.length "req_" + 13) 'c' sample
+            ]
+          patternValue = either (error . show) id (idDomainTextPattern contract)
+      idDomainVersion contract `shouldBe` "keiro-dsl/id-domain/typeid-v7/1"
+      idDomainSeparator contract `shouldBe` '_'
+      idDomainSuffixLength contract `shouldBe` 26
+      idDomainMaxLength contract `shouldBe` T.length sample
+      forM_ accepted $ \value -> do
+        validateIdDomainText contract value `shouldBe` Right ()
+        matchesTextPattern patternValue value `shouldBe` True
+      forM_ rejected $ \value -> do
+        validateIdDomainText contract value `shouldSatisfy` isLeft
+        matchesTextPattern patternValue value `shouldBe` False
+
+    it "agrees for generated canonical and malformed domain values" $ property $ do
+      let crockford = "0123456789abcdefghjkmnpqrstvwxyz"
+          segment count = vectorOf count (elements crockford)
+      leading <- elements "01234567"
+      beforeVersion <- segment 9
+      version <- elements "ef"
+      beforeVariant <- segment 2
+      variant <- elements "89abrstv"
+      afterVariant <- segment 12
+      let value = T.pack ("req_" <> [leading] <> beforeVersion <> [version] <> beforeVariant <> [variant] <> afterVariant)
+          contract = typeIdV7Domain "req"
+          patternValue = either (error . show) id (idDomainTextPattern contract)
+          invalidValues = [T.toUpper value, "other_" <> T.drop 4 value, T.dropEnd 1 value, value <> "0"]
+      pure $
+        conjoin
+          ( counterexample (T.unpack value) (validateIdDomainText contract value == Right () && matchesTextPattern patternValue value)
+              : [counterexample (T.unpack invalid) (isLeft (validateIdDomainText contract invalid) && not (matchesTextPattern patternValue invalid)) | invalid <- invalidValues]
+          )
+
+    it "enforces the same contract before consumer binding conversion and explains its version" $ do
+      v2Source <- readTestText "test/fixtures/nominal-scalars.keiro"
+      let v3Text = T.replace "language keiro-dsl 2" "language keiro-dsl 3" v2Source
+      parsed <- case parseSource "nominal-scalars-v3.keiro" v3Text of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let service = checkedSource parsed
+          spec = checkedSpec service
+          modules = scaffoldServiceModules (defaultContext (specContext spec)) service
+          generatedText suffix = case [moduleText value | value <- modules, T.pack suffix `T.isSuffixOf` T.pack (modulePath value)] of
+            [value] -> value
+            values -> error ("expected one generated module ending in " <> suffix <> ", got " <> show (length values))
+          codecModule = generatedText "NominalLedger/Codec.hs"
+          projectionModule = generatedText "NominalProjections.hs"
+          harnessModule = generatedText "NominalLedger/Harness.hs"
+      validateService service `shouldBe` []
+      codecModule `shouldSatisfy` T.isInfixOf "case validateIdDomainText (typeIdV7Domain \"ord\") input of"
+      codecModule `shouldSatisfy` T.isInfixOf "Right () -> case KindID.parseText @\"ord\" input of"
+      projectionModule `shouldSatisfy` T.isInfixOf "idDomainTextPattern (typeIdV7Domain \"ord\")"
+      projectionModule `shouldSatisfy` T.isInfixOf "validateIdDomainText (typeIdV7Domain \"ord\") value"
+      harnessModule `shouldSatisfy` T.isInfixOf "nominal ID binding preserves canonical representations: OrderId"
+      harnessModule `shouldSatisfy` T.isInfixOf "nominal ID boundary rejects wrong-prefix and normalized text: OrderId"
+      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligationsForService service)
+      let orderIdBindings = [obligation | obligation <- obligations, obligationMappedName obligation == "OrderId", obligationKind obligation == BindingValue]
+      map obligationIdDomainContract orderIdBindings `shouldBe` [Just "keiro-dsl/id-domain/typeid-v7/1"]
+      renderBindingObligations (specContext spec) obligations
+        `shouldSatisfy` T.isInfixOf "id-domain-contract: \"keiro-dsl/id-domain/typeid-v7/1\""
+
+    it "reports adoption by boundary, invalidates snapshots, and preserves replay compatibility" $ do
+      v2Text <- readTestText "test/fixtures/id-domain-migration-v3.keiro"
+      let oldText = T.replace "language keiro-dsl 3" "language keiro-dsl 2" v2Text
+      oldSource <- case parseSource "id-domain-migration-v2.keiro" oldText of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      newSource <- case parseSource "id-domain-migration-v3.keiro" v2Text of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let oldService = checkedSource oldSource
+          newService = checkedSource newSource
+          changes = diffSources oldSource newSource
+          findings = [kindOfChange change | change <- changes, changeCode change == IdDomainContractChanged]
+      length findings `shouldBe` 1
+      forM_ findings $ \finding -> do
+        verdictFor PrivateHistoryRead (ckVector finding) `shouldBe` VCompatible
+        verdictFor OldBinaryReadNewEvents (ckVector finding) `shouldBe` VCompatible
+        verdictFor SnapshotHydration (ckVector finding) `shouldBe` VAdvisory
+        verdictFor PublicConsumer (ckVector finding) `shouldBe` VBreaking
+        verdictFor PersistedIdentity (ckVector finding) `shouldBe` VCompatible
+        verdictFor ConsumerBuild (ckVector finding) `shouldBe` VAdvisory
+        ckDetail finding `shouldSatisfy` T.isInfixOf "historical event replay retains its legacy decoder"
+        remediationFor (ckContext finding) (ckCode finding)
+          `shouldBe` RemedyDeploymentOrder RolloutProducerLast :| [RemedyStateCodecBump, RemedyRecompileConsumers, RemedyRunConformance]
+      [ckDetail finding | change <- changes, changeCode change == SourceLanguageDeclarationChanged, let finding = kindOfChange change]
+        `shouldSatisfy` all (T.isInfixOf "effective runtime semantics changed")
+      idDomainIdentitiesForService oldService `shouldBe` []
+      idDomainIdentitiesForService newService
+        `shouldSatisfy` any (T.isInfixOf "contract=keiro-dsl/id-domain/typeid-v7/1")
+      resolvedFold (ReplayImpact.replayImpactServices oldService newService) `shouldSatisfy` \case
+        ReplayImpact.ReplayAffected impacts ->
+          maybe False includeSnapshotStreams (Map.lookup "OrderBook" impacts)
+        ReplayImpact.ReplayNeutral -> False
+
+    it "keeps the raw constructor outside the compiled public module surface" $
+      withTempDirectory "keiro-dsl-id-domain-hidden-constructor" $ \out -> do
+        sourceText <- readTestText "test/fixtures/id-domain-migration-v3.keiro"
+        parsed <- case parseSource "id-domain-migration-v3.keiro" sourceText of
+          Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+          Right value -> pure value
+        let service = checkedSource parsed
+            spec = checkedSpec service
+            ctx = defaultContext (specContext spec)
+            modules = scaffoldServiceModules ctx service
+            attempt = out </> "Attempt.hs"
+            ghcOutput = out </> ".ghc"
+        result <- executeServiceScaffold out False "id-domain-migration-v3.keiro" (parsedSourceLanguage parsed) ctx service modules
+        result `shouldSatisfy` isRight
+        recordContents <- TIO.readFile (out </> recordFileName (specContext spec))
+        record <- case parseRecord recordContents of
+          Nothing -> expectationFailure "generated ID-domain scaffold record did not parse" >> fail "unreachable"
+          Just value -> pure value
+        recIdDomains record `shouldBe` idDomainIdentitiesForService service
+        recNominalEqualities record
+          `shouldSatisfy` any (T.isInfixOf "keiro-dsl/id-domain/typeid-v7/1")
+        createDirectoryIfMissing True ghcOutput
+        TIO.writeFile
+          attempt
+          ( T.unlines
+              [ "module Attempt where",
+                "import Generated.IdDomainMigration.Nominals (OrderId (..))",
+                "bad :: OrderId",
+                "bad = OrderId \"ord_LEGACY-NOT-TYPEID\""
+              ]
+          )
+        (exitCode, standardOutput, standardError) <-
+          readProcessWithExitCode
+            "cabal"
+            [ "exec",
+              "--",
+              "ghc",
+              "-XGHC2024",
+              "-XOverloadedStrings",
+              "-fno-code",
+              "-fforce-recomp",
+              "-outputdir",
+              ghcOutput,
+              "-i" <> out,
+              attempt
+            ]
+            ""
+        exitCode `shouldSatisfy` (/= ExitSuccess)
+        (standardOutput <> standardError) `shouldContain` "OrderId"
+
+    it "emits one enforced nominal owner for a version-3 workspace" $ do
+      manifest <- readTestText "test/fixtures/workspace-nominals/service.keiro-workspace"
+      shared <- readTestText "test/fixtures/workspace-nominals/domain/shared.keiro"
+      project <- readTestText "test/fixtures/workspace-nominals/domain/project.keiro"
+      artifact <- readTestText "test/fixtures/workspace-nominals/domain/project-artifact.keiro"
+      let v3 = T.replace "language keiro-dsl 2" "language keiro-dsl 3"
+          source =
+            memoryContentSource
+              ( Map.fromList
+                  [ ("service.keiro-workspace", manifest),
+                    ("domain/shared.keiro", v3 shared),
+                    ("domain/project.keiro", v3 project),
+                    ("domain/project-artifact.keiro", v3 artifact)
+                  ]
+              )
+      loaded <- loadWorkspace source "service.keiro-workspace"
+      workspace <- case loaded of
+        Left failure -> expectationFailure (show failure) >> fail "unreachable"
+        Right value -> pure value
+      plan <- case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
+        Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
+        Right value -> pure value
+      let paths = map (modulePath . fst) (wpModules plan)
+      length (filter (== "Generated/WorkspaceNominalProof/Nominals.hs") paths) `shouldBe` 1
+      length (filter (== "Generated/WorkspaceNominalProof/Nominals/Internal.hs") paths) `shouldBe` 1
+      forM_ [moduleText value | (value, _) <- wpModules plan, "/Domain.hs" `T.isSuffixOf` T.pack (modulePath value)] $ \domainText ->
+        domainText `shouldSatisfy` (not . T.isInfixOf "ProjectId (..)")
+      withTempDirectory "keiro-dsl-v3-workspace-record" $ \out -> do
+        emitted <- executeWorkspaceScaffold out False plan
+        emitted `shouldSatisfy` isRight
+        recordContents <- TIO.readFile (out </> workspaceRecordFileName (wsService workspace))
+        record <- case parseWorkspaceRecord recordContents of
+          Nothing -> expectationFailure "version-3 workspace record did not parse" >> fail "unreachable"
+          Just value -> pure value
+        wrIdDomains record `shouldBe` idDomainIdentitiesForService (wpCheckedService plan)
+        wrNominalEqualities record
+          `shouldSatisfy` any (T.isInfixOf "keiro-dsl/id-domain/typeid-v7/1")
+
+    it "emits an abstract public ID, an internal legacy seam, and exact equality" $ do
+      v2Source <- readTestText "test/fixtures/aggregate-scalar-expressions-v2.keiro"
+      let v3Source = T.replace "language keiro-dsl 2" "language keiro-dsl 3" v2Source
+      parsed <- case parseSource "aggregate-scalar-expressions-v3.keiro" v3Source of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let service = checkedSource parsed
+          spec = checkedSpec service
+          modules = scaffoldServiceModules (defaultContext (specContext spec)) service
+          moduleAt path = case [value | value <- modules, modulePath value == path] of
+            [value] -> pure value
+            values -> expectationFailure ("expected one module at " <> path <> ", got " <> show (map modulePath values)) >> fail "unreachable"
+      validateService service `shouldBe` []
+      publicNominals <- moduleAt "Generated/AggregateScalarExpressions/Nominals.hs"
+      internalNominals <- moduleAt "Generated/AggregateScalarExpressions/Nominals/Internal.hs"
+      domainModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs"
+      codecModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs"
+      transducerModule <- moduleAt "Generated/AggregateScalarExpressions/ScalarAccount/Transducer.hs"
+      moduleText publicNominals `shouldSatisfy` T.isInfixOf "parseRequestId"
+      moduleText publicNominals `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection RequestIdEqualityProjection"
+      moduleText publicNominals `shouldSatisfy` T.isInfixOf "idDomainTextPattern (typeIdV7Domain \"req\")"
+      moduleText publicNominals `shouldSatisfy` (not . T.isInfixOf "unsafeRequestIdFromLegacyText")
+      moduleText publicNominals `shouldSatisfy` (not . T.isInfixOf "newtype RequestId")
+      moduleText internalNominals `shouldSatisfy` T.isInfixOf "newtype RequestId = RequestId Text"
+      moduleText internalNominals `shouldSatisfy` T.isInfixOf "unsafeRequestIdFromLegacyText"
+      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "RequestId (..)")
+      moduleText codecModule `shouldSatisfy` T.isInfixOf "unsafeRequestIdFromLegacyText <$>"
+      moduleText transducerModule `shouldSatisfy` T.isInfixOf "case parseRequestId"
+      firewallBreaches modules `shouldBe` []
+
+  describe "scalar expressions" $ do
+    it "parses, validates, and round-trips the authoritative stable scalar fixture" $ do
+      source <- readTestText "test/fixtures/aggregate-scalar-expressions-v2.keiro"
+      parsed <- case parseSource "aggregate-scalar-expressions-v2.keiro" source of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      validateSpec (parsedSpec parsed) `shouldBe` []
+      parseSource "round-trip.keiro" (renderSource parsed) `shouldBe` Right parsed
+      case [aggregate | NAggregate aggregate <- specNodes (parsedSpec parsed)] of
+        [aggregate] -> case aggTransitions aggregate of
+          transition : holeTransition : [] -> do
+            tImplementation transition `shouldBe` GeneratedImplementation
+            tImplementation holeTransition `shouldBe` HoleImplementation
+            let environment = expressionEnvironment (parsedSpec parsed) aggregate transition
+            case lookup "reserved" (tWrites transition) >>= either (const Nothing) Just . resolveWriteExpr environment "reserved" of
+              Just resolved -> do
+                typedScalarType resolved `shouldBe` AggregateNatural
+                show (typedScalarNode resolved) `shouldContain` "TotalNaturalArithmetic"
+              Nothing -> expectationFailure "reserved write did not resolve"
+            let service = checkedSource parsed
+                modules = scaffoldServiceModules (defaultContext (specContext (parsedSpec parsed))) service
+                transducer = generatedTextEndingIn "Transducer.hs" modules
+                holes = holeTextEndingIn "Holes.hs" modules
+                surface = aggregateFoldSurfaceForService service aggregate
+                manifest = renderManifestForService "aggregate-scalar-expressions-v2.keiro" modules service
+                readableTransducer = T.unwords (T.words transducer)
+            aggregateFoldFingerprintForService service aggregate `shouldBe` "60f4f059f718b2ee2bca06360ea20221"
+            T.lines surface
+              `shouldBe` [ "semantic-contract:keiro-dsl/runtime-semantics/2",
+                           "state:Open|terminal=false",
+                           "state:Reviewed|terminal=false",
+                           "state:Closed|terminal=true",
+                           "reg:balance:Integer=0",
+                           "reg:reserved:Natural=0",
+                           "reg:capacity:Natural=5",
+                           "reg:machine:Int=0",
+                           "reg:label:Text=\"\"",
+                           "reg:active:Bool=False",
+                           "reg:mode:AccountMode=Normal",
+                           "reg:requestId:RequestId=placeholder",
+                           "reg:openedAt:Time=(UTCTime (fromGregorian 2026 1 1) (picosecondsToDiffTime 0))",
+                           "reg:limits:Limits=initial",
+                           "mapped-register:Limits|wire=4463db782a5b9924|canonical=scalar-expressions.Limits.v1|binding=ScalarExpressions.Bindings.limitsBinding|binding-version=1|initial=ScalarExpressions.Bindings.initialLimits",
+                           "nominal-equality-use:nominal-equality|name=AccountMode|contract=keiro-dsl/nominal-equality/1|key=Text|domain=finite-text:normal,restricted|owner=generated",
+                           "nominal-equality-use:nominal-equality|name=RequestId|contract=keiro-dsl/nominal-equality/2|key=Text|domain=typeid-v7-text:req:keiro-dsl/id-domain/typeid-v7/1|owner=generated",
+                           "transition:live|Open|Adjust|implementation=generated|guard=cmd.balance + reg.balance >= -100 && reg.reserved + cmd.requested <= reg.capacity && cmd.observedAt >= reg.openedAt && cmd.limits.minimum >= reg.limits.minimum && cmd.active == false && cmd.mode == reg.mode && cmd.requestId == reg.requestId|writes=balance:=reg.balance + cmd.balance * 2;reserved:=reg.reserved + (cmd.requested - reg.capacity);machine:=-7;label:=\"adjusted\";active:=true;mode:=AccountMode.Restricted;requestId:=RequestId(\"req_01h455vb4pex5vsknk084sn02q\");openedAt:=\"2026-02-03T04:05:06Z\";limits:=cmd.limits|emits=Adjusted|outputs=Adjusted=generated-command-identity:Adjust[balance=balance:Integer,requested=requested:Natural,machine=machine:Int,label=label:Text,active=active:Bool,mode=mode:AccountMode,requestId=requestId:RequestId,observedAt=observedAt:Time,limits=limits:Limits]|goto=Reviewed",
+                           "transition:live|Reviewed|Close|implementation=hole|guard=|writes=|emits=ClosedEvent|outputs=ClosedEvent=generated-command-identity:Close[balance=balance:Integer]|goto=Closed"
+                         ]
+            diffServices service service `shouldBe` []
+            resolvedFold (ReplayImpact.replayImpactServices service service) `shouldBe` ReplayNeutral
+            manifest `shouldSatisfy` (not . T.isInfixOf "Generated.AggregateScalarExpressions.ScalarAccount.Expressions")
+            map modulePath modules `shouldSatisfy` all (not . T.isSuffixOf "Expressions.hs" . T.pack)
+            map modulePath modules `shouldSatisfy` any (T.isSuffixOf "Transducer.hs" . T.pack)
+            transducer `shouldSatisfy` T.isInfixOf "let commandLimitsMinimum = K.inpProj"
+            transducer `shouldSatisfy` T.isInfixOf "registerLimitsMinimum = K.regProj"
+            readableTransducer `shouldSatisfy` T.isInfixOf "B.requireGuard $ (((((d.balance .+ B.reg @\"balance\" .>= K.lit (-100 :: Integer) .&& B.reg @\"reserved\" .+ d.requested .<= B.reg @\"capacity\") .&& d.observedAt .>= B.reg @\"openedAt\") .&& commandLimitsMinimum .>= registerLimitsMinimum) .&& d.active .== K.lit False) .&& commandMode .== registerMode) .&& commandRequestId .== registerRequestId"
+            transducer `shouldSatisfy` T.isInfixOf "B.slot @\"balance\" =: (B.reg @\"balance\" .+ d.balance .* K.lit (2 :: Integer))"
+            transducer `shouldSatisfy` T.isInfixOf "B.slot @\"reserved\" =: (B.reg @\"reserved\" .+ (d.requested .- B.reg @\"capacity\"))"
+            transducer `shouldSatisfy` (not . T.isInfixOf "K.PAnd")
+            transducer `shouldSatisfy` (not . T.isInfixOf "K.tadd")
+            transducer `shouldSatisfy` T.isInfixOf "scalarAccountPredicateVerifications"
+            transducer `shouldSatisfy` T.isInfixOf "S.verifyPredicate predicate"
+            transducer `shouldSatisfy` T.isInfixOf "B.emit wireAdjusted (AdjustedTermFields"
+            transducer `shouldSatisfy` T.isInfixOf "balance = d.balance"
+            transducer `shouldSatisfy` (not . T.isInfixOf "transition1OpenAdjustOutput1Adjusted")
+            holes `shouldSatisfy` (not . T.isInfixOf "transition1OpenAdjustOutput1Adjusted")
+            holes `shouldSatisfy` (not . T.isInfixOf "transition2ReviewedCloseOutput1ClosedEvent")
+            holes `shouldSatisfy` T.isInfixOf "transition2ReviewedCloseHoleFoldVersion"
+            holes `shouldSatisfy` (not . T.isInfixOf "scalarAccountTransducer")
+            firewallBreaches modules `shouldBe` []
+          _ -> expectationFailure "expected one generated and one Hole scalar transition"
+        _ -> expectationFailure "expected one scalar aggregate"
+
+    it "pins every readable operator, equal-precedence child position, and bare Boolean guard" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context readable-renderer",
+                "enum RenderStatus { Ready=ready Waiting=waiting }",
+                "aggregate Renderer",
+                "  regs",
+                "    total Integer = 0",
+                "    leftNested Integer = 0",
+                "    product Integer = 1",
+                "    status RenderStatus = Ready",
+                "  states Open Closed!",
+                "  command Evaluate { left:Integer right:Integer third:Integer status:RenderStatus }",
+                "  event Evaluated = fields(Evaluate)",
+                "  Open -- Evaluate -->",
+                "    guard ((cmd.left < cmd.right || cmd.left <= cmd.right) || (cmd.left > cmd.right || cmd.left >= cmd.right))",
+                "      && (cmd.left == cmd.right && cmd.left != cmd.third)",
+                "      && cmd.status == RenderStatus.Waiting",
+                "    write total := reg.total + (cmd.left - cmd.right)",
+                "    write leftNested := (reg.leftNested + cmd.left) - cmd.right",
+                "    write product := cmd.left * (cmd.right * cmd.third)",
+                "    write status := RenderStatus.Ready",
+                "    emit Evaluated",
+                "    goto Closed",
+                "aggregate BooleanRenderer",
+                "  regs",
+                "    enabled Bool = False",
+                "  states Open Closed!",
+                "  command Enable { enabled:Bool }",
+                "  event Enabled = fields(Enable)",
+                "  Open -- Enable -->",
+                "    guard cmd.enabled",
+                "    write enabled := cmd.enabled",
+                "    emit Enabled",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<readable-renderer>" source
+      errorCodes spec `shouldBe` []
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          moduleAt suffix = case [moduleText value | value <- modules, T.pack suffix `T.isSuffixOf` T.pack (modulePath value)] of
+            [value] -> pure value
+            values -> expectationFailure ("expected one generated module ending in " <> suffix <> ", got " <> show (length values)) >> fail "unreachable"
+      renderer <- moduleAt "/Renderer/Transducer.hs"
+      booleanRenderer <- moduleAt "/BooleanRenderer/Transducer.hs"
+      let normalizedRenderer = T.unwords (T.words renderer)
+          normalizedBooleanRenderer = T.unwords (T.words booleanRenderer)
+      renderer
+        `shouldSatisfy` T.isInfixOf "import Keiki.Core (HsPred, SymTransducer, (.*), (.+), (.-), (.==), (./=), (.<), (.<=), (.>), (.>=), (.&&), (.||))"
+      normalizedRenderer
+        `shouldSatisfy` T.isInfixOf "(d.left .< d.right .|| d.left .<= d.right) .|| d.left .> d.right .|| d.left .>= d.right"
+      normalizedRenderer
+        `shouldSatisfy` T.isInfixOf ".&& d.left .== d.right .&& d.left ./= d.third"
+      normalizedRenderer
+        `shouldSatisfy` T.isInfixOf ".&& commandStatus .== K.lit (\"waiting\" :: Text)"
+      renderer
+        `shouldSatisfy` T.isInfixOf "B.slot @\"total\" =: (B.reg @\"total\" .+ (d.left .- d.right))"
+      renderer
+        `shouldSatisfy` T.isInfixOf "B.slot @\"leftNested\" =: (B.reg @\"leftNested\" .+ d.left .- d.right)"
+      renderer
+        `shouldSatisfy` T.isInfixOf "B.slot @\"product\" =: d.left .* (d.right .* d.third)"
+      normalizedBooleanRenderer `shouldSatisfy` T.isInfixOf "B.requireGuard $ d.enabled .== K.lit True"
+      firewallBreaches modules `shouldBe` []
+
+    it "suffixes normalized projection-alias collisions deterministically" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context projection-alias-collision",
+                "mapped structural record AliasCollision {",
+                "  haskell package=keiro-dsl module=Renderer.Domain type=AliasCollision",
+                "  binding = \"Renderer.Bindings.aliasCollisionBinding\"",
+                "  binding-version = \"1\"",
+                "  canonical-type = \"renderer.AliasCollision.v1\"",
+                "  fixtures = \"Renderer.Bindings.aliasCollisionCases\"",
+                "  initial = \"Renderer.Bindings.initialAliasCollision\"",
+                "  wire object constructor=AliasCollision unknown-fields=reject {",
+                "    dash as \"foo-bar\" : Integer required",
+                "    underscore as \"foo_bar\" : Integer required",
+                "  }",
+                "}",
+                "aggregate AliasRenderer",
+                "  regs",
+                "    values AliasCollision = initial",
+                "  states Open Closed!",
+                "  command Compare { values:AliasCollision }",
+                "  event Compared = fields(Compare)",
+                "  Open -- Compare -->",
+                "    guard cmd.values.dash == reg.values.dash",
+                "      && cmd.values.underscore == reg.values.underscore",
+                "    emit Compared",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<projection-alias-collision>" source
+      errorCodes spec `shouldBe` []
+      let transducer = generatedTextEndingIn "Transducer.hs" (scaffoldModules (defaultContext (specContext spec)) spec)
+      transducer `shouldSatisfy` T.isInfixOf "let commandValuesFooBar = K.inpProj"
+      transducer `shouldSatisfy` T.isInfixOf "registerValuesFooBar = K.regProj"
+      transducer `shouldSatisfy` T.isInfixOf "commandValuesFooBar2 = K.inpProj"
+      transducer `shouldSatisfy` T.isInfixOf "registerValuesFooBar2 = K.regProj"
+
+    it "keeps evolution identity independent of module layout but sensitive to checked behavior" $ do
+      source <- readTestText "test/fixtures/aggregate-scalar-expressions-v2.keiro"
+      original <- case parseSource "aggregate-scalar-expressions-v2.keiro" source of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      changed <- case parseSource "aggregate-scalar-expressions-changed.keiro" (T.replace "cmd.active == false" "cmd.active == true" source) of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      case ( [aggregate | NAggregate aggregate <- specNodes (parsedSpec original)],
+             [aggregate | NAggregate aggregate <- specNodes (parsedSpec changed)]
+           ) of
+        ([originalAggregate], [changedAggregate]) -> do
+          let service = checkedSource original
+              changedService = checkedSource changed
+              prefixed = defaultContext (specContext (parsedSpec original))
+              collocated = prefixed {moduleRoot = "Acme", placement = CollocatedLeaf}
+              prefixedModules = scaffoldServiceModules prefixed service
+              collocatedModules = scaffoldServiceModules collocated service
+              originalSurface = aggregateFoldSurfaceForService service originalAggregate
+              originalFingerprint = aggregateFoldFingerprintForService service originalAggregate
+          map modulePath prefixedModules `shouldNotBe` map modulePath collocatedModules
+          sum (map (T.length . moduleText) prefixedModules) `shouldSatisfy` (> 0)
+          sum (map (T.length . moduleText) collocatedModules) `shouldSatisfy` (> 0)
+          aggregateFoldSurfaceForService service originalAggregate `shouldBe` originalSurface
+          aggregateFoldFingerprintForService service originalAggregate `shouldBe` originalFingerprint
+          aggregateFoldSurfaceForService changedService changedAggregate `shouldNotBe` originalSurface
+          aggregateFoldFingerprintForService changedService changedAggregate `shouldNotBe` originalFingerprint
+        found -> expectationFailure ("expected one aggregate before and after behavior mutation, got " <> show (length (fst found), length (snd found)))
+
+    it "rejects cross-command fields(Command) output before scaffolding" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context output-command-mismatch",
+                "aggregate Account",
+                "  regs",
+                "  states Open Closed!",
+                "  command OpenAccount { accountId:Text }",
+                "  command CloseAccount { accountId:Text }",
+                "  event AccountOpened = fields(OpenAccount)",
+                "  Open -- CloseAccount --> emit AccountOpened ; goto Closed"
+              ]
+      spec <- parseInlineSpec "<output-command-mismatch>" source
+      errorCodes spec `shouldContain` [EventOutputCommandMismatch]
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        [aggregate] -> case aggTransitions aggregate of
+          [transition] ->
+            eventOutputMapping spec aggregate transition 1 "AccountOpened"
+              `shouldBe` Left (OutputCommandMismatch "OpenAccount" "CloseAccount" "AccountOpened")
+          _ -> expectationFailure "expected one transition"
+        _ -> expectationFailure "expected one aggregate"
+
+    it "rejects Int arithmetic and mixed numeric operands before scaffolding" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-errors",
+                "aggregate Counter",
+                "  regs",
+                "    machine Int = 0",
+                "    exact Integer = 0",
+                "  states Open Closed!",
+                "  command Add { machine:Int exact:Integer }",
+                "  event Added = fields(Add)",
+                "  Open -- Add -->",
+                "    guard cmd.machine + 1 >= 0 && cmd.exact == cmd.machine",
+                "    emit Added",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<scalar-errors>" source
+      errorCodes spec `shouldContain` [AggregateExpressionOperatorUnsupported, AggregateExpressionOperandTypeMismatch]
+
+    it "rejects nominal type confusion and unqualified enum values at source checking" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context nominal-type-confusion",
+                "id OrderId prefix=ord",
+                "id UserId prefix=usr",
+                "enum OrderStatus { Draft=draft Submitted=submitted }",
+                "enum UserStatus { Active=active Disabled=disabled }",
+                "aggregate Account",
+                "  regs",
+                "    orderId OrderId = placeholder",
+                "    status OrderStatus = Draft",
+                "  states Open Closed!",
+                "  command Compare { orderId:OrderId userId:UserId status:OrderStatus userStatus:UserStatus label:Text }",
+                "  event Compared = fields(Compare)",
+                "  Open -- Compare -->",
+                "    guard cmd.orderId == cmd.userId",
+                "      && cmd.status == cmd.userStatus",
+                "      && cmd.orderId == cmd.label",
+                "      && cmd.status == Draft",
+                "    emit Compared",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<nominal-type-confusion>" source
+      let diagnostics = validateSpec spec
+      length [() | diagnostic <- diagnostics, code diagnostic == AggregateExpressionOperandTypeMismatch]
+        `shouldBe` 3
+      errorCodes spec `shouldContain` [AggregateExpressionRootUnknown]
+      T.unlines (map message diagnostics) `shouldSatisfy` T.isInfixOf "qualify"
+
+    it "rejects machine-Int arithmetic at both platform bounds" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-int-bounds",
+                "aggregate Counter",
+                "  regs",
+                "    machine Int = 0",
+                "  states Open Closed!",
+                "  command Set { machine:Int }",
+                "  event SetEvent = fields(Set)",
+                "  Open -- Set -->",
+                "    guard cmd.machine + 1 >= " <> T.pack (show (minBound :: Int)),
+                "      && cmd.machine - 1 <= " <> T.pack (show (maxBound :: Int)),
+                "    emit SetEvent",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<scalar-int-bounds>" source
+      length [() | diagnostic <- validateSpec spec, code diagnostic == AggregateExpressionOperatorUnsupported]
+        `shouldBe` 2
+
+    it "rejects predicate-valued Bool writes that Keiki cannot represent as scalar terms" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-bool-write",
+                "aggregate Flag",
+                "  regs",
+                "    active Bool = False",
+                "  states Open Closed!",
+                "  command Set { active:Bool }",
+                "  event SetEvent = fields(Set)",
+                "  Open -- Set -->",
+                "    write active := cmd.active == true",
+                "    emit SetEvent",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<scalar-bool-write>" source
+      errorCodes spec `shouldContain` [AggregateExpressionOperatorUnsupported]
+
+    it "requires explicit roots for a same-named register and command field" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-ambiguity",
+                "aggregate Counter",
+                "  regs",
+                "    amount Integer = 0",
+                "  states Open Closed!",
+                "  command Set { amount:Integer }",
+                "  event SetEvent = fields(Set)",
+                "  Open -- Set -->",
+                "    guard amount == 0",
+                "    emit SetEvent",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<scalar-ambiguity>" source
+      errorCodes spec `shouldContain` [AggregateExpressionRootAmbiguous]
+
+    it "enforces exclusive Hole ownership and preserves its canonical spelling" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-hole",
+                "aggregate Counter",
+                "  regs",
+                "    amount Integer = 0",
+                "  states Open Closed!",
+                "  command Set { amount:Integer }",
+                "  event SetEvent = fields(Set)",
+                "  Open -- Set -->",
+                "    implementation hole",
+                "    guard cmd.amount >= 0",
+                "    emit SetEvent",
+                "    goto Closed"
+              ]
+      parsed <- case parseSource "<scalar-hole>" source of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      errorCodes (parsedSpec parsed) `shouldContain` [AggregateTransitionOwnershipConflict]
+      renderSource parsed `shouldSatisfy` T.isInfixOf "implementation hole"
+
+    it "generates a stable per-transition Hole boundary and fold token" $ do
+      let source =
+            T.unlines
+              [ "language keiro-dsl 2",
+                "context scalar-hole",
+                "aggregate Counter",
+                "  regs",
+                "    amount Integer = 0",
+                "  states Open Closed!",
+                "  command Set { amount:Integer }",
+                "  event SetEvent = fields(Set)",
+                "  Open -- Set -->",
+                "    implementation hole",
+                "    emit SetEvent",
+                "    goto Closed"
+              ]
+      spec <- parseInlineSpec "<scalar-hole-valid>" source
+      aggregate <- case [value | NAggregate value <- specNodes spec] of
+        [value] -> pure value
+        _ -> expectationFailure "expected one Hole aggregate" >> fail "unreachable"
+      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
+          transducer = generatedTextEndingIn "Transducer.hs" modules
+          holes = holeTextEndingIn "Holes.hs" modules
+      errorCodes spec `shouldBe` []
+      map modulePath modules `shouldSatisfy` all (not . T.isSuffixOf "Expressions.hs" . T.pack)
+      transducer `shouldSatisfy` T.isInfixOf "Holes.transition1OpenSetHole d"
+      transducer `shouldSatisfy` T.isInfixOf "foldToken Holes.transition1OpenSetHoleFoldVersion"
+      holes `shouldSatisfy` T.isInfixOf "transition1OpenSetHole _d = B.requireGuard K.PTop"
+      holes `shouldSatisfy` T.isInfixOf "transition1OpenSetHoleFoldVersion = FoldVersion"
+      holes `shouldSatisfy` (not . T.isInfixOf "counterTransducer")
+
+    it "pins v1 and collection rejection at their stable boundaries" $ do
+      v1Source <- readTestText "test/fixtures/aggregate-scalar-expressions-v1-rejects.keiro"
+      case parseSource "v1.keiro" v1Source of
+        Left (SourceLanguageFailure diagnostic) -> sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
+        other -> expectationFailure ("expected v1 source-language refusal, got " <> show other)
+      collectionSource <- readTestText "test/fixtures/aggregate-collection-expressions-v2-rejects.keiro"
+      case parseSource "collections.keiro" collectionSource of
+        Left failure -> renderParseFailure failure `shouldSatisfy` T.isInfixOf "CollectionExpressionUnsupported"
+        Right _ -> expectationFailure "collection syntax unexpectedly parsed"
+
+    it "keeps arithmetic operands intact when complementing a scalar comparison" $ do
+      let left = EAdd noLoc (EPath noLoc CommandRoot ["balance"]) (ELiteral noLoc (LiteralIntegral 2))
+          right = ESubtract noLoc (EPath noLoc RegisterRoot ["balance"]) (ELiteral noLoc (LiteralIntegral 3))
+          predicate = ECmp OpLt left right
+      complementExpr predicate `shouldBe` ECmp OpGe left right
+      complementExpr (complementExpr predicate) `shouldBe` predicate
+
+    it "keeps the committed scalar-expression conformance tree fresh" $ do
+      modules <- scaffoldFixture "test/fixtures/aggregate-scalar-expressions-v2.keiro"
+      forM_ [generatedModule | generatedModule <- modules, kind generatedModule == Generated] $ \generatedModule -> do
+        committed <- readTestText ("test/conformance-scalar-expressions/" <> modulePath generatedModule)
+        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generatedModule)
+
+  describe "behavior obligations" $ do
+    it "inventories every live-reachable cell, guarded edge, terminal rejection, and replay edge" $ do
+      spec <- specOf "test/fixtures/behavior-complete.keiro"
+      requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
+      length requirements `shouldBe` 14
+      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.LiveTransition] `shouldBe` 5
+      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.RequiredRejection] `shouldBe` 8
+      length [() | requirement <- requirements, Behavior.requirementKind requirement == Behavior.ReplayTransition] `shouldBe` 1
+      [Behavior.requirementSource requirement | requirement <- requirements, Behavior.requirementKind requirement == Behavior.RequiredRejection]
+        `shouldContain` ["Active", "Closed"]
+      length [() | requirement <- requirements, Behavior.requirementGuardCoverage requirement == Behavior.GuardTotal] `shouldBe` 4
+      length [() | requirement <- requirements, Behavior.requirementGuardCoverage requirement == Behavior.GuardUnknown] `shouldBe` 1
+      let report = Behavior.BehaviorObligationsReport "behavior-complete.keiro" Nothing requirements
+          encoded = Behavior.encodeBehaviorObligationsJson report
+      encoded `shouldSatisfy` T.isInfixOf "\"schema\":\"keiro-dsl/behavior-obligations/1\""
+      encoded `shouldSatisfy` T.isInfixOf "\"source\":\"Closed\""
+      encoded `shouldSatisfy` T.isInfixOf "\"kind\":\"replay-transition\""
+      encoded `shouldSatisfy` (not . T.isInfixOf "\"filled\"")
+      encoded `shouldSatisfy` (not . T.isInfixOf "\"missing\"")
+
+    it "keeps semantic keys stable across line movement and canonical pretty printing" $ do
+      source <- readTestText "test/fixtures/behavior-complete.keiro"
+      parsed <- case parseSource "behavior-complete.keiro" source of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let original = parsedSpec parsed
+      moved <- parseInlineSpec "behavior-complete-moved.keiro" ("# line movement must not rename witnesses\n\n" <> source)
+      pretty <- parseInlineSpec "behavior-complete-pretty.keiro" (renderSource parsed)
+      let keys spec = fmap (map Behavior.requirementKey) (Behavior.deriveBehaviorRequirements spec)
+      keys moved `shouldBe` keys original
+      keys pretty `shouldBe` keys original
+
+    it "generates direct fields(Command) output and separate create-once pending witnesses" $ do
+      spec <- specOf "test/fixtures/behavior-complete.keiro"
+      aggregate <- case [value | NAggregate value <- specNodes spec] of
+        [value] -> pure value
+        _ -> expectationFailure "expected one behavior-complete aggregate" >> fail "unreachable"
+      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
+          transducer = generatedTextEndingIn "Transducer.hs" modules
+          codec = generatedTextEndingIn "Codec.hs" modules
+          contract = generatedTextEndingIn "BehaviorContract.hs" modules
+          behaviorHoles = case [moduleText value | value <- modules, T.isSuffixOf "BehaviorHoles.hs" (T.pack (modulePath value))] of
+            [value] -> value
+            values -> error ("expected one BehaviorHoles module, got " <> show (length values))
+          ordinaryHoles = [value | value <- modules, T.isSuffixOf "/Holes.hs" (T.pack (modulePath value)), not (T.isSuffixOf "BehaviorHoles.hs" (T.pack (modulePath value)))]
+      transducer `shouldSatisfy` T.isInfixOf "requestId = d.requestId"
+      transducer `shouldSatisfy` T.isInfixOf "observedAt = d.observedAt"
+      transducer `shouldSatisfy` T.isInfixOf "amount = d.amount"
+      transducer `shouldSatisfy` T.isInfixOf "details = d.details"
+      codec `shouldSatisfy` T.isInfixOf "display_label"
+      codec `shouldSatisfy` T.isInfixOf "optional_note"
+      transducer `shouldSatisfy` (not . T.isInfixOf "Output")
+      ordinaryHoles `shouldBe` []
+      obsoleteGeneratedOutputHooks spec `shouldContain` [("Journey", "transition1EmptyStartOutput1Started")]
+      contract `shouldSatisfy` T.isInfixOf "keiro/behavior-conformance/1"
+      contract `shouldSatisfy` T.isInfixOf "commandKind command == requirementCommandName requirement"
+      T.count "Pending (BehaviorKey " behaviorHoles `shouldBe` 14
+      behaviorHoles `shouldSatisfy` (not . T.isInfixOf "undefined")
+      behaviorHoles `shouldSatisfy` (not . T.isInfixOf "error")
+
+    it "rejects eventless state or register changes while accepting a true no-op" $ do
+      invalid <-
+        parseInlineSpec "<eventless-change>" $
+          T.unlines
+            [ "language keiro-dsl 2",
+              "context eventless-change",
+              "aggregate Counter",
+              "  regs",
+              "    count Natural = 0",
+              "  states Open Closed!",
+              "  command Tick { count:Natural }",
+              "  Open -- Tick --> write count := cmd.count ; goto Closed"
+            ]
+      errorCodes invalid `shouldContain` [AggregateEventlessStateChange]
+      valid <-
+        parseInlineSpec "<eventless-noop>" $
+          T.unlines
+            [ "language keiro-dsl 2",
+              "context eventless-noop",
+              "aggregate Counter",
+              "  regs",
+              "    count Natural = 0",
+              "  states Open",
+              "  command Tick { count:Natural }",
+              "  Open -- Tick --> goto Open"
+            ]
+      errorCodes valid `shouldBe` []
+
+    it "refuses duplicate semantic behavior identities before scaffolding" $ do
+      duplicate <-
+        parseInlineSpec "<duplicate-behavior>" $
+          T.unlines
+            [ "language keiro-dsl 2",
+              "context duplicate-behavior",
+              "aggregate Counter",
+              "  regs",
+              "  states Open",
+              "  command Tick { amount:Natural }",
+              "  event Ticked = fields(Tick)",
+              "  Open -- Tick --> emit Ticked ; goto Open",
+              "  Open -- Tick --> emit Ticked ; goto Open"
+            ]
+      let isBehaviorRefusal (BehaviorRefusal _) = True
+          isBehaviorRefusal _ = False
+      case planScaffold (defaultContext (specContext duplicate)) duplicate of
+        Left refusals -> refusals `shouldSatisfy` any isBehaviorRefusal
+        Right _ -> expectationFailure "duplicate behavior identity reached a scaffold write set"
+
+    it "round-trips additive single-file and workspace behavior rows with member ownership" $ do
+      spec <- specOf "test/fixtures/behavior-complete.keiro"
+      requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
+      version <- maybe (expectationFailure "language version 2 was not constructible" >> fail "unreachable") pure (languageVersion 2)
+      let rows = Behavior.behaviorRecordRows requirements
+          singleRecord =
+            ScaffoldRecord
+              { recSpecPath = "behavior-complete.keiro",
+                recModuleRoot = "",
+                recLayout = "prefixed",
+                recSourceLanguage = DeclaredLanguage version noLoc,
+                recLanguageContract = effectiveLanguageContract (DeclaredLanguage version noLoc),
+                recFiles = [],
+                recMappings = [],
+                recIdDomains = [],
+                recNominalEqualities = [],
+                recBindingObligations = [],
+                recBehaviorRequirements = rows
+              }
+      T.count "behavior " (renderRecord singleRecord) `shouldBe` 14
+      parseRecord (renderRecord singleRecord) `shouldBe` Just singleRecord
+
+      workspace <- shouldComposeWorkspace "test/fixtures/behavior-complete-workspace/service.keiro-workspace"
+      workspaceRequirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements (wsMergedSpec workspace))
+      let ownedRequirements =
+            map
+              (Behavior.attributeBehaviorOwner (fmap fst . nodeOwner (wsOwnership workspace) "aggregate"))
+              workspaceRequirements
+          ownedRows = Behavior.behaviorRecordRows ownedRequirements
+          workspaceRecord =
+            WorkspaceRecord
+              { wrService = wsService workspace,
+                wrManifest = "service.keiro-workspace",
+                wrContext = wsContext workspace,
+                wrModuleRoot = "",
+                wrLayout = "prefixed",
+                wrMembers = map wmPath (wsMembers workspace),
+                wrSourceLanguages = [WorkspaceSourceLanguageRow (wmPath member) (wmSourceLanguage member) | member <- wsMembers workspace],
+                wrLanguageContract = wsLanguageContract workspace,
+                wrModules = [],
+                wrMappings = [],
+                wrIdDomains = [],
+                wrNominalEqualities = [],
+                wrBindingObligations = [],
+                wrBehaviorRequirements = ownedRows,
+                wrAdopted = []
+              }
+      map Behavior.behaviorRecordOwner ownedRows `shouldSatisfy` all (== Just "journey.keiro")
+      T.count "behavior " (renderWorkspaceRecord workspaceRecord) `shouldBe` 14
+      parseWorkspaceRecord (renderWorkspaceRecord workspaceRecord) `shouldBe` Just workspaceRecord
+
+  describe "nominal consumer types" $ do
+    it "resolves every category through one checked registry and explains exact obligations" $ do
+      spec <- specOf "test/fixtures/nominal-scalars.keiro"
+      errorCodes spec `shouldBe` []
+      registry <- case resolveNominalTypes spec of
+        Left errors -> expectationFailure (show errors) >> fail "unreachable"
+        Right value -> pure value
+      Map.keys (nominalTypes registry)
+        `shouldBe` ["AccountNumber", "FeatureFlag", "ObservedAt", "OrderId", "OrderStatus", "RiskScore", "SequenceNumber"]
+      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
+      length obligations `shouldBe` 21
+      map obligationCategory obligations `shouldSatisfy` all (`elem` ["nominal-id", "nominal-enum", "nominal-scalar"])
+      length [() | obligation <- obligations, obligationEqualityContract obligation /= Nothing] `shouldBe` 2
+      renderBindingObligations (specContext spec) obligations `shouldSatisfy` T.isInfixOf "equality-contract:"
+      let signatures = map obligationSignature obligations
+      forM_
+        [ "orderIdBinding :: NominalBinding NominalConformance.Domain.OrderId (KindID \"ord\")",
+          "orderStatusBinding :: NominalBinding NominalConformance.Domain.OrderStatus Generated.NominalScalars.Nominal.Shape.OrderStatus.OrderStatusRepresentation",
+          "accountNumberBinding :: NominalBinding NominalConformance.Domain.AccountNumber Text",
+          "orderIdFixtures :: NominalFixtureCases NominalConformance.Domain.OrderId",
+          "initialAccountNumber :: NominalConformance.Domain.AccountNumber"
+        ]
+        (`shouldSatisfy` (`elem` signatures))
+      map obligationCanonicalType obligations `shouldSatisfy` all (/= Nothing)
+      let rendered = renderBindingObligations (specContext spec) obligations
+      rendered `shouldSatisfy` T.isInfixOf "nominal-id type OrderId"
+      rendered `shouldSatisfy` T.isInfixOf "canonical-type: \"nominal.OrderId.v1\""
+      case obligations of
+        firstObligation : _ ->
+          (Aeson.eitherDecode (Aeson.encode firstObligation) :: Either String BindingObligation)
+            `shouldBe` Right firstObligation
+        [] -> expectationFailure "expected nominal binding obligations"
+
+    it "allocates distinct stable diagnostics for incomplete or incompatible nominal declarations" $ do
+      missing <- errorCodesOf "test/fixtures/nominal-missing-facts.keiro"
+      missing `shouldBe` replicate 5 NominalMissingIngredient
+      errorCodesOf "test/fixtures/nominal-bad-qualified.keiro" `shouldReturn` [NominalInvalidQualifiedName]
+      errorCodesOf "test/fixtures/nominal-invalid-prefix.keiro" `shouldReturn` replicate 2 NominalInvalidIdPrefix
+      errorCodesOf "test/fixtures/nominal-unsupported-representation.keiro" `shouldReturn` [NominalUnsupportedRepresentation]
+      errorCodesOf "test/fixtures/nominal-missing-initial.keiro" `shouldReturn` [NominalMissingInitialValue]
+      errorCodesOf "test/fixtures/nominal-name-collision.keiro" `shouldReturn` [NominalNameCollision, NominalNameCollision]
+
+    it "keeps v1 rejection at the source-language boundary" $ do
+      source <- readTestText "test/fixtures/nominal-v1.keiro"
+      case parseSource "nominal-v1.keiro" source of
+        Left (SourceLanguageFailure diagnostic) -> sourceLanguageErrorCode diagnostic `shouldBe` LanguageFeatureRequiresVersion
+        other -> expectationFailure ("expected source-language refusal, got " <> show other)
+
+    it "scaffolds consumer types, checked codecs, enum representation, projections, and deterministic manifests" $ do
+      spec <- specOf "test/fixtures/nominal-scalars.keiro"
+      let ctx = defaultContext (specContext spec)
+          modules = scaffoldModules ctx spec
+          moduleAt path = case [value | value <- modules, modulePath value == path] of
+            [value] -> pure value
+            values -> expectationFailure ("expected one module at " <> path <> ", got " <> show (map modulePath values)) >> fail "unreachable"
+      domainModule <- moduleAt "Generated/NominalScalars/NominalLedger/Domain.hs"
+      codecModule <- moduleAt "Generated/NominalScalars/NominalLedger/Codec.hs"
+      enumModule <- moduleAt "Generated/NominalScalars/Nominal/Shape/OrderStatus.hs"
+      projectionModule <- moduleAt "Generated/NominalScalars/NominalProjections.hs"
+      bindingModule <- moduleAt "NominalConformance/Bindings.hs"
+      map modulePath modules `shouldNotContain` ["NominalScalars/NominalLedger/Holes.hs"]
+      moduleText domainModule `shouldSatisfy` T.isInfixOf "NominalConformance.Domain.OrderId"
+      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "newtype OrderId")
+      moduleText domainModule `shouldSatisfy` (not . T.isInfixOf "data OrderStatus =")
+      moduleText codecModule `shouldSatisfy` T.isInfixOf "KindID.parseText @\"ord\""
+      moduleText codecModule `shouldSatisfy` T.isInfixOf "KindID.toText (nominalToRepresentation"
+      moduleText codecModule `shouldSatisfy` T.isInfixOf "nominalFromRepresentation"
+      forM_ ["coerce", "unsafe", "read ", "error "] $ \forbidden ->
+        moduleText codecModule `shouldSatisfy` (not . T.isInfixOf forbidden)
+      moduleText enumModule `shouldSatisfy` T.isInfixOf "data OrderStatusRepresentation = Draft | Submitted"
+      moduleText enumModule `shouldSatisfy` (not . T.isInfixOf "NominalConformance")
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "type FieldOwner AccountNumberNominalProjection = NominalConformance.Domain.AccountNumber"
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "projectFieldValue _ = nominalToRepresentation NominalConformance.Bindings.accountNumberBinding"
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection OrderIdEqualityProjection"
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "textProjectionDomain orderIdEqualityPattern"
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "instance ExactFieldProjection OrderStatusEqualityProjection"
+      moduleText projectionModule `shouldSatisfy` T.isInfixOf "finiteProjectionDomain (\"draft\" :| [\"submitted\"])"
+      kind bindingModule `shouldBe` HoleStub
+      moduleText bindingModule `shouldSatisfy` T.isInfixOf "orderIdBinding :: NominalBinding NominalConformance.Domain.OrderId (KindID \"ord\")"
+      firewallBreaches modules `shouldBe` []
+      scaffoldModules ctx spec `shouldBe` modules
+      manifestDependencies spec `shouldContain` ["mmzk-typeid", "nominal-conformance"]
+
+    it "persists nominal provenance in a separate forward-compatible row kind" $ do
+      spec <- specOf "test/fixtures/nominal-scalars.keiro"
+      workspace <- shouldComposeWorkspace canonicalWorkspacePath
+      let plan = consumerPlan spec
+          record =
+            ScaffoldRecord
+              { recSpecPath = "nominal-scalars.keiro",
+                recModuleRoot = "",
+                recLayout = "prefixed",
+                recSourceLanguage = LegacyUnversioned,
+                recLanguageContract = effectiveLanguageContract LegacyUnversioned,
+                recFiles = [],
+                recMappings = consumerMappings plan,
+                recIdDomains = [],
+                recNominalEqualities = nominalEqualityIdentities spec,
+                recBindingObligations = [],
+                recBehaviorRequirements = []
+              }
+          encoded = renderRecord record
+          workspaceRecord =
+            (sampleWorkspaceRecord workspace)
+              { wrMappings = consumerMappings plan
+              }
+          workspaceEncoded = renderWorkspaceRecord workspaceRecord
+      consumerPackages plan `shouldBe` ["nominal-conformance"]
+      length [() | NominalMapping {} <- consumerMappings plan] `shouldBe` 7
+      T.count "nominal-mapping " encoded `shouldBe` 7
+      T.count "nominal-equality " encoded `shouldBe` 2
+      T.count "\nmapping " encoded `shouldBe` 0
+      parseRecord encoded `shouldBe` Just record
+      T.count "nominal-mapping " workspaceEncoded `shouldBe` 7
+      T.count "nominal-equality " workspaceEncoded `shouldSatisfy` (>= 2)
+      T.count "\nmapping " workspaceEncoded `shouldBe` 0
+      parseWorkspaceRecord workspaceEncoded `shouldBe` Just workspaceRecord
+
+    it "reports bound-ID decoder tightening and makes binding provenance replay-visible" $ do
+      current <- specOf "test/fixtures/nominal-scalars.keiro"
+      let useGeneratedIdInitial (NAggregate aggregate) =
+            NAggregate
+              aggregate
+                { aggRegs =
+                    [ if regName register == "orderId"
+                        then register {regInitial = RegInitBare "placeholder"}
+                        else register
+                    | register <- aggRegs aggregate
+                    ]
+                }
+          useGeneratedIdInitial node = node
+          unbound =
+            current
+              { specIds = [declaration {idBinding = Nothing} | declaration <- specIds current],
+                specNodes = map useGeneratedIdInitial (specNodes current)
+              }
+          adoption = diffSpecs unbound current
+          decoderFindings = [kindOfChange change | change <- adoption, changeCode change == NominalIdDecoderTightened]
+      map ckSubject decoderFindings `shouldContain` ["NominalLedger event NominalsRecorded .orderId"]
+      decoderFindings `shouldSatisfy` all ((== VAdvisory) . verdictFor PrivateHistoryRead . ckVector)
+      let bumped =
+            current
+              { specIds =
+                  [ declaration
+                      { idBinding = fmap (\binding -> binding {nominalBindingVersion = Just "2"}) (idBinding declaration)
+                      }
+                  | declaration <- specIds current
+                  ]
+              }
+          bindingChanges = diffSpecs current bumped
+      map changeCode bindingChanges `shouldContain` [NominalBindingChanged]
+      replayImpactSpecs current bumped `shouldSatisfy` \case
+        ReplayImpact.ReplayAffected impacts ->
+          maybe False (\impact -> Set.member "NominalsRecorded" (ReplayImpact.eventTypes impact) && includeSnapshotStreams impact) (Map.lookup "NominalLedger" impacts)
+        ReplayImpact.ReplayNeutral -> False
+      case [aggregate | NAggregate aggregate <- specNodes current] of
+        aggregate : _ -> do
+          aggregateFoldSurface current aggregate `shouldSatisfy` T.isInfixOf "nominal-equality-use:"
+          aggregateFoldSurface current aggregate `shouldNotBe` aggregateFoldSurface bumped aggregate
+        [] -> expectationFailure "expected nominal aggregate"
+
+  describe "historical codec comparison" $ do
+    it "treats object-key order as RFC 8785 parity" $ do
+      let historical = object ["z" .= (1 :: Int), "a" .= (2 :: Int)]
+          generated = object ["a" .= (2 :: Int), "z" .= (1 :: Int)]
+      classifyObservation (EncodeObservation "ordered-object" historical generated)
+        `shouldBe` Right JsonParity
+    it "classifies an omitted key versus explicit null as version work at that pointer" $ do
+      let historical = object []
+          generated = object ["description" .= Aeson.Null]
+      classifyObservation (EncodeObservation "absent-description" historical generated)
+        `shouldBe` Right (RequiresVersionWork (EncodedValueDifference (JsonPointer "/description") historical generated))
+    it "classifies generated rejection of a historical value as version work" $
+      classifyObservation
+        ( DecodeObservation
+            "legacy.json"
+            (object ["tag" .= ("legacy" :: T.Text)])
+            (DecodedShape (object ["tag" .= ("legacy" :: T.Text)]))
+            (DecodeFailed "unknown tag")
+        )
+        `shouldBe` Right (RequiresVersionWork (GeneratedDecodeRejected "unknown tag"))
+    it "treats historical-codec rejection as invalid input rather than parity" $
+      classifyObservation
+        ( DecodeObservation
+            "corrupt.json"
+            Aeson.Null
+            (DecodeFailed "not historical data")
+            (DecodeFailed "not generated data")
+        )
+        `shouldBe` Left (HistoricalCodecRejected "corrupt.json" "not historical data")
+    it "reports uncovered union arms separately by corpus origin" $ do
+      let canonical = DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
+          local = DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local_file")
+          report = compareReport comparisonProvenance [] [] [canonical, local] [ObservedBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local_file")]
+      crCoverageGaps report
+        `shouldBe` [CoverageGap HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")]
+      reportSucceeded report `shouldBe` False
+    it "derives optional, null, and union-arm observations from a generated branch schema" $ do
+      let schema =
+            BranchRecord
+              [ BranchField "description" True (BranchOptional BranchScalar),
+                BranchField "location" False (BranchUnion "tag" "contents" [BranchArm "local" (Just BranchScalar), BranchArm "canonical" Nothing])
+              ]
+          historical = object ["location" .= object ["tag" .= ("canonical" :: T.Text)]]
+      observedBranchesFor HistoricalGolden schema historical
+        `shouldBe` [ ObservedBranch HistoricalGolden (JsonPointer "/description") OptionalMissing,
+                     ObservedBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
+                   ]
+      let declared = declaredBranchesFor HistoricalGolden schema
+      forM_
+        [ DeclaredBranch HistoricalGolden (JsonPointer "/description") OptionalMissing,
+          DeclaredBranch HistoricalGolden (JsonPointer "/description") OptionalPresent,
+          DeclaredBranch HistoricalGolden (JsonPointer "/description") ExplicitNull,
+          DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "local"),
+          DeclaredBranch HistoricalGolden (JsonPointer "/location") (UnionArm "canonical")
+        ]
+        (\branch -> declared `shouldContain` [branch])
+    it "round-trips the stable machine report" $ do
+      let observation = EncodeObservation "parity" (object ["a" .= (1 :: Int)]) (object ["a" .= (1 :: Int)])
+          report = compareReport comparisonProvenance [] [observation] [] []
+      Aeson.eitherDecode (Aeson.encode report) `shouldBe` Right report
+    it "atomically writes and replaces the machine report" $
+      withTempDirectory "keiro-dsl-codec-compare" $ \out -> do
+        let path = out </> "report.json"
+            firstReport = compareReport comparisonProvenance [] [] [] []
+            secondReport = compareReport comparisonProvenance [HistoricalGoldenUnreadable "bad.json" "bad JSON"] [] [] []
+        writeCompareReportAtomic path firstReport `shouldReturn` Right ()
+        Aeson.eitherDecodeFileStrict path `shouldReturn` Right firstReport
+        writeCompareReportAtomic path secondReport `shouldReturn` Right ()
+        Aeson.eitherDecodeFileStrict path `shouldReturn` Right secondReport
+
+  describe "historical codec comparison scaffold" $ do
+    it "emits an opt-in non-production runner without entering the ordinary module registry" $ do
+      spec <- specOf "test/fixtures/structural-conformance.keiro"
+      let ctx = defaultContext (specContext spec)
+          planned = codecComparisonModule ctx spec "ArtifactInfo"
+          ordinary = scaffoldModules ctx spec
+      case planned of
+        Left err -> expectationFailure (T.unpack err)
+        Right comparisonModule -> do
+          modulePath comparisonModule
+            `shouldBe` "Generated/StructuralConformance/Structural/CodecCompare/ArtifactInfo.hs"
+          moduleText comparisonModule `shouldSatisfy` T.isInfixOf codecComparisonBanner
+          moduleText comparisonModule `shouldSatisfy` T.isInfixOf "Generated.StructuralConformance.ArtifactCatalog.Codec qualified as GeneratedCodec"
+          moduleText comparisonModule `shouldSatisfy` T.isInfixOf "branchSchema = BranchRecord"
+          map modulePath ordinary `shouldNotContain` [modulePath comparisonModule]
+    it "refuses opaque selections rather than upgrading their claim" $ do
+      spec <- specOf "test/fixtures/structural-conformance.keiro"
+      codecComparisonModule (defaultContext (specContext spec)) spec "VendorGeometry"
+        `shouldSatisfy` either (T.isInfixOf "is opaque") (const False)
+
+  describe "structural/opaque coverage reporting" $ do
+    it "reports mapped private-event roots and consumer-json register boundaries without a percentage" $ do
+      spec <- specOf "test/fixtures/structural-conformance.keiro"
+      report <- shouldResolveCoverage "structural-conformance.keiro" spec
+      Coverage.privateEventPayloads (Coverage.coverageSummary report)
+        `shouldBe` Coverage.CoverageCounts 2 1 1 0
+      Coverage.snapshotRegisters (Coverage.coverageSummary report)
+        `shouldBe` Coverage.CoverageCounts 2 1 1 0
+      map Coverage.opaqueMappedType (Coverage.coverageOpaqueBoundaries report)
+        `shouldBe` ["VendorGeometry"]
+      map Coverage.snapshotEncoding (Coverage.coverageSnapshotBoundaries report)
+        `shouldBe` ["consumer-json-cache", "consumer-json-cache"]
+      map Coverage.snapshotInvalidation (Coverage.coverageSnapshotBoundaries report)
+        `shouldBe` ["tracked-by-mapped-wire-fingerprint", "tracked-by-mapped-wire-fingerprint"]
+      map Coverage.findingCode (Coverage.coverageFindings report)
+        `shouldBe` [CoverageOpaqueSurface]
+      map Coverage.findingSeverity (Coverage.coverageFindings report)
+        `shouldBe` [Warning]
+      case Aeson.toJSON report of
+        Aeson.Object values ->
+          forM_ ["spec", "roots", "opaqueBoundaries", "snapshotBoundaries", "unsupportedSurfaces"] $
+            \key -> KeyMap.member key values `shouldBe` True
+        value -> expectationFailure ("coverage report was not an object: " <> show value)
+    it "reports explicit Json leaves by their complete persisted path" $ do
+      spec <- withMetadataJson <$> specOf "test/fixtures/structural-conformance.keiro"
+      report <- shouldResolveCoverage "structural-conformance-json.keiro" spec
+      Coverage.jsonBoundaries (Coverage.privateEventPayloads (Coverage.coverageSummary report))
+        `shouldBe` 1
+      map Coverage.jsonPath (Coverage.coverageJsonBoundaries report)
+        `shouldBe` ["ArtifactCatalog event ArtifactRecorded .artifact : ArtifactInfo .metadata : ArtifactMetadata .note"]
+    it "keeps a zero-opaque spec advisory-free and makes rejection explicitly opt-in" $ do
+      original <- specOf "test/fixtures/structural-conformance.keiro"
+      clear <- shouldResolveCoverage "structural-only.keiro" (withoutVendorGeometry original)
+      Coverage.opaqueRoots (Coverage.privateEventPayloads (Coverage.coverageSummary clear)) `shouldBe` 0
+      Coverage.coverageOpaqueBoundaries clear `shouldBe` []
+      Coverage.coverageFindings clear `shouldBe` []
+      opaque <- shouldResolveCoverage "structural-conformance.keiro" original
+      Coverage.coverageSucceeded opaque `shouldBe` True
+      let gated = Coverage.failOnOpaque opaque
+      Coverage.coverageSucceeded gated `shouldBe` False
+      map Coverage.findingCode (Coverage.coverageFindings gated)
+        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueGateExceeded]
+      map Coverage.findingSeverity (Coverage.coverageFindings gated)
+        `shouldBe` [Warning, Error]
+    it "diffs named opaque boundaries and fails only an explicitly gated increase" $ do
+      newSpec <- specOf "test/fixtures/structural-conformance.keiro"
+      report <- case Coverage.coverageDiffReport "structural-conformance.keiro" "HEAD" (withoutVendorGeometry newSpec) newSpec of
+        Left err -> expectationFailure (show err) >> fail "unreachable"
+        Right value -> pure value
+      fmap Coverage.opaqueBoundaryDelta (Coverage.coverageDelta report) `shouldBe` Just 1
+      fmap (map Coverage.opaqueMappedType . Coverage.addedOpaqueBoundaries) (Coverage.coverageDelta report)
+        `shouldBe` Just ["VendorGeometry"]
+      map Coverage.findingCode (Coverage.coverageFindings report)
+        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueBoundaryAdded]
+      Coverage.coverageSucceeded report `shouldBe` True
+      let gated = Coverage.failOnOpaqueIncrease report
+      Coverage.coverageSucceeded gated `shouldBe` False
+      map Coverage.findingCode (Coverage.coverageFindings gated)
+        `shouldBe` [CoverageOpaqueSurface, CoverageOpaqueBoundaryAdded, CoverageOpaqueGateExceeded]
+    it "appends the six stable coverage and comparison registry codes" $
+      map
+        show
+        [ CoverageOpaqueSurface,
+          CoverageOpaqueBoundaryAdded,
+          CoverageOpaqueGateExceeded,
+          CodecCompareDifference,
+          CodecCompareCoverageGap,
+          CodecCompareInvalidInput
+        ]
+        `shouldBe` [ "CoverageOpaqueSurface",
+                     "CoverageOpaqueBoundaryAdded",
+                     "CoverageOpaqueGateExceeded",
+                     "CodecCompareDifference",
+                     "CodecCompareCoverageGap",
+                     "CodecCompareInvalidInput"
+                   ]
+
+  describe "parse . pretty round-trip" $
+    do
+      it "re-parses any generated spec to an equal AST (modulo source locations)" $
+        checkCoverage $
+          forAll genSpec $ \s ->
+            let families = map nodeTag (specNodes s)
+                roundTrip = parseSpec "<gen>" (renderSpec s) === Right s
+             in cover 5 (not (null (specMapped s))) "mapped" $
+                  foldr (\family -> cover 1 (family `elem` families) family) roundTrip allNodeTags
+      it "round-trips an aggregate with no states" $
+        parseSpec "<empty-states>" (renderSpec emptyStatesSpec) `shouldBe` Right emptyStatesSpec
+      it "separates transition emit clauses from following nodes" $ do
+        spec <- parseInlineSpec "<cross-family-boundaries>" crossFamilyBoundarySpec
+        case specNodes spec of
+          [NAggregate first, NEmit _, NAggregate second, NPgmqDispatch _] -> do
+            concatMap tEmits (aggTransitions first) `shouldBe` ["Changed"]
+            aggStates second `shouldBe` []
+          nodes -> expectationFailure ("unexpected node sequence: " <> show (map nodeTag nodes))
+
+  describe "mapped types (EP-149)" $ do
+    it "round-trips the canonical structural and opaque consumer fixture" $ do
+      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
+      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
+      parseStableRenderedSpec "<consumer-types-round-trip>" spec `shouldBe` Right spec
+      length (specMapped spec) `shouldBe` 4
+    it "preserves every missing-value policy, nested type expression, and unit union arm" $ do
+      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
+      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
+      let fields = [field | MappedStructural {msShape = ShapeRecord _ _ recordFields} <- specMapped spec, field <- recordFields]
+          arms = [arm | MappedStructural {msShape = ShapeUnion _ unionArms} <- specMapped spec, arm <- unionArms]
+      [value | field <- fields, Just value <- [wfOnMissing field]]
+        `shouldBe` [OmCtor "Guide", OmNull, OmInt 0, OmBool False, OmEmptyList, OmEmptyMap]
+      [wfType field | field <- fields, wfHaskell field == "labels"]
+        `shouldBe` [TList (TOptional TText)]
+      [waCtor arm | arm <- arms, waPayload arm == Nothing]
+        `shouldBe` ["Unknown"]
+    it "rejects every mapped validation fixture with its stable diagnostic code" $ do
+      let cases =
+            [ ("mapped-unresolved.keiro", MappedUnresolvedName),
+              ("mapped-ambiguous.keiro", MappedAmbiguousName),
+              ("mapped-dup-fieldname.keiro", MappedDuplicateFieldName),
+              ("mapped-dup-wirekey.keiro", MappedDuplicateWireKey),
+              ("mapped-dup-armname.keiro", MappedDuplicateArmName),
+              ("mapped-dup-tag.keiro", MappedDuplicateWireTag),
+              ("mapped-recursive.keiro", MappedRecursiveType),
+              ("mapped-recursive-mutual.keiro", MappedRecursiveType),
+              ("mapped-bad-encoding.keiro", MappedUnsupportedEncoding),
+              ("mapped-union-key-collision.keiro", MappedUnsupportedEncoding),
+              ("mapped-optional-json.keiro", MappedNonInjectiveNullability),
+              ("mapped-optional-optional.keiro", MappedNonInjectiveNullability),
+              ("mapped-optional-opaque.keiro", MappedNonInjectiveNullability),
+              ("mapped-missing-binding.keiro", MappedMissingIngredient),
+              ("mapped-missing-binding-version.keiro", MappedMissingIngredient),
+              ("mapped-missing-canonical.keiro", MappedMissingIngredient),
+              ("mapped-missing-fixture.keiro", MappedMissingIngredient),
+              ("mapped-missing-initial.keiro", MappedMissingInitialValue),
+              ("mapped-bad-haskell-name.keiro", MappedInvalidHaskellName),
+              ("mapped-empty-identity.keiro", MappedInvalidIdentity),
+              ("mapped-import-conflict.keiro", MappedImportConflict),
+              ("mapped-illtyped-default.keiro", MappedDefaultIllTyped),
+              ("mapped-guard.keiro", AggregateExpressionOperatorUnsupported)
+            ]
+      forM_ cases $ \(fixture, expected) ->
+        errorCodesOf ("test/fixtures/" <> fixture) `shouldReturn` [expected]
+    it "keeps Time and Natural in Keiki's curated comparison set" $ do
+      errorCodesOf "test/fixtures/mapped-guard-time.keiro" `shouldReturn` []
+      errorCodesOf "test/fixtures/mapped-guard-natural.keiro" `shouldReturn` []
+    it "rejects required defaults, missing optional policies, Int overflow, and negative Natural defaults" $ do
+      let invalidFields =
+            [ WireField "requiredDefault" "requiredDefault" TText PRequired (Just (OmText "x")) noLoc,
+              WireField "missingPolicy" "missingPolicy" TText POptional Nothing noLoc,
+              WireField "overflow" "overflow" TInt POptional (Just (OmInt (toInteger (maxBound :: Int) + 1))) noLoc,
+              WireField "negativeNatural" "negativeNatural" TNatural POptional (Just (OmInt (-1))) noLoc
+            ]
+          declaration = completeStructural "Defaults" (ShapeRecord "Defaults" RejectUnknown invalidFields)
+      errorCodes (mappedSpec [declaration])
+        `shouldBe` [MappedDefaultIllTyped, MappedMissingIngredient, MappedDefaultIllTyped, MappedDefaultIllTyped]
+
+  describe "aggregate type capabilities" $ do
+    it "enumerates the policy for every resolved type and use site" $ do
+      let resolvedTypes =
+            [ AggregateText,
+              AggregateInt,
+              AggregateBool,
+              AggregateTime,
+              AggregateNatural,
+              AggregateNominal (ResolvedNominalType "EntityId" (IdRepresentation "ent") GeneratedNominal noLoc),
+              AggregateNominal (ResolvedNominalType "Status" (EnumRepresentation (("Active", "active") :| [])) GeneratedNominal noLoc),
+              AggregateNominal (ResolvedNominalType "Amount" (ScalarRepresentation NominalInt) (consumerNominalFor "Amount") noLoc),
+              AggregateNominal (ResolvedNominalType "Label" (ScalarRepresentation NominalText) (consumerNominalFor "Label") noLoc),
+              AggregateVertex "EntityVertex",
+              AggregateMapped (MappedKey "ConsumerValue")
+            ]
+          useSites = [minBound .. maxBound]
+          expected useSite resolvedType = case useSite of
+            OrderingGuardUse -> case resolvedType of
+              AggregateInt -> SolverVisible
+              AggregateTime -> SolverVisible
+              AggregateNatural -> SolverVisible
+              AggregateNominal nominal -> case resolvedNominalRepresentation nominal of
+                ScalarRepresentation NominalInt -> SolverVisible
+                ScalarRepresentation NominalNatural -> SolverVisible
+                ScalarRepresentation NominalTime -> SolverVisible
+                _ -> Unsupported
+              _ -> Unsupported
+            EqualityGuardUse -> case resolvedType of
+              AggregateMapped {} -> Unsupported
+              AggregateNominal {} -> SolverVisible
+              AggregateVertex {} -> OpaqueOnly
+              _ -> SolverVisible
+            _ -> case resolvedType of
+              AggregateNominal nominal -> case resolvedNominalRepresentation nominal of
+                ScalarRepresentation {} -> SolverVisible
+                _ -> OpaqueOnly
+              AggregateVertex {} -> OpaqueOnly
+              AggregateMapped {} -> OpaqueOnly
+              _ -> SolverVisible
+          actual =
+            [ (useSite, resolvedType, aggregateCapability useSite resolvedType)
+            | useSite <- useSites,
+              resolvedType <- resolvedTypes
+            ]
+          wanted =
+            [ (useSite, resolvedType, expected useSite resolvedType)
+            | useSite <- useSites,
+              resolvedType <- resolvedTypes
+            ]
+      actual `shouldBe` wanted
+    it "lowers direct Time and Natural through every generated aggregate boundary" $ do
+      spec <- specOf "test/fixtures/aggregate-scalars.keiro"
+      errorCodes spec `shouldBe` []
+      let aggregate = onlyAggregate spec
+          generated =
+            [ moduleText generatedModule
+            | generatedModule <- scaffoldAggregate (defaultContext (specContext spec)) spec aggregate,
+              Keiro.Dsl.Scaffold.kind generatedModule == Generated
+            ]
+          domain = generatedTextEndingIn "Domain.hs" (scaffoldAggregate (defaultContext (specContext spec)) spec aggregate)
+      domain `shouldSatisfy` T.isInfixOf "observedAt :: !UTCTime"
+      domain `shouldSatisfy` T.isInfixOf "revision :: !Natural"
+      domain `shouldSatisfy` T.isInfixOf "UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)"
+      domain `shouldSatisfy` T.isInfixOf "import Data.Time.Calendar (fromGregorian)"
+      domain `shouldSatisfy` T.isInfixOf "import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)"
+      domain `shouldSatisfy` T.isInfixOf "import Numeric.Natural (Natural)"
+      manifestDependencies spec `shouldContain` ["time"]
+      manifestDependencies spec `shouldNotContain` ["keiki-codec-json"]
+      generated `shouldSatisfy` all (not . T.isInfixOf "error")
+      generated `shouldSatisfy` all (not . T.isInfixOf "getCurrentTime")
+      generated `shouldSatisfy` all (not . T.isInfixOf "iso8601ParseM")
+    it "canonicalizes Time and UTCTime across pretty, diff, and fold identity" $ do
+      source <- readTestText "test/fixtures/aggregate-scalars.keiro"
+      canonical <- parseInlineSpec "<time>" source
+      alias <- parseInlineSpec "<utctime>" (T.replace ":Time" ":UTCTime" (T.replace " Time =" " UTCTime =" source))
+      renderSpec alias `shouldBe` renderSpec canonical
+      legacyDiffSpecs canonical alias `shouldBe` []
+      legacyAggregateFoldFingerprint canonical (onlyAggregate canonical)
+        `shouldBe` legacyAggregateFoldFingerprint alias (onlyAggregate alias)
+      legacyAggregateFoldSurface canonical (onlyAggregate canonical)
+        `shouldBe` legacyAggregateFoldSurface alias (onlyAggregate alias)
+    it "keeps the committed scalar conformance generated tree fresh" $ do
+      modules <- scaffoldFixture "test/fixtures/aggregate-scalars.keiro"
+      forM_ [generatedModule | generatedModule <- modules, Keiro.Dsl.Scaffold.kind generatedModule == Generated] $ \generatedModule -> do
+        committed <- readTestText ("test/conformance-aggregate-scalars/" <> modulePath generatedModule)
+        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generatedModule)
+    it "never sends a clean scalar aggregate to a type scaffold refusal" $
+      property $
+        forAll (elements scalarRegisterCases) $ \(typeName, initialValue) ->
+          case parseSpec "<clean-scalar>" (cleanScalarAggregateSpec typeName initialValue) of
+            Left parseError -> counterexample (T.unpack parseError) False
+            Right spec ->
+              let diagnostics = [diagnostic | diagnostic <- validateSpec spec, severity diagnostic == Error]
+                  modules = scaffoldModules (defaultContext (specContext spec)) spec
+               in counterexample
+                    (show diagnostics <> "\n" <> show (scaffoldRefusals spec))
+                    ( null diagnostics
+                        && null (scaffoldRefusals spec)
+                        && all (not . T.null . moduleText) modules
+                    )
+
+  describe "aggregate scalar diagnostics" $ do
+    it "reports unsupported shapes, invalid initials, and mismatched guards at stable lines" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/aggregate-scalars-unsupported.keiro"
+      [(code diagnostic, line diagnostic) | diagnostic <- diagnostics, severity diagnostic == Error]
+        `shouldBe` [ (AggregateRegisterInitialInvalid, 6),
+                     (AggregateRegisterInitialInvalid, 7),
+                     (AggregateTypeUnsupportedAtUse, 10),
+                     (AggregateExpressionOperandTypeMismatch, 14)
+                   ]
+      map message diagnostics `shouldSatisfy` any (T.isInfixOf "non-negative integral literals")
+      map message diagnostics `shouldSatisfy` any (T.isInfixOf "ISO-8601 UTC timestamps")
+      map message diagnostics `shouldSatisfy` any (T.isInfixOf "mapped structural declaration")
+    it "accepts Natural aggregate arithmetic in the stable language" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/aggregate-scalars-arithmetic.keiro"
+      [(code diagnostic, line diagnostic) | diagnostic <- diagnostics, severity diagnostic == Error]
+        `shouldBe` []
+    it "covers unknown, container, fractional, out-of-range, and ordering failures" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/aggregate-scalars-invalid-capabilities.keiro"
+      [(code diagnostic, line diagnostic) | diagnostic <- diagnostics, severity diagnostic == Error]
+        `shouldBe` [ (AggregateRegisterInitialInvalid, 6),
+                     (AggregateRegisterInitialInvalid, 7),
+                     (AggregateTypeUnknown, 10),
+                     (AggregateTypeUnsupportedAtUse, 10),
+                     (AggregateTypeUnsupportedAtUse, 10),
+                     (AggregateTypeUnsupportedAtUse, 10),
+                     (AggregateExpressionOperatorUnsupported, 13)
+                   ]
+    it "keeps one-member workspace diagnostics identical to the single file" $ do
+      direct <- diagnosticsOf "test/fixtures/aggregate-scalars-unsupported.keiro"
+      composed <- shouldComposeWorkspace "test/fixtures/aggregate-scalars-workspace/service.keiro-workspace"
+      let directErrors =
+            [(code diagnostic, line diagnostic, message diagnostic) | diagnostic <- direct, severity diagnostic == Error]
+          workspaceErrors =
+            [ (wdCode diagnostic, wlLine (NE.head (wdLocations diagnostic)), wdMessage diagnostic)
+            | diagnostic <- checkWorkspace composed,
+              wdSeverity diagnostic == Error
+            ]
+      workspaceErrors `shouldBe` directErrors
+
+  describe "mapped type graph (EP-149)" $ do
+    it "resolves checked declarations, transitive reachability, and every aggregate root path" $ do
+      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
+      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
+      graph <- shouldResolveTypeGraph spec
+      Map.size (tgDeclarations graph) `shouldBe` 4
+      Map.lookup (MappedKey "ArtifactInfo") (tgReachability graph)
+        `shouldBe` Just (Set.fromList [MappedKey "ArtifactKind", MappedKey "ArtifactLocation"])
+      map renderUsePath (usePaths graph "ArtifactLocation")
+        `shouldBe` [ "Catalog command ObserveArtifact .artifact : ArtifactInfo .location : ArtifactLocation",
+                     "Catalog event ArtifactObserved .artifact : ArtifactInfo .location : ArtifactLocation",
+                     "Catalog register currentArtifact : ArtifactInfo .location : ArtifactLocation"
+                   ]
+    it "resolves every builtin through the complete expression algebra" $ do
+      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
+      spec <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
+      graph <- shouldResolveTypeGraph spec
+      case Map.lookup (MappedKey "ArtifactInfo") (tgDeclarations graph) of
+        Just (ResolvedStructural _ (RRecord _ _ fields)) ->
+          Set.fromList (concatMap (foldTypeExpr expressionTags . rwfType) fields)
+            `shouldBe` Set.fromList ["text", "int", "bool", "natural", "time", "json", "optional", "list", "map", "ref:ArtifactKind", "ref:ArtifactLocation"]
+        declaration -> expectationFailure ("unexpected ArtifactInfo declaration: " <> show declaration)
+    it "rejects direct, mutual, wrapped, and union-arm recursion" $ do
+      let direct = mappedSpec [completeStructural "A" (recordShape [TRef "A"])]
+          mutual = mappedSpec [completeStructural "A" (recordShape [TRef "B"]), completeStructural "B" (recordShape [TRef "A"])]
+          wrapped = mappedSpec [completeStructural "A" (recordShape [TList (TOptional (TRef "A"))])]
+          throughArm = mappedSpec [completeStructural "A" (ShapeUnion (TaggedObject "tag" "contents" RejectUnknown) [WireArm "Again" "again" (Just (TRef "A")) noLoc])]
+      map (hasTypeGraphError isRecursive . resolveTypeGraph) [direct, mutual, wrapped, throughArm]
+        `shouldBe` replicate 4 True
+    it "keeps existing ids and enums outside the mapped-reference namespace" $ do
+      let spec =
+            (mappedSpec [completeStructural "A" (recordShape [TRef "ExistingId"])])
+              { specIds = [IdDecl "ExistingId" "id" Nothing noLoc]
+              }
+      resolveTypeGraph spec `shouldSatisfy` hasTypeGraphError isUnresolved
+    it "fingerprints wire identity while ignoring Haskell selector names" $ do
+      source <- TIO.readFile "test/fixtures/consumer-types.keiro"
+      base <- parseInlineSpec "test/fixtures/consumer-types.keiro" source
+      baseGraph <- shouldResolveTypeGraph base
+      haskellRenameGraph <- shouldResolveTypeGraph (mapArtifactField (\field -> field {wfHaskell = "renamedKey"}) base)
+      wireRenameGraph <- shouldResolveTypeGraph (mapArtifactField (\field -> field {wfKey = "renamed_key"}) base)
+      wireFingerprint haskellRenameGraph "ArtifactInfo" `shouldBe` wireFingerprint baseGraph "ArtifactInfo"
+      wireFingerprint wireRenameGraph "ArtifactInfo" `shouldNotBe` wireFingerprint baseGraph "ArtifactInfo"
+
+  describe "string literal integrity" $ do
+    it "parses an escaped emit-map value as exactly one row" $ do
+      let src =
+            T.unlines
+              [ "context svc",
+                "",
+                "emit e {",
+                "  contract c",
+                "  topic events",
+                "  source \"svc\"",
+                "  key thingId",
+                "  map status {",
+                "    \"a\\\" => Wat \\\"b\" => ThingAccepted",
+                "    _ => skip",
+                "  }",
+                "  messageId derive hole",
+                "  idempotencyKey derive hole",
+                "}"
+              ]
+      case parseSpec "<escaped-map>" src of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> case [row | NEmit e <- specNodes spec, row <- emMap e] of
+          [row] -> do
+            emrValue row `shouldBe` "a\" => Wat \"b"
+            emrEvent row `shouldBe` "ThingAccepted"
+          rows -> expectationFailure ("expected one emit-map row, got " <> show (length rows))
+    it "rejects a raw newline inside a quoted string" $ do
+      let src = "context svc\n\ncontract c {\n  schemaVersion 1\n  discriminator kind\n  topic events \"first\nsecond\"\n}\n"
+      parseSpec "<raw-newline>" src `shouldSatisfy` leftContains "unescaped newline"
+    it "rejects an unknown escape sequence" $ do
+      let src = "context svc\n\ncontract c {\n  schemaVersion 1\n  discriminator kind\n  topic events \"bad\\q\"\n}\n"
+      parseSpec "<unknown-escape>" src `shouldSatisfy` leftContains "unknown escape"
+    it "round-trips adversarial text through topics, emit maps, and quoted bindings" $
+      property $
+        forAll genAdversarialText $ \t ->
+          let spec = escapedSpec t
+              rendered = renderSpec spec
+           in counterexample (T.unpack rendered) (parseSpec "<escaped-round-trip>" rendered === Right spec)
+
+  describe "partial status maps" $ do
+    it "suppresses totality only when the partial marker is present" $ do
+      partial <- parseInlineSpec "<partial-status-map>" (statusMapSpec " partial")
+      totalSpec <- parseInlineSpec "<total-status-map>" (statusMapSpec "")
+      map code (validateSpec partial) `shouldNotContain` [StatusMapNotTotal]
+      map code (validateSpec totalSpec) `shouldContain` [StatusMapNotTotal]
+      parseSpec "<partial-round-trip>" (renderSpec partial) `shouldBe` Right partial
+
+  describe "positioned parser diagnostics" $ do
+    it "rejects a duplicate goto at the second clause" $ do
+      err <- parseErrorOf "<duplicate-goto>" duplicateGotoSpec
+      err `shouldSatisfy` T.isInfixOf "duplicate goto"
+      err `shouldSatisfy` T.isInfixOf "<duplicate-goto>:10:"
+    it "rejects duplicate wire and projection blocks at their second occurrences" $ do
+      wireErr <- parseErrorOf "<duplicate-wire>" duplicateWireSpec
+      wireErr `shouldSatisfy` T.isInfixOf "duplicate wire block"
+      wireErr `shouldSatisfy` T.isInfixOf "<duplicate-wire>:8:"
+      projectionErr <- parseErrorOf "<duplicate-projection>" duplicateProjectionSpec
+      projectionErr `shouldSatisfy` T.isInfixOf "duplicate projection block"
+      projectionErr `shouldSatisfy` T.isInfixOf "<duplicate-projection>:9:"
+    it "anchors a missing goto on the transition line" $ do
+      err <- parseErrorOf "<missing-goto>" missingGotoSpec
+      err `shouldSatisfy` T.isInfixOf "missing a goto clause"
+      err `shouldSatisfy` T.isInfixOf "<missing-goto>:8:"
+    it "stops before a misplaced dispatch-id and expects schedule at its start" $ do
+      let src = misplacedDispatchIdSpec
+          expectedPosition =
+            "<misplaced-dispatch-id>:"
+              <> T.pack (show (lineNumberContaining "dispatch-id" src))
+              <> ":5:"
+      err <- parseErrorOf "<misplaced-dispatch-id>" src
+      err `shouldSatisfy` T.isInfixOf "schedule"
+      err `shouldSatisfy` T.isInfixOf expectedPosition
+    it "keeps a malformed register declaration's equals error" $ do
+      err <- parseErrorOf "<malformed-register>" malformedRegisterSpec
+      err `shouldSatisfy` T.isInfixOf "expecting '='"
+
+  describe "bounded decimal literals" $ do
+    forM_ decimalOverflowSpecs $ \(site, src) ->
+      it ("rejects overflow at " <> site) $ do
+        err <- parseErrorOf ("<overflow-" <> site <> ">") src
+        err `shouldSatisfy` T.isInfixOf ("decimal literal " <> decimalOverflow <> " is out of range")
+    it "accepts maxBound without changing its value" $ do
+      spec <- parseInlineSpec "<max-bound>" (wireDecimalSpec (T.pack (show (maxBound :: Int))))
+      [wireSchemaVersion wire | NAggregate aggregate <- specNodes spec, Just wire <- [aggWire aggregate]]
+        `shouldBe` [maxBound]
+
+  describe "identifier hygiene" $ do
+    it "reports constructor shape and Haskell keywords at their owning declarations" $ do
+      spec <- parseInlineSpec "<identifier-hygiene>" identifierHygieneSpec
+      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic `elem` [IdentNotConstructorSafe, IdentHaskellKeyword]]
+        `shouldContain` [(IdentNotConstructorSafe, 3), (IdentHaskellKeyword, 7)]
+    it "rejects generated vertex constructors that collide with event constructors" $ do
+      spec <- parseInlineSpec "<vertex-collision>" vertexCollisionSpec
+      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic == VertexCtorCollision]
+        `shouldBe` [(VertexCtorCollision, 3)]
+    it "rejects underscore-leading names whose title-casing cannot make a module segment" $ do
+      spec <- parseInlineSpec "<underscore-node>" underscoreNodeSpec
+      [(code diagnostic, line diagnostic) | diagnostic <- validateSpec spec, code diagnostic == IdentNotConstructorSafe]
+        `shouldBe` [(IdentNotConstructorSafe, 3)]
+    it "rejects non-ASCII identifier characters in the parser" $
+      parseSpec "<unicode-identifier>" unicodeIdentifierSpec `shouldSatisfy` leftContains "unexpected"
+
+  describe "canonical reservation.keiro" $
+    it "parses into the expected aggregate shape" $ do
+      input <- readTestText "test/fixtures/reservation.keiro"
+      case parseSpec "test/fixtures/reservation.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> do
+          specContext spec `shouldBe` "hospital-capacity"
+          length (specIds spec) `shouldBe` 3
+          length (specEnums spec) `shouldBe` 3
+          length (specRules spec) `shouldBe` 1
+          case specNodes spec of
+            [NAggregate a] -> do
+              aggName a `shouldBe` "Reservation"
+              length (aggStates a) `shouldBe` 6
+              length (aggCommands a) `shouldBe` 2
+              length (aggEvents a) `shouldBe` 2
+              length (aggTransitions a) `shouldBe` 2
+              map stTerminal (aggStates a) `shouldBe` [False, False, False, True, True, True]
+            other -> expectationFailure ("expected one aggregate node, got " <> show (length other))
+
+  describe "validator" $ do
+    it "accepts the canonical reservation.keiro" $ do
+      codes <- errorCodesOf "test/fixtures/reservation.keiro"
+      codes `shouldBe` []
+    it "rejects policy words that generated Haskell cannot lower" $ do
+      emitSpec <- specOf "test/fixtures/emit.keiro"
+      intakeSpec <- specOf "test/fixtures/intake.keiro"
+      let unknownOrdering = mapPublisher (\publisher -> publisher {pubOrdering = "banana"}) emitSpec
+          unknownBackoff =
+            mapPublisher
+              (\publisher -> publisher {pubBackoff = (pubBackoff publisher) {boKind = "banana"}})
+              emitSpec
+          incompleteBackoff =
+            mapPublisher
+              (\publisher -> publisher {pubBackoff = BackoffSpec "exponential" "2s" Nothing Nothing})
+              emitSpec
+          unknownDedupe = mapIntake (\intake -> intake {inkDedupePolicy = "Banana"}) intakeSpec
+      errorCodes unknownOrdering `shouldContain` [PublisherOrderingUnknown]
+      errorCodes unknownBackoff `shouldContain` [PublisherBackoffInvalid]
+      errorCodes incompleteBackoff `shouldContain` [PublisherBackoffInvalid]
+      errorCodes unknownDedupe `shouldContain` [IntakeDedupePolicyUnknown]
+    it "gates numeric floors on the unreleased language-4 contract" $ do
+      emitSpec <- specOf "test/fixtures/emit.keiro"
+      intakeSpec <- specOf "test/fixtures/intake.keiro"
+      readModelSpec <- specOf "test/fixtures/workflow.keiro"
+      let zeroContract = mapContract (\contract -> contract {ctrSchemaVersion = 0}) emitSpec
+          zeroAttempts = mapPublisher (\publisher -> publisher {pubMaxAttempts = 0}) emitSpec
+          zeroDecode = mapIntake (\intake -> intake {inkDecode = (inkDecode intake) {decBodySchemaVersion = 0}}) intakeSpec
+          zeroReadModel = modifyReadModel "transferDecision" (\readModel -> readModel {rmVersion = 0}) readModelSpec
+          floors =
+            [ (zeroContract, ContractSchemaVersionBelowMinimum),
+              (zeroAttempts, PublisherMaxAttemptsBelowMinimum),
+              (zeroDecode, IntakeDecodeSchemaVersionBelowMinimum),
+              (zeroReadModel, ReadModelVersionBelowMinimum)
+            ]
+      forM_ floors $ \(candidate, expected) -> do
+        serviceErrorCodes 3 candidate `shouldNotContain` [expected]
+        serviceErrorCodes 4 candidate `shouldContain` [expected]
+    it "rejects duplicate declarations whose generated behavior cannot work" $ do
+      reservation <- specOf "test/fixtures/reservation.keiro"
+      integration <- specOf "test/fixtures/emit.keiro"
+      let duplicateCommandField =
+            modifyAggregate
+              "Reservation"
+              (\aggregate -> aggregate {aggCommands = updateFirst (\command -> command {cmdFields = duplicateFirst (cmdFields command)}) (aggCommands aggregate)})
+              reservation
+          duplicateState = modifyAggregate "Reservation" (\aggregate -> aggregate {aggStates = duplicateFirst (aggStates aggregate)}) reservation
+          duplicateTransition =
+            modifyAggregate
+              "Reservation"
+              (\aggregate -> aggregate {aggTransitions = aggTransitions aggregate <> take 1 (reverse (aggTransitions aggregate))})
+              reservation
+          duplicateContractField =
+            mapContract
+              (\contract -> contract {ctrEvents = updateFirst (\event -> event {ceFields = duplicateFirst (ceFields event)}) (ctrEvents contract)})
+              integration
+          duplicateContractEvent = mapContract (\contract -> contract {ctrEvents = duplicateFirst (ctrEvents contract)}) integration
+          duplicateTopicAlias = mapContract (\contract -> contract {ctrTopics = duplicateFirst (ctrTopics contract)}) integration
+          cases =
+            [ (duplicateCommandField, AggregateDuplicateFieldName),
+              (duplicateState, AggregateDuplicateState),
+              (duplicateTransition, TransitionDuplicateUnguarded),
+              (duplicateContractField, ContractDuplicateFieldName),
+              (duplicateContractEvent, ContractDuplicateEvent),
+              (duplicateTopicAlias, ContractDuplicateTopicAlias)
+            ]
+      forM_ cases $ \(candidate, expected) -> errorCodes candidate `shouldContain` [expected]
+    it "gates ambiguous and silently shadowed duplicate surfaces on language 4" $ do
+      reservation <- specOf "test/fixtures/reservation.keiro"
+      integration <- specOf "test/fixtures/emit.keiro"
+      let duplicateRegister = modifyAggregate "Reservation" (\aggregate -> aggregate {aggRegs = duplicateFirst (aggRegs aggregate)}) reservation
+          duplicateNominal = reservation {specIds = duplicateFirst (specIds reservation)}
+          duplicateMap = mapEmit (\emitNode -> emitNode {emMap = duplicateFirst (emMap emitNode)}) integration
+          shadowDiscriminator =
+            mapContract
+              ( \contract ->
+                  contract
+                    { ctrEvents =
+                        updateFirst
+                          (\event -> event {ceFields = updateFirst (\field -> field {cfName = ctrDiscriminator contract}) (ceFields event)})
+                          (ctrEvents contract)
+                    }
+              )
+              integration
+          guardedSibling =
+            modifyAggregate
+              "Reservation"
+              ( \aggregate ->
+                  aggregate
+                    { aggTransitions =
+                        aggTransitions aggregate
+                          <> [transition {tGuard = Just (EAtom (ABool True))} | transition <- take 1 (reverse (aggTransitions aggregate))]
+                    }
+              )
+              reservation
+          cases =
+            [ (duplicateRegister, AggregateDuplicateRegister),
+              (duplicateNominal, NominalDuplicateDeclaration),
+              (duplicateMap, EmitMapDuplicateCase),
+              (shadowDiscriminator, ContractFieldShadowsDiscriminator),
+              (guardedSibling, TransitionUnguardedSibling)
+            ]
+      forM_ cases $ \(candidate, expected) -> do
+        serviceErrorCodes 3 candidate `shouldNotContain` [expected]
+        serviceErrorCodes 4 candidate `shouldContain` [expected]
+    it "gates stable identities and external names on language 4" $ do
+      workflowSpec <- specOf "test/fixtures/workflow.keiro"
+      processSpec <- specOf "test/fixtures/surge-service.keiro"
+      routerSpec <- specOf "test/fixtures/transfer-routing.keiro"
+      integration <- specOf "test/fixtures/emit.keiro"
+      let invalidIdentity = mapWorkflow (\workflow -> workflow {wfStable = ""}) workflowSpec
+          duplicateIdentity =
+            processSpec
+              { specNodes =
+                  specNodes processSpec
+                    <> [NRouter (router {rtName = "surge-demo"}) | NRouter router <- specNodes routerSpec]
+              }
+          invalidTopic = mapContract (\contract -> contract {ctrTopics = [(alias, "bad topic") | (alias, _) <- ctrTopics contract]}) integration
+          emptyTopic = mapContract (\contract -> contract {ctrTopics = [(alias, "") | (alias, _) <- ctrTopics contract]}) integration
+          invalidReadModel = modifyReadModel "transferDecision" (\readModel -> readModel {rmTable = "Bad-Table"}) workflowSpec
+          duplicateColumn = modifyReadModel "transferDecision" (\readModel -> readModel {rmColumns = duplicateFirst (rmColumns readModel)}) workflowSpec
+          gatedCases =
+            [ (invalidIdentity, RuntimeIdentityInvalid),
+              (duplicateIdentity, RuntimeIdentityDuplicate),
+              (invalidTopic, ContractTopicNameInvalid),
+              (invalidReadModel, ReadModelIdentifierInvalid),
+              (duplicateColumn, ReadModelDuplicateColumn)
+            ]
+      forM_ gatedCases $ \(candidate, expected) -> do
+        serviceErrorCodes 3 candidate `shouldNotContain` [expected]
+        serviceErrorCodes 4 candidate `shouldContain` [expected]
+      serviceErrorCodes 3 emptyTopic `shouldContain` [ContractTopicNameInvalid]
+      serviceErrorCodes 4 emptyTopic `shouldContain` [ContractTopicNameInvalid]
+    it "gates declared integration and wire couplings on language 4" $ do
+      intakeSpec <- specOf "test/fixtures/intake.keiro"
+      reservation <- specOf "test/fixtures/reservation.keiro"
+      let unresolvedBind =
+            mapIntake
+              (\intake -> intake {inkBinds = updateFirst (\binding -> binding {brField = "ghost"}) (inkBinds intake)})
+              intakeSpec
+          acceptedEventBind =
+            mapIntake
+              (\intake -> intake {inkBinds = updateFirst (\binding -> binding {brField = "region"}) (inkBinds intake)})
+              intakeSpec
+          unresolvedDedupe = mapIntake (\intake -> intake {inkDedupeKey = "ghost"}) intakeSpec
+          unknownEnvelope = mapIntake (\intake -> intake {inkDecode = (inkDecode intake) {decEnvelope = "banana policy"}}) intakeSpec
+          mismatchedSchema = mapIntake (\intake -> intake {inkDecode = (inkDecode intake) {decBodySchemaVersion = 2}}) intakeSpec
+          unresolvedAlias =
+            mapContract
+              (\contract -> contract {ctrEvents = updateFirst (\event -> event {ceTopic = "ghost"}) (ctrEvents contract)})
+              intakeSpec
+          unsupportedWire =
+            modifyAggregate
+              "Reservation"
+              (\aggregate -> aggregate {aggWire = fmap (\wire -> wire {wireKind = "banana"}) (aggWire aggregate)})
+              reservation
+          cases =
+            [ (unresolvedBind, IntakeBindUnresolved),
+              (unresolvedDedupe, IntakeDedupeKeyUnresolved),
+              (unknownEnvelope, IntakeEnvelopePolicyUnknown),
+              (mismatchedSchema, IntakeDecodeSchemaVersionMismatch),
+              (unresolvedAlias, ContractTopicAliasUnresolved),
+              (unsupportedWire, WireClauseUnsupported)
+            ]
+      forM_ cases $ \(candidate, expected) -> do
+        serviceErrorCodes 3 candidate `shouldNotContain` [expected]
+        serviceErrorCodes 4 candidate `shouldContain` [expected]
+      serviceErrorCodes 4 acceptedEventBind `shouldNotContain` [IntakeBindUnresolved]
+    it "pins every emitted legacy single-spec diagnostic that lacked a direct negative test" $ do
+      reservation <- specOf "test/fixtures/reservation.keiro"
+      intakeSpec <- specOf "test/fixtures/intake.keiro"
+      emitSpec <- specOf "test/fixtures/emit.keiro"
+      processSpec <- specOf "test/fixtures/surge-service.keiro"
+      queueSpec <- specOf "test/fixtures/reservation-work.keiro"
+      workflowSpec <- specOf "test/fixtures/workflow.keiro"
+      let updateFirstTransition update aggregate = aggregate {aggTransitions = updateFirst update (aggTransitions aggregate)}
+          undeclaredEvent = modifyAggregate "Reservation" (updateFirstTransition (\transition -> transition {tEmits = ["GhostEvent"]})) reservation
+          undeclaredState = modifyAggregate "Reservation" (updateFirstTransition (\transition -> transition {tGoto = "GhostState"})) reservation
+          terminalOutgoing = modifyAggregate "Reservation" (updateFirstTransition (\transition -> transition {tSource = "Expired"})) reservation
+          deprecatedEmitted = modifyAggregate "Reservation" (\aggregate -> aggregate {aggEvents = updateFirst (\event -> event {evDeprecated = True}) (aggEvents aggregate)}) reservation
+          wireVersionMismatch = modifyAggregate "Reservation" (\aggregate -> aggregate {aggWire = fmap (\wire -> wire {wireSchemaVersion = 2}) (aggWire aggregate)}) reservation
+          decodeRetry =
+            mapIntake
+              ( \intake ->
+                  intake
+                    { inkDisposition =
+                        [ if drOutcome row == "decodeFailed" then row {drAction = IRetry "5s"} else row
+                        | row <- inkDisposition intake
+                        ]
+                    }
+              )
+              intakeSpec
+          unresolvedPublisher = mapPublisher (\publisher -> publisher {pubEmit = "ghost"}) emitSpec
+          unresolvedIntake = mapIntake (\intake -> intake {inkContract = "ghost"}) intakeSpec
+          unboundedQueue = mapWorkqueue (\queue -> queue {wqMaxRetries = 0}) queueSpec
+          unresolvedEnqueue = mapDispatch (\dispatch -> dispatch {pdEnqueueTo = "ghost"}) queueSpec
+          unresolvedWorkflow =
+            mapOperation
+              ( \operation -> case opShape operation of
+                  RunOp _ input outcome -> operation {opShape = RunOp "GhostWorkflow" input outcome}
+                  _ -> operation
+              )
+              workflowSpec
+          cases =
+            [ (undeclaredEvent, UndeclaredEvent),
+              (undeclaredState, UndeclaredState),
+              (terminalOutgoing, TerminalHasOutgoing),
+              (deprecatedEmitted, DeprecatedEventStillEmitted),
+              (wireVersionMismatch, WireSchemaVersionMismatch),
+              (processSpec, ProcessBenignInversion),
+              (decodeRetry, DispositionDecodeUnboundedRetry),
+              (unresolvedPublisher, PublisherUnresolvedEmit),
+              (unresolvedIntake, IntakeUnresolvedContract),
+              (unboundedQueue, WqDlqWithoutCeiling),
+              (unresolvedEnqueue, DispatchEnqueueUnresolved),
+              (unresolvedWorkflow, RunWorkflowUnresolved)
+            ]
+      forM_ cases $ \(candidate, expected) -> diagnosticCodes candidate `shouldContain` [expected]
+    it "rejects a missing status-map as StatusMapNotTotal" $ do
+      codes <- diagnosticCodesOf "test/fixtures/reservation-no-statusmap.keiro"
+      codes `shouldContain` [StatusMapNotTotal]
+    it "rejects an undeclared command as UndeclaredCommand" $ do
+      codes <- diagnosticCodesOf "test/fixtures/reservation-bad-command.keiro"
+      codes `shouldContain` [UndeclaredCommand]
+    it "rejects a wall-clock guard atom as ClockSampled" $ do
+      codes <- diagnosticCodesOf "test/fixtures/reservation-clock.keiro"
+      codes `shouldContain` [ClockSampled]
+    it "accepts a v2 event with a contiguous upcaster hole" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-v2.keiro"
+      codes `shouldBe` []
+    it "rejects a v2 event with no upcaster as EvtVersionMissingUpcaster" $ do
+      codes <- diagnosticCodesOf "test/fixtures/reservation-v2-noupcast.keiro"
+      codes `shouldContain` [EvtVersionMissingUpcaster]
+    it "accepts shared upcaster sources for different event kinds" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-dup-upcast-source.keiro"
+      codes `shouldNotContain` [DuplicateUpcasterSource]
+    it "rejects a gap in the aggregate-global upcaster chain" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-chain-gap.keiro"
+      codes `shouldContain` [UpcasterChainGap]
+    it "warns while a retiring event keeps its live emitting transition" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/reservation-retiring.keiro"
+      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
+      [code d | d <- diagnostics, severity d == Warning]
+        `shouldContain` [EventRetirementInProgress]
+    it "rejects a retiring event after its live emitting transition disappears" $ do
+      source <- readTestText "test/fixtures/reservation-retiring.keiro"
+      spec <- parseInlineSpec "<retiring-without-emitter>" (T.replace "emit TransferReservationConfirmed ; " "" source)
+      [code d | d <- validateSpec spec, severity d == Error]
+        `shouldContain` [EventRetirementInProgress]
+    it "warns when a deprecated event has no replay-only emitting transition" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/reservation-deprecated.keiro"
+      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
+      [code d | d <- diagnostics, severity d == Warning]
+        `shouldContain` [DeprecatedEventReplayHazard]
+    it "recognises deprecated plus replay-only as the replay-safe cutover" $ do
+      diagnostics <- diagnosticsOf "test/fixtures/reservation-deprecated-replay-only.keiro"
+      [code d | d <- diagnostics, severity d == Error] `shouldBe` []
+      [code d | d <- diagnostics, severity d == Warning]
+        `shouldContain` [EventRetirementInProgress]
+      [code d | d <- diagnostics] `shouldNotContain` [DeprecatedEventReplayHazard]
+    it "requires exact, unique status-map event keys" $ do
+      dangling <- errorCodesOf "test/fixtures/statusmap-dangling.keiro"
+      mapM_ (\expected -> dangling `shouldContain` [expected]) [StatusMapDanglingKey, StatusMapNotTotal]
+      duplicate <- errorCodesOf "test/fixtures/statusmap-dup-key.keiro"
+      duplicate `shouldContain` [StatusMapDuplicateKey]
+    it "rejects duplicate spec and aggregate names" $ do
+      codes <- errorCodesOf "test/fixtures/duplicate-names.keiro"
+      mapM_
+        (\expected -> codes `shouldContain` [expected])
+        [ DuplicateNodeName,
+          DuplicateEnumCtor,
+          DuplicateEnumWire,
+          DuplicateIdPrefix,
+          DuplicateCommandName,
+          DuplicateEventName
+        ]
+    it "rejects aggregate-local references that do not resolve" $ do
+      codes <- errorCodesOf "test/fixtures/aggregate-bad-refs.keiro"
+      mapM_ (\expected -> codes `shouldContain` [expected]) [RegisterInitialOutOfScope, UndeclaredCommand, WriteTargetNotRegister]
+    it "anchors UnreachableState on the state row" $ do
+      let src =
+            T.unlines
+              [ "context repro",
+                "",
+                "aggregate Thing",
+                "  regs",
+                "  states",
+                "    Initial",
+                "    Unreachable"
+              ]
+      case parseSpec "<unreachable-row>" src of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec ->
+          [line d | d <- validateSpec spec, code d == UnreachableState]
+            `shouldBe` [7]
+    it "accepts a replay-only twin with a live sibling (plan 143)" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-guard-tightened-twin.keiro"
+      codes `shouldBe` []
+    it "rejects a replay-only transition that emits nothing" $ do
+      case parseSpec "<replay-only-no-emit>" (replayOnlySpecWith ["    write reservationState := Held", "    goto  Held"]) of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec ->
+          [code d | d <- validateSpec spec, severity d == Error]
+            `shouldContain` [ReplayOnlyEmitsNothing]
+    it "warns when a replay-only transition has no live sibling" $ do
+      case parseSpec "<replay-only-orphan>" (replayOnlySpecWith ["    emit  TransferReservationCreated", "    goto  Held"]) of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> do
+          [code d | d <- validateSpec spec, severity d == Warning]
+            `shouldContain` [ReplayOnlyCommandStillLive]
+          [code d | d <- validateSpec spec, severity d == Error]
+            `shouldNotContain` [ReplayOnlyCommandStillLive]
+
+  describe "complementExpr (plan 143)" $ do
+    it "applies De Morgan over and/or and flips comparison operators" $ do
+      let a = EAtom (AName "a")
+          b = EAtom (AName "b")
+      complementExpr (EAnd a b)
+        `shouldBe` EOr (ECmp OpEq a (EAtom (ABool False))) (ECmp OpEq b (EAtom (ABool False)))
+      complementExpr (ECmp OpLt a b) `shouldBe` ECmp OpGe a b
+      complementExpr (ECmp OpEq a b) `shouldBe` ECmp OpNeq a b
+      complementExpr (ECmp OpLe a b) `shouldBe` ECmp OpGt a b
+      complementExpr (ECmp OpGt a b) `shouldBe` ECmp OpLe a b
+      complementExpr (ECmp OpGe a b) `shouldBe` ECmp OpLt a b
+      complementExpr (ECmp OpNeq a b) `shouldBe` ECmp OpEq a b
+    it "flips boolean literals and grounds bare names as == false" $ do
+      complementExpr (EAtom (ABool True)) `shouldBe` EAtom (ABool False)
+      complementExpr (EAtom (AName "open"))
+        `shouldBe` ECmp OpEq (EAtom (AName "open")) (EAtom (ABool False))
+    it "stays inside the grammar: the complement of any guard re-parses" $
+      property $
+        forAll genExpr $ \e ->
+          let twin =
+                replayOnlySpecWith
+                  [ "    guard " <> renderExprText (complementExpr e),
+                    "    emit  TransferReservationCreated",
+                    "    goto  Held"
+                  ]
+           in case parseSpec "<complement>" twin of
+                Left err -> counterexample (T.unpack err) False
+                Right spec ->
+                  [tGuard t | NAggregate a <- specNodes spec, t <- aggTransitions a]
+                    === [Just (complementExpr e)]
+
+  describe "evolution parsing" $ do
+    it "parses event version and upcaster from reservation-v2.keiro" $ do
+      input <- readTestText "test/fixtures/reservation-v2.keiro"
+      case parseSpec "test/fixtures/reservation-v2.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> case [e | NAggregate a <- specNodes spec, e <- aggEvents a, evName e == "TransferReservationCreated"] of
+          (e : _) -> do
+            evVersion e `shouldBe` 2
+            evUpcastFrom e `shouldBe` Just (1, Hole)
+          [] -> expectationFailure "TransferReservationCreated not found"
+    it "round-trips the retiring marker" $ do
+      spec <- specOf "test/fixtures/reservation-retiring.keiro"
+      parseStableRenderedSpec "<retiring-round-trip>" spec `shouldBe` Right spec
+      [evRetiring event | NAggregate aggregate <- specNodes spec, event <- aggEvents aggregate, evName event == "TransferReservationConfirmed"]
+        `shouldBe` [True]
+    it "rejects an event marked both retiring and deprecated" $ do
+      source <- readTestText "test/fixtures/reservation-retiring.keiro"
+      let conflicting = T.replace "retiring event TransferReservationConfirmed" "retiring deprecated event TransferReservationConfirmed" source
+      parseSpec "<conflicting-retirement-markers>" conflicting `shouldSatisfy` isLeft
+
+  describe "aggregate snapshots (EP-109)" $ do
+    it "parses, validates, and round-trips a snapshot policy with codec fixture" $ do
+      spec <- specOf "test/fixtures/reservation-snapshot.keiro"
+      errorCodesOf "test/fixtures/reservation-snapshot.keiro" `shouldReturn` []
+      parseStableRenderedSpec "<snapshot-round-trip>" spec `shouldBe` Right spec
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        [aggregate] -> aggSnapshot aggregate `shouldBe` Just (SnapshotSpec (SnapEvery 100) 1 "7a181ceb7d798d883d28c85201c5c1692bd314a7b489da9128bff91e0f38cd28" noLoc)
+        aggregates -> expectationFailure ("expected one snapshot aggregate, got " <> show (length aggregates))
+    it "rejects disabled intervals and invalid codec fixtures" $ do
+      source <- readTestText "test/fixtures/reservation-snapshot.keiro"
+      interval <- parseInlineSpec "<snapshot-zero>" (T.replace "snapshot every 100" "snapshot every 0" source)
+      map code (validateSpec interval) `shouldContain` [SnapshotIntervalInvalid]
+      version <- parseInlineSpec "<snapshot-version-zero>" (T.replace "state-codec version=1" "state-codec version=0" source)
+      map code (validateSpec version) `shouldContain` [SnapshotCodecFixtureInvalid]
+      emptyHash <- parseInlineSpec "<snapshot-empty-hash>" (T.replace "shape-hash=\"7a181ceb7d798d883d28c85201c5c1692bd314a7b489da9128bff91e0f38cd28\"" "shape-hash=\"\"" source)
+      map code (validateSpec emptyHash) `shouldContain` [SnapshotCodecFixtureInvalid]
+    it "conditionally lowers JSON instances and the live defaultStateCodec" $ do
+      snapshotService <- checkedServiceOf "test/fixtures/reservation-snapshot.keiro"
+      ordinaryService <- checkedServiceOf "test/fixtures/reservation.keiro"
+      let snapshot = checkedSpec snapshotService
+          ordinary = checkedSpec ordinaryService
+      case ([aggregate | NAggregate aggregate <- specNodes snapshot], [aggregate | NAggregate aggregate <- specNodes ordinary]) of
+        ([snapshotAggregate], [ordinaryAggregate]) -> do
+          let snapshotModules = scaffoldServiceModules (defaultContext (specContext snapshot)) snapshotService
+              ordinaryModules = scaffoldServiceModules (defaultContext (specContext ordinary)) ordinaryService
+              snapshotDomain = generatedTextEndingIn "Domain.hs" snapshotModules
+              snapshotStream = generatedTextEndingIn "EventStream.hs" snapshotModules
+              ordinaryDomain = generatedTextEndingIn "Domain.hs" ordinaryModules
+              ordinaryStream = generatedTextEndingIn "EventStream.hs" ordinaryModules
+          snapshotDomain `shouldSatisfy` T.isInfixOf "deriving anyclass (ToJSON, FromJSON)"
+          snapshotStream `shouldSatisfy` T.isInfixOf "snapshotPolicy = Every 100"
+          snapshotStream `shouldSatisfy` T.isInfixOf "stateCodec = Just (withFoldFingerprint"
+          snapshotStream `shouldSatisfy` T.isInfixOf "Spec-visible fold changes invalidate old"
+          snapshotStream `shouldSatisfy` T.isInfixOf "reservationSnapshotFixture = (1, \"7a181ceb7d798d883d28c85201c5c1692bd314a7b489da9128bff91e0f38cd28\")"
+          ordinaryDomain `shouldNotSatisfy` T.isInfixOf "DeriveAnyClass"
+          ordinaryStream `shouldSatisfy` T.isInfixOf "snapshotPolicy = Never"
+          ordinaryStream `shouldSatisfy` T.isInfixOf "stateCodec = Nothing"
+          ordinaryStream `shouldSatisfy` T.isInfixOf "reservationCategory = Stream.categoryUnsafe \"reservation\""
+          firewallBreaches snapshotModules `shouldBe` []
+        _ -> expectationFailure "expected one aggregate in each snapshot test spec"
+
+  describe "aggregate fold fingerprints (plan 138)" $ do
+    it "is deterministic across repeated parses and formatting-only changes" $ do
+      source <- readTestText "test/fixtures/reservation.keiro"
+      first <- parseInlineSpec "<first>" source
+      second <- shouldParseStableRenderedSpec "<second>" first
+      aggregateFoldFingerprint first (onlyAggregate first)
+        `shouldBe` aggregateFoldFingerprint second (onlyAggregate second)
+    it "changes for transition writes, guards, and referenced rule bodies" $ do
+      base <- specOf "test/fixtures/reservation.keiro"
+      writeChanged <- specOf "test/fixtures/reservation-foldchange.keiro"
+      guardChanged <- specOf "test/fixtures/reservation-guard-tightened.keiro"
+      source <- readTestText "test/fixtures/reservation.keiro"
+      ruleChanged <- parseInlineSpec "<rule-change>" (T.replace "RedTag => true" "RedTag => false" source)
+      let baseFingerprint = aggregateFoldFingerprint base (onlyAggregate base)
+      aggregateFoldFingerprint writeChanged (onlyAggregate writeChanged) `shouldNotBe` baseFingerprint
+      aggregateFoldFingerprint guardChanged (onlyAggregate guardChanged) `shouldNotBe` baseFingerprint
+      aggregateFoldFingerprint ruleChanged (onlyAggregate ruleChanged) `shouldNotBe` baseFingerprint
+    it "ignores wire and projection changes" $ do
+      base <- specOf "test/fixtures/reservation.keiro"
+      wireChanged <- specOf "test/fixtures/reservation-wire.keiro"
+      source <- readTestText "test/fixtures/reservation.keiro"
+      projectionChanged <- parseInlineSpec "<projection-change>" (T.replace "projection transfer_decisions" "projection renamed_projection" source)
+      let surface = aggregateFoldSurface base (onlyAggregate base)
+      aggregateFoldSurface wireChanged (onlyAggregate wireChanged) `shouldBe` surface
+      aggregateFoldSurface projectionChanged (onlyAggregate projectionChanged) `shouldBe` surface
+    it "invalidates mapped-register snapshots when binding or wire identity changes" $ do
+      base <- specOf "test/fixtures/consumer-types.keiro"
+      bindingChanged <- specOf "test/fixtures/consumer-types-binding-change.keiro"
+      wireChanged <- specOf "test/fixtures/consumer-types-wirekey.keiro"
+      let baseFingerprint = aggregateFoldFingerprint base (onlyAggregate base)
+      aggregateFoldFingerprint bindingChanged (onlyAggregate bindingChanged) `shouldNotBe` baseFingerprint
+      aggregateFoldFingerprint wireChanged (onlyAggregate wireChanged) `shouldNotBe` baseFingerprint
+
+  describe "process/timer (EP-3)" $ do
+    it "parses the hospital-surge process + nested timer" $ do
+      input <- readTestText "test/fixtures/hospital-surge.keiro"
+      case parseSpec "test/fixtures/hospital-surge.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> case [p | NProcess p <- specNodes spec] of
+          (p : _) -> do
+            procId p `shouldBe` "HospitalSurge"
+            procName p `shouldBe` "hospital-surge"
+            procRejected p `shouldBe` PolHalt
+            procPoison p `shouldBe` PolHalt
+            sagaCategory (procSaga p) `shouldBe` "hospitalSurge"
+            tmName (procTimer p) `shouldBe` "surgeFollowUp"
+            onReject (fireDisposition (tmFire (procTimer p))) `shouldBe` OFired
+            onAmbiguous (fireDisposition (tmFire (procTimer p))) `shouldBe` ORetry
+            tmMaxAttempts (procTimer p) `shouldBe` 5
+          [] -> expectationFailure "no process node parsed"
+    it "round-trips the hospital-surge spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/hospital-surge.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseStableRenderedSpec "in" spec `shouldBe` Right spec
+    it "accepts the hospital-surge spec (no errors; benign-inversion warnings only)" $ do
+      codes <- errorCodesOf "test/fixtures/hospital-surge.keiro"
+      codes `shouldBe` []
+    it "rejects illegal saga categories and no longer parses the raw stream-prefix clause" $ do
+      spec <- specOf "test/fixtures/hospital-surge.keiro"
+      mapM_
+        (\categoryName -> processErrorCodes (\process -> process {procSaga = (procSaga process) {sagaCategory = categoryName}}) spec `shouldContain` [SagaCategoryIllegal])
+        ["", "$all", "hospital-surge", "hospital surge", "wf:surge"]
+      source <- readTestText "test/fixtures/hospital-surge.keiro"
+      parseSpec "<legacy-saga>" (T.replace "saga Surge category \"hospitalSurge\"" "saga Surge stream=\"hospital-surge-\" <> correlationId" source)
+        `shouldSatisfy` isLeft
+    it "rejects a wall-clock fireAt as ProcessFireAtNotInjected" $ do
+      codes <- errorCodesOf "test/fixtures/hospital-surge-clock.keiro"
+      codes `shouldContain` [ProcessFireAtNotInjected]
+    it "reports one ProcessFireAtNotInjected for a wholly unknown fireAt field" $ do
+      codes <- errorCodesOf "test/fixtures/hospital-surge-clock.keiro"
+      length (filter (== ProcessFireAtNotInjected) codes) `shouldBe` 1
+    it "rejects a user-supplied dispatch id as ProcessDispatchIdSupplied" $ do
+      codes <- errorCodesOf "test/fixtures/hospital-surge-dispatchid.keiro"
+      codes `shouldContain` [ProcessDispatchIdSupplied]
+    it "rejects an unresolved saga reference as ProcessUnresolvedRef" $ do
+      codes <- errorCodesOf "test/fixtures/hospital-surge-badref.keiro"
+      codes `shouldContain` [ProcessUnresolvedRef]
+    it "rejects unresolved process commands, projections, schedules, and advance ids" $ do
+      codes <- errorCodesOf "test/fixtures/process-ghost-refs.keiro"
+      length (filter (== ProcessUnresolvedRef) codes) `shouldBe` 5
+      codes `shouldContain` [ProcessDispatchIdSupplied]
+
+  describe "router (EP-108)" $ do
+    it "parses the incident-paging router shape" $ do
+      input <- readTestText "test/fixtures/incident-paging/incident-paging.keiro"
+      case parseSpec "test/fixtures/incident-paging/incident-paging.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> case [router | NRouter router <- specNodes spec] of
+          [router] -> do
+            rtId router `shouldBe` "PagingRouter"
+            rtName router `shouldBe` "jitsurei-paging"
+            corrField (rtKey router) `shouldBe` "incidentId"
+            rvSource (rtResolve router) `shouldBe` ResolveReadModel "service_oncall"
+            rvRow (rtResolve router) `shouldBe` ["responderId"]
+            rdCommand (rtDispatch router) `shouldBe` "SendPage"
+            rtRejected router `shouldBe` PolDeadLetter
+            rtPoison router `shouldBe` PolHalt
+          routers -> expectationFailure ("expected one router, got " <> show (length routers))
+    it "round-trips the incident-paging spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/incident-paging/incident-paging.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseStableRenderedSpec "in" spec `shouldBe` Right spec
+    it "accepts the incident-paging router with warnings only" $ do
+      codes <- errorCodesOf "test/fixtures/incident-paging/incident-paging.keiro"
+      codes `shouldBe` []
+      diagnostics <- diagnosticCodesOf "test/fixtures/incident-paging/incident-paging.keiro"
+      diagnostics `shouldContain` [PolicyDeadLetterUnused, AmbiguousFollowsRejectedPolicy]
+    it "rejects unresolved targets, keys, commands, and binding scopes" $ do
+      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      routerErrorCodes (\router -> router {rtTarget = "Pge"}) spec `shouldContain` [RouterUnresolvedRef]
+      routerErrorCodes (\router -> router {rtKey = (rtKey router) {corrField = "incidntId"}}) spec `shouldContain` [RouterKeyFieldUnknown]
+      routerErrorCodes (\router -> router {rtDispatch = (rtDispatch router) {rdCommand = "SendPag"}}) spec `shouldContain` [RouterCommandUnknown]
+      routerErrorCodes
+        ( \router ->
+            let dispatch = rtDispatch router
+             in router {rtDispatch = dispatch {rdFields = [FieldBinding "responderId" (Just "resolved.responder")]}}
+        )
+        spec
+        `shouldContain` [RouterBindingUnscoped]
+    it "rejects unresolved read models and contradictory rejection policies" $ do
+      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      let withoutReadModel = removeReadModel "service_oncall" spec
+      errorCodes withoutReadModel `shouldContain` [RouterUnresolvedRef]
+      routerErrorCodes
+        ( \router ->
+            let dispatch = rtDispatch router
+                disposition = rdDisposition dispatch
+             in router
+                  { rtRejected = PolHalt,
+                    rtDispatch = dispatch {rdDisposition = disposition {onFailed = DDeadLetter "page rejected"}}
+                  }
+        )
+        spec
+        `shouldContain` [PolicyContradiction]
+    it "rejects on-ambiguous Fired for process timers" $ do
+      spec <- specOf "test/fixtures/hospital-surge.keiro"
+      let changed =
+            spec
+              { specNodes =
+                  [ case node of
+                      NProcess process ->
+                        let timer = procTimer process
+                            fire = tmFire timer
+                            disposition = fireDisposition fire
+                         in NProcess process {procTimer = timer {tmFire = fire {fireDisposition = disposition {onAmbiguous = OFired}}}}
+                      _ -> node
+                  | node <- specNodes spec
+                  ]
+              }
+      errorCodes changed `shouldContain` [AmbiguousMarkedBenign]
+    it "requires explicit policy and ambiguity clauses in the grammar" $ do
+      source <- readTestText "test/fixtures/hospital-surge.keiro"
+      parseSpec "<missing-poison>" (T.replace "  poison => halt\n" "" source) `shouldSatisfy` isLeft
+      parseSpec "<missing-ambiguous>" (T.replace " ; on-ambiguous Retry" "" source) `shouldSatisfy` isLeft
+    it "scaffolds firewall-clean router wiring, policies, and typed-hole guidance" $ do
+      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      case [router | NRouter router <- specNodes spec] of
+        [router] -> do
+          let ctx = defaultContext (specContext spec)
+              modules = scaffoldRouter ctx router
+              generated = [m | m <- modules, kind m == Generated]
+              holes = [m | m <- modules, kind m == HoleStub]
+          firewallBreaches generated `shouldBe` []
+          case (generated, holes) of
+            ([generatedModule], [holeModule]) -> do
+              moduleText generatedModule `shouldSatisfy` T.isInfixOf "pagingRouterWorkerOptions"
+              moduleText generatedModule `shouldSatisfy` T.isInfixOf "rejectedCommandPolicy = RejectedDeadLetter"
+              moduleText holeModule `shouldSatisfy` T.isInfixOf "UNION of resolved target identities"
+              moduleText holeModule `shouldSatisfy` T.isInfixOf "confirmBenignDuplicate"
+            _ -> expectationFailure "expected one generated router module and one router hole module"
+        routers -> expectationFailure ("expected one router, got " <> show (length routers))
+    it "requires a caller callback for non-halting poison policies" $ do
+      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      case [router | NRouter router <- specNodes spec] of
+        [router] -> do
+          let ctx = defaultContext (specContext spec)
+              generatedFor choice = [moduleText m | m <- scaffoldRouter ctx router {rtPoison = choice}, kind m == Generated]
+          mapM_
+            ( \(choice, constructor) -> case generatedFor choice of
+                [generatedModule] -> do
+                  generatedModule `shouldSatisfy` T.isInfixOf "(Envelope msg -> Eff es ()) -> WorkerOptions es msg"
+                  generatedModule `shouldSatisfy` T.isInfixOf (constructor <> " poisonCallback")
+                _ -> expectationFailure "expected one generated router module"
+            )
+            [(PolDeadLetter, "PoisonDeadLetter"), (PolSkip, "PoisonSkip")]
+          case [moduleText m | m <- scaffoldRouter ctx router {rtRejected = PolSkip}, kind m == Generated] of
+            [generatedModule] -> generatedModule `shouldSatisfy` T.isInfixOf "rejectedCommandPolicy = RejectedSkip"
+            _ -> expectationFailure "expected one generated router module"
+        routers -> expectationFailure ("expected one router, got " <> show (length routers))
+    it "emits router harness facts that pin policy and target-keyed identity" $ do
+      spec <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      case [router | NRouter router <- specNodes spec] of
+        [router] -> case harnessRouter (defaultContext (specContext spec)) router of
+          [facts] -> do
+            moduleText facts `shouldSatisfy` T.isInfixOf "(\"rejectedPolicy\", \"deadLetter\")"
+            moduleText facts `shouldSatisfy` T.isInfixOf "targetStreamName, occurrence"
+          modules -> expectationFailure ("expected one router harness, got " <> show (length modules))
+        routers -> expectationFailure ("expected one router, got " <> show (length routers))
+    it "rejects invalid timer ceilings and target field bindings" $ do
+      codes <- errorCodesOf "test/fixtures/process-bad-timer.keiro"
+      mapM_
+        (\expected -> codes `shouldContain` [expected])
+        [ProcessTimerCeilingInvalid, ProcessFieldBindingUnresolved]
+    it "accepts resolved process projection references" $ do
+      codes <- errorCodesOf "test/fixtures/surge-service.keiro"
+      codes `shouldBe` []
+    it "scaffolds the process: Generated wiring is firewall-clean + a HoleStub" $ do
+      mods <- legacyScaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
+      let gens = [m | m <- mods, kind m == Generated]
+          holes = [m | m <- mods, kind m == HoleStub]
+      length holes `shouldBe` 1
+      firewallBreaches gens `shouldBe` []
+      case gens of
+        [generatedModule] -> do
+          -- the worker uses the spec's ceiling, never the dangerous default
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "max-attempts = 5"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "hospitalSurgeProcessWorkerOptions"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "import Generated.HospitalCapacity.Surge.EventStream (SurgeEventStreamDef)"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "hospitalSurgeCategory :: Stream.StreamCategory SurgeEventStreamDef"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "hospitalSurgeCategory = Stream.categoryUnsafe \"hospitalSurge\""
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "confirmBenignDuplicate"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "StreamName -> EventId -> CommandError -> Eff es Bool"
+          moduleText generatedModule `shouldSatisfy` T.isInfixOf "Left (CommandAmbiguous _)"
+          case holes of
+            [holeModule] -> moduleText holeModule `shouldSatisfy` T.isInfixOf "entityStream hospitalSurgeCategory"
+            _ -> expectationFailure "expected one process hole module"
+        _ -> expectationFailure "expected one generated process module"
+    it "process scaffold is deterministic" $ do
+      a <- legacyScaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
+      b <- legacyScaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
+      map moduleText a `shouldBe` map moduleText b
+    it "separates aggregate event-stream and command-target categories and emits stable typed sums" $ do
+      spec <- specOf "test/fixtures/hospital-surge.keiro"
+      let ctx = defaultContext (specContext spec)
+          modules = concat [scaffoldAggregate ctx spec aggregate | NAggregate aggregate <- specNodes spec]
+          surgeStream = generatedTextEndingIn "Surge/EventStream.hs" modules
+          surgeDomain = generatedTextEndingIn "Surge/Domain.hs" modules
+      surgeStream `shouldSatisfy` T.isInfixOf "surgeCategory :: Stream.StreamCategory SurgeEventStreamDef"
+      surgeStream `shouldSatisfy` T.isInfixOf "surgeCommandCategory :: Stream.StreamCategory SurgeCommand"
+      surgeDomain `shouldNotSatisfy` T.isInfixOf "{-# LANGUAGE EmptyDataDecls #-}"
+      surgeDomain `shouldSatisfy` T.isInfixOf "data SurgeEvent = SurgeThresholdNoted"
+      surgeDomain `shouldSatisfy` (not . T.isInfixOf "data SurgeEvent = ()")
+
+  describe "contract (EP-4)" $ do
+    it "parses the emergency contract (topics + events-on-topic + typed fields)" $ do
+      input <- readTestText "test/fixtures/contract.keiro"
+      case parseSpec "test/fixtures/contract.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> case [c | NContract c <- specNodes spec] of
+          (c : _) -> do
+            ctrName c `shouldBe` "emergency"
+            ctrDiscriminator c `shouldBe` "messageType"
+            map fst (ctrTopics c) `shouldBe` ["incidentEvents", "hospitalEvents"]
+            map ceName (ctrEvents c) `shouldBe` ["IncidentTransferNeedDeclared", "TransferReservationAccepted"]
+          [] -> expectationFailure "no contract node parsed"
+
+    it "branches contract scaffolding, manifests, and durable identities only for language 4" $ do
+      sourceText <- readTestText "test/fixtures/contract-v4.keiro"
+      parsed <- case parseSource "contract-v4.keiro" sourceText of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let service = checkedSource parsed
+          spec = checkedSpec service
+          ctx = defaultContext (specContext spec)
+      contract <- case [value | NContract value <- specNodes spec] of
+        [value] -> pure value
+        values -> expectationFailure ("expected one contract, got " <> show (length values)) >> fail "unreachable"
+      legacyModule <- case scaffoldContract ctx contract of
+        [value] -> pure value
+        values -> expectationFailure ("expected one legacy module, got " <> show (length values)) >> fail "unreachable"
+      typedModule <- case scaffoldContractForService ctx service contract of
+        [value] -> pure value
+        values -> expectationFailure ("expected one typed module, got " <> show (length values)) >> fail "unreachable"
+      let dependencies = manifestDependenciesForService service
+          identities = idDomainIdentitiesForService service
+          manifestText = renderManifestForService "contract-v4.keiro" [typedModule] service
+      committed <- readTestText "test/conformance-contract/Generated/HospitalCapacity/Emergency/Contract.hs"
+      normalizeGenerated (moduleText typedModule) `shouldBe` normalizeGenerated committed
+      moduleText legacyModule `shouldSatisfy` T.isInfixOf "incidentId :: !Text"
+      moduleText legacyModule `shouldSatisfy` (not . T.isInfixOf "KindID")
+      moduleText typedModule `shouldSatisfy` T.isInfixOf "incidentId :: !(KindID \"inc\")"
+      moduleText typedModule `shouldSatisfy` T.isInfixOf "KindID.toText payload.incidentId"
+      moduleText typedModule `shouldSatisfy` T.isInfixOf "explicitParseField (parseKindIdV7Value @\"inc\") o \"incidentId\""
+      moduleText typedModule `shouldSatisfy` T.isInfixOf "  , incidentEventsTopic"
+      moduleText typedModule `shouldSatisfy` T.isInfixOf "  , hospitalEventsTopic"
+      moduleText typedModule `shouldSatisfy` (not . T.isInfixOf "Wno-unused-top-binds")
+      dependencies `shouldBe` ["aeson", "base", "keiro-core", "mmzk-typeid", "text"]
+      manifestDependencies spec `shouldBe` ["aeson", "base", "text"]
+      forM_ dependencies $ \dependency -> manifestText `shouldSatisfy` T.isInfixOf ("    , " <> dependency)
+      identities
+        `shouldBe` [ "id-domain|name=contract:emergency.IncidentTransferNeedDeclared.incidentId|contract=keiro-dsl/id-domain/typeid-v7/1|prefix=inc|separator=_|json=canonical-json-text",
+                     "id-domain|name=contract:emergency.TransferReservationAccepted.incidentId|contract=keiro-dsl/id-domain/typeid-v7/1|prefix=inc|separator=_|json=canonical-json-text",
+                     "id-domain|name=contract:emergency.TransferReservationAccepted.reservationId|contract=keiro-dsl/id-domain/typeid-v7/1|prefix=rsv|separator=_|json=canonical-json-text",
+                     "id-domain|name=contract:emergency.TransferReservationAccepted.hospitalId|contract=keiro-dsl/id-domain/typeid-v7/1|prefix=hsp|separator=_|json=canonical-json-text"
+                   ]
+
+    it "persists contract ID domains in single-file and workspace records with owner attribution" $ do
+      sourceText <- readTestText "test/fixtures/contract-v4.keiro"
+      parsed <- case parseSource "contract-v4.keiro" sourceText of
+        Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+        Right value -> pure value
+      let service = checkedSource parsed
+          spec = checkedSpec service
+          ctx = defaultContext (specContext spec)
+          modules = scaffoldServiceModules ctx service
+          identities = idDomainIdentitiesForService service
+      duplicateIdentity <- case identities of
+        value : _ -> pure value
+        [] -> expectationFailure "typed contract service did not expose ID-domain identities" >> fail "unreachable"
+      withTempDirectory "keiro-dsl-v4-contract-record" $ \out -> do
+        result <- executeServiceScaffold out False "contract-v4.keiro" (parsedSourceLanguage parsed) ctx service modules
+        result `shouldSatisfy` isRight
+        contents <- TIO.readFile (out </> recordFileName (specContext spec))
+        record <- maybe (expectationFailure "typed contract scaffold record did not parse" >> fail "unreachable") pure (parseRecord contents)
+        recIdDomains record `shouldBe` identities
+        parseRecord (contents <> "id-domain " <> duplicateIdentity <> "\n") `shouldBe` Nothing
+
+      let manifest = "service hospital-capacity\nspec domain/contract.keiro\n"
+          source = memoryContentSource (Map.fromList [("service.keiro-workspace", manifest), ("domain/contract.keiro", sourceText)])
+      loaded <- loadWorkspace source "service.keiro-workspace"
+      workspace <- either (\failure -> expectationFailure (show failure) >> fail "unreachable") pure loaded
+      workspacePlan <- either (\refusals -> expectationFailure (show refusals) >> fail "unreachable") pure (planWorkspaceScaffold "goldens" ctx workspace)
+      case [provenance | (scaffoldModule, provenance) <- wpModules workspacePlan, modulePath scaffoldModule == "Generated/HospitalCapacity/Emergency/Contract.hs"] of
+        [MemberOwned owner] -> owner `shouldBe` "domain/contract.keiro"
+        values -> expectationFailure ("expected one member-owned contract module, got " <> show values)
+      withTempDirectory "keiro-dsl-v4-contract-workspace-record" $ \out -> do
+        result <- executeWorkspaceScaffold out False workspacePlan
+        result `shouldSatisfy` isRight
+        contents <- TIO.readFile (out </> workspaceRecordFileName (wsService workspace))
+        record <- maybe (expectationFailure "typed contract workspace record did not parse" >> fail "unreachable") pure (parseWorkspaceRecord contents)
+        wrIdDomains record `shouldBe` identities
+        [(wrmPath row, wrmOwner row) | row <- wrModules record, wrmPath row == "Generated/HospitalCapacity/Emergency/Contract.hs"]
+          `shouldBe` [("Generated/HospitalCapacity/Emergency/Contract.hs", Just "domain/contract.keiro")]
+        parseWorkspaceRecord (contents <> "id-domain " <> duplicateIdentity <> "\n") `shouldBe` Nothing
+    it "round-trips the contract spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/contract.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
+    it "round-trips the intake (inbox) spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/intake.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
+    it "accepts the intake spec (complete disposition, no inversions)" $ do
+      codes <- errorCodesOf "test/fixtures/intake.keiro"
+      codes `shouldBe` []
+    it "lowers explicit dedupe-only persistence and defaults omission to full-envelope" $ do
+      spec <- specOf "test/fixtures/intake.keiro"
+      ordinary <- specOf "test/fixtures/intake-decode.keiro"
+      case ([intake | NIntake intake <- specNodes spec], [intake | NIntake intake <- specNodes ordinary]) of
+        ([intake], [defaultIntake]) -> do
+          inkPersist intake `shouldBe` InkPersistDedupeOnly
+          inkPersist defaultIntake `shouldBe` InkPersistFull
+          renderSpec spec `shouldSatisfy` T.isInfixOf "persist = dedupe-only"
+          renderSpec ordinary `shouldNotSatisfy` T.isInfixOf "persist ="
+          let inbox = generatedTextEndingIn "Inbox.hs" (scaffoldIntake (defaultContext (specContext spec)) intake)
+          inbox `shouldSatisfy` T.isInfixOf "inboxPersistence = PersistDedupeOnly"
+          inbox `shouldSatisfy` T.isInfixOf "data IncidentInboxOutcome"
+          inbox `shouldSatisfy` T.isInfixOf "data IncidentInboxDisposition"
+          inbox `shouldSatisfy` T.isInfixOf "InboxRetryAfter !RetryDelay !(Maybe InboxFailure)"
+          inbox `shouldSatisfy` T.isInfixOf "InboxDeadLetter !(Maybe Text) !(Maybe InboxFailure)"
+          inbox `shouldSatisfy` T.isInfixOf "InboxHandlerFailed reason attempts ->"
+          inbox `shouldNotSatisfy` T.isInfixOf "Nothing -> InboxRetry"
+        (intakes, defaultIntakes) ->
+          expectationFailure ("expected one intake in each fixture, got " <> show (length intakes, length defaultIntakes))
+    it "rejects duplicate => retry (inversion 1)" $ do
+      codes <- errorCodesOf "test/fixtures/intake-dup-retry.keiro"
+      codes `shouldContain` [DispositionDuplicateRetry]
+    it "rejects previouslyFailed => retry (inversion 2)" $ do
+      codes <- errorCodesOf "test/fixtures/intake-pf-retry.keiro"
+      codes `shouldContain` [DispositionPreviouslyFailedRetry]
+    it "rejects an incomplete disposition table" $ do
+      codes <- errorCodesOf "test/fixtures/intake-incomplete.keiro"
+      codes `shouldContain` [DispositionIncomplete]
+    it "rejects a shadowing duplicate intake disposition row" $ do
+      codes <- errorCodesOf "test/fixtures/intake-dup-row.keiro"
+      codes `shouldContain` [DispositionDuplicateOutcome]
+    it "rejects intake events declared on another topic" $ do
+      codes <- errorCodesOf "test/fixtures/intake-topic-mismatch.keiro"
+      codes `shouldContain` [TopicAffinityMismatch]
+    it "round-trips the emit/publisher spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/emit.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
+    it "accepts the emit/publisher spec (skip present, coupling resolves)" $ do
+      codes <- errorCodesOf "test/fixtures/emit.keiro"
+      codes `shouldBe` []
+    it "rejects a missing _ => skip catch-all as EmitSkipMissing" $ do
+      codes <- errorCodesOf "test/fixtures/emit-noskip.keiro"
+      codes `shouldContain` [EmitSkipMissing]
+    it "rejects mapping to an undeclared contract event as EmitUnresolvedContract" $ do
+      codes <- errorCodesOf "test/fixtures/emit-badevent.keiro"
+      codes `shouldContain` [EmitUnresolvedContract]
+    it "rejects emit events declared on another topic" $ do
+      codes <- errorCodesOf "test/fixtures/emit-topic-mismatch.keiro"
+      codes `shouldContain` [TopicAffinityMismatch]
+
+  describe "pgmq workqueue/dispatch (EP-5)" $ do
+    it "round-trips the reservation-work spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/reservation-work.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseSpec "in" (renderSpec spec) `shouldBe` Right spec
+    it "accepts the reservation-work spec (physical matches, no inversions)" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-work.keiro"
+      codes `shouldBe` []
+    it "rejects a divergent captured physical name as WqPhysicalDivergence" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-work-divergent.keiro"
+      codes `shouldContain` [WqPhysicalDivergence]
+    it "rejects storeFailure => deadLetter as WqStoreFailureNotRetry" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-work-sf-deadletter.keiro"
+      codes `shouldContain` [WqStoreFailureNotRetry]
+    it "rejects decodeFailure => retry as WqDecodeFailureNotDeadLetter" $ do
+      codes <- errorCodesOf "test/fixtures/reservation-work-df-retry.keiro"
+      codes `shouldContain` [WqDecodeFailureNotDeadLetter]
+    it "requires complete, unique workqueue disposition rows" $ do
+      incomplete <- errorCodesOf "test/fixtures/workqueue-incomplete.keiro"
+      incomplete `shouldContain` [WqDispositionIncomplete]
+      duplicateSpec <- specOf "test/fixtures/workqueue-dup-row.keiro"
+      let duplicateDiagnostics = [d | d <- validateSpec duplicateSpec, code d == DispositionDuplicateOutcome]
+      map line duplicateDiagnostics `shouldBe` [18]
+    it "checks the captured queueRef dlq and table fixtures" $ do
+      dlqCodes <- errorCodesOf "test/fixtures/workqueue-dlq-divergent.keiro"
+      dlqCodes `shouldContain` [WqDlqDivergence]
+      tableCodes <- errorCodesOf "test/fixtures/workqueue-table-divergent.keiro"
+      tableCodes `shouldContain` [WqTableDivergence]
+    it "matches queueRef for upper-case, punctuation, and hashed logical names" $ do
+      upper <- errorCodesOf "test/fixtures/workqueue-uppercase-logical.keiro"
+      upper `shouldBe` []
+      hashed <- errorCodesOf "test/fixtures/workqueue-hashed-logical.keiro"
+      hashed `shouldBe` []
+      derivedQueueTrio "hospital_capacity.reservation_work.per_hospital_fifo_lane_assignments"
+        `shouldBe` ( "hospital_capacity_reservat_757040df00976c33",
+                     "hospital_capacity_reservat_757040df00976c33_dlq",
+                     "pgmq.q_hospital_capacity_reservat_757040df00976c33"
+                   )
+    it "resolves dispatch dedup queues and payload wire fields" $ do
+      ghost <- errorCodesOf "test/fixtures/dispatch-dedup-ghost-queue.keiro"
+      ghost `shouldContain` [DispatchDedupQueueUnresolved]
+      field <- errorCodesOf "test/fixtures/dispatch-dedup-bad-field.keiro"
+      field `shouldContain` [DispatchDedupFieldUnresolved]
+    it "requires a resolvable group key exactly when ordering is FIFO" $ do
+      noKey <- errorCodesOf "test/fixtures/reservation-work-fifo-nokey.keiro"
+      noKey `shouldContain` [WqGroupKeyMissing]
+      unordered <- errorCodesOf "test/fixtures/reservation-work-key-unordered.keiro"
+      unordered `shouldContain` [WqGroupKeyWithoutFifo]
+      source <- readTestText "test/fixtures/reservation-work.keiro"
+      unresolved <- parseInlineSpec "<unresolved-group-key>" (T.replace "group key from reservationId" "group key from missingId" source)
+      map code (validateSpec unresolved) `shouldContain` [WqGroupKeyUnresolved]
+    it "warns on unlogged storage and rejects empty partition settings" $ do
+      warningCodes <- diagnosticCodesOf "test/fixtures/reservation-work-unlogged.keiro"
+      warningCodes `shouldContain` [WqUnloggedDurability]
+      partitionCodes <- errorCodesOf "test/fixtures/reservation-work-partitioned-empty.keiro"
+      partitionCodes `shouldContain` [WqPartitionSpecEmpty]
+    it "lowers ordering, provisioning, and raw group-key projection" $ do
+      spec <- specOf "test/fixtures/reservation-work.keiro"
+      case [workqueue | NWorkqueue workqueue <- specNodes spec] of
+        workqueue : _ -> do
+          let modules = scaffoldWorkqueue (defaultContext (specContext spec)) workqueue
+              queue = generatedTextEndingIn "Queue.hs" modules
+              policy = generatedTextEndingIn "QueuePolicy.hs" modules
+          queue `shouldSatisfy` T.isInfixOf "groupKeyFor payload = payload.reservationId"
+          policy `shouldSatisfy` T.isInfixOf "jobOrdering = FifoThroughput"
+          policy `shouldSatisfy` T.isInfixOf "withFifoIndexProvision (standardProvision)"
+          policy `shouldSatisfy` T.isInfixOf "data ReservationWorkOutcome"
+          policy `shouldSatisfy` T.isInfixOf "jobOutcomeFor :: ReservationWorkOutcome -> JobOutcome"
+          policy `shouldNotSatisfy` T.isInfixOf "jobOutcomeFor :: Text -> JobOutcome"
+          policy `shouldNotSatisfy` T.isInfixOf "  _ -> Retry"
+          firewallBreaches modules `shouldBe` []
+        [] -> expectationFailure "reservation-work fixture has no workqueue"
+
+  describe "readmodel (EP-107)" $ do
+    it "parses and round-trips first-class read models" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      case [readModel | NReadModel readModel <- specNodes spec] of
+        [subscriptionModel, inlineModel] -> do
+          rmName subscriptionModel `shouldBe` "transfer_decisions"
+          rmColumns subscriptionModel
+            `shouldBe` [ RmColumn "reservation_id" "text" True,
+                         RmColumn "hospital_id" "text" True,
+                         RmColumn "status" "text" True,
+                         RmColumn "decided_at" "timestamptz" False
+                       ]
+          rmScope subscriptionModel `shouldBe` Just (RmCategory "reservation")
+          rmFeed subscriptionModel `shouldBe` RmSubscription
+          rmSubscription subscriptionModel `shouldBe` Just "hospital-capacity-transfer-decisions-sub"
+          rmName inlineModel `shouldBe` "subscriptions"
+          rmScope inlineModel `shouldBe` Nothing
+          rmFeed inlineModel `shouldBe` RmInline
+        nodes -> expectationFailure ("expected two readmodel nodes, got " <> show (length nodes))
+      parseStableRenderedSpec "in" spec `shouldBe` Right spec
+    it "accepts an aggregate projection without a consistency clause" $ do
+      spec <- parseInlineSpec "<projection-without-consistency>" projectionWithoutConsistencySpec
+      case [projection | NAggregate aggregate <- specNodes spec, Just projection <- [aggProjection aggregate]] of
+        [projection] -> projConsistency projection `shouldBe` Nothing
+        projections -> expectationFailure ("expected one projection, got " <> show (length projections))
+    it "pins the canonical UTF-8 shape digest and runtime identities" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      case [readModel | NReadModel readModel <- specNodes spec] of
+        (subscriptionModel : inlineModel : _) -> do
+          canonicalShape subscriptionModel
+            `shouldBe` "transfer_decisions|reservation_id:text:req|hospital_id:text:req|status:text:req|decided_at:timestamptz:null"
+          deriveShapeHash subscriptionModel `shouldBe` "fnv1a:3717f6d9e3c44bd6"
+          deriveShapeHash inlineModel `shouldBe` "fnv1a:f54d9bb2f40a6738"
+          registryNameFor (specContext spec) subscriptionModel `shouldBe` "hospital-capacity-transfer-decisions"
+          subscriptionNameFor (specContext spec) subscriptionModel `shouldBe` "hospital-capacity-transfer-decisions-sub"
+          subscriptionNameFor "billing" inlineModel `shouldBe` "billing-subscriptions-sub"
+        nodes -> expectationFailure ("expected readmodel nodes, got " <> show (length nodes))
+    it "accepts the positive readmodel fixture with all references resolved" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      validateSpec spec `shouldBe` []
+    it "rejects shape drift and unknown SQL column types" $ do
+      codes <- errorCodesOf "test/fixtures/readmodel-shape-drift.keiro"
+      codes `shouldContain` [RmShapeHashDrift, RmUnknownColumnType]
+    it "rejects Strong on inline and standalone projections" $ do
+      inlineCodes <- errorCodesOf "test/fixtures/readmodel-strong-inline.keiro"
+      inlineCodes `shouldContain` [RmStrongInlineOnly]
+      standalone <- specOf "test/fixtures/readmodel-strong-standalone.keiro"
+      let diagnostics = validateSpec standalone
+      map code diagnostics `shouldContain` [RmStrongInlineOnly, RmProjectionWithoutNode]
+      [severity diagnostic | diagnostic <- diagnostics, code diagnostic == RmProjectionWithoutNode]
+        `shouldBe` [Warning]
+    it "rejects scope without Strong and an unreferenced inline feed" $ do
+      scopeCodes <- errorCodesOf "test/fixtures/readmodel-scope-eventual.keiro"
+      scopeCodes `shouldContain` [RmScopeWithoutStrong]
+      inlineCodes <- errorCodesOf "test/fixtures/readmodel-inline-unreferenced.keiro"
+      inlineCodes `shouldContain` [RmInlineFeedUnreferenced]
+    it "rejects projection consistency conflicts" $ do
+      codes <- errorCodesOf "test/fixtures/readmodel-consistency-conflict.keiro"
+      codes `shouldContain` [RmConsistencyConflict]
+    it "resolves query read models and validates query consistency" $ do
+      codes <- errorCodesOf "test/fixtures/readmodel-query-unresolved.keiro"
+      codes `shouldContain` [QueryUnresolvedReadModel, QueryConsistencyInvalid]
+    it "resolves dispatch read models and declared dedup columns" $ do
+      codes <- errorCodesOf "test/fixtures/readmodel-dispatch-unresolved.keiro"
+      codes `shouldContain` [DispatchReadModelUnresolved, DispatchReadModelFieldUnknown]
+    it "scaffolds runtime records, rebuild helpers, async wiring, and typed holes" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      let ctx = defaultContext (specContext spec)
+          readModels = [readModel | NReadModel readModel <- specNodes spec]
+          modules = concatMap (scaffoldReadModel ctx) readModels
+          transfer = generatedTextEndingIn "Transfer_decisions/ReadModel.hs" modules
+          inline = generatedTextEndingIn "Subscriptions/ReadModel.hs" modules
+          transferHoles = [moduleText m | m <- modules, "Transfer_decisions/ReadModelHoles.hs" `T.isSuffixOf` T.pack (modulePath m)]
+      length modules `shouldBe` 6
+      length [m | m <- modules, kind m == Generated] `shouldBe` 4
+      length [m | m <- modules, kind m == HoleStub] `shouldBe` 2
+      firewallBreaches modules `shouldBe` []
+      transfer `shouldSatisfy` T.isInfixOf "registerTransferDecisions"
+      transfer `shouldSatisfy` T.isInfixOf "Rebuild.startRebuild transferDecisionsReadModel [\"hospital-capacity-transfer-decisions-async\"]"
+      transfer `shouldSatisfy` T.isInfixOf "strongScope = CategoryHead \"reservation\""
+      transfer `shouldSatisfy` T.isInfixOf "transferDecisionsAsyncProjection"
+      inline `shouldSatisfy` T.isInfixOf "Rebuild.startRebuild subscriptionsReadModel []"
+      inline `shouldNotSatisfy` T.isInfixOf "AsyncProjection"
+      transferHoles `shouldSatisfy` any (T.isInfixOf "RecordedEvent -> Tx.Transaction ()")
+    it "threads qualified table and column guidance into aggregate projection holes" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        [aggregate] -> do
+          let modules = scaffoldAggregate (defaultContext (specContext spec)) spec aggregate
+              holes = [moduleText m | m <- modules, kind m == HoleStub]
+              projection = generatedTextEndingIn "Projection.hs" modules
+          holes `shouldSatisfy` any (T.isInfixOf "subscriptionsQualifiedTable")
+          holes `shouldSatisfy` any (T.isInfixOf "Table: \"billing\".\"subscriptions\"")
+          projection `shouldSatisfy` T.isInfixOf "ReadModelTable.subscriptionsQualifiedTable"
+        aggregates -> expectationFailure ("expected one aggregate, got " <> show (length aggregates))
+    it "emits runtime-free derivation facts for each read model" $ do
+      spec <- specOf "test/fixtures/readmodel.keiro"
+      case [readModel | NReadModel readModel <- specNodes spec] of
+        (subscriptionModel : _) -> do
+          let modules = harnessReadModel (defaultContext (specContext spec)) subscriptionModel
+              harnessText = generatedTextEndingIn "ReadModelHarness.hs" modules
+          length modules `shouldBe` 1
+          firewallBreaches modules `shouldBe` []
+          harnessText `shouldSatisfy` T.isInfixOf "(\"shapeHash\", \"fnv1a:3717f6d9e3c44bd6\", \"fnv1a:3717f6d9e3c44bd6\")"
+          harnessText `shouldSatisfy` T.isInfixOf "(\"strongScope\", \"CategoryHead reservation\", \"CategoryHead reservation\")"
+          harnessText `shouldSatisfy` T.isInfixOf "runReadModelFacts"
+        nodes -> expectationFailure ("expected readmodel nodes, got " <> show (length nodes))
+
+  describe "workflow/operation (EP-6)" $ do
+    it "round-trips the workflow spec through parse . pretty" $ do
+      input <- readTestText "test/fixtures/workflow.keiro"
+      case parseSpec "in" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> parseStableRenderedSpec "in" spec `shouldBe` Right spec
+    it "accepts the workflow spec (await<->signal matches, run resolves)" $ do
+      codes <- errorCodesOf "test/fixtures/workflow.keiro"
+      codes `shouldBe` []
+    it "rejects a signal label with no matching await as AwaitSignalMismatch" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-signal-mismatch.keiro"
+      codes `shouldContain` [AwaitSignalMismatch]
+    it "rejects duplicate workflow labels" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-dup-label.keiro"
+      codes `shouldContain` [WorkflowDuplicateLabel]
+    it "rejects unresolved workflow id and sleep fields" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-unresolved-fields.keiro"
+      codes `shouldContain` [WorkflowIdFieldUnresolved, WorkflowSleepDelayUnresolved]
+    it "validates rule domains, totality, case constructors, and bodies" $ do
+      unresolved <- errorCodesOf "test/fixtures/rule-bad-domain.keiro"
+      unresolved `shouldBe` [RuleDomainUnresolved]
+      codes <- errorCodesOf "test/fixtures/rule-not-total.keiro"
+      mapM_
+        (\expected -> codes `shouldContain` [expected])
+        [RuleNotTotal, RuleCaseUnknownCtor, ClockSampled, GuardAtomOutOfScope]
+    it "rejects unresolved command operation references" $ do
+      codes <- errorCodesOf "test/fixtures/operation-ghost-aggregate.keiro"
+      codes `shouldContain` [OperationUnresolvedRef]
+    it "rejects a signal value type that differs from its await" $ do
+      codes <- errorCodesOf "test/fixtures/operation-signal-value.keiro"
+      codes `shouldContain` [AwaitSignalValueMismatch]
+    it "round-trips guarded patches and terminal continueAsNew" $ do
+      input <- readTestText "test/fixtures/workflow-evolution.keiro"
+      case parseSpec "workflow-evolution" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> do
+          parseSpec "workflow-evolution" (renderSpec spec) `shouldBe` Right spec
+          errorCodes spec `shouldBe` []
+    it "rejects duplicate patch ids anywhere in the workflow body" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-patch-dup.keiro"
+      codes `shouldBe` [WorkflowPatchDuplicate]
+    it "rejects non-terminal and nested continueAsNew" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-can-mid.keiro"
+      codes `shouldBe` [WorkflowContinueAsNewNotTerminal, WorkflowContinueAsNewNotTerminal]
+    it "rejects a colon in a patch id with a workflow diagnostic" $ do
+      codes <- errorCodesOf "test/fixtures/workflow-patch-colon.keiro"
+      codes `shouldBe` [WorkflowPatchIdInvalid]
+    it "lowers patch facts and live runtime declarations" $ do
+      spec <- specOf "test/fixtures/workflow-evolution.keiro"
+      case [workflow | NWorkflow workflow <- specNodes spec] of
+        [workflow] -> do
+          let modules = harnessWorkflow (defaultContext (specContext spec)) workflow
+              facts = generatedTextEndingIn "WorkflowFacts.hs" modules
+              runtime = generatedTextEndingIn "WorkflowRuntime.hs" modules
+          facts `shouldSatisfy` T.isInfixOf "patch:fraud-check-v2(step:fraud-check)"
+          facts `shouldSatisfy` T.isInfixOf "continueAsNew:RolloverSeed"
+          facts `shouldSatisfy` T.isInfixOf "data WorkflowFacts = WorkflowFacts"
+          facts `shouldSatisfy` T.isInfixOf "workflowFactBody = [\"step:create-transfer-hold\", \"patch:fraud-check-v2(step:fraud-check)\""
+          facts `shouldSatisfy` T.isInfixOf "workflowFactAwaitLabels = [\"reservation-confirmation\"]"
+          facts `shouldSatisfy` T.isInfixOf "workflowFactPatchIds = [\"fraud-check-v2\"]"
+          runtime `shouldSatisfy` T.isInfixOf "declaredPatches = Set.fromList [PatchId \"fraud-check-v2\"]"
+          runtime `shouldSatisfy` T.isInfixOf "opts{activePatches = declaredPatches}"
+        workflows -> expectationFailure ("expected one workflow, got " <> show (length workflows))
+
+  describe "replay impact" $ do
+    it "treats new events and transitions as replay-neutral" $ do
+      old <- specOf "test/fixtures/reservation.keiro"
+      let aggregate = onlyAggregate old
+      case (aggEvents aggregate, aggTransitions aggregate) of
+        (event : _, transition : _) -> do
+          let newEvent =
+                event
+                  { evName = "ReservationReviewed",
+                    evLoc = noLoc
+                  }
+              newTransition =
+                transition
+                  { tEmits = ["ReservationReviewed"],
+                    tLoc = noLoc
+                  }
+              new =
+                modifyAggregate
+                  "Reservation"
+                  ( \candidate ->
+                      candidate
+                        { aggEvents = aggEvents candidate <> [newEvent],
+                          aggTransitions = aggTransitions candidate <> [newTransition]
+                        }
+                  )
+                  old
+          replayImpactSpecs old new `shouldBe` ReplayNeutral
+        _ -> expectationFailure "reservation fixture must contain an event and transition"
+
+    it "narrows a guard edit to that transition's event types" $ do
+      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened.keiro"
+      impact
+        `shouldBe` ReplayAffected
+          ( Map.singleton
+              "Reservation"
+              AggregateImpact
+                { eventTypes = Set.singleton "TransferReservationCreated",
+                  includeSnapshotStreams = True
+                }
+          )
+
+    it "proves a syntactic guard loosening replay-neutral" $ do
+      old <- specOf "test/fixtures/reservation.keiro"
+      let loosened =
+            modifyAggregate
+              "Reservation"
+              ( \aggregate ->
+                  aggregate
+                    { aggTransitions =
+                        [ transition {tGuard = Nothing}
+                        | transition <- aggTransitions aggregate
+                        ]
+                    }
+              )
+              old
+      replayImpactSpecs old loosened `shouldBe` ReplayNeutral
+
+    it "pairs guard-disambiguated siblings independently of both declaration orders" $ do
+      base <- specOf "test/fixtures/reservation.keiro"
+      let aggregate = onlyAggregate base
+      case (aggTransitions aggregate, aggEvents aggregate) of
+        (prototype : _, firstEvent : secondEvent : _) -> do
+          let sibling guardExpression eventName =
+                prototype
+                  { tGuard = guardExpression,
+                    tEmits = [eventName],
+                    tLoc = noLoc
+                  }
+              commandOverride = EPath noLoc CommandRoot ["lifeCriticalOverride"]
+              exact = sibling (Just (EAtom (ABool True))) (evName firstEvent)
+              loosenedOld = sibling (Just commandOverride) (evName firstEvent)
+              loosenedNew = sibling Nothing (evName firstEvent)
+              changedOld = sibling (Just (ECmp OpEq commandOverride (ELiteral noLoc (LiteralBool False)))) (evName secondEvent)
+              changedNew = sibling (Just (ECmp OpEq commandOverride (ELiteral noLoc (LiteralBool True)))) (evName firstEvent)
+              oldSiblings = [exact, loosenedOld, changedOld]
+              newSiblings = [exact, loosenedNew, changedNew]
+              withTransitions transitions =
+                modifyAggregate
+                  (aggName aggregate)
+                  (\candidate -> candidate {aggTransitions = transitions})
+                  base
+              impacts =
+                [ replayImpactSpecs (withTransitions oldOrder) (withTransitions newOrder)
+                | oldOrder <- permutations oldSiblings,
+                  newOrder <- permutations newSiblings
+                ]
+          case impacts of
+            firstImpact : remainingImpacts -> do
+              remainingImpacts `shouldSatisfy` all (== firstImpact)
+              firstImpact `shouldSatisfy` (/= ReplayNeutral)
+            [] -> expectationFailure "permutations unexpectedly produced no replay comparisons"
+        _ -> expectationFailure "reservation fixture must contain one transition and two events"
+
+    it "marks every existing event when the aggregate wire convention changes" $ do
+      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-wire.keiro"
+      case impact of
+        ReplayAffected aggregates ->
+          ReplayImpact.eventTypes <$> Map.lookup "Reservation" aggregates
+            `shouldBe` Just (Set.fromList ["TransferReservationCreated", "TransferReservationConfirmed"])
+        ReplayNeutral -> expectationFailure "expected a wire-clause replay impact"
+
+    it "includes snapshot streams when a write expression changes" $ do
+      impact <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-foldchange.keiro"
+      case impact of
+        ReplayAffected aggregates ->
+          includeSnapshotStreams <$> Map.lookup "Reservation" aggregates
+            `shouldBe` Just True
+        ReplayNeutral -> expectationFailure "expected a fold replay impact"
+
+    it "detects codec evolution and ignores formatting-only rewrites" $ do
+      changed <- replayImpactFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v2.keiro"
+      changed `shouldSatisfy` (/= ReplayNeutral)
+      old <- specOf "test/fixtures/reservation.keiro"
+      formatted <- shouldParseStableRenderedSpec "<formatted>" old
+      replayImpactSpecs old formatted `shouldBe` ReplayNeutral
+
+    it "names mapped nested event and snapshot roots while ignoring Haskell-only changes" $ do
+      nested <- replayImpactFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-nested-propagation.keiro"
+      case nested of
+        ReplayAffected aggregates ->
+          Map.lookup "Catalog" aggregates
+            `shouldBe` Just AggregateImpact {eventTypes = Set.singleton "ArtifactObserved", includeSnapshotStreams = True}
+        ReplayNeutral -> expectationFailure "expected nested mapped wire change to affect replay"
+      sourceOnly <- replayImpactFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-haskell-rename.keiro"
+      sourceOnly `shouldBe` ReplayNeutral
+
+    it "generates one context target for every aggregate, including the process saga" $ do
+      spec <- specOf "test/fixtures/surge-service.keiro"
+      case scaffoldReplayAudit (defaultContext (specContext spec)) spec of
+        [assembly] -> do
+          modulePath assembly `shouldBe` "Generated/SurgeDemo/ReplayAudit.hs"
+          moduleText assembly `shouldSatisfy` T.isInfixOf "Hospital.hospitalEventStream"
+          moduleText assembly `shouldSatisfy` T.isInfixOf "Surge.surgeEventStream"
+          T.count "      AuditTarget" (moduleText assembly) `shouldBe` 2
+        assemblies -> expectationFailure ("expected one replay-audit assembly, got " <> show (length assemblies))
+
+  describe "diff (evolution classification)" $ do
+    it "covers every node family exactly once and explains exclusions" $ do
+      sort (map fst familyRegistry) `shouldBe` ([minBound .. maxBound] :: [NodeFamily])
+      [reason | (_, OutOfDiffScope reason) <- familyRegistry, T.null reason] `shouldBe` []
+    it "derives every exercised headline from its vector under the default gate" $ do
+      changes <-
+        concat
+          <$> mapM
+            (uncurry diffFixtures)
+            [ ("test/fixtures/reservation.keiro", "test/fixtures/reservation-fieldadd.keiro"),
+              ("test/fixtures/reservation.keiro", "test/fixtures/reservation-v2.keiro"),
+              ("test/fixtures/reservation.keiro", "test/fixtures/reservation-enumadd.keiro"),
+              ("test/fixtures/contract.keiro", "test/fixtures/contract-fieldadd.keiro"),
+              ("test/fixtures/reservation-work.keiro", "test/fixtures/reservation-work-rename.keiro")
+            ]
+      forM_ changes $ \change ->
+        do
+          deriveLabel defaultGate (ckVector (kindOfChange change))
+            `shouldBe` labelOfChange change
+          gatedBreaking defaultGate change `shouldBe` isBreaking change
+    it "never removes a breaking result when the gate grows" $
+      property $
+        forAll genCompatibilityVector $ \compatibility ->
+          forAll genSurfaceSet $ \gate ->
+            forAll genSurfaceSet $ \extra ->
+              deriveLabel gate compatibility
+                == LabelBreaking
+                  ==> deriveLabel (gate <> extra) compatibility
+                == LabelBreaking
+    it "renders the consumer-neutral matrix with separate private, snapshot, and public surfaces" $ do
+      changes <- diffFixtures "test/fixtures/compatibility-vector-old.keiro" "test/fixtures/compatibility-vector-new.keiro"
+      golden <- readTestText "test/fixtures/compatibility-vector.diff.golden"
+      let rendered = T.intercalate "\n" (map renderFinding changes)
+          explained = T.intercalate "\n" (map renderExplainBlock changes)
+          reportJson = T.pack (show (Aeson.toJSON (diffReport defaultGate changes)))
+      T.stripEnd rendered `shouldBe` T.stripEnd golden
+      rendered `shouldSatisfy` T.isInfixOf "Reservation.event.TransferReservationCreated.patientAcuity"
+      rendered `shouldSatisfy` T.isInfixOf "old-binary-read-new-events=breaking"
+      rendered `shouldSatisfy` T.isInfixOf "snapshot-hydration=advisory"
+      rendered `shouldSatisfy` T.isInfixOf "public-consumer=breaking"
+      explained `shouldSatisfy` T.isInfixOf "invalidate and rebuild snapshots"
+      reportJson `shouldSatisfy` T.isInfixOf "keiro-dsl/diff-report/1"
+      reportJson `shouldSatisfy` T.isInfixOf "Reservation.event.TransferReservationCreated.patientAcuity"
+      let eventEnumFindings =
+            [ change
+            | change@(Advisory kind) <- changes,
+              ckCode kind == EnumCtorAdded,
+              verdictFor OldBinaryReadNewEvents (ckVector kind) == VBreaking
+            ]
+      eventEnumFindings `shouldSatisfy` all (not . gatedBreaking defaultGate)
+      eventEnumFindings `shouldSatisfy` all (gatedBreaking (gateWith [OldBinaryReadNewEvents]))
+      forM_ changes $ \change ->
+        remediationFor (ckContext (kindOfChange change)) (ckCode (kindOfChange change))
+          `shouldSatisfy` (not . null)
+    it "rejects unknown --gate values with the valid surface list" $ do
+      parseSurfaceName "mystery-surface"
+        `shouldSatisfy` either (T.isInfixOf "old-binary-read-new-events" . T.pack) (const False)
+    it "covers the mapped evolution matrix with stable codes and non-empty remedies" $ do
+      let cases =
+            [ ("consumer-types-fieldadd-default.keiro", MappedFieldAddedWithDefault),
+              ("consumer-types-fieldadd-nodefault.keiro", MappedFieldAddedNoDefault),
+              ("consumer-types-fieldremove.keiro", MappedFieldRemoved),
+              ("consumer-types-wirekey.keiro", MappedWireKeyChanged),
+              ("consumer-types-haskell-rename.keiro", MappedHaskellSourceChanged),
+              ("consumer-types-binding-change.keiro", MappedBindingChanged),
+              ("consumer-types-fixtures-change.keiro", MappedFixturesChanged),
+              ("consumer-types-initial-change.keiro", MappedInitialChanged),
+              ("consumer-types-armadd.keiro", MappedArmAdded),
+              ("consumer-types-tagchange.keiro", MappedArmTagChanged),
+              ("consumer-types-enumadd.keiro", MappedEnumValueAdded),
+              ("consumer-types-enumremove.keiro", MappedEnumValueRemoved),
+              ("consumer-types-enumspelling.keiro", MappedEnumSpellingChanged),
+              ("consumer-types-encoding.keiro", MappedUnionEncodingChanged),
+              ("consumer-types-opaque-version.keiro", MappedOpaqueCodecChanged),
+              ("consumer-types-mode-cross.keiro", MappedModeCrossed),
+              ("consumer-types-nested-propagation.keiro", MappedArmTagChanged)
+            ]
+      forM_ cases $ \(fixture, expectedCode) -> do
+        changes <- diffFixtures "test/fixtures/consumer-types.keiro" ("test/fixtures/" <> fixture)
+        map (ckCode . kindOfChange) changes `shouldContain` [expectedCode]
+        forM_ changes $ \change ->
+          remediationFor (ckContext (kindOfChange change)) (ckCode (kindOfChange change))
+            `shouldSatisfy` (not . null)
+    it "separates mapped event migration, snapshot invalidation, and directional rollout" $ do
+      breakingAdd <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-fieldadd-nodefault.keiro"
+      let noDefault = [change | change <- breakingAdd, ckCode (kindOfChange change) == MappedFieldAddedNoDefault]
+      [ckFacet kind | Breaking kind <- noDefault] `shouldContain` ["mapped-event"]
+      [ckFacet kind | Advisory kind <- noDefault] `shouldContain` ["mapped-register"]
+      defaulted <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-fieldadd-default.keiro"
+      [change | change <- defaulted, isBreaking change] `shouldBe` []
+      let eventDefaults = [kind | Advisory kind <- defaulted, ckCode kind == MappedFieldAddedWithDefault, ckFacet kind == "mapped-event"]
+      eventDefaults `shouldSatisfy` any ((== VBreaking) . verdictFor OldBinaryReadNewEvents . ckVector)
+      armAdded <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-armadd.keiro"
+      [change | change <- armAdded, isBreaking change] `shouldBe` []
+      [kind | Advisory kind <- armAdded, ckCode kind == MappedArmAdded, ckFacet kind == "mapped-event"]
+        `shouldSatisfy` any ((== VBreaking) . verdictFor OldBinaryReadNewEvents . ckVector)
+    it "propagates a nested mapped leaf to complete command, event, and register paths" $ do
+      changes <- diffFixtures "test/fixtures/consumer-types.keiro" "test/fixtures/consumer-types-nested-propagation.keiro"
+      let subjects =
+            [ ckSubject kind
+            | change <- changes,
+              let kind = kindOfChange change,
+              ckCode kind == MappedArmTagChanged
+            ]
+      subjects
+        `shouldContain` [ "Catalog command ObserveArtifact .artifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]",
+                          "Catalog event ArtifactObserved .artifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]",
+                          "Catalog register currentArtifact : ArtifactInfo .location : ArtifactLocation .arm RepoPath[\"repository_path\"]"
+                        ]
+    it "classifies every remaining mapped field and declaration evolution row" $ do
+      base <- specOf "test/fixtures/consumer-types.keiro"
+      let mutationCodes =
+            [ (mapArtifactNamedField "key" (\field -> field {wfType = TInt}) base, MappedFieldTypeChanged),
+              (mapArtifactNamedField "key" (\field -> field {wfPresence = POptional, wfOnMissing = Just (OmText "")}) base, MappedPresenceChanged),
+              (mapArtifactNamedField "key" (\field -> field {wfType = TOptional TText}) base, MappedNullabilityChanged),
+              (mapArtifactNamedField "description" (\field -> field {wfOnMissing = Nothing}) base, MappedDefaultRemoved),
+              (mapArtifactNamedField "count" (\field -> field {wfOnMissing = Just (OmInt 1)}) base, MappedDefaultChanged),
+              (mapMappedStructural "ArtifactInfo" renameMappedRecordConstructor base, MappedRecordConstructorChanged),
+              (mapMappedStructural "ArtifactInfo" changeMappedCanonical base, MappedCanonicalTypeChanged)
+            ]
+      forM_ mutationCodes $ \(candidate, expectedCode) ->
+        map (ckCode . kindOfChange) (diffSpecs base candidate) `shouldContain` [expectedCode]
+      let declarationA = completeStructural "A" (recordShape [TText])
+          declarationB = completeStructural "B" (recordShape [TInt])
+          onlyA = mappedSpec [declarationA]
+          withB = mappedSpec [declarationA, declarationB]
+      map (ckCode . kindOfChange) (diffSpecs onlyA withB) `shouldContain` [MappedDeclAdded]
+      map (ckCode . kindOfChange) (diffSpecs withB onlyA) `shouldContain` [MappedDeclRemoved]
+      diffSpecs base (mapArtifactNamedField "key" (\field -> field {wfHaskell = "renamedKey"}) base)
+        `shouldBe` []
+    it "visits every mapped wire mutation and reports every complete root path" $ do
+      base <- specOf "test/fixtures/consumer-types.keiro"
+      let mutations = mappedWireMutations base
+      mutations `shouldSatisfy` (not . null)
+      visited <- fmap Set.unions . forM mutations $ \mutation -> do
+        let changes =
+              [ change
+              | change <- diffSpecs base (mmCandidate mutation),
+                ckCode (kindOfChange change) == mmCode mutation
+              ]
+            actualSubjects = Set.fromList (map (ckSubject . kindOfChange) changes)
+        changes `shouldSatisfy` any (not . isAdditiveChange)
+        actualSubjects `shouldBe` mmExpectedSubjects mutation
+        pure actualSubjects
+      visited `shouldBe` Set.unions (map mmExpectedSubjects mutations)
+    it "reports the exact ingredient code when every required mapped fact is deleted" $ do
+      base <- specOf "test/fixtures/consumer-types.keiro"
+      forM_ (mappedIngredientMutations base) $ \(candidate, expectedCode) ->
+        errorCodes candidate `shouldContain` [expectedCode]
+    it "classifies a field added without a version bump as BREAKING" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldadd.keiro"
+      any isBreaking cs `shouldBe` True
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldAddedWithoutBump]
+    it "classifies the same field wrapped as v2 + upcaster as ADDITIVE" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v2.keiro"
+      any isBreaking cs `shouldBe` False
+      [ck | Additive ck <- cs] `shouldSatisfy` any ((== "TransferReservationCreated") . ckSubject)
+    it "reports no breaking change when the spec is unchanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation.keiro"
+      any isBreaking cs `shouldBe` False
+    it "classifies a direct event field type change as EvtFieldTypeChanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldtype.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldTypeChanged]
+    it "resolves fields(Command) before comparing event field types" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-cmdfieldtype.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldTypeChanged]
+    it "uses EvtFieldRemovedSameVersion for an unchanged-version removal" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-fieldremove.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtFieldRemovedSameVersion]
+    it "uses EvtVersionDecreased for a version decrease" $ do
+      cs <- diffFixtures "test/fixtures/reservation-v2.keiro" "test/fixtures/reservation.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtVersionDecreased]
+    it "rejects a v1 to v3 jump whose only upcaster starts at v2" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-v3-dangling.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EvtVersionMissingUpcaster]
+    it "classifies a vanished historical upcaster rung as UpcasterChainGap" $ do
+      cs <- diffFixtures "test/fixtures/reservation-v2.keiro" "test/fixtures/reservation-chain-gap.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [UpcasterChainGap]
+    it "classifies an enum constructor removal as EnumCtorRemoved" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumdrop.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EnumCtorRemoved]
+    it "classifies an enum wire-spelling change as EnumWireSpellingChanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumwire.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [EnumWireSpellingChanged]
+    it "classifies an enum constructor addition per use site as advisory" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-enumadd.keiro"
+      any isBreaking cs `shouldBe` False
+      let enumFindings = [k | Advisory k <- cs, ckCode k == EnumCtorAdded]
+      [ckSubject k | k <- enumFindings] `shouldContain` ["BlackTag"]
+      [verdictFor SnapshotHydration (ckVector k) | k <- enumFindings]
+        `shouldContain` [VAdvisory]
+    it "classifies an effective wire convention change as WireSpecChanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-wire.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WireSpecChanged]
+    it "advises when the aggregate fold surface changes" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-foldchange.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [AggFoldSurfaceChanged]
+    it "advises on hazardous deprecation and reports un-deprecation" $ do
+      deprecated <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-deprecated.keiro"
+      any isBreaking deprecated `shouldBe` False
+      [ckCode k | Advisory k <- deprecated] `shouldContain` [DeprecatedEventReplayHazard]
+      restored <- diffFixtures "test/fixtures/reservation-deprecated.keiro" "test/fixtures/reservation.keiro"
+      any isAdvisory restored `shouldBe` True
+      [ckCode k | Advisory k <- restored] `shouldContain` [EventUndeprecated]
+    it "recognises replay-only deprecation as a replay-safe retirement cutover" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-deprecated-replay-only.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [EventRetirementInProgress]
+      [ckCode k | Advisory k <- cs] `shouldNotContain` [DeprecatedEventReplayHazard]
+    it "advises when event retirement starts" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-retiring.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [EventRetirementInProgress]
+    it "does not recommend decode-only deprecation for an event removal" $ do
+      old <- specOf "test/fixtures/reservation.keiro"
+      let new =
+            old
+              { specNodes =
+                  [ case node of
+                      NAggregate aggregate ->
+                        NAggregate
+                          aggregate
+                            { aggEvents =
+                                [ event
+                                | event <- aggEvents aggregate,
+                                  evName event /= "TransferReservationConfirmed"
+                                ],
+                              aggTransitions =
+                                [ transition {tEmits = filter (/= "TransferReservationConfirmed") (tEmits transition)}
+                                | transition <- aggTransitions aggregate
+                                ]
+                            }
+                      _ -> node
+                  | node <- specNodes old
+                  ]
+              }
+          removals = [change | change@(Breaking kind) <- diffSpecs old new, ckCode kind == EvtRemovedNotDeprecated]
+      removals `shouldSatisfy` (not . null)
+      [ckDetail kind | Breaking kind <- removals]
+        `shouldSatisfy` all (not . T.isInfixOf "so old payloads still decode")
+    it "prints a paste-ready replay-only twin when a guard tightens (plan 143)" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened.keiro"
+      any isBreaking cs `shouldBe` False
+      let advisories = [k | Advisory k <- cs, ckCode k == AggGuardTightened]
+      map ckSubject advisories `shouldBe` ["Unrequested -- RequestTransferReservation"]
+      detail <- case advisories of
+        [k] -> pure (ckDetail k)
+        other -> expectationFailure ("expected one advisory, got " <> show other) >> pure ""
+      detail `shouldSatisfy` T.isInfixOf "replay-only Unrequested -- RequestTransferReservation"
+      -- The printed twin is paste-ready: appended to the new spec it
+      -- parses, validates without errors, and silences the advisory.
+      tightened <- readTestText "test/fixtures/reservation-guard-tightened.keiro"
+      let twinText = snd (T.breakOnEnd "\n\n" detail)
+          pasted = tightened <> "\n" <> twinText <> "\n"
+      case parseSpec "<pasted-twin>" pasted of
+        Left err -> expectationFailure (T.unpack err)
+        Right pastedSpec -> do
+          [code d | d <- validateSpec pastedSpec, severity d == Error] `shouldBe` []
+          base <- specOf "test/fixtures/reservation.keiro"
+          [k | Advisory k <- diffSpecs base pastedSpec, ckCode k == AggGuardTightened]
+            `shouldBe` []
+    it "omits the twin advisory when the twin is already present (plan 143)" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-guard-tightened-twin.keiro"
+      [k | Advisory k <- cs, ckCode k == AggGuardTightened] `shouldBe` []
+    it "classifies a removed contract event as ContractEventRemoved" $ do
+      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-eventdrop.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [ContractEventRemoved]
+    it "classifies contract field type changes and unversioned additions as ContractFieldChanged" $ do
+      changed <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-fieldtype.keiro"
+      [ckCode k | Breaking k <- changed] `shouldContain` [ContractFieldChanged]
+      added <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-fieldadd.keiro"
+      [ckCode k | Breaking k <- added] `shouldContain` [ContractFieldChanged]
+    it "goldens language-3 to language-4 contract TypeID admission and rollout" $ do
+      let source versionNumber prefix =
+            T.unlines
+              [ "language keiro-dsl " <> T.pack (show versionNumber),
+                "context hospital-capacity",
+                "contract emergency {",
+                "  schemaVersion 1",
+                "  discriminator messageType",
+                "  topic incidentEvents \"emergency.incident.events\"",
+                "  event IncidentTransferNeedDeclared on incidentEvents {",
+                "    incidentId: typeid \"" <> prefix <> "\"",
+                "  }",
+                "}"
+              ]
+          checked name input = case parseSource name input of
+            Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+            Right parsed -> pure (checkedSource parsed)
+      v1 <- checked "contract-typeid-v1.keiro" (source (1 :: Int) "inc")
+      v3 <- checked "contract-typeid-v3.keiro" (source (3 :: Int) "inc")
+      v4 <- checked "contract-typeid-v4.keiro" (source (4 :: Int) "inc")
+      v4Edited <- checked "contract-typeid-v4-edited.keiro" (source (4 :: Int) "rsv")
+      let changes = diffServices v3 v4
+          textGolden = T.intercalate "\n" (map renderFinding changes)
+          jsonGolden = LazyText.toStrict (LazyTextEncoding.decodeUtf8 (Aeson.encode (diffReport defaultGate changes)))
+          findings = [kind | Breaking kind <- changes, ckCode kind == ContractTypeIdDomainChanged]
+      expectedText <- readTestText "test/fixtures/contract-typeid-domain.diff.golden"
+      expectedJson <- readTestText "test/fixtures/contract-typeid-domain.diff.json.golden"
+      T.stripEnd textGolden `shouldBe` T.stripEnd expectedText
+      T.stripEnd jsonGolden `shouldBe` T.stripEnd expectedJson
+      case findings of
+        [finding] -> do
+          verdictFor PublicConsumer (ckVector finding) `shouldBe` VBreaking
+          verdictFor ConsumerBuild (ckVector finding) `shouldBe` VBreaking
+          [verdictFor surface (ckVector finding) | surface <- [PrivateHistoryRead, OldBinaryReadNewEvents, SnapshotHydration, PersistedIdentity]]
+            `shouldBe` replicate 4 VNotApplicable
+          cvRollout (ckVector finding) `shouldBe` Set.fromList [RolloutDrainRequired, RolloutProducerFirst]
+          deriveLabel (Set.singleton PublicConsumer) (ckVector finding) `shouldBe` LabelBreaking
+          deriveLabel (Set.singleton ConsumerBuild) (ckVector finding) `shouldBe` LabelBreaking
+          remediationFor (ckContext finding) (ckCode finding)
+            `shouldBe` RemedyEmitContractTypeIdDomain :| [RemedyDrainLegacyInvalidContractMessages, RemedyRescaffoldContractConsumers, RemedyRunContractConformance]
+        values -> expectationFailure ("expected one contract TypeID-domain finding, got " <> show (length values))
+      [kind | change <- diffServices v1 v3, let { kind = kindOfChange change }, ckCode kind == ContractTypeIdDomainChanged] `shouldBe` []
+      [kind | change <- diffServices v4 v4, let { kind = kindOfChange change }, ckCode kind == ContractTypeIdDomainChanged] `shouldBe` []
+      let edited = diffServices v3 v4Edited
+      map (ckCode . kindOfChange) edited `shouldContain` [ContractFieldChanged]
+      [kind | change <- edited, let { kind = kindOfChange change }, ckCode kind == ContractTypeIdDomainChanged] `shouldBe` []
+    it "reports a field addition with a contract version bump as an advisory" $ do
+      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-bump-fieldadd.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [ContractSchemaVersionBumped]
+    it "classifies a contract schema version decrease separately" $ do
+      cs <- diffFixtures "test/fixtures/contract-bump-fieldadd.keiro" "test/fixtures/contract.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [ContractSchemaVersionDecreased]
+    it "classifies contract topic and discriminator changes separately" $ do
+      topic <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-topic.keiro"
+      [ckCode k | Breaking k <- topic] `shouldContain` [ContractTopicChanged]
+      discriminator <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-discriminator.keiro"
+      [ckCode k | Breaking k <- discriminator] `shouldContain` [ContractDiscriminatorChanged]
+    it "classifies a new contract event as additive" $ do
+      cs <- diffFixtures "test/fixtures/contract.keiro" "test/fixtures/contract-eventadd.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckSubject k | Additive k <- cs] `shouldContain` ["IncidentTransferNeedCancelled"]
+    it "classifies workqueue wire names, types, and required additions as WqPayloadFieldChanged" $ do
+      wire <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-wirename.keiro"
+      [ckCode k | Breaking k <- wire] `shouldContain` [WqPayloadFieldChanged]
+      fieldTypeChange <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-fieldtype.keiro"
+      [ckCode k | Breaking k <- fieldTypeChange] `shouldContain` [WqPayloadFieldChanged]
+      required <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-reqfield.keiro"
+      [ckCode k | Breaking k <- required] `shouldContain` [WqPayloadFieldChanged]
+    it "classifies a new optional workqueue payload field as additive" $ do
+      cs <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-optfield.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckSubject k | Additive k <- cs] `shouldContain` ["note"]
+    it "classifies workqueue ordering changes as breaking delivery-contract changes" $ do
+      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-ordering-change.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WqOrderingChanged]
+      [ckDetail k | Breaking k <- cs, ckCode k == WqOrderingChanged]
+        `shouldSatisfy` any (T.isInfixOf "delivery-order contract")
+    it "classifies workqueue provision changes as operational migrations" $ do
+      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-provision-change.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WqProvisionChanged]
+      [ckDetail k | Breaking k <- cs, ckCode k == WqProvisionChanged]
+        `shouldSatisfy` any (T.isInfixOf "migrate the existing queue operationally")
+    it "classifies workqueue group-key changes as breaking repartitioning" $ do
+      cs <- diffFixtures "test/fixtures/workqueue-policy-base.keiro" "test/fixtures/workqueue-group-key-change.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WqGroupKeyChanged]
+      [ckDetail k | Breaking k <- cs, ckCode k == WqGroupKeyChanged]
+        `shouldSatisfy` any (T.isInfixOf "re-partitioned")
+    it "classifies a process input type change as ProcessInputChanged" $ do
+      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-inputtype.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [ProcessInputChanged]
+    it "classifies workflow input and output changes as WorkflowShapeChanged" $ do
+      input <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-inputfield.keiro"
+      [ckCode k | Breaking k <- input] `shouldContain` [WorkflowShapeChanged]
+      output <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-output.keiro"
+      [ckCode k | Breaking k <- output] `shouldContain` [WorkflowShapeChanged]
+    it "classifies workflow relabeling and appends as WorkflowBodyChanged" $ do
+      relabeled <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-body.keiro"
+      [ckCode k | Breaking k <- relabeled] `shouldContain` [WorkflowBodyChanged]
+      appended <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-stepadd.keiro"
+      [ckCode k | Breaking k <- appended] `shouldContain` [WorkflowBodyChanged]
+      [ckDetail k | Breaking k <- appended, ckCode k == WorkflowBodyChanged]
+        `shouldSatisfy` any (T.isInfixOf "new patch guard")
+    it "classifies a body addition wholly guarded by a new patch as additive" $ do
+      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-evolution-diff.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckSubject k | Additive k <- cs, ckFacet k == "workflow-patch"] `shouldContain` ["fraud-check-v2"]
+      [ckSubject k | Additive k <- cs, ckFacet k == "workflow-continue-as-new"] `shouldContain` ["RolloverSeed"]
+    it "classifies removing an existing patch as breaking" $ do
+      cs <- diffFixtures "test/fixtures/workflow-evolution-diff.keiro" "test/fixtures/workflow-continue.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WorkflowPatchRemoved]
+      [ckDetail k | Breaking k <- cs, ckCode k == WorkflowPatchRemoved]
+        `shouldSatisfy` any (T.isInfixOf "cannot prove")
+    it "classifies terminal continueAsNew append as additive and seed drift as breaking" $ do
+      appended <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-continue.keiro"
+      any isBreaking appended `shouldBe` False
+      [ckFacet k | Additive k <- appended] `shouldContain` ["workflow-continue-as-new"]
+      changed <- diffFixtures "test/fixtures/workflow-continue.keiro" "test/fixtures/workflow-continue-seed-v2.keiro"
+      [ckCode k | Breaking k <- changed] `shouldContain` [WorkflowContinueSeedChanged]
+      [ckDetail k | Breaking k <- changed, ckCode k == WorkflowContinueSeedChanged]
+        `shouldSatisfy` any (T.isInfixOf "restoreSeed")
+    it "classifies a workflow stable-name change as WorkflowStableNameChanged" $ do
+      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-rename.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [WorkflowStableNameChanged]
+    it "classifies workflow id-derivation changes as DerivedIdentityChanged" $ do
+      cs <- diffFixtures "test/fixtures/workflow.keiro" "test/fixtures/workflow-idfield.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [DerivedIdentityChanged]
+    it "classifies an id prefix change as IdPrefixChanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-idprefix.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [IdPrefixChanged]
+    it "classifies intake dedupe key and policy changes as DedupeIdentityChanged" $ do
+      policy <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-dedupepolicy.keiro"
+      [ckCode k | Breaking k <- policy] `shouldContain` [DedupeIdentityChanged]
+      key <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-dedupekey.keiro"
+      [ckCode k | Breaking k <- key] `shouldContain` [DedupeIdentityChanged]
+    it "reports intake decode-posture changes as warnings" $ do
+      cs <- diffFixtures "test/fixtures/intake.keiro" "test/fixtures/intake-decode.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [DecodePostureChanged]
+      [ckCode k | Advisory k <- cs] `shouldContain` [IntakePersistenceChanged]
+    it "classifies process and timer derivation changes as DerivedIdentityChanged" $ do
+      processName <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-procname.keiro"
+      [ckCode k | Breaking k <- processName] `shouldContain` [DerivedIdentityChanged]
+      timerId <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-timerid.keiro"
+      [ckCode k | Breaking k <- timerId] `shouldContain` [DerivedIdentityChanged]
+      base <- specOf "test/fixtures/hospital-surge.keiro"
+      let categoryChange = diffSpecs base (modifyProcess "HospitalSurge" (\process -> process {procSaga = (procSaga process) {sagaCategory = "hospitalSurgeV2"}}) base)
+      [ckCode k | Breaking k <- categoryChange] `shouldContain` [DerivedIdentityChanged]
+    it "classifies router stable names, keys, and targets as identity-bearing" $ do
+      base <- specOf "test/fixtures/incident-paging/incident-paging.keiro"
+      let stableName = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtName = "paging-v2"}) base)
+          keyDerivation = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtKey = (rtKey router) {corrVia = "otherIdText"}}) base)
+          target = diffSpecs base (modifyRouter "PagingRouter" (\router -> router {rtTarget = "OtherPage"}) base)
+      [ckCode k | Breaking k <- stableName] `shouldContain` [RouterStableNameChanged]
+      [ckCode k | Breaking k <- keyDerivation] `shouldContain` [DerivedIdentityChanged]
+      [ckCode k | Breaking k <- target] `shouldContain` [DerivedIdentityChanged]
+    it "advises on router dispatch-surface changes without making them breaking" $ do
+      cs <- diffFixtures "test/fixtures/incident-paging/incident-paging.keiro" "test/fixtures/incident-paging/incident-paging-dispatch.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldBe` [RouterDecideSurfaceChanged]
+    it "advises on process dispatch-surface changes without making them breaking" $ do
+      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-handle.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldBe` [ProcessDecideSurfaceChanged]
+    it "advises on unversioned timer payload changes without making them breaking" $ do
+      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-payload.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldBe` [ProcessTimerPayloadChanged]
+    it "ignores formatting-only process and timer surface rewrites" $ do
+      original <- specOf "test/fixtures/hospital-surge.keiro"
+      formatted <- shouldParseStableRenderedSpec "<formatted-process>" original
+      diffSpecs original formatted `shouldBe` []
+    it "reports a timer window change as a warning" $ do
+      cs <- diffFixtures "test/fixtures/hospital-surge.keiro" "test/fixtures/hospital-surge-window.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [TimerWindowChanged]
+    it "reports emit-map changes as warnings and derive changes as breaking" $ do
+      mapping <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-mapchange.keiro"
+      any isBreaking mapping `shouldBe` False
+      [ckCode k | Advisory k <- mapping] `shouldContain` [EmitMappingChanged]
+      derive <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-derive.keiro"
+      [ckCode k | Breaking k <- derive] `shouldContain` [DerivedIdentityChanged]
+    it "classifies publisher outbox identity and ordering independently" $ do
+      outbox <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-outboxfield.keiro"
+      [ckCode k | Breaking k <- outbox] `shouldContain` [DerivedIdentityChanged]
+      ordering <- diffFixtures "test/fixtures/emit.keiro" "test/fixtures/emit-ordering.keiro"
+      any isBreaking ordering `shouldBe` False
+      [ckCode k | Advisory k <- ordering] `shouldContain` [PublisherPolicyChanged]
+    it "classifies workqueue names as QueueIdentityChanged" $ do
+      cs <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-rename.keiro"
+      [ckCode k | Breaking k <- cs] `shouldContain` [QueueIdentityChanged]
+    it "classifies pgmq dispatch dedupe and retargeting independently" $ do
+      dedupe <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-dedupkey.keiro"
+      [ckCode k | Breaking k <- dedupe] `shouldContain` [DedupeIdentityChanged]
+      retarget <- diffFixtures "test/fixtures/reservation-work.keiro" "test/fixtures/reservation-work-retarget.keiro"
+      any isBreaking retarget `shouldBe` False
+      [ckCode k | Advisory k <- retarget] `shouldContain` [DispatchRetargeted]
+    it "reports aggregate projection changes as warnings" $ do
+      cs <- diffFixtures "test/fixtures/reservation.keiro" "test/fixtures/reservation-projection.keiro"
+      any isBreaking cs `shouldBe` False
+      [ckCode k | Advisory k <- cs] `shouldContain` [ProjectionChanged]
+    it "classifies read-model version and unversioned shape changes" $ do
+      base <- specOf "test/fixtures/readmodel-runtime.keiro"
+      let versionTwo = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmVersion = 2}) base
+          changedShape = modifyReadModel "transfer_decisions" changeReadModelShape base
+          bumpedShape = modifyReadModel "transfer_decisions" (\readModel -> (changeReadModelShape readModel) {rmVersion = 2}) base
+          decreased = diffSpecs versionTwo base
+          unversioned = diffSpecs base changedShape
+          bumped = diffSpecs base bumpedShape
+      [ckCode k | Breaking k <- decreased] `shouldContain` [ReadModelVersionDecreased]
+      [ckCode k | Breaking k <- unversioned] `shouldContain` [ReadModelShapeChangedWithoutBump]
+      any isBreaking bumped `shouldBe` False
+      [ckFacet k | Additive k <- bumped] `shouldContain` ["read-model-version"]
+    it "classifies read-model registry, table, subscription, and removal identities" $ do
+      base <- specOf "test/fixtures/readmodel-runtime.keiro"
+      let tableChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmTable = "transfer_decisions_v2"}) base
+          subscriptionChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmSubscription = Just "transfer-decisions-v2"}) base
+          renamed = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmName = "reservation_decisions"}) base
+          removed = removeReadModel "transfer_decisions" base
+      mapM_
+        (\changes -> [ckCode k | Breaking k <- changes] `shouldContain` [DerivedIdentityChanged])
+        [diffSpecs base tableChanged, diffSpecs base subscriptionChanged, diffSpecs base renamed, diffSpecs base removed]
+    it "classifies read-model feed flips and consistency/scope weakening as breaking" $ do
+      base <- specOf "test/fixtures/readmodel-runtime.keiro"
+      let feedChanged = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmFeed = RmInline}) base
+          consistencyWeakened = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmConsistency = Eventual}) base
+          entireLog = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmScope = Just RmEntireLog}) base
+      [ckCode k | Breaking k <- diffSpecs base feedChanged] `shouldContain` [ReadModelFeedChanged]
+      [ckCode k | Breaking k <- diffSpecs base consistencyWeakened] `shouldContain` [ReadModelConsistencyWeakened]
+      [ckCode k | Breaking k <- diffSpecs entireLog base] `shouldContain` [ReadModelConsistencyWeakened]
+    it "classifies Eventual to Strong read-model consistency as additive" $ do
+      strong <- specOf "test/fixtures/readmodel-runtime.keiro"
+      let eventual = modifyReadModel "transfer_decisions" (\readModel -> readModel {rmConsistency = Eventual}) strong
+          changes = diffSpecs eventual strong
+      any isBreaking changes `shouldBe` False
+      [ckFacet k | Additive k <- changes] `shouldContain` ["read-model-consistency"]
+
+  describe "module placement (M1)" $ do
+    it "GeneratedPrefix is today's namespace (Generated.<Ctx>.<Node>, holes at <Ctx>.<Node>)" $ do
+      let ctx = defaultContext "hospital-capacity"
+      genPrefixFor ctx "Reservation" `shouldBe` "Generated.HospitalCapacity.Reservation"
+      holePrefixFor ctx "Reservation" `shouldBe` "HospitalCapacity.Reservation"
+    it "module-root prefixes both layers" $ do
+      let ctx = (defaultContext "hospital-capacity") {moduleRoot = "Acme"}
+      genPrefixFor ctx "Reservation" `shouldBe` "Acme.Generated.HospitalCapacity.Reservation"
+      holePrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation"
+    it "CollocatedLeaf places the generated layer under the domain leaf" $ do
+      let ctx = (defaultContext "hospital-capacity") {moduleRoot = "Acme", placement = CollocatedLeaf}
+      genPrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation.Generated"
+      holePrefixFor ctx "Reservation" `shouldBe` "Acme.HospitalCapacity.Reservation"
+    it "parses and preserves the module/layout clauses through parse . pretty" $ do
+      let src = "context hospital-capacity\nmodule Acme.Services\nlayout collocated\n\naggregate Reservation\n  regs\n  states Open\n"
+      case parseSpec "<m1>" src of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> do
+          specModuleRoot spec `shouldBe` Just "Acme.Services"
+          specLayout spec `shouldBe` Just CollocatedLeaf
+          parseSpec "<m1>" (renderSpec spec) `shouldBe` Right spec
+    it "a spec without the clauses leaves placement at the default" $ do
+      input <- readTestText "test/fixtures/reservation.keiro"
+      case parseSpec "test/fixtures/reservation.keiro" input of
+        Left err -> expectationFailure (T.unpack err)
+        Right spec -> do
+          specModuleRoot spec `shouldBe` Nothing
+          specLayout spec `shouldBe` Nothing
+
+  describe "structural scaffold" $ do
+    it "emits one private shape module per structural declaration and one context facade" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          paths = map modulePath modules
+      paths
+        `shouldContain` [ "Generated/ConsumerDemo/Structural/Shape/ArtifactInfo.hs",
+                          "Generated/ConsumerDemo/Structural/Shape/ArtifactKind.hs",
+                          "Generated/ConsumerDemo/Structural/Shape/ArtifactLocation.hs",
+                          "Generated/ConsumerDemo/StructuralProjections.hs"
+                        ]
+      paths `shouldNotContain` ["Generated/ConsumerDemo/Structural/Shape/VendorGeometry.hs"]
+      firewallBreaches modules `shouldBe` []
+    it "emits one create-once binding skeleton per owning module and derives Generic for private shapes" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          skeletons = [moduleValue | moduleValue <- modules, kind moduleValue == HoleStub, modulePath moduleValue == "Example/Artifact/KeiroBindings.hs"]
+          shape = generatedTextEndingIn "Structural/Shape/ArtifactInfo.hs" modules
+      case skeletons of
+        [skeleton] -> do
+          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactInfoBinding :: StructuralBinding"
+          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactKindBinding :: StructuralBinding"
+          moduleText skeleton `shouldSatisfy` T.isInfixOf "artifactLocationBinding :: StructuralBinding"
+          moduleText skeleton `shouldSatisfy` T.isInfixOf "HOLE: fill ArtifactInfo bindingToShape.key"
+        _ -> expectationFailure ("expected exactly one shared binding skeleton, got " <> show (map modulePath skeletons))
+      shape `shouldSatisfy` T.isInfixOf "deriving stock (Eq, Generic, Show)"
+      shape `shouldSatisfy` T.isInfixOf "import GHC.Generics (Generic)"
+    it "never overwrites an existing binding skeleton" $
+      withTempDirectory "keiro-dsl-binding-create-once" $ \out -> do
+        spec <- specOf "test/fixtures/consumer-types.keiro"
+        let ctx = defaultContext (specContext spec)
+            bindingPath = out </> "Example/Artifact/KeiroBindings.hs"
+        _ <- executePlannedScaffold out "consumer-types.keiro" ctx spec
+        TIO.writeFile bindingPath "hand-owned binding\n"
+        second <- executePlannedScaffold out "consumer-types.keiro" ctx spec
+        TIO.readFile bindingPath `shouldReturn` "hand-owned binding\n"
+        reportDispositions second
+          `shouldSatisfy` any (\(moduleValue, disposition) -> modulePath moduleValue == "Example/Artifact/KeiroBindings.hs" && disposition == Skipped)
+    it "fresh binding skeletons compile at the application boundary" $
+      withTempDirectory "keiro-dsl-binding-compiles" $ \out -> do
+        spec <- specOf "test/fixtures/structural-conformance.keiro"
+        let ctx = defaultContext (specContext spec)
+            bindingSource = out </> "Conformance/Structural/Bindings.hs"
+            ghcOutput = out </> ".ghc"
+        _ <- executePlannedScaffold out "structural-conformance.keiro" ctx spec
+        createDirectoryIfMissing True ghcOutput
+        (exitCode, standardOutput, standardError) <-
+          readProcessWithExitCode
+            "cabal"
+            [ "exec",
+              "--",
+              "ghc",
+              "-XGHC2024",
+              "-XOverloadedStrings",
+              "-fno-code",
+              "-fforce-recomp",
+              "-outputdir",
+              ghcOutput,
+              "-i" <> out,
+              "-itest/conformance-structural",
+              "-i../keiro-core/src",
+              bindingSource
+            ]
+            ""
+        unless (exitCode == ExitSuccess) $
+          expectationFailure (standardOutput <> standardError)
+    it "keeps consumer types in Domain while the generated Codec owns keys, tags, and defaults" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          domain = generatedTextEndingIn "Catalog/Domain.hs" modules
+          codec = generatedTextEndingIn "Catalog/Codec.hs" modules
+      domain `shouldSatisfy` T.isInfixOf "Example.Artifact.Domain.ArtifactInfo"
+      domain `shouldSatisfy` T.isInfixOf "Vendor.Geometry.Geometry"
+      domain `shouldSatisfy` T.isInfixOf "Example.Artifact.KeiroBindings.emptyArtifactInfo"
+      codec `shouldSatisfy` T.isInfixOf "\"location\" .= encodeArtifactLocationShape"
+      codec `shouldSatisfy` T.isInfixOf "\"local_file\""
+      codec `shouldSatisfy` T.isInfixOf "Nothing -> pure Generated.ConsumerDemo.Structural.Shape.ArtifactKind.Guide"
+      codec `shouldSatisfy` T.isInfixOf "rejectUnknownFields \"ArtifactInfo\""
+      codec `shouldSatisfy` T.isInfixOf "toJSON payload.geometry"
+      codec `shouldSatisfy` (not . T.isInfixOf "vendor.geometry.json")
+    it "generates shape-only nested types and schema-derived Keiki witnesses" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          shape = generatedTextEndingIn "Structural/Shape/ArtifactInfo.hs" modules
+          facade = generatedTextEndingIn "StructuralProjections.hs" modules
+      shape `shouldSatisfy` T.isInfixOf "data ArtifactInfoShape = ArtifactInfo"
+      shape `shouldSatisfy` T.isInfixOf "ArtifactKind.ArtifactKindShape"
+      shape `shouldSatisfy` (not . T.isInfixOf "KeiroBindings")
+      facade `shouldSatisfy` T.isInfixOf "type FieldName"
+      facade `shouldSatisfy` T.isInfixOf "= \"/key\""
+      facade `shouldSatisfy` T.isInfixOf "fieldShapeId _ = \"example.artifact.ArtifactInfo.v1\""
+      facade `shouldSatisfy` T.isInfixOf "bindingToShape Example.Artifact.KeiroBindings.artifactInfoBinding owner"
+      facade `shouldSatisfy` T.isInfixOf "artifactInfoKeyWitness"
+      facade `shouldNotSatisfy` T.isInfixOf "structuralProjectionC"
+    it "suffixes only structural witness names that collide after normalization" $ do
+      source <- readTestText "test/fixtures/consumer-types.keiro"
+      collisionSpec <-
+        parseInlineSpec
+          "<projection-name-collision>"
+          ( T.replace
+              "    key         as \"key\"         : Text                 required"
+              ( T.unlines
+                  [ "    key         as \"key\"         : Text                 required",
+                    "    fooDash     as \"foo-bar\"     : Text                 required",
+                    "    fooUnder    as \"foo_bar\"     : Text                 required"
+                  ]
+              )
+              source
+          )
+      graph <- shouldResolveTypeGraph collisionSpec
+      let specs = projectionSpecs graph
+          keyWitnesses = [spWitness spec | spec <- specs, spPointer spec == "/key"]
+          collidedWitnesses = [spWitness spec | spec <- specs, spPointer spec `elem` ["/foo-bar", "/foo_bar"]]
+      keyWitnesses `shouldBe` ["artifactInfoKeyWitness"]
+      length collidedWitnesses `shouldBe` 2
+      Set.size (Set.fromList collidedWitnesses) `shouldBe` 2
+      collidedWitnesses `shouldSatisfy` all (T.isPrefixOf "artifactInfoFooBar")
+      collidedWitnesses `shouldSatisfy` all (T.isSuffixOf "Witness")
+      collidedWitnesses `shouldSatisfy` all ((== 8) . T.length . T.dropEnd (T.length ("Witness" :: T.Text)) . T.drop (T.length ("artifactInfoFooBar" :: T.Text)))
+
+  describe "structural manifest" $ do
+    it "lists consumer packages and every domain, binding, fixture, and initial module" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let modules = scaffoldModules (defaultContext (specContext spec)) spec
+          manifest = renderManifest "consumer-types.keiro" modules spec
+      mapM_ (\packageName -> manifestDependencies spec `shouldContain` [packageName]) ["artifact-domain", "vendor-geometry"]
+      manifest `shouldSatisfy` T.isInfixOf "consumer-packages:\n    artifact-domain\n    vendor-geometry"
+      mapM_
+        (\moduleName -> manifest `shouldSatisfy` T.isInfixOf moduleName)
+        [ "Example.Artifact.Domain",
+          "Example.Artifact.KeiroBindings",
+          "Vendor.Geometry",
+          "Vendor.Geometry.KeiroBindings"
+        ]
+
+  describe "structural scaffold record" $ do
+    it "round-trips canonical mapping rows and reports binding drift on the next run" $
+      withTempDirectory "keiro-dsl-mapping-record" $ \out -> do
+        spec <- specOf "test/fixtures/consumer-types.keiro"
+        let ctx = defaultContext (specContext spec)
+        first <- executePlannedScaffold out "consumer-types.keiro" ctx spec
+        length (consumerMappings (reportConsumerPlan first)) `shouldBe` 4
+        recordText <- TIO.readFile (out </> recordFileName (specContext spec))
+        let mappingRows = filter (T.isPrefixOf "mapping ") (T.lines recordText)
+            bindingRows = filter (T.isPrefixOf "binding ") (T.lines recordText)
+        length mappingRows `shouldBe` 4
+        bindingRows `shouldSatisfy` (not . null)
+        fmap recMappings (parseRecord recordText) `shouldSatisfy` maybe False ((== 4) . length)
+        fmap recBindingObligations (parseRecord recordText) `shouldSatisfy` maybe False ((== length bindingRows) . length)
+        let bumped = spec {specMapped = map bumpArtifactBindingVersion (specMapped spec)}
+        second <- executePlannedScaffold out "consumer-types.keiro" ctx bumped
+        reportMappingDrift second
+          `shouldSatisfy` any (\drift -> driftSpecName drift == "ArtifactInfo" && driftPrevious drift /= driftCurrent drift)
+        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "mapping drift:")
+        case mappingRows of
+          row : _ -> parseRecord (recordText <> row <> "\n") `shouldBe` Nothing
+          [] -> expectationFailure "expected mapping rows"
+        case bindingRows of
+          row : _ -> parseRecord (recordText <> row <> "\n") `shouldBe` Nothing
+          [] -> expectationFailure "expected binding rows"
+    it "reports exactly the newly added binding field without rewriting the shared skeleton" $
+      withTempDirectory "keiro-dsl-binding-drift" $ \out -> do
+        spec <- specOf "test/fixtures/consumer-types.keiro"
+        let ctx = defaultContext (specContext spec)
+        _ <- executePlannedScaffold out "consumer-types.keiro" ctx spec
+        let extended = spec {specMapped = map addArtifactSummaryField (specMapped spec)}
+        second <- executePlannedScaffold out "consumer-types.keiro" ctx extended
+        reportNewHoles second
+          `shouldBe` [ BindingHole
+                         { holeMappedName = "ArtifactInfo",
+                           holeModule = "Example.Artifact.KeiroBindings",
+                           holeSymbol = "artifactInfoBinding",
+                           holeKind = BindingValue,
+                           holePath = Just "summary",
+                           holeSignature = "artifactInfoBinding.summary :: Text"
+                         }
+                     ]
+        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "artifactInfoBinding.summary :: Text")
+    it "rejects malformed known mapping JSON while ignoring unrelated future rows" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      withTempDirectory "keiro-dsl-mapping-malformed" $ \out -> do
+        report <- executePlannedScaffold out "consumer-types.keiro" (defaultContext (specContext spec)) spec
+        recordText <- TIO.readFile (reportRecordPath report)
+        parseRecord (recordText <> "mapping {not-json}\n") `shouldBe` Nothing
+        parseRecord (recordText <> "future-row retained\n") `shouldBe` parseRecord recordText
+
+  describe "structural import plan" $ do
+    it "reports the successful dependency plan in the scaffold report" $
+      withTempDirectory "keiro-dsl-dependency-plan" $ \out -> do
+        spec <- specOf "test/fixtures/consumer-types.keiro"
+        report <- executePlannedScaffold out "consumer-types.keiro" (defaultContext (specContext spec)) spec
+        renderScaffoldReport report
+          `shouldSatisfy` any (T.isInfixOf "dependency plan: consumer packages [artifact-domain, vendor-geometry]")
+    it "refuses a binding module inside the generated namespace with the exact cycle" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let cyclic = spec {specMapped = map moveArtifactBindingIntoGenerated (specMapped spec)}
+      case planScaffold (defaultContext (specContext cyclic)) cyclic of
+        Left refusals -> do
+          refusals `shouldSatisfy` any isImportCycle
+          renderRefusals refusals `shouldSatisfy` any (T.isInfixOf "Generated.ConsumerDemo.Bindings")
+        Right _ -> expectationFailure "expected an import-cycle refusal"
+    it "refuses missing mapped register initials but permits command/event-only use" $ do
+      missing <- specOf "test/fixtures/mapped-missing-initial.keiro"
+      planScaffold (defaultContext (specContext missing)) missing `shouldSatisfy` isFoldSurfaceRefusal
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let commandOnly = removeMappedRegisterRequirements spec
+      planScaffold (defaultContext (specContext commandOnly)) commandOnly `shouldSatisfy` isRight
+
+  describe "binding explanations" $ do
+    it "lists binding, fixture, and use-site-scoped initial obligations deterministically" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
+      length obligations `shouldBe` 7
+      obligations
+        `shouldSatisfy` any
+          ( \obligation ->
+              obligationKind obligation == BindingValue
+                && obligationSymbol obligation == "artifactInfoBinding"
+                && obligationBindingVersion obligation == Just "1"
+          )
+      obligations
+        `shouldSatisfy` any
+          ( \obligation ->
+              obligationKind obligation == InitialValue
+                && obligationSymbol obligation == "emptyArtifactInfo"
+                && any (T.isInfixOf "Catalog register currentArtifact") (obligationUseSites obligation)
+          )
+      let rendered = renderBindingObligations (specContext spec) obligations
+      rendered `shouldSatisfy` T.isInfixOf "binding obligations for context consumer-demo"
+      rendered `shouldSatisfy` T.isInfixOf "artifactInfoBinding :: StructuralBinding Example.Artifact.Domain.ArtifactInfo ArtifactInfoShape"
+      rendered `shouldSatisfy` T.isInfixOf "provenance: binding-version \"1\""
+    it "states explicitly when a spec has no structural obligations" $ do
+      spec <- specOf "test/fixtures/reservation.keiro"
+      obligations <- either (\errors -> expectationFailure (show errors) >> pure []) pure (bindingObligations spec)
+      renderBindingObligations (specContext spec) obligations
+        `shouldBe` "no binding obligations for context hospital-capacity"
+
+  describe "exact generic structural bindings" $ do
+    forM_
+      [ ("renamed-field", "selector mismatch"),
+        ("reordered-field", "selector mismatch"),
+        ("arity-mismatch", "no exact nominal correspondence"),
+        ("incompatible-type", "no exact nominal correspondence")
+      ]
+      $ \(fixture, diagnostic) ->
+        it ("rejects " <> fixture <> " and directs the author to the scaffolded module") $
+          expectGenericCompileFailure fixture diagnostic
+
+  describe "structural harness" $ do
+    it "emits every structural, wire-policy, projection, and replay assertion family" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let aggregate = onlyAggregate spec
+          ctx = defaultContext (specContext spec)
+          harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx spec aggregate)
+      mapM_
+        (\needle -> harness `shouldSatisfy` T.isInfixOf needle)
+        [ "binding domain round-trip: example.artifact.ArtifactInfo.v1/",
+          "binding shape round-trip: example.artifact.ArtifactInfo.v1/",
+          "mapped codec round-trip: ArtifactObserved/artifact/",
+          "fixture coverage: example.artifact.ArtifactLocation.v1",
+          "wire policy missing default: example.artifact.ArtifactInfo.v1/description",
+          "wire policy explicit null: example.artifact.ArtifactInfo.v1/description",
+          "wire policy unknown fields: example.artifact.ArtifactInfo.v1",
+          "wire union arm: example.artifact.ArtifactLocation.v1/local_file",
+          "canonical identity: example.artifact.ArtifactInfo.v1",
+          "projection witness agreement: example.artifact.ArtifactInfo.v1/key",
+          "forward/replay equality: ObserveArtifact from CatalogEmpty -- ",
+          "register currentArtifact"
+        ]
+    it "keeps opaque assertions at the declared codec boundary" $ do
+      spec <- specOf "test/fixtures/consumer-types.keiro"
+      let aggregate = onlyAggregate spec
+          ctx = defaultContext (specContext spec)
+          modules = scaffoldAggregate ctx spec aggregate <> harnessFor ctx spec aggregate
+          harness = generatedTextEndingIn "Harness.hs" modules
+          codec = generatedTextEndingIn "Codec.hs" modules
+      harness `shouldSatisfy` T.isInfixOf "opaque codec round-trip: vendor.geometry.json@3/"
+      harness `shouldNotSatisfy` T.isInfixOf "wire policy unknown fields: vendor.geometry.json"
+      harness `shouldNotSatisfy` T.isInfixOf "fixture coverage: vendor.geometry"
+      codec `shouldNotSatisfy` T.isInfixOf "encodeVendorGeometryShape"
+
+  describe "manifest (M2)" $ do
+    it "lists exactly the modules the scaffolder produced" $ do
+      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      service <- checkedServiceOf "test/fixtures/reservation.keiro"
+      let manifest = renderManifestForService "reservation.keiro" mods service
+          expectedNames = sort (map (moduleNameOf . modulePath) mods)
+      -- every produced module name appears in the manifest…
+      mapM_ (\m -> (m `T.isInfixOf` manifest) `shouldBe` True) expectedNames
+      -- …and the module list is exactly the scaffolder's output set.
+      expectedNames
+        `shouldBe` sort
+          [ "Generated.HospitalCapacity.Reservation.Codec",
+            "Generated.HospitalCapacity.Reservation.BehaviorContract",
+            "Generated.HospitalCapacity.Reservation.Domain",
+            "Generated.HospitalCapacity.Reservation.EventStream",
+            "Generated.HospitalCapacity.Reservation.Harness",
+            "Generated.HospitalCapacity.Reservation.Projection",
+            "Generated.HospitalCapacity.Reservation.Transducer",
+            "Generated.HospitalCapacity.Nominals",
+            "Generated.HospitalCapacity.Nominals.Internal",
+            "Generated.HospitalCapacity.ReplayAudit",
+            "HospitalCapacity.Reservation.BehaviorHoles",
+            "HospitalCapacity.Reservation.Holes"
+          ]
+    it "derives the dependency set from the node kinds present (aggregate)" $ do
+      service <- checkedServiceOf "test/fixtures/reservation.keiro"
+      manifestDependenciesForService service `shouldBe` ["aeson", "base", "keiki", "keiro", "text"]
+    it "derives the process dependency set, including worker-policy runtime imports" $ do
+      service <- checkedServiceOf "test/fixtures/hospital-surge.keiro"
+      let dependencies = manifestDependenciesForService service
+      mapM_ (\dependency -> dependencies `shouldContain` [dependency]) ["time", "uuid", "shibuya-core", "keiki", "keiro"]
+    it "uses the registered shibuya-core package name for router scaffolds" $ do
+      service <- checkedServiceOf "test/fixtures/incident-paging/incident-paging.keiro"
+      let dependencies = manifestDependenciesForService service
+      mapM_ (\dependency -> dependencies `shouldContain` [dependency]) ["effectful-core", "keiro", "shibuya-core"]
+      dependencies `shouldNotContain` ["shibuya"]
+
+  describe "new <kind> skeletons (M5)" $ do
+    forM_ skeletonKinds $ \skeletonKind ->
+      it ("the " <> T.unpack skeletonKind <> " skeleton selects and preserves the registered stable language") $
+        assertSkeletonUsesStableLanguage skeletonKind
+    it "every skeleton parses and validates with zero error diagnostics" $
+      mapM_ assertSkeletonValid skeletonKinds
+    it "every skeleton passes the scaffold refusal gates" $
+      mapM_ assertSkeletonScaffoldable skeletonKinds
+    it "stable skeleton scaffolds match the committed compiling modules" $
+      mapM_ (uncurry assertStableSkeletonMatchesCommitted) skeletonModuleRoots
+    it "rejects an unknown kind with a helpful message" $
+      case skeletonFor "bogus" of
+        Left msg -> ("Valid kinds:" `T.isInfixOf` msg) `shouldBe` True
+        Right _ -> expectationFailure "expected an error for an unknown kind"
+
+  describe "firewall self-check (M3)" $ do
+    it "flags a forbidden operator in a Generated module" $ do
+      let m = ScaffoldModule {modulePath = "Gen/Foo.hs", moduleText = "x = a ./= b", kind = Generated, origin = "test"}
+      firewallBreaches [m] `shouldBe` [("Gen/Foo.hs", "./=", 1)]
+    it "ignores forbidden operators in a HoleStub module (holes own them)" $ do
+      let m = ScaffoldModule {modulePath = "Foo/Holes.hs", moduleText = "x = lit 1 .== y", kind = HoleStub, origin = "test"}
+      firewallBreaches [m] `shouldBe` []
+    it "matches `lit` as a word, not a substring of quality/split" $ do
+      let clean = ScaffoldModule {modulePath = "Gen/Q.hs", moduleText = "quality = split facility", kind = Generated, origin = "test"}
+          dirty = ScaffoldModule {modulePath = "Gen/L.hs", moduleText = "v = lit foo", kind = Generated, origin = "test"}
+      firewallBreaches [clean] `shouldBe` []
+      firewallBreaches [dirty] `shouldBe` [("Gen/L.hs", "lit", 1)]
+    it "skips strings and comments and maximal-munches symbolic tokens" $ do
+      let clean = syntheticGenerated "Gen/Clean.hs" "wire = \"lit .== B.slot\"\n-- x =: y\nx = a .<= b"
+          dirty = syntheticGenerated "Gen/Dirty.hs" "x = a .< b\ny = c =: d"
+      firewallBreaches [clean] `shouldBe` [("Gen/Clean.hs", ".<=", 3)]
+      firewallBreaches [dirty] `shouldBe` [("Gen/Dirty.hs", ".<", 1), ("Gen/Dirty.hs", "=:", 2)]
+    it "guards keiki imports while allowing the generated Core allowlist" $ do
+      let forbidden = syntheticGenerated "Gen/Builder.hs" "import Keiki.Builder"
+          restricted = syntheticGenerated "Gen/CoreBad.hs" "import Keiki.Core (lit)"
+          allowed = syntheticGenerated "Gen/CoreGood.hs" "import Keiki.Core (RegFile (..), HsPred, step)"
+      firewallBreaches [forbidden] `shouldBe` [("Gen/Builder.hs", "import:Keiki.Builder", 1)]
+      firewallBreaches [restricted] `shouldBe` [("Gen/CoreBad.hs", "import:Keiki.Core", 1)]
+      firewallBreaches [allowed] `shouldBe` []
+    it "exempts only the authoritative generated transducer module path" $ do
+      let expressions = syntheticGenerated "Gen/Aggregate/Expressions.hs" "import Keiki.Core qualified as K\nx = K.lit 1"
+          transducer = syntheticGenerated "Gen/Aggregate/Transducer.hs" "import Keiki.Builder qualified as B\nx = B.slot"
+          ordinary = syntheticGenerated "Gen/Aggregate/Projection.hs" "import Keiki.Builder qualified as B"
+      firewallBreaches [expressions, transducer]
+        `shouldBe` [("Gen/Aggregate/Expressions.hs", "import:Keiki.Core", 1)]
+      firewallBreaches [ordinary] `shouldBe` [("Gen/Aggregate/Projection.hs", "import:Keiki.Builder", 1)]
+    it "finds no breach in real scaffolder output (aggregate + process fixtures)" $ do
+      aggMods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      procMods <- legacyScaffoldProcessFixture "test/fixtures/hospital-surge.keiro"
+      firewallBreaches (aggMods <> procMods) `shouldBe` []
+
+  describe "generated provenance banners (plan 182 M4)" $ do
+    it "stamps the running package version, effective language, and module origin" $ do
+      service <- checkedServiceOf "test/fixtures/contract-v4.keiro"
+      let ctx = defaultContext (specContext (checkedSpec service))
+      case planServiceScaffold ctx service of
+        Left refusals -> expectationFailure (show refusals)
+        Right modules -> do
+          let generated = [moduleValue | moduleValue <- modules, kind moduleValue == Generated]
+          generated `shouldSatisfy` (not . null)
+          forM_ generated $ \moduleValue -> do
+            let recognized = filter isGeneratedBannerLine (T.lines (moduleText moduleValue))
+                expected = generatedBannerFor (checkedLanguageContract service) (origin moduleValue)
+            recognized `shouldBe` [expected]
+            expected
+              `shouldSatisfy` T.isInfixOf
+                ( "keiro-dsl "
+                    <> T.pack (showVersion Package.version)
+                    <> " (language keiro-dsl 4) from contract emergency"
+                )
+      workspace <- shouldComposeWorkspace canonicalWorkspacePath
+      workspacePlan <- shouldPlanWorkspaceSpec workspace
+      forM_ [moduleValue | (moduleValue, _) <- wpModules workspacePlan, kind moduleValue == Generated] $ \moduleValue ->
+        filter isGeneratedBannerLine (T.lines (moduleText moduleValue))
+          `shouldBe` [generatedBannerFor (checkedLanguageContract (checkedWorkspace workspace)) (origin moduleValue)]
+    it "recognizes only the historical banner and the stamped format" $ do
+      let contract = effectiveLanguageContract LegacyUnversioned
+      isGeneratedBannerLine generatedBanner `shouldBe` True
+      isGeneratedBannerLine (generatedBannerFor contract "aggregate Counter (line 2)") `shouldBe` True
+      isGeneratedBannerLine "-- @generated by another tool" `shouldBe` False
+      isGeneratedBannerLine codecComparisonBanner `shouldBe` False
+    it "migrates a legacy-banner file and keeps repeated scaffold bytes stable" $
+      withTempDirectory "keiro-dsl-stamped-banner" $ \out -> do
+        spec <- parseInlineSpec "<stamped-banner>" loweringAggregateSpec
+        let ctx = defaultContext (specContext spec)
+        modules <- case planScaffold ctx spec of
+          Left refusals -> expectationFailure (show refusals) >> pure []
+          Right planned -> pure planned
+        case [moduleValue | moduleValue <- modules, kind moduleValue == Generated] of
+          target : _ -> do
+            let path = out </> modulePath target
+                stamped = generatedBannerFor (effectiveLanguageContract LegacyUnversioned) (origin target)
+                legacyText = T.replace stamped generatedBanner (moduleText target)
+            createDirectoryIfMissing True (takeDirectory path)
+            TIO.writeFile path legacyText
+            first <- executeScaffold out False "counter.keiro" ctx spec modules
+            first `shouldSatisfy` isSuccessfulScaffold
+            firstTree <- treeSnapshot out
+            second <- executeScaffold out False "counter.keiro" ctx spec modules
+            second `shouldSatisfy` isSuccessfulScaffold
+            treeSnapshot out `shouldReturn` firstTree
+            TIO.readFile path `shouldReturn` moduleText target
+          [] -> expectationFailure "counter scaffold has no Generated module"
+
+  describe "service-aware fixture helpers" $ do
+    it "retains version-4 contract TypeIDs and their durable admission identities" $ do
+      service <- checkedServiceOf "test/fixtures/contract-v4.keiro"
+      modules <- scaffoldFixture "test/fixtures/contract-v4.keiro"
+      let contractModule = generatedTextEndingIn "Contract.hs" modules
+          identities = idDomainIdentitiesForService service
+      contractModule `shouldSatisfy` T.isInfixOf "incidentId :: !(KindID \"inc\")"
+      contractModule `shouldSatisfy` T.isInfixOf "reservationId :: !(KindID \"rsv\")"
+      identities
+        `shouldContain` ["id-domain|name=contract:emergency.IncidentTransferNeedDeclared.incidentId|contract=keiro-dsl/id-domain/typeid-v7/1|prefix=inc|separator=_|json=canonical-json-text"]
+
+  describe "scaffold gates" $ do
+    it "refuses duplicate and case-folded module paths with both origins" $ do
+      spec <- specOf "test/fixtures/reservation.keiro"
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        aggregate : _ -> do
+          let duplicate = spec {specNodes = [NAggregate aggregate, NAggregate aggregate]}
+              caseVariant = spec {specNodes = [NAggregate aggregate, NAggregate aggregate {aggName = T.toUpper (aggName aggregate)}]}
+          planScaffold (defaultContext (specContext spec)) duplicate `shouldSatisfy` hasPathCollisionWithTwoOrigins
+          planScaffold (defaultContext (specContext spec)) caseVariant `shouldSatisfy` hasPathCollisionWithTwoOrigins
+        [] -> expectationFailure "reservation fixture has no aggregate"
+    it "refuses a bannerless Generated target without changing its bytes" $
+      withTempDirectory "keiro-dsl-banner" $ \out -> do
+        spec <- specOf "test/fixtures/reservation.keiro"
+        let ctx = defaultContext (specContext spec)
+        case planScaffold ctx spec of
+          Left refusals -> expectationFailure ("unexpected planning refusal: " <> show refusals)
+          Right modules -> case [m | m <- modules, kind m == Generated] of
+            generated : _ -> do
+              let target = out </> modulePath generated
+              createDirectoryIfMissing True (takeDirectory target)
+              TIO.writeFile target "hand owned\n"
+              result <- executeScaffold out False "test/fixtures/reservation.keiro" ctx spec modules
+              result `shouldSatisfy` isMissingBannerRefusal
+              TIO.readFile target `shouldReturn` "hand owned\n"
+              forced <- executeScaffold out True "test/fixtures/reservation.keiro" ctx spec modules
+              forced `shouldSatisfy` isSuccessfulScaffold
+              TIO.readFile target `shouldReturn` moduleText generated
+            [] -> expectationFailure "reservation scaffold has no Generated module"
+    it "reports renamed-node modules as stale without deleting them" $
+      withTempDirectory "keiro-dsl-stale-rename" $ \out -> do
+        spec <- parseInlineSpec "<stale-rename>" loweringAggregateSpec
+        first <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext spec)) spec
+        let renamed = spec {specNodes = map renameCounter (specNodes spec)}
+        second <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext renamed)) renamed
+        let oldDomain = onlyPathEndingIn "Counter/Domain.hs" (map fst (reportDispositions first))
+            oldHoles = onlyPathEndingIn "Counter/Holes.hs" (map fst (reportDispositions first))
+        reportStale second `shouldSatisfy` \stale ->
+          StaleModule Generated oldDomain (Just ExactGeneratedBannerPresent) `elem` stale
+            && StaleModule HoleStub oldHoles Nothing `elem` stale
+        doesFileExist (out </> oldDomain) `shouldReturn` True
+        doesFileExist (out </> oldHoles) `shouldReturn` True
+        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "exact generated banner present; verify unchanged bytes before deleting")
+        renderScaffoldReport second `shouldSatisfy` all (not . T.isInfixOf "safe to delete")
+    it "preserves a stale generated path whose exact banner is missing" $
+      withTempDirectory "keiro-dsl-stale-banner" $ \out -> do
+        spec <- parseInlineSpec "<stale-banner>" loweringAggregateSpec
+        first <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext spec)) spec
+        let oldDomain = onlyPathEndingIn "Counter/Domain.hs" (map fst (reportDispositions first))
+            renamed = spec {specNodes = map renameCounter (specNodes spec)}
+        TIO.writeFile (out </> oldDomain) "-- generated by something else\n"
+        second <- executePlannedScaffold out "counter.keiro" (defaultContext (specContext renamed)) renamed
+        reportStale second `shouldSatisfy` elem (StaleModule Generated oldDomain (Just ExactGeneratedBannerMissing))
+        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "exact generated banner missing; preserve and review")
+        TIO.readFile (out </> oldDomain) `shouldReturn` "-- generated by something else\n"
+    it "reports the entire old tree across a module-root flip" $
+      withTempDirectory "keiro-dsl-stale-root" $ \out -> do
+        spec <- parseInlineSpec "<stale-root>" loweringAggregateSpec
+        let initialCtx = defaultContext (specContext spec)
+            rootedCtx = initialCtx {moduleRoot = "Acme"}
+        first <- executePlannedScaffold out "counter.keiro" initialCtx spec
+        second <- executePlannedScaffold out "moved-counter.keiro" rootedCtx spec
+        reportStale second
+          `shouldMatchList` [ StaleModule (kind m) (modulePath m) (if kind m == Generated then Just ExactGeneratedBannerPresent else Nothing)
+                            | (m, _) <- reportDispositions first
+                            ]
+        forM_ (reportStale second) $ \stale -> doesFileExist (out </> stalePath stale) `shouldReturn` True
+        renderScaffoldReport second `shouldSatisfy` any (T.isInfixOf "previous scaffold record used spec counter.keiro")
+    it "reports moved generated modules across a layout flip" $
+      withTempDirectory "keiro-dsl-stale-layout" $ \out -> do
+        spec <- parseInlineSpec "<stale-layout>" loweringAggregateSpec
+        let initialCtx = defaultContext (specContext spec)
+            collocatedCtx = initialCtx {placement = CollocatedLeaf}
+        first <- executePlannedScaffold out "counter.keiro" initialCtx spec
+        second <- executePlannedScaffold out "counter.keiro" collocatedCtx spec
+        let oldGenerated = [StaleModule Generated (modulePath m) (Just ExactGeneratedBannerPresent) | (m, _) <- reportDispositions first, kind m == Generated]
+        reportStale second `shouldSatisfy` all (`elem` oldGenerated)
+        length (reportStale second) `shouldBe` length oldGenerated
+    it "writes a parseable record and no stale section for a fresh output" $
+      withTempDirectory "keiro-dsl-record" $ \out -> do
+        spec <- parseInlineSpec "<fresh-record>" loweringAggregateSpec
+        let ctx = defaultContext (specContext spec)
+        report <- executePlannedScaffold out "counter.keiro" ctx spec
+        reportStale report `shouldBe` []
+        renderScaffoldReport report `shouldSatisfy` all (not . T.isPrefixOf "stale:")
+        contents <- TIO.readFile (out </> recordFileName (specContext spec))
+        requirements <- either (\errors -> expectationFailure (show errors) >> pure []) pure (Behavior.deriveBehaviorRequirements spec)
+        let expected =
+              ScaffoldRecord
+                { recSpecPath = "counter.keiro",
+                  recModuleRoot = "",
+                  recLayout = "prefixed",
+                  recSourceLanguage = LegacyUnversioned,
+                  recLanguageContract = effectiveLanguageContract LegacyUnversioned,
+                  recFiles = [(kind m, modulePath m) | (m, _) <- reportDispositions report],
+                  recMappings = [],
+                  recIdDomains = [],
+                  recNominalEqualities = [],
+                  recBindingObligations = [],
+                  recBehaviorRequirements = Behavior.behaviorRecordRows requirements
+                }
+            sourceRows = filter ("source-language " `T.isPrefixOf`) (T.lines contents)
+            withoutSourceRows = T.unlines (filter (not . T.isPrefixOf "source-language ") (T.lines contents))
+            semanticRows = filter ("semantic-contract " `T.isPrefixOf`) (T.lines contents)
+            withoutSemanticRows = T.unlines (filter (not . T.isPrefixOf "semantic-contract ") (T.lines contents))
+        parseRecord contents `shouldBe` Just expected
+        parseRecord withoutSourceRows `shouldBe` Just expected
+        parseRecord withoutSemanticRows `shouldBe` Just expected
+        case sourceRows of
+          [sourceRow] -> do
+            parseRecord (T.replace sourceRow (sourceRow <> "\n" <> sourceRow) contents) `shouldBe` Nothing
+            parseRecord (T.replace sourceRow "source-language {malformed}" contents) `shouldBe` Nothing
+          _ -> expectationFailure "expected exactly one source-language row"
+        case semanticRows of
+          [semanticRow] -> do
+            parseRecord (T.replace semanticRow (semanticRow <> "\n" <> semanticRow) contents) `shouldBe` Nothing
+            parseRecord (T.replace semanticRow "semantic-contract {malformed}" contents) `shouldBe` Nothing
+            parseRecord (T.replace "\"languageVersion\":1" "\"languageVersion\":2" contents) `shouldBe` Nothing
+          _ -> expectationFailure "expected exactly one semantic-contract row"
+        parseRecord (T.replace "spec: " "future-field: retained\nspec: " contents) `shouldBe` parseRecord contents
+        parseRecord (T.replace "record v1" "record v2" contents) `shouldBe` Nothing
+    it "records declared provenance and reports a header-only scaffold drift" $
+      withTempDirectory "keiro-dsl-language-drift" $ \out -> do
+        spec <- parseInlineSpec "<language-drift>" loweringAggregateSpec
+        let ctx = defaultContext (specContext spec)
+        modules <- case planScaffold ctx spec of
+          Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
+          Right planned -> pure planned
+        _ <- executePlannedScaffold out "counter.keiro" ctx spec
+        case languageVersion 1 of
+          Nothing -> expectationFailure "version 1 was not constructible"
+          Just version -> do
+            let declared = DeclaredLanguage version noLoc
+            result <- executeScaffoldWithLanguage out False "counter.keiro" declared ctx spec modules
+            case result of
+              Left refusals -> expectationFailure (show refusals)
+              Right report -> do
+                reportSourceLanguageDrift report
+                  `shouldBe` Just (SourceLanguageDrift LegacyUnversioned declared)
+                contents <- TIO.readFile (reportRecordPath report)
+                recSourceLanguage <$> parseRecord contents `shouldBe` Just declared
+
+  describe "faithful scaffold lowering" $ do
+    it "escapes a trailing-backslash payload literal exactly once" $ do
+      spec <- specOf "test/fixtures/hospital-surge.keiro"
+      case [process | NProcess process <- specNodes spec] of
+        process : _ -> do
+          let timer = (procTimer process) {tmPayload = [FieldBinding "kind" (Just "\"follow-up\\\"")]}
+              modules = scaffoldProcess (defaultContext (specContext spec)) process {procTimer = timer}
+          generatedTextEndingIn "Process.hs" modules
+            `shouldSatisfy` T.isInfixOf "\"kind\" .= (\"follow-up\\\\\" :: Value)"
+        [] -> expectationFailure "hospital-surge fixture has no process"
+    it "preserves quoted Text register initials and refuses unsafe register shapes" $ do
+      spec <- parseInlineSpec "<register-initials>" loweringAggregateSpec
+      let modules = scaffoldAggregate (defaultContext (specContext spec)) spec =<< [aggregate | NAggregate aggregate <- specNodes spec]
+          domain = generatedTextEndingIn "Domain.hs" modules
+      domain `shouldSatisfy` T.isInfixOf "RCons (Proxy @\"note\") \"hello world\""
+      scaffoldRefusals spec `shouldBe` []
+      bare <- parseInlineSpec "<bare-text-initial>" (T.replace "\"hello world\"" "hello" loweringAggregateSpec)
+      scaffoldRefusals bare `shouldSatisfy` any (T.isInfixOf "RegTextInitialNotQuoted")
+      unsupported <- parseInlineSpec "<unsupported-field>" (T.replace "count:Int" "count:Json" loweringAggregateSpec)
+      scaffoldRefusals unsupported `shouldSatisfy` any (T.isInfixOf "FieldTypeUnrepresentable")
+    it "lowers seconds, minutes, hours, and both backoff constructors faithfully" $ do
+      windowSeconds "90s" `shouldBe` Right 90
+      windowSeconds "5m" `shouldBe` Right 300
+      windowSeconds "2h" `shouldBe` Right 7200
+      emitSource <- readTestText "test/fixtures/emit.keiro"
+      let exponentialSource = T.replace "backoff constant 2s" "backoff exponential 2s max=60s multiplier=2.0" emitSource
+      exponential <- parseInlineSpec "<exponential-backoff>" exponentialSource
+      case [publisher | NPublisher publisher <- specNodes exponential] of
+        publisher : _ -> do
+          let generated = generatedTextEndingIn "Publisher.hs" (scaffoldPublisher (defaultContext (specContext exponential)) publisher)
+          generated `shouldSatisfy` T.isInfixOf "ExponentialBackoff ExponentialBackoffOptions { initial = 2, maxDelay = 60, multiplier = 2.0 }"
+          parseSpec "<exponential-round-trip>" (renderSpec exponential) `shouldBe` Right exponential
+        [] -> expectationFailure "emit fixture has no publisher"
+      constant <- parseInlineSpec "<constant-backoff>" (T.replace "backoff constant 2s" "backoff constant 2m" emitSource)
+      case [publisher | NPublisher publisher <- specNodes constant] of
+        publisher : _ -> generatedTextEndingIn "Publisher.hs" (scaffoldPublisher (defaultContext (specContext constant)) publisher) `shouldSatisfy` T.isInfixOf "ConstantBackoff 120"
+        [] -> expectationFailure "emit fixture has no publisher"
+    it "refuses incomplete exponential backoff and rejects unknown window units" $ do
+      emitSource <- readTestText "test/fixtures/emit.keiro"
+      incomplete <- parseInlineSpec "<incomplete-backoff>" (T.replace "backoff constant 2s" "backoff exponential 2s" emitSource)
+      scaffoldRefusals incomplete `shouldSatisfy` any (T.isInfixOf "BackoffExponentialIncomplete")
+      parseSpec "<bad-window>" (T.replace "backoff constant 2s" "backoff constant 2x" emitSource)
+        `shouldSatisfy` leftContains "time unit: s, m, or h"
+    it "lowers workqueue retry windows in minutes to seconds" $ do
+      queueSource <- readTestText "test/fixtures/reservation-work.keiro"
+      queueSpec <- parseInlineSpec "<minute-queue>" (T.replace "5s" "5m" queueSource)
+      case [workqueue | NWorkqueue workqueue <- specNodes queueSpec] of
+        workqueue : _ -> do
+          let policy = generatedTextEndingIn "QueuePolicy.hs" (scaffoldWorkqueue (defaultContext (specContext queueSpec)) workqueue)
+          policy `shouldSatisfy` T.isInfixOf "defaultRetryDelay = RetryDelay 300"
+          policy `shouldSatisfy` T.isInfixOf "Retry (RetryDelay 300)"
+        [] -> expectationFailure "queue fixture has no workqueue"
+    it "uses exact status-map keys and emits total Int harness samples" $ do
+      statusSpec <- parseInlineSpec "<exact-status>" exactStatusSpec
+      case [aggregate | NAggregate aggregate <- specNodes statusSpec] of
+        aggregate : _ -> do
+          let ctx = defaultContext (specContext statusSpec)
+              projection = generatedTextEndingIn "Projection.hs" (scaffoldAggregate ctx statusSpec aggregate)
+              harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx statusSpec aggregate)
+          projection `shouldSatisfy` T.isInfixOf "ReservationUnHeld {} -> Just \"available\""
+          harness `shouldSatisfy` T.isInfixOf "CountBumpedData 0"
+          harness `shouldNotSatisfy` T.isInfixOf "sample: unsupported"
+        [] -> expectationFailure "exact-status spec has no aggregate"
+
+  describe "scaffold" $ do
+    it "synthesizes the exact old wire shape and embeds it in the harness" $ do
+      oldSpec <- specOf "test/fixtures/reservation.keiro"
+      newSpec <- specOf "test/fixtures/reservation-v2.keiro"
+      case goldensForDiff oldSpec newSpec of
+        [golden] -> do
+          goldenRelativePath golden
+            `shouldBe` "hospital-capacity/Reservation/TransferReservationCreated.v1.json"
+          goldenJson golden
+            `shouldBe` "{\"commandId\":\"cmd_01hzy3v7q2e8kaw2m5x0d41n9c\",\"divertStatus\":\"open\",\"hospitalId\":\"hosp_01hzy3v7q2e8kaw2m5x0d41n9c\",\"kind\":\"TransferReservationCreated\",\"lifeCriticalOverride\":true,\"patientAcuity\":\"red\",\"reservationId\":\"rsv_01hzy3v7q2e8kaw2m5x0d41n9c\"}\n"
+          goldenEvidence golden `shouldBe` SynthesizedWeakStandIn
+          let aggregate = onlyAggregate newSpec
+              modules =
+                harnessForWithGoldens
+                  [golden]
+                  (defaultContext (specContext newSpec))
+                  newSpec
+                  aggregate
+              harness = generatedTextEndingIn "Harness.hs" modules
+          harness `shouldSatisfy` T.isInfixOf "golden TransferReservationCreated.v1 decodes"
+          harness `shouldSatisfy` T.isInfixOf "\\\"reservationId\\\":\\\"rsv_"
+          harness `shouldSatisfy` (not . T.isInfixOf "current-shape stand-in")
+        goldens -> expectationFailure ("expected one synthesized golden, got " <> show goldens)
+    it "synthesizes complete nested mapped old shapes deterministically and never overwrites captured evidence" $ do
+      oldSpec <- specOf "test/fixtures/consumer-types.keiro"
+      newSpec <- specOf "test/fixtures/consumer-types-v2.keiro"
+      case goldensForDiff oldSpec newSpec of
+        [golden] -> do
+          goldenEvidence golden `shouldBe` SynthesizedWeakStandIn
+          goldenJson golden `shouldSatisfy` T.isInfixOf "\"artifact\":{"
+          goldenJson golden `shouldSatisfy` T.isInfixOf "\"location\":{\"contents\":\"sample\",\"tag\":\"local_file\"}"
+          goldenJson golden `shouldSatisfy` T.isInfixOf "\"labels\":[\"sample\"]"
+          goldenJson golden `shouldSatisfy` T.isInfixOf "\"revision\":1"
+          goldenJson golden `shouldSatisfy` T.isInfixOf "\"observedAt\":\"2026-01-01T00:00:00Z\""
+          goldensForDiff oldSpec newSpec `shouldBe` [golden]
+          withTempDirectory "keiro-golden-preserve" $ \root -> do
+            let target = root </> goldenRelativePath golden
+            createDirectoryIfMissing True (takeDirectory target)
+            TIO.writeFile target "hand captured\n"
+            emitGoldenPayloads root oldSpec newSpec `shouldReturn` []
+            TIO.readFile target `shouldReturn` "hand captured\n"
+          withTempDirectory "keiro-golden-write" $ \root -> do
+            let target = root </> goldenRelativePath golden
+            emitGoldenPayloads root oldSpec newSpec `shouldReturn` [target]
+            TIO.readFile target `shouldReturn` goldenJson golden
+        goldens -> expectationFailure ("expected one nested synthesized golden, got " <> show goldens)
+    it "dispatches shared-version upcasters by wire event type and passes foreign kinds through" $ do
+      parsed <- parsedSourceOf "test/fixtures/reservation-dup-upcast-source.keiro"
+      let spec = parsedSpec parsed
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        [aggregate] -> do
+          let modules = scaffoldServiceModules (defaultContext (specContext spec)) (checkedSource parsed)
+              codec = generatedTextEndingIn "Codec.hs" modules
+              holes = case [moduleText m | m <- modules, "/Holes.hs" `T.isSuffixOf` T.pack (modulePath m)] of
+                [text] -> text
+                _ -> ""
+          codec `shouldSatisfy` T.isInfixOf "upcasters = [(1, upcastRungV1)]"
+          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 (EventType \"TransferReservationCreated\") value = upcastTransferReservationCreatedV1 value"
+          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 (EventType \"TransferReservationConfirmed\") value = upcastTransferReservationConfirmedV1 value"
+          codec `shouldSatisfy` T.isInfixOf "upcastRungV1 _ value = Right value"
+          holes `shouldSatisfy` T.isInfixOf "receives ONLY TransferReservationCreated payloads"
+        _ -> expectationFailure "expected exactly one aggregate"
+    it "keeps foreign payloads byte-for-byte and invokes both same-rung event upcasters" $ do
+      let payloadA = object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (2 :: Int)]
+          payloadB = object ["kind" .= ("AmountRenamed" :: T.Text), "amount" .= (3 :: Int)]
+          foreignPayload = object ["kind" .= ("AmountObserved" :: T.Text), "amount" .= (7 :: Int)]
+          upcastA _ = Right (object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (200 :: Int)])
+          upcastB _ = Right (object ["kind" .= ("AmountRenamed" :: T.Text), "amountInCents" .= (300 :: Int)])
+          rung (EventType "AmountScaled") = upcastA
+          rung (EventType "AmountRenamed") = upcastB
+          rung _ = Right
+          codec =
+            Codec
+              { eventTypes = EventType "AmountScaled" :| [EventType "AmountRenamed", EventType "AmountObserved"],
+                eventType = const (EventType "AmountObserved"),
+                schemaVersion = 2,
+                encode = id,
+                decode = \_ -> Right,
+                upcasters = [(1, rung)]
+              } ::
+              Codec Value
+      decodeRaw codec (EventType "AmountObserved") 1 foreignPayload `shouldBe` Right foreignPayload
+      decodeRaw codec (EventType "AmountScaled") 1 payloadA
+        `shouldBe` Right (object ["kind" .= ("AmountScaled" :: T.Text), "amount" .= (200 :: Int)])
+      decodeRaw codec (EventType "AmountRenamed") 1 payloadB
+        `shouldBe` Right (object ["kind" .= ("AmountRenamed" :: T.Text), "amountInCents" .= (300 :: Int)])
+    it "never emits a keiki symbolic operator into a Generated module (firewall)" $ do
+      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      firewallBreaches mods `shouldBe` []
+    it "marks the Holes module HoleStub and the rest Generated" $ do
+      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      let holes = [m | m <- mods, kind m == HoleStub]
+      map (takeFileName . modulePath) holes `shouldBe` ["BehaviorHoles.hs", "Holes.hs"]
+      -- Context nominals/internal/replay plus the stable aggregate surface.
+      length [m | m <- mods, kind m == Generated] `shouldBe` 10
+    it "is deterministic (re-scaffolding yields byte-identical text)" $ do
+      a <- scaffoldFixture "test/fixtures/reservation.keiro"
+      b <- scaffoldFixture "test/fixtures/reservation.keiro"
+      map moduleText a `shouldBe` map moduleText b
+    it "keeps retiring as validator-only metadata in generated modules" $ do
+      ordinary <- scaffoldFixture "test/fixtures/reservation.keiro"
+      retiring <- scaffoldFixture "test/fixtures/reservation-retiring.keiro"
+      map (\m -> (modulePath m, kind m, moduleText m)) retiring
+        `shouldBe` map (\m -> (modulePath m, kind m, moduleText m)) ordinary
+    it "matches the committed compiling Generated conformance modules (modulo whitespace)" $ do
+      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      mapM_ assertMatchesCommitted [m | m <- mods, kind m == Generated]
+    it "matches every committed new-surface Generated module (modulo formatting)" $ do
+      modules <- scaffoldFixture "test/fixtures/transfer-routing.keiro"
+      forM_ [m | m <- modules, kind m == Generated] $ \m -> do
+        committed <- readTestText ("test/conformance-newsurface/" <> modulePath m)
+        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
+    it "scaffolds the register-free OrderStream smoke target without error" $ do
+      mods <- scaffoldFixture "test/fixtures/order.keiro"
+      -- Stable context and aggregate modules plus both hand-owned hole surfaces.
+      length mods `shouldBe` 12
+      firewallBreaches mods `shouldBe` []
+      let harness = generatedTextEndingIn "Harness.hs" mods
+      harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: PlaceOrder from OrderNotStarted -- \""
+      harness `shouldSatisfy` T.isInfixOf "prefix <> \"final vertex\""
+      harness `shouldNotSatisfy` T.isInfixOf "prefix <> \"register "
+    it "emits forward/replay checks with field-distinct Text samples" $ do
+      spec <- parseInlineSpec "<forward-replay-samples>" (T.replace "command Bump { count:Int }" "command Bump { count:Int noteText:Text echo:Text }" loweringAggregateSpec)
+      case [aggregate | NAggregate aggregate <- specNodes spec] of
+        aggregate : _ -> do
+          let ctx = defaultContext (specContext spec)
+              harness = generatedTextEndingIn "Harness.hs" (harnessFor ctx spec aggregate)
+          harness `shouldSatisfy` T.isInfixOf "\"sample-noteText\" \"sample-echo\""
+          harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: Bump from CounterPending -- \""
+          harness `shouldSatisfy` T.isInfixOf "prefix <> \"register note\""
+        [] -> expectationFailure "forward/replay sample spec has no aggregate"
+    it "keeps inequality-guard samples distinct from register initials" $ do
+      mods <- scaffoldFixture "test/fixtures/subscription.keiro"
+      let harness = generatedTextEndingIn "Harness.hs" mods
+      harness `shouldSatisfy` T.isInfixOf "ActivateSubscriptionData"
+      harness `shouldSatisfy` T.isInfixOf "Paid"
+      harness `shouldNotSatisfy` T.isInfixOf "ActivateSubscriptionData (case parseSubscriptionId \"sub_01h455vb4pex5vsknk084sn02q\" of Right parsed -> parsed; Left _ -> error \"generated valid ID sample failed to parse\") (case parseCustomerId \"cust_01h455vb4pex5vsknk084sn02q\" of Right parsed -> parsed; Left _ -> error \"generated valid ID sample failed to parse\") Free"
+    it "uses consumer-owned nominal initials for equality-guard samples" $ do
+      mods <- scaffoldFixture "test/fixtures/nominal-scalars.keiro"
+      let harness = generatedTextEndingIn "Harness.hs" mods
+      harness `shouldSatisfy` T.isInfixOf "NominalConformance.Bindings.initialOrderId"
+      harness `shouldNotSatisfy` T.isInfixOf "case parseOrderId"
+    it "emits the canonical reservation register checks" $ do
+      mods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      let harness = generatedTextEndingIn "Harness.hs" mods
+      harness `shouldSatisfy` T.isInfixOf "prefix = \"forward/replay equality: RequestTransferReservation from ReservationUnrequested -- \""
+      harness `shouldSatisfy` T.isInfixOf "prefix <> \"register reservationId\""
+      harness `shouldSatisfy` T.isInfixOf "prefix <> \"register hospitalId\""
+      harness `shouldSatisfy` T.isInfixOf "prefix <> \"register patientAcuity\""
+      harness `shouldNotSatisfy` T.isInfixOf "prefix <> \"register reservationState\""
+    it "lowers a replay-only transition to B.replayOnly in the holes skeleton (plan 143)" $ do
+      twinMods <- scaffoldFixture "test/fixtures/reservation-guard-tightened-twin.keiro"
+      map moduleText twinMods `shouldSatisfy` any (T.isInfixOf "B.replayOnly")
+      let twinHarness = generatedTextEndingIn "Harness.hs" twinMods
+      T.count "forwardReplayRequestTransferReservation ::" twinHarness `shouldBe` 1
+      plainMods <- scaffoldFixture "test/fixtures/reservation.keiro"
+      map moduleText plainMods `shouldSatisfy` all (not . T.isInfixOf "B.replayOnly")
+
+  describe "service workspace (EP-153)" $ do
+    describe "manifest grammar" $ do
+      it "round-trips the canonical fixture manifest byte-for-byte" $ do
+        source <- readTestText canonicalWorkspacePath
+        manifest <- shouldParseManifest canonicalWorkspacePath source
+        wmfService manifest `shouldBe` "demo-project"
+        wmfModuleRoot manifest `shouldBe` Just "Demo.Modules.Project"
+        wmfLayout manifest `shouldBe` Just CollocatedLeaf
+        map wmrPath (NE.toList (wmfMembers manifest))
+          `shouldBe` [ "domain/project-artifact.keiro",
+                       "domain/project.keiro",
+                       "domain/shared.keiro"
+                     ]
+        renderWorkspaceManifest manifest
+          `shouldBe` T.intercalate
+            "\n"
+            [ "service demo-project",
+              "module Demo.Modules.Project",
+              "layout collocated",
+              "spec domain/project-artifact.keiro",
+              "spec domain/project.keiro",
+              "spec domain/shared.keiro"
+            ]
+      it "treats membership as a set: source order changes neither the AST nor the bytes" $ do
+        canonical <- readTestText canonicalWorkspacePath >>= shouldParseManifest canonicalWorkspacePath
+        reordered <-
+          shouldParseManifest "<reordered>" $
+            T.unlines
+              [ "service demo-project",
+                "layout collocated",
+                "spec domain/shared.keiro",
+                "module Demo.Modules.Project",
+                "spec domain/project.keiro",
+                "spec ./domain/project-artifact.keiro"
+              ]
+        reordered `shouldBe` canonical
+        renderWorkspaceManifest reordered `shouldBe` renderWorkspaceManifest canonical
+      it "satisfies parse . render == id and render . parse . render == render" $
+        property $
+          forAll genWorkspaceManifest $ \manifest ->
+            let rendered = renderWorkspaceManifest manifest
+             in case parseWorkspaceManifest "<generated>" rendered of
+                  Left err -> counterexample (T.unpack err) False
+                  Right reparsed ->
+                    counterexample (T.unpack rendered) $
+                      reparsed == manifest && renderWorkspaceManifest reparsed == rendered
+      it "recognizes a workspace manifest by extension, case-insensitively" $ do
+        map
+          isWorkspacePath
+          [ "service.keiro-workspace",
+            "a/b/Service.KEIRO-Workspace",
+            "service.keiro",
+            ".keiro-workspace",
+            "keiro-workspace"
+          ]
+          `shouldBe` [True, True, False, False, False]
+    describe "manifest refusals" $ do
+      let rejects description source expected =
+            it description $ case parseWorkspaceManifest "<manifest>" source of
+              Right _ -> expectationFailure ("expected a refusal, got a manifest for:\n" <> T.unpack source)
+              Left err -> T.unpack err `shouldContain` expected
+      rejects
+        "an empty manifest"
+        "# only a comment\n"
+        "must begin with a 'service <name>' clause"
+      rejects
+        "a manifest with no service clause"
+        "spec domain/a.keiro\n"
+        "first clause of a workspace manifest must be 'service <name>'"
+      rejects
+        "a manifest whose first clause is not service"
+        "module Demo\nservice demo\nspec domain/a.keiro\n"
+        "first clause of a workspace manifest must be 'service <name>'"
+      rejects
+        "a duplicate service clause"
+        "service demo\nservice demo\nspec domain/a.keiro\n"
+        "duplicate 'service' clause"
+      rejects
+        "a duplicate module clause"
+        "service demo\nmodule Demo\nmodule Demo\nspec domain/a.keiro\n"
+        "duplicate 'module' clause"
+      rejects
+        "a duplicate layout clause"
+        "service demo\nlayout prefixed\nlayout prefixed\nspec domain/a.keiro\n"
+        "duplicate 'layout' clause"
+      rejects
+        "a manifest with no members"
+        "service demo\nmodule Demo\n"
+        "must list at least one 'spec <path>.keiro' member"
+      rejects
+        "the same member listed twice"
+        "service demo\nspec domain/a.keiro\nspec ./domain/a.keiro\n"
+        "duplicate workspace member 'domain/a.keiro'"
+      rejects
+        "two members that differ only by case"
+        "service demo\nspec domain/a.keiro\nspec domain/A.keiro\n"
+        "differ only by case"
+      rejects
+        "an absolute member path"
+        "service demo\nspec /etc/a.keiro\n"
+        "must be relative, not absolute"
+      rejects
+        "a member path escaping the manifest directory"
+        "service demo\nspec ../escape.keiro\n"
+        "must not contain '..' segments"
+      rejects
+        "a member that is not a .keiro spec"
+        "service demo\nspec domain/a.txt\n"
+        "must name a .keiro spec"
+      rejects
+        "a manifest listing another manifest"
+        "service demo\nspec domain/other.keiro-workspace\n"
+        "must name a .keiro spec"
+    describe "line relocation" $ do
+      it "shifts every location the AST carries, and only the locations" $ do
+        spec <- specOf "test/fixtures/reservation.keiro"
+        let shifted = relocateLocs (+ 1000) spec
+        collectLocs spec `shouldSatisfy` (not . null)
+        collectLocs shifted `shouldBe` map (+ 1000) (collectLocs spec)
+        -- Loc's Eq deliberately ignores the line, so relocation cannot
+        -- change any equality-based behavior anywhere downstream.
+        shifted `shouldBe` spec
+      it "leaves the placeholder location alone so it never lands inside a member range" $ do
+        spec <- specOf "test/fixtures/reservation.keiro"
+        let blanked = relocateLocs (const 0) spec
+            reshifted = relocateLocs (\n -> if n <= 0 then n else n + 500) blanked
+        collectLocs reshifted `shouldBe` map (const 0) (collectLocs spec)
+    describe "composition" $ do
+      it "resolves cross-file ids, enums, mapped types, and read-model feeds" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        wsService workspace `shouldBe` "demo-project"
+        wsContext workspace `shouldBe` "demo-project"
+        wsModuleRoot workspace `shouldBe` Just "Demo.Modules.Project"
+        wsLayout workspace `shouldBe` Just CollocatedLeaf
+        map wmPath (wsMembers workspace)
+          `shouldBe` [ "domain/project-artifact.keiro",
+                       "domain/project.keiro",
+                       "domain/shared.keiro"
+                     ]
+        -- Every member is individually incomplete; together they check.
+        checkWorkspace workspace `shouldBe` []
+      it "records which member owns each shared declaration and node" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let ownership = wsOwnership workspace
+        fmap fst (declarationOwner ownership "id" "ProjectId")
+          `shouldBe` Just "domain/shared.keiro"
+        fmap fst (declarationOwner ownership "enum" "ProjectPhase")
+          `shouldBe` Just "domain/shared.keiro"
+        fmap fst (declarationOwner ownership "rule" "phaseIsTerminal")
+          `shouldBe` Just "domain/shared.keiro"
+        fmap fst (declarationOwner ownership "mapped" "ProjectSummary")
+          `shouldBe` Just "domain/shared.keiro"
+        fmap fst (nodeOwner ownership "aggregate" "Project")
+          `shouldBe` Just "domain/project.keiro"
+        fmap fst (nodeOwner ownership "aggregate" "ProjectArtifact")
+          `shouldBe` Just "domain/project-artifact.keiro"
+        fmap fst (nodeOwner ownership "readmodel" "project_activity")
+          `shouldBe` Just "domain/project-artifact.keiro"
+      it "maps every merged line back to the member that wrote it" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let bases = [(wmPath m, wmLineBase m, wmLineCount m) | m <- wsMembers workspace]
+        -- Ranges are disjoint and contiguous from zero.
+        map (\(_, base, _) -> base) bases `shouldBe` scanl (+) 0 (init [c | (_, _, c) <- bases])
+        sequence_
+          [ resolveWorkspaceLine workspace (base + offset) `shouldBe` Just (path, offset)
+          | (path, base, memberLines) <- bases,
+            offset <- [1, memberLines]
+          ]
+        resolveWorkspaceLine workspace 0 `shouldBe` Nothing
+      it "is insensitive to the order members are listed in" $ do
+        canonical <- shouldComposeWorkspace canonicalWorkspacePath
+        reordered <- shouldComposeWorkspace reorderedWorkspacePath
+        reordered {wsManifestPath = wsManifestPath canonical} `shouldBe` canonical
+      it "checks a single .keiro file as a one-member workspace, diagnostic for diagnostic" $ do
+        let fixtures =
+              [ "test/fixtures/reservation.keiro",
+                "test/fixtures/consumer-types.keiro",
+                "test/fixtures/aggregate-bad-refs.keiro",
+                "test/fixtures/readmodel.keiro"
+              ]
+        forM_ fixtures $ \path -> do
+          spec <- specOf path
+          let workspace = oneMemberWorkspace path spec
+              viaWorkspace = map (renderWorkspaceDiagnostic path) (checkWorkspace workspace)
+              direct = map (renderDiagnostic path) (validateSpec spec)
+          viaWorkspace `shouldBe` direct
+        -- At least one of those fixtures must actually produce errors,
+        -- or the equivalence claim is vacuous.
+        badRefs <- specOf "test/fixtures/aggregate-bad-refs.keiro"
+        checkWorkspace (oneMemberWorkspace "test/fixtures/aggregate-bad-refs.keiro" badRefs)
+          `shouldSatisfy` any ((== Error) . wdSeverity)
+    describe "composition refusals" $ do
+      let refusesWith path expectedCode expectedFiles = do
+            diagnostics <- shouldRefuseWorkspace path
+            map wdCode (NE.toList diagnostics) `shouldContain` [expectedCode]
+            let cited =
+                  [ wlFile location
+                  | diagnostic <- NE.toList diagnostics,
+                    wdCode diagnostic == expectedCode,
+                    location <- NE.toList (wdLocations diagnostic)
+                  ]
+            sort (nubOrd cited) `shouldBe` sort expectedFiles
+      it "refuses members that declare different contexts, citing every context clause" $
+        refusesWith
+          "test/fixtures/workspace-context-mismatch/service.keiro-workspace"
+          WorkspaceContextMismatch
+          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
+      it "refuses a member layout clause that contradicts the manifest authority" $
+        refusesWith
+          "test/fixtures/workspace-authority-conflict/service.keiro-workspace"
+          WorkspaceAuthorityConflict
+          [WorkspaceManifestFile, WorkspaceMemberFile "domain/b.keiro"]
+      it "refuses a textually identical shared declaration owned by two members" $
+        refusesWith
+          "test/fixtures/workspace-dup-decl/service.keiro-workspace"
+          WorkspaceDuplicateDeclaration
+          [WorkspaceMemberFile "domain/project.keiro", WorkspaceMemberFile "domain/shared.keiro"]
+      it "refuses one aggregate defined in two members" $
+        refusesWith
+          "test/fixtures/workspace-dup-node/service.keiro-workspace"
+          WorkspaceDuplicateNodeName
+          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
+      it "refuses generated paths that collide across members under case folding" $
+        refusesWith
+          "test/fixtures/workspace-path-collision/service.keiro-workspace"
+          WorkspacePathCollision
+          [WorkspaceMemberFile "domain/a.keiro", WorkspaceMemberFile "domain/b.keiro"]
+      it "reports a listed member that is missing from disk" $
+        refusesWith
+          "test/fixtures/workspace-missing-member/service.keiro-workspace"
+          WorkspaceMemberUnreadable
+          [WorkspaceManifestFile]
+      it "reports a member that does not parse" $
+        refusesWith
+          "test/fixtures/workspace-member-parse-failed/service.keiro-workspace"
+          WorkspaceMemberParseFailed
+          [WorkspaceManifestFile]
+      it "surfaces a cross-file unresolved reference through the merged validator" $ do
+        workspace <- shouldComposeWorkspace "test/fixtures/workspace-unresolved/service.keiro-workspace"
+        let errors = [d | d <- checkWorkspace workspace, wdSeverity d == Error]
+        map wdCode errors `shouldContain` [GuardAtomOutOfScope]
+        [wlFile location | d <- errors, location <- NE.toList (wdLocations d)]
+          `shouldContain` [WorkspaceMemberFile "domain/project.keiro"]
+    describe "multi-file diagnostic rendering" $ do
+      it "puts the primary location in the established shape and every other file on a note line" $ do
+        diagnostics <- shouldRefuseWorkspace "test/fixtures/workspace-dup-decl/service.keiro-workspace"
+        let manifest = "keiro-dsl/test/fixtures/workspace-dup-decl/service.keiro-workspace"
+        map (renderWorkspaceDiagnostic manifest) (NE.toList diagnostics)
+          `shouldBe` [ T.intercalate
+                         "\n"
+                         [ "keiro-dsl/test/fixtures/workspace-dup-decl/domain/project.keiro:4: error[WorkspaceDuplicateDeclaration]: duplicate declaration 'ProjectId': a shared declaration has exactly one owning member (identical duplicates do not merge)",
+                           "  keiro-dsl/test/fixtures/workspace-dup-decl/domain/shared.keiro:4: note: also declared here, as id 'ProjectId'"
+                         ]
+                     ]
+    describe "whole-service check through the CLI" $ do
+      it "prints OK and exits zero for the composed fixture workspace" $ do
+        (exitCode, out, err) <- runKeiroDsl ["check", canonicalWorkspacePath]
+        unless (exitCode == ExitSuccess) (expectationFailure (out <> err))
+        lines out `shouldBe` ["OK"]
+      it "exits non-zero and names every involved file for a cross-file refusal" $ do
+        (exitCode, _, err) <-
+          runKeiroDsl ["check", "test/fixtures/workspace-dup-decl/service.keiro-workspace"]
+        exitCode `shouldBe` ExitFailure 1
+        err `shouldContain` "error[WorkspaceDuplicateDeclaration]"
+        err `shouldContain` "workspace-dup-decl/domain/project.keiro:4"
+        err `shouldContain` "workspace-dup-decl/domain/shared.keiro:4"
+      it "attributes a merged-graph validation error to the member that wrote it" $ do
+        (exitCode, _, err) <-
+          runKeiroDsl ["check", "test/fixtures/workspace-unresolved/service.keiro-workspace"]
+        exitCode `shouldBe` ExitFailure 1
+        err `shouldContain` "workspace-unresolved/domain/project.keiro:12: error[GuardAtomOutOfScope]"
+      it "produces byte-identical output for a manifest whose members are listed in reverse" $ do
+        (canonicalCode, canonicalOut, _) <- runKeiroDsl ["check", canonicalWorkspacePath, "--emit"]
+        (reorderedCode, reorderedOut, _) <- runKeiroDsl ["check", reorderedWorkspacePath, "--emit"]
+        canonicalCode `shouldBe` ExitSuccess
+        reorderedCode `shouldBe` ExitSuccess
+        reorderedOut `shouldBe` canonicalOut
+        (_, canonicalParse, _) <- runKeiroDsl ["parse", canonicalWorkspacePath]
+        (_, reorderedParse, _) <- runKeiroDsl ["parse", reorderedWorkspacePath]
+        reorderedParse `shouldBe` canonicalParse
+      it "keeps the single-file path working, byte for byte" $ do
+        (exitCode, out, err) <- runKeiroDsl ["check", "test/fixtures/reservation.keiro"]
+        unless (exitCode == ExitSuccess) (expectationFailure (out <> err))
+        lines out `shouldBe` ["OK"]
+      it "explains bindings and reports coverage against the merged graph" $ do
+        (bindingsCode, bindingsOut, _) <-
+          runKeiroDsl ["check", canonicalWorkspacePath, "--explain-bindings"]
+        bindingsCode `shouldBe` ExitSuccess
+        bindingsOut `shouldContain` "binding obligations for context demo-project"
+        -- The obligation's use sites span both aggregate members, which
+        -- is only possible because the graph was resolved once, merged.
+        bindingsOut `shouldContain` "Project register summary : ProjectSummary"
+        bindingsOut `shouldContain` "ProjectArtifact command RecordArtifact .artifactSummary : ProjectSummary"
+        withTempDirectory "keiro-dsl-workspace-coverage" $ \out -> do
+          let reportPath = out </> "coverage.json"
+          (coverageCode, coverageOut, _) <-
+            runKeiroDsl ["check", canonicalWorkspacePath, "--coverage-report", reportPath]
+          coverageCode `shouldBe` ExitSuccess
+          coverageOut `shouldContain` "structural/opaque boundaries (reporting only)"
+          report <- Aeson.eitherDecodeFileStrict reportPath
+          case report of
+            Left err -> expectationFailure err
+            Right value -> coverageSpecPath value `shouldBe` Just (T.pack canonicalWorkspacePath)
+
+  describe "workspace diff revision loading (EP-155 M1)" $ do
+    it "composes added, removed, and renamed members through an in-memory content source" $ do
+      project <- readTestText "test/fixtures/workspace/domain/project.keiro"
+      artifact <- readTestText "test/fixtures/workspace/domain/project-artifact.keiro"
+      shared <- readTestText "test/fixtures/workspace/domain/shared.keiro"
+      let extra = "language keiro-dsl 4\ncontext demo-project\n\nid ExtraId prefix=extra\n"
+          manifest members =
+            T.unlines
+              ( ["service demo-project", "module Demo.Modules.Project", "layout collocated"]
+                  <> ["spec " <> T.pack member | member <- members]
+              )
+          baseFiles =
+            Map.fromList
+              [ ("domain/project.keiro", project),
+                ("domain/project-artifact.keiro", artifact),
+                ("domain/shared.keiro", shared)
+              ]
+          loadFrom members files =
+            loadWorkspace
+              (memoryContentSource (Map.insert "service.keiro-workspace" (manifest members) files))
+              "service.keiro-workspace"
+          baseMembers = ["domain/project.keiro", "domain/project-artifact.keiro", "domain/shared.keiro"]
+          expectLoaded result = case result of
+            Left failure -> expectationFailure (show failure) >> error "unreachable"
+            Right workspace -> pure workspace
+
+      oldAdded <- loadFrom baseMembers baseFiles >>= expectLoaded
+      newAdded <-
+        loadFrom
+          (baseMembers <> ["domain/extra.keiro"])
+          (Map.insert "domain/extra.keiro" extra baseFiles)
+          >>= expectLoaded
+      map changeCode (diffSpecs (wsMergedSpec oldAdded) (wsMergedSpec newAdded))
+        `shouldContain` [DeclarationAdded]
+
+      oldRemoved <- loadFrom baseMembers baseFiles >>= expectLoaded
+      newRemoved <-
+        loadFrom
+          ["domain/project.keiro", "domain/shared.keiro"]
+          (Map.delete "domain/project-artifact.keiro" baseFiles)
+          >>= expectLoaded
+      map changeCode (diffSpecs (wsMergedSpec oldRemoved) (wsMergedSpec newRemoved))
+        `shouldContain` [EvtRemovedNotDeprecated]
+
+      oldRenamed <- loadFrom baseMembers baseFiles >>= expectLoaded
+      let renamedMembers = ["domain/project-renamed.keiro", "domain/project-artifact.keiro", "domain/shared.keiro"]
+          renamedFiles = Map.insert "domain/project-renamed.keiro" project (Map.delete "domain/project.keiro" baseFiles)
+      newRenamed <- loadFrom renamedMembers renamedFiles >>= expectLoaded
+      diffSpecs (wsMergedSpec oldRenamed) (wsMergedSpec newRenamed) `shouldBe` []
+
+  describe "workspace diff ownership and unified reports (EP-155 M2)" $ do
+    it "classifies shared declarations at use sites across every member with owned citations" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
+      let changes = diffWorkspaces old new
+          enumChanges = filter ((== EnumCtorAdded) . changeCode . wcChange) changes
+          mappedChanges = filter ((== MappedFieldTypeChanged) . changeCode . wcChange) changes
+          citedFiles workspaceChanges =
+            [ osFile site
+            | change <- workspaceChanges,
+              (_, Just site) <- wcUseSites change
+            ]
+      enumChanges `shouldSatisfy` (not . null)
+      mappedChanges `shouldSatisfy` (not . null)
+      let enumWireChanges =
+            [ change
+            | workspaceChange <- enumChanges,
+              let change = wcChange workspaceChange,
+              OldBinaryReadNewEvents `elem` breakingSurfaces change
+            ]
+      enumWireChanges `shouldSatisfy` (not . null)
+      enumWireChanges `shouldSatisfy` all (not . gatedBreaking defaultGate)
+      enumWireChanges `shouldSatisfy` all (gatedBreaking (gateWith [OldBinaryReadNewEvents]))
+      map (fmap osFile . wcDeclarationSite) (enumChanges <> mappedChanges)
+        `shouldSatisfy` all (== Just "domain/shared.keiro")
+      citedFiles enumChanges `shouldContain` ["domain/order.keiro", "domain/shipment.keiro"]
+      citedFiles mappedChanges `shouldContain` ["domain/order.keiro", "domain/shipment.keiro"]
+      let rendered = T.intercalate "\n" (map renderWorkspaceFinding (enumChanges <> mappedChanges))
+      rendered `shouldSatisfy` T.isInfixOf "    declared: domain/shared.keiro:4"
+      rendered `shouldSatisfy` T.isInfixOf "    use-site: Order"
+      rendered `shouldSatisfy` T.isInfixOf "(domain/order.keiro:"
+      rendered `shouldSatisfy` T.isInfixOf "(domain/shipment.keiro:"
+      golden <- readTestText "test/fixtures/workspace-diff-new/workspace.diff.golden"
+      T.unlines (map renderWorkspaceFinding changes) `shouldBe` golden
+
+    it "emits one additive version-1 report with workspace provenance" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
+      let changes = diffWorkspaces old new
+          meta =
+            WorkspaceMeta
+              { wmIdentity = wsService new,
+                wmManifest = "service.keiro-workspace",
+                wmSince = "HEAD",
+                wmMembersOld = map wmPath (wsMembers old),
+                wmMembersNew = map wmPath (wsMembers new),
+                wmAdoptionBaseline = False
+              }
+      case Aeson.toJSON (workspaceDiffReport meta defaultGate changes) of
+        Aeson.Object report -> do
+          KeyMap.lookup "schema" report `shouldBe` Just (Aeson.String "keiro-dsl/diff-report/1")
+          case KeyMap.lookup "workspace" report of
+            Just (Aeson.Object workspace) -> do
+              KeyMap.lookup "identity" workspace `shouldBe` Just (Aeson.String "workspace-diff")
+              KeyMap.lookup "adoptionBaseline" workspace `shouldBe` Just (Aeson.Bool False)
+            other -> expectationFailure ("missing workspace report metadata: " <> show other)
+          case KeyMap.lookup "findings" report of
+            Just (Aeson.Array findings) -> do
+              findings `shouldSatisfy` (not . null)
+              let objects = [finding | Aeson.Object finding <- toList findings]
+              objects `shouldSatisfy` any (KeyMap.member "declaration")
+              objects `shouldSatisfy` any (KeyMap.member "useSites")
+            other -> expectationFailure ("missing workspace findings: " <> show other)
+        other -> expectationFailure ("workspace report was not an object: " <> show other)
+
+    it "computes one replay-impact value over both aggregates" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      new <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
+      case replayImpactSpecs (wsMergedSpec old) (wsMergedSpec new) of
+        ReplayAffected affected -> Map.keysSet affected `shouldBe` Set.fromList ["Order", "Shipment"]
+        ReplayNeutral -> expectationFailure "shared mapped evolution unexpectedly reported replay-neutral"
+
+  describe "workspace ownership and authority changes (EP-155 M3)" $ do
+    it "reports an unchanged aggregate move once without wire evolution" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      moved <- shouldComposeWorkspace "test/fixtures/workspace-diff-moved/service.keiro-workspace"
+      let changes = diffWorkspaces old moved
+      map (changeCode . wcChange) changes `shouldBe` [OwnershipMoved]
+      forM_ changes $ \workspaceMove -> do
+        let move = wcChange workspaceMove
+        move `shouldSatisfy` isAdvisory
+        move `shouldSatisfy` (not . gatedBreaking defaultGate)
+        move `shouldSatisfy` (not . gatedBreaking (gateWith [minBound .. maxBound]))
+        deriveLabel defaultGate (ckVector (workspaceChangeKind move)) `shouldBe` LabelAdvisory
+        remediationFor (ckContext (workspaceChangeKind move)) OwnershipMoved
+          `shouldBe` (RemedyRescaffoldWorkspace :| [])
+        renderWorkspaceFinding workspaceMove
+          `shouldSatisfy` T.isInfixOf "declaration moved domain/shipment.keiro -> domain/order.keiro"
+
+    it "treats a member rename as the same owner-map change" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      let ownership = wsOwnership old
+          renamed =
+            old
+              { wsOwnership =
+                  ownership
+                    { oiNodes =
+                        Map.adjust
+                          (\(_, loc) -> ("domain/shipping.keiro", loc))
+                          ("aggregate", "Shipment")
+                          (oiNodes ownership)
+                    }
+              }
+          moves = filter ((== OwnershipMoved) . changeCode . wcChange) (diffWorkspaces old renamed)
+      length moves `shouldBe` 1
+      forM_ moves $ \move ->
+        renderWorkspaceFinding move `shouldSatisfy` T.isInfixOf "domain/shipment.keiro -> domain/shipping.keiro"
+
+    it "reports ownership motion beside an independently classified wire edit" $ do
+      old <- shouldComposeWorkspace "test/fixtures/workspace-diff-old/service.keiro-workspace"
+      edited <- shouldComposeWorkspace "test/fixtures/workspace-diff-new/service.keiro-workspace"
+      let ownership = wsOwnership edited
+          movedAndEdited =
+            edited
+              { wsOwnership =
+                  ownership
+                    { oiNodes =
+                        Map.adjust
+                          (\(_, loc) -> ("domain/order.keiro", loc))
+                          ("aggregate", "Shipment")
+                          (oiNodes ownership)
+                    }
+              }
+          codes = map (changeCode . wcChange) (diffWorkspaces old movedAndEdited)
+      codes `shouldContain` [OwnershipMoved]
+      codes `shouldContain` [MappedFieldTypeChanged]
+
+    it "reports context authority separately from derived read-model identity breaks" $ do
+      old <- shouldComposeWorkspace canonicalWorkspacePath
+      let newContext = "demo-project-renamed"
+          renamed =
+            old
+              { wsContext = newContext,
+                wsMergedSpec = (wsMergedSpec old) {specContext = newContext}
+              }
+          changes = diffWorkspaces old renamed
+          codes = map (changeCode . wcChange) changes
+      codes `shouldContain` [WorkspaceAuthorityChanged]
+      codes `shouldContain` [DerivedIdentityChanged]
+      map wcChange changes `shouldSatisfy` any (gatedBreaking defaultGate)
+
+    it "keeps service, module-root, and layout authority advisories non-blocking" $ do
+      old <- shouldComposeWorkspace canonicalWorkspacePath
+      let changed =
+            old
+              { wsService = "demo-project-renamed",
+                wsModuleRoot = Just "Demo.Modules.Renamed",
+                wsLayout = Just GeneratedPrefix
+              }
+          authority = filter ((== WorkspaceAuthorityChanged) . changeCode . wcChange) (diffWorkspaces old changed)
+      length authority `shouldBe` 3
+      forM_ (map wcChange authority) $ \change -> do
+        deriveLabel defaultGate (ckVector (workspaceChangeKind change)) `shouldBe` LabelAdvisory
+        change `shouldSatisfy` (not . gatedBreaking (gateWith [minBound .. maxBound]))
+        remediationFor (ckContext (workspaceChangeKind change)) WorkspaceAuthorityChanged
+          `shouldBe` (RemedyRescaffoldWorkspace :| [RemedyRecompileConsumers])
+
+  describe "workspace scaffold (EP-154)" $ do
+    describe "workspace record" $ do
+      it "round-trips modules, owners, members, mappings, obligations, and adoptions" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let record = sampleWorkspaceRecord workspace
+            rendered = renderWorkspaceRecord record
+        parseWorkspaceRecord rendered `shouldBe` Just record
+        -- The header pins the schema: a v1 context-keyed record and a
+        -- workspace record can never be read as each other.
+        T.lines rendered `shouldSatisfy` \case
+          header : _ -> header == "keiro-dsl workspace scaffold record v1"
+          [] -> False
+        parseRecord rendered `shouldBe` Nothing
+        parseWorkspaceRecord (T.replace "record v1" "record v2" rendered) `shouldBe` Nothing
+      it "ignores unknown rows and unknown JSON keys, and keeps context-level rows ownerless" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let record = sampleWorkspaceRecord workspace
+            rendered = renderWorkspaceRecord record
+        parseWorkspaceRecord (T.replace "service: " "future-row: retained\nservice: " rendered)
+          `shouldBe` Just record
+        parseWorkspaceRecord (T.replace "\"kind\":\"generated\"" "\"kind\":\"generated\",\"future\":1" rendered)
+          `shouldBe` Just record
+        [row | row <- wrModules record, wrmOwner row == Nothing]
+          `shouldSatisfy` (not . null)
+      it "rejects absent stable language rows and partial, duplicate, malformed, or inconsistent contracts" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let record = sampleWorkspaceRecord workspace
+            rendered = renderWorkspaceRecord record
+            sourceRows = filter ("source-language " `T.isPrefixOf`) (T.lines rendered)
+            withoutSourceRows = T.unlines (filter (not . T.isPrefixOf "source-language ") (T.lines rendered))
+            semanticRows = filter ("semantic-contract " `T.isPrefixOf`) (T.lines rendered)
+            withoutSemanticRows = T.unlines (filter (not . T.isPrefixOf "semantic-contract ") (T.lines rendered))
+        parseWorkspaceRecord withoutSourceRows `shouldBe` Nothing
+        case sourceRows of
+          firstRow : secondRow : _ -> do
+            parseWorkspaceRecord (T.unlines (filter (/= secondRow) (T.lines rendered))) `shouldBe` Nothing
+            parseWorkspaceRecord (T.replace firstRow (firstRow <> "\n" <> firstRow) rendered) `shouldBe` Nothing
+            parseWorkspaceRecord (T.replace firstRow "source-language {malformed}" rendered) `shouldBe` Nothing
+          _ -> expectationFailure "expected multiple workspace source-language rows"
+        parseWorkspaceRecord withoutSemanticRows `shouldBe` Just record
+        case semanticRows of
+          [semanticRow] -> do
+            parseWorkspaceRecord (T.replace semanticRow (semanticRow <> "\n" <> semanticRow) rendered) `shouldBe` Nothing
+            parseWorkspaceRecord (T.replace semanticRow "semantic-contract {malformed}" rendered) `shouldBe` Nothing
+            parseWorkspaceRecord (T.replace "\"languageVersion\":4" "\"languageVersion\":3" rendered) `shouldBe` Nothing
+          _ -> expectationFailure "expected one workspace semantic-contract row"
+      it "rejects unsafe module, owner, member, and adoption paths" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let rendered = renderWorkspaceRecord (sampleWorkspaceRecord workspace)
+            corrupt from to = parseWorkspaceRecord (T.replace from to rendered)
+        corrupt "member domain/shared.keiro" "member /etc/passwd" `shouldBe` Nothing
+        corrupt "member domain/shared.keiro" "member ../escape.keiro" `shouldBe` Nothing
+        corrupt "\"owner\":\"domain/shared.keiro\"" "\"owner\":\"../shared.keiro\"" `shouldBe` Nothing
+        corrupt "\"path\":\"claimed/One.hs\"" "\"path\":\"/tmp/One.hs\"" `shouldBe` Nothing
+      it "keys history by service in a slot no context name can reach" $ do
+        -- A context name is lexed as letters/digits/_/- and can never
+        -- contain a dot, so the workspace slot cannot alias a legacy
+        -- record even when the service is named after its context.
+        workspaceRecordFileName "demo-project"
+          `shouldBe` "keiro-dsl-scaffold-record.workspace.demo-project.txt"
+        workspaceManifestFileName "demo-project"
+          `shouldBe` "keiro-dsl-manifest.workspace.demo-project.txt"
+        workspaceRecordFileName "demo-project" `shouldNotBe` recordFileName "demo-project"
+        map
+          (T.isInfixOf "." . T.pack)
+          [ workspaceRecordFileName "demo-project",
+            recordFileName "demo-project"
+          ]
+          `shouldBe` [True, True]
+        supersededByLine "demo-project"
+          `shouldBe` "superseded-by: keiro-dsl-scaffold-record.workspace.demo-project.txt"
+
+    describe "workspace plan" $ do
+      it "emits the context-level facade and replay-audit exactly once from the merged graph" $ do
+        plan <- shouldPlanWorkspace canonicalWorkspacePath
+        let modules = map fst (wpModules plan)
+            facades = [m | m <- modules, "StructuralProjections.hs" `isSuffixOfPath` m]
+            audits = [m | m <- modules, "ReplayAudit.hs" `isSuffixOfPath` m]
+            shapes = [m | m <- modules, "Structural/Shape/ProjectSummary.hs" `isSuffixOfPath` m]
+        length facades `shouldBe` 1
+        length audits `shouldBe` 1
+        length shapes `shouldBe` 1
+        -- The audit assembles aggregates owned by two different member
+        -- files, which is only possible from one merged graph.
+        forM_ audits $ \audit -> do
+          moduleText audit `shouldSatisfy` T.isInfixOf "Project.projectEventStream"
+          moduleText audit `shouldSatisfy` T.isInfixOf "ProjectArtifact.projectArtifactEventStream"
+      it "gives every generated ID and enum one context owner and imports only aggregate uses" $ do
+        plan <- shouldPlanWorkspace canonicalWorkspacePath
+        let ctx = wpContext plan
+            modules = map fst (wpModules plan)
+            nominalModules = [m | m <- modules, modulePath m == T.unpack (T.replace "." "/" (generatedNominalModule ctx) <> ".hs")]
+            internalNominalModules = [m | m <- modules, modulePath m == T.unpack (T.replace "." "/" (generatedNominalModule ctx) <> "/Internal.hs")]
+            domainFor suffix = case [m | m <- modules, suffix `isSuffixOfPath` m] of
+              [m] -> pure m
+              found -> expectationFailure ("expected one domain ending in " <> suffix <> ", got " <> show (map modulePath found)) >> fail "unreachable"
+        ownerModule <- case nominalModules of
+          [m] -> pure m
+          found -> expectationFailure ("expected one generated nominal owner, got " <> show (map modulePath found)) >> fail "unreachable"
+        internalOwnerModule <- case internalNominalModules of
+          [m] -> pure m
+          found -> expectationFailure ("expected one generated internal nominal owner, got " <> show (map modulePath found)) >> fail "unreachable"
+        let nominalText = moduleText ownerModule
+            internalNominalText = moduleText internalOwnerModule
+        T.count "newtype ProjectId" nominalText `shouldBe` 0
+        T.count "newtype ProjectId" internalNominalText `shouldBe` 1
+        T.count "data ProjectPhase =" nominalText `shouldBe` 1
+        T.count "data WorkspaceVisibility =" nominalText `shouldBe` 1
+        projectDomain <- domainFor "Project/Generated/Domain.hs"
+        artifactDomain <- domainFor "ProjectArtifact/Generated/Domain.hs"
+        forM_ [projectDomain, artifactDomain] $ \domain -> do
+          moduleText domain `shouldSatisfy` (not . T.isInfixOf "newtype ProjectId")
+          moduleText domain `shouldSatisfy` (not . T.isInfixOf "data ProjectPhase")
+          moduleText domain `shouldSatisfy` T.isInfixOf (generatedNominalModule ctx <> " (ProjectId, parseProjectId, ProjectPhase (..))")
+          moduleText domain `shouldSatisfy` (not . T.isInfixOf "WorkspaceVisibility")
+        singleFileModules <- case planServiceScaffold ctx (stableCheckedService (wsMergedSpec (wpWorkspace plan))) of
+          Left refusals -> expectationFailure (show refusals) >> fail "unreachable"
+          Right values -> pure values
+        let withoutOrigin m = (modulePath m, moduleText m, kind m)
+        map withoutOrigin singleFileModules `shouldBe` map withoutOrigin modules
+        owners <- case planNominalGeneration ctx (wsMergedSpec (wpWorkspace plan)) of
+          Left errors -> expectationFailure (show errors) >> fail "unreachable"
+          Right values -> pure values
+        map (resolvedNominalName . nominalDeclaration) owners
+          `shouldBe` ["ProjectId", "ProjectPhase", "WorkspaceVisibility"]
+        case [owner | owner <- owners, resolvedNominalName (nominalDeclaration owner) == "ProjectId"] of
+          [owner] -> do
+            nominalModule owner `shouldBe` generatedNominalModule ctx
+            Set.fromList [NominalUseSite "Project" RegisterUse, NominalUseSite "ProjectArtifact" EventFieldUse]
+              `shouldSatisfy` (`Set.isSubsetOf` nominalUseSites owner)
+          found -> expectationFailure ("expected one ProjectId owner, got " <> show (length found))
+      it "attributes every module to its owning member and leaves shared ones context-level" $ do
+        plan <- shouldPlanWorkspace canonicalWorkspacePath
+        let memberPaths = map wmPath (wsMembers (wpWorkspace plan))
+            ownerOf suffix =
+              case [provenance | (m, provenance) <- wpModules plan, suffix `isSuffixOfPath` m] of
+                [provenance] -> Just provenance
+                _ -> Nothing
+        ownerOf "StructuralProjections.hs" `shouldBe` Just ContextLevel
+        ownerOf "Generated/Nominals.hs" `shouldBe` Just ContextLevel
+        ownerOf "ReplayAudit.hs" `shouldBe` Just ContextLevel
+        ownerOf "Structural/Shape/ProjectSummary.hs"
+          `shouldBe` Just (MemberOwned "domain/shared.keiro")
+        ownerOf "Project/Generated/Domain.hs"
+          `shouldBe` Just (MemberOwned "domain/project.keiro")
+        ownerOf "ProjectArtifact/Generated/Domain.hs"
+          `shouldBe` Just (MemberOwned "domain/project-artifact.keiro")
+        ownerOf "Project_activity/Generated/ReadModel.hs"
+          `shouldBe` Just (MemberOwned "domain/project-artifact.keiro")
+        -- No module may claim an owner that is not a member of the
+        -- workspace: the record's owner column has to stay resolvable.
+        map (provenanceOwner . snd) (wpModules plan)
+          `shouldSatisfy` all (maybe True (`elem` memberPaths))
+      it "keeps the compiled workspace nominal conformance tree byte-current" $ do
+        workspace <- shouldComposeWorkspace "test/fixtures/workspace-nominals/service.keiro-workspace"
+        plan <- shouldPlanWorkspaceSpec workspace
+        let compiledPaths =
+              [ "Generated/WorkspaceNominalProof/Nominals.hs",
+                "Generated/WorkspaceNominalProof/Project/Domain.hs",
+                "Generated/WorkspaceNominalProof/Project/Codec.hs",
+                "Generated/WorkspaceNominalProof/Project/Transducer.hs",
+                "Generated/WorkspaceNominalProof/Project/BehaviorContract.hs",
+                "Generated/WorkspaceNominalProof/Project/EventStream.hs",
+                "Generated/WorkspaceNominalProof/Project/Harness.hs",
+                "Generated/WorkspaceNominalProof/Project/Projection.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs",
+                "Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs",
+                "Generated/WorkspaceNominalProof/ReplayAudit.hs"
+              ]
+        map fst (wpModules plan) `shouldSatisfy` all (not . isSuffixOfPath "/Holes.hs")
+        forM_ compiledPaths $ \path ->
+          case [m | (m, _) <- wpModules plan, modulePath m == path] of
+            [generated] -> do
+              committed <- readTestText ("test/conformance-workspace-nominals/" <> path)
+              normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText generated)
+            found -> expectationFailure ("expected one generated module at " <> path <> ", got " <> show (map modulePath found))
+      it "plans a one-member workspace byte-identically to the single-file path" $ do
+        let fixtures =
+              [ "test/fixtures/reservation.keiro",
+                "test/fixtures/consumer-types.keiro",
+                "test/fixtures/readmodel.keiro",
+                "test/fixtures/hospital-surge.keiro"
+              ]
+        -- Modules and refusals both: hospital-surge refuses on both
+        -- paths, which proves the gates agree as well as the emitters.
+        forM_ fixtures $ \path -> do
+          spec <- specOf path
+          let ctx = defaultContext (specContext spec)
+              workspace = oneMemberWorkspace path spec
+          fmap (map fst . wpModules) (planWorkspaceScaffold "goldens" ctx workspace)
+            `shouldBe` planScaffold ctx spec
+        -- The equality is not vacuous: at least one fixture plans, and
+        -- its per-node modules are attributed to the single member.
+        spec <- specOf "test/fixtures/reservation.keiro"
+        let workspace = oneMemberWorkspace "test/fixtures/reservation.keiro" spec
+        case planWorkspaceScaffold "goldens" (defaultContext (specContext spec)) workspace of
+          Left refusals -> expectationFailure ("reservation should plan: " <> show refusals)
+          Right plan -> do
+            wpModules plan `shouldSatisfy` (not . null)
+            map snd (wpModules plan)
+              `shouldSatisfy` all (`elem` [ContextLevel, MemberOwned "reservation.keiro"])
+            map snd (wpModules plan)
+              `shouldSatisfy` elem (MemberOwned "reservation.keiro")
+      it "computes obligations from the complete merged graph, spanning members" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        case bindingObligations (wsMergedSpec workspace) of
+          Left graphErrors -> expectationFailure ("merged graph did not resolve: " <> show graphErrors)
+          Right obligations ->
+            case [o | o <- obligations, obligationMappedName o == "ProjectSummary", obligationKind o == BindingValue] of
+              [obligation] -> do
+                obligationUseSites obligation
+                  `shouldSatisfy` any (T.isInfixOf "Project register summary")
+                obligationUseSites obligation
+                  `shouldSatisfy` any (T.isInfixOf "ProjectArtifact command RecordArtifact")
+              found -> expectationFailure ("expected one ProjectSummary binding obligation, got " <> show (length found))
+      it "refuses a case-folded path collision across members, naming both files" $ do
+        workspace <- shouldComposeWorkspace canonicalWorkspacePath
+        let collided = withCaseVariantAggregate workspace
+        case planWorkspaceScaffold "goldens" (workspaceContext collided) collided of
+          Right _ -> expectationFailure "expected a cross-member path collision refusal"
+          Left refusals -> do
+            let origins = concat [os | PathCollision _ os <- refusals]
+            origins `shouldSatisfy` any (T.isInfixOf "domain/project.keiro: ")
+            origins `shouldSatisfy` any (T.isInfixOf "domain/project-artifact.keiro: ")
+      it "refuses golden fixtures stranded beside a member instead of under the workspace root" $
+        withTempDirectory "keiro-dsl-workspace-goldens" $ \root -> do
+          workspace <- writeGoldenWorkspace root
+          let workspaceGoldens = root </> "golden-payloads"
+              fixture = "hospital-capacity/Reservation/TransferReservationCreated.v1.json"
+              beside = root </> "domain/golden-payloads" </> fixture
+          goldenRootDivergence workspaceGoldens workspace `shouldReturn` []
+          createDirectoryIfMissing True (takeDirectory beside)
+          TIO.writeFile beside "{}\n"
+          refusals <- goldenRootDivergence workspaceGoldens workspace
+          refusals `shouldBe` [GoldenRootDivergence workspaceGoldens [beside]]
+          renderRefusals refusals
+            `shouldSatisfy` any (T.isInfixOf "one golden root per workspace")
+          -- The same fixture under the workspace root is no divergence.
+          let atRoot = workspaceGoldens </> fixture
+          createDirectoryIfMissing True (takeDirectory atRoot)
+          TIO.writeFile atRoot "{}\n"
+          goldenRootDivergence workspaceGoldens workspace `shouldReturn` []
+
+    describe "workspace scaffold" $ do
+      it "writes workspace-keyed history and no context-keyed file at all" $
+        withWorkspaceFixture "keiro-dsl-workspace-history" id $ \_ out workspace -> do
+          report <- executePlannedWorkspaceScaffold out workspace
+          wsrRecordPath report
+            `shouldBe` out </> "keiro-dsl-scaffold-record.workspace.demo-project.txt"
+          wsrBuildManifestPath report
+            `shouldBe` out </> "keiro-dsl-manifest.workspace.demo-project.txt"
+          doesFileExist (out </> recordFileName "demo-project") `shouldReturn` False
+          doesFileExist (out </> "keiro-dsl-manifest.demo-project.txt") `shouldReturn` False
+          contents <- TIO.readFile (wsrRecordPath report)
+          case parseWorkspaceRecord contents of
+            Nothing -> expectationFailure ("workspace record did not parse:\n" <> T.unpack contents)
+            Just record -> do
+              wrService record `shouldBe` "demo-project"
+              wrManifest record `shouldBe` "service.keiro-workspace"
+              wrMembers record
+                `shouldBe` [ "domain/project-artifact.keiro",
+                             "domain/project.keiro",
+                             "domain/shared.keiro"
+                           ]
+              -- Context-level modules are ownerless; everything
+              -- else names the member that produced it.
+              [wrmPath row | row <- wrModules record, wrmOwner row == Nothing]
+                `shouldSatisfy` \ownerless ->
+                  length ownerless == 4
+                    && any (T.isSuffixOf "StructuralProjections.hs" . T.pack) ownerless
+                    && any (T.isSuffixOf "Nominals.hs" . T.pack) ownerless
+                    && any (T.isSuffixOf "Nominals/Internal.hs" . T.pack) ownerless
+                    && any (T.isSuffixOf "ReplayAudit.hs" . T.pack) ownerless
+              [ wrmOwner row
+                | row <- wrModules record,
+                  "Project/Generated/Domain.hs" `T.isSuffixOf` T.pack (wrmPath row)
+                ]
+                `shouldBe` [Just "domain/project.keiro"]
+      it "is idempotent: an unchanged second run rewrites nothing and reports nothing" $
+        withWorkspaceFixture "keiro-dsl-workspace-idempotent" id $ \_ out workspace -> do
+          first <- executePlannedWorkspaceScaffold out workspace
+          before <- treeSnapshot out
+          second <- executePlannedWorkspaceScaffold out workspace
+          after <- treeSnapshot out
+          after `shouldBe` before
+          map thd3 (wsrDispositions second)
+            `shouldSatisfy` all (`elem` [Unchanged, Skipped])
+          wsrStale second `shouldBe` []
+          wsrOwnershipMoves second `shouldBe` []
+          wsrMappingDrift second `shouldBe` []
+          wsrNewHoles second `shouldBe` []
+          -- The first run had to write; the claim is not vacuous.
+          map thd3 (wsrDispositions first) `shouldSatisfy` any (== Overwritten)
+          renderWorkspaceScaffoldReport second
+            `shouldSatisfy` all (not . T.isPrefixOf "stale:")
+      it "produces byte-identical output for members listed in reverse order" $
+        withWorkspaceFixture "keiro-dsl-workspace-order-a" id $ \_ outA workspaceA ->
+          withWorkspaceFixture "keiro-dsl-workspace-order-b" reverse $ \_ outB workspaceB -> do
+            _ <- executePlannedWorkspaceScaffold outA workspaceA
+            _ <- executePlannedWorkspaceScaffold outB workspaceB
+            treeB <- treeSnapshot outB
+            treeA <- treeSnapshot outA
+            treeB `shouldBe` treeA
+            map fst treeA `shouldSatisfy` elem "keiro-dsl-scaffold-record.workspace.demo-project.txt"
+      it "reports stale files only for the member that changed" $
+        withWorkspaceFixture "keiro-dsl-workspace-stale" id $ \root out workspace -> do
+          first <- executePlannedWorkspaceScaffold out workspace
+          let siblingPaths =
+                [ modulePath m
+                | (m, provenance, _) <- wsrDispositions first,
+                  provenance == MemberOwned "domain/project-artifact.keiro"
+                ]
+          siblingsBefore <- traverse (TIO.readFile . (out </>)) siblingPaths
+          renamed <- renameMemberAggregate root "domain/project.keiro" "Project" "Ledger"
+          second <- executePlannedWorkspaceScaffold out renamed
+          let stalePaths = map stalePath (wsrStale second)
+          stalePaths `shouldSatisfy` (not . null)
+          stalePaths `shouldSatisfy` all (T.isInfixOf "/Project/" . T.pack)
+          -- Nothing the sibling member owns is stale, and nothing it
+          -- owns changed on disk: no cross-member false positives.
+          stalePaths `shouldSatisfy` all (`notElem` siblingPaths)
+          siblingsAfter <- traverse (TIO.readFile . (out </>)) siblingPaths
+          siblingsAfter `shouldBe` siblingsBefore
+          forM_ stalePaths $ \path -> doesFileExist (out </> path) `shouldReturn` True
+          wsrStale second
+            `shouldSatisfy` all
+              ( \stale -> case staleKind stale of
+                  Generated -> staleGeneratedEvidence stale == Just ExactGeneratedBannerPresent
+                  HoleStub -> staleGeneratedEvidence stale == Nothing
+              )
+          renderWorkspaceScaffoldReport second
+            `shouldSatisfy` any (T.isInfixOf "keiro-dsl never deletes files.")
+          renderWorkspaceScaffoldReport second
+            `shouldSatisfy` any (T.isInfixOf "exact generated banner present; verify unchanged bytes before deleting")
+          renderWorkspaceScaffoldReport second
+            `shouldSatisfy` all (not . T.isInfixOf "safe to delete")
+      it "reports an aggregate moved between members as an ownership move, not stale churn" $
+        withWorkspaceFixture "keiro-dsl-workspace-move" id $ \root out workspace -> do
+          _ <- executePlannedWorkspaceScaffold out workspace
+          before <- treeSnapshot out
+          moved <- moveArtifactAggregate root
+          second <- executePlannedWorkspaceScaffold out moved
+          wsrStale second `shouldBe` []
+          let moves = wsrOwnershipMoves second
+          moves `shouldSatisfy` (not . null)
+          moves
+            `shouldSatisfy` all
+              ( \move ->
+                  omPrevious move == Just "domain/project-artifact.keiro"
+                    && omCurrent move == Just "domain/project.keiro"
+              )
+          map omPath moves
+            `shouldSatisfy` any (T.isInfixOf "ProjectArtifact" . T.pack)
+          -- Stable behavior contracts retain source-line attribution, so
+          -- moving a declaration may rewrite only those attributed modules.
+          -- Every unaffected module and the build manifest remain untouched.
+          map thd3 (wsrDispositions second)
+            `shouldSatisfy` all (`elem` [Unchanged, Skipped, Overwritten])
+          let overwrittenPaths =
+                [modulePath generatedModule | (generatedModule, _, Overwritten) <- wsrDispositions second]
+          length overwrittenPaths `shouldBe` 2
+          overwrittenPaths `shouldSatisfy` all (T.isSuffixOf "/Generated/BehaviorContract.hs" . T.pack)
+          after <- treeSnapshot out
+          map fst after `shouldBe` map fst before
+          let unaffected (path, _) =
+                not ("scaffold-record" `T.isInfixOf` T.pack path)
+                  && path `notElem` overwrittenPaths
+          filter unaffected after `shouldBe` filter unaffected before
+          renderWorkspaceScaffoldReport second
+            `shouldSatisfy` any (T.isInfixOf "changed owning member")
+      it "leaves the tree, record, and manifest untouched when any member refuses" $
+        withWorkspaceFixture "keiro-dsl-workspace-atomic" id $ \_ out workspace -> do
+          _ <- executePlannedWorkspaceScaffold out workspace
+          before <- treeSnapshot out
+          let broken = withCaseVariantAggregate workspace
+          case planWorkspaceScaffold "goldens" (workspaceContext broken) broken of
+            Right _ -> expectationFailure "expected the broken workspace to refuse"
+            Left refusals -> refusals `shouldSatisfy` any isPathCollision
+          treeSnapshot out `shouldReturn` before
+          -- A fresh output directory is never even created.
+          withTempDirectory "keiro-dsl-workspace-atomic-fresh" $ \fresh -> do
+            let target = fresh </> "out"
+            case planWorkspaceScaffold "goldens" (workspaceContext broken) broken of
+              Right _ -> expectationFailure "expected the broken workspace to refuse"
+              Left _ -> doesDirectoryExist target `shouldReturn` False
+      it "leaves prior workspace output byte-identical for parse, validation, and collision failures" $
+        withWorkspaceFixture "keiro-dsl-workspace-atomic-cli" id $ \root out workspace -> do
+          _ <- executePlannedWorkspaceScaffold out workspace
+          before <- treeSnapshot out
+          let member = root </> "domain/project-artifact.keiro"
+              manifest = root </> "service.keiro-workspace"
+          original <- TIO.readFile member
+          let failures =
+                [ ("parse", "context demo-project\naggregate !!!\n"),
+                  ("validation", T.replace "ProjectId" "MissingProjectId" original),
+                  ("collision", T.replace "aggregate ProjectArtifact" "aggregate PROJECT" original)
+                ]
+          forM_ failures $ \(failureKind, brokenSource) -> do
+            TIO.writeFile member brokenSource
+            (exitCode, stdoutText, stderrText) <-
+              runKeiroDsl ["scaffold", manifest, "--out", out]
+            unless (exitCode == ExitFailure 1) $
+              expectationFailure
+                (failureKind <> " failure unexpectedly scaffolded:\n" <> stdoutText <> stderrText)
+            treeSnapshot out `shouldReturn` before
+            TIO.writeFile member original
+      it "refuses the whole workspace for one bannerless Generated target, changing nothing" $
+        withWorkspaceFixture "keiro-dsl-workspace-banner" id $ \_ out workspace -> do
+          plan <- shouldPlanWorkspaceSpec workspace
+          let generated = [m | (m, _) <- wpModules plan, kind m == Generated]
+          case generated of
+            [] -> expectationFailure "workspace fixture has no Generated module"
+            target : _ -> do
+              let path = out </> modulePath target
+              createDirectoryIfMissing True (takeDirectory path)
+              TIO.writeFile path "hand owned\n"
+              before <- treeSnapshot out
+              refused <- executeWorkspaceScaffold out False plan
+              refused `shouldSatisfy` isMissingBannerRefusal
+              treeSnapshot out `shouldReturn` before
+              forced <- executeWorkspaceScaffold out True plan
+              forced `shouldSatisfy` isSuccessfulScaffold
+              TIO.readFile path `shouldReturn` moduleText target
+      it "scaffolds a whole workspace through the CLI" $
+        withTempDirectory "keiro-dsl-workspace-cli" $ \out -> do
+          (exitCode, stdoutText, stderrText) <-
+            runKeiroDsl ["scaffold", canonicalWorkspacePath, "--out", out]
+          unless (exitCode == ExitSuccess) (expectationFailure (stdoutText <> stderrText))
+          stderrText `shouldContain` "workspace: demo-project"
+          doesFileExist (out </> "keiro-dsl-scaffold-record.workspace.demo-project.txt")
+            `shouldReturn` True
+          tree <- treeSnapshot out
+          length [path | (path, _) <- tree, "StructuralProjections.hs" `T.isSuffixOf` T.pack path]
+            `shouldBe` 1
+          length [path | (path, _) <- tree, "ReplayAudit.hs" `T.isSuffixOf` T.pack path]
+            `shouldBe` 1
+          (secondCode, _, secondErr) <-
+            runKeiroDsl ["scaffold", canonicalWorkspacePath, "--out", out]
+          secondCode `shouldBe` ExitSuccess
+          secondErr `shouldSatisfy` (not . isInfixOfString "(overwritten)")
+          treeSnapshot out `shouldReturn` tree
+
+    describe "workspace adoption" $ do
+      it "replaces embedded 0.6 nominal declarations only in generated files" $
+        withWorkspaceFixture "keiro-dsl-workspace-nominal-adopt" id $ \_ out workspace -> do
+          plan <- shouldPlanWorkspaceSpec workspace
+          let pathEndingIn suffix selectedKind =
+                case [modulePath m | (m, _) <- wpModules plan, kind m == selectedKind, suffix `isSuffixOfPath` m] of
+                  [path] -> pure path
+                  found -> expectationFailure ("expected one path ending in " <> suffix <> ", got " <> show found) >> fail "unreachable"
+          domainPath <- pathEndingIn "Project/Generated/Domain.hs" Generated
+          nominalPath <- pathEndingIn "Generated/Nominals.hs" Generated
+          internalNominalPath <- pathEndingIn "Generated/Nominals/Internal.hs" Generated
+          holePath <- pathEndingIn "Project/Holes.hs" HoleStub
+          writeFileWithParents
+            (out </> domainPath)
+            (generatedBanner <> "\n-- legacy 0.6 fixture\nmodule LegacyDomain where\nnewtype ProjectId = ProjectId String\ndata ProjectPhase = Draft | Active\n")
+          writeFileWithParents (out </> holePath) "-- hand-owned 0.6 implementation\n"
+
+          report <- executePlannedWorkspaceScaffold out workspace
+          wsrStale report `shouldBe` []
+          [disposition | (m, _, disposition) <- wsrDispositions report, modulePath m == domainPath]
+            `shouldBe` [Overwritten]
+          [disposition | (m, _, disposition) <- wsrDispositions report, modulePath m == nominalPath]
+            `shouldBe` [Overwritten]
+          newDomain <- TIO.readFile (out </> domainPath)
+          newDomain `shouldSatisfy` (not . T.isInfixOf "newtype ProjectId")
+          newDomain `shouldSatisfy` T.isInfixOf "Generated.Nominals (ProjectId, parseProjectId, ProjectPhase (..))"
+          newNominals <- TIO.readFile (out </> nominalPath)
+          T.count "newtype ProjectId" newNominals `shouldBe` 0
+          T.count "data ProjectPhase =" newNominals `shouldBe` 1
+          newInternalNominals <- TIO.readFile (out </> internalNominalPath)
+          T.count "newtype ProjectId" newInternalNominals `shouldBe` 1
+          TIO.readFile (out </> holePath) `shouldReturn` "-- hand-owned 0.6 implementation\n"
+      it "adopts an overwritten same-context record pair by record and by banner" $
+        withInlineWorkspace "keiro-dsl-workspace-adopt" adoptionMembers $ \_ out workspace -> do
+          -- Reproduce today's defect first: two same-context specs
+          -- scaffolded independently into one directory, the second
+          -- replacing the first's record and calling its files stale.
+          specA <- parseInlineSpec "domain/a.keiro" adoptionMemberA
+          specB <- parseInlineSpec "domain/b.keiro" adoptionMemberB
+          let ctx = defaultContext "adoption-demo"
+          legacyA <- executePlannedScaffold out "domain/a.keiro" ctx specA
+          legacyB <- executePlannedScaffold out "domain/b.keiro" ctx specB
+          reportStale legacyB `shouldSatisfy` (not . null)
+          legacyBefore <- TIO.readFile (out </> recordFileName "adoption-demo")
+
+          report <- executePlannedWorkspaceScaffold out workspace
+          wsrStale report `shouldBe` []
+          case wsrMigration report of
+            Nothing -> expectationFailure "expected the first workspace run to adopt"
+            Just migration -> do
+              let generatedOf run = sort [modulePath m | (m, _) <- reportDispositions run, kind m == Generated]
+                  claimedBy evidence = sort [cfPath entry | entry <- mrClaimed migration, cfEvidence entry == evidence]
+              -- The surviving record attributes B's files; A's
+              -- files survived only as banners, which is exactly
+              -- the orphan case the overwrite created.
+              claimedBy ClaimedFromRecord `shouldBe` generatedOf legacyB
+              claimedBy ClaimedFromBanner `shouldBe` sort (generatedOf legacyA \\ generatedOf legacyB)
+              claimedBy ClaimedFromBanner `shouldSatisfy` (not . null)
+              mrLikelyStale migration `shouldBe` []
+              mrLegacyRecord migration
+                `shouldBe` Just (recordFileName "adoption-demo", "domain/b.keiro")
+              -- Provenance is persisted, not merely printed.
+              recorded <- parseWorkspaceRecord <$> TIO.readFile (wsrRecordPath report)
+              fmap (sort . map adPath . wrAdopted) recorded
+                `shouldBe` Just (sort (map cfPath (mrClaimed migration)))
+              fmap (sort . nubOrd . map adEvidence . wrAdopted) recorded
+                `shouldBe` Just ["banner", "record"]
+              persisted <- TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
+              persisted `shouldBe` T.unlines (renderMigrationReport migration)
+              renderWorkspaceScaffoldReport report
+                `shouldSatisfy` any (T.isInfixOf "adopting pre-workspace scaffold output")
+
+          -- The legacy record gained one line and nothing else: it
+          -- still parses to the same value for an old binary.
+          legacyAfter <- TIO.readFile (out </> recordFileName "adoption-demo")
+          T.lines legacyAfter `shouldSatisfy` elem (supersededByLine "adoption-demo")
+          parseRecord legacyAfter `shouldBe` parseRecord legacyBefore
+          T.lines legacyAfter
+            `shouldBe` T.lines legacyBefore <> [supersededByLine "adoption-demo"]
+
+          -- Adoption is not a content change: the generated tree is
+          -- what a fresh workspace scaffold of the same members emits.
+          withInlineWorkspace "keiro-dsl-workspace-adopt-fresh" adoptionMembers $ \_ fresh freshWorkspace -> do
+            freshReport <- executePlannedWorkspaceScaffold fresh freshWorkspace
+            wsrMigration freshReport `shouldBe` Nothing
+            adoptedTree <- treeSnapshot out
+            freshTree <- treeSnapshot fresh
+            haskellOnly adoptedTree `shouldBe` haskellOnly freshTree
+      it "lists hand-written files as unclaimed and leaves their bytes alone" $
+        withInlineWorkspace "keiro-dsl-workspace-unclaimed" adoptionMembers $ \_ out workspace -> do
+          plan <- shouldPlanWorkspaceSpec workspace
+          case [modulePath m | (m, _) <- wpModules plan, kind m == HoleStub] of
+            [] -> expectationFailure "adoption fixture emits no hole module"
+            holePath : _ -> do
+              writeFileWithParents (out </> holePath) "-- hand filled\n"
+              writeFileWithParents (out </> "Notes.hs") "module Notes where\n"
+              report <- executePlannedWorkspaceScaffold out workspace
+              case wsrMigration report of
+                Nothing -> expectationFailure "expected a report for a directory holding hand-written files"
+                Just migration -> do
+                  mrLegacyRecord migration `shouldBe` Nothing
+                  mrClaimed migration `shouldBe` []
+                  mrUnclaimed migration `shouldBe` sort [holePath, "Notes.hs"]
+              TIO.readFile (out </> holePath) `shouldReturn` "-- hand filled\n"
+              TIO.readFile (out </> "Notes.hs") `shouldReturn` "module Notes where\n"
+      it "never claims a bannerless file at a planned Generated path" $
+        withInlineWorkspace "keiro-dsl-workspace-unattributable" adoptionMembers $ \_ out workspace -> do
+          plan <- shouldPlanWorkspaceSpec workspace
+          case [modulePath m | (m, _) <- wpModules plan, kind m == Generated] of
+            [] -> expectationFailure "adoption fixture emits no Generated module"
+            target : _ -> do
+              writeFileWithParents (out </> target) "hand owned\n"
+              refused <- executeWorkspaceScaffold out False plan
+              refused `shouldSatisfy` isMissingBannerRefusal
+              TIO.readFile (out </> target) `shouldReturn` "hand owned\n"
+              doesFileExist (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
+                `shouldReturn` False
+      it "adopts at most once, and the second run is an ordinary idempotent run" $
+        withInlineWorkspace "keiro-dsl-workspace-adopt-once" adoptionMembers $ \_ out workspace -> do
+          specA <- parseInlineSpec "domain/a.keiro" adoptionMemberA
+          _ <- executePlannedScaffold out "domain/a.keiro" (defaultContext "adoption-demo") specA
+          first <- executePlannedWorkspaceScaffold out workspace
+          wsrMigration first `shouldSatisfy` \case Just _ -> True; Nothing -> False
+          before <- treeSnapshot out
+          reportBefore <- TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
+          legacyBefore <- TIO.readFile (out </> recordFileName "adoption-demo")
+
+          second <- executePlannedWorkspaceScaffold out workspace
+          wsrMigration second `shouldBe` Nothing
+          wsrStale second `shouldBe` []
+          map thd3 (wsrDispositions second) `shouldSatisfy` all (`elem` [Unchanged, Skipped])
+          treeSnapshot out `shouldReturn` before
+          TIO.readFile (out </> "keiro-dsl-migration-report.workspace.adoption-demo.txt")
+            `shouldReturn` reportBefore
+          legacyAfter <- TIO.readFile (out </> recordFileName "adoption-demo")
+          legacyAfter `shouldBe` legacyBefore
+          length (filter (== supersededByLine "adoption-demo") (T.lines legacyAfter))
+            `shouldBe` 1
+
+comparisonProvenance :: CompareProvenance
+comparisonProvenance =
+  CompareProvenance
+    { cpHistoricalCodecIdentity = "example.historical",
+      cpHistoricalCodecVersion = "legacy-v1",
+      cpCanonicalType = CanonicalTypeId "example.Artifact.v1",
+      cpBindingSymbol = QualifiedValueName "Example.Bindings.artifactBinding",
+      cpBindingVersion = BindingVersion "1",
+      cpWireFingerprint = "deadbeef"
+    }
+
+syntheticGenerated :: FilePath -> T.Text -> ScaffoldModule
+syntheticGenerated path contents =
+  ScaffoldModule {modulePath = path, moduleText = contents, kind = Generated, origin = "test"}
+
+generatedTextEndingIn :: T.Text -> [ScaffoldModule] -> T.Text
+generatedTextEndingIn suffix modules = case [moduleText m | m <- modules, kind m == Generated, suffix `T.isSuffixOf` T.pack (modulePath m)] of
+  contents : _ -> contents
+  [] -> ""
+
+holeTextEndingIn :: T.Text -> [ScaffoldModule] -> T.Text
+holeTextEndingIn suffix modules = case [moduleText m | m <- modules, kind m == HoleStub, suffix `T.isSuffixOf` T.pack (modulePath m), not ("BehaviorHoles.hs" `T.isSuffixOf` T.pack (modulePath m))] of
+  contents : _ -> contents
+  [] -> ""
+
+onlyAggregate :: Spec -> Aggregate
+onlyAggregate spec = case [aggregate | NAggregate aggregate <- specNodes spec] of
+  [aggregate] -> aggregate
+  aggregates -> error ("expected one aggregate, got " <> show (length aggregates))
+
+loweringAggregateSpec :: T.Text
+loweringAggregateSpec =
+  T.unlines
+    [ "context samples",
+      "",
+      "aggregate Counter",
+      "  regs",
+      "    note Text = \"hello world\"",
+      "    count Int = 0",
+      "    state CounterVertex = Pending",
+      "  states Pending Done!",
+      "  command Bump { count:Int }",
+      "  event CountBumped { count:Int }",
+      "  Pending -- Bump --> emit CountBumped ; goto Done"
+    ]
+
+scalarRegisterCases :: [(T.Text, T.Text)]
+scalarRegisterCases =
+  [ ("Text", "\"sample\""),
+    ("Int", "0"),
+    ("Bool", "False"),
+    ("Time", "\"2026-01-02T03:04:05.123456789012Z\""),
+    ("Natural", "0")
+  ]
+
+cleanScalarAggregateSpec :: T.Text -> T.Text -> T.Text
+cleanScalarAggregateSpec typeName initialValue =
+  T.unlines
+    [ "context clean-scalar",
+      "",
+      "aggregate Scalar",
+      "  regs",
+      "    value " <> typeName <> " = " <> initialValue,
+      "  states Empty Done!",
+      "  command Set { value:" <> typeName <> " }",
+      "  event SetDone { value:" <> typeName <> " }",
+      "  Empty -- Set --> write value := value ; emit SetDone ; goto Done"
+    ]
+
+exactStatusSpec :: T.Text
+exactStatusSpec =
+  T.unlines
+    [ "context samples",
+      "",
+      "aggregate Reservation",
+      "  regs",
+      "    state ReservationVertex = Open",
+      "  states Open Closed!",
+      "  command Bump { count:Int }",
+      "  event ReservationHeld { count:Int }",
+      "  event ReservationUnHeld { count:Int }",
+      "  event CountBumped { count:Int }",
+      "  Open -- Bump --> emit CountBumped ; goto Closed",
+      "  projection reservation_status consistency=Eventual key=count",
+      "    status-map { ReservationHeld=>held ReservationUnHeld=>available CountBumped=>bumped }"
+    ]
+
+hasPathCollisionWithTwoOrigins :: Either [Refusal] [ScaffoldModule] -> Bool
+hasPathCollisionWithTwoOrigins = \case
+  Left refusals -> any hasTwo refusals
+  Right _ -> False
+  where
+    hasTwo (PathCollision _ origins) = length origins == 2
+    hasTwo _ = False
+
+isMissingBannerRefusal :: Either [Refusal] a -> Bool
+isMissingBannerRefusal = \case
+  Left [MissingGeneratedBanner paths] -> not (null paths)
+  _ -> False
+
+isSuccessfulScaffold :: Either [Refusal] a -> Bool
+isSuccessfulScaffold = \case
+  Right _ -> True
+  Left _ -> False
+
+executePlannedScaffold :: FilePath -> FilePath -> Context -> Spec -> IO ScaffoldReport
+executePlannedScaffold out specPath ctx spec = case planScaffold ctx spec of
+  Left refusals -> expectationFailure ("unexpected scaffold refusal: " <> show refusals) >> error "unreachable"
+  Right modules -> do
+    result <- executeScaffold out False specPath ctx spec modules
+    case result of
+      Left refusals -> expectationFailure ("unexpected execution refusal: " <> show refusals) >> error "unreachable"
+      Right report -> pure report
+
+renameCounter :: Node -> Node
+renameCounter (NAggregate aggregate) =
+  NAggregate
+    aggregate
+      { aggName = "Widget",
+        aggRegs = [reg {regType = if regType reg == TRef "CounterVertex" then TRef "WidgetVertex" else regType reg} | reg <- aggRegs aggregate]
+      }
+renameCounter node = node
+
+onlyPathEndingIn :: FilePath -> [ScaffoldModule] -> FilePath
+onlyPathEndingIn suffix modules = case [modulePath m | m <- modules, T.pack suffix `T.isSuffixOf` T.pack (modulePath m)] of
+  [path] -> path
+  paths -> error ("expected one path ending in " <> suffix <> ", got " <> show paths)
+
+withTempDirectory :: String -> (FilePath -> IO a) -> IO a
+withTempDirectory template = bracket acquire removePathForcibly
+  where
+    acquire = do
+      base <- getTemporaryDirectory
+      (path, handle) <- openTempFile base template
+      hClose handle
+      removeFile path
+      createDirectory path
+      pure path
+
+-- | Parse a fixture and return the validator's diagnostic codes (failing the
+-- test on a parse error).
+diagnosticCodesOf :: FilePath -> IO [DiagnosticCode]
+diagnosticCodesOf path = do
+  map code <$> diagnosticsOf path
+
+-- | Parse a fixture and return all validator diagnostics.
+diagnosticsOf :: FilePath -> IO [Diagnostic]
+diagnosticsOf path = do
+  service <- checkedServiceOf path
+  pure (validateService service)
+
+-- | Like 'diagnosticCodesOf' but only the Error-severity codes (warnings, e.g.
+-- the benign-inversion notices, are excluded).
+errorCodesOf :: FilePath -> IO [DiagnosticCode]
+errorCodesOf path = do
+  diagnostics <- diagnosticsOf path
+  pure [code d | d <- diagnostics, severity d == Error]
+
+-- | Parse two fixtures and diff them (old, new).
+-- | Plan 143: render an Expr in concrete guard syntax by printing a dummy
+-- transition through the real pretty-printer and slicing its guard clause,
+-- so the test exercises the exact printer the diff advisory uses.
+renderExprText :: Expr -> T.Text
+renderExprText e =
+  case [T.strip l | l <- T.lines rendered, "guard " `T.isPrefixOf` T.strip l] of
+    [guardLine] -> T.strip (T.drop (T.length "guard ") guardLine)
+    _ -> error ("renderExprText: unexpected printer output: " <> T.unpack rendered)
+  where
+    rendered =
+      renderTransition
+        Transition
+          { tSource = "S",
+            tCommand = "C",
+            tImplementation = LegacyHoleImplementation,
+            tGuard = Just e,
+            tWrites = [],
+            tEmits = [],
+            tGoto = "S",
+            tMode = TmLive,
+            tLoc = noLoc
+          }
+
+-- | Plan 143: a minimal spec whose only transition is replay-only, with the
+-- supplied clause lines spliced into its body.
+replayOnlySpecWith :: [T.Text] -> T.Text
+replayOnlySpecWith clauseLines =
+  T.unlines $
+    [ "context hospital-capacity",
+      "",
+      "id TransferReservationId prefix=rsv",
+      "",
+      "aggregate Reservation",
+      "  regs",
+      "    reservationId    TransferReservationId = placeholder",
+      "    reservationState ReservationVertex     = Unrequested",
+      "  states Unrequested Held",
+      "",
+      "  command RequestTransferReservation { reservationId }",
+      "",
+      "  event TransferReservationCreated = fields(RequestTransferReservation)",
+      "",
+      "  replay-only Unrequested -- RequestTransferReservation -->"
+    ]
+      ++ clauseLines
+
+diffFixtures :: FilePath -> FilePath -> IO [Change]
+diffFixtures oldP newP = do
+  old <- parsedSourceOf oldP
+  new <- parsedSourceOf newP
+  pure (diffSources old new)
+
+kindOfChange :: Change -> ChangeKind
+kindOfChange (Additive kind) = kind
+kindOfChange (Advisory kind) = kind
+kindOfChange (Breaking kind) = kind
+
+labelOfChange :: Change -> Label
+labelOfChange Additive {} = LabelAdditive
+labelOfChange Advisory {} = LabelAdvisory
+labelOfChange Breaking {} = LabelBreaking
+
+genSurfaceSet :: Gen (Set.Set CompatibilitySurface)
+genSurfaceSet = Set.fromList <$> listOf (elements [minBound .. maxBound])
+
+genCompatibilityVector :: Gen CompatibilityVector
+genCompatibilityVector =
+  CompatibilityVector
+    <$> genVerdict
+    <*> genVerdict
+    <*> genVerdict
+    <*> genVerdict
+    <*> genVerdict
+    <*> genVerdict
+    <*> (Set.fromList <$> listOf (elements rolloutConstraints))
+  where
+    genVerdict = elements [VCompatible, VAdvisory, VBreaking, VNotApplicable]
+    rolloutConstraints =
+      [ RolloutStopTheWorld,
+        RolloutWorkersFirst,
+        RolloutDrainRequired,
+        RolloutProducerLast,
+        RolloutProducerFirst
+      ]
+
+replayImpactFixtures :: FilePath -> FilePath -> IO ReplayImpact
+replayImpactFixtures oldPath newPath = do
+  old <- checkedServiceOf oldPath
+  new <- checkedServiceOf newPath
+  pure (resolvedFold (ReplayImpact.replayImpactServices old new))
+
+modifyAggregate :: Name -> (Aggregate -> Aggregate) -> Spec -> Spec
+modifyAggregate target update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NAggregate aggregate | aggName aggregate == target -> NAggregate (update aggregate)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+modifyReadModel :: Name -> (ReadModelNode -> ReadModelNode) -> Spec -> Spec
+modifyReadModel target update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NReadModel readModel | rmName readModel == target -> NReadModel (update readModel)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapContract :: (ContractNode -> ContractNode) -> Spec -> Spec
+mapContract update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NContract contract -> NContract (update contract)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapIntake :: (IntakeNode -> IntakeNode) -> Spec -> Spec
+mapIntake update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NIntake intake -> NIntake (update intake)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapEmit :: (EmitNode -> EmitNode) -> Spec -> Spec
+mapEmit update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NEmit emitNode -> NEmit (update emitNode)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapWorkflow :: (WorkflowNode -> WorkflowNode) -> Spec -> Spec
+mapWorkflow update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NWorkflow workflow -> NWorkflow (update workflow)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapWorkqueue :: (WorkqueueNode -> WorkqueueNode) -> Spec -> Spec
+mapWorkqueue update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NWorkqueue queue -> NWorkqueue (update queue)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapDispatch :: (PgmqDispatchNode -> PgmqDispatchNode) -> Spec -> Spec
+mapDispatch update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NPgmqDispatch dispatch -> NPgmqDispatch (update dispatch)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapOperation :: (OperationNode -> OperationNode) -> Spec -> Spec
+mapOperation update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NOperation operation -> NOperation (update operation)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+mapPublisher :: (PublisherNode -> PublisherNode) -> Spec -> Spec
+mapPublisher update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NPublisher publisher -> NPublisher (update publisher)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+serviceErrorCodes :: Int -> Spec -> [DiagnosticCode]
+serviceErrorCodes versionNumber spec =
+  [code diagnostic | diagnostic <- validateService service, severity diagnostic == Error]
+  where
+    service = case languageVersion (fromIntegral versionNumber) >>= effectiveLanguageContractForVersion of
+      Nothing -> error ("unsupported test language version " <> show versionNumber)
+      Just languageContract -> CheckedService languageContract spec
+
+duplicateFirst :: [a] -> [a]
+duplicateFirst = \case
+  [] -> []
+  first : rest -> first : first : rest
+
+updateFirst :: (a -> a) -> [a] -> [a]
+updateFirst update = \case
+  [] -> []
+  first : rest -> update first : rest
+
+removeReadModel :: Name -> Spec -> Spec
+removeReadModel target spec =
+  spec {specNodes = [node | node <- specNodes spec, not (isTarget node)]}
+  where
+    isTarget (NReadModel readModel) = rmName readModel == target
+    isTarget _ = False
+
+modifyRouter :: Name -> (RouterNode -> RouterNode) -> Spec -> Spec
+modifyRouter target update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NRouter router | rtId router == target -> NRouter (update router)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+routerErrorCodes :: (RouterNode -> RouterNode) -> Spec -> [DiagnosticCode]
+routerErrorCodes update = errorCodes . modifyRouter "PagingRouter" update
+
+modifyProcess :: Name -> (ProcessNode -> ProcessNode) -> Spec -> Spec
+modifyProcess target update spec =
+  spec
+    { specNodes =
+        [ case node of
+            NProcess process | procId process == target -> NProcess (update process)
+            _ -> node
+        | node <- specNodes spec
+        ]
+    }
+
+processErrorCodes :: (ProcessNode -> ProcessNode) -> Spec -> [DiagnosticCode]
+processErrorCodes update = errorCodes . modifyProcess "HospitalSurge" update
+
+errorCodes :: Spec -> [DiagnosticCode]
+errorCodes spec = [code diagnostic | diagnostic <- validateSpec spec, severity diagnostic == Error]
+
+diagnosticCodes :: Spec -> [DiagnosticCode]
+diagnosticCodes = map code . validateSpec
+
+changeReadModelShape :: ReadModelNode -> ReadModelNode
+changeReadModelShape readModel =
+  readModel
+    { rmColumns = rmColumns readModel <> [RmColumn "reviewed_by" "text" False],
+      rmShape = "fnv1a:0000000000000000"
+    }
+
+-- | Assert a @new \<kind\>@ skeleton parses and validates with zero
+-- error-severity diagnostics.
+assertSkeletonValid :: T.Text -> IO ()
+assertSkeletonValid kind = case skeletonFor kind of
+  Left err -> expectationFailure (T.unpack ("skeleton for " <> kind <> ": " <> err))
+  Right src -> case parseSpec ("new:" <> T.unpack kind) src of
+    Left perr -> expectationFailure (T.unpack ("skeleton for " <> kind <> " failed to parse: " <> perr))
+    Right spec ->
+      [code d | d <- validateSpec spec, severity d == Error]
+        `shouldBe` ([] :: [DiagnosticCode])
+
+assertSkeletonUsesStableLanguage :: T.Text -> IO ()
+assertSkeletonUsesStableLanguage kind = case skeletonFor kind of
+  Left err -> expectationFailure (T.unpack ("skeleton for " <> kind <> ": " <> err))
+  Right source -> case parseSource ("new:" <> T.unpack kind) source of
+    Left failure -> expectationFailure (T.unpack (renderParseFailure failure))
+    Right parsed -> do
+      let service = checkedSource parsed
+      effectiveContractLanguageVersion (checkedLanguageContract service) `shouldBe` currentStableLanguageVersion
+      effectiveLanguageSupport (checkedLanguageContract service) `shouldBe` Stable
+      [code diagnostic | diagnostic <- validateService service, severity diagnostic == Error]
+        `shouldBe` ([] :: [DiagnosticCode])
+      scaffoldServiceModules (defaultContext (specContext (checkedSpec service))) service
+        `shouldSatisfy` (not . null)
+
+assertSkeletonScaffoldable :: T.Text -> IO ()
+assertSkeletonScaffoldable kind = case skeletonFor kind of
+  Left err -> expectationFailure (T.unpack ("skeleton for " <> kind <> ": " <> err))
+  Right src -> case parseSpec ("new:" <> T.unpack kind) src of
+    Left perr -> expectationFailure (T.unpack perr)
+    Right spec -> planScaffold (defaultContext (specContext spec)) spec `shouldSatisfy` isSuccessfulScaffold
+
+skeletonModuleRoots :: [(T.Text, T.Text)]
+skeletonModuleRoots =
+  [ ("aggregate", "SkelAggregate"),
+    ("process", "SkelProcess"),
+    ("router", "SkelRouter"),
+    ("contract", "SkelContract"),
+    ("intake", "SkelIntake"),
+    ("emit", "SkelEmit"),
+    ("workqueue", "SkelQueue"),
+    ("workflow", "SkelWorkflow")
+  ]
+
+assertStableSkeletonMatchesCommitted :: T.Text -> T.Text -> IO ()
+assertStableSkeletonMatchesCommitted kind root = case skeletonFor kind of
+  Left err -> expectationFailure (T.unpack err)
+  Right source -> case parseSource ("new:" <> T.unpack kind) source of
+    Left err -> expectationFailure (T.unpack (renderParseFailure err))
+    Right parsed -> do
+      let service = checkedSource parsed
+          spec = checkedSpec service
+      let ctx = (defaultContext (specContext spec)) {moduleRoot = root}
+      forM_ [m | m <- scaffoldServiceModules ctx service, kindOf m == Generated] $ \m -> do
+        committed <- readTestText ("test/conformance-skeletons/" <> modulePath m)
+        normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
+  where
+    kindOf = Keiro.Dsl.Scaffold.kind
+
+bumpArtifactBindingVersion :: MappedDecl -> MappedDecl
+bumpArtifactBindingVersion declaration@MappedStructural {msName = "ArtifactInfo"} =
+  declaration {msBindingVersion = Just "2"}
+bumpArtifactBindingVersion declaration = declaration
+
+addArtifactSummaryField :: MappedDecl -> MappedDecl
+addArtifactSummaryField declaration@MappedStructural {msName = "ArtifactInfo", msShape = ShapeRecord constructor unknownFields fields} =
+  declaration
+    { msShape =
+        ShapeRecord
+          constructor
+          unknownFields
+          ( fields
+              <> [ WireField
+                     { wfHaskell = "summary",
+                       wfKey = "summary",
+                       wfType = TText,
+                       wfPresence = PRequired,
+                       wfOnMissing = Nothing,
+                       wfLoc = Loc 0
+                     }
+                 ]
+          )
+    }
+addArtifactSummaryField declaration = declaration
+
+expectGenericCompileFailure :: FilePath -> String -> Expectation
+expectGenericCompileFailure fixture expectedDiagnostic = do
+  let fixtureDir = "../keiro-core/test/compile-fail" </> fixture
+      fixtureSource = fixtureDir </> "Fixture.hs"
+  (exitCode, standardOutput, standardError) <-
+    readProcessWithExitCode
+      "cabal"
+      [ "exec",
+        "--",
+        "ghc",
+        "-XGHC2024",
+        "-fno-code",
+        "-fforce-recomp",
+        "-i../keiro-core/src",
+        "-i" <> fixtureDir,
+        fixtureSource
+      ]
+      ""
+  exitCode `shouldSatisfy` (/= ExitSuccess)
+  let compilerOutput = standardOutput <> standardError
+  compilerOutput `shouldContain` expectedDiagnostic
+  compilerOutput `shouldContain` "Run keiro-dsl scaffold and fill the binding by hand at this error location in the scaffolded module."
+  compilerOutput `shouldContain` fixtureSource
+
+moveArtifactBindingIntoGenerated :: MappedDecl -> MappedDecl
+moveArtifactBindingIntoGenerated declaration@MappedStructural {msName = "ArtifactInfo"} =
+  declaration {msBinding = Just "Generated.ConsumerDemo.Bindings.artifactInfoBinding"}
+moveArtifactBindingIntoGenerated declaration = declaration
+
+removeMappedRegisterRequirements :: Spec -> Spec
+removeMappedRegisterRequirements spec =
+  spec
+    { specMapped = map removeInitial (specMapped spec),
+      specNodes = map removeRegisters (specNodes spec)
+    }
+  where
+    removeInitial declaration@MappedStructural {} = declaration {msInitial = Nothing}
+    removeInitial declaration@MappedOpaque {} = declaration {moInitial = Nothing}
+    removeRegisters (NAggregate aggregate) =
+      NAggregate
+        aggregate
+          { aggRegs = [],
+            aggTransitions = [transition {tWrites = []} | transition <- aggTransitions aggregate]
+          }
+    removeRegisters node = node
+
+isImportCycle :: Refusal -> Bool
+isImportCycle ImportCycle {} = True
+isImportCycle _ = False
+
+isFoldSurfaceRefusal :: Either [Refusal] modules -> Bool
+isFoldSurfaceRefusal (Left refusals) = any isFold refusals
+  where
+    isFold FoldSurfaceRefusal {} = True
+    isFold _ = False
+isFoldSurfaceRefusal (Right _) = False
+
+-- | The canonical positive workspace fixture: three members under one context.
+canonicalWorkspacePath :: FilePath
+canonicalWorkspacePath = "test/fixtures/workspace/service.keiro-workspace"
+
+-- | Deterministic workspace source used to model git blobs without invoking git.
+memoryContentSource :: Map.Map FilePath T.Text -> ContentSource
+memoryContentSource files =
+  ContentSource
+    { csRead = \path ->
+        pure $ maybe (Left ("missing in-memory content: " <> T.pack path)) Right (Map.lookup path files)
+    }
+
+changeCode :: Change -> DiagnosticCode
+changeCode (Additive kind) = ckCode kind
+changeCode (Advisory kind) = ckCode kind
+changeCode (Breaking kind) = ckCode kind
+
+breakingSurfaces :: Change -> [CompatibilitySurface]
+breakingSurfaces change =
+  [ surface
+  | surface <- [minBound .. maxBound],
+    verdictFor surface (ckVector kind) == VBreaking
+  ]
+  where
+    kind = case change of
+      Additive value -> value
+      Advisory value -> value
+      Breaking value -> value
+
+workspaceChangeKind :: Change -> ChangeKind
+workspaceChangeKind (Additive kind) = kind
+workspaceChangeKind (Advisory kind) = kind
+workspaceChangeKind (Breaking kind) = kind
+
+-- | The same members as 'canonicalWorkspacePath', listed in reverse order.
+reorderedWorkspacePath :: FilePath
+reorderedWorkspacePath = "test/fixtures/workspace/service-reordered.keiro-workspace"
+
+-- | Load and compose a workspace fixture, failing the test on a refusal. The
+-- fixture path is package-relative; the loader is rooted at the manifest's own
+-- directory, exactly as the CLI roots it.
+shouldComposeWorkspace :: FilePath -> IO WorkspaceSpec
+shouldComposeWorkspace path = do
+  resolved <- resolveTestPath path
+  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) resolved
+  case loaded of
+    Left failure ->
+      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure resolved failure)))
+        >> error "unreachable"
+    Right workspace -> pure workspace {wsManifestPath = path}
+
+-- | The 'Context' a workspace scaffolds under, with no CLI overrides: the
+-- members' unanimous context name, the manifest's module-root and layout
+-- authority, and the built-in defaults where the manifest is silent.
+workspaceContext :: WorkspaceSpec -> Context
+workspaceContext workspace =
+  Context
+    { contextName = wsContext workspace,
+      moduleRoot = maybe "" id (wsModuleRoot workspace),
+      placement = maybe GeneratedPrefix id (wsLayout workspace)
+    }
+
+-- | Compose and plan a workspace fixture, failing the test on any refusal.
+shouldPlanWorkspace :: FilePath -> IO WorkspacePlan
+shouldPlanWorkspace path = do
+  workspace <- shouldComposeWorkspace path
+  case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
+    Left refusals -> expectationFailure ("unexpected workspace plan refusal: " <> show refusals) >> error "unreachable"
+    Right plan -> pure plan
+
+-- | Does a scaffolded module's path end in this suffix?
+isSuffixOfPath :: FilePath -> ScaffoldModule -> Bool
+isSuffixOfPath suffix m = T.pack suffix `T.isSuffixOf` T.pack (modulePath m)
+
+-- | A workspace record built from real composed data plus two synthetic
+-- adoption rows, so the round-trip test exercises every row kind including the
+-- JSON encodings shared with the v1 record.
+sampleWorkspaceRecord :: WorkspaceSpec -> WorkspaceRecord
+sampleWorkspaceRecord workspace =
+  WorkspaceRecord
+    { wrService = wsService workspace,
+      wrManifest = "service.keiro-workspace",
+      wrContext = wsContext workspace,
+      wrModuleRoot = maybe "" id (wsModuleRoot workspace),
+      wrLayout = "collocated",
+      wrMembers = map wmPath (wsMembers workspace),
+      wrSourceLanguages =
+        [ WorkspaceSourceLanguageRow (wmPath member) (wmSourceLanguage member)
+        | member <- wsMembers workspace
+        ],
+      wrLanguageContract = wsLanguageContract workspace,
+      wrModules =
+        [ WorkspaceModuleRow Generated "Demo/Generated/StructuralProjections.hs" Nothing,
+          WorkspaceModuleRow Generated "Demo/Project/Generated/Domain.hs" (Just "domain/project.keiro"),
+          WorkspaceModuleRow HoleStub "Demo/Project/Holes.hs" (Just "domain/shared.keiro")
+        ],
+      wrMappings = consumerMappings (consumerPlan (wsMergedSpec workspace)),
+      wrIdDomains = [],
+      wrNominalEqualities = nominalEqualityIdentities (wsMergedSpec workspace),
+      wrBindingObligations = either (const []) id (bindingHoles (wsMergedSpec workspace)),
+      wrBehaviorRequirements = [],
+      wrAdopted =
+        [ AdoptedRow "claimed/One.hs" "record" (Just "keiro-dsl-scaffold-record.demo-project.txt") (Just "project.keiro"),
+          AdoptedRow "claimed/Two.hs" "banner" Nothing Nothing
+        ]
+    }
+
+-- | The canonical workspace with a case-variant copy of one member's aggregate
+-- grafted onto another member. Composition refuses this shape (EP-153 catches it
+-- at the earliest boundary), so the planner's own cross-member collision gate can
+-- only be exercised by constructing the graph directly — which is exactly what
+-- this does, mirroring the single-file @caseVariant@ construction.
+withCaseVariantAggregate :: WorkspaceSpec -> WorkspaceSpec
+withCaseVariantAggregate workspace = case [aggregate | NAggregate aggregate <- specNodes merged, aggName aggregate == "Project"] of
+  [] -> error "canonical workspace fixture has no Project aggregate"
+  aggregate : _ ->
+    let shouted = aggregate {aggName = T.toUpper (aggName aggregate)}
+        ownership = wsOwnership workspace
+     in workspace
+          { wsMergedSpec = merged {specNodes = specNodes merged <> [NAggregate shouted]},
+            wsOwnership =
+              ownership
+                { oiNodes =
+                    Map.insert
+                      ("aggregate", aggName shouted)
+                      ("domain/project-artifact.keiro", Loc 1)
+                      (oiNodes ownership)
+                }
+          }
+  where
+    merged = wsMergedSpec workspace
+
+-- | Write a one-member workspace whose member declares an upcaster, so its
+-- golden payload fixture has a canonical location. Returns the composed
+-- workspace; the caller decides where the fixture lives.
+writeGoldenWorkspace :: FilePath -> IO WorkspaceSpec
+writeGoldenWorkspace root = do
+  source <- readTestText "test/fixtures/reservation-v2.keiro"
+  createDirectoryIfMissing True (root </> "domain")
+  TIO.writeFile (root </> "domain/reservation.keiro") source
+  let manifestPath = root </> "service.keiro-workspace"
+  TIO.writeFile manifestPath "service gold-demo\nspec domain/reservation.keiro\n"
+  loaded <- loadWorkspace (fileContentSource root) manifestPath
+  case loaded of
+    Left failure ->
+      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure manifestPath failure)))
+        >> error "unreachable"
+    Right workspace -> pure workspace
+
+-- | Materialize the canonical fixture workspace in a fresh temporary directory
+-- and hand the callback its root, a sibling output directory, and the composed
+-- workspace. Working on a copy is what lets a test edit a member and re-scaffold.
+--
+-- The manifest's @spec@ lines are passed through the given function first, so a
+-- caller can list the same members in a different order; the manifest __file
+-- name__ stays the same, which is what makes two runs comparable byte for byte.
+withWorkspaceFixture ::
+  String ->
+  ([FilePath] -> [FilePath]) ->
+  (FilePath -> FilePath -> WorkspaceSpec -> IO a) ->
+  IO a
+withWorkspaceFixture template orderMembers act =
+  withTempDirectory template $ \base -> do
+    let root = base </> "workspace"
+        out = base </> "out"
+        members =
+          [ "domain/project-artifact.keiro",
+            "domain/project.keiro",
+            "domain/shared.keiro"
+          ]
+    createDirectoryIfMissing True (root </> "domain")
+    forM_ members $ \relative -> do
+      source <- readTestText ("test/fixtures/workspace" </> relative)
+      TIO.writeFile (root </> relative) source
+    TIO.writeFile
+      (root </> "service.keiro-workspace")
+      ( T.unlines
+          ( ["service demo-project", "module Demo.Modules.Project", "layout collocated"]
+              <> ["spec " <> T.pack relative | relative <- orderMembers members]
+          )
+      )
+    workspace <- loadTempWorkspace root
+    act root out workspace
+
+-- | Materialize an inline workspace — a manifest plus literal member sources —
+-- in a fresh temporary directory, and hand the callback its root, a sibling output
+-- directory, and the composed workspace.
+withInlineWorkspace ::
+  String ->
+  (T.Text, [(FilePath, T.Text)]) ->
+  (FilePath -> FilePath -> WorkspaceSpec -> IO a) ->
+  IO a
+withInlineWorkspace template (service, members) act =
+  withTempDirectory template $ \base -> do
+    let root = base </> "workspace"
+        out = base </> "out"
+    forM_ members $ \(relative, source) -> writeFileWithParents (root </> relative) source
+    TIO.writeFile
+      (root </> "service.keiro-workspace")
+      ( T.unlines
+          (("service " <> service) : ["spec " <> T.pack relative | (relative, _) <- members])
+      )
+    workspace <- loadTempWorkspace root
+    act root out workspace
+
+-- | Two independently valid members under one context. Each is a complete spec
+-- that the pre-workspace single-file scaffolder accepts, which is what lets a test
+-- reproduce the overwritten-record defect before adopting.
+adoptionMembers :: (T.Text, [(FilePath, T.Text)])
+adoptionMembers = ("adoption-demo", [("domain/a.keiro", adoptionMemberA), ("domain/b.keiro", adoptionMemberB)])
+
+adoptionMemberA :: T.Text
+adoptionMemberA =
+  T.unlines
+    [ "context adoption-demo",
+      "",
+      "aggregate Counter",
+      "  regs",
+      "    count Int = 0",
+      "    state CounterVertex = Pending",
+      "  states Pending Done!",
+      "  command Bump { count:Int }",
+      "  event CountBumped { count:Int }",
+      "  Pending -- Bump --> emit CountBumped ; goto Done"
+    ]
+
+adoptionMemberB :: T.Text
+adoptionMemberB =
+  T.unlines
+    [ "context adoption-demo",
+      "",
+      "aggregate Widget",
+      "  regs",
+      "    size Int = 0",
+      "    state WidgetVertex = Draft",
+      "  states Draft Shipped!",
+      "  command Ship { size:Int }",
+      "  event WidgetShipped { size:Int }",
+      "  Draft -- Ship --> emit WidgetShipped ; goto Shipped"
+    ]
+
+writeFileWithParents :: FilePath -> T.Text -> IO ()
+writeFileWithParents path contents = do
+  createDirectoryIfMissing True (takeDirectory path)
+  TIO.writeFile path contents
+
+-- | Only the Haskell sources of a tree snapshot, dropping bookkeeping files.
+haskellOnly :: [(FilePath, T.Text)] -> [(FilePath, T.Text)]
+haskellOnly entries = [entry | entry@(path, _) <- entries, ".hs" `T.isSuffixOf` T.pack path]
+
+-- | Compose a workspace that a test just wrote to disk.
+loadTempWorkspace :: FilePath -> IO WorkspaceSpec
+loadTempWorkspace root = do
+  let manifestPath = root </> "service.keiro-workspace"
+  loaded <- loadWorkspace (fileContentSource root) manifestPath
+  case loaded of
+    Left failure ->
+      expectationFailure (T.unpack (T.intercalate "\n" (renderWorkspaceFailure manifestPath failure)))
+        >> error "unreachable"
+    Right workspace -> pure workspace
+
+-- | Plan an already-composed workspace, failing the test on a refusal.
+shouldPlanWorkspaceSpec :: WorkspaceSpec -> IO WorkspacePlan
+shouldPlanWorkspaceSpec workspace =
+  case planWorkspaceScaffold "goldens" (workspaceContext workspace) workspace of
+    Left refusals -> expectationFailure ("unexpected workspace plan refusal: " <> show refusals) >> error "unreachable"
+    Right plan -> pure plan
+
+-- | Plan then execute a whole-workspace scaffold, failing loudly on either.
+executePlannedWorkspaceScaffold :: FilePath -> WorkspaceSpec -> IO WorkspaceScaffoldReport
+executePlannedWorkspaceScaffold out workspace = do
+  plan <- shouldPlanWorkspaceSpec workspace
+  result <- executeWorkspaceScaffold out False plan
+  case result of
+    Left refusals -> expectationFailure ("unexpected workspace execution refusal: " <> show refusals) >> error "unreachable"
+    Right report -> pure report
+
+-- | Rename one member's aggregate in place and recompose. Only the
+-- @aggregate \<Name\>@ header is rewritten, so declarations that merely share the
+-- prefix (@ProjectId@, @ProjectSummary@) are untouched.
+renameMemberAggregate :: FilePath -> FilePath -> T.Text -> T.Text -> IO WorkspaceSpec
+renameMemberAggregate root member from to = do
+  source <- TIO.readFile (root </> member)
+  TIO.writeFile (root </> member) (T.replace ("aggregate " <> from <> "\n") ("aggregate " <> to <> "\n") source)
+  loadTempWorkspace root
+
+-- | Move the @ProjectArtifact@ aggregate from the artifact member into the
+-- project member, and recompose.
+--
+-- It is prepended, so the merged spec's node order — and therefore every emitted
+-- byte, including the replay-audit assembly's aggregate list — is exactly what it
+-- was. That isolates the change to ownership, which is the point of the test.
+moveArtifactAggregate :: FilePath -> IO WorkspaceSpec
+moveArtifactAggregate root = do
+  artifact <- TIO.readFile (root </> "domain/project-artifact.keiro")
+  project <- TIO.readFile (root </> "domain/project.keiro")
+  case T.breakOn "aggregate ProjectArtifact" artifact of
+    (kept, moved) | not (T.null moved) -> do
+      TIO.writeFile (root </> "domain/project-artifact.keiro") kept
+      TIO.writeFile
+        (root </> "domain/project.keiro")
+        (T.replace "aggregate Project\n" (moved <> "\naggregate Project\n") project)
+      loadTempWorkspace root
+    _ -> expectationFailure "artifact member has no ProjectArtifact aggregate" >> error "unreachable"
+
+-- | Every regular file under a directory, as @(relative path, contents)@ sorted
+-- by path — the comparison unit for "byte-identical output".
+treeSnapshot :: FilePath -> IO [(FilePath, T.Text)]
+treeSnapshot root = do
+  exists <- doesDirectoryExist root
+  if not exists then pure [] else sort <$> walk ""
+  where
+    walk relative = do
+      entries <- listDirectory (root </> relative)
+      fmap concat . forM (sort entries) $ \entry -> do
+        let child = if null relative then entry else relative </> entry
+        isDirectory <- doesDirectoryExist (root </> child)
+        if isDirectory
+          then walk child
+          else do
+            contents <- TIO.readFile (root </> child)
+            pure [(child, contents)]
+
+thd3 :: (a, b, c) -> c
+thd3 (_, _, value) = value
+
+isPathCollision :: Refusal -> Bool
+isPathCollision PathCollision {} = True
+isPathCollision _ = False
+
+isInfixOfString :: String -> String -> Bool
+isInfixOfString needle haystack = T.isInfixOf (T.pack needle) (T.pack haystack)
+
+-- | Load a workspace fixture expecting a compose refusal, and return it.
+shouldRefuseWorkspace :: FilePath -> IO (NonEmpty WorkspaceDiagnostic)
+shouldRefuseWorkspace path = do
+  resolved <- resolveTestPath path
+  loaded <- loadWorkspace (fileContentSource (takeDirectory resolved)) resolved
+  case loaded of
+    Left (WorkspaceRefused diagnostics) -> pure diagnostics
+    Left other ->
+      expectationFailure
+        ("expected compose refusals, got:\n" <> T.unpack (T.intercalate "\n" (renderWorkspaceFailure resolved other)))
+        >> error "unreachable"
+    Right _ -> expectationFailure ("expected " <> path <> " to be refused") >> error "unreachable"
+
+-- | Invoke the built @keiro-dsl@ executable. Fixture paths are resolved first,
+-- so the test works whether it runs from the package directory or the repository
+-- root.
+runKeiroDsl :: [String] -> IO (ExitCode, String, String)
+runKeiroDsl arguments = do
+  resolved <- traverse resolveArgument arguments
+  readProcessWithExitCode "cabal" (["run", "-v0", "keiro-dsl", "--"] <> resolved) ""
+  where
+    resolveArgument argument
+      | "test/fixtures/" `isPrefixOfString` argument = resolveTestPath argument
+      | otherwise = pure argument
+    isPrefixOfString prefix value = take (length prefix) value == prefix
+
+-- | The @spec@ field of a coverage report, i.e. what the report says it covers.
+coverageSpecPath :: Value -> Maybe T.Text
+coverageSpecPath value = case value of
+  Aeson.Object fields -> case KeyMap.lookup "spec" fields of
+    Just (Aeson.String path) -> Just path
+    _ -> Nothing
+  _ -> Nothing
+
+-- | Order-preserving deduplication for comparing cited file sets.
+nubOrd :: (Eq a) => [a] -> [a]
+nubOrd = go []
+  where
+    go seen [] = reverse seen
+    go seen (x : xs) = if x `elem` seen then go seen xs else go (x : seen) xs
+
+-- | Parse a workspace manifest, failing the test on a refusal.
+shouldParseManifest :: FilePath -> T.Text -> IO WorkspaceManifest
+shouldParseManifest path source = case parseWorkspaceManifest path source of
+  Left err -> expectationFailure (T.unpack err) >> error "unreachable"
+  Right manifest -> pure manifest
+
+-- | Generate a canonical workspace manifest. Members are drawn from a pool of
+-- paths that are distinct even under case folding and are held sorted, which is
+-- the invariant every parsed manifest satisfies.
+genWorkspaceManifest :: Gen WorkspaceManifest
+genWorkspaceManifest = do
+  service <- elements ["demo-project", "mori", "kotei", "a1", "svc-2"]
+  moduleRoot <- elements [Nothing, Just "Demo", Just "Demo.Modules.Project"]
+  layout <- elements [Nothing, Just GeneratedPrefix, Just CollocatedLeaf]
+  chosen <-
+    sublistOf
+      [ "a.keiro",
+        "d-e_f.keiro",
+        "domain/b.keiro",
+        "domain/sub/c.keiro",
+        "x1.keiro"
+      ]
+      `suchThat` (not . null)
+  pure
+    WorkspaceManifest
+      { wmfService = service,
+        wmfServiceLoc = Loc 1,
+        wmfModuleRoot = moduleRoot,
+        wmfModuleRootLoc = Loc 2,
+        wmfLayout = layout,
+        wmfLayoutLoc = Loc 3,
+        wmfMembers = NE.fromList [WorkspaceMemberRef path (Loc 4) | path <- sort chosen]
+      }
+
+-- | Parse a fixture while retaining its released language contract.
+parsedSourceOf :: FilePath -> IO ParsedSource
+parsedSourceOf path = do
+  input <- readTestText path
+  case parseSource path input of
+    Left failure -> expectationFailure (T.unpack (renderParseFailure failure)) >> fail "unreachable"
+    Right parsed -> pure parsed
+
+checkedServiceOf :: FilePath -> IO CheckedService
+checkedServiceOf = fmap checkedSource . parsedSourceOf
+
+renderFoldBaseline :: T.Text -> CheckedService -> T.Text
+renderFoldBaseline fixture service =
+  T.intercalate
+    "\n\n"
+    [ T.unlines
+        ( [ "fixture=" <> fixture,
+            "aggregate=" <> aggName aggregate,
+            "fingerprint=" <> aggregateFoldFingerprintForService service aggregate,
+            "surface-begin"
+          ]
+            <> T.lines (aggregateFoldSurfaceForService service aggregate)
+            <> ["surface-end"]
+        )
+    | NAggregate aggregate <- specNodes (checkedSpec service)
+    ]
+
+-- | Parse a fixture through the source-aware boundary and return its graph.
+specOf :: FilePath -> IO Spec
+specOf = fmap checkedSpec . checkedServiceOf
+
+-- | Parse a fixture and scaffold its checked semantic service.
+scaffoldFixture :: FilePath -> IO [ScaffoldModule]
+scaffoldFixture path = do
+  service <- checkedServiceOf path
+  pure (scaffoldServiceModules (defaultContext (specContext (checkedSpec service))) service)
+
+legacyScaffoldProcessFixture :: FilePath -> IO [ScaffoldModule]
+legacyScaffoldProcessFixture path = do
+  spec <- specOf path
+  pure $ concat [scaffoldProcess (ctx spec) process | NProcess process <- specNodes spec]
+  where
+    ctx spec = defaultContext (specContext spec)
+
+-- | Assert a freshly-scaffolded Generated module matches its committed copy
+-- under test/conformance/ (whitespace-normalized). The committed copies are the
+-- ones the keiro-dsl-conformance suite compiles, so this pins the live scaffolder
+-- to known-compiling output.
+assertMatchesCommitted :: ScaffoldModule -> IO ()
+assertMatchesCommitted m = do
+  let committedPath = "test/conformance/" <> modulePath m
+  committed <- readTestText committedPath
+  normalizeGenerated committed `shouldBe` normalizeGenerated (moduleText m)
+
+normalizeGenerated :: T.Text -> (T.Text, [T.Text])
+normalizeGenerated text =
+  let (orderInsensitiveLines, body) = partition isOrderInsensitive (T.lines text)
+   in (normalizeBody body, sort (map normalizeImport orderInsensitiveLines))
+  where
+    -- Compare the deterministic body exactly as before and imports/language
+    -- pragmas as a sorted, whitespace-normalized list. Sorting tolerates
+    -- formatter reordering while additions, removals, and renamed entries still
+    -- fail the pin.
+    isOrderInsensitive line = isImport line || "{-# LANGUAGE " `T.isPrefixOf` line
+    normalizeBody =
+      -- Fourmolu parenthesizes a single class constraint while the emitter's
+      -- compact spelling remains valid Haskell.  Treat that formatter-only
+      -- rewrite like the whitespace and comma placement normalized below.
+      T.replace "(Show value) =>" "Show value =>"
+        . T.replace "( " "("
+        . T.replace " )" ")"
+        . T.replace " , )" " )"
+        . T.unwords
+        . T.words
+        . T.replace "}" " } "
+        . T.replace "{" " { "
+        . T.replace "]" " ] "
+        . T.replace "[" " [ "
+        . T.replace "," " , "
+        . T.unlines
+        . map normalizeBanner
+    normalizeBanner line
+      | isGeneratedBannerLine line = generatedBanner
+      | otherwise = line
+    normalizeImport line =
+      let reordered = case T.words line of
+            "import" : "qualified" : moduleName : rest -> T.unwords ("import" : moduleName : "qualified" : rest)
+            wordsInImport -> T.unwords wordsInImport
+          (prefix, explicit) = T.breakOn " (" reordered
+       in if T.null explicit
+            then prefix
+            else
+              let members =
+                    sort
+                      . map (T.replace " (" "(" . T.unwords . T.words)
+                      . T.splitOn ","
+                      . T.dropEnd 1
+                      $ T.drop 2 explicit
+               in prefix <> " (" <> T.intercalate "," members <> ")"
+    isImport line = case T.words line of
+      "import" : _ -> True
+      _ -> False
+
+-- | Locate and read a test fixture or committed conformance source regardless
+-- of whether the suite was launched from the package directory or repo root.
+readTestText :: FilePath -> IO T.Text
+readTestText path = resolveTestPath path >>= TIO.readFile
+
+-- | Locate a repo file regardless of the test process's current directory.
+resolveTestPath :: FilePath -> IO FilePath
+resolveTestPath rel = do
+  override <- lookupEnv "KEIRO_DSL_TEST_ROOT"
+  let candidates = [rel, "keiro-dsl" </> rel] <> maybe [] (\root -> [root </> rel]) override
+  existing <- filterM doesFileExist candidates
+  case existing of
+    path : _ -> pure path
+    [] ->
+      fail $
+        "unable to locate keiro-dsl test file "
+          <> show rel
+          <> "; tried "
+          <> show candidates
+
+leftContains :: T.Text -> Either T.Text a -> Bool
+leftContains needle = \case
+  Left err -> needle `T.isInfixOf` err
+  Right _ -> False
+
+parseInlineSpec :: FilePath -> T.Text -> IO Spec
+parseInlineSpec sourceName src = case parseSpec sourceName src of
+  Left err -> expectationFailure (T.unpack err) >> error "unreachable"
+  Right spec -> pure spec
+
+parseStableRenderedSpec :: FilePath -> Spec -> Either T.Text Spec
+parseStableRenderedSpec sourceName spec =
+  case parseSource sourceName stableSource of
+    Left failure -> Left (renderParseFailure failure)
+    Right parsed -> Right (parsedSpec parsed)
+  where
+    stableSource =
+      "language keiro-dsl "
+        <> T.pack (show (languageVersionNumber currentStableLanguageVersion))
+        <> "\n"
+        <> renderSpec spec
+
+shouldParseStableRenderedSpec :: FilePath -> Spec -> IO Spec
+shouldParseStableRenderedSpec sourceName spec =
+  case parseStableRenderedSpec sourceName spec of
+    Left failure -> expectationFailure (T.unpack failure) >> fail "unreachable"
+    Right reparsed -> pure reparsed
 
 shouldResolveTypeGraph :: Spec -> IO TypeGraph
 shouldResolveTypeGraph spec = case resolveTypeGraph spec of
diff --git a/test/conformance-aggregate-scalars/AggregateScalars/ScalarLedger/Holes.hs b/test/conformance-aggregate-scalars/AggregateScalars/ScalarLedger/Holes.hs
--- a/test/conformance-aggregate-scalars/AggregateScalars/ScalarLedger/Holes.hs
+++ b/test/conformance-aggregate-scalars/AggregateScalars/ScalarLedger/Holes.hs
@@ -1,72 +1,23 @@
-{-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
--- overwrites it. The generated harness pins this implementation's scalar
--- guards, writes, event, and forward/replay behavior.
-module AggregateScalars.ScalarLedger.Holes (
-    scalarLedgerTransducer,
-    dishonestWireScalarsRecorded,
-    -- (no projection)
-) where
+-- This is a HAND-OWNED language-4 hook module. Generated code owns the
+-- transition envelope, guards, writes, and lifecycle. This module supplies
+-- the explicit event-field mapping requested by the source declaration.
+module AggregateScalars.ScalarLedger.Holes
+  ( transition1EmptyRecordOutput1ScalarsRecorded
+  ) where
 
-import Data.Time.Clock (UTCTime)
 import Generated.AggregateScalars.ScalarLedger.Domain
-import Keiki.Builder ((=:))
-import qualified Keiki.Builder as B
-import Keiki.Core (HsPred, SymTransducer, WireCtor (..), (.&&), (.==), (.>=))
-import Numeric.Natural (Natural)
-
-scalarLedgerTransducer ::
-    SymTransducer
-        (HsPred ScalarLedgerRegs ScalarLedgerCommand)
-        ScalarLedgerRegs
-        ScalarLedgerVertex
-        ScalarLedgerCommand
-        ScalarLedgerEvent
-scalarLedgerTransducer =
-    B.buildTransducer ScalarLedgerEmpty initialScalarLedgerRegs isTerminal do
-        B.from ScalarLedgerEmpty do
-            B.onCmd inCtorRecord $ \d -> B.do
-                B.requireGuard
-                    ( d.observedAt
-                        .== d.observedAt
-                        .&& d.observedAt
-                        .>= d.observedAt
-                        .&& d.revision
-                        .== d.revision
-                        .&& d.revision
-                        .>= d.revision
-                    )
-                B.slot @"observedAt" =: d.observedAt
-                B.slot @"revision" =: d.revision
-                B.emit
-                    emitWire
-                    ScalarsRecordedTermFields
-                        { observedAt = d.observedAt
-                        , revision = d.revision
-                        }
-                B.goto ScalarLedgerRecorded
-  where
-    isTerminal = \case
-        ScalarLedgerRecorded -> True
-        _ -> False
-
--- The mutation test changes this indirection to the dormant dishonest ctor.
-emitWire :: WireCtor ScalarLedgerEvent (UTCTime, (Natural, ()))
-emitWire = wireScalarsRecorded
-
--- This builder pins every revision to one. The rewrite is idempotent, while
--- the unchanged output terms still prove both command fields were recovered.
-dishonestWireScalarsRecorded :: WireCtor ScalarLedgerEvent (UTCTime, (Natural, ()))
-dishonestWireScalarsRecorded =
-    wireScalarsRecorded
-        { wcBuild = wcBuild wireScalarsRecorded . forceRevisionOne
-        }
+import Keiki.Builder qualified as B
+import Keiki.Generics (RegFieldsOf)
 
-forceRevisionOne :: (UTCTime, (Natural, ())) -> (UTCTime, (Natural, ()))
-forceRevisionOne (observedAtValue, (_revisionValue, ())) =
-    (observedAtValue, (1, ()))
+transition1EmptyRecordOutput1ScalarsRecorded
+  :: B.PayloadProj ScalarLedgerRegs ScalarLedgerCommand (RegFieldsOf RecordData)
+  -> ScalarsRecordedTermFields ScalarLedgerRegs ScalarLedgerCommand (RegFieldsOf RecordData)
+transition1EmptyRecordOutput1ScalarsRecorded d =
+  ScalarsRecordedTermFields
+    { observedAt = d.observedAt
+    , revision = d.revision
+    }
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ReplayAudit.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ReplayAudit.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ReplayAudit.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context aggregate-scalars replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Codec.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Codec.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Codec.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Codec.hs
@@ -1,49 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
 module Generated.AggregateScalars.ScalarLedger.Codec (
     scalarLedgerCodec,
     parseScalarLedgerEvent,
     encodeScalarLedgerEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.AggregateScalars.ScalarLedger.Domain
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
-import Generated.AggregateScalars.ScalarLedger.Domain
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 scalarLedgerCodec :: Codec ScalarLedgerEvent
 scalarLedgerCodec =
-    Codec
-        { eventTypes = EventType "ScalarsRecorded" :| []
-        , eventType = \case
-            ScalarsRecorded{} -> EventType "ScalarsRecorded"
-        , schemaVersion = 1
-        , encode = encodeScalarLedgerEvent
-        , decode = parseScalarLedgerEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ScalarsRecorded" :| []
+    , eventType = \case
+        ScalarsRecorded{} -> EventType "ScalarsRecorded"
+    , schemaVersion = 1
+    , encode = encodeScalarLedgerEvent
+    , decode = parseScalarLedgerEvent
+    , upcasters = []
+    }
 
 encodeScalarLedgerEvent :: ScalarLedgerEvent -> Value
 encodeScalarLedgerEvent = \case
-    ScalarsRecorded payload ->
-        object
-            [ "kind" .= ("ScalarsRecorded" :: Text)
-            , "observedAt" .= payload.observedAt
-            , "revision" .= payload.revision
-            ]
+  ScalarsRecorded payload ->
+    object
+      [ "kind" .= ("ScalarsRecorded" :: Text)
+      , "observedAt" .= payload.observedAt
+      , "revision" .= payload.revision
+      ]
 
 parseScalarLedgerEvent :: EventType -> Value -> Either Text ScalarLedgerEvent
 parseScalarLedgerEvent (EventType tag) = mapLeftText . parseEither (withObject "ScalarLedgerEvent" go)
   where
     go o = do
-        case tag of
-            "ScalarsRecorded" ->
-                ScalarsRecorded <$> (ScalarsRecordedData <$> o .: "observedAt" <*> o .: "revision")
-            _ -> fail "unknown event type"
+      case tag of
+        "ScalarsRecorded" ->
+          ScalarsRecorded
+            <$> ( ScalarsRecordedData
+                    <$> o .: "observedAt"
+                    <*> o .: "revision"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: ScalarsRecorded")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Domain.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Domain.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Domain.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Domain.hs
@@ -3,56 +3,56 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
 module Generated.AggregateScalars.ScalarLedger.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
-import Data.Time.Calendar (fromGregorian)
-import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
+import Data.Time.Calendar (fromGregorian)
+import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 import Numeric.Natural (Natural)
+import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ScalarLedgerVertex = ScalarLedgerEmpty | ScalarLedgerRecorded
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-    deriving anyclass (ToJSON, FromJSON)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
 instance CanonicalStateShape ScalarLedgerVertex
 instance CanonicalTypeName ScalarLedgerVertex
 
 data RecordData = RecordData
-    { observedAt :: !UTCTime
-    , revision :: !Natural
-    }
-    deriving stock (Generic, Eq, Show)
+  { observedAt :: !UTCTime
+  , revision :: !Natural
+  }
+  deriving stock (Generic, Eq, Show)
 
 data ScalarLedgerCommand = Record !RecordData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data ScalarsRecordedData = ScalarsRecordedData
-    { observedAt :: !UTCTime
-    , revision :: !Natural
-    }
-    deriving stock (Generic, Eq, Show)
+  { observedAt :: !UTCTime
+  , revision :: !Natural
+  }
+  deriving stock (Generic, Eq, Show)
 
 data ScalarLedgerEvent = ScalarsRecorded !ScalarsRecordedData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 type ScalarLedgerRegs =
-    '[ '("observedAt", UTCTime)
-     , '("revision", Natural)
-     ]
+  '[ '("observedAt", UTCTime)
+   , '("revision", Natural)
+   ]
 
 initialScalarLedgerRegs :: RegFile ScalarLedgerRegs
 initialScalarLedgerRegs =
-    RCons (Proxy @"observedAt") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) $
-        RCons (Proxy @"revision") 0 RNil
+  RCons (Proxy @"observedAt") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) $
+  RCons (Proxy @"revision") 0 RNil
 
 $(deriveAggregateCtorsAll ''ScalarLedgerCommand ''ScalarLedgerRegs)
+
+
 
 $(deriveWireCtorsAll ''ScalarLedgerEvent)
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/EventStream.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/EventStream.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/EventStream.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
 module Generated.AggregateScalars.ScalarLedger.EventStream
   ( scalarLedgerCategory
+  , scalarLedgerCommandCategory
   , scalarLedgerEventStream
   , scalarLedgerEventStreamDef
   , ScalarLedgerEventStream
@@ -10,7 +11,7 @@
 
 import Generated.AggregateScalars.ScalarLedger.Domain
 import Generated.AggregateScalars.ScalarLedger.Codec (scalarLedgerCodec)
-import AggregateScalars.ScalarLedger.Holes (scalarLedgerTransducer)
+import Generated.AggregateScalars.ScalarLedger.Transducer (scalarLedgerFoldFingerprint, scalarLedgerTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -21,9 +22,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-scalarLedgerCategory :: Stream.StreamCategory a
+scalarLedgerCategory :: Stream.StreamCategory ScalarLedgerEventStreamDef
 scalarLedgerCategory = Stream.categoryUnsafe "scalarLedger"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+scalarLedgerCommandCategory :: Stream.StreamCategory ScalarLedgerCommand
+scalarLedgerCommandCategory = Stream.categoryUnsafe "scalarLedger"
+
 type ScalarLedgerEventStreamDef =
   EventStream (HsPred ScalarLedgerRegs ScalarLedgerCommand) ScalarLedgerRegs ScalarLedgerVertex ScalarLedgerCommand ScalarLedgerEvent
 
@@ -33,21 +38,21 @@
 scalarLedgerEventStreamDef :: ScalarLedgerEventStreamDef
 scalarLedgerEventStreamDef =
   EventStream
-    { transducer = scalarLedgerTransducer
-    , initialState = ScalarLedgerEmpty
-    , initialRegisters = initialScalarLedgerRegs
-    , eventCodec = scalarLedgerCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Every 1
-    -- The snapshot discriminator composes: the spec's state-codec version (bump it
-    -- in the spec's `state-codec version=` clause), keiki's register and
-    -- control-state shape hashes, and this fold fingerprint derived from the
-    -- spec's transition surface (guards, writes, emits, states, register
-    -- initials, referenced rules). Spec-visible fold changes invalidate old
-    -- snapshots automatically. Fold changes made ONLY in the hand-owned Holes
-    -- module are invisible here: bump `state-codec version=` manually or old
-    -- snapshots will be served stale.
-    , stateCodec = Just (withFoldFingerprint "f1fd9ca5a924e6b9" (defaultStateCodec 1))
+    { transducer = scalarLedgerTransducer,
+      initialState = ScalarLedgerEmpty,
+      initialRegisters = initialScalarLedgerRegs,
+      eventCodec = scalarLedgerCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Every 1,
+      -- The snapshot discriminator composes: the spec's state-codec version (bump it
+      -- in the spec's `state-codec version=` clause), keiki's register and
+      -- control-state shape hashes, and this fold fingerprint derived from the
+      -- spec's transition surface (guards, writes, emits, states, register
+      -- initials, referenced rules). Spec-visible fold changes invalidate old
+      -- snapshots automatically. Version-2 Hole-owned transitions additionally
+      -- compose their explicit hand-owned FoldVersion tokens here; bump the
+      -- corresponding token whenever that Hole behavior changes.
+      stateCodec = Just (withFoldFingerprint scalarLedgerFoldFingerprint (defaultStateCodec 1))
     }
 
 scalarLedgerSnapshotFixture :: (Int, Text)
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Harness.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Harness.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Harness.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Harness.hs
@@ -1,29 +1,27 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
 module Generated.AggregateScalars.ScalarLedger.Harness (harnessAssertions) where
 
-import AggregateScalars.ScalarLedger.Holes (scalarLedgerTransducer)
-import Data.Time.Calendar (fromGregorian)
-import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)
-import Generated.AggregateScalars.ScalarLedger.Codec (encodeScalarLedgerEvent, parseScalarLedgerEvent, scalarLedgerCodec)
 import Generated.AggregateScalars.ScalarLedger.Domain
+import Generated.AggregateScalars.ScalarLedger.Codec (encodeScalarLedgerEvent, parseScalarLedgerEvent, scalarLedgerCodec)
+import Generated.AggregateScalars.ScalarLedger.Transducer (scalarLedgerTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
+import Data.Time.Calendar (fromGregorian)
+import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions scalarLedgerTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: ScalarsRecorded", roundTrips sampleEventScalarsRecorded)
-    , ("accepts Record from ScalarLedgerEmpty", acceptRecord)
-    ]
-        ++ forwardReplayRecord
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions scalarLedgerTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: ScalarsRecorded", roundTrips sampleEventScalarsRecorded)
+  , ("accepts Record from ScalarLedgerEmpty", acceptRecord)
+  ]
+  ++ forwardReplayRecord
 
 roundTrips :: ScalarLedgerEvent -> Bool
 roundTrips e = parseScalarLedgerEvent (eventType scalarLedgerCodec e) (encodeScalarLedgerEvent e) == Right e
@@ -33,26 +31,26 @@
 
 acceptRecord :: Bool
 acceptRecord =
-    case step scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) ((Record (RecordData (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0))) of
-        Just (v, _, _) -> v == ScalarLedgerRecorded
-        Nothing -> False
+  case step scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) ((Record (RecordData (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0))) of
+    Just (v, _, _) -> v == ScalarLedgerRecorded
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRecord :: [(String, Bool)]
 forwardReplayRecord =
-    case step scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) ((Record (RecordData (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, forwardRegs, emitted) ->
-            case mapM (\event -> parseScalarLedgerEvent (eventType scalarLedgerCodec event) (encodeScalarLedgerEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            , (prefix <> "register observedAt", (replayRegs ! #observedAt) == (forwardRegs ! #observedAt))
-                            , (prefix <> "register revision", (replayRegs ! #revision) == (forwardRegs ! #revision))
-                            ]
+  case step scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) ((Record (RecordData (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, forwardRegs, emitted) ->
+      case mapM (\event -> parseScalarLedgerEvent (eventType scalarLedgerCodec event) (encodeScalarLedgerEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither scalarLedgerTransducer (ScalarLedgerEmpty, initialScalarLedgerRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              , (prefix <> "register observedAt", (replayRegs ! #observedAt) == (forwardRegs ! #observedAt))
+              , (prefix <> "register revision", (replayRegs ! #revision) == (forwardRegs ! #revision))
+              ]
   where
     prefix = "forward/replay equality: Record from ScalarLedgerEmpty -- "
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Projection.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Projection.hs
--- a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Projection.hs
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
 module Generated.AggregateScalars.ScalarLedger.Projection () where
diff --git a/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Transducer.hs b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-aggregate-scalars/Generated/AggregateScalars/ScalarLedger/Transducer.hs
@@ -0,0 +1,63 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarLedger; do not edit.
+module Generated.AggregateScalars.ScalarLedger.Transducer
+  ( scalarLedgerTransducer
+  , scalarLedgerFoldFingerprint
+  , BehaviorOwnership (..)
+  , scalarLedgerPredicateVerifications
+  ) where
+
+import Generated.AggregateScalars.ScalarLedger.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer, (.>=), (.&&))
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import AggregateScalars.ScalarLedger.Holes qualified as Holes
+import Keiki.Builder ((=:))
+
+scalarLedgerTransducer
+  :: SymTransducer
+       (HsPred ScalarLedgerRegs ScalarLedgerCommand)
+       ScalarLedgerRegs
+       ScalarLedgerVertex
+       ScalarLedgerCommand
+       ScalarLedgerEvent
+scalarLedgerTransducer =
+  B.buildTransducer ScalarLedgerEmpty initialScalarLedgerRegs isTerminal do
+    B.from ScalarLedgerEmpty do
+      B.onCmd inCtorRecord $ \d -> B.do
+        B.requireGuard $
+          d.observedAt .>= B.reg @"observedAt"
+          .&& d.revision .>= B.reg @"revision"
+        B.slot @"observedAt" =: d.observedAt
+        B.slot @"revision" =: d.revision
+        B.emit wireScalarsRecorded (Holes.transition1EmptyRecordOutput1ScalarsRecorded d)
+        B.goto ScalarLedgerRecorded
+ where
+  isTerminal = \case
+    ScalarLedgerRecorded -> True
+    _ -> False
+
+scalarLedgerFoldFingerprint :: Text
+scalarLedgerFoldFingerprint = "c18684310d0f8e508077f8bd61bbabf3"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+scalarLedgerPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+scalarLedgerPredicateVerifications = sequence
+  [ verifyTransition "transition1EmptyRecord" GeneratedOwned ScalarLedgerEmpty 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut scalarLedgerTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-aggregate-scalars/Main.hs b/test/conformance-aggregate-scalars/Main.hs
--- a/test/conformance-aggregate-scalars/Main.hs
+++ b/test/conformance-aggregate-scalars/Main.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
@@ -12,6 +11,7 @@
 import Data.Aeson (Result (..), Value (..), object, toJSON, (.=))
 import Data.Aeson qualified as Aeson
 import Data.Proxy (Proxy (..))
+import Data.Text qualified as T
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)
 import Generated.AggregateScalars.ScalarLedger.Codec (encodeScalarLedgerEvent, parseScalarLedgerEvent, scalarLedgerCodec)
@@ -21,7 +21,7 @@
 import Keiki.Builder qualified as B
 import Keiki.Core (HsPred, SymTransducer, TransducerValidationWarning (..), ValidationOptions (..), defaultValidationOptions, lit, tadd, validateTransducer, (!), (.>=))
 import Keiki.Shape (CanonicalTypeName (..))
-import Keiro.Codec (eventType)
+import Keiro.Codec (EventType (..), eventType)
 import Keiro.EventStream (EventStream (..), StateCodec (..))
 import Numeric.Natural (Natural)
 import System.Exit (exitFailure)
@@ -40,6 +40,7 @@
                    , ("Natural JSON rejects a fractional number", naturalJsonRejects (Number 1.5))
                    , ("Natural canonical type name is stable", canonicalTypeName (Proxy @Natural) == "Natural")
                    , ("Natural arithmetic is structural in Keiki", naturalArithmeticIsStructural)
+                   , ("unknown event types name the value and expected set", unknownEventTypeIsDiagnosable)
                    ]
     forM_ checks $ \(label, passed) ->
         putStrLn ((if passed then "PASS  " else "FAIL  ") <> label)
@@ -66,6 +67,14 @@
             , "observedAt" .= sampleTime
             , "revision" .= (7 :: Natural)
             ]
+
+unknownEventTypeIsDiagnosable :: Bool
+unknownEventTypeIsDiagnosable =
+    case parseScalarLedgerEvent (EventType "ScalarRecorded") (object []) of
+        Left problem ->
+            "ScalarRecorded" `T.isInfixOf` problem
+                && "ScalarsRecorded" `T.isInfixOf` problem
+        Right _ -> False
 
 snapshotRoundTrip :: Bool
 snapshotRoundTrip = case stateCodec scalarLedgerEventStreamDef of
diff --git a/test/conformance-behavior-complete/BehaviorComplete/Journey/BehaviorHoles.hs b/test/conformance-behavior-complete/BehaviorComplete/Journey/BehaviorHoles.hs
--- a/test/conformance-behavior-complete/BehaviorComplete/Journey/BehaviorHoles.hs
+++ b/test/conformance-behavior-complete/BehaviorComplete/Journey/BehaviorHoles.hs
@@ -7,6 +7,7 @@
 import Data.Time.Clock (UTCTime (..))
 import Generated.BehaviorComplete.Journey.BehaviorContract
 import Generated.BehaviorComplete.Journey.Domain
+import Generated.BehaviorComplete.Nominals (RequestId, parseRequestId)
 import Numeric.Natural (Natural)
 
 behaviorWitnesses :: [BehaviorWitness]
@@ -64,7 +65,10 @@
 retirementAuditedEvent amountValue = RetirementAudited (RetirementAuditedData amountValue)
 
 requestIdValue :: RequestId
-requestIdValue = RequestId "req_behavior_complete"
+requestIdValue =
+  case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of
+    Right parsed -> parsed
+    Left problem -> error (show problem)
 
 observedAtValue :: UTCTime
 observedAtValue = UTCTime (fromGregorian 2026 8 1) 0
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/BehaviorContract.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/BehaviorContract.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/BehaviorContract.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/BehaviorContract.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.BehaviorContract where
 
 import Generated.BehaviorComplete.Journey.Codec (encodeJourneyEvent, parseJourneyEvent, journeyCodec)
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Codec.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Codec.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Codec.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Codec.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.Codec (
     journeyCodec,
     parseJourneyEvent,
@@ -9,11 +9,13 @@
 ) where
 
 import Generated.BehaviorComplete.Journey.Domain
+import Generated.BehaviorComplete.Nominals (RequestId, requestIdText)
+import Generated.BehaviorComplete.Nominals.Internal (unsafeRequestIdFromLegacyText)
 import Control.Monad (unless)
 import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))
 import Data.Aeson.Key qualified as Key
 import Data.Aeson.KeyMap qualified as KeyMap
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
@@ -48,8 +50,8 @@
 parseStartPayloadShape = withObject "StartPayloadShape" $ \objectValue -> do
   rejectUnknownFields "StartPayload" ["display_label", "optional_note"] objectValue
   Generated.BehaviorComplete.Structural.Shape.StartPayload.StartPayload
-    <$> ((objectValue .: "display_label" :: Parser Value) >>= (parseJSON))
-    <*> (case KeyMap.lookup (Key.fromText "optional_note") objectValue of Nothing -> pure Nothing; Just presentValue -> (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other) presentValue)
+    <$> explicitParseField (parseJSON) objectValue "display_label"
+    <*> (case KeyMap.lookup (Key.fromText "optional_note") objectValue of Nothing -> pure Nothing; Just _ -> explicitParseField (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other) objectValue "optional_note")
 
 journeyCodec :: Codec JourneyEvent
 journeyCodec =
@@ -98,14 +100,29 @@
     go o = do
       case tag of
         "Started" ->
-          Started <$> (StartedData <$> (RequestId <$> o .: "requestId") <*> o .: "observedAt" <*> o .: "amount" <*> (o .: "details" >>= parseStartPayloadMapped))
+          Started
+            <$> ( StartedData
+                    <$> (unsafeRequestIdFromLegacyText <$> o .: "requestId")
+                    <*> o .: "observedAt"
+                    <*> o .: "amount"
+                    <*> explicitParseField parseStartPayloadMapped o "details"
+                )
         "DecisionRecorded" ->
-          DecisionRecorded <$> (DecisionRecordedData <$> o .: "amount")
+          DecisionRecorded
+            <$> ( DecisionRecordedData
+                    <$> o .: "amount"
+                )
         "Retired" ->
-          Retired <$> (RetiredData <$> o .: "amount")
+          Retired
+            <$> ( RetiredData
+                    <$> o .: "amount"
+                )
         "RetirementAudited" ->
-          RetirementAudited <$> (RetirementAuditedData <$> o .: "amount")
-        _ -> fail "unknown event type"
+          RetirementAudited
+            <$> ( RetirementAuditedData
+                    <$> o .: "amount"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: Started, DecisionRecorded, Retired, RetirementAudited")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Domain.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Domain.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Domain.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Domain.hs
@@ -3,8 +3,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
@@ -13,19 +12,12 @@
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
 import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
+import Generated.BehaviorComplete.Nominals (RequestId, parseRequestId)
 import BehaviorComplete.Domain qualified
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 import Numeric.Natural (Natural)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
-
-newtype RequestId = RequestId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName RequestId
-
-requestIdText :: RequestId -> Text
-requestIdText (RequestId t) = t
 
 data JourneyVertex = JourneyEmpty | JourneyActive | JourneyClosed
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/EventStream.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/EventStream.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/EventStream.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.EventStream
   ( journeyCategory
+  , journeyCommandCategory
   , journeyEventStream
   , journeyEventStreamDef
   , JourneyEventStream
@@ -21,9 +22,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-journeyCategory :: Stream.StreamCategory a
+journeyCategory :: Stream.StreamCategory JourneyEventStreamDef
 journeyCategory = Stream.categoryUnsafe "journey"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+journeyCommandCategory :: Stream.StreamCategory JourneyCommand
+journeyCommandCategory = Stream.categoryUnsafe "journey"
+
 type JourneyEventStreamDef =
   EventStream (HsPred JourneyRegs JourneyCommand) JourneyRegs JourneyVertex JourneyCommand JourneyEvent
 
@@ -33,21 +38,21 @@
 journeyEventStreamDef :: JourneyEventStreamDef
 journeyEventStreamDef =
   EventStream
-    { transducer = journeyTransducer
-    , initialState = JourneyEmpty
-    , initialRegisters = initialJourneyRegs
-    , eventCodec = journeyCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Every 1
-    -- The snapshot discriminator composes: the spec's state-codec version (bump it
-    -- in the spec's `state-codec version=` clause), keiki's register and
-    -- control-state shape hashes, and this fold fingerprint derived from the
-    -- spec's transition surface (guards, writes, emits, states, register
-    -- initials, referenced rules). Spec-visible fold changes invalidate old
-    -- snapshots automatically. Version-2 Hole-owned transitions additionally
-    -- compose their explicit hand-owned FoldVersion tokens here; bump the
-    -- corresponding token whenever that Hole behavior changes.
-    , stateCodec = Just (withFoldFingerprint journeyFoldFingerprint (defaultStateCodec 1))
+    { transducer = journeyTransducer,
+      initialState = JourneyEmpty,
+      initialRegisters = initialJourneyRegs,
+      eventCodec = journeyCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Every 1,
+      -- The snapshot discriminator composes: the spec's state-codec version (bump it
+      -- in the spec's `state-codec version=` clause), keiki's register and
+      -- control-state shape hashes, and this fold fingerprint derived from the
+      -- spec's transition surface (guards, writes, emits, states, register
+      -- initials, referenced rules). Spec-visible fold changes invalidate old
+      -- snapshots automatically. Version-2 Hole-owned transitions additionally
+      -- compose their explicit hand-owned FoldVersion tokens here; bump the
+      -- corresponding token whenever that Hole behavior changes.
+      stateCodec = Just (withFoldFingerprint journeyFoldFingerprint (defaultStateCodec 1))
     }
 
 journeySnapshotFixture :: (Int, Text)
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Expressions.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Expressions.hs
deleted file mode 100644
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Expressions.hs
+++ /dev/null
@@ -1,51 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.BehaviorComplete.Journey.Expressions
-  ( transition1EmptyStartWriteLastAmount
-  , transition2ActiveDecideGuard
-  , transition2ActiveDecideWriteLastAmount
-  , transition3ActiveDecideGuard
-  , transition3ActiveDecideWriteLastAmount
-  , transition5ActiveRetireGuard
-  , transition5ActiveRetireWriteLastAmount
-  , transition6ActiveRetireGuard
-  , transition6ActiveRetireWriteLastAmount
-  ) where
-
-import Generated.BehaviorComplete.Journey.Domain
-import Keiki.Builder qualified as B
-import Keiki.Core qualified as K
-import Keiki.Generics (RegFieldsOf)
-import Numeric.Natural (Natural)
-import Generated.BehaviorComplete.StructuralProjections qualified as StructuralProjections
-import BehaviorComplete.Domain qualified
-
-transition1EmptyStartWriteLastAmount :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf StartData) -> K.Term JourneyRegs JourneyCommand (RegFieldsOf StartData) Natural
-transition1EmptyStartWriteLastAmount d = d.amount
-
-transition2ActiveDecideGuard :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf DecideData) -> K.HsPred JourneyRegs JourneyCommand
-transition2ActiveDecideGuard d = K.PCmp K.CmpLe (d.amount) (K.lit (5 :: Natural))
-
-transition2ActiveDecideWriteLastAmount :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf DecideData) -> K.Term JourneyRegs JourneyCommand (RegFieldsOf DecideData) Natural
-transition2ActiveDecideWriteLastAmount d = d.amount
-
-transition3ActiveDecideGuard :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf DecideData) -> K.HsPred JourneyRegs JourneyCommand
-transition3ActiveDecideGuard d = K.PCmp K.CmpGt (d.amount) (K.lit (5 :: Natural))
-
-transition3ActiveDecideWriteLastAmount :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf DecideData) -> K.Term JourneyRegs JourneyCommand (RegFieldsOf DecideData) Natural
-transition3ActiveDecideWriteLastAmount d = d.amount
-
-transition5ActiveRetireGuard :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf RetireData) -> K.HsPred JourneyRegs JourneyCommand
-transition5ActiveRetireGuard d = K.PCmp K.CmpGt (d.amount) (K.lit (0 :: Natural))
-
-transition5ActiveRetireWriteLastAmount :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf RetireData) -> K.Term JourneyRegs JourneyCommand (RegFieldsOf RetireData) Natural
-transition5ActiveRetireWriteLastAmount d = d.amount
-
-transition6ActiveRetireGuard :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf RetireData) -> K.HsPred JourneyRegs JourneyCommand
-transition6ActiveRetireGuard d = K.PCmp K.CmpLe (d.amount) (K.lit (0 :: Natural))
-
-transition6ActiveRetireWriteLastAmount :: B.PayloadProj JourneyRegs JourneyCommand (RegFieldsOf RetireData) -> K.Term JourneyRegs JourneyCommand (RegFieldsOf RetireData) Natural
-transition6ActiveRetireWriteLastAmount d = d.amount
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Harness.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Harness.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Harness.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Harness.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.Harness (harnessAssertions) where
 
 import Generated.BehaviorComplete.Journey.Domain
@@ -9,6 +9,7 @@
 import Generated.BehaviorComplete.Journey.Transducer (journeyTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, fieldWitnessAgrees, (!))
 import Keiro.Codec (eventType)
+import Generated.BehaviorComplete.Nominals (RequestId, parseRequestId)
 import Data.Aeson qualified as Aeson
 import Data.Aeson.Key qualified as AesonKey
 import Data.Aeson.KeyMap qualified as AesonKeyMap
@@ -27,10 +28,9 @@
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions journeyTransducer))
@@ -48,7 +48,7 @@
 roundTrips e = parseJourneyEvent (eventType journeyCodec e) (encodeJourneyEvent e) == Right e
 
 sampleEventStarted :: JourneyEvent
-sampleEventStarted = (Started (StartedData (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))
+sampleEventStarted = (Started (StartedData (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))
 
 sampleEventDecisionRecorded :: JourneyEvent
 sampleEventDecisionRecorded = (DecisionRecorded (DecisionRecordedData 0))
@@ -61,7 +61,7 @@
 
 acceptStart :: Bool
 acceptStart =
-  case step journeyTransducer (JourneyEmpty, initialJourneyRegs) ((Start (StartData (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))) of
+  case step journeyTransducer (JourneyEmpty, initialJourneyRegs) ((Start (StartData (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))) of
     Just (v, _, _) -> v == JourneyActive
     Nothing -> False
 
@@ -69,7 +69,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayStart :: [(String, Bool)]
 forwardReplayStart =
-  case step journeyTransducer (JourneyEmpty, initialJourneyRegs) ((Start (StartData (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))) of
+  case step journeyTransducer (JourneyEmpty, initialJourneyRegs) ((Start (StartData (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 (snd (NonEmpty.head (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseJourneyEvent (eventType journeyCodec event) (encodeJourneyEvent event)) emitted of
@@ -120,7 +120,7 @@
 
 startedDetailsAssertions :: [(String, Bool)]
 startedDetailsAssertions =
-  [ ("mapped codec round-trip: Started/details/" <> T.unpack label, roundTrips (Started (StartedData (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 mappedValue)))
+  [ ("mapped codec round-trip: Started/details/" <> T.unpack label, roundTrips (Started (StartedData (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) 0 mappedValue)))
   | (label, mappedValue) <- NonEmpty.toList (fixtureCases BehaviorComplete.Bindings.startPayloadCases)
   ]
 
@@ -133,7 +133,7 @@
 
 structuralProjectionAssertions :: [(String, Bool)]
 structuralProjectionAssertions =
-  [ ("projection witness agreement: behavior-complete.StartPayload.v1/display_label", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.structuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZWitness (\referenceOwner -> Generated.BehaviorComplete.Structural.Shape.StartPayload.label (bindingToShape BehaviorComplete.Bindings.startPayloadBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))
+  [ ("projection witness agreement: behavior-complete.StartPayload.v1/display_label", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.startPayloadDisplayLabelWitness (\referenceOwner -> Generated.BehaviorComplete.Structural.Shape.StartPayload.label (bindingToShape BehaviorComplete.Bindings.startPayloadBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases BehaviorComplete.Bindings.startPayloadCases)))
   ]
 
 deleteObjectField :: T.Text -> Aeson.Value -> Aeson.Value
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Projection.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Projection.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Projection.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.Projection () where
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Transducer.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Transducer.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Transducer.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Journey/Transducer.hs
@@ -4,7 +4,7 @@
 {-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Journey; do not edit.
 module Generated.BehaviorComplete.Journey.Transducer
   ( journeyTransducer
   , journeyFoldFingerprint
@@ -14,11 +14,11 @@
 
 import Generated.BehaviorComplete.Journey.Domain
 import Data.Text (Text)
+import Numeric.Natural (Natural)
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core (HsPred, SymTransducer, (.<=), (.>))
 import Keiki.Core qualified as K
 import Keiki.Symbolic qualified as S
-import Generated.BehaviorComplete.Journey.Expressions qualified as Expressions
 import Keiki.Builder ((=:))
 
 journeyTransducer
@@ -32,7 +32,7 @@
   B.buildTransducer JourneyEmpty initialJourneyRegs isTerminal do
     B.from JourneyEmpty do
       B.onCmd inCtorStart $ \d -> B.do
-        B.slot @"lastAmount" =: Expressions.transition1EmptyStartWriteLastAmount d
+        B.slot @"lastAmount" =: d.amount
         B.emit wireStarted (StartedTermFields
           { requestId = d.requestId
           , observedAt = d.observedAt
@@ -42,25 +42,28 @@
         B.goto JourneyActive
     B.from JourneyActive do
       B.onCmd inCtorDecide $ \d -> B.do
-        B.requireGuard (Expressions.transition2ActiveDecideGuard d)
-        B.slot @"lastAmount" =: Expressions.transition2ActiveDecideWriteLastAmount d
+        B.requireGuard $
+          d.amount .<= K.lit (5 :: Natural)
+        B.slot @"lastAmount" =: d.amount
         B.emit wireDecisionRecorded (DecisionRecordedTermFields
           { amount = d.amount
           })
         B.goto JourneyActive
       B.onCmd inCtorDecide $ \d -> B.do
-        B.requireGuard (Expressions.transition3ActiveDecideGuard d)
-        B.slot @"lastAmount" =: Expressions.transition3ActiveDecideWriteLastAmount d
+        B.requireGuard $
+          d.amount .> K.lit (5 :: Natural)
+        B.slot @"lastAmount" =: d.amount
         B.emit wireDecisionRecorded (DecisionRecordedTermFields
           { amount = d.amount
           })
         B.goto JourneyActive
-      B.onCmd inCtorPing $ \d -> B.do
+      B.onCmd inCtorPing $ \_d -> B.do
         B.noEmit
         B.goto JourneyActive
       B.onCmd inCtorRetire $ \d -> B.do
-        B.requireGuard (Expressions.transition5ActiveRetireGuard d)
-        B.slot @"lastAmount" =: Expressions.transition5ActiveRetireWriteLastAmount d
+        B.requireGuard $
+          d.amount .> K.lit (0 :: Natural)
+        B.slot @"lastAmount" =: d.amount
         B.emit wireRetired (RetiredTermFields
           { amount = d.amount
           })
@@ -70,8 +73,9 @@
         B.goto JourneyClosed
       B.onCmd inCtorRetire $ \d -> B.do
         B.replayOnly
-        B.requireGuard (Expressions.transition6ActiveRetireGuard d)
-        B.slot @"lastAmount" =: Expressions.transition6ActiveRetireWriteLastAmount d
+        B.requireGuard $
+          d.amount .<= K.lit (0 :: Natural)
+        B.slot @"lastAmount" =: d.amount
         B.emit wireRetired (RetiredTermFields
           { amount = d.amount
           })
@@ -85,7 +89,7 @@
     _ -> False
 
 journeyFoldFingerprint :: Text
-journeyFoldFingerprint = "c5e44f6d33b3dd6f"
+journeyFoldFingerprint = "6a9b89765aef0e4cd37ce6a7ea34514a"
 
 data BehaviorOwnership = GeneratedOwned | HoleOwned
   deriving stock (Eq, Show)
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context behavior-complete generated nominal declarations; do not edit.
+module Generated.BehaviorComplete.Nominals
+  ( RequestId
+  , parseRequestId
+  , mkRequestId
+  , requestIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.BehaviorComplete.Nominals.Internal (RequestId, mkRequestId, parseRequestId, requestIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+instance CanonicalTypeName RequestId
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals/Internal.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Nominals/Internal.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context behavior-complete generated nominal ID internals; do not edit.
+module Generated.BehaviorComplete.Nominals.Internal
+  ( RequestId
+  , parseRequestId
+  , mkRequestId
+  , requestIdText
+  , unsafeRequestIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype RequestId = RequestId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON RequestId where
+  toJSON = toJSON . requestIdText
+
+instance FromJSON RequestId where
+  parseJSON = withText "RequestId" (either (fail . T.unpack) pure . parseRequestId)
+
+parseRequestId :: Text -> Either Text RequestId
+parseRequestId input = case validateIdDomainText (typeIdV7Domain "req") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (RequestId input)
+
+mkRequestId :: Text -> Either Text RequestId
+mkRequestId = parseRequestId
+
+requestIdText :: RequestId -> Text
+requestIdText (RequestId value) = value
+
+unsafeRequestIdFromLegacyText :: Text -> RequestId
+unsafeRequestIdFromLegacyText = RequestId
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/ReplayAudit.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/ReplayAudit.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/ReplayAudit.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context behavior-complete replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/Structural/Shape/StartPayload.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/Structural/Shape/StartPayload.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/Structural/Shape/StartPayload.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/Structural/Shape/StartPayload.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DuplicateRecordFields #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural StartPayload; do not edit.
 module Generated.BehaviorComplete.Structural.Shape.StartPayload (StartPayloadShape (..)) where
 
 import Data.Text (Text)
diff --git a/test/conformance-behavior-complete/Generated/BehaviorComplete/StructuralProjections.hs b/test/conformance-behavior-complete/Generated/BehaviorComplete/StructuralProjections.hs
--- a/test/conformance-behavior-complete/Generated/BehaviorComplete/StructuralProjections.hs
+++ b/test/conformance-behavior-complete/Generated/BehaviorComplete/StructuralProjections.hs
@@ -1,11 +1,11 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context behavior-complete mapped structural facade; do not edit.
 -- Equality witnesses are emitted for Text, Int, Bool, Natural, and UTCTime.
 -- Int, Natural, and UTCTime belong to Keiki's ordered subset.
 module Generated.BehaviorComplete.StructuralProjections
-  ( structuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZWitness
+  ( startPayloadDisplayLabelWitness
   ) where
 
 import Data.Text (Text)
@@ -17,14 +17,14 @@
 import BehaviorComplete.Domain qualified
 import Generated.BehaviorComplete.Structural.Shape.StartPayload qualified
 
-data StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ
+data StartPayloadDisplayLabelProjection
 
-instance FieldProjection StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ where
-  type FieldName StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ = "/display_label"
-  type FieldOwner StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ = BehaviorComplete.Domain.StartPayload
-  type FieldResult StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ = Text
+instance FieldProjection StartPayloadDisplayLabelProjection where
+  type FieldName StartPayloadDisplayLabelProjection = "/display_label"
+  type FieldOwner StartPayloadDisplayLabelProjection = BehaviorComplete.Domain.StartPayload
+  type FieldResult StartPayloadDisplayLabelProjection = Text
   fieldShapeId _ = "behavior-complete.StartPayload.v1"
   projectFieldValue _ owner = Generated.BehaviorComplete.Structural.Shape.StartPayload.label (bindingToShape BehaviorComplete.Bindings.startPayloadBinding owner)
 
-structuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZWitness :: FieldWitness StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ
-structuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZWitness = fieldWitness @StructuralProjectionC53ZC74ZC61ZC72ZC74ZC50ZC61ZC79ZC6cZC6fZC61ZC64ZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6cZC61ZC62ZC65ZC6cZ
+startPayloadDisplayLabelWitness :: FieldWitness StartPayloadDisplayLabelProjection
+startPayloadDisplayLabelWitness = fieldWitness @StartPayloadDisplayLabelProjection
diff --git a/test/conformance-coldstart/Billing/Subscription/Holes.hs b/test/conformance-coldstart/Billing/Subscription/Holes.hs
--- a/test/conformance-coldstart/Billing/Subscription/Holes.hs
+++ b/test/conformance-coldstart/Billing/Subscription/Holes.hs
@@ -1,57 +1,27 @@
-{-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- HAND-FILLED hole module for the EP-7 cold-start demo: a fresh `subscription`
--- aggregate authored from only the skill notation, then filled against the
--- generated signatures. The harness pins this fill.
-module Billing.Subscription.Holes (
-    subscriptionTransducer,
-    applySubscriptions,
-) where
+-- HAND-FILLED language-4 hooks for the EP-7 cold-start demo. Generated code
+-- owns the transition lifecycle; this module contains only the explicit event
+-- mapping and the projection integration point.
+module Billing.Subscription.Holes
+  ( transition2ActiveCancelSubscriptionOutput1SubscriptionCancelled
+  , applySubscriptions
+  ) where
 
 import Generated.Billing.Subscription.Domain
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, lit, (./=))
+import Keiki.Generics (RegFieldsOf)
 
-subscriptionTransducer ::
-    SymTransducer
-        (HsPred SubscriptionRegs SubscriptionCommand)
-        SubscriptionRegs
-        SubscriptionVertex
-        SubscriptionCommand
-        SubscriptionEvent
-subscriptionTransducer =
-    B.buildTransducer SubscriptionInactive initialSubscriptionRegs isTerminal do
-        B.from SubscriptionInactive do
-            B.onCmd inCtorActivateSubscription $ \d -> B.do
-                B.requireGuard (d.plan ./= lit Free)
-                B.slot @"subscriptionState" =: lit SubscriptionActive
-                B.emit
-                    wireSubscriptionActivated
-                    SubscriptionActivatedTermFields
-                        { subscriptionId = d.subscriptionId
-                        , customerId = d.customerId
-                        , plan = d.plan
-                        }
-                B.goto SubscriptionActive
-        B.from SubscriptionActive do
-            B.onCmd inCtorCancelSubscription $ \d -> B.do
-                B.slot @"subscriptionState" =: lit SubscriptionClosed
-                B.emit
-                    wireSubscriptionCancelled
-                    SubscriptionCancelledTermFields
-                        { subscriptionId = d.subscriptionId
-                        , customerId = d.customerId
-                        }
-                B.goto SubscriptionClosed
-  where
-    isTerminal = \case
-        SubscriptionClosed -> True
-        _ -> False
+transition2ActiveCancelSubscriptionOutput1SubscriptionCancelled
+  :: B.PayloadProj SubscriptionRegs SubscriptionCommand (RegFieldsOf CancelSubscriptionData)
+  -> SubscriptionCancelledTermFields SubscriptionRegs SubscriptionCommand (RegFieldsOf CancelSubscriptionData)
+transition2ActiveCancelSubscriptionOutput1SubscriptionCancelled d =
+  SubscriptionCancelledTermFields
+    { subscriptionId = d.subscriptionId
+    , customerId = d.customerId
+    }
 
 applySubscriptions :: SubscriptionEvent -> recorded -> txn ()
 applySubscriptions _event _recorded = error "HOLE: fill subscriptions projection apply"
diff --git a/test/conformance-coldstart/Generated/Billing/Nominals.hs b/test/conformance-coldstart/Generated/Billing/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-coldstart/Generated/Billing/Nominals.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context billing generated nominal declarations; do not edit.
+module Generated.Billing.Nominals
+  ( CustomerId
+  , parseCustomerId
+  , mkCustomerId
+  , customerIdText
+  , Plan (..)
+  , planText
+  , PlanEqualityProjection
+  , planEqualityWitness
+  , SubscriptionId
+  , parseSubscriptionId
+  , mkSubscriptionId
+  , subscriptionIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.Billing.Nominals.Internal (CustomerId, mkCustomerId, parseCustomerId, customerIdText, SubscriptionId, mkSubscriptionId, parseSubscriptionId, subscriptionIdText)
+import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
+import Data.List.NonEmpty (NonEmpty (..))
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+instance CanonicalTypeName CustomerId
+
+data Plan = Paid | Free
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName Plan
+
+planText :: Plan -> Text
+planText = \case
+  Paid -> "paid"
+  Free -> "free"
+
+data PlanEqualityProjection
+
+instance FieldProjection PlanEqualityProjection where
+  type FieldName PlanEqualityProjection = "Plan"
+  type FieldOwner PlanEqualityProjection = Plan
+  type FieldResult PlanEqualityProjection = Text
+  fieldShapeId _ = "nominal-equality|name=Plan|contract=keiro-dsl/nominal-equality/1|key=Text|domain=finite-text:paid,free|owner=generated"
+  projectFieldValue _ = planText
+
+instance ExactFieldProjection PlanEqualityProjection where
+  fieldProjectionDomain _ = finiteProjectionDomain ("paid" :| ["free"])
+  reconstructFieldOwner _ = \case
+    "paid" -> Just Paid
+    "free" -> Just Free
+    _ -> Nothing
+
+planEqualityWitness :: FieldWitness PlanEqualityProjection
+planEqualityWitness = exactFieldWitness @PlanEqualityProjection
+
+instance CanonicalTypeName SubscriptionId
diff --git a/test/conformance-coldstart/Generated/Billing/Nominals/Internal.hs b/test/conformance-coldstart/Generated/Billing/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-coldstart/Generated/Billing/Nominals/Internal.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context billing generated nominal ID internals; do not edit.
+module Generated.Billing.Nominals.Internal
+  ( CustomerId
+  , parseCustomerId
+  , mkCustomerId
+  , customerIdText
+  , unsafeCustomerIdFromLegacyText
+  , SubscriptionId
+  , parseSubscriptionId
+  , mkSubscriptionId
+  , subscriptionIdText
+  , unsafeSubscriptionIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CustomerId = CustomerId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CustomerId where
+  toJSON = toJSON . customerIdText
+
+instance FromJSON CustomerId where
+  parseJSON = withText "CustomerId" (either (fail . T.unpack) pure . parseCustomerId)
+
+parseCustomerId :: Text -> Either Text CustomerId
+parseCustomerId input = case validateIdDomainText (typeIdV7Domain "cust") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CustomerId input)
+
+mkCustomerId :: Text -> Either Text CustomerId
+mkCustomerId = parseCustomerId
+
+customerIdText :: CustomerId -> Text
+customerIdText (CustomerId value) = value
+
+unsafeCustomerIdFromLegacyText :: Text -> CustomerId
+unsafeCustomerIdFromLegacyText = CustomerId
+
+newtype SubscriptionId = SubscriptionId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON SubscriptionId where
+  toJSON = toJSON . subscriptionIdText
+
+instance FromJSON SubscriptionId where
+  parseJSON = withText "SubscriptionId" (either (fail . T.unpack) pure . parseSubscriptionId)
+
+parseSubscriptionId :: Text -> Either Text SubscriptionId
+parseSubscriptionId input = case validateIdDomainText (typeIdV7Domain "sub") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (SubscriptionId input)
+
+mkSubscriptionId :: Text -> Either Text SubscriptionId
+mkSubscriptionId = parseSubscriptionId
+
+subscriptionIdText :: SubscriptionId -> Text
+subscriptionIdText (SubscriptionId value) = value
+
+unsafeSubscriptionIdFromLegacyText :: Text -> SubscriptionId
+unsafeSubscriptionIdFromLegacyText = SubscriptionId
diff --git a/test/conformance-coldstart/Generated/Billing/ReplayAudit.hs b/test/conformance-coldstart/Generated/Billing/ReplayAudit.hs
--- a/test/conformance-coldstart/Generated/Billing/ReplayAudit.hs
+++ b/test/conformance-coldstart/Generated/Billing/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context billing replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Subscription.subscriptionEventStream
-            , category = Stream.categoryText Subscription.subscriptionCategory
-            , mkStream = streamInCategory (Stream.categoryText Subscription.subscriptionCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Subscription.subscriptionEventStream
+        , category = Stream.categoryText Subscription.subscriptionCategory
+        , mkStream = streamInCategory (Stream.categoryText Subscription.subscriptionCategory)
+        }
+  ]
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/Codec.hs b/test/conformance-coldstart/Generated/Billing/Subscription/Codec.hs
--- a/test/conformance-coldstart/Generated/Billing/Subscription/Codec.hs
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/Codec.hs
@@ -1,65 +1,78 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
 module Generated.Billing.Subscription.Codec (
     subscriptionCodec,
     parseSubscriptionEvent,
     encodeSubscriptionEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.Billing.Subscription.Domain
+import Generated.Billing.Nominals (CustomerId, customerIdText, Plan (..), planText, SubscriptionId, subscriptionIdText)
+import Generated.Billing.Nominals.Internal (unsafeCustomerIdFromLegacyText, unsafeSubscriptionIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.Billing.Subscription.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
 parsePlan :: Text -> Parser Plan
 parsePlan = \case
-    "paid" -> pure Paid
-    "free" -> pure Free
-    _ -> fail "unknown Plan"
+  "paid" -> pure Paid
+  "free" -> pure Free
+  tag -> fail ("unknown Plan " <> show tag <> "; expected one of: paid, free")
 
+
 subscriptionCodec :: Codec SubscriptionEvent
 subscriptionCodec =
-    Codec
-        { eventTypes = EventType "SubscriptionActivated" :| [EventType "SubscriptionCancelled"]
-        , eventType = \case
-            SubscriptionActivated{} -> EventType "SubscriptionActivated"
-            SubscriptionCancelled{} -> EventType "SubscriptionCancelled"
-        , schemaVersion = 1
-        , encode = encodeSubscriptionEvent
-        , decode = parseSubscriptionEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "SubscriptionActivated" :| [EventType "SubscriptionCancelled"]
+    , eventType = \case
+        SubscriptionActivated{} -> EventType "SubscriptionActivated"
+        SubscriptionCancelled{} -> EventType "SubscriptionCancelled"
+    , schemaVersion = 1
+    , encode = encodeSubscriptionEvent
+    , decode = parseSubscriptionEvent
+    , upcasters = []
+    }
 
 encodeSubscriptionEvent :: SubscriptionEvent -> Value
 encodeSubscriptionEvent = \case
-    SubscriptionActivated payload ->
-        object
-            [ "kind" .= ("SubscriptionActivated" :: Text)
-            , "subscriptionId" .= subscriptionIdText payload.subscriptionId
-            , "customerId" .= customerIdText payload.customerId
-            , "plan" .= planText payload.plan
-            ]
-    SubscriptionCancelled payload ->
-        object
-            [ "kind" .= ("SubscriptionCancelled" :: Text)
-            , "subscriptionId" .= subscriptionIdText payload.subscriptionId
-            , "customerId" .= customerIdText payload.customerId
-            ]
+  SubscriptionActivated payload ->
+    object
+      [ "kind" .= ("SubscriptionActivated" :: Text)
+      , "subscriptionId" .= subscriptionIdText payload.subscriptionId
+      , "customerId" .= customerIdText payload.customerId
+      , "plan" .= planText payload.plan
+      ]
+  SubscriptionCancelled payload ->
+    object
+      [ "kind" .= ("SubscriptionCancelled" :: Text)
+      , "subscriptionId" .= subscriptionIdText payload.subscriptionId
+      , "customerId" .= customerIdText payload.customerId
+      ]
 
 parseSubscriptionEvent :: EventType -> Value -> Either Text SubscriptionEvent
 parseSubscriptionEvent (EventType tag) = mapLeftText . parseEither (withObject "SubscriptionEvent" go)
   where
     go o = do
-        case tag of
-            "SubscriptionActivated" ->
-                SubscriptionActivated <$> (SubscriptionActivatedData <$> (SubscriptionId <$> o .: "subscriptionId") <*> (CustomerId <$> o .: "customerId") <*> (o .: "plan" >>= parsePlan))
-            "SubscriptionCancelled" ->
-                SubscriptionCancelled <$> (SubscriptionCancelledData <$> (SubscriptionId <$> o .: "subscriptionId") <*> (CustomerId <$> o .: "customerId"))
-            _ -> fail "unknown event type"
+      case tag of
+        "SubscriptionActivated" ->
+          SubscriptionActivated
+            <$> ( SubscriptionActivatedData
+                    <$> (unsafeSubscriptionIdFromLegacyText <$> o .: "subscriptionId")
+                    <*> (unsafeCustomerIdFromLegacyText <$> o .: "customerId")
+                    <*> explicitParseField (withText "Plan" parsePlan) o "plan"
+                )
+        "SubscriptionCancelled" ->
+          SubscriptionCancelled
+            <$> ( SubscriptionCancelledData
+                    <$> (unsafeSubscriptionIdFromLegacyText <$> o .: "subscriptionId")
+                    <*> (unsafeCustomerIdFromLegacyText <$> o .: "customerId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SubscriptionActivated, SubscriptionCancelled")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/Domain.hs b/test/conformance-coldstart/Generated/Billing/Subscription/Domain.hs
--- a/test/conformance-coldstart/Generated/Billing/Subscription/Domain.hs
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/Domain.hs
@@ -2,36 +2,16 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
 module Generated.Billing.Subscription.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
+import Generated.Billing.Nominals (CustomerId, parseCustomerId, Plan (..), SubscriptionId, parseSubscriptionId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
-newtype SubscriptionId = SubscriptionId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-subscriptionIdText :: SubscriptionId -> Text
-subscriptionIdText (SubscriptionId t) = t
-
-newtype CustomerId = CustomerId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-customerIdText :: CustomerId -> Text
-customerIdText (CustomerId t) = t
-
-data Plan = Paid | Free
-  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-
-planText :: Plan -> Text
-planText = \case
-  Paid -> "paid"
-  Free -> "free"
-
 data SubscriptionVertex = SubscriptionInactive | SubscriptionActive | SubscriptionClosed
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
@@ -71,13 +51,11 @@
 
 type SubscriptionRegs =
   '[ '("plan", Plan)
-   , '("subscriptionState", SubscriptionVertex)
    ]
 
 initialSubscriptionRegs :: RegFile SubscriptionRegs
 initialSubscriptionRegs =
-  RCons (Proxy @"plan") Free $
-  RCons (Proxy @"subscriptionState") SubscriptionInactive RNil
+  RCons (Proxy @"plan") Free RNil
 
 $(deriveAggregateCtorsAll ''SubscriptionCommand ''SubscriptionRegs)
 
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/EventStream.hs b/test/conformance-coldstart/Generated/Billing/Subscription/EventStream.hs
--- a/test/conformance-coldstart/Generated/Billing/Subscription/EventStream.hs
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/EventStream.hs
@@ -1,15 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.Billing.Subscription.EventStream (
-    subscriptionCategory,
-    subscriptionEventStream,
-    subscriptionEventStreamDef,
-    SubscriptionEventStream,
-    SubscriptionEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
+module Generated.Billing.Subscription.EventStream
+  ( subscriptionCategory
+  , subscriptionCommandCategory
+  , subscriptionEventStream
+  , subscriptionEventStreamDef
+  , SubscriptionEventStream
+  , SubscriptionEventStreamDef
+  ) where
 
-import Billing.Subscription.Holes (subscriptionTransducer)
-import Generated.Billing.Subscription.Codec (subscriptionCodec)
 import Generated.Billing.Subscription.Domain
+import Generated.Billing.Subscription.Codec (subscriptionCodec)
+import Generated.Billing.Subscription.Transducer (subscriptionFoldFingerprint, subscriptionTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,27 +19,31 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-subscriptionCategory :: Stream.StreamCategory a
+subscriptionCategory :: Stream.StreamCategory SubscriptionEventStreamDef
 subscriptionCategory = Stream.categoryUnsafe "subscription"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+subscriptionCommandCategory :: Stream.StreamCategory SubscriptionCommand
+subscriptionCommandCategory = Stream.categoryUnsafe "subscription"
+
 type SubscriptionEventStreamDef =
-    EventStream (HsPred SubscriptionRegs SubscriptionCommand) SubscriptionRegs SubscriptionVertex SubscriptionCommand SubscriptionEvent
+  EventStream (HsPred SubscriptionRegs SubscriptionCommand) SubscriptionRegs SubscriptionVertex SubscriptionCommand SubscriptionEvent
 
 type SubscriptionEventStream =
-    ValidatedEventStream (HsPred SubscriptionRegs SubscriptionCommand) SubscriptionRegs SubscriptionVertex SubscriptionCommand SubscriptionEvent
+  ValidatedEventStream (HsPred SubscriptionRegs SubscriptionCommand) SubscriptionRegs SubscriptionVertex SubscriptionCommand SubscriptionEvent
 
 subscriptionEventStreamDef :: SubscriptionEventStreamDef
 subscriptionEventStreamDef =
-    EventStream
-        { transducer = subscriptionTransducer
-        , initialState = SubscriptionInactive
-        , initialRegisters = initialSubscriptionRegs
-        , eventCodec = subscriptionCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = subscriptionTransducer,
+      initialState = SubscriptionInactive,
+      initialRegisters = initialSubscriptionRegs,
+      eventCodec = subscriptionCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 subscriptionEventStream :: SubscriptionEventStream
 subscriptionEventStream =
-    mkEventStreamOrThrow "Subscription" subscriptionEventStreamDef
+  mkEventStreamOrThrow "Subscription" subscriptionEventStreamDef
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/Harness.hs b/test/conformance-coldstart/Generated/Billing/Subscription/Harness.hs
--- a/test/conformance-coldstart/Generated/Billing/Subscription/Harness.hs
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/Harness.hs
@@ -1,60 +1,58 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
 module Generated.Billing.Subscription.Harness (harnessAssertions) where
 
-import Billing.Subscription.Holes (subscriptionTransducer)
-import Generated.Billing.Subscription.Codec (encodeSubscriptionEvent, parseSubscriptionEvent, subscriptionCodec)
 import Generated.Billing.Subscription.Domain
+import Generated.Billing.Subscription.Codec (encodeSubscriptionEvent, parseSubscriptionEvent, subscriptionCodec)
+import Generated.Billing.Subscription.Transducer (subscriptionTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
+import Generated.Billing.Nominals (CustomerId, parseCustomerId, Plan (..), SubscriptionId, parseSubscriptionId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions subscriptionTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: SubscriptionActivated", roundTrips sampleEventSubscriptionActivated)
-    , ("golden round-trip: SubscriptionCancelled", roundTrips sampleEventSubscriptionCancelled)
-    , ("accepts ActivateSubscription from SubscriptionInactive", acceptActivateSubscription)
-    ]
-        ++ forwardReplayActivateSubscription
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions subscriptionTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: SubscriptionActivated", roundTrips sampleEventSubscriptionActivated)
+  , ("golden round-trip: SubscriptionCancelled", roundTrips sampleEventSubscriptionCancelled)
+  , ("accepts ActivateSubscription from SubscriptionInactive", acceptActivateSubscription)
+  ]
+  ++ forwardReplayActivateSubscription
 
 roundTrips :: SubscriptionEvent -> Bool
 roundTrips e = parseSubscriptionEvent (eventType subscriptionCodec e) (encodeSubscriptionEvent e) == Right e
 
 sampleEventSubscriptionActivated :: SubscriptionEvent
-sampleEventSubscriptionActivated = (SubscriptionActivated (SubscriptionActivatedData (SubscriptionId "sample") (CustomerId "sample") Paid))
+sampleEventSubscriptionActivated = (SubscriptionActivated (SubscriptionActivatedData (case parseSubscriptionId "sub_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCustomerId "cust_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Paid))
 
 sampleEventSubscriptionCancelled :: SubscriptionEvent
-sampleEventSubscriptionCancelled = (SubscriptionCancelled (SubscriptionCancelledData (SubscriptionId "sample") (CustomerId "sample")))
+sampleEventSubscriptionCancelled = (SubscriptionCancelled (SubscriptionCancelledData (case parseSubscriptionId "sub_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCustomerId "cust_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))
 
 acceptActivateSubscription :: Bool
 acceptActivateSubscription =
-    case step subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) ((ActivateSubscription (ActivateSubscriptionData (SubscriptionId "sample") (CustomerId "sample") Paid))) of
-        Just (v, _, _) -> v == SubscriptionActive
-        Nothing -> False
+  case step subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) ((ActivateSubscription (ActivateSubscriptionData (case parseSubscriptionId "sub_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCustomerId "cust_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Paid))) of
+    Just (v, _, _) -> v == SubscriptionActive
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayActivateSubscription :: [(String, Bool)]
 forwardReplayActivateSubscription =
-    case step subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) ((ActivateSubscription (ActivateSubscriptionData (SubscriptionId "sample") (CustomerId "sample") Paid))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, forwardRegs, emitted) ->
-            case mapM (\event -> parseSubscriptionEvent (eventType subscriptionCodec event) (encodeSubscriptionEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            , (prefix <> "register plan", (replayRegs ! #plan) == (forwardRegs ! #plan))
-                            , (prefix <> "register subscriptionState", (replayRegs ! #subscriptionState) == (forwardRegs ! #subscriptionState))
-                            ]
+  case step subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) ((ActivateSubscription (ActivateSubscriptionData (case parseSubscriptionId "sub_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCustomerId "cust_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Paid))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, forwardRegs, emitted) ->
+      case mapM (\event -> parseSubscriptionEvent (eventType subscriptionCodec event) (encodeSubscriptionEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither subscriptionTransducer (SubscriptionInactive, initialSubscriptionRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              , (prefix <> "register plan", (replayRegs ! #plan) == (forwardRegs ! #plan))
+              ]
   where
     prefix = "forward/replay equality: ActivateSubscription from SubscriptionInactive -- "
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/Projection.hs b/test/conformance-coldstart/Generated/Billing/Subscription/Projection.hs
--- a/test/conformance-coldstart/Generated/Billing/Subscription/Projection.hs
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/Projection.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
 module Generated.Billing.Subscription.Projection
   ( subscriptionsProjection
   , subscriptionsStatusFor
@@ -13,6 +13,7 @@
 -- The deterministic event->status mapping (hole-kind 3, /mapping/), derived
 -- from the spec's status-map. The read-model SQL that consumes it lives in
 -- the hand-owned Holes module (a DB-coupled hole, delegated to codd).
+-- WARNING: no readmodel node declares 'subscriptions'; unqualified SQL depends on search_path.
 subscriptionsStatusFor :: SubscriptionEvent -> Maybe Text
 subscriptionsStatusFor = \case
   SubscriptionActivated {} -> Just "active"
diff --git a/test/conformance-coldstart/Generated/Billing/Subscription/Transducer.hs b/test/conformance-coldstart/Generated/Billing/Subscription/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-coldstart/Generated/Billing/Subscription/Transducer.hs
@@ -0,0 +1,73 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Subscription; do not edit.
+module Generated.Billing.Subscription.Transducer
+  ( subscriptionTransducer
+  , subscriptionFoldFingerprint
+  , BehaviorOwnership (..)
+  , subscriptionPredicateVerifications
+  ) where
+
+import Generated.Billing.Subscription.Domain
+import Data.Text (Text)
+import Generated.Billing.Nominals (Plan (..))
+import Generated.Billing.Nominals qualified as GeneratedNominals
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer, (./=))
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import Billing.Subscription.Holes qualified as Holes
+import Keiki.Generics (RegFieldsOf)
+
+subscriptionTransducer
+  :: SymTransducer
+       (HsPred SubscriptionRegs SubscriptionCommand)
+       SubscriptionRegs
+       SubscriptionVertex
+       SubscriptionCommand
+       SubscriptionEvent
+subscriptionTransducer =
+  B.buildTransducer SubscriptionInactive initialSubscriptionRegs isTerminal do
+    B.from SubscriptionInactive do
+      B.onCmd inCtorActivateSubscription $ \d -> B.do
+        let commandPlan = K.inpProj GeneratedNominals.planEqualityWitness inCtorActivateSubscription (#plan :: K.Index (RegFieldsOf ActivateSubscriptionData) Plan)
+        B.requireGuard $
+          commandPlan ./= K.lit ("free" :: Text)
+        B.emit wireSubscriptionActivated (SubscriptionActivatedTermFields
+          { subscriptionId = d.subscriptionId
+          , customerId = d.customerId
+          , plan = d.plan
+          })
+        B.goto SubscriptionActive
+    B.from SubscriptionActive do
+      B.onCmd inCtorCancelSubscription $ \d -> B.do
+        B.emit wireSubscriptionCancelled (Holes.transition2ActiveCancelSubscriptionOutput1SubscriptionCancelled d)
+        B.goto SubscriptionClosed
+ where
+  isTerminal = \case
+    SubscriptionClosed -> True
+    _ -> False
+
+subscriptionFoldFingerprint :: Text
+subscriptionFoldFingerprint = "8fdbe22f164b53fe045097803ecdfa2c"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+subscriptionPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+subscriptionPredicateVerifications = sequence
+  [ verifyTransition "transition1InactiveActivateSubscription" GeneratedOwned SubscriptionInactive 0
+  , verifyTransition "transition2ActiveCancelSubscription" GeneratedOwned SubscriptionActive 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut subscriptionTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-contract-v1-compat/Generated/HospitalCapacity/Emergency/Contract.hs b/test/conformance-contract-v1-compat/Generated/HospitalCapacity/Emergency/Contract.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-contract-v1-compat/Generated/HospitalCapacity/Emergency/Contract.hs
@@ -0,0 +1,95 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 1) from contract emergency; do not edit.
+module Generated.HospitalCapacity.Emergency.Contract
+  ( EmergencyPayload (..),
+    IncidentTransferNeedDeclaredData (..),
+    TransferReservationAcceptedData (..),
+    incidentEventsTopic,
+    hospitalEventsTopic,
+    messageTypeOf,
+    encodeEmergencyPayload,
+    parseEmergencyPayload,
+  )
+where
+
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.Text (Text)
+import Data.Text qualified as T
+
+-- topic constants
+incidentEventsTopic :: Text
+incidentEventsTopic = "emergency.incident.events"
+
+hospitalEventsTopic :: Text
+hospitalEventsTopic = "emergency.hospital.events"
+
+-- the closed payload set (discriminated by "messageType")
+data IncidentTransferNeedDeclaredData = IncidentTransferNeedDeclaredData {incidentId :: !Text, triageRecordId :: !Text, region :: !Text, redCount :: !Int}
+  deriving stock (Eq, Show)
+
+data TransferReservationAcceptedData = TransferReservationAcceptedData {incidentId :: !Text, reservationId :: !Text, hospitalId :: !Text, expirationDeadline :: !Text}
+  deriving stock (Eq, Show)
+
+data EmergencyPayload
+  = IncidentTransferNeedDeclared !IncidentTransferNeedDeclaredData
+  | TransferReservationAccepted !TransferReservationAcceptedData
+  deriving stock (Eq, Show)
+
+messageTypeOf :: EmergencyPayload -> Text
+messageTypeOf = \case
+  IncidentTransferNeedDeclared {} -> "IncidentTransferNeedDeclared"
+  TransferReservationAccepted {} -> "TransferReservationAccepted"
+
+encodeEmergencyPayload :: EmergencyPayload -> Value
+encodeEmergencyPayload = \case
+  IncidentTransferNeedDeclared payload ->
+    object
+      [ "messageType" .= ("IncidentTransferNeedDeclared" :: Text),
+        "incidentId" .= payload.incidentId,
+        "triageRecordId" .= payload.triageRecordId,
+        "region" .= payload.region,
+        "redCount" .= payload.redCount
+      ]
+  TransferReservationAccepted payload ->
+    object
+      [ "messageType" .= ("TransferReservationAccepted" :: Text),
+        "incidentId" .= payload.incidentId,
+        "reservationId" .= payload.reservationId,
+        "hospitalId" .= payload.hospitalId,
+        "expirationDeadline" .= payload.expirationDeadline
+      ]
+
+parseEmergencyPayload :: Value -> Either Text EmergencyPayload
+parseEmergencyPayload = mapLeftText . parseEither (withObject "EmergencyPayload" go)
+  where
+    go o = do
+      kind <- explicitParseField (withText "messageType" validateMessageType) o "messageType"
+      case kind of
+        "IncidentTransferNeedDeclared" ->
+          IncidentTransferNeedDeclared
+            <$> ( IncidentTransferNeedDeclaredData
+                    <$> o .: "incidentId"
+                    <*> o .: "triageRecordId"
+                    <*> o .: "region"
+                    <*> o .: "redCount"
+                )
+        "TransferReservationAccepted" ->
+          TransferReservationAccepted
+            <$> ( TransferReservationAcceptedData
+                    <$> o .: "incidentId"
+                    <*> o .: "reservationId"
+                    <*> o .: "hospitalId"
+                    <*> o .: "expirationDeadline"
+                )
+        _ -> fail "validated message type was not handled"
+
+mapLeftText :: Either String b -> Either Text b
+mapLeftText = either (Left . T.pack) Right
+
+validateMessageType :: Text -> Parser Text
+validateMessageType kind
+  | kind `elem` ["IncidentTransferNeedDeclared", "TransferReservationAccepted"] = pure kind
+  | otherwise = fail ("unknown message type " <> show kind <> "; expected one of: IncidentTransferNeedDeclared, TransferReservationAccepted")
diff --git a/test/conformance-contract-v1-compat/Main.hs b/test/conformance-contract-v1-compat/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-contract-v1-compat/Main.hs
@@ -0,0 +1,55 @@
+-- | Conformance driver for the scaffolded EP-4 contract layer. Compiling this
+-- component proves the scaffolded @Generated.…Emergency.Contract@ module (the
+-- payload ADT + topic constants + messageType discriminator + strict codec) is
+-- real, self-contained Haskell; running it proves every contract event type
+-- round-trips through encode/decode and that messageTypeOf agrees.
+module Main (main) where
+
+import Control.Monad (forM_, unless)
+import Data.Aeson (encode, object, (.=))
+import Data.ByteString.Lazy (ByteString)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Generated.HospitalCapacity.Emergency.Contract
+import System.Exit (exitFailure)
+
+samples :: [(String, EmergencyPayload, ByteString)]
+samples =
+  [ ( "IncidentTransferNeedDeclared",
+      IncidentTransferNeedDeclared (IncidentTransferNeedDeclaredData "inc-1" "tri-1" "north" 3),
+      "{\"incidentId\":\"inc-1\",\"messageType\":\"IncidentTransferNeedDeclared\",\"redCount\":3,\"region\":\"north\",\"triageRecordId\":\"tri-1\"}"
+    ),
+    ( "TransferReservationAccepted",
+      TransferReservationAccepted (TransferReservationAcceptedData "inc-1" "rsv-1" "hsp-1" "2026-01-01"),
+      "{\"expirationDeadline\":\"2026-01-01\",\"hospitalId\":\"hsp-1\",\"incidentId\":\"inc-1\",\"messageType\":\"TransferReservationAccepted\",\"reservationId\":\"rsv-1\"}"
+    )
+  ]
+
+main :: IO ()
+main = do
+  let results =
+        [ (label, parseEmergencyPayload (encodeEmergencyPayload p) == Right p && messageTypeOf p == T.pack label && encode (encodeEmergencyPayload p) == expectedBytes)
+        | (label, p, expectedBytes) <- samples
+        ]
+      unknownTagRejected = case parseEmergencyPayload (object ["messageType" .= ("UnknownMessage" :: Text)]) of
+        Left problem ->
+          all
+            (`T.isInfixOf` problem)
+            [ "$.messageType",
+              "UnknownMessage",
+              "IncidentTransferNeedDeclared",
+              "TransferReservationAccepted"
+            ]
+        Right _ -> False
+      missingFieldRejected = case parseEmergencyPayload (object ["messageType" .= ("IncidentTransferNeedDeclared" :: Text)]) of
+        Left problem -> "incidentId" `T.isInfixOf` problem
+        Right _ -> False
+      topicsExported =
+        incidentEventsTopic == "emergency.incident.events"
+          && hospitalEventsTopic == "emergency.hospital.events"
+  forM_ results $ \(label, ok) -> putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
+  let failed = [label | (label, ok) <- results, not ok]
+  putStrLn ("unknown discriminator is diagnosed at its field: " <> show unknownTagRejected)
+  putStrLn ("missing field is named: " <> show missingFieldRejected)
+  putStrLn ("topic constants are exported: " <> show topicsExported)
+  unless (null failed && unknownTagRejected && missingFieldRejected && topicsExported) $ putStrLn ("contract: failed " <> show failed) >> exitFailure
diff --git a/test/conformance-contract/Generated/HospitalCapacity/Emergency/Contract.hs b/test/conformance-contract/Generated/HospitalCapacity/Emergency/Contract.hs
--- a/test/conformance-contract/Generated/HospitalCapacity/Emergency/Contract.hs
+++ b/test/conformance-contract/Generated/HospitalCapacity/Emergency/Contract.hs
@@ -1,35 +1,45 @@
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+{-# LANGUAGE TypeApplications #-}
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from contract emergency; do not edit.
 module Generated.HospitalCapacity.Emergency.Contract
-  ( EmergencyPayload (..)
-  , IncidentTransferNeedDeclaredData (..)
-  , TransferReservationAcceptedData (..)
-  , messageTypeOf
-  , encodeEmergencyPayload
-  , parseEmergencyPayload
-  ) where
+  ( EmergencyPayload (..),
+    IncidentTransferNeedDeclaredData (..),
+    TransferReservationAcceptedData (..),
+    incidentEventsTopic,
+    hospitalEventsTopic,
+    messageTypeOf,
+    encodeEmergencyPayload,
+    parseEmergencyPayload,
+  )
+where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.KindID (KindID)
+import Data.KindID qualified as KindID
 import Data.Text (Text)
-import qualified Data.Text as T
+import Data.Text qualified as T
+import Keiro.Codec.IdDomain (parseKindIdV7Value)
 
 -- topic constants
 incidentEventsTopic :: Text
 incidentEventsTopic = "emergency.incident.events"
+
 hospitalEventsTopic :: Text
 hospitalEventsTopic = "emergency.hospital.events"
 
 -- the closed payload set (discriminated by "messageType")
-data IncidentTransferNeedDeclaredData = IncidentTransferNeedDeclaredData { incidentId :: !Text, triageRecordId :: !Text, region :: !Text, redCount :: !Int }
+data IncidentTransferNeedDeclaredData = IncidentTransferNeedDeclaredData {incidentId :: !(KindID "inc"), triageRecordId :: !Text, region :: !Text, redCount :: !Int}
   deriving stock (Eq, Show)
 
-data TransferReservationAcceptedData = TransferReservationAcceptedData { incidentId :: !Text, reservationId :: !Text, hospitalId :: !Text, expirationDeadline :: !Text }
+data TransferReservationAcceptedData = TransferReservationAcceptedData {incidentId :: !(KindID "inc"), reservationId :: !(KindID "rsv"), hospitalId :: !(KindID "hsp"), expirationDeadline :: !Text}
   deriving stock (Eq, Show)
 
-data EmergencyPayload = IncidentTransferNeedDeclared !IncidentTransferNeedDeclaredData
+data EmergencyPayload
+  = IncidentTransferNeedDeclared !IncidentTransferNeedDeclaredData
   | TransferReservationAccepted !TransferReservationAcceptedData
   deriving stock (Eq, Show)
 
@@ -42,32 +52,49 @@
 encodeEmergencyPayload = \case
   IncidentTransferNeedDeclared payload ->
     object
-      [ "messageType" .= ("IncidentTransferNeedDeclared" :: Text)
-      , "incidentId" .= payload.incidentId
-      , "triageRecordId" .= payload.triageRecordId
-      , "region" .= payload.region
-      , "redCount" .= payload.redCount
+      [ "messageType" .= ("IncidentTransferNeedDeclared" :: Text),
+        "incidentId" .= KindID.toText payload.incidentId,
+        "triageRecordId" .= payload.triageRecordId,
+        "region" .= payload.region,
+        "redCount" .= payload.redCount
       ]
   TransferReservationAccepted payload ->
     object
-      [ "messageType" .= ("TransferReservationAccepted" :: Text)
-      , "incidentId" .= payload.incidentId
-      , "reservationId" .= payload.reservationId
-      , "hospitalId" .= payload.hospitalId
-      , "expirationDeadline" .= payload.expirationDeadline
+      [ "messageType" .= ("TransferReservationAccepted" :: Text),
+        "incidentId" .= KindID.toText payload.incidentId,
+        "reservationId" .= KindID.toText payload.reservationId,
+        "hospitalId" .= KindID.toText payload.hospitalId,
+        "expirationDeadline" .= payload.expirationDeadline
       ]
 
 parseEmergencyPayload :: Value -> Either Text EmergencyPayload
 parseEmergencyPayload = mapLeftText . parseEither (withObject "EmergencyPayload" go)
   where
     go o = do
-      kind <- o .: "messageType" :: Parser Text
+      kind <- explicitParseField (withText "messageType" validateMessageType) o "messageType"
       case kind of
         "IncidentTransferNeedDeclared" ->
-          IncidentTransferNeedDeclared <$> (IncidentTransferNeedDeclaredData <$> o .: "incidentId" <*> o .: "triageRecordId" <*> o .: "region" <*> o .: "redCount")
+          IncidentTransferNeedDeclared
+            <$> ( IncidentTransferNeedDeclaredData
+                    <$> explicitParseField (parseKindIdV7Value @"inc") o "incidentId"
+                    <*> o .: "triageRecordId"
+                    <*> o .: "region"
+                    <*> o .: "redCount"
+                )
         "TransferReservationAccepted" ->
-          TransferReservationAccepted <$> (TransferReservationAcceptedData <$> o .: "incidentId" <*> o .: "reservationId" <*> o .: "hospitalId" <*> o .: "expirationDeadline")
-        _ -> fail "unknown message type"
+          TransferReservationAccepted
+            <$> ( TransferReservationAcceptedData
+                    <$> explicitParseField (parseKindIdV7Value @"inc") o "incidentId"
+                    <*> explicitParseField (parseKindIdV7Value @"rsv") o "reservationId"
+                    <*> explicitParseField (parseKindIdV7Value @"hsp") o "hospitalId"
+                    <*> o .: "expirationDeadline"
+                )
+        _ -> fail "validated message type was not handled"
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
+
+validateMessageType :: Text -> Parser Text
+validateMessageType kind
+  | kind `elem` ["IncidentTransferNeedDeclared", "TransferReservationAccepted"] = pure kind
+  | otherwise = fail ("unknown message type " <> show kind <> "; expected one of: IncidentTransferNeedDeclared, TransferReservationAccepted")
diff --git a/test/conformance-contract/Main.hs b/test/conformance-contract/Main.hs
--- a/test/conformance-contract/Main.hs
+++ b/test/conformance-contract/Main.hs
@@ -1,29 +1,89 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeApplications #-}
 
-{- | Conformance driver for the scaffolded EP-4 contract layer. Compiling this
-component proves the scaffolded @Generated.…Emergency.Contract@ module (the
-payload ADT + topic constants + messageType discriminator + strict codec) is
-real, self-contained Haskell; running it proves every contract event type
-round-trips through encode/decode and that messageTypeOf agrees.
--}
+-- | Conformance driver for language-4 prefix-indexed integration contracts.
+-- Compiling this component proves distinct contract fields carry distinct
+-- @KindID@ types; running it proves canonical JSON remains text and every frozen
+-- admission failure is attributed to the owning field path.
 module Main (main) where
 
 import Control.Monad (forM_, unless)
-import Data.Text qualified as T
+import Data.Aeson (Value, object, (.=))
+import Data.KindID (KindID)
+import Data.Text (Text)
+import qualified Data.Text as T
 import Generated.HospitalCapacity.Emergency.Contract
+import Keiro.Codec.IdDomain (parseKindIdV7Text)
 import System.Exit (exitFailure)
 
-samples :: [(String, EmergencyPayload)]
-samples =
-    [ ("IncidentTransferNeedDeclared", IncidentTransferNeedDeclared (IncidentTransferNeedDeclaredData "inc-1" "tri-1" "north" 3))
-    , ("TransferReservationAccepted", TransferReservationAccepted (TransferReservationAcceptedData "inc-1" "rsv-1" "hsp-1" "2026-01-01"))
+canonicalSuffix :: Text
+canonicalSuffix = "01h455vb4pex5vsknk084sn02q"
+
+incidentIdValue :: KindID "inc"
+incidentIdValue = either (error . show) id (parseKindIdV7Text @"inc" ("inc_" <> canonicalSuffix))
+
+reservationIdValue :: KindID "rsv"
+reservationIdValue = either (error . show) id (parseKindIdV7Text @"rsv" ("rsv_" <> canonicalSuffix))
+
+hospitalIdValue :: KindID "hsp"
+hospitalIdValue = either (error . show) id (parseKindIdV7Text @"hsp" ("hsp_" <> canonicalSuffix))
+
+incidentPayload :: EmergencyPayload
+incidentPayload =
+  IncidentTransferNeedDeclared
+    (IncidentTransferNeedDeclaredData incidentIdValue "tri-1" "north" 3)
+
+reservationPayload :: EmergencyPayload
+reservationPayload =
+  TransferReservationAccepted
+    (TransferReservationAcceptedData incidentIdValue reservationIdValue hospitalIdValue "2026-01-01")
+
+incidentJson :: Text -> Value
+incidentJson rawIncidentId =
+  object
+    [ "messageType" .= ("IncidentTransferNeedDeclared" :: Text),
+      "incidentId" .= rawIncidentId,
+      "triageRecordId" .= ("tri-1" :: Text),
+      "region" .= ("north" :: Text),
+      "redCount" .= (3 :: Int)
     ]
 
 main :: IO ()
 main = do
-    let results =
-            [ (label, parseEmergencyPayload (encodeEmergencyPayload p) == Right p && messageTypeOf p == T.pack label)
-            | (label, p) <- samples
-            ]
-    forM_ results $ \(label, ok) -> putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
-    let failed = [label | (label, ok) <- results, not ok]
-    unless (null failed) $ putStrLn ("contract: failed " <> show failed) >> exitFailure
+  let validIncidentJson = incidentJson ("inc_" <> canonicalSuffix)
+      validReservationJson =
+        object
+          [ "messageType" .= ("TransferReservationAccepted" :: Text),
+            "incidentId" .= ("inc_" <> canonicalSuffix),
+            "reservationId" .= ("rsv_" <> canonicalSuffix),
+            "hospitalId" .= ("hsp_" <> canonicalSuffix),
+            "expirationDeadline" .= ("2026-01-01" :: Text)
+          ]
+      checks =
+        [ ( "topic constants exported",
+            incidentEventsTopic == "emergency.incident.events"
+              && hospitalEventsTopic == "emergency.hospital.events"
+          ),
+          ( "IncidentTransferNeedDeclared round-trip",
+            encodeEmergencyPayload incidentPayload == validIncidentJson
+              && parseEmergencyPayload validIncidentJson == Right incidentPayload
+          ),
+          ( "TransferReservationAccepted round-trip",
+            encodeEmergencyPayload reservationPayload == validReservationJson
+              && parseEmergencyPayload validReservationJson == Right reservationPayload
+          ),
+          rejection "malformed $.incidentId" "inc-1" "malformed TypeID text",
+          rejection "wrong-prefix $.incidentId" ("rsv_" <> canonicalSuffix) "prefix mismatch",
+          rejection "non-canonical $.incidentId" "inc_01H455VB4PEX5VSKNK084SN02Q" "not canonical lowercase",
+          rejection "non-v7 $.incidentId" "inc_00041061050r3gg28a1c60t3gf" "not UUIDv7"
+        ]
+  forM_ checks $ \(label, ok) -> putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
+  let failed = [label | (label, ok) <- checks, not ok]
+  unless (null failed) $ putStrLn ("typed contract: failed " <> show failed) >> exitFailure
+  where
+    rejection label raw expected =
+      ( label,
+        case parseEmergencyPayload (incidentJson raw) of
+          Left problem -> "$.incidentId" `T.isInfixOf` problem && expected `T.isInfixOf` problem
+          Right _ -> False
+      )
diff --git a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/Queue.hs b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/Queue.hs
--- a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/Queue.hs
+++ b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/Queue.hs
@@ -1,22 +1,17 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation_work.Queue (
-    ReservationWorkItem (..),
-    encodeReservationWorkItem,
-    parseReservationWorkItem,
-    queuePhysical,
-    queueDlq,
-    queueTable,
-    groupKeyField,
-    groupKeyFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module Generated.HospitalCapacity.Reservation_work.Queue
+  ( ReservationWorkItem (..)
+  , encodeReservationWorkItem
+  , parseReservationWorkItem
+  , queuePhysical, queueDlq, queueTable
+  , groupKeyField, groupKeyFor
+  ) where
 
 import Data.Aeson (Value, object, withObject, (.:), (.=))
 import Data.Aeson.Types (parseEither)
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 
 queuePhysical, queueDlq, queueTable :: Text
 queuePhysical = "hospital_capacity_reservation_work"
@@ -30,21 +25,21 @@
 groupKeyFor payload = payload.reservationId
 
 data ReservationWorkItem = ReservationWorkItem
-    { reservationId :: !Text
-    , hospitalId :: !Text
-    , commandId :: !Text
-    , lifeCriticalOverride :: !Bool
-    }
-    deriving stock (Eq, Show)
+  { reservationId :: !Text
+  , hospitalId :: !Text
+  , commandId :: !Text
+  , lifeCriticalOverride :: !Bool
+  }
+  deriving stock (Eq, Show)
 
 encodeReservationWorkItem :: ReservationWorkItem -> Value
 encodeReservationWorkItem p =
-    object
-        [ "reservation_id" .= p.reservationId
-        , "hospital_id" .= p.hospitalId
-        , "command_id" .= p.commandId
-        , "life_critical_override" .= p.lifeCriticalOverride
-        ]
+  object
+    [ "reservation_id" .= p.reservationId
+    , "hospital_id" .= p.hospitalId
+    , "command_id" .= p.commandId
+    , "life_critical_override" .= p.lifeCriticalOverride
+    ]
 
 parseReservationWorkItem :: Value -> Either Text ReservationWorkItem
 parseReservationWorkItem = mapLeftText . parseEither (withObject "ReservationWorkItem" go)
diff --git a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
--- a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
+++ b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
@@ -1,32 +1,29 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-
-{- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
-
-Deploy workers before producers when raising its schema version. Do not
-adopt this codec on a non-empty bare-payload queue without draining it
-(or supplying a transitional codec), or in-flight messages will
-dead-letter. This is telemetry-neutral:
-docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
-spans and acknowledgement vocabulary.
--}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+-- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
+--
+-- Deploy workers before producers when raising its schema version. Do not
+-- adopt this codec on a non-empty bare-payload queue without draining it
+-- (or supplying a transitional codec), or in-flight messages will
+-- dead-letter. This is telemetry-neutral:
+-- docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
+-- spans and acknowledgement vocabulary.
 module Generated.HospitalCapacity.Reservation_work.QueueCodec (reservationWorkPayloadCodec, reservationWorkJobCodec) where
 
 import Data.List.NonEmpty (NonEmpty (..))
-import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 import Keiro.Codec (Codec (..), EventType (..))
 import Keiro.PGMQ.Codec (JobCodec, keiroJobCodec)
+import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 
 reservationWorkPayloadCodec :: Codec ReservationWorkItem
 reservationWorkPayloadCodec =
-    Codec
-        { eventTypes = EventType "ReservationWorkItem" :| []
-        , eventType = \_ -> EventType "ReservationWorkItem"
-        , schemaVersion = 1
-        , encode = encodeReservationWorkItem
-        , decode = \_ -> parseReservationWorkItem
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ReservationWorkItem" :| []
+    , eventType = \_ -> EventType "ReservationWorkItem"
+    , schemaVersion = 1
+    , encode = encodeReservationWorkItem
+    , decode = \_ -> parseReservationWorkItem
+    , upcasters = []
+    }
 
 reservationWorkJobCodec :: JobCodec ReservationWorkItem
 reservationWorkJobCodec = keiroJobCodec reservationWorkPayloadCodec
diff --git a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
--- a/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
+++ b/test/conformance-dispatch-full/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
@@ -1,14 +1,10 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation_work.QueuePolicy (
-    retryPolicy,
-    jobOutcomeFor,
-    jobOrdering,
-    jobTuningFor,
-    queueProvision,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module Generated.HospitalCapacity.Reservation_work.QueuePolicy
+  ( ReservationWorkOutcome (..)
+  , retryPolicy, jobOutcomeFor
+  , jobOrdering, jobTuningFor, queueProvision
+  ) where
 
-import Data.Text (Text)
 import Keiro.PGMQ.Job (JobOrdering (..), JobOutcome (..), JobTuning, PartitionSpec (..), QueueProvision, RetryDelay (..), RetryPolicy (..), partitionedProvision, standardProvision, unloggedProvision, withFifoIndexProvision, withOrdering)
 
 jobOrdering :: JobOrdering
@@ -24,18 +20,24 @@
 
 retryPolicy :: RetryPolicy
 retryPolicy =
-    RetryPolicy
-        { maxRetries = 3
-        , defaultRetryDelay = RetryDelay 5
-        , useDeadLetter = True
-        }
+  RetryPolicy
+    { maxRetries = 3
+    , defaultRetryDelay = RetryDelay 5
+    , useDeadLetter = True
+    }
 
 -- The consumer JobOutcome disposition over the spec's named domain outcomes,
 -- lowered to the live Keiro.PGMQ.Job.JobOutcome.
-jobOutcomeFor :: Text -> JobOutcome
+data ReservationWorkOutcome
+  = StoreFailure
+  | CommandRejected
+  | DecodeFailure
+  | OnCodecReject
+  deriving stock (Eq, Show)
+
+jobOutcomeFor :: ReservationWorkOutcome -> JobOutcome
 jobOutcomeFor o = case o of
-    "storeFailure" -> Retry (RetryDelay 5)
-    "commandRejected" -> Dead "dead-lettered"
-    "decodeFailure" -> Dead "dead-lettered"
-    "onCodecReject" -> Dead "dead-lettered"
-    _ -> Retry (RetryDelay 5)
+  StoreFailure -> Retry (RetryDelay 5)
+  CommandRejected -> Dead "dead-lettered"
+  DecodeFailure -> Dead "dead-lettered"
+  OnCodecReject -> Dead "dead-lettered"
diff --git a/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals.hs b/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals.hs
--- a/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals.hs
+++ b/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals.hs
@@ -1,18 +1,21 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 3) from context id-domain-migration generated nominal declarations; do not edit.
 module Generated.IdDomainMigration.Nominals
-  ( OrderId
-  , parseOrderId
-  , mkOrderId
-  , orderIdText
-  ) where
+  ( OrderId,
+    parseOrderId,
+    mkOrderId,
+    orderIdText,
+  )
+where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
+import Generated.IdDomainMigration.Nominals.Internal (OrderId, mkOrderId, orderIdText, parseOrderId)
 import Keiki.Shape (CanonicalTypeName)
-import Generated.IdDomainMigration.Nominals.Internal (OrderId, mkOrderId, parseOrderId, orderIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 instance CanonicalTypeName OrderId
diff --git a/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals/Internal.hs b/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals/Internal.hs
--- a/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals/Internal.hs
+++ b/test/conformance-id-domain-migration/Generated/IdDomainMigration/Nominals/Internal.hs
@@ -1,12 +1,14 @@
 {-# LANGUAGE DeriveGeneric #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 3) from context id-domain-migration generated nominal ID internals; do not edit.
 module Generated.IdDomainMigration.Nominals.Internal
-  ( OrderId
-  , parseOrderId
-  , mkOrderId
-  , orderIdText
-  , unsafeOrderIdFromLegacyText
-  ) where
+  ( OrderId,
+    parseOrderId,
+    mkOrderId,
+    orderIdText,
+    unsafeOrderIdFromLegacyText,
+  )
+where
 
 import Data.Aeson (FromJSON (..), ToJSON (..), withText)
 import Data.Text (Text)
diff --git a/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Codec.hs b/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Codec.hs
--- a/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Codec.hs
+++ b/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Codec.hs
@@ -1,43 +1,41 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.IdDomainMigration.OrderBook.Codec (
-    orderBookCodec,
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 3) from aggregate OrderBook; do not edit.
+module Generated.IdDomainMigration.OrderBook.Codec
+  ( orderBookCodec,
     parseOrderBookEvent,
     encodeOrderBookEvent,
-) where
+  )
+where
 
-import Generated.IdDomainMigration.OrderBook.Domain
-import Generated.IdDomainMigration.Nominals (OrderId, orderIdText)
-import Generated.IdDomainMigration.Nominals.Internal (unsafeOrderIdFromLegacyText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import qualified Data.Text as T
+import Data.Text qualified as T
+import Generated.IdDomainMigration.Nominals (OrderId, orderIdText)
+import Generated.IdDomainMigration.Nominals.Internal (unsafeOrderIdFromLegacyText)
+import Generated.IdDomainMigration.OrderBook.Domain
 import Keiro.Codec (Codec (..), EventType (..))
 
-
-
-
-
 orderBookCodec :: Codec OrderBookEvent
 orderBookCodec =
   Codec
-    { eventTypes = EventType "OrderRecorded" :| []
-    , eventType = \case
-        OrderRecorded{} -> EventType "OrderRecorded"
-    , schemaVersion = 1
-    , encode = encodeOrderBookEvent
-    , decode = parseOrderBookEvent
-    , upcasters = []
+    { eventTypes = EventType "OrderRecorded" :| [],
+      eventType = \case
+        OrderRecorded {} -> EventType "OrderRecorded",
+      schemaVersion = 1,
+      encode = encodeOrderBookEvent,
+      decode = parseOrderBookEvent,
+      upcasters = []
     }
 
 encodeOrderBookEvent :: OrderBookEvent -> Value
 encodeOrderBookEvent = \case
   OrderRecorded payload ->
     object
-      [ "kind" .= ("OrderRecorded" :: Text)
-      , "orderId" .= orderIdText payload.orderId
+      [ "kind" .= ("OrderRecorded" :: Text),
+        "orderId" .= orderIdText payload.orderId
       ]
 
 parseOrderBookEvent :: EventType -> Value -> Either Text OrderBookEvent
@@ -46,8 +44,11 @@
     go o = do
       case tag of
         "OrderRecorded" ->
-          OrderRecorded <$> (OrderRecordedData <$> (unsafeOrderIdFromLegacyText <$> o .: "orderId"))
-        _ -> fail "unknown event type"
+          OrderRecorded
+            <$> ( OrderRecordedData
+                    <$> (unsafeOrderIdFromLegacyText <$> o .: "orderId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: OrderRecorded")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Domain.hs b/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Domain.hs
--- a/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Domain.hs
+++ b/test/conformance-id-domain-migration/Generated/IdDomainMigration/OrderBook/Domain.hs
@@ -3,23 +3,25 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 3) from aggregate OrderBook; do not edit.
 module Generated.IdDomainMigration.OrderBook.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
-import Keiki.Core (RegFile (..))
-import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
 import Generated.IdDomainMigration.Nominals (OrderId, parseOrderId)
+import Keiki.Core (RegFile (..))
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
+import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
 
 data OrderBookVertex = OrderBookEmpty | OrderBookRecorded
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
   deriving anyclass (ToJSON, FromJSON)
+
 instance CanonicalStateShape OrderBookVertex
+
 instance CanonicalTypeName OrderBookVertex
 
 data RecordData = RecordData
@@ -47,7 +49,5 @@
   RCons (Proxy @"orderId") (case parseOrderId "ord_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RNil
 
 $(deriveAggregateCtorsAll ''OrderBookCommand ''OrderBookRegs)
-
-
 
 $(deriveWireCtorsAll ''OrderBookEvent)
diff --git a/test/conformance-intake-full/Generated/HospitalCapacity/IncidentInbox/Inbox.hs b/test/conformance-intake-full/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
--- a/test/conformance-intake-full/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
+++ b/test/conformance-intake-full/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
@@ -1,38 +1,80 @@
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.IncidentInbox.Inbox (
-    InboxAck (..),
-    inboxDedupePolicy,
-    inboxPersistence,
-    inboxDisposition,
-) where
+{-# LANGUAGE OverloadedStrings #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from intake incidentInbox; do not edit.
+module Generated.HospitalCapacity.IncidentInbox.Inbox
+  ( InboxFailure (..)
+  , IncidentInboxOutcome (..)
+  , IncidentInboxDisposition (..)
+  , inboxDedupePolicy
+  , inboxPersistence
+  , inboxDispositionFor
+  , inboxDisposition
+  ) where
 
-import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..))
+import Data.Text (Text)
+import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..), RetryDelay (..))
 
 -- The dedupe policy (hole-kind 4), lowered to the live InboxDedupePolicy.
 inboxDedupePolicy :: InboxDedupePolicy
 inboxDedupePolicy = PreferIntegrationMessageId
 
-{- | Success-path envelope retention passed to runInboxTransactionWith.
-Failures always retain their full operator-facing dead-letter envelope.
-Dedupe-only success rows decode with an empty payload.
--}
+-- | Success-path envelope retention passed to runInboxTransactionWith.
+-- Failures always retain their full operator-facing dead-letter envelope.
+-- Dedupe-only success rows decode with an empty payload.
 inboxPersistence :: InboxPersistence
 inboxPersistence = PersistDedupeOnly
 
--- The service's ack decision for each inbox classification.
-data InboxAck = InboxAckOk | InboxRetry | InboxDeadLetter
-    deriving stock (Eq, Show)
+-- Runtime failure detail retained when the inbox wrapper reports a failed handler attempt.
+data InboxFailure = InboxFailure
+  { inboxFailureReason :: !Text
+  , inboxFailureAttempt :: !(Maybe Int)
+  }
+  deriving stock (Eq, Show)
 
--- The disposition table (hole-kind 2) over the LIVE Keiro.Inbox.Types.InboxResult.
--- duplicate => ackOk and previouslyFailed => deadLetter are the dangerous
--- inversions the spec states explicitly.
-inboxDisposition :: InboxResult a -> InboxAck
+-- Every classification named by the spec. Keeping this closed makes the
+-- generated table exhaustive and gives handler holes typed inputs.
+data IncidentInboxOutcome
+  = IncidentInboxProcessed
+  | IncidentInboxDuplicate
+  | IncidentInboxInProgress
+  | IncidentInboxPreviouslyFailed
+  | IncidentInboxDecodeFailed
+  | IncidentInboxDedupeFailed
+  | IncidentInboxStoreFailed
+  deriving stock (Eq, Show)
+
+-- The service's declared acknowledgement decision, including its details.
+data IncidentInboxDisposition
+  = InboxAccept
+  | InboxRetryAfter !RetryDelay !(Maybe InboxFailure)
+  | InboxDeadLetter !(Maybe Text) !(Maybe InboxFailure)
+  deriving stock (Eq, Show)
+
+-- The complete disposition table (hole-kind 2).
+inboxDispositionFor :: IncidentInboxOutcome -> IncidentInboxDisposition
+inboxDispositionFor outcome = case outcome of
+  IncidentInboxProcessed -> InboxAccept
+  IncidentInboxDuplicate -> InboxAccept
+  IncidentInboxInProgress -> InboxRetryAfter (RetryDelay 5) Nothing
+  IncidentInboxPreviouslyFailed -> InboxDeadLetter (Just "previous inbox failure") Nothing
+  IncidentInboxDecodeFailed -> InboxDeadLetter Nothing Nothing
+  IncidentInboxDedupeFailed -> InboxDeadLetter Nothing Nothing
+  IncidentInboxStoreFailed -> InboxRetryAfter (RetryDelay 5) Nothing
+
+-- Lower the LIVE Keiro.Inbox.Types.InboxResult without an open fallback.
+inboxDisposition :: InboxResult a -> IncidentInboxDisposition
 inboxDisposition r = case r of
-    InboxProcessed _ -> InboxAckOk
-    InboxDuplicate -> InboxAckOk
-    InboxInProgress -> InboxRetry
-    InboxPreviouslyFailed _ -> InboxDeadLetter
+  InboxProcessed _ -> inboxDispositionFor IncidentInboxProcessed
+  InboxDuplicate -> inboxDispositionFor IncidentInboxDuplicate
+  InboxInProgress -> inboxDispositionFor IncidentInboxInProgress
+  InboxPreviouslyFailed failureReason ->
+    maybe (inboxDispositionFor IncidentInboxPreviouslyFailed)
+      (\reason -> attachFailure (InboxFailure reason Nothing) (inboxDispositionFor IncidentInboxPreviouslyFailed))
+      failureReason
+  InboxHandlerFailed reason attempts ->
+    attachFailure (InboxFailure reason (Just attempts)) (inboxDispositionFor IncidentInboxStoreFailed)
 
--- handler-level failures (not InboxResult): decodeFailed => deadLetter, dedupeFailed => deadLetter, storeFailed => retry
+attachFailure :: InboxFailure -> IncidentInboxDisposition -> IncidentInboxDisposition
+attachFailure failure disposition = case disposition of
+  InboxRetryAfter delay _ -> InboxRetryAfter delay (Just failure)
+  InboxDeadLetter reason _ -> InboxDeadLetter reason (Just failure)
+  InboxAccept -> InboxAccept
diff --git a/test/conformance-intake-runtime/Generated/HospitalCapacity/IncidentInbox/Inbox.hs b/test/conformance-intake-runtime/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
--- a/test/conformance-intake-runtime/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
+++ b/test/conformance-intake-runtime/Generated/HospitalCapacity/IncidentInbox/Inbox.hs
@@ -1,38 +1,80 @@
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.IncidentInbox.Inbox (
-    InboxAck (..),
-    inboxDedupePolicy,
-    inboxPersistence,
-    inboxDisposition,
-) where
+{-# LANGUAGE OverloadedStrings #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from intake incidentInbox; do not edit.
+module Generated.HospitalCapacity.IncidentInbox.Inbox
+  ( InboxFailure (..)
+  , IncidentInboxOutcome (..)
+  , IncidentInboxDisposition (..)
+  , inboxDedupePolicy
+  , inboxPersistence
+  , inboxDispositionFor
+  , inboxDisposition
+  ) where
 
-import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..))
+import Data.Text (Text)
+import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..), RetryDelay (..))
 
 -- The dedupe policy (hole-kind 4), lowered to the live InboxDedupePolicy.
 inboxDedupePolicy :: InboxDedupePolicy
 inboxDedupePolicy = PreferIntegrationMessageId
 
-{- | Success-path envelope retention passed to runInboxTransactionWith.
-Failures always retain their full operator-facing dead-letter envelope.
-Dedupe-only success rows decode with an empty payload.
--}
+-- | Success-path envelope retention passed to runInboxTransactionWith.
+-- Failures always retain their full operator-facing dead-letter envelope.
+-- Dedupe-only success rows decode with an empty payload.
 inboxPersistence :: InboxPersistence
 inboxPersistence = PersistDedupeOnly
 
--- The service's ack decision for each inbox classification.
-data InboxAck = InboxAckOk | InboxRetry | InboxDeadLetter
-    deriving stock (Eq, Show)
+-- Runtime failure detail retained when the inbox wrapper reports a failed handler attempt.
+data InboxFailure = InboxFailure
+  { inboxFailureReason :: !Text
+  , inboxFailureAttempt :: !(Maybe Int)
+  }
+  deriving stock (Eq, Show)
 
--- The disposition table (hole-kind 2) over the LIVE Keiro.Inbox.Types.InboxResult.
--- duplicate => ackOk and previouslyFailed => deadLetter are the dangerous
--- inversions the spec states explicitly.
-inboxDisposition :: InboxResult a -> InboxAck
+-- Every classification named by the spec. Keeping this closed makes the
+-- generated table exhaustive and gives handler holes typed inputs.
+data IncidentInboxOutcome
+  = IncidentInboxProcessed
+  | IncidentInboxDuplicate
+  | IncidentInboxInProgress
+  | IncidentInboxPreviouslyFailed
+  | IncidentInboxDecodeFailed
+  | IncidentInboxDedupeFailed
+  | IncidentInboxStoreFailed
+  deriving stock (Eq, Show)
+
+-- The service's declared acknowledgement decision, including its details.
+data IncidentInboxDisposition
+  = InboxAccept
+  | InboxRetryAfter !RetryDelay !(Maybe InboxFailure)
+  | InboxDeadLetter !(Maybe Text) !(Maybe InboxFailure)
+  deriving stock (Eq, Show)
+
+-- The complete disposition table (hole-kind 2).
+inboxDispositionFor :: IncidentInboxOutcome -> IncidentInboxDisposition
+inboxDispositionFor outcome = case outcome of
+  IncidentInboxProcessed -> InboxAccept
+  IncidentInboxDuplicate -> InboxAccept
+  IncidentInboxInProgress -> InboxRetryAfter (RetryDelay 5) Nothing
+  IncidentInboxPreviouslyFailed -> InboxDeadLetter (Just "previous inbox failure") Nothing
+  IncidentInboxDecodeFailed -> InboxDeadLetter Nothing Nothing
+  IncidentInboxDedupeFailed -> InboxDeadLetter Nothing Nothing
+  IncidentInboxStoreFailed -> InboxRetryAfter (RetryDelay 5) Nothing
+
+-- Lower the LIVE Keiro.Inbox.Types.InboxResult without an open fallback.
+inboxDisposition :: InboxResult a -> IncidentInboxDisposition
 inboxDisposition r = case r of
-    InboxProcessed _ -> InboxAckOk
-    InboxDuplicate -> InboxAckOk
-    InboxInProgress -> InboxRetry
-    InboxPreviouslyFailed _ -> InboxDeadLetter
+  InboxProcessed _ -> inboxDispositionFor IncidentInboxProcessed
+  InboxDuplicate -> inboxDispositionFor IncidentInboxDuplicate
+  InboxInProgress -> inboxDispositionFor IncidentInboxInProgress
+  InboxPreviouslyFailed failureReason ->
+    maybe (inboxDispositionFor IncidentInboxPreviouslyFailed)
+      (\reason -> attachFailure (InboxFailure reason Nothing) (inboxDispositionFor IncidentInboxPreviouslyFailed))
+      failureReason
+  InboxHandlerFailed reason attempts ->
+    attachFailure (InboxFailure reason (Just attempts)) (inboxDispositionFor IncidentInboxStoreFailed)
 
--- handler-level failures (not InboxResult): decodeFailed => deadLetter, dedupeFailed => deadLetter, storeFailed => retry
+attachFailure :: InboxFailure -> IncidentInboxDisposition -> IncidentInboxDisposition
+attachFailure failure disposition = case disposition of
+  InboxRetryAfter delay _ -> InboxRetryAfter delay (Just failure)
+  InboxDeadLetter reason _ -> InboxDeadLetter reason (Just failure)
+  InboxAccept -> InboxAccept
diff --git a/test/conformance-intake-runtime/Main.hs b/test/conformance-intake-runtime/Main.hs
--- a/test/conformance-intake-runtime/Main.hs
+++ b/test/conformance-intake-runtime/Main.hs
@@ -1,26 +1,38 @@
-{- | EP-4 runtime conformance: the scaffolded intake @Inbox@ module's disposition
-wiring — the dedupe policy (a real @Keiro.Inbox.Types.InboxDedupePolicy@) and
-the disposition over the real @InboxResult@ — compiled against the LIVE keiro
-runtime. Running it pins the two dangerous inversions: a duplicate redelivery
-is ackOk (success), and a previously-failed delivery dead-letters (not retry).
--}
+-- | EP-4 runtime conformance: the scaffolded intake @Inbox@ module's disposition
+-- wiring — the dedupe policy (a real @Keiro.Inbox.Types.InboxDedupePolicy@) and
+-- the disposition over the real @InboxResult@ — compiled against the LIVE keiro
+-- runtime. Running it pins the two dangerous inversions: a duplicate redelivery
+-- is ackOk (success), and a previously-failed delivery dead-letters (not retry).
 module Main (main) where
 
 import Control.Monad (unless)
-import Generated.HospitalCapacity.IncidentInbox.Inbox (InboxAck (..), inboxDisposition, inboxPersistence)
-import Keiro.Inbox.Types (InboxPersistence (..), InboxResult (..))
+import Generated.HospitalCapacity.IncidentInbox.Inbox (InboxFailure (..), IncidentInboxDisposition (..), IncidentInboxOutcome (..), inboxDisposition, inboxDispositionFor, inboxPersistence)
+import Keiro.Inbox.Types (InboxPersistence (..), InboxResult (..), RetryDelay (..))
 import System.Exit (exitFailure)
 
 main :: IO ()
 main = do
-    let dupOk = inboxDisposition (InboxDuplicate :: InboxResult ()) == InboxAckOk
-        pfOk = inboxDisposition (InboxPreviouslyFailed Nothing :: InboxResult ()) == InboxDeadLetter
-        procOk = inboxDisposition (InboxProcessed () :: InboxResult ()) == InboxAckOk
-        ipOk = inboxDisposition (InboxInProgress :: InboxResult ()) == InboxRetry
-        persistenceOk = inboxPersistence == PersistDedupeOnly
-    putStrLn ("duplicate => ackOk (inversion 1): " <> show dupOk)
-    putStrLn ("previouslyFailed => deadLetter (inversion 2): " <> show pfOk)
-    putStrLn ("processed => ackOk: " <> show procOk)
-    putStrLn ("inProgress => retry: " <> show ipOk)
-    putStrLn ("success persistence => dedupe-only: " <> show persistenceOk)
-    unless (dupOk && pfOk && procOk && ipOk && persistenceOk) exitFailure
+  let dupOk = inboxDisposition (InboxDuplicate :: InboxResult ()) == InboxAccept
+      pfOk = inboxDisposition (InboxPreviouslyFailed Nothing :: InboxResult ()) == InboxDeadLetter (Just "previous inbox failure") Nothing
+      previousFailureDetailOk =
+        inboxDisposition (InboxPreviouslyFailed (Just "poison detail") :: InboxResult ())
+          == InboxDeadLetter (Just "previous inbox failure") (Just (InboxFailure "poison detail" Nothing))
+      procOk = inboxDisposition (InboxProcessed () :: InboxResult ()) == InboxAccept
+      ipOk = inboxDisposition (InboxInProgress :: InboxResult ()) == InboxRetryAfter (RetryDelay 5) Nothing
+      handlerFailureOk =
+        inboxDisposition (InboxHandlerFailed "database unavailable" 2 :: InboxResult ())
+          == InboxRetryAfter (RetryDelay 5) (Just (InboxFailure "database unavailable" (Just 2)))
+      completeTableOk =
+        inboxDispositionFor IncidentInboxDecodeFailed == InboxDeadLetter Nothing Nothing
+          && inboxDispositionFor IncidentInboxDedupeFailed == InboxDeadLetter Nothing Nothing
+          && inboxDispositionFor IncidentInboxStoreFailed == InboxRetryAfter (RetryDelay 5) Nothing
+      persistenceOk = inboxPersistence == PersistDedupeOnly
+  putStrLn ("duplicate => ackOk (inversion 1): " <> show dupOk)
+  putStrLn ("previouslyFailed => deadLetter (inversion 2): " <> show pfOk)
+  putStrLn ("previouslyFailed retains declared and runtime reasons: " <> show previousFailureDetailOk)
+  putStrLn ("processed => ackOk: " <> show procOk)
+  putStrLn ("inProgress => retry: " <> show ipOk)
+  putStrLn ("handler failure retains reason and attempt: " <> show handlerFailureOk)
+  putStrLn ("handler-level table is exhaustive and detailed: " <> show completeTableOk)
+  putStrLn ("success persistence => dedupe-only: " <> show persistenceOk)
+  unless (dupOk && pfOk && previousFailureDetailOk && procOk && ipOk && handlerFailureOk && completeTableOk && persistenceOk) exitFailure
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Codec.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Codec.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Codec.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Codec.hs
@@ -1,49 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.TransferRouting.Hospital.Codec (
     hospitalCodec,
     parseHospitalEvent,
     encodeHospitalEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.TransferRouting.Hospital.Domain
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.TransferRouting.Hospital.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 hospitalCodec :: Codec HospitalEvent
 hospitalCodec =
-    Codec
-        { eventTypes = EventType "AcceptedTransferNeedRouted" :| []
-        , eventType = \case
-            AcceptedTransferNeedRouted{} -> EventType "AcceptedTransferNeedRouted"
-        , schemaVersion = 1
-        , encode = encodeHospitalEvent
-        , decode = parseHospitalEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "AcceptedTransferNeedRouted" :| []
+    , eventType = \case
+        AcceptedTransferNeedRouted{} -> EventType "AcceptedTransferNeedRouted"
+    , schemaVersion = 1
+    , encode = encodeHospitalEvent
+    , decode = parseHospitalEvent
+    , upcasters = []
+    }
 
 encodeHospitalEvent :: HospitalEvent -> Value
 encodeHospitalEvent = \case
-    AcceptedTransferNeedRouted payload ->
-        object
-            [ "kind" .= ("AcceptedTransferNeedRouted" :: Text)
-            , "transferNeedId" .= payload.transferNeedId
-            , "hospitalId" .= payload.hospitalId
-            ]
+  AcceptedTransferNeedRouted payload ->
+    object
+      [ "kind" .= ("AcceptedTransferNeedRouted" :: Text)
+      , "transferNeedId" .= payload.transferNeedId
+      , "hospitalId" .= payload.hospitalId
+      ]
 
 parseHospitalEvent :: EventType -> Value -> Either Text HospitalEvent
 parseHospitalEvent (EventType tag) = mapLeftText . parseEither (withObject "HospitalEvent" go)
   where
     go o = do
-        case tag of
-            "AcceptedTransferNeedRouted" ->
-                AcceptedTransferNeedRouted <$> (AcceptedTransferNeedRoutedData <$> o .: "transferNeedId" <*> o .: "hospitalId")
-            _ -> fail "unknown event type"
+      case tag of
+        "AcceptedTransferNeedRouted" ->
+          AcceptedTransferNeedRouted
+            <$> ( AcceptedTransferNeedRoutedData
+                    <$> o .: "transferNeedId"
+                    <*> o .: "hospitalId"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: AcceptedTransferNeedRouted")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Domain.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Domain.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Domain.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Domain.hs
@@ -2,9 +2,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.TransferRouting.Hospital.Domain where
 
 import Data.Proxy (Proxy (..))
@@ -14,33 +12,35 @@
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data HospitalVertex = HospitalAccepting
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
 data RouteAcceptedTransferNeedData = RouteAcceptedTransferNeedData
-    { transferNeedId :: !Text
-    , hospitalId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { transferNeedId :: !Text
+  , hospitalId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data HospitalCommand = RouteAcceptedTransferNeed !RouteAcceptedTransferNeedData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data AcceptedTransferNeedRoutedData = AcceptedTransferNeedRoutedData
-    { transferNeedId :: !Text
-    , hospitalId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { transferNeedId :: !Text
+  , hospitalId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data HospitalEvent = AcceptedTransferNeedRouted !AcceptedTransferNeedRoutedData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 type HospitalRegs =
-    '[]
+  '[]
 
 initialHospitalRegs :: RegFile HospitalRegs
 initialHospitalRegs =
-    RNil
+  RNil
 
 $(deriveAggregateCtorsAll ''HospitalCommand ''HospitalRegs)
+
+
 
 $(deriveWireCtorsAll ''HospitalEvent)
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/EventStream.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/EventStream.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital/EventStream.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.TransferRouting.Hospital.EventStream (
-    hospitalCategory,
-    hospitalEventStream,
-    hospitalEventStreamDef,
-    HospitalEventStream,
-    HospitalEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module Generated.TransferRouting.Hospital.EventStream
+  ( hospitalCategory
+  , hospitalCommandCategory
+  , hospitalEventStream
+  , hospitalEventStreamDef
+  , HospitalEventStream
+  , HospitalEventStreamDef
+  ) where
 
-import Generated.TransferRouting.Hospital.Codec (hospitalCodec)
 import Generated.TransferRouting.Hospital.Domain
+import Generated.TransferRouting.Hospital.Codec (hospitalCodec)
+import Generated.TransferRouting.Hospital.Transducer (hospitalFoldFingerprint, hospitalTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import TransferRouting.Hospital.Holes (hospitalTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-hospitalCategory :: Stream.StreamCategory a
+hospitalCategory :: Stream.StreamCategory HospitalEventStreamDef
 hospitalCategory = Stream.categoryUnsafe "hospital"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+hospitalCommandCategory :: Stream.StreamCategory HospitalCommand
+hospitalCommandCategory = Stream.categoryUnsafe "hospital"
+
 type HospitalEventStreamDef =
-    EventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
+  EventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
 
 type HospitalEventStream =
-    ValidatedEventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
+  ValidatedEventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
 
 hospitalEventStreamDef :: HospitalEventStreamDef
 hospitalEventStreamDef =
-    EventStream
-        { transducer = hospitalTransducer
-        , initialState = HospitalAccepting
-        , initialRegisters = initialHospitalRegs
-        , eventCodec = hospitalCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = hospitalTransducer,
+      initialState = HospitalAccepting,
+      initialRegisters = initialHospitalRegs,
+      eventCodec = hospitalCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 hospitalEventStream :: HospitalEventStream
 hospitalEventStream =
-    mkEventStreamOrThrow "Hospital" hospitalEventStreamDef
+  mkEventStreamOrThrow "Hospital" hospitalEventStreamDef
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Harness.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Harness.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Harness.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Harness.hs
@@ -1,27 +1,25 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.TransferRouting.Hospital.Harness (harnessAssertions) where
 
-import Generated.TransferRouting.Hospital.Codec (encodeHospitalEvent, hospitalCodec, parseHospitalEvent)
 import Generated.TransferRouting.Hospital.Domain
+import Generated.TransferRouting.Hospital.Codec (encodeHospitalEvent, parseHospitalEvent, hospitalCodec)
+import Generated.TransferRouting.Hospital.Transducer (hospitalTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer)
 import Keiro.Codec (eventType)
-import TransferRouting.Hospital.Holes (hospitalTransducer)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions hospitalTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: AcceptedTransferNeedRouted", roundTrips sampleEventAcceptedTransferNeedRouted)
-    , ("accepts RouteAcceptedTransferNeed from HospitalAccepting", acceptRouteAcceptedTransferNeed)
-    ]
-        ++ forwardReplayRouteAcceptedTransferNeed
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions hospitalTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: AcceptedTransferNeedRouted", roundTrips sampleEventAcceptedTransferNeedRouted)
+  , ("accepts RouteAcceptedTransferNeed from HospitalAccepting", acceptRouteAcceptedTransferNeed)
+  ]
+  ++ forwardReplayRouteAcceptedTransferNeed
 
 roundTrips :: HospitalEvent -> Bool
 roundTrips e = parseHospitalEvent (eventType hospitalCodec e) (encodeHospitalEvent e) == Right e
@@ -31,24 +29,24 @@
 
 acceptRouteAcceptedTransferNeed :: Bool
 acceptRouteAcceptedTransferNeed =
-    case step hospitalTransducer (HospitalAccepting, initialHospitalRegs) ((RouteAcceptedTransferNeed (RouteAcceptedTransferNeedData "sample-transferNeedId" "sample-hospitalId"))) of
-        Just (v, _, _) -> v == HospitalAccepting
-        Nothing -> False
+  case step hospitalTransducer (HospitalAccepting, initialHospitalRegs) ((RouteAcceptedTransferNeed (RouteAcceptedTransferNeedData "sample-transferNeedId" "sample-hospitalId"))) of
+    Just (v, _, _) -> v == HospitalAccepting
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRouteAcceptedTransferNeed :: [(String, Bool)]
 forwardReplayRouteAcceptedTransferNeed =
-    case step hospitalTransducer (HospitalAccepting, initialHospitalRegs) ((RouteAcceptedTransferNeed (RouteAcceptedTransferNeedData "sample-transferNeedId" "sample-hospitalId"))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, _forwardRegs, emitted) ->
-            case mapM (\event -> parseHospitalEvent (eventType hospitalCodec event) (encodeHospitalEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither hospitalTransducer (HospitalAccepting, initialHospitalRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, _replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            ]
+  case step hospitalTransducer (HospitalAccepting, initialHospitalRegs) ((RouteAcceptedTransferNeed (RouteAcceptedTransferNeedData "sample-transferNeedId" "sample-hospitalId"))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, _forwardRegs, emitted) ->
+      case mapM (\event -> parseHospitalEvent (eventType hospitalCodec event) (encodeHospitalEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither hospitalTransducer (HospitalAccepting, initialHospitalRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, _replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              ]
   where
     prefix = "forward/replay equality: RouteAcceptedTransferNeed from HospitalAccepting -- "
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Projection.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Projection.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Projection.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.TransferRouting.Hospital.Projection () where
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital/Transducer.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital/Transducer.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module Generated.TransferRouting.Hospital.Transducer
+  ( hospitalTransducer
+  , hospitalFoldFingerprint
+  , BehaviorOwnership (..)
+  , hospitalPredicateVerifications
+  ) where
+
+import Generated.TransferRouting.Hospital.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+hospitalTransducer
+  :: SymTransducer
+       (HsPred HospitalRegs HospitalCommand)
+       HospitalRegs
+       HospitalVertex
+       HospitalCommand
+       HospitalEvent
+hospitalTransducer =
+  B.buildTransducer HospitalAccepting initialHospitalRegs isTerminal do
+    B.from HospitalAccepting do
+      B.onCmd inCtorRouteAcceptedTransferNeed $ \d -> B.do
+        B.emit wireAcceptedTransferNeedRouted (AcceptedTransferNeedRoutedTermFields
+          { transferNeedId = d.transferNeedId
+          , hospitalId = d.hospitalId
+          })
+        B.goto HospitalAccepting
+ where
+  isTerminal = \case
+
+    _ -> False
+
+hospitalFoldFingerprint :: Text
+hospitalFoldFingerprint = "a5fa22bc0f5337c57d80b5e1c43bb860"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+hospitalPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+hospitalPredicateVerifications = sequence
+  [ verifyTransition "transition1AcceptingRouteAcceptedTransferNeed" GeneratedOwned HospitalAccepting 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut hospitalTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/Router.hs b/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/Router.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/Router.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/Router.hs
@@ -1,9 +1,8 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.TransferRouting.HospitalTransferRouter.Router (
-    hospitalTransferRouterName,
-    hospitalTransferRouterWorkerOptions,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router HospitalTransferRouter; do not edit.
+module Generated.TransferRouting.HospitalTransferRouter.Router
+  ( hospitalTransferRouterName
+  , hospitalTransferRouterWorkerOptions
+  ) where
 
 import Data.Text (Text)
 import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
@@ -21,9 +20,9 @@
 -- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions.
 hospitalTransferRouterWorkerOptions :: WorkerOptions es msg
 hospitalTransferRouterWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedDeadLetter
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedDeadLetter,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
diff --git a/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/RouterHarness.hs b/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/RouterHarness.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/RouterHarness.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/HospitalTransferRouter/RouterHarness.hs
@@ -1,16 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router HospitalTransferRouter; do not edit.
 module Generated.TransferRouting.HospitalTransferRouter.RouterHarness (routerHarnessValues) where
 
 routerHarnessValues :: [(String, String)]
 routerHarnessValues =
-    [ ("routerName", "hospital-transfer-router")
-    , ("keyField", "transferNeedId")
-    , ("resolveSource", "read-model hospital_load")
-    , ("resolveRow", "hospitalId")
-    , ("dispatchCommand", "RouteAcceptedTransferNeed")
-    , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
-    , ("onDuplicate", "AckOk")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "deadLetter")
-    , ("poisonPolicy", "halt")
-    ]
+  [ ("routerName", "hospital-transfer-router")
+  , ("keyField", "transferNeedId")
+  , ("resolveSource", "read-model hospital_load")
+  , ("resolveRow", "hospitalId")
+  , ("dispatchCommand", "RouteAcceptedTransferNeed")
+  , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
+  , ("onDuplicate", "AckOk")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "deadLetter")
+  , ("poisonPolicy", "halt")
+  ]
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModel.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModel.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModel.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModel.hs
@@ -1,62 +1,61 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.TransferRouting.Hospital_load.ReadModel (
-    hospitalLoadReadModel,
-    hospitalLoadQualifiedTable,
-    registerHospitalLoad,
-    startHospitalLoadRebuild,
-    finishHospitalLoadRebuild,
-    abandonHospitalLoadRebuild,
-    hospitalLoadAsyncProjection,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel hospital_load; do not edit.
+module Generated.TransferRouting.Hospital_load.ReadModel
+  ( hospitalLoadReadModel
+  , hospitalLoadQualifiedTable
+  , registerHospitalLoad
+  , startHospitalLoadRebuild
+  , finishHospitalLoadRebuild
+  , abandonHospitalLoadRebuild
+  , hospitalLoadAsyncProjection
+  ) where
 
 import Data.Functor (void)
 import Effectful (Eff, (:>))
 import Generated.TransferRouting.Hospital_load.ReadModelTable (hospitalLoadQualifiedTable)
+import TransferRouting.Hospital_load.ReadModelHoles (HospitalLoadQueryInput, HospitalLoadQueryResult, hospitalLoadQuery, applyHospitalLoad)
 import Keiro.Projection (AsyncProjection (..))
 import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)
 import Keiro.ReadModel.Rebuild qualified as Rebuild
 import Kiroku.Store.Effect (Store)
 import Kiroku.Store.Types (GlobalPosition, RecordedEvent (..))
-import TransferRouting.Hospital_load.ReadModelHoles (HospitalLoadQueryInput, HospitalLoadQueryResult, applyHospitalLoad, hospitalLoadQuery)
 
 hospitalLoadReadModel :: ReadModel HospitalLoadQueryInput HospitalLoadQueryResult
 hospitalLoadReadModel =
-    ReadModel
-        { name = "transfer-routing-hospital-load"
-        , tableName = "hospital_load"
-        , schema = "hospital_transfer"
-        , subscriptionName = "transfer-routing-hospital-load-sub"
-        , version = 1
-        , shapeHash = "fnv1a:977395d28f254ddb"
-        , defaultConsistency = Eventual
-        , strongScope = EntireLog
-        , query = hospitalLoadQuery
-        }
+  ReadModel
+    { name = "transfer-routing-hospital-load"
+    , tableName = "hospital_load"
+    , schema = "hospital_transfer"
+    , subscriptionName = "transfer-routing-hospital-load-sub"
+    , version = 1
+    , shapeHash = "fnv1a:977395d28f254ddb"
+    , defaultConsistency = Eventual
+    , strongScope = EntireLog
+    , query = hospitalLoadQuery
+    }
 
 -- Call once at projection startup before serving queries.
 registerHospitalLoad :: (Store :> es) => Eff es ()
 registerHospitalLoad =
-    void (registerReadModel "transfer-routing-hospital-load" 1 "fnv1a:977395d28f254ddb")
+  void (registerReadModel "transfer-routing-hospital-load" 1 "fnv1a:977395d28f254ddb")
 
 startHospitalLoadRebuild :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata
 startHospitalLoadRebuild =
-    Rebuild.startRebuild hospitalLoadReadModel ["transfer-routing-hospital-load-async"]
+  Rebuild.startRebuild hospitalLoadReadModel ["transfer-routing-hospital-load-async"]
 
 finishHospitalLoadRebuild :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)
 finishHospitalLoadRebuild =
-    Rebuild.finishRebuild hospitalLoadReadModel ["transfer-routing-hospital-load-async"]
+  Rebuild.finishRebuild hospitalLoadReadModel ["transfer-routing-hospital-load-async"]
 
 abandonHospitalLoadRebuild :: (Store :> es) => Eff es ReadModelMetadata
 abandonHospitalLoadRebuild = Rebuild.abandonRebuild hospitalLoadReadModel
 
 hospitalLoadAsyncProjection :: AsyncProjection
 hospitalLoadAsyncProjection =
-    AsyncProjection
-        { name = "transfer-routing-hospital-load-async"
-        , readModelName = "transfer-routing-hospital-load"
-        , subscriptionName = "transfer-routing-hospital-load-sub"
-        , applyRecorded = applyHospitalLoad
-        , idempotencyKey = \recorded -> recorded.eventId
-        }
+  AsyncProjection
+    { name = "transfer-routing-hospital-load-async"
+    , readModelName = "transfer-routing-hospital-load"
+    , subscriptionName = "transfer-routing-hospital-load-sub"
+    , applyRecorded = applyHospitalLoad
+    , idempotencyKey = \recorded -> recorded.eventId
+    }
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelHarness.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelHarness.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelHarness.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelHarness.hs
@@ -1,19 +1,19 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel hospital_load; do not edit.
 module Generated.TransferRouting.Hospital_load.ReadModelHarness (readModelFacts, runReadModelFacts) where
 
 -- | (fact, expected from notation, actual shared derivation/lowering).
 readModelFacts :: [(String, String, String)]
 readModelFacts =
-    [ ("registryName", "transfer-routing-hospital-load", "transfer-routing-hospital-load")
-    , ("subscriptionName", "transfer-routing-hospital-load-sub", "transfer-routing-hospital-load-sub")
-    , ("shapeHash", "fnv1a:977395d28f254ddb", "fnv1a:977395d28f254ddb")
-    , ("asyncProjectionName", "transfer-routing-hospital-load-async", "transfer-routing-hospital-load-async")
-    , ("consistency", "Eventual", "Eventual")
-    , ("strongScope", "EntireLog", "EntireLog")
-    ]
+  [ ("registryName", "transfer-routing-hospital-load", "transfer-routing-hospital-load")
+  , ("subscriptionName", "transfer-routing-hospital-load-sub", "transfer-routing-hospital-load-sub")
+  , ("shapeHash", "fnv1a:977395d28f254ddb", "fnv1a:977395d28f254ddb")
+  , ("asyncProjectionName", "transfer-routing-hospital-load-async", "transfer-routing-hospital-load-async")
+  , ("consistency", "Eventual", "Eventual")
+  , ("strongScope", "EntireLog", "EntireLog")
+  ]
 
 runReadModelFacts :: IO Bool
 runReadModelFacts = do
-    let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
-    mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
-    pure (null failures)
+  let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
+  mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
+  pure (null failures)
diff --git a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelTable.hs b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelTable.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelTable.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/Hospital_load/ReadModelTable.hs
@@ -1,5 +1,4 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel hospital_load; do not edit.
 module Generated.TransferRouting.Hospital_load.ReadModelTable (hospitalLoadQualifiedTable) where
 
 import Data.Text (Text)
diff --git a/test/conformance-newsurface/Generated/TransferRouting/ReplayAudit.hs b/test/conformance-newsurface/Generated/TransferRouting/ReplayAudit.hs
--- a/test/conformance-newsurface/Generated/TransferRouting/ReplayAudit.hs
+++ b/test/conformance-newsurface/Generated/TransferRouting/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context transfer-routing replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Hospital.hospitalEventStream
-            , category = Stream.categoryText Hospital.hospitalCategory
-            , mkStream = streamInCategory (Stream.categoryText Hospital.hospitalCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Hospital.hospitalEventStream
+        , category = Stream.categoryText Hospital.hospitalCategory
+        , mkStream = streamInCategory (Stream.categoryText Hospital.hospitalCategory)
+        }
+  ]
diff --git a/test/conformance-newsurface/Main.hs b/test/conformance-newsurface/Main.hs
--- a/test/conformance-newsurface/Main.hs
+++ b/test/conformance-newsurface/Main.hs
@@ -5,7 +5,7 @@
 import Control.Monad (unless)
 import Effectful (runPureEff)
 import Generated.TransferRouting.Hospital.Domain qualified as Hospital
-import Generated.TransferRouting.Hospital.EventStream (hospitalCategory, hospitalEventStream)
+import Generated.TransferRouting.Hospital.EventStream (hospitalCommandCategory, hospitalEventStream)
 import Generated.TransferRouting.Hospital.Harness (harnessAssertions)
 import Generated.TransferRouting.HospitalTransferRouter.RouterHarness (routerHarnessValues)
 import Generated.TransferRouting.Hospital_load.ReadModelHarness (runReadModelFacts)
@@ -43,7 +43,7 @@
                        )
                    ,
                        ( "resolver targets the selected hospital aggregate"
-                       , map target commands == [entityStream hospitalCategory "north-general"]
+                       , map target commands == [entityStream hospitalCommandCategory "north-general"]
                        )
                    ,
                        ( "resolver dispatches the accepted need"
diff --git a/test/conformance-newsurface/TransferRouting/HospitalTransferRouter/RouterValue.hs b/test/conformance-newsurface/TransferRouting/HospitalTransferRouter/RouterValue.hs
--- a/test/conformance-newsurface/TransferRouting/HospitalTransferRouter/RouterValue.hs
+++ b/test/conformance-newsurface/TransferRouting/HospitalTransferRouter/RouterValue.hs
@@ -12,7 +12,7 @@
 import Effectful (Eff)
 import Generated.TransferRouting.Hospital.Domain qualified as Hospital
 import Generated.TransferRouting.Hospital.EventStream (
-    hospitalCategory,
+    hospitalCommandCategory,
     hospitalEventStream,
  )
 import Generated.TransferRouting.HospitalTransferRouter.Router (hospitalTransferRouterName)
@@ -50,7 +50,7 @@
     rows <- resolveHospitalLoad input
     pure
         [ PMCommand
-            { target = entityStream hospitalCategory row.hospitalId
+            { target = entityStream hospitalCommandCategory row.hospitalId
             , command =
                 Hospital.RouteAcceptedTransferNeed
                     (Hospital.RouteAcceptedTransferNeedData input.transferNeedId row.hospitalId)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/Nominal/Shape/OrderStatus.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/Nominal/Shape/OrderStatus.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/Nominal/Shape/OrderStatus.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/Nominal/Shape/OrderStatus.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from bound nominal enum representation OrderStatus; do not edit.
 module Generated.NominalScalars.Nominal.Shape.OrderStatus (OrderStatusRepresentation (..), orderStatusRepresentationText) where
 
 import Data.Text (Text)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/BehaviorContract.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/BehaviorContract.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/BehaviorContract.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/BehaviorContract.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.BehaviorContract where
 
 import Generated.NominalScalars.NominalLedger.Codec (encodeNominalLedgerEvent, parseNominalLedgerEvent, nominalLedgerCodec)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Codec.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Codec.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Codec.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Codec.hs
@@ -2,7 +2,7 @@
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.Codec (
     nominalLedgerCodec,
     parseNominalLedgerEvent,
@@ -10,12 +10,13 @@
 ) where
 
 import Generated.NominalScalars.NominalLedger.Domain
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
 import Data.KindID qualified as KindID
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
 import Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)
 import Keiro.Codec (Codec (..), EventType (..))
 
@@ -31,15 +32,17 @@
 
 
 parseOrderIdNominal :: Text -> Parser NominalConformance.Domain.OrderId
-parseOrderIdNominal input = case KindID.parseText @"ord" input of
+parseOrderIdNominal input = case validateIdDomainText (typeIdV7Domain "ord") input of
   Left reason -> fail (show reason)
-  Right representation -> pure (nominalFromRepresentation NominalConformance.Bindings.orderIdBinding representation)
+  Right () -> case KindID.parseText @"ord" input of
+    Left reason -> fail (show reason)
+    Right representation -> pure (nominalFromRepresentation NominalConformance.Bindings.orderIdBinding representation)
 
 parseOrderStatusNominal :: Text -> Parser NominalConformance.Domain.OrderStatus
 parseOrderStatusNominal = \case
   "draft" -> pure (nominalFromRepresentation NominalConformance.Bindings.orderStatusBinding Generated.NominalScalars.Nominal.Shape.OrderStatus.Draft)
   "submitted" -> pure (nominalFromRepresentation NominalConformance.Bindings.orderStatusBinding Generated.NominalScalars.Nominal.Shape.OrderStatus.Submitted)
-  _ -> fail "unknown OrderStatus wire value"
+  tag -> fail ("unknown OrderStatus wire value " <> show tag <> "; expected one of: draft, submitted")
 
 
 
@@ -77,8 +80,17 @@
     go o = do
       case tag of
         "NominalsRecorded" ->
-          NominalsRecorded <$> (NominalsRecordedData <$> (o .: "orderId" >>= parseOrderIdNominal) <*> (o .: "status" >>= parseOrderStatusNominal) <*> (nominalFromRepresentation NominalConformance.Bindings.accountNumberBinding <$> o .: "accountNumber") <*> (nominalFromRepresentation NominalConformance.Bindings.riskScoreBinding <$> o .: "riskScore") <*> (nominalFromRepresentation NominalConformance.Bindings.sequenceNumberBinding <$> o .: "sequenceNumber") <*> (nominalFromRepresentation NominalConformance.Bindings.featureFlagBinding <$> o .: "featureFlag") <*> (nominalFromRepresentation NominalConformance.Bindings.observedAtBinding <$> o .: "observedAt"))
-        _ -> fail "unknown event type"
+          NominalsRecorded
+            <$> ( NominalsRecordedData
+                    <$> explicitParseField (withText "OrderId" parseOrderIdNominal) o "orderId"
+                    <*> explicitParseField (withText "OrderStatus" parseOrderStatusNominal) o "status"
+                    <*> (nominalFromRepresentation NominalConformance.Bindings.accountNumberBinding <$> o .: "accountNumber")
+                    <*> (nominalFromRepresentation NominalConformance.Bindings.riskScoreBinding <$> o .: "riskScore")
+                    <*> (nominalFromRepresentation NominalConformance.Bindings.sequenceNumberBinding <$> o .: "sequenceNumber")
+                    <*> (nominalFromRepresentation NominalConformance.Bindings.featureFlagBinding <$> o .: "featureFlag")
+                    <*> (nominalFromRepresentation NominalConformance.Bindings.observedAtBinding <$> o .: "observedAt")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: NominalsRecorded")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Domain.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Domain.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Domain.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Domain.hs
@@ -3,8 +3,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/EventStream.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/EventStream.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/EventStream.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.EventStream
   ( nominalLedgerCategory
+  , nominalLedgerCommandCategory
   , nominalLedgerEventStream
   , nominalLedgerEventStreamDef
   , NominalLedgerEventStream
@@ -21,9 +22,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-nominalLedgerCategory :: Stream.StreamCategory a
+nominalLedgerCategory :: Stream.StreamCategory NominalLedgerEventStreamDef
 nominalLedgerCategory = Stream.categoryUnsafe "nominalLedger"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+nominalLedgerCommandCategory :: Stream.StreamCategory NominalLedgerCommand
+nominalLedgerCommandCategory = Stream.categoryUnsafe "nominalLedger"
+
 type NominalLedgerEventStreamDef =
   EventStream (HsPred NominalLedgerRegs NominalLedgerCommand) NominalLedgerRegs NominalLedgerVertex NominalLedgerCommand NominalLedgerEvent
 
@@ -33,21 +38,21 @@
 nominalLedgerEventStreamDef :: NominalLedgerEventStreamDef
 nominalLedgerEventStreamDef =
   EventStream
-    { transducer = nominalLedgerTransducer
-    , initialState = NominalLedgerEmpty
-    , initialRegisters = initialNominalLedgerRegs
-    , eventCodec = nominalLedgerCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Every 1
-    -- The snapshot discriminator composes: the spec's state-codec version (bump it
-    -- in the spec's `state-codec version=` clause), keiki's register and
-    -- control-state shape hashes, and this fold fingerprint derived from the
-    -- spec's transition surface (guards, writes, emits, states, register
-    -- initials, referenced rules). Spec-visible fold changes invalidate old
-    -- snapshots automatically. Version-2 Hole-owned transitions additionally
-    -- compose their explicit hand-owned FoldVersion tokens here; bump the
-    -- corresponding token whenever that Hole behavior changes.
-    , stateCodec = Just (withFoldFingerprint nominalLedgerFoldFingerprint (defaultStateCodec 1))
+    { transducer = nominalLedgerTransducer,
+      initialState = NominalLedgerEmpty,
+      initialRegisters = initialNominalLedgerRegs,
+      eventCodec = nominalLedgerCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Every 1,
+      -- The snapshot discriminator composes: the spec's state-codec version (bump it
+      -- in the spec's `state-codec version=` clause), keiki's register and
+      -- control-state shape hashes, and this fold fingerprint derived from the
+      -- spec's transition surface (guards, writes, emits, states, register
+      -- initials, referenced rules). Spec-visible fold changes invalidate old
+      -- snapshots automatically. Version-2 Hole-owned transitions additionally
+      -- compose their explicit hand-owned FoldVersion tokens here; bump the
+      -- corresponding token whenever that Hole behavior changes.
+      stateCodec = Just (withFoldFingerprint nominalLedgerFoldFingerprint (defaultStateCodec 1))
     }
 
 nominalLedgerSnapshotFixture :: (Int, Text)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Expressions.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Expressions.hs
deleted file mode 100644
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Expressions.hs
+++ /dev/null
@@ -1,47 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.NominalScalars.NominalLedger.Expressions
-  ( transition1EmptyRecordNominalsGuard
-  , transition1EmptyRecordNominalsWriteOrderId
-  , transition1EmptyRecordNominalsWriteStatus
-  , transition1EmptyRecordNominalsWriteAccountNumber
-  , transition1EmptyRecordNominalsWriteRiskScore
-  , transition1EmptyRecordNominalsWriteSequenceNumber
-  , transition1EmptyRecordNominalsWriteFeatureFlag
-  , transition1EmptyRecordNominalsWriteObservedAt
-  ) where
-
-import Generated.NominalScalars.NominalLedger.Domain
-import Keiki.Builder qualified as B
-import Keiki.Core qualified as K
-import Keiki.Generics (RegFieldsOf)
-import Data.Text (Text)
-import Generated.NominalScalars.NominalProjections qualified as NominalProjections
-import NominalConformance.Domain qualified
-
-transition1EmptyRecordNominalsGuard :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.HsPred NominalLedgerRegs NominalLedgerCommand
-transition1EmptyRecordNominalsGuard d = K.PAnd (K.PEq (K.inpProj NominalProjections.orderIdEqualityWitness inCtorRecordNominals (#orderId :: K.Index (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderId)) (K.regProj NominalProjections.orderIdEqualityWitness (#orderId :: K.Index NominalLedgerRegs NominalConformance.Domain.OrderId))) (K.PEq (K.inpProj NominalProjections.orderStatusEqualityWitness inCtorRecordNominals (#status :: K.Index (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderStatus)) (K.regProj NominalProjections.orderStatusEqualityWitness (#status :: K.Index NominalLedgerRegs NominalConformance.Domain.OrderStatus)))
-
-transition1EmptyRecordNominalsWriteOrderId :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderId
-transition1EmptyRecordNominalsWriteOrderId d = d.orderId
-
-transition1EmptyRecordNominalsWriteStatus :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderStatus
-transition1EmptyRecordNominalsWriteStatus d = d.status
-
-transition1EmptyRecordNominalsWriteAccountNumber :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.AccountNumber
-transition1EmptyRecordNominalsWriteAccountNumber d = d.accountNumber
-
-transition1EmptyRecordNominalsWriteRiskScore :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.RiskScore
-transition1EmptyRecordNominalsWriteRiskScore d = d.riskScore
-
-transition1EmptyRecordNominalsWriteSequenceNumber :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.SequenceNumber
-transition1EmptyRecordNominalsWriteSequenceNumber d = d.sequenceNumber
-
-transition1EmptyRecordNominalsWriteFeatureFlag :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.FeatureFlag
-transition1EmptyRecordNominalsWriteFeatureFlag d = d.featureFlag
-
-transition1EmptyRecordNominalsWriteObservedAt :: B.PayloadProj NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) -> K.Term NominalLedgerRegs NominalLedgerCommand (RegFieldsOf RecordNominalsData) NominalConformance.Domain.ObservedAt
-transition1EmptyRecordNominalsWriteObservedAt d = d.observedAt
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Harness.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Harness.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Harness.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Harness.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.Harness (harnessAssertions) where
 
 import Generated.NominalScalars.NominalLedger.Domain
@@ -10,13 +10,15 @@
 import Keiro.Codec (eventType)
 import Data.List.NonEmpty qualified as NonEmpty
 import Keiro.Codec.Nominal (nominalDomainRoundTrip, nominalFixtureCases, nominalFixtureDomain, nominalRepresentationRoundTrip, nominalToRepresentation)
+import Data.KindID qualified as KindID
+import Data.Text qualified as T
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
 import NominalConformance.Bindings qualified
 import Generated.NominalScalars.NominalProjections qualified as NominalProjections
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions nominalLedgerTransducer))
@@ -77,6 +79,10 @@
   , ("nominal projection agreement: ObservedAt", all (\fixture -> fieldWitnessAgrees NominalProjections.observedAtWitness (nominalToRepresentation NominalConformance.Bindings.observedAtBinding) (nominalFixtureDomain fixture)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.observedAtFixtures)))
   , ("nominal domain law: OrderId", all (\fixture -> nominalDomainRoundTrip NominalConformance.Bindings.orderIdBinding (nominalFixtureDomain fixture)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderIdFixtures)))
   , ("nominal representation law: OrderId", all (\fixture -> let domainValue = nominalFixtureDomain fixture in nominalRepresentationRoundTrip NominalConformance.Bindings.orderIdBinding (nominalToRepresentation NominalConformance.Bindings.orderIdBinding domainValue)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderIdFixtures)))
+  , ("nominal ID projection agreement: OrderId", all (\fixture -> fieldWitnessAgrees NominalProjections.orderIdEqualityWitness (KindID.toText . nominalToRepresentation NominalConformance.Bindings.orderIdBinding) (nominalFixtureDomain fixture)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderIdFixtures)))
+  , ("nominal ID fixture domain agreement: OrderId", all (\fixture -> case validateIdDomainText (typeIdV7Domain "ord") (KindID.toText (nominalToRepresentation NominalConformance.Bindings.orderIdBinding (nominalFixtureDomain fixture))) of Right () -> True; Left _ -> False) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderIdFixtures)))
+  , ("nominal ID binding preserves canonical representations: OrderId", all (nominalRepresentationRoundTrip NominalConformance.Bindings.orderIdBinding) [(case KindID.parseText @"ord" "ord_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated canonical ID conformance probe failed to parse"), (case KindID.parseText @"ord" "ord_01h455vb4pex5vsknk084sn02r" of Right parsed -> parsed; Left _ -> error "generated canonical ID conformance probe failed to parse")])
+  , ("nominal ID boundary rejects wrong-prefix and normalized text: OrderId", case (validateIdDomainText (typeIdV7Domain "ord") "wrong_01h455vb4pex5vsknk084sn02q", validateIdDomainText (typeIdV7Domain "ord") (T.toUpper "ord_01h455vb4pex5vsknk084sn02q")) of (Left _, Left _) -> True; _ -> False)
   , ("nominal domain law: OrderStatus", all (\fixture -> nominalDomainRoundTrip NominalConformance.Bindings.orderStatusBinding (nominalFixtureDomain fixture)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderStatusFixtures)))
   , ("nominal representation law: OrderStatus", all (\fixture -> let domainValue = nominalFixtureDomain fixture in nominalRepresentationRoundTrip NominalConformance.Bindings.orderStatusBinding (nominalToRepresentation NominalConformance.Bindings.orderStatusBinding domainValue)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.orderStatusFixtures)))
   , ("nominal domain law: RiskScore", all (\fixture -> nominalDomainRoundTrip NominalConformance.Bindings.riskScoreBinding (nominalFixtureDomain fixture)) (NonEmpty.toList (nominalFixtureCases NominalConformance.Bindings.riskScoreFixtures)))
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Projection.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Projection.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Projection.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.Projection () where
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Transducer.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Transducer.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Transducer.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalLedger/Transducer.hs
@@ -2,9 +2,10 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate NominalLedger; do not edit.
 module Generated.NominalScalars.NominalLedger.Transducer
   ( nominalLedgerTransducer
   , nominalLedgerFoldFingerprint
@@ -14,12 +15,14 @@
 
 import Generated.NominalScalars.NominalLedger.Domain
 import Data.Text (Text)
+import Generated.NominalScalars.NominalProjections qualified as NominalProjections
+import NominalConformance.Domain qualified
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core (HsPred, SymTransducer, (.==), (.&&))
 import Keiki.Core qualified as K
 import Keiki.Symbolic qualified as S
-import Generated.NominalScalars.NominalLedger.Expressions qualified as Expressions
 import Keiki.Builder ((=:))
+import Keiki.Generics (RegFieldsOf)
 
 nominalLedgerTransducer
   :: SymTransducer
@@ -32,14 +35,20 @@
   B.buildTransducer NominalLedgerEmpty initialNominalLedgerRegs isTerminal do
     B.from NominalLedgerEmpty do
       B.onCmd inCtorRecordNominals $ \d -> B.do
-        B.requireGuard (Expressions.transition1EmptyRecordNominalsGuard d)
-        B.slot @"orderId" =: Expressions.transition1EmptyRecordNominalsWriteOrderId d
-        B.slot @"status" =: Expressions.transition1EmptyRecordNominalsWriteStatus d
-        B.slot @"accountNumber" =: Expressions.transition1EmptyRecordNominalsWriteAccountNumber d
-        B.slot @"riskScore" =: Expressions.transition1EmptyRecordNominalsWriteRiskScore d
-        B.slot @"sequenceNumber" =: Expressions.transition1EmptyRecordNominalsWriteSequenceNumber d
-        B.slot @"featureFlag" =: Expressions.transition1EmptyRecordNominalsWriteFeatureFlag d
-        B.slot @"observedAt" =: Expressions.transition1EmptyRecordNominalsWriteObservedAt d
+        let commandOrderId = K.inpProj NominalProjections.orderIdEqualityWitness inCtorRecordNominals (#orderId :: K.Index (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderId)
+            registerOrderId = K.regProj NominalProjections.orderIdEqualityWitness (#orderId :: K.Index NominalLedgerRegs NominalConformance.Domain.OrderId)
+            commandStatus = K.inpProj NominalProjections.orderStatusEqualityWitness inCtorRecordNominals (#status :: K.Index (RegFieldsOf RecordNominalsData) NominalConformance.Domain.OrderStatus)
+            registerStatus = K.regProj NominalProjections.orderStatusEqualityWitness (#status :: K.Index NominalLedgerRegs NominalConformance.Domain.OrderStatus)
+        B.requireGuard $
+          commandOrderId .== registerOrderId
+          .&& commandStatus .== registerStatus
+        B.slot @"orderId" =: d.orderId
+        B.slot @"status" =: d.status
+        B.slot @"accountNumber" =: d.accountNumber
+        B.slot @"riskScore" =: d.riskScore
+        B.slot @"sequenceNumber" =: d.sequenceNumber
+        B.slot @"featureFlag" =: d.featureFlag
+        B.slot @"observedAt" =: d.observedAt
         B.emit wireNominalsRecorded (NominalsRecordedTermFields
           { orderId = d.orderId
           , status = d.status
@@ -56,7 +65,7 @@
     _ -> False
 
 nominalLedgerFoldFingerprint :: Text
-nominalLedgerFoldFingerprint = "d452ae7d73a3bf7f"
+nominalLedgerFoldFingerprint = "757ae5b82b33565e6c0c7fe2af1b527c"
 
 data BehaviorOwnership = GeneratedOwned | HoleOwned
   deriving stock (Eq, Show)
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalProjections.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalProjections.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/NominalProjections.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/NominalProjections.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context nominal-scalars nominal scalar projection facade; do not edit.
 module Generated.NominalScalars.NominalProjections where
 
 import Data.KindID qualified as KindID
@@ -11,6 +11,7 @@
 import Generated.NominalScalars.Nominal.Shape.OrderStatus qualified
 import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
 import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, matchesTextPattern, textCharSet, textConcat, textLiteral, textProjectionDomain, textRepeatBetween)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain, validateIdDomainText)
 import Keiro.Codec.Nominal (nominalFromRepresentation, nominalToRepresentation)
 import NominalConformance.Bindings qualified
 import NominalConformance.Domain qualified
@@ -53,12 +54,7 @@
 observedAtWitness = fieldWitness @ObservedAtNominalProjection
 
 orderIdEqualityPattern :: TextPattern
-orderIdEqualityPattern = either (error . show) id $ do
-  prefix <- textLiteral "ord_"
-  leading <- textCharSet ('0' :| "1234567")
-  crockford <- textCharSet ('0' :| "123456789abcdefghjkmnpqrstvwxyz")
-  suffix <- textRepeatBetween 25 25 crockford
-  pure (textConcat (prefix :| [leading, suffix]))
+orderIdEqualityPattern = either (error . show) id (idDomainTextPattern (typeIdV7Domain "ord"))
 
 data OrderIdEqualityProjection
 
@@ -66,12 +62,13 @@
   type FieldName OrderIdEqualityProjection = "OrderId"
   type FieldOwner OrderIdEqualityProjection = NominalConformance.Domain.OrderId
   type FieldResult OrderIdEqualityProjection = Text
-  fieldShapeId _ = "nominal-equality|name=OrderId|contract=keiro-dsl/nominal-equality/1|key=Text|domain=typeid-text:ord|owner=consumer;canonical=nominal.OrderId.v1;binding=NominalConformance.Bindings.orderIdBinding;binding-version=1"
+  fieldShapeId _ = "nominal-equality|name=OrderId|contract=keiro-dsl/nominal-equality/2|key=Text|domain=typeid-v7-text:ord:keiro-dsl/id-domain/typeid-v7/1|owner=consumer;canonical=nominal.OrderId.v1;binding=NominalConformance.Bindings.orderIdBinding;binding-version=1"
   projectFieldValue _ = KindID.toText . nominalToRepresentation NominalConformance.Bindings.orderIdBinding
 
 instance ExactFieldProjection OrderIdEqualityProjection where
   fieldProjectionDomain _ = textProjectionDomain orderIdEqualityPattern
   reconstructFieldOwner _ value
+    | Left _ <- validateIdDomainText (typeIdV7Domain "ord") value = Nothing
     | not (matchesTextPattern orderIdEqualityPattern value) = Nothing
     | otherwise = case KindID.parseText @"ord" value of
         Left _ -> Nothing
diff --git a/test/conformance-nominal-scalars/Generated/NominalScalars/ReplayAudit.hs b/test/conformance-nominal-scalars/Generated/NominalScalars/ReplayAudit.hs
--- a/test/conformance-nominal-scalars/Generated/NominalScalars/ReplayAudit.hs
+++ b/test/conformance-nominal-scalars/Generated/NominalScalars/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context nominal-scalars replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-nominal-scalars/Main.hs b/test/conformance-nominal-scalars/Main.hs
--- a/test/conformance-nominal-scalars/Main.hs
+++ b/test/conformance-nominal-scalars/Main.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
@@ -15,6 +14,7 @@
 import qualified Data.List.NonEmpty as NE
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
+import Data.Text qualified as T
 import qualified Generated.NominalScalars.Nominal.Shape.OrderStatus as Representation
 import Generated.NominalScalars.NominalLedger.Codec
 import Generated.NominalScalars.NominalLedger.Domain
@@ -120,7 +120,7 @@
     toJSON (nominalToRepresentation Bindings.riskScoreBinding (RiskScore 7)) == toJSON (8 :: Int)
 
 oneDirectionIdLaw :: Bool
-oneDirectionIdLaw = case KindID.parseText @"ord" "ord_00041061050r3gg28a1c60t3ge" of
+oneDirectionIdLaw = case KindID.parseText @"ord" "ord_01h455vb4pex5vsknk084sn02r" of
     Left _ -> False
     Right changed ->
         nominalDomainRoundTrip
@@ -195,7 +195,10 @@
 malformedIdRejected = rejects (replace "orderId" (String "not-a-typeid") expectedEventJson)
 
 unknownEnumRejected :: Bool
-unknownEnumRejected = rejects (replace "status" (String "retired") expectedEventJson)
+unknownEnumRejected =
+    case parseNominalLedgerEvent (EventType "NominalsRecorded") (replace "status" (String "retired") expectedEventJson) of
+        Left problem -> all (`T.isInfixOf` problem) ["$.status", "retired", "draft", "submitted"]
+        Right _ -> False
 
 rejects :: Value -> Bool
 rejects value = case parseNominalLedgerEvent (EventType "NominalsRecorded") value of
@@ -345,7 +348,7 @@
     setStatus value (RecordNominals command) = RecordNominals command{status = value}
 
 alternateOrderId :: OrderId
-alternateOrderId = case KindID.parseText @"ord" "ord_00041061050r3gg28a1c60t3ge" of
+alternateOrderId = case KindID.parseText @"ord" "ord_01h455vb4pex5vsknk084sn02r" of
     Left reason -> error ("invalid alternate conformance TypeID: " <> show reason)
     Right value -> OrderId value
 
diff --git a/test/conformance-nominal-scalars/NominalConformance/Bindings.hs b/test/conformance-nominal-scalars/NominalConformance/Bindings.hs
--- a/test/conformance-nominal-scalars/NominalConformance/Bindings.hs
+++ b/test/conformance-nominal-scalars/NominalConformance/Bindings.hs
@@ -46,7 +46,7 @@
 observedAtBinding = NominalBinding unObservedAt ObservedAt
 
 validOrderIdText :: Text
-validOrderIdText = "ord_00041061050r3gg28a1c60t3gf"
+validOrderIdText = "ord_01h455vb4pex5vsknk084sn02q"
 
 sampleOrderId :: OrderId
 sampleOrderId = case KindID.parseText @"ord" validOrderIdText of
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Codec.hs b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Codec.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Codec.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Codec.hs
@@ -1,48 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.SurgeDemo.Hospital.Codec (
     hospitalCodec,
     parseHospitalEvent,
     encodeHospitalEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.SurgeDemo.Hospital.Domain
+import Generated.SurgeDemo.Nominals (HospitalId, hospitalIdText)
+import Generated.SurgeDemo.Nominals.Internal (unsafeHospitalIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.SurgeDemo.Hospital.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 hospitalCodec :: Codec HospitalEvent
 hospitalCodec =
-    Codec
-        { eventTypes = EventType "SurgeActivated" :| []
-        , eventType = \case
-            SurgeActivated{} -> EventType "SurgeActivated"
-        , schemaVersion = 1
-        , encode = encodeHospitalEvent
-        , decode = parseHospitalEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "SurgeActivated" :| []
+    , eventType = \case
+        SurgeActivated{} -> EventType "SurgeActivated"
+    , schemaVersion = 1
+    , encode = encodeHospitalEvent
+    , decode = parseHospitalEvent
+    , upcasters = []
+    }
 
 encodeHospitalEvent :: HospitalEvent -> Value
 encodeHospitalEvent = \case
-    SurgeActivated payload ->
-        object
-            [ "kind" .= ("SurgeActivated" :: Text)
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            ]
+  SurgeActivated payload ->
+    object
+      [ "kind" .= ("SurgeActivated" :: Text)
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      ]
 
 parseHospitalEvent :: EventType -> Value -> Either Text HospitalEvent
 parseHospitalEvent (EventType tag) = mapLeftText . parseEither (withObject "HospitalEvent" go)
   where
     go o = do
-        case tag of
-            "SurgeActivated" ->
-                SurgeActivated <$> (SurgeActivatedData <$> (HospitalId <$> o .: "hospitalId"))
-            _ -> fail "unknown event type"
+      case tag of
+        "SurgeActivated" ->
+          SurgeActivated
+            <$> ( SurgeActivatedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SurgeActivated")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Domain.hs b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Domain.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Domain.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Domain.hs
@@ -2,22 +2,16 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module Generated.SurgeDemo.Hospital.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
+import Generated.SurgeDemo.Nominals (HospitalId, parseHospitalId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
-newtype HospitalId = HospitalId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId t) = t
-
 data HospitalVertex = HospitalIdle | HospitalSurging
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
@@ -38,12 +32,11 @@
   deriving stock (Generic, Eq, Show)
 
 type HospitalRegs =
-  '[ '("hospitalState", HospitalVertex)
-   ]
+  '[]
 
 initialHospitalRegs :: RegFile HospitalRegs
 initialHospitalRegs =
-  RCons (Proxy @"hospitalState") HospitalIdle RNil
+  RNil
 
 $(deriveAggregateCtorsAll ''HospitalCommand ''HospitalRegs)
 
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Hospital/EventStream.hs b/test/conformance-process-full/Generated/SurgeDemo/Hospital/EventStream.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Hospital/EventStream.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Hospital/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.SurgeDemo.Hospital.EventStream (
-    hospitalCategory,
-    hospitalEventStream,
-    hospitalEventStreamDef,
-    HospitalEventStream,
-    HospitalEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module Generated.SurgeDemo.Hospital.EventStream
+  ( hospitalCategory
+  , hospitalCommandCategory
+  , hospitalEventStream
+  , hospitalEventStreamDef
+  , HospitalEventStream
+  , HospitalEventStreamDef
+  ) where
 
-import Generated.SurgeDemo.Hospital.Codec (hospitalCodec)
 import Generated.SurgeDemo.Hospital.Domain
+import Generated.SurgeDemo.Hospital.Codec (hospitalCodec)
+import Generated.SurgeDemo.Hospital.Transducer (hospitalFoldFingerprint, hospitalTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import SurgeDemo.Hospital.Holes (hospitalTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-hospitalCategory :: Stream.StreamCategory a
+hospitalCategory :: Stream.StreamCategory HospitalEventStreamDef
 hospitalCategory = Stream.categoryUnsafe "hospital"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+hospitalCommandCategory :: Stream.StreamCategory HospitalCommand
+hospitalCommandCategory = Stream.categoryUnsafe "hospital"
+
 type HospitalEventStreamDef =
-    EventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
+  EventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
 
 type HospitalEventStream =
-    ValidatedEventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
+  ValidatedEventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
 
 hospitalEventStreamDef :: HospitalEventStreamDef
 hospitalEventStreamDef =
-    EventStream
-        { transducer = hospitalTransducer
-        , initialState = HospitalIdle
-        , initialRegisters = initialHospitalRegs
-        , eventCodec = hospitalCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = hospitalTransducer,
+      initialState = HospitalIdle,
+      initialRegisters = initialHospitalRegs,
+      eventCodec = hospitalCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 hospitalEventStream :: HospitalEventStream
 hospitalEventStream =
-    mkEventStreamOrThrow "Hospital" hospitalEventStreamDef
+  mkEventStreamOrThrow "Hospital" hospitalEventStreamDef
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Projection.hs b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Projection.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Projection.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Projection.hs
@@ -1,15 +1,14 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.SurgeDemo.Hospital.Projection (
-    hospitalProjection,
-    hospitalStatusFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module Generated.SurgeDemo.Hospital.Projection
+  ( hospitalProjection
+  , hospitalStatusFor
+  ) where
 
-import Data.Text (Text)
 import Generated.SurgeDemo.Hospital.Domain
-import Keiro.Projection (InlineProjection (..))
 import SurgeDemo.Hospital.Holes (applyHospital)
+import Data.Text (Text)
+import Keiro.Projection (InlineProjection (..))
 
 -- The deterministic event->status mapping (hole-kind 3, /mapping/), derived
 -- from the spec's status-map. The read-model SQL that consumes it lives in
@@ -17,11 +16,11 @@
 -- WARNING: no readmodel node declares 'hospital'; unqualified SQL depends on search_path.
 hospitalStatusFor :: HospitalEvent -> Maybe Text
 hospitalStatusFor = \case
-    SurgeActivated{} -> Just "surging"
+  SurgeActivated {} -> Just "surging"
 
 hospitalProjection :: InlineProjection HospitalEvent
 hospitalProjection =
-    InlineProjection
-        { name = "surge-demo-hospital-inline"
-        , apply = applyHospital
-        }
+  InlineProjection
+    { name = "surge-demo-hospital-inline"
+    , apply = applyHospital
+    }
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Hospital/Transducer.hs b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-full/Generated/SurgeDemo/Hospital/Transducer.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module Generated.SurgeDemo.Hospital.Transducer
+  ( hospitalTransducer
+  , hospitalFoldFingerprint
+  , BehaviorOwnership (..)
+  , hospitalPredicateVerifications
+  ) where
+
+import Generated.SurgeDemo.Hospital.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+hospitalTransducer
+  :: SymTransducer
+       (HsPred HospitalRegs HospitalCommand)
+       HospitalRegs
+       HospitalVertex
+       HospitalCommand
+       HospitalEvent
+hospitalTransducer =
+  B.buildTransducer HospitalIdle initialHospitalRegs isTerminal do
+    B.from HospitalIdle do
+      B.onCmd inCtorActivateSurge $ \d -> B.do
+        B.emit wireSurgeActivated (SurgeActivatedTermFields
+          { hospitalId = d.hospitalId
+          })
+        B.goto HospitalSurging
+ where
+  isTerminal = \case
+    HospitalSurging -> True
+    _ -> False
+
+hospitalFoldFingerprint :: Text
+hospitalFoldFingerprint = "91309fbeb315f8bdf69502624990c044"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+hospitalPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+hospitalPredicateVerifications = sequence
+  [ verifyTransition "transition1IdleActivateSurge" GeneratedOwned HospitalIdle 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut hospitalTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Nominals.hs b/test/conformance-process-full/Generated/SurgeDemo/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-full/Generated/SurgeDemo/Nominals.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context surge-demo generated nominal declarations; do not edit.
+module Generated.SurgeDemo.Nominals
+  ( HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.SurgeDemo.Nominals.Internal (HospitalId, mkHospitalId, parseHospitalId, hospitalIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+instance CanonicalTypeName HospitalId
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Nominals/Internal.hs b/test/conformance-process-full/Generated/SurgeDemo/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-full/Generated/SurgeDemo/Nominals/Internal.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context surge-demo generated nominal ID internals; do not edit.
+module Generated.SurgeDemo.Nominals.Internal
+  ( HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
diff --git a/test/conformance-process-full/Generated/SurgeDemo/ReplayAudit.hs b/test/conformance-process-full/Generated/SurgeDemo/ReplayAudit.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/ReplayAudit.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context surge-demo replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -10,23 +9,23 @@
 --   * any non-zero audit exit blocks deployment.
 module Generated.SurgeDemo.ReplayAudit (auditTargets) where
 
-import Generated.SurgeDemo.Hospital.EventStream qualified as Hospital
 import Generated.SurgeDemo.Surge.EventStream qualified as Surge
+import Generated.SurgeDemo.Hospital.EventStream qualified as Hospital
 import Keiro.ReplayAudit (AuditTarget (..), SomeAuditTarget (..), streamInCategory)
 import Keiro.Stream qualified as Stream
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Hospital.hospitalEventStream
-            , category = Stream.categoryText Hospital.hospitalCategory
-            , mkStream = streamInCategory (Stream.categoryText Hospital.hospitalCategory)
-            }
-    , SomeAuditTarget
-        AuditTarget
-            { eventStream = Surge.surgeEventStream
-            , category = Stream.categoryText Surge.surgeCategory
-            , mkStream = streamInCategory (Stream.categoryText Surge.surgeCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Surge.surgeEventStream
+        , category = Stream.categoryText Surge.surgeCategory
+        , mkStream = streamInCategory (Stream.categoryText Surge.surgeCategory)
+        }
+  , SomeAuditTarget
+      AuditTarget
+        { eventStream = Hospital.hospitalEventStream
+        , category = Stream.categoryText Hospital.hospitalCategory
+        , mkStream = streamInCategory (Stream.categoryText Hospital.hospitalCategory)
+        }
+  ]
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Surge/Codec.hs b/test/conformance-process-full/Generated/SurgeDemo/Surge/Codec.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Surge/Codec.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Surge/Codec.hs
@@ -1,56 +1,67 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module Generated.SurgeDemo.Surge.Codec (
     surgeCodec,
     parseSurgeEvent,
     encodeSurgeEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.SurgeDemo.Surge.Domain
+import Generated.SurgeDemo.Nominals (HospitalId, hospitalIdText)
+import Generated.SurgeDemo.Nominals.Internal (unsafeHospitalIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.SurgeDemo.Surge.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 surgeCodec :: Codec SurgeEvent
 surgeCodec =
-    Codec
-        { eventTypes = EventType "SurgeThresholdNoted" :| [EventType "SurgeTimerFired"]
-        , eventType = \case
-            SurgeThresholdNoted{} -> EventType "SurgeThresholdNoted"
-            SurgeTimerFired{} -> EventType "SurgeTimerFired"
-        , schemaVersion = 1
-        , encode = encodeSurgeEvent
-        , decode = parseSurgeEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "SurgeThresholdNoted" :| [EventType "SurgeTimerFired"]
+    , eventType = \case
+        SurgeThresholdNoted{} -> EventType "SurgeThresholdNoted"
+        SurgeTimerFired{} -> EventType "SurgeTimerFired"
+    , schemaVersion = 1
+    , encode = encodeSurgeEvent
+    , decode = parseSurgeEvent
+    , upcasters = []
+    }
 
 encodeSurgeEvent :: SurgeEvent -> Value
 encodeSurgeEvent = \case
-    SurgeThresholdNoted payload ->
-        object
-            [ "kind" .= ("SurgeThresholdNoted" :: Text)
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            ]
-    SurgeTimerFired payload ->
-        object
-            [ "kind" .= ("SurgeTimerFired" :: Text)
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            ]
+  SurgeThresholdNoted payload ->
+    object
+      [ "kind" .= ("SurgeThresholdNoted" :: Text)
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      ]
+  SurgeTimerFired payload ->
+    object
+      [ "kind" .= ("SurgeTimerFired" :: Text)
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      ]
 
 parseSurgeEvent :: EventType -> Value -> Either Text SurgeEvent
 parseSurgeEvent (EventType tag) = mapLeftText . parseEither (withObject "SurgeEvent" go)
   where
     go o = do
-        case tag of
-            "SurgeThresholdNoted" ->
-                SurgeThresholdNoted <$> (SurgeThresholdNotedData <$> (HospitalId <$> o .: "hospitalId"))
-            "SurgeTimerFired" ->
-                SurgeTimerFired <$> (SurgeTimerFiredData <$> (HospitalId <$> o .: "hospitalId"))
-            _ -> fail "unknown event type"
+      case tag of
+        "SurgeThresholdNoted" ->
+          SurgeThresholdNoted
+            <$> ( SurgeThresholdNotedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                )
+        "SurgeTimerFired" ->
+          SurgeTimerFired
+            <$> ( SurgeTimerFiredData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SurgeThresholdNoted, SurgeTimerFired")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Surge/Domain.hs b/test/conformance-process-full/Generated/SurgeDemo/Surge/Domain.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Surge/Domain.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Surge/Domain.hs
@@ -2,22 +2,16 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module Generated.SurgeDemo.Surge.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
+import Generated.SurgeDemo.Nominals (HospitalId, parseHospitalId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
-newtype HospitalId = HospitalId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId t) = t
-
 data SurgeVertex = SurgeWatching | SurgeNoted | SurgeFired
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
@@ -50,12 +44,11 @@
   deriving stock (Generic, Eq, Show)
 
 type SurgeRegs =
-  '[ '("surgeState", SurgeVertex)
-   ]
+  '[]
 
 initialSurgeRegs :: RegFile SurgeRegs
 initialSurgeRegs =
-  RCons (Proxy @"surgeState") SurgeWatching RNil
+  RNil
 
 $(deriveAggregateCtorsAll ''SurgeCommand ''SurgeRegs)
 
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Surge/EventStream.hs b/test/conformance-process-full/Generated/SurgeDemo/Surge/EventStream.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Surge/EventStream.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Surge/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.SurgeDemo.Surge.EventStream (
-    surgeCategory,
-    surgeEventStream,
-    surgeEventStreamDef,
-    SurgeEventStream,
-    SurgeEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.SurgeDemo.Surge.EventStream
+  ( surgeCategory
+  , surgeCommandCategory
+  , surgeEventStream
+  , surgeEventStreamDef
+  , SurgeEventStream
+  , SurgeEventStreamDef
+  ) where
 
-import Generated.SurgeDemo.Surge.Codec (surgeCodec)
 import Generated.SurgeDemo.Surge.Domain
+import Generated.SurgeDemo.Surge.Codec (surgeCodec)
+import Generated.SurgeDemo.Surge.Transducer (surgeFoldFingerprint, surgeTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import SurgeDemo.Surge.Holes (surgeTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-surgeCategory :: Stream.StreamCategory a
+surgeCategory :: Stream.StreamCategory SurgeEventStreamDef
 surgeCategory = Stream.categoryUnsafe "surge"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+surgeCommandCategory :: Stream.StreamCategory SurgeCommand
+surgeCommandCategory = Stream.categoryUnsafe "surge"
+
 type SurgeEventStreamDef =
-    EventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
+  EventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
 
 type SurgeEventStream =
-    ValidatedEventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
+  ValidatedEventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
 
 surgeEventStreamDef :: SurgeEventStreamDef
 surgeEventStreamDef =
-    EventStream
-        { transducer = surgeTransducer
-        , initialState = SurgeWatching
-        , initialRegisters = initialSurgeRegs
-        , eventCodec = surgeCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = surgeTransducer,
+      initialState = SurgeWatching,
+      initialRegisters = initialSurgeRegs,
+      eventCodec = surgeCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 surgeEventStream :: SurgeEventStream
 surgeEventStream =
-    mkEventStreamOrThrow "Surge" surgeEventStreamDef
+  mkEventStreamOrThrow "Surge" surgeEventStreamDef
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Surge/Projection.hs b/test/conformance-process-full/Generated/SurgeDemo/Surge/Projection.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/Surge/Projection.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/Surge/Projection.hs
@@ -1,15 +1,14 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.SurgeDemo.Surge.Projection (
-    surgeProjection,
-    surgeStatusFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.SurgeDemo.Surge.Projection
+  ( surgeProjection
+  , surgeStatusFor
+  ) where
 
-import Data.Text (Text)
 import Generated.SurgeDemo.Surge.Domain
-import Keiro.Projection (InlineProjection (..))
 import SurgeDemo.Surge.Holes (applySurge)
+import Data.Text (Text)
+import Keiro.Projection (InlineProjection (..))
 
 -- The deterministic event->status mapping (hole-kind 3, /mapping/), derived
 -- from the spec's status-map. The read-model SQL that consumes it lives in
@@ -17,12 +16,12 @@
 -- WARNING: no readmodel node declares 'surge'; unqualified SQL depends on search_path.
 surgeStatusFor :: SurgeEvent -> Maybe Text
 surgeStatusFor = \case
-    SurgeThresholdNoted{} -> Just "noted"
-    SurgeTimerFired{} -> Just "fired"
+  SurgeThresholdNoted {} -> Just "noted"
+  SurgeTimerFired {} -> Just "fired"
 
 surgeProjection :: InlineProjection SurgeEvent
 surgeProjection =
-    InlineProjection
-        { name = "surge-demo-surge-inline"
-        , apply = applySurge
-        }
+  InlineProjection
+    { name = "surge-demo-surge-inline"
+    , apply = applySurge
+    }
diff --git a/test/conformance-process-full/Generated/SurgeDemo/Surge/Transducer.hs b/test/conformance-process-full/Generated/SurgeDemo/Surge/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-full/Generated/SurgeDemo/Surge/Transducer.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.SurgeDemo.Surge.Transducer
+  ( surgeTransducer
+  , surgeFoldFingerprint
+  , BehaviorOwnership (..)
+  , surgePredicateVerifications
+  ) where
+
+import Generated.SurgeDemo.Surge.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+surgeTransducer
+  :: SymTransducer
+       (HsPred SurgeRegs SurgeCommand)
+       SurgeRegs
+       SurgeVertex
+       SurgeCommand
+       SurgeEvent
+surgeTransducer =
+  B.buildTransducer SurgeWatching initialSurgeRegs isTerminal do
+    B.from SurgeWatching do
+      B.onCmd inCtorNoteSurgeThreshold $ \d -> B.do
+        B.emit wireSurgeThresholdNoted (SurgeThresholdNotedTermFields
+          { hospitalId = d.hospitalId
+          })
+        B.goto SurgeNoted
+    B.from SurgeNoted do
+      B.onCmd inCtorMarkSurgeTimerFired $ \d -> B.do
+        B.emit wireSurgeTimerFired (SurgeTimerFiredTermFields
+          { hospitalId = d.hospitalId
+          })
+        B.goto SurgeFired
+ where
+  isTerminal = \case
+    SurgeFired -> True
+    _ -> False
+
+surgeFoldFingerprint :: Text
+surgeFoldFingerprint = "df50137adf2682fb4ac2fc216ab74fd2"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+surgePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+surgePredicateVerifications = sequence
+  [ verifyTransition "transition1WatchingNoteSurgeThreshold" GeneratedOwned SurgeWatching 0
+  , verifyTransition "transition2NotedMarkSurgeTimerFired" GeneratedOwned SurgeNoted 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut surgeTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-process-full/Generated/SurgeDemo/SurgeFlow/Process.hs b/test/conformance-process-full/Generated/SurgeDemo/SurgeFlow/Process.hs
--- a/test/conformance-process-full/Generated/SurgeDemo/SurgeFlow/Process.hs
+++ b/test/conformance-process-full/Generated/SurgeDemo/SurgeFlow/Process.hs
@@ -1,23 +1,23 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.SurgeDemo.SurgeFlow.Process (
-    surgeFlowProcessName,
-    surgeFlowCategory,
-    surgeFlowProcessWorkerOptions,
-    surgeFlowTimerRequest,
-    surgeFlowFireOutcome,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from process SurgeFlow; do not edit.
+module Generated.SurgeDemo.SurgeFlow.Process
+  ( surgeFlowProcessName
+  , surgeFlowCategory
+  , surgeFlowProcessWorkerOptions
+  , surgeFlowTimerRequest
+  , surgeFlowFireOutcome
+  ) where
 
 import Data.Aeson (Value, object, (.=))
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 import Data.Time (UTCTime)
 import Data.UUID (UUID)
-import Data.UUID.V5 qualified as UUID.V5
+import qualified Data.UUID.V5 as UUID.V5
+import Generated.SurgeDemo.Surge.EventStream (SurgeEventStreamDef)
 import Keiro.Command (CommandError (..))
-import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
 import Keiro.Stream qualified as Stream
 import Keiro.Timer (TimerId (..), TimerRequest (..))
+import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
 import Shibuya.Core.Ack (RetryDelay (..))
 
 -- The define-once ProcessManager name (hole-kind 5: referenced, never retyped).
@@ -27,32 +27,32 @@
 -- The validated saga stream category (hole-kind 5: referenced, never retyped).
 -- Saga streams are '<category>-<correlationId>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because keiro-dsl check proved the literal legal.
-surgeFlowCategory :: Stream.StreamCategory a
+surgeFlowCategory :: Stream.StreamCategory SurgeEventStreamDef
 surgeFlowCategory = Stream.categoryUnsafe "surge"
 
 -- Node-level worker policy lowered from the spec. Pass this value to
 -- Keiro.ProcessManager.runProcessManagerWorkerWith.
 surgeFlowProcessWorkerOptions :: WorkerOptions es msg
 surgeFlowProcessWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedHalt
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedHalt,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
 
 -- The deterministic timer-request builder: id derived from the correlation
 -- key (hole-kind 1), processManagerName referenced, payload from the spec.
 -- (timer id derived as uuidv5 of "surge-timer:" <> correlationId)
 surgeFlowTimerRequest :: Text -> UTCTime -> TimerRequest
 surgeFlowTimerRequest correlationId fireAtTime =
-    TimerRequest
-        { timerId = TimerId (namedUuid ("surge-timer:" <> correlationId))
-        , processManagerName = surgeFlowProcessName
-        , correlationId = correlationId
-        , fireAt = fireAtTime
-        , payload = object ["kind" .= ("surge-follow-up" :: Value)]
-        }
+  TimerRequest
+    { timerId = TimerId (namedUuid ("surge-timer:" <> correlationId))
+    , processManagerName = surgeFlowProcessName
+    , correlationId = correlationId
+    , fireAt = fireAtTime
+    , payload = object [ "kind" .= ("surge-follow-up" :: Value) ]
+    }
 
 -- The timer-fire disposition table (hole-kind 2), derived from the spec.
 -- on-reject => Fired is the benign inversion.
@@ -62,10 +62,10 @@
 -- Fold True into the duplicate result and surface False as the failure.
 surgeFlowFireOutcome :: Either CommandError a -> Maybe ()
 surgeFlowFireOutcome result = case result of
-    Right{} -> Just () -- Fired
-    Left CommandRejected -> Just () -- Fired
-    Left (CommandAmbiguous _) -> Nothing -- Retry  -- explicit definition-bug arm
-    Left{} -> Nothing -- Retry
+  Right{} -> Just ()  -- Fired
+  Left CommandRejected -> Just ()  -- Fired
+  Left (CommandAmbiguous _) -> Nothing  -- Retry  -- explicit definition-bug arm
+  Left{} -> Nothing  -- Retry
 
 -- max-attempts = 5, dead-letter = "ceiling"
 -- (the timer worker must pass Just 5 to runTimerWorkerWith, never the
diff --git a/test/conformance-process-full/Main.hs b/test/conformance-process-full/Main.hs
--- a/test/conformance-process-full/Main.hs
+++ b/test/conformance-process-full/Main.hs
@@ -17,10 +17,11 @@
 
 main :: IO ()
 main = do
-    let input = SurgeInput{hospitalId = "hosp-1", observedAt = posixSecondsToUTCTime 0}
+    let hospitalIdValue = "hosp_01h455vb4pex5vsknk084sn02q"
+        input = SurgeInput{hospitalId = hospitalIdValue, observedAt = posixSecondsToUTCTime 0}
         action = surgeManager.handle input
         nameOk = surgeManager.name == "surge-demo"
-        corrOk = surgeManager.correlate input == "hosp-1"
+        corrOk = surgeManager.correlate input == hospitalIdValue
         dispatchOk = length action.commands == 1
         timerOk = length action.timers == 1
         duplicateContractOk = dispatchOk
diff --git a/test/conformance-process-full/SurgeDemo/SurgeFlow/Manager.hs b/test/conformance-process-full/SurgeDemo/SurgeFlow/Manager.hs
--- a/test/conformance-process-full/SurgeDemo/SurgeFlow/Manager.hs
+++ b/test/conformance-process-full/SurgeDemo/SurgeFlow/Manager.hs
@@ -19,7 +19,8 @@
 import Data.Text (Text)
 import Data.Time (UTCTime)
 import Generated.SurgeDemo.Hospital.Domain qualified as H
-import Generated.SurgeDemo.Hospital.EventStream (hospitalCategory, hospitalEventStream)
+import Generated.SurgeDemo.Hospital.EventStream (hospitalCommandCategory, hospitalEventStream)
+import Generated.SurgeDemo.Nominals qualified as N
 import Generated.SurgeDemo.Surge.Domain qualified as S
 import Generated.SurgeDemo.Surge.EventStream (surgeEventStream)
 import Generated.SurgeDemo.SurgeFlow.Process (surgeFlowCategory, surgeFlowTimerRequest)
@@ -54,14 +55,21 @@
         , targetEventStream = hospitalEventStream
         , targetProjections = const []
         , handle = \i ->
+            let checkedId = checkedHospitalId (hospitalId i)
+             in
             ProcessManagerAction
-                { command = S.NoteSurgeThreshold (S.NoteSurgeThresholdData (S.HospitalId (hospitalId i)))
+                { command = S.NoteSurgeThreshold (S.NoteSurgeThresholdData checkedId)
                 , commands =
                     [ PMCommand
-                        { target = entityStream hospitalCategory (hospitalId i)
-                        , command = H.ActivateSurge (H.ActivateSurgeData (H.HospitalId (hospitalId i)))
+                        { target = entityStream hospitalCommandCategory (hospitalId i)
+                            , command = H.ActivateSurge (H.ActivateSurgeData checkedId)
                         }
                     ]
                 , timers = [surgeFlowTimerRequest (hospitalId i) (observedAt i)]
                 }
         }
+
+checkedHospitalId :: Text -> N.HospitalId
+checkedHospitalId raw = case N.parseHospitalId raw of
+    Right value -> value
+    Left problem -> error (show problem)
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/HospitalSurge/Process.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/HospitalSurge/Process.hs
--- a/test/conformance-process-runtime/Generated/HospitalCapacity/HospitalSurge/Process.hs
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/HospitalSurge/Process.hs
@@ -1,23 +1,23 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.HospitalSurge.Process (
-    hospitalSurgeProcessName,
-    hospitalSurgeCategory,
-    hospitalSurgeProcessWorkerOptions,
-    hospitalSurgeTimerRequest,
-    hospitalSurgeFireOutcome,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from process HospitalSurge; do not edit.
+module Generated.HospitalCapacity.HospitalSurge.Process
+  ( hospitalSurgeProcessName
+  , hospitalSurgeCategory
+  , hospitalSurgeProcessWorkerOptions
+  , hospitalSurgeTimerRequest
+  , hospitalSurgeFireOutcome
+  ) where
 
 import Data.Aeson (Value, object, (.=))
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 import Data.Time (UTCTime)
 import Data.UUID (UUID)
-import Data.UUID.V5 qualified as UUID.V5
+import qualified Data.UUID.V5 as UUID.V5
+import Generated.HospitalCapacity.Surge.EventStream (SurgeEventStreamDef)
 import Keiro.Command (CommandError (..))
-import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
 import Keiro.Stream qualified as Stream
 import Keiro.Timer (TimerId (..), TimerRequest (..))
+import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
 import Shibuya.Core.Ack (RetryDelay (..))
 
 -- The define-once ProcessManager name (hole-kind 5: referenced, never retyped).
@@ -27,32 +27,32 @@
 -- The validated saga stream category (hole-kind 5: referenced, never retyped).
 -- Saga streams are '<category>-<correlationId>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because keiro-dsl check proved the literal legal.
-hospitalSurgeCategory :: Stream.StreamCategory a
+hospitalSurgeCategory :: Stream.StreamCategory SurgeEventStreamDef
 hospitalSurgeCategory = Stream.categoryUnsafe "hospitalSurge"
 
 -- Node-level worker policy lowered from the spec. Pass this value to
 -- Keiro.ProcessManager.runProcessManagerWorkerWith.
 hospitalSurgeProcessWorkerOptions :: WorkerOptions es msg
 hospitalSurgeProcessWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedHalt
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedHalt,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
 
 -- The deterministic timer-request builder: id derived from the correlation
 -- key (hole-kind 1), processManagerName referenced, payload from the spec.
 -- (timer id derived as uuidv5 of "hospital-surge-timer:" <> correlationId)
 hospitalSurgeTimerRequest :: Text -> UTCTime -> TimerRequest
 hospitalSurgeTimerRequest correlationId fireAtTime =
-    TimerRequest
-        { timerId = TimerId (namedUuid ("hospital-surge-timer:" <> correlationId))
-        , processManagerName = hospitalSurgeProcessName
-        , correlationId = correlationId
-        , fireAt = fireAtTime
-        , payload = object ["kind" .= ("hospital-surge-follow-up" :: Value)]
-        }
+  TimerRequest
+    { timerId = TimerId (namedUuid ("hospital-surge-timer:" <> correlationId))
+    , processManagerName = hospitalSurgeProcessName
+    , correlationId = correlationId
+    , fireAt = fireAtTime
+    , payload = object [ "kind" .= ("hospital-surge-follow-up" :: Value) ]
+    }
 
 -- The timer-fire disposition table (hole-kind 2), derived from the spec.
 -- on-reject => Fired is the benign inversion.
@@ -62,10 +62,10 @@
 -- Fold True into the duplicate result and surface False as the failure.
 hospitalSurgeFireOutcome :: Either CommandError a -> Maybe ()
 hospitalSurgeFireOutcome result = case result of
-    Right{} -> Just () -- Fired
-    Left CommandRejected -> Just () -- Fired
-    Left (CommandAmbiguous _) -> Nothing -- Retry  -- explicit definition-bug arm
-    Left{} -> Nothing -- Retry
+  Right{} -> Just ()  -- Fired
+  Left CommandRejected -> Just ()  -- Fired
+  Left (CommandAmbiguous _) -> Nothing  -- Retry  -- explicit definition-bug arm
+  Left{} -> Nothing  -- Retry
 
 -- max-attempts = 5, dead-letter = "surge timer exceeded ceiling"
 -- (the timer worker must pass Just 5 to runTimerWorkerWith, never the
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals.hs
@@ -0,0 +1,25 @@
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal declarations; do not edit.
+module Generated.HospitalCapacity.Nominals
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.HospitalCapacity.Nominals.Internal (CommandId, mkCommandId, parseCommandId, commandIdText, HospitalId, mkHospitalId, parseHospitalId, hospitalIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+instance CanonicalTypeName CommandId
+
+instance CanonicalTypeName HospitalId
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals/Internal.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Nominals/Internal.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal ID internals; do not edit.
+module Generated.HospitalCapacity.Nominals.Internal
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , unsafeCommandIdFromLegacyText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CommandId = CommandId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CommandId where
+  toJSON = toJSON . commandIdText
+
+instance FromJSON CommandId where
+  parseJSON = withText "CommandId" (either (fail . T.unpack) pure . parseCommandId)
+
+parseCommandId :: Text -> Either Text CommandId
+parseCommandId input = case validateIdDomainText (typeIdV7Domain "cmd") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CommandId input)
+
+mkCommandId :: Text -> Either Text CommandId
+mkCommandId = parseCommandId
+
+commandIdText :: CommandId -> Text
+commandIdText (CommandId value) = value
+
+unsafeCommandIdFromLegacyText :: Text -> CommandId
+unsafeCommandIdFromLegacyText = CommandId
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Codec.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Codec.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Codec.hs
@@ -0,0 +1,75 @@
+{-# LANGUAGE OverloadedRecordDot #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.HospitalCapacity.Surge.Codec (
+    surgeCodec,
+    parseSurgeEvent,
+    encodeSurgeEvent,
+) where
+
+import Generated.HospitalCapacity.Surge.Domain
+import Generated.HospitalCapacity.Nominals (HospitalId, hospitalIdText)
+import Generated.HospitalCapacity.Nominals.Internal (unsafeHospitalIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.Text (Text)
+import qualified Data.Text as T
+import Keiro.Codec (Codec (..), EventType (..))
+
+
+
+
+
+surgeCodec :: Codec SurgeEvent
+surgeCodec =
+  Codec
+    { eventTypes = EventType "SurgeThresholdNoted" :| [EventType "SurgeTimerMarked"]
+    , eventType = \case
+        SurgeThresholdNoted{} -> EventType "SurgeThresholdNoted"
+        SurgeTimerMarked{} -> EventType "SurgeTimerMarked"
+    , schemaVersion = 1
+    , encode = encodeSurgeEvent
+    , decode = parseSurgeEvent
+    , upcasters = []
+    }
+
+encodeSurgeEvent :: SurgeEvent -> Value
+encodeSurgeEvent = \case
+  SurgeThresholdNoted payload ->
+    object
+      [ "kind" .= ("SurgeThresholdNoted" :: Text)
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "availableIcuBeds" .= payload.availableIcuBeds
+      , "redDemand" .= payload.redDemand
+      , "timerId" .= payload.timerId
+      ]
+  SurgeTimerMarked payload ->
+    object
+      [ "kind" .= ("SurgeTimerMarked" :: Text)
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "timerId" .= payload.timerId
+      ]
+
+parseSurgeEvent :: EventType -> Value -> Either Text SurgeEvent
+parseSurgeEvent (EventType tag) = mapLeftText . parseEither (withObject "SurgeEvent" go)
+  where
+    go o = do
+      case tag of
+        "SurgeThresholdNoted" ->
+          SurgeThresholdNoted
+            <$> ( SurgeThresholdNotedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> o .: "availableIcuBeds"
+                    <*> o .: "redDemand"
+                    <*> o .: "timerId"
+                )
+        "SurgeTimerMarked" ->
+          SurgeTimerMarked
+            <$> ( SurgeTimerMarkedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> o .: "timerId"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SurgeThresholdNoted, SurgeTimerMarked")
+
+mapLeftText :: Either String b -> Either Text b
+mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Domain.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Domain.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Domain.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
+{-# LANGUAGE TemplateHaskell #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.HospitalCapacity.Surge.Domain where
+
+import Data.Proxy (Proxy (..))
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Core (RegFile (..))
+import Generated.HospitalCapacity.Nominals (HospitalId, parseHospitalId)
+import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
+
+data SurgeVertex = SurgeIdle
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+
+data NoteSurgeThresholdData = NoteSurgeThresholdData
+  { hospitalId :: !HospitalId
+  , availableIcuBeds :: !Int
+  , redDemand :: !Int
+  , timerId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
+
+data MarkSurgeTimerFiredData = MarkSurgeTimerFiredData
+  { hospitalId :: !HospitalId
+  , timerId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
+
+data SurgeCommand = NoteSurgeThreshold !NoteSurgeThresholdData
+  | MarkSurgeTimerFired !MarkSurgeTimerFiredData
+  deriving stock (Generic, Eq, Show)
+
+data SurgeThresholdNotedData = SurgeThresholdNotedData
+  { hospitalId :: !HospitalId
+  , availableIcuBeds :: !Int
+  , redDemand :: !Int
+  , timerId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
+
+data SurgeTimerMarkedData = SurgeTimerMarkedData
+  { hospitalId :: !HospitalId
+  , timerId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
+
+data SurgeEvent = SurgeThresholdNoted !SurgeThresholdNotedData
+  | SurgeTimerMarked !SurgeTimerMarkedData
+  deriving stock (Generic, Eq, Show)
+
+type SurgeRegs =
+  '[]
+
+initialSurgeRegs :: RegFile SurgeRegs
+initialSurgeRegs =
+  RNil
+
+$(deriveAggregateCtorsAll ''SurgeCommand ''SurgeRegs)
+
+
+
+$(deriveWireCtorsAll ''SurgeEvent)
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/EventStream.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/EventStream.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/EventStream.hs
@@ -0,0 +1,49 @@
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.HospitalCapacity.Surge.EventStream
+  ( surgeCategory
+  , surgeCommandCategory
+  , surgeEventStream
+  , surgeEventStreamDef
+  , SurgeEventStream
+  , SurgeEventStreamDef
+  ) where
+
+import Generated.HospitalCapacity.Surge.Domain
+import Generated.HospitalCapacity.Surge.Codec (surgeCodec)
+import Generated.HospitalCapacity.Surge.Transducer (surgeFoldFingerprint, surgeTransducer)
+import Keiki.Core (HsPred)
+import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
+import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
+import Keiro.Stream qualified as Stream
+
+-- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
+-- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
+-- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
+surgeCategory :: Stream.StreamCategory SurgeEventStreamDef
+surgeCategory = Stream.categoryUnsafe "surge"
+
+-- The same category text, typed for command envelopes such as PMCommand.
+surgeCommandCategory :: Stream.StreamCategory SurgeCommand
+surgeCommandCategory = Stream.categoryUnsafe "surge"
+
+type SurgeEventStreamDef =
+  EventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
+
+type SurgeEventStream =
+  ValidatedEventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
+
+surgeEventStreamDef :: SurgeEventStreamDef
+surgeEventStreamDef =
+  EventStream
+    { transducer = surgeTransducer,
+      initialState = SurgeIdle,
+      initialRegisters = initialSurgeRegs,
+      eventCodec = surgeCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
+
+surgeEventStream :: SurgeEventStream
+surgeEventStream =
+  mkEventStreamOrThrow "Surge" surgeEventStreamDef
diff --git a/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Transducer.hs b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/Generated/HospitalCapacity/Surge/Transducer.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module Generated.HospitalCapacity.Surge.Transducer
+  ( surgeTransducer
+  , surgeFoldFingerprint
+  , BehaviorOwnership (..)
+  , surgePredicateVerifications
+  ) where
+
+import Generated.HospitalCapacity.Surge.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+surgeTransducer
+  :: SymTransducer
+       (HsPred SurgeRegs SurgeCommand)
+       SurgeRegs
+       SurgeVertex
+       SurgeCommand
+       SurgeEvent
+surgeTransducer =
+  B.buildTransducer SurgeIdle initialSurgeRegs isTerminal do
+    B.from SurgeIdle do
+      B.onCmd inCtorNoteSurgeThreshold $ \d -> B.do
+        B.emit wireSurgeThresholdNoted (SurgeThresholdNotedTermFields
+          { hospitalId = d.hospitalId
+          , availableIcuBeds = d.availableIcuBeds
+          , redDemand = d.redDemand
+          , timerId = d.timerId
+          })
+        B.goto SurgeIdle
+      B.onCmd inCtorMarkSurgeTimerFired $ \d -> B.do
+        B.emit wireSurgeTimerMarked (SurgeTimerMarkedTermFields
+          { hospitalId = d.hospitalId
+          , timerId = d.timerId
+          })
+        B.goto SurgeIdle
+ where
+  isTerminal = \case
+
+    _ -> False
+
+surgeFoldFingerprint :: Text
+surgeFoldFingerprint = "f5cf8e2b3a7ba5deee7b74834492edd1"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+surgePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+surgePredicateVerifications = sequence
+  [ verifyTransition "transition1IdleNoteSurgeThreshold" GeneratedOwned SurgeIdle 0
+  , verifyTransition "transition2IdleMarkSurgeTimerFired" GeneratedOwned SurgeIdle 1
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut surgeTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-process-runtime/HospitalCapacity/Surge/Holes.hs b/test/conformance-process-runtime/HospitalCapacity/Surge/Holes.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-process-runtime/HospitalCapacity/Surge/Holes.hs
@@ -0,0 +1,33 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+
+-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
+-- overwrites it. Fill the transducer body (and any other holes) against the
+-- generated signatures, then run the harness to confirm behaviour.
+module HospitalCapacity.Surge.Holes (
+    surgeTransducer,
+    -- (no projection)
+) where
+
+import Generated.HospitalCapacity.Surge.Domain
+import Keiki.Builder ((=:))
+import qualified Keiki.Builder as B
+import Keiki.Core (HsPred, RegFile, SymTransducer, lit, (./=), (.==), (.||))
+
+-- HOLE: the transducer body. Reproduce the structure below, replacing each
+-- `-- HOLE` line with the keiki symbolic operators it describes.
+surgeTransducer ::
+    SymTransducer
+        (HsPred SurgeRegs SurgeCommand)
+        SurgeRegs
+        SurgeVertex
+        SurgeCommand
+        SurgeEvent
+surgeTransducer =
+    B.buildTransducer SurgeIdle initialSurgeRegs isTerminal (pure ())
+  where
+    isTerminal = \case
+        _ -> False
diff --git a/test/conformance-process-runtime/Main.hs b/test/conformance-process-runtime/Main.hs
--- a/test/conformance-process-runtime/Main.hs
+++ b/test/conformance-process-runtime/Main.hs
@@ -12,15 +12,17 @@
 import Data.Text (Text)
 import Data.Time.Clock.POSIX (posixSecondsToUTCTime)
 import Generated.HospitalCapacity.HospitalSurge.Process (
+    hospitalSurgeCategory,
     hospitalSurgeFireOutcome,
     hospitalSurgeProcessName,
     hospitalSurgeProcessWorkerOptions,
     hospitalSurgeTimerRequest,
  )
+import Generated.HospitalCapacity.Surge.EventStream (SurgeEventStreamDef)
 import Keiro.Command (CommandError (..))
 import Keiro.Dsl.Validate (sagaCategoryError)
 import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
-import Keiro.Stream (CategoryError, StreamCategory, category)
+import Keiro.Stream (CategoryError, Stream, StreamCategory, category, entityStream)
 import Keiro.Timer (TimerRequest (..))
 import System.Exit (exitFailure)
 
@@ -35,6 +37,8 @@
         ambiguousOk = hospitalSurgeFireOutcome (Left (CommandAmbiguous [0, 1]) :: Either CommandError ()) == Nothing
         rejectedPolicyOk = rejectedCommandPolicy hospitalSurgeProcessWorkerOptions == RejectedHalt
         poisonPolicyOk = poisonIsHalt hospitalSurgeProcessWorkerOptions
+        sagaStream = entityStream hospitalSurgeCategory "hosp-1" :: Stream SurgeEventStreamDef
+        sagaCategoryTyped = sagaStream `seq` True
         categoryMirrorOk = all categoryAgreement ["hospitalSurge", "surge", "", "$all", "hospital-surge", "hospital surge", "bad\NULcategory"]
         colonReservedOk = sagaCategoryError "wf:surge" /= Nothing && not (runtimeRejectsCategory "wf:surge")
     putStrLn ("process name: " <> show nameOk)
@@ -44,9 +48,10 @@
     putStrLn ("on-ambiguous => Retry: " <> show ambiguousOk)
     putStrLn ("rejected policy lowered: " <> show rejectedPolicyOk)
     putStrLn ("poison policy lowered: " <> show poisonPolicyOk)
+    putStrLn ("saga category carries its event-stream phantom: " <> show sagaCategoryTyped)
     putStrLn ("DSL saga category mirror agrees with Keiro.Stream.category: " <> show categoryMirrorOk)
     putStrLn ("DSL additionally reserves ':' for workflow streams: " <> show colonReservedOk)
-    unless (nameOk && reqOk && okOk && rejectOk && ambiguousOk && rejectedPolicyOk && poisonPolicyOk && categoryMirrorOk && colonReservedOk) exitFailure
+    unless (nameOk && reqOk && okOk && rejectOk && ambiguousOk && rejectedPolicyOk && poisonPolicyOk && sagaCategoryTyped && categoryMirrorOk && colonReservedOk) exitFailure
 
 categoryAgreement :: Text -> Bool
 categoryAgreement value = (sagaCategoryError value /= Nothing) == runtimeRejectsCategory value
diff --git a/test/conformance-process/Generated/HospitalCapacity/HospitalSurge/ProcessHarness.hs b/test/conformance-process/Generated/HospitalCapacity/HospitalSurge/ProcessHarness.hs
--- a/test/conformance-process/Generated/HospitalCapacity/HospitalSurge/ProcessHarness.hs
+++ b/test/conformance-process/Generated/HospitalCapacity/HospitalSurge/ProcessHarness.hs
@@ -1,24 +1,22 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from process HospitalSurge; do not edit.
 module Generated.HospitalCapacity.HospitalSurge.ProcessHarness (processHarnessValues) where
 
-{- | (label, value): the spec's deterministic process/timer decisions,
-lowered to plain values so a driver can assert them against a committed
-expectation. The driver's expectation is hand-written (not generated), so a
-spec change that alters a decision diverges from it and turns a specific
-assertion red — the spec->behaviour pin. (Live-runtime behavioural
-conformance of the filled ProcessManager is the M5 step.)
--}
+-- | (label, value): the spec's deterministic process/timer decisions,
+-- lowered to plain values so a driver can assert them against a committed
+-- expectation. The driver's expectation is hand-written (not generated), so a
+-- spec change that alters a decision diverges from it and turns a specific
+-- assertion red — the spec->behaviour pin. (Live-runtime behavioural
+-- conformance of the filled ProcessManager is the M5 step.)
 processHarnessValues :: [(String, String)]
 processHarnessValues =
-    [ ("fireAtField", "observedAt")
-    , ("timerIdPrefix", "hospital-surge-timer:")
-    , ("firedEventIdPrefix", "hospital-surge-fired:")
-    , ("dispatchIdUserField", "none")
-    , ("onReject", "Fired")
-    , ("onAmbiguous", "Retry")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "halt")
-    , ("poisonPolicy", "halt")
-    , ("maxAttempts", "5")
-    ]
+  [ ("fireAtField", "observedAt")
+  , ("timerIdPrefix", "hospital-surge-timer:")
+  , ("firedEventIdPrefix", "hospital-surge-fired:")
+  , ("dispatchIdUserField", "none")
+  , ("onReject", "Fired")
+  , ("onAmbiguous", "Retry")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "halt")
+  , ("poisonPolicy", "halt")
+  , ("maxAttempts", "5")
+  ]
diff --git a/test/conformance-publisher-runtime/Generated/HospitalCapacity/HospitalPublisher/Publisher.hs b/test/conformance-publisher-runtime/Generated/HospitalCapacity/HospitalPublisher/Publisher.hs
--- a/test/conformance-publisher-runtime/Generated/HospitalCapacity/HospitalPublisher/Publisher.hs
+++ b/test/conformance-publisher-runtime/Generated/HospitalCapacity/HospitalPublisher/Publisher.hs
@@ -1,12 +1,11 @@
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from publisher hospitalPublisher; do not edit.
 module Generated.HospitalCapacity.HospitalPublisher.Publisher
   ( publisherOrdering
   , publisherBackoff
   , publisherMaxAttempts
   ) where
 
-import Keiro.Outbox.Types (BackoffSchedule (..), OrderingPolicy (..))
+import Keiro.Outbox.Types (BackoffSchedule (..), ExponentialBackoffOptions (..), OrderingPolicy (..))
 
 publisherOrdering :: OrderingPolicy
 publisherOrdering = PerKeyHeadOfLine
diff --git a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/Queue.hs b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/Queue.hs
--- a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/Queue.hs
+++ b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/Queue.hs
@@ -1,22 +1,17 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation_work.Queue (
-    ReservationWorkItem (..),
-    encodeReservationWorkItem,
-    parseReservationWorkItem,
-    queuePhysical,
-    queueDlq,
-    queueTable,
-    groupKeyField,
-    groupKeyFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module Generated.HospitalCapacity.Reservation_work.Queue
+  ( ReservationWorkItem (..)
+  , encodeReservationWorkItem
+  , parseReservationWorkItem
+  , queuePhysical, queueDlq, queueTable
+  , groupKeyField, groupKeyFor
+  ) where
 
 import Data.Aeson (Value, object, withObject, (.:), (.=))
 import Data.Aeson.Types (parseEither)
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 
 queuePhysical, queueDlq, queueTable :: Text
 queuePhysical = "hospital_capacity_reservation_work"
@@ -30,21 +25,21 @@
 groupKeyFor payload = payload.reservationId
 
 data ReservationWorkItem = ReservationWorkItem
-    { reservationId :: !Text
-    , hospitalId :: !Text
-    , commandId :: !Text
-    , lifeCriticalOverride :: !Bool
-    }
-    deriving stock (Eq, Show)
+  { reservationId :: !Text
+  , hospitalId :: !Text
+  , commandId :: !Text
+  , lifeCriticalOverride :: !Bool
+  }
+  deriving stock (Eq, Show)
 
 encodeReservationWorkItem :: ReservationWorkItem -> Value
 encodeReservationWorkItem p =
-    object
-        [ "reservation_id" .= p.reservationId
-        , "hospital_id" .= p.hospitalId
-        , "command_id" .= p.commandId
-        , "life_critical_override" .= p.lifeCriticalOverride
-        ]
+  object
+    [ "reservation_id" .= p.reservationId
+    , "hospital_id" .= p.hospitalId
+    , "command_id" .= p.commandId
+    , "life_critical_override" .= p.lifeCriticalOverride
+    ]
 
 parseReservationWorkItem :: Value -> Either Text ReservationWorkItem
 parseReservationWorkItem = mapLeftText . parseEither (withObject "ReservationWorkItem" go)
diff --git a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
--- a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
+++ b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
@@ -1,32 +1,29 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-
-{- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
-
-Deploy workers before producers when raising its schema version. Do not
-adopt this codec on a non-empty bare-payload queue without draining it
-(or supplying a transitional codec), or in-flight messages will
-dead-letter. This is telemetry-neutral:
-docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
-spans and acknowledgement vocabulary.
--}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+-- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
+--
+-- Deploy workers before producers when raising its schema version. Do not
+-- adopt this codec on a non-empty bare-payload queue without draining it
+-- (or supplying a transitional codec), or in-flight messages will
+-- dead-letter. This is telemetry-neutral:
+-- docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
+-- spans and acknowledgement vocabulary.
 module Generated.HospitalCapacity.Reservation_work.QueueCodec (reservationWorkPayloadCodec, reservationWorkJobCodec) where
 
 import Data.List.NonEmpty (NonEmpty (..))
-import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 import Keiro.Codec (Codec (..), EventType (..))
 import Keiro.PGMQ.Codec (JobCodec, keiroJobCodec)
+import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 
 reservationWorkPayloadCodec :: Codec ReservationWorkItem
 reservationWorkPayloadCodec =
-    Codec
-        { eventTypes = EventType "ReservationWorkItem" :| []
-        , eventType = \_ -> EventType "ReservationWorkItem"
-        , schemaVersion = 1
-        , encode = encodeReservationWorkItem
-        , decode = \_ -> parseReservationWorkItem
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ReservationWorkItem" :| []
+    , eventType = \_ -> EventType "ReservationWorkItem"
+    , schemaVersion = 1
+    , encode = encodeReservationWorkItem
+    , decode = \_ -> parseReservationWorkItem
+    , upcasters = []
+    }
 
 reservationWorkJobCodec :: JobCodec ReservationWorkItem
 reservationWorkJobCodec = keiroJobCodec reservationWorkPayloadCodec
diff --git a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
--- a/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
+++ b/test/conformance-queue-runtime/Generated/HospitalCapacity/Reservation_work/QueuePolicy.hs
@@ -1,14 +1,10 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation_work.QueuePolicy (
-    retryPolicy,
-    jobOutcomeFor,
-    jobOrdering,
-    jobTuningFor,
-    queueProvision,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module Generated.HospitalCapacity.Reservation_work.QueuePolicy
+  ( ReservationWorkOutcome (..)
+  , retryPolicy, jobOutcomeFor
+  , jobOrdering, jobTuningFor, queueProvision
+  ) where
 
-import Data.Text (Text)
 import Keiro.PGMQ.Job (JobOrdering (..), JobOutcome (..), JobTuning, PartitionSpec (..), QueueProvision, RetryDelay (..), RetryPolicy (..), partitionedProvision, standardProvision, unloggedProvision, withFifoIndexProvision, withOrdering)
 
 jobOrdering :: JobOrdering
@@ -24,18 +20,24 @@
 
 retryPolicy :: RetryPolicy
 retryPolicy =
-    RetryPolicy
-        { maxRetries = 3
-        , defaultRetryDelay = RetryDelay 5
-        , useDeadLetter = True
-        }
+  RetryPolicy
+    { maxRetries = 3
+    , defaultRetryDelay = RetryDelay 5
+    , useDeadLetter = True
+    }
 
 -- The consumer JobOutcome disposition over the spec's named domain outcomes,
 -- lowered to the live Keiro.PGMQ.Job.JobOutcome.
-jobOutcomeFor :: Text -> JobOutcome
+data ReservationWorkOutcome
+  = StoreFailure
+  | CommandRejected
+  | DecodeFailure
+  | OnCodecReject
+  deriving stock (Eq, Show)
+
+jobOutcomeFor :: ReservationWorkOutcome -> JobOutcome
 jobOutcomeFor o = case o of
-    "storeFailure" -> Retry (RetryDelay 5)
-    "commandRejected" -> Dead "dead-lettered"
-    "decodeFailure" -> Dead "dead-lettered"
-    "onCodecReject" -> Dead "dead-lettered"
-    _ -> Retry (RetryDelay 5)
+  StoreFailure -> Retry (RetryDelay 5)
+  CommandRejected -> Dead "dead-lettered"
+  DecodeFailure -> Dead "dead-lettered"
+  OnCodecReject -> Dead "dead-lettered"
diff --git a/test/conformance-queue-runtime/Main.hs b/test/conformance-queue-runtime/Main.hs
--- a/test/conformance-queue-runtime/Main.hs
+++ b/test/conformance-queue-runtime/Main.hs
@@ -1,16 +1,17 @@
-{- | EP-5 runtime conformance: the scaffolded pgmq @QueuePolicy@ — the
-@RetryPolicy@ and the @JobOutcome@ disposition — compiled against the LIVE
-@Keiro.PGMQ.Job@ runtime. Running it pins the dangerous inversions over the
-real JobOutcome: storeFailure ⇒ Retry (transient) and decodeFailure ⇒ Dead
-(poison), plus the dlq=on ceiling.
--}
+-- | EP-5 runtime conformance: the scaffolded pgmq @QueuePolicy@ — the
+-- @RetryPolicy@ and the @JobOutcome@ disposition — compiled against the LIVE
+-- @Keiro.PGMQ.Job@ runtime. Running it pins the dangerous inversions over the
+-- real JobOutcome: storeFailure ⇒ Retry (transient) and decodeFailure ⇒ Dead
+-- (poison), plus the dlq=on ceiling.
 module Main (main) where
 
 import Control.Monad (unless)
+import Data.Aeson (encode, object, (.=))
+import Data.ByteString.Lazy (ByteString)
 import Data.Text (Text)
-import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem (..), groupKeyFor)
+import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem (..), encodeReservationWorkItem, groupKeyFor, parseReservationWorkItem)
 import Generated.HospitalCapacity.Reservation_work.QueueCodec (reservationWorkJobCodec)
-import Generated.HospitalCapacity.Reservation_work.QueuePolicy (jobOrdering, jobOutcomeFor, jobTuningFor, queueProvision, retryPolicy)
+import Generated.HospitalCapacity.Reservation_work.QueuePolicy (ReservationWorkOutcome (..), jobOrdering, jobOutcomeFor, jobTuningFor, queueProvision, retryPolicy)
 import Keiro.Dsl.Validate (derivedQueueTrio)
 import Keiro.PGMQ.Job (Job (..), JobOrdering (..), JobOutcome (..), JobTuning (..), RetryPolicy (..), defaultJobTuning, queueProvisionConfigs)
 import Keiro.PGMQ.Runtime (QueueRef (..), queueRef)
@@ -28,56 +29,66 @@
 
 main :: IO ()
 main = do
-    let storeOk = isRetry (jobOutcomeFor "storeFailure") -- transient: MUST retry
-        decodeOk = isDead (jobOutcomeFor "decodeFailure") -- poison: MUST dead-letter
-        ceilingOk = maxRetries retryPolicy == 3 && useDeadLetter retryPolicy
-        orderingOk = jobOrdering == FifoThroughput
-        tuningOk = ordering (jobTuningFor defaultJobTuning) == FifoThroughput
-        job =
-            Job
-                { jobName = "reservation-work"
-                , jobQueue = queueRef "hospital_capacity.reservation_work"
-                , jobCodec = reservationWorkJobCodec
-                , jobPolicy = retryPolicy
-                }
-        provisionOk = case queueProvisionConfigs queueProvision job of
-            [mainQueue, deadLetterQueue] ->
-                Config.fifoIndex mainQueue
-                    && not (Config.fifoIndex deadLetterQueue)
-                    && isStandard mainQueue
-                    && isStandard deadLetterQueue
-            _ -> False
-        groupKeyOk = groupKeyFor (ReservationWorkItem "rsv-123" "hsp-1" "cmd-1" True) == "rsv-123"
-        vectors =
-            [ "hospital_capacity.reservation_work"
-            , "Repro.Work"
-            , "a__b..c"
-            , "9lives"
-            , "already_dlq"
-            , "hospital_capacity.reservation_work.per_hospital_fifo_lane_assignments"
-            ]
-        parity = [(logical, derivedQueueTrio logical == liveQueueTrio logical) | logical <- vectors]
-    putStrLn ("storeFailure => Retry (transient): " <> show storeOk)
-    putStrLn ("decodeFailure => Dead (poison): " <> show decodeOk)
-    putStrLn ("retry ceiling + dlq on: " <> show ceilingOk)
-    putStrLn ("ordering lowered to FifoThroughput: " <> show orderingOk)
-    putStrLn ("provision includes the FIFO index: " <> show provisionOk)
-    putStrLn ("groupKeyFor projects the payload field: " <> show groupKeyOk)
-    putStrLn ("jobTuningFor overlays deployment tuning: " <> show tuningOk)
-    mapM_ (\(logical, matches) -> putStrLn ("derivedQueueTrio " <> show logical <> " == live queueRef: " <> show matches)) parity
-    unless (storeOk && decodeOk && ceilingOk && orderingOk && provisionOk && groupKeyOk && tuningOk && all snd parity) exitFailure
+  let sample = ReservationWorkItem "rsv-123" "hsp-1" "cmd-1" True
+      expectedBytes :: ByteString
+      expectedBytes = "{\"command_id\":\"cmd-1\",\"hospital_id\":\"hsp-1\",\"life_critical_override\":true,\"reservation_id\":\"rsv-123\"}"
+      wireBytesOk = encode (encodeReservationWorkItem sample) == expectedBytes
+      decodeAcceptanceOk = parseReservationWorkItem (encodeReservationWorkItem sample) == Right sample
+      decodeRejectionOk = case parseReservationWorkItem (object ["reservation_id" .= ("rsv-123" :: Text)]) of
+        Left _ -> True
+        Right _ -> False
+      storeOk = isRetry (jobOutcomeFor StoreFailure) -- transient: MUST retry
+      decodeOk = isDead (jobOutcomeFor DecodeFailure) -- poison: MUST dead-letter
+      ceilingOk = maxRetries retryPolicy == 3 && useDeadLetter retryPolicy
+      orderingOk = jobOrdering == FifoThroughput
+      tuningOk = ordering (jobTuningFor defaultJobTuning) == FifoThroughput
+      job =
+        Job
+          { jobName = "reservation-work",
+            jobQueue = queueRef "hospital_capacity.reservation_work",
+            jobCodec = reservationWorkJobCodec,
+            jobPolicy = retryPolicy
+          }
+      provisionOk = case queueProvisionConfigs queueProvision job of
+        [mainQueue, deadLetterQueue] ->
+          Config.fifoIndex mainQueue
+            && not (Config.fifoIndex deadLetterQueue)
+            && isStandard mainQueue
+            && isStandard deadLetterQueue
+        _ -> False
+      groupKeyOk = groupKeyFor sample == "rsv-123"
+      vectors =
+        [ "hospital_capacity.reservation_work",
+          "Repro.Work",
+          "a__b..c",
+          "9lives",
+          "already_dlq",
+          "hospital_capacity.reservation_work.per_hospital_fifo_lane_assignments"
+        ]
+      parity = [(logical, derivedQueueTrio logical == liveQueueTrio logical) | logical <- vectors]
+  putStrLn ("storeFailure => Retry (transient): " <> show storeOk)
+  putStrLn ("decodeFailure => Dead (poison): " <> show decodeOk)
+  putStrLn ("retry ceiling + dlq on: " <> show ceilingOk)
+  putStrLn ("ordering lowered to FifoThroughput: " <> show orderingOk)
+  putStrLn ("provision includes the FIFO index: " <> show provisionOk)
+  putStrLn ("groupKeyFor projects the payload field: " <> show groupKeyOk)
+  putStrLn ("jobTuningFor overlays deployment tuning: " <> show tuningOk)
+  putStrLn ("queue payload bytes pinned: " <> show wireBytesOk)
+  putStrLn ("queue decoder acceptance/rejection pinned: " <> show (decodeAcceptanceOk && decodeRejectionOk))
+  mapM_ (\(logical, matches) -> putStrLn ("derivedQueueTrio " <> show logical <> " == live queueRef: " <> show matches)) parity
+  unless (wireBytesOk && decodeAcceptanceOk && decodeRejectionOk && storeOk && decodeOk && ceilingOk && orderingOk && provisionOk && groupKeyOk && tuningOk && all snd parity) exitFailure
 
 liveQueueTrio :: Text -> (Text, Text, Text)
 liveQueueTrio logical =
-    ( physical
-    , queueNameToText (dlqName ref)
-    , "pgmq.q_" <> physical
-    )
+  ( physical,
+    queueNameToText (dlqName ref),
+    "pgmq.q_" <> physical
+  )
   where
     ref = queueRef logical
     physical = queueNameToText (physicalName ref)
 
 isStandard :: Config.QueueConfig -> Bool
 isStandard config = case Config.queueType config of
-    Config.StandardQueue -> True
-    _ -> False
+  Config.StandardQueue -> True
+  _ -> False
diff --git a/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/Queue.hs b/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/Queue.hs
--- a/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/Queue.hs
+++ b/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/Queue.hs
@@ -1,22 +1,17 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation_work.Queue (
-    ReservationWorkItem (..),
-    encodeReservationWorkItem,
-    parseReservationWorkItem,
-    queuePhysical,
-    queueDlq,
-    queueTable,
-    groupKeyField,
-    groupKeyFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module Generated.HospitalCapacity.Reservation_work.Queue
+  ( ReservationWorkItem (..)
+  , encodeReservationWorkItem
+  , parseReservationWorkItem
+  , queuePhysical, queueDlq, queueTable
+  , groupKeyField, groupKeyFor
+  ) where
 
 import Data.Aeson (Value, object, withObject, (.:), (.=))
 import Data.Aeson.Types (parseEither)
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 
 queuePhysical, queueDlq, queueTable :: Text
 queuePhysical = "hospital_capacity_reservation_work"
@@ -30,21 +25,21 @@
 groupKeyFor payload = payload.reservationId
 
 data ReservationWorkItem = ReservationWorkItem
-    { reservationId :: !Text
-    , hospitalId :: !Text
-    , commandId :: !Text
-    , lifeCriticalOverride :: !Bool
-    }
-    deriving stock (Eq, Show)
+  { reservationId :: !Text
+  , hospitalId :: !Text
+  , commandId :: !Text
+  , lifeCriticalOverride :: !Bool
+  }
+  deriving stock (Eq, Show)
 
 encodeReservationWorkItem :: ReservationWorkItem -> Value
 encodeReservationWorkItem p =
-    object
-        [ "reservation_id" .= p.reservationId
-        , "hospital_id" .= p.hospitalId
-        , "command_id" .= p.commandId
-        , "life_critical_override" .= p.lifeCriticalOverride
-        ]
+  object
+    [ "reservation_id" .= p.reservationId
+    , "hospital_id" .= p.hospitalId
+    , "command_id" .= p.commandId
+    , "life_critical_override" .= p.lifeCriticalOverride
+    ]
 
 parseReservationWorkItem :: Value -> Either Text ReservationWorkItem
 parseReservationWorkItem = mapLeftText . parseEither (withObject "ReservationWorkItem" go)
diff --git a/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs b/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
--- a/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
+++ b/test/conformance-queue/Generated/HospitalCapacity/Reservation_work/QueueCodec.hs
@@ -1,32 +1,29 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-
-{- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
-
-Deploy workers before producers when raising its schema version. Do not
-adopt this codec on a non-empty bare-payload queue without draining it
-(or supplying a transitional codec), or in-flight messages will
-dead-letter. This is telemetry-neutral:
-docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
-spans and acknowledgement vocabulary.
--}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+-- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
+--
+-- Deploy workers before producers when raising its schema version. Do not
+-- adopt this codec on a non-empty bare-payload queue without draining it
+-- (or supplying a transitional codec), or in-flight messages will
+-- dead-letter. This is telemetry-neutral:
+-- docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
+-- spans and acknowledgement vocabulary.
 module Generated.HospitalCapacity.Reservation_work.QueueCodec (reservationWorkPayloadCodec, reservationWorkJobCodec) where
 
 import Data.List.NonEmpty (NonEmpty (..))
-import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 import Keiro.Codec (Codec (..), EventType (..))
 import Keiro.PGMQ.Codec (JobCodec, keiroJobCodec)
+import Generated.HospitalCapacity.Reservation_work.Queue (ReservationWorkItem, encodeReservationWorkItem, parseReservationWorkItem)
 
 reservationWorkPayloadCodec :: Codec ReservationWorkItem
 reservationWorkPayloadCodec =
-    Codec
-        { eventTypes = EventType "ReservationWorkItem" :| []
-        , eventType = \_ -> EventType "ReservationWorkItem"
-        , schemaVersion = 1
-        , encode = encodeReservationWorkItem
-        , decode = \_ -> parseReservationWorkItem
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ReservationWorkItem" :| []
+    , eventType = \_ -> EventType "ReservationWorkItem"
+    , schemaVersion = 1
+    , encode = encodeReservationWorkItem
+    , decode = \_ -> parseReservationWorkItem
+    , upcasters = []
+    }
 
 reservationWorkJobCodec :: JobCodec ReservationWorkItem
 reservationWorkJobCodec = keiroJobCodec reservationWorkPayloadCodec
diff --git a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModel.hs b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModel.hs
--- a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModel.hs
+++ b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModel.hs
@@ -1,20 +1,19 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Transfer_decisions.ReadModel (
-    transferDecisionsReadModel,
-    transferDecisionsQualifiedTable,
-    registerTransferDecisions,
-    startTransferDecisionsRebuild,
-    finishTransferDecisionsRebuild,
-    abandonTransferDecisionsRebuild,
-    transferDecisionsAsyncProjection,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
+module Generated.HospitalCapacity.Transfer_decisions.ReadModel
+  ( transferDecisionsReadModel
+  , transferDecisionsQualifiedTable
+  , registerTransferDecisions
+  , startTransferDecisionsRebuild
+  , finishTransferDecisionsRebuild
+  , abandonTransferDecisionsRebuild
+  , transferDecisionsAsyncProjection
+  ) where
 
 import Data.Functor (void)
 import Effectful (Eff, (:>))
 import Generated.HospitalCapacity.Transfer_decisions.ReadModelTable (transferDecisionsQualifiedTable)
-import HospitalCapacity.Transfer_decisions.ReadModelHoles (TransferDecisionsQueryInput, TransferDecisionsQueryResult, applyTransferDecisions, transferDecisionsQuery)
+import HospitalCapacity.Transfer_decisions.ReadModelHoles (TransferDecisionsQueryInput, TransferDecisionsQueryResult, transferDecisionsQuery, applyTransferDecisions)
 import Keiro.Projection (AsyncProjection (..))
 import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)
 import Keiro.ReadModel.Rebuild qualified as Rebuild
@@ -23,40 +22,40 @@
 
 transferDecisionsReadModel :: ReadModel TransferDecisionsQueryInput TransferDecisionsQueryResult
 transferDecisionsReadModel =
-    ReadModel
-        { name = "hospital-capacity-transfer-decisions"
-        , tableName = "transfer_decisions"
-        , schema = "hospital_capacity"
-        , subscriptionName = "hospital-capacity-transfer-decisions-sub"
-        , version = 1
-        , shapeHash = "fnv1a:3717f6d9e3c44bd6"
-        , defaultConsistency = Strong
-        , strongScope = CategoryHead "reservation"
-        , query = transferDecisionsQuery
-        }
+  ReadModel
+    { name = "hospital-capacity-transfer-decisions"
+    , tableName = "transfer_decisions"
+    , schema = "hospital_capacity"
+    , subscriptionName = "hospital-capacity-transfer-decisions-sub"
+    , version = 1
+    , shapeHash = "fnv1a:3717f6d9e3c44bd6"
+    , defaultConsistency = Strong
+    , strongScope = CategoryHead "reservation"
+    , query = transferDecisionsQuery
+    }
 
 -- Call once at projection startup before serving queries.
 registerTransferDecisions :: (Store :> es) => Eff es ()
 registerTransferDecisions =
-    void (registerReadModel "hospital-capacity-transfer-decisions" 1 "fnv1a:3717f6d9e3c44bd6")
+  void (registerReadModel "hospital-capacity-transfer-decisions" 1 "fnv1a:3717f6d9e3c44bd6")
 
 startTransferDecisionsRebuild :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata
 startTransferDecisionsRebuild =
-    Rebuild.startRebuild transferDecisionsReadModel ["hospital-capacity-transfer-decisions-async"]
+  Rebuild.startRebuild transferDecisionsReadModel ["hospital-capacity-transfer-decisions-async"]
 
 finishTransferDecisionsRebuild :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)
 finishTransferDecisionsRebuild =
-    Rebuild.finishRebuild transferDecisionsReadModel ["hospital-capacity-transfer-decisions-async"]
+  Rebuild.finishRebuild transferDecisionsReadModel ["hospital-capacity-transfer-decisions-async"]
 
 abandonTransferDecisionsRebuild :: (Store :> es) => Eff es ReadModelMetadata
 abandonTransferDecisionsRebuild = Rebuild.abandonRebuild transferDecisionsReadModel
 
 transferDecisionsAsyncProjection :: AsyncProjection
 transferDecisionsAsyncProjection =
-    AsyncProjection
-        { name = "hospital-capacity-transfer-decisions-async"
-        , readModelName = "hospital-capacity-transfer-decisions"
-        , subscriptionName = "hospital-capacity-transfer-decisions-sub"
-        , applyRecorded = applyTransferDecisions
-        , idempotencyKey = \recorded -> recorded.eventId
-        }
+  AsyncProjection
+    { name = "hospital-capacity-transfer-decisions-async"
+    , readModelName = "hospital-capacity-transfer-decisions"
+    , subscriptionName = "hospital-capacity-transfer-decisions-sub"
+    , applyRecorded = applyTransferDecisions
+    , idempotencyKey = \recorded -> recorded.eventId
+    }
diff --git a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelHarness.hs b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelHarness.hs
--- a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelHarness.hs
+++ b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelHarness.hs
@@ -1,19 +1,19 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
 module Generated.HospitalCapacity.Transfer_decisions.ReadModelHarness (readModelFacts, runReadModelFacts) where
 
 -- | (fact, expected from notation, actual shared derivation/lowering).
 readModelFacts :: [(String, String, String)]
 readModelFacts =
-    [ ("registryName", "hospital-capacity-transfer-decisions", "hospital-capacity-transfer-decisions")
-    , ("subscriptionName", "hospital-capacity-transfer-decisions-sub", "hospital-capacity-transfer-decisions-sub")
-    , ("shapeHash", "fnv1a:3717f6d9e3c44bd6", "fnv1a:3717f6d9e3c44bd6")
-    , ("asyncProjectionName", "hospital-capacity-transfer-decisions-async", "hospital-capacity-transfer-decisions-async")
-    , ("consistency", "Strong", "Strong")
-    , ("strongScope", "CategoryHead reservation", "CategoryHead reservation")
-    ]
+  [ ("registryName", "hospital-capacity-transfer-decisions", "hospital-capacity-transfer-decisions")
+  , ("subscriptionName", "hospital-capacity-transfer-decisions-sub", "hospital-capacity-transfer-decisions-sub")
+  , ("shapeHash", "fnv1a:3717f6d9e3c44bd6", "fnv1a:3717f6d9e3c44bd6")
+  , ("asyncProjectionName", "hospital-capacity-transfer-decisions-async", "hospital-capacity-transfer-decisions-async")
+  , ("consistency", "Strong", "Strong")
+  , ("strongScope", "CategoryHead reservation", "CategoryHead reservation")
+  ]
 
 runReadModelFacts :: IO Bool
 runReadModelFacts = do
-    let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
-    mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
-    pure (null failures)
+  let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
+  mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
+  pure (null failures)
diff --git a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelTable.hs b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelTable.hs
--- a/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelTable.hs
+++ b/test/conformance-readmodel-runtime/Generated/HospitalCapacity/Transfer_decisions/ReadModelTable.hs
@@ -1,5 +1,4 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
 module Generated.HospitalCapacity.Transfer_decisions.ReadModelTable (transferDecisionsQualifiedTable) where
 
 import Data.Text (Text)
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/Codec.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/Codec.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/Note/Codec.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/Codec.hs
@@ -1,49 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
 module Generated.ReplayDivergence.Note.Codec (
     noteCodec,
     parseNoteEvent,
     encodeNoteEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.ReplayDivergence.Note.Domain
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.ReplayDivergence.Note.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 noteCodec :: Codec NoteEvent
 noteCodec =
-    Codec
-        { eventTypes = EventType "NoteWritten" :| []
-        , eventType = \case
-            NoteWritten{} -> EventType "NoteWritten"
-        , schemaVersion = 1
-        , encode = encodeNoteEvent
-        , decode = parseNoteEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "NoteWritten" :| []
+    , eventType = \case
+        NoteWritten{} -> EventType "NoteWritten"
+    , schemaVersion = 1
+    , encode = encodeNoteEvent
+    , decode = parseNoteEvent
+    , upcasters = []
+    }
 
 encodeNoteEvent :: NoteEvent -> Value
 encodeNoteEvent = \case
-    NoteWritten payload ->
-        object
-            [ "kind" .= ("NoteWritten" :: Text)
-            , "noteText" .= payload.noteText
-            , "echo" .= payload.echo
-            ]
+  NoteWritten payload ->
+    object
+      [ "kind" .= ("NoteWritten" :: Text)
+      , "noteText" .= payload.noteText
+      , "echo" .= payload.echo
+      ]
 
 parseNoteEvent :: EventType -> Value -> Either Text NoteEvent
 parseNoteEvent (EventType tag) = mapLeftText . parseEither (withObject "NoteEvent" go)
   where
     go o = do
-        case tag of
-            "NoteWritten" ->
-                NoteWritten <$> (NoteWrittenData <$> o .: "noteText" <*> o .: "echo")
-            _ -> fail "unknown event type"
+      case tag of
+        "NoteWritten" ->
+          NoteWritten
+            <$> ( NoteWrittenData
+                    <$> o .: "noteText"
+                    <*> o .: "echo"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: NoteWritten")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/Domain.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/Domain.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/Note/Domain.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/Domain.hs
@@ -2,9 +2,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
 module Generated.ReplayDivergence.Note.Domain where
 
 import Data.Proxy (Proxy (..))
@@ -14,34 +12,36 @@
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data NoteVertex = NoteEmpty | NoteRecorded
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
 data WriteNoteData = WriteNoteData
-    { noteText :: !Text
-    , echo :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { noteText :: !Text
+  , echo :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data NoteCommand = WriteNote !WriteNoteData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data NoteWrittenData = NoteWrittenData
-    { noteText :: !Text
-    , echo :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { noteText :: !Text
+  , echo :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data NoteEvent = NoteWritten !NoteWrittenData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 type NoteRegs =
-    '[ '("note", Text)
-     ]
+  '[ '("note", Text)
+   ]
 
 initialNoteRegs :: RegFile NoteRegs
 initialNoteRegs =
-    RCons (Proxy @"note") "" RNil
+  RCons (Proxy @"note") "" RNil
 
 $(deriveAggregateCtorsAll ''NoteCommand ''NoteRegs)
+
+
 
 $(deriveWireCtorsAll ''NoteEvent)
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/EventStream.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/EventStream.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/Note/EventStream.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.ReplayDivergence.Note.EventStream (
-    noteCategory,
-    noteEventStream,
-    noteEventStreamDef,
-    NoteEventStream,
-    NoteEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
+module Generated.ReplayDivergence.Note.EventStream
+  ( noteCategory
+  , noteCommandCategory
+  , noteEventStream
+  , noteEventStreamDef
+  , NoteEventStream
+  , NoteEventStreamDef
+  ) where
 
-import Generated.ReplayDivergence.Note.Codec (noteCodec)
 import Generated.ReplayDivergence.Note.Domain
+import Generated.ReplayDivergence.Note.Codec (noteCodec)
+import Generated.ReplayDivergence.Note.Transducer (noteFoldFingerprint, noteTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import ReplayDivergence.Note.Holes (noteTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-noteCategory :: Stream.StreamCategory a
+noteCategory :: Stream.StreamCategory NoteEventStreamDef
 noteCategory = Stream.categoryUnsafe "note"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+noteCommandCategory :: Stream.StreamCategory NoteCommand
+noteCommandCategory = Stream.categoryUnsafe "note"
+
 type NoteEventStreamDef =
-    EventStream (HsPred NoteRegs NoteCommand) NoteRegs NoteVertex NoteCommand NoteEvent
+  EventStream (HsPred NoteRegs NoteCommand) NoteRegs NoteVertex NoteCommand NoteEvent
 
 type NoteEventStream =
-    ValidatedEventStream (HsPred NoteRegs NoteCommand) NoteRegs NoteVertex NoteCommand NoteEvent
+  ValidatedEventStream (HsPred NoteRegs NoteCommand) NoteRegs NoteVertex NoteCommand NoteEvent
 
 noteEventStreamDef :: NoteEventStreamDef
 noteEventStreamDef =
-    EventStream
-        { transducer = noteTransducer
-        , initialState = NoteEmpty
-        , initialRegisters = initialNoteRegs
-        , eventCodec = noteCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = noteTransducer,
+      initialState = NoteEmpty,
+      initialRegisters = initialNoteRegs,
+      eventCodec = noteCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 noteEventStream :: NoteEventStream
 noteEventStream =
-    mkEventStreamOrThrow "Note" noteEventStreamDef
+  mkEventStreamOrThrow "Note" noteEventStreamDef
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/Harness.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/Harness.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/Note/Harness.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/Harness.hs
@@ -1,27 +1,25 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
 module Generated.ReplayDivergence.Note.Harness (harnessAssertions) where
 
-import Generated.ReplayDivergence.Note.Codec (encodeNoteEvent, noteCodec, parseNoteEvent)
 import Generated.ReplayDivergence.Note.Domain
+import Generated.ReplayDivergence.Note.Codec (encodeNoteEvent, parseNoteEvent, noteCodec)
+import Generated.ReplayDivergence.Note.Transducer (noteTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
-import ReplayDivergence.Note.Holes (noteTransducer)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions noteTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: NoteWritten", roundTrips sampleEventNoteWritten)
-    , ("accepts WriteNote from NoteEmpty", acceptWriteNote)
-    ]
-        ++ forwardReplayWriteNote
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions noteTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: NoteWritten", roundTrips sampleEventNoteWritten)
+  , ("accepts WriteNote from NoteEmpty", acceptWriteNote)
+  ]
+  ++ forwardReplayWriteNote
 
 roundTrips :: NoteEvent -> Bool
 roundTrips e = parseNoteEvent (eventType noteCodec e) (encodeNoteEvent e) == Right e
@@ -31,25 +29,25 @@
 
 acceptWriteNote :: Bool
 acceptWriteNote =
-    case step noteTransducer (NoteEmpty, initialNoteRegs) ((WriteNote (WriteNoteData "sample-noteText" "sample-echo"))) of
-        Just (v, _, _) -> v == NoteRecorded
-        Nothing -> False
+  case step noteTransducer (NoteEmpty, initialNoteRegs) ((WriteNote (WriteNoteData "sample-noteText" "sample-echo"))) of
+    Just (v, _, _) -> v == NoteRecorded
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayWriteNote :: [(String, Bool)]
 forwardReplayWriteNote =
-    case step noteTransducer (NoteEmpty, initialNoteRegs) ((WriteNote (WriteNoteData "sample-noteText" "sample-echo"))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, forwardRegs, emitted) ->
-            case mapM (\event -> parseNoteEvent (eventType noteCodec event) (encodeNoteEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither noteTransducer (NoteEmpty, initialNoteRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            , (prefix <> "register note", (replayRegs ! #note) == (forwardRegs ! #note))
-                            ]
+  case step noteTransducer (NoteEmpty, initialNoteRegs) ((WriteNote (WriteNoteData "sample-noteText" "sample-echo"))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, forwardRegs, emitted) ->
+      case mapM (\event -> parseNoteEvent (eventType noteCodec event) (encodeNoteEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither noteTransducer (NoteEmpty, initialNoteRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              , (prefix <> "register note", (replayRegs ! #note) == (forwardRegs ! #note))
+              ]
   where
     prefix = "forward/replay equality: WriteNote from NoteEmpty -- "
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/Projection.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/Projection.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/Note/Projection.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
 module Generated.ReplayDivergence.Note.Projection () where
diff --git a/test/conformance-replay/Generated/ReplayDivergence/Note/Transducer.hs b/test/conformance-replay/Generated/ReplayDivergence/Note/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-replay/Generated/ReplayDivergence/Note/Transducer.hs
@@ -0,0 +1,61 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Note; do not edit.
+module Generated.ReplayDivergence.Note.Transducer
+  ( noteTransducer
+  , noteFoldFingerprint
+  , BehaviorOwnership (..)
+  , notePredicateVerifications
+  ) where
+
+import Generated.ReplayDivergence.Note.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import Keiki.Builder ((=:))
+
+noteTransducer
+  :: SymTransducer
+       (HsPred NoteRegs NoteCommand)
+       NoteRegs
+       NoteVertex
+       NoteCommand
+       NoteEvent
+noteTransducer =
+  B.buildTransducer NoteEmpty initialNoteRegs isTerminal do
+    B.from NoteEmpty do
+      B.onCmd inCtorWriteNote $ \d -> B.do
+        B.slot @"note" =: d.noteText
+        B.emit wireNoteWritten (NoteWrittenTermFields
+          { noteText = d.noteText
+          , echo = d.echo
+          })
+        B.goto NoteRecorded
+ where
+  isTerminal = \case
+    NoteRecorded -> True
+    _ -> False
+
+noteFoldFingerprint :: Text
+noteFoldFingerprint = "54c4c90b6f56cfffee8fd8d05b8917d4"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+notePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+notePredicateVerifications = sequence
+  [ verifyTransition "transition1EmptyWriteNote" GeneratedOwned NoteEmpty 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut noteTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-replay/Generated/ReplayDivergence/ReplayAudit.hs b/test/conformance-replay/Generated/ReplayDivergence/ReplayAudit.hs
--- a/test/conformance-replay/Generated/ReplayDivergence/ReplayAudit.hs
+++ b/test/conformance-replay/Generated/ReplayDivergence/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context replay-divergence replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Note.noteEventStream
-            , category = Stream.categoryText Note.noteCategory
-            , mkStream = streamInCategory (Stream.categoryText Note.noteCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Note.noteEventStream
+        , category = Stream.categoryText Note.noteCategory
+        , mkStream = streamInCategory (Stream.categoryText Note.noteCategory)
+        }
+  ]
diff --git a/test/conformance-router-full/Generated/IncidentPaging/Page/Codec.hs b/test/conformance-router-full/Generated/IncidentPaging/Page/Codec.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/Page/Codec.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/Page/Codec.hs
@@ -1,49 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module Generated.IncidentPaging.Page.Codec (
     pageCodec,
     parsePageEvent,
     encodePageEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.IncidentPaging.Page.Domain
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.IncidentPaging.Page.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
+
+
+
+
 pageCodec :: Codec PageEvent
 pageCodec =
-    Codec
-        { eventTypes = EventType "PageSent" :| []
-        , eventType = \case
-            PageSent{} -> EventType "PageSent"
-        , schemaVersion = 1
-        , encode = encodePageEvent
-        , decode = parsePageEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "PageSent" :| []
+    , eventType = \case
+        PageSent{} -> EventType "PageSent"
+    , schemaVersion = 1
+    , encode = encodePageEvent
+    , decode = parsePageEvent
+    , upcasters = []
+    }
 
 encodePageEvent :: PageEvent -> Value
 encodePageEvent = \case
-    PageSent payload ->
-        object
-            [ "kind" .= ("PageSent" :: Text)
-            , "incidentId" .= payload.incidentId
-            , "responderId" .= payload.responderId
-            ]
+  PageSent payload ->
+    object
+      [ "kind" .= ("PageSent" :: Text)
+      , "incidentId" .= payload.incidentId
+      , "responderId" .= payload.responderId
+      ]
 
 parsePageEvent :: EventType -> Value -> Either Text PageEvent
 parsePageEvent (EventType tag) = mapLeftText . parseEither (withObject "PageEvent" go)
   where
     go o = do
-        case tag of
-            "PageSent" ->
-                PageSent <$> (PageSentData <$> o .: "incidentId" <*> o .: "responderId")
-            _ -> fail "unknown event type"
+      case tag of
+        "PageSent" ->
+          PageSent
+            <$> ( PageSentData
+                    <$> o .: "incidentId"
+                    <*> o .: "responderId"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: PageSent")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-router-full/Generated/IncidentPaging/Page/Domain.hs b/test/conformance-router-full/Generated/IncidentPaging/Page/Domain.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/Page/Domain.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/Page/Domain.hs
@@ -2,9 +2,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module Generated.IncidentPaging.Page.Domain where
 
 import Data.Proxy (Proxy (..))
@@ -14,33 +12,35 @@
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data PageVertex = PagePending | PageDelivered
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
 data SendPageData = SendPageData
-    { incidentId :: !Text
-    , responderId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { incidentId :: !Text
+  , responderId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data PageCommand = SendPage !SendPageData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data PageSentData = PageSentData
-    { incidentId :: !Text
-    , responderId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { incidentId :: !Text
+  , responderId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data PageEvent = PageSent !PageSentData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 type PageRegs =
-    '[]
+  '[]
 
 initialPageRegs :: RegFile PageRegs
 initialPageRegs =
-    RNil
+  RNil
 
 $(deriveAggregateCtorsAll ''PageCommand ''PageRegs)
+
+
 
 $(deriveWireCtorsAll ''PageEvent)
diff --git a/test/conformance-router-full/Generated/IncidentPaging/Page/EventStream.hs b/test/conformance-router-full/Generated/IncidentPaging/Page/EventStream.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/Page/EventStream.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/Page/EventStream.hs
@@ -1,15 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.IncidentPaging.Page.EventStream (
-    pageCategory,
-    pageEventStream,
-    pageEventStreamDef,
-    PageEventStream,
-    PageEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
+module Generated.IncidentPaging.Page.EventStream
+  ( pageCategory
+  , pageCommandCategory
+  , pageEventStream
+  , pageEventStreamDef
+  , PageEventStream
+  , PageEventStreamDef
+  ) where
 
-import Generated.IncidentPaging.Page.Codec (pageCodec)
 import Generated.IncidentPaging.Page.Domain
-import IncidentPaging.Page.Holes (pageTransducer)
+import Generated.IncidentPaging.Page.Codec (pageCodec)
+import Generated.IncidentPaging.Page.Transducer (pageFoldFingerprint, pageTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,27 +19,31 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-pageCategory :: Stream.StreamCategory a
+pageCategory :: Stream.StreamCategory PageEventStreamDef
 pageCategory = Stream.categoryUnsafe "page"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+pageCommandCategory :: Stream.StreamCategory PageCommand
+pageCommandCategory = Stream.categoryUnsafe "page"
+
 type PageEventStreamDef =
-    EventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
+  EventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
 
 type PageEventStream =
-    ValidatedEventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
+  ValidatedEventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
 
 pageEventStreamDef :: PageEventStreamDef
 pageEventStreamDef =
-    EventStream
-        { transducer = pageTransducer
-        , initialState = PagePending
-        , initialRegisters = initialPageRegs
-        , eventCodec = pageCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = pageTransducer,
+      initialState = PagePending,
+      initialRegisters = initialPageRegs,
+      eventCodec = pageCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 pageEventStream :: PageEventStream
 pageEventStream =
-    mkEventStreamOrThrow "Page" pageEventStreamDef
+  mkEventStreamOrThrow "Page" pageEventStreamDef
diff --git a/test/conformance-router-full/Generated/IncidentPaging/Page/Transducer.hs b/test/conformance-router-full/Generated/IncidentPaging/Page/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-router-full/Generated/IncidentPaging/Page/Transducer.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
+module Generated.IncidentPaging.Page.Transducer
+  ( pageTransducer
+  , pageFoldFingerprint
+  , BehaviorOwnership (..)
+  , pagePredicateVerifications
+  ) where
+
+import Generated.IncidentPaging.Page.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+pageTransducer
+  :: SymTransducer
+       (HsPred PageRegs PageCommand)
+       PageRegs
+       PageVertex
+       PageCommand
+       PageEvent
+pageTransducer =
+  B.buildTransducer PagePending initialPageRegs isTerminal do
+    B.from PagePending do
+      B.onCmd inCtorSendPage $ \d -> B.do
+        B.emit wirePageSent (PageSentTermFields
+          { incidentId = d.incidentId
+          , responderId = d.responderId
+          })
+        B.goto PageDelivered
+ where
+  isTerminal = \case
+    PageDelivered -> True
+    _ -> False
+
+pageFoldFingerprint :: Text
+pageFoldFingerprint = "32a87df625245b05c0307606293a5650"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+pagePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+pagePredicateVerifications = sequence
+  [ verifyTransition "transition1PendingSendPage" GeneratedOwned PagePending 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut pageTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/Router.hs b/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/Router.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/Router.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/Router.hs
@@ -1,9 +1,8 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.IncidentPaging.PagingRouter.Router (
-    pagingRouterName,
-    pagingRouterWorkerOptions,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
+module Generated.IncidentPaging.PagingRouter.Router
+  ( pagingRouterName
+  , pagingRouterWorkerOptions
+  ) where
 
 import Data.Text (Text)
 import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
@@ -21,9 +20,9 @@
 -- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions.
 pagingRouterWorkerOptions :: WorkerOptions es msg
 pagingRouterWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedDeadLetter
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedDeadLetter,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
diff --git a/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/RouterHarness.hs b/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
@@ -1,16 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
 module Generated.IncidentPaging.PagingRouter.RouterHarness (routerHarnessValues) where
 
 routerHarnessValues :: [(String, String)]
 routerHarnessValues =
-    [ ("routerName", "jitsurei-paging")
-    , ("keyField", "incidentId")
-    , ("resolveSource", "read-model service_oncall")
-    , ("resolveRow", "responderId")
-    , ("dispatchCommand", "SendPage")
-    , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
-    , ("onDuplicate", "AckOk")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "deadLetter")
-    , ("poisonPolicy", "halt")
-    ]
+  [ ("routerName", "jitsurei-paging")
+  , ("keyField", "incidentId")
+  , ("resolveSource", "read-model service_oncall")
+  , ("resolveRow", "responderId")
+  , ("dispatchCommand", "SendPage")
+  , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
+  , ("onDuplicate", "AckOk")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "deadLetter")
+  , ("poisonPolicy", "halt")
+  ]
diff --git a/test/conformance-router-full/Generated/IncidentPaging/ReplayAudit.hs b/test/conformance-router-full/Generated/IncidentPaging/ReplayAudit.hs
--- a/test/conformance-router-full/Generated/IncidentPaging/ReplayAudit.hs
+++ b/test/conformance-router-full/Generated/IncidentPaging/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context incident-paging replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Page.pageEventStream
-            , category = Stream.categoryText Page.pageCategory
-            , mkStream = streamInCategory (Stream.categoryText Page.pageCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Page.pageEventStream
+        , category = Stream.categoryText Page.pageCategory
+        , mkStream = streamInCategory (Stream.categoryText Page.pageCategory)
+        }
+  ]
diff --git a/test/conformance-router-full/IncidentPaging/PagingRouter/RouterValue.hs b/test/conformance-router-full/IncidentPaging/PagingRouter/RouterValue.hs
--- a/test/conformance-router-full/IncidentPaging/PagingRouter/RouterValue.hs
+++ b/test/conformance-router-full/IncidentPaging/PagingRouter/RouterValue.hs
@@ -11,7 +11,7 @@
 import Data.Text (Text)
 import Effectful (Eff)
 import Generated.IncidentPaging.Page.Domain qualified as Page
-import Generated.IncidentPaging.Page.EventStream (pageCategory, pageEventStream)
+import Generated.IncidentPaging.Page.EventStream (pageCommandCategory, pageEventStream)
 import Generated.IncidentPaging.PagingRouter.Router (pagingRouterName)
 import Keiki.Core (HsPred)
 import Keiro.ProcessManager (PMCommand (..))
@@ -28,11 +28,11 @@
 resolveTargets input =
     pure
         [ PMCommand
-            { target = entityStream pageCategory (input.incidentId <> "-responder-a")
+            { target = entityStream pageCommandCategory (input.incidentId <> "-responder-a")
             , command = Page.SendPage (Page.SendPageData input.incidentId "responder-a")
             }
         , PMCommand
-            { target = entityStream pageCategory (input.incidentId <> "-responder-b")
+            { target = entityStream pageCommandCategory (input.incidentId <> "-responder-b")
             , command = Page.SendPage (Page.SendPageData input.incidentId "responder-b")
             }
         ]
diff --git a/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/Router.hs b/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/Router.hs
--- a/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/Router.hs
+++ b/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/Router.hs
@@ -1,9 +1,8 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.IncidentPaging.PagingRouter.Router (
-    pagingRouterName,
-    pagingRouterWorkerOptions,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
+module Generated.IncidentPaging.PagingRouter.Router
+  ( pagingRouterName
+  , pagingRouterWorkerOptions
+  ) where
 
 import Data.Text (Text)
 import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
@@ -21,9 +20,9 @@
 -- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions.
 pagingRouterWorkerOptions :: WorkerOptions es msg
 pagingRouterWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedDeadLetter
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedDeadLetter,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
diff --git a/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/RouterHarness.hs b/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
--- a/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
+++ b/test/conformance-router-runtime/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
@@ -1,16 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
 module Generated.IncidentPaging.PagingRouter.RouterHarness (routerHarnessValues) where
 
 routerHarnessValues :: [(String, String)]
 routerHarnessValues =
-    [ ("routerName", "jitsurei-paging")
-    , ("keyField", "incidentId")
-    , ("resolveSource", "read-model service_oncall")
-    , ("resolveRow", "responderId")
-    , ("dispatchCommand", "SendPage")
-    , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
-    , ("onDuplicate", "AckOk")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "deadLetter")
-    , ("poisonPolicy", "halt")
-    ]
+  [ ("routerName", "jitsurei-paging")
+  , ("keyField", "incidentId")
+  , ("resolveSource", "read-model service_oncall")
+  , ("resolveRow", "responderId")
+  , ("dispatchCommand", "SendPage")
+  , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
+  , ("onDuplicate", "AckOk")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "deadLetter")
+  , ("poisonPolicy", "halt")
+  ]
diff --git a/test/conformance-router/Generated/IncidentPaging/PagingRouter/RouterHarness.hs b/test/conformance-router/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
--- a/test/conformance-router/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
+++ b/test/conformance-router/Generated/IncidentPaging/PagingRouter/RouterHarness.hs
@@ -1,16 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
 module Generated.IncidentPaging.PagingRouter.RouterHarness (routerHarnessValues) where
 
 routerHarnessValues :: [(String, String)]
 routerHarnessValues =
-    [ ("routerName", "jitsurei-paging")
-    , ("keyField", "incidentId")
-    , ("resolveSource", "read-model service_oncall")
-    , ("resolveRow", "responderId")
-    , ("dispatchCommand", "SendPage")
-    , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
-    , ("onDuplicate", "AckOk")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "deadLetter")
-    , ("poisonPolicy", "halt")
-    ]
+  [ ("routerName", "jitsurei-paging")
+  , ("keyField", "incidentId")
+  , ("resolveSource", "read-model service_oncall")
+  , ("resolveRow", "responderId")
+  , ("dispatchCommand", "SendPage")
+  , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
+  , ("onDuplicate", "AckOk")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "deadLetter")
+  , ("poisonPolicy", "halt")
+  ]
diff --git a/test/conformance-scalar-expressions/AggregateScalarExpressions/ScalarAccount/BehaviorHoles.hs b/test/conformance-scalar-expressions/AggregateScalarExpressions/ScalarAccount/BehaviorHoles.hs
--- a/test/conformance-scalar-expressions/AggregateScalarExpressions/ScalarAccount/BehaviorHoles.hs
+++ b/test/conformance-scalar-expressions/AggregateScalarExpressions/ScalarAccount/BehaviorHoles.hs
@@ -5,7 +5,7 @@
 
 behaviorWitnesses :: [BehaviorWitness]
 behaviorWitnesses =
-  [ Pending (BehaviorKey "behavior-v1-78f2c698e2ec1dc0"),
+  [ Pending (BehaviorKey "behavior-v1-e9c863d5daf20011"),
     Pending (BehaviorKey "behavior-v1-1de91883b9e3f8bc"),
     Pending (BehaviorKey "behavior-v1-5c58e0aafeb5f400"),
     Pending (BehaviorKey "behavior-v1-a19a8e94d935493f"),
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals.hs
@@ -4,16 +4,29 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.AggregateScalarExpressions.Nominals where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context aggregate-scalar-expressions generated nominal declarations; do not edit.
+module Generated.AggregateScalarExpressions.Nominals
+  ( AccountMode (..)
+  , accountModeText
+  , AccountModeEqualityProjection
+  , accountModeEqualityWitness
+  , RequestId
+  , parseRequestId
+  , mkRequestId
+  , requestIdText
+  , RequestIdEqualityProjection
+  , requestIdEqualityWitness
+  ) where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Shape (CanonicalTypeName)
+import Generated.AggregateScalarExpressions.Nominals.Internal (RequestId, mkRequestId, parseRequestId, requestIdText)
 import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
 import Data.List.NonEmpty (NonEmpty (..))
-import Keiki.ProjectionDomain (finiteProjectionDomain)
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 data AccountMode = Normal | Restricted
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
@@ -45,23 +58,23 @@
 accountModeEqualityWitness :: FieldWitness AccountModeEqualityProjection
 accountModeEqualityWitness = exactFieldWitness @AccountModeEqualityProjection
 
-newtype RequestId = RequestId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
-
 instance CanonicalTypeName RequestId
 
-requestIdText :: RequestId -> Text
-requestIdText (RequestId value) = value
-
 data RequestIdEqualityProjection
 
 instance FieldProjection RequestIdEqualityProjection where
   type FieldName RequestIdEqualityProjection = "RequestId"
   type FieldOwner RequestIdEqualityProjection = RequestId
   type FieldResult RequestIdEqualityProjection = Text
-  fieldShapeId _ = "nominal-equality|name=RequestId|contract=keiro-dsl/nominal-equality/1|key=Text|domain=legacy-unrestricted-text|owner=generated"
+  fieldShapeId _ = "nominal-equality|name=RequestId|contract=keiro-dsl/nominal-equality/2|key=Text|domain=typeid-v7-text:req:keiro-dsl/id-domain/typeid-v7/1|owner=generated"
   projectFieldValue _ = requestIdText
 
+requestIdIdDomainPattern :: TextPattern
+requestIdIdDomainPattern = either (error . show) id (idDomainTextPattern (typeIdV7Domain "req"))
+
+instance ExactFieldProjection RequestIdEqualityProjection where
+  fieldProjectionDomain _ = textProjectionDomain requestIdIdDomainPattern
+  reconstructFieldOwner _ = either (const Nothing) Just . parseRequestId
+
 requestIdEqualityWitness :: FieldWitness RequestIdEqualityProjection
-requestIdEqualityWitness = fieldWitness @RequestIdEqualityProjection
+requestIdEqualityWitness = exactFieldWitness @RequestIdEqualityProjection
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals/Internal.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Nominals/Internal.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context aggregate-scalar-expressions generated nominal ID internals; do not edit.
+module Generated.AggregateScalarExpressions.Nominals.Internal
+  ( RequestId
+  , parseRequestId
+  , mkRequestId
+  , requestIdText
+  , unsafeRequestIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype RequestId = RequestId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON RequestId where
+  toJSON = toJSON . requestIdText
+
+instance FromJSON RequestId where
+  parseJSON = withText "RequestId" (either (fail . T.unpack) pure . parseRequestId)
+
+parseRequestId :: Text -> Either Text RequestId
+parseRequestId input = case validateIdDomainText (typeIdV7Domain "req") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (RequestId input)
+
+mkRequestId :: Text -> Either Text RequestId
+mkRequestId = parseRequestId
+
+requestIdText :: RequestId -> Text
+requestIdText (RequestId value) = value
+
+unsafeRequestIdFromLegacyText :: Text -> RequestId
+unsafeRequestIdFromLegacyText = RequestId
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ReplayAudit.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ReplayAudit.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ReplayAudit.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context aggregate-scalar-expressions replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/BehaviorContract.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/BehaviorContract.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/BehaviorContract.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/BehaviorContract.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.BehaviorContract where
 
 import Generated.AggregateScalarExpressions.ScalarAccount.Codec (encodeScalarAccountEvent, parseScalarAccountEvent, scalarAccountCodec)
@@ -112,10 +112,10 @@
 behaviorRequirements =
   [ BehaviorRequirement (BehaviorKey "behavior-v1-1de91883b9e3f8bc") RequiredRejection GeneratedAuthoritative GuardNotApplicable ScalarAccountOpen "Close" Nothing Nothing [] 32
   , BehaviorRequirement (BehaviorKey "behavior-v1-5c58e0aafeb5f400") RequiredRejection GeneratedAuthoritative GuardNotApplicable ScalarAccountReviewed "Adjust" Nothing Nothing [] 32
-  , BehaviorRequirement (BehaviorKey "behavior-v1-78f2c698e2ec1dc0") LiveTransition GeneratedAuthoritative GuardUnknown ScalarAccountOpen "Adjust" (Just (K.EdgeRef ScalarAccountOpen 0)) (Just ScalarAccountReviewed) ["Adjusted"] 42
   , BehaviorRequirement (BehaviorKey "behavior-v1-a19a8e94d935493f") RequiredRejection GeneratedAuthoritative GuardNotApplicable ScalarAccountClosed "Adjust" Nothing Nothing [] 32
   , BehaviorRequirement (BehaviorKey "behavior-v1-b7db52f28632d8da") LiveTransition HoleWitnessed GuardTotal ScalarAccountReviewed "Close" (Just (K.EdgeRef ScalarAccountReviewed 0)) (Just ScalarAccountClosed) ["ClosedEvent"] 62
   , BehaviorRequirement (BehaviorKey "behavior-v1-dbf483c868dd6d34") RequiredRejection GeneratedAuthoritative GuardNotApplicable ScalarAccountClosed "Close" Nothing Nothing [] 32
+  , BehaviorRequirement (BehaviorKey "behavior-v1-e9c863d5daf20011") LiveTransition GeneratedAuthoritative GuardUnknown ScalarAccountOpen "Adjust" (Just (K.EdgeRef ScalarAccountOpen 0)) (Just ScalarAccountReviewed) ["Adjusted"] 42
   ]
 
 behaviorCoverageReport :: [BehaviorWitness] -> BehaviorConformanceReport
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Codec.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.Codec (
     scalarAccountCodec,
     parseScalarAccountEvent,
@@ -10,12 +10,13 @@
 ) where
 
 import Generated.AggregateScalarExpressions.ScalarAccount.Domain
-import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), accountModeText, RequestId (..), requestIdText)
+import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), accountModeText, RequestId, requestIdText)
+import Generated.AggregateScalarExpressions.Nominals.Internal (unsafeRequestIdFromLegacyText)
 import Control.Monad (unless)
 import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))
 import Data.Aeson.Key qualified as Key
 import Data.Aeson.KeyMap qualified as KeyMap
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
@@ -32,7 +33,7 @@
 parseAccountMode = \case
   "normal" -> pure Normal
   "restricted" -> pure Restricted
-  _ -> fail "unknown AccountMode"
+  tag -> fail ("unknown AccountMode " <> show tag <> "; expected one of: normal, restricted")
 
 encodeLimitsMapped :: ScalarExpressions.Domain.Limits -> Value
 encodeLimitsMapped = encodeLimitsShape . bindingToShape ScalarExpressions.Bindings.limitsBinding
@@ -54,8 +55,8 @@
 parseLimitsShape = withObject "LimitsShape" $ \objectValue -> do
   rejectUnknownFields "Limits" ["minimum", "ceiling"] objectValue
   Generated.AggregateScalarExpressions.Structural.Shape.Limits.Limits
-    <$> ((objectValue .: "minimum" :: Parser Value) >>= (parseJSON))
-    <*> ((objectValue .: "ceiling" :: Parser Value) >>= (parseJSON))
+    <$> explicitParseField (parseJSON) objectValue "minimum"
+    <*> explicitParseField (parseJSON) objectValue "ceiling"
 
 scalarAccountCodec :: Codec ScalarAccountEvent
 scalarAccountCodec =
@@ -97,10 +98,24 @@
     go o = do
       case tag of
         "Adjusted" ->
-          Adjusted <$> (AdjustedData <$> o .: "balance" <*> o .: "requested" <*> o .: "machine" <*> o .: "label" <*> o .: "active" <*> (o .: "mode" >>= parseAccountMode) <*> (RequestId <$> o .: "requestId") <*> o .: "observedAt" <*> (o .: "limits" >>= parseLimitsMapped))
+          Adjusted
+            <$> ( AdjustedData
+                    <$> o .: "balance"
+                    <*> o .: "requested"
+                    <*> o .: "machine"
+                    <*> o .: "label"
+                    <*> o .: "active"
+                    <*> explicitParseField (withText "AccountMode" parseAccountMode) o "mode"
+                    <*> (unsafeRequestIdFromLegacyText <$> o .: "requestId")
+                    <*> o .: "observedAt"
+                    <*> explicitParseField parseLimitsMapped o "limits"
+                )
         "ClosedEvent" ->
-          ClosedEvent <$> (ClosedEventData <$> o .: "balance")
-        _ -> fail "unknown event type"
+          ClosedEvent
+            <$> ( ClosedEventData
+                    <$> o .: "balance"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: Adjusted, ClosedEvent")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Domain.hs
@@ -3,8 +3,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
@@ -13,7 +12,7 @@
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
 import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
-import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId (..))
+import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId, parseRequestId)
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 import Numeric.Natural (Natural)
@@ -93,7 +92,7 @@
   RCons (Proxy @"label") "" $
   RCons (Proxy @"active") False $
   RCons (Proxy @"mode") Normal $
-  RCons (Proxy @"requestId") (RequestId "") $
+  RCons (Proxy @"requestId") (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
   RCons (Proxy @"openedAt") (UTCTime (fromGregorian 2026 1 1) (picosecondsToDiffTime 0)) $
   RCons (Proxy @"limits") ScalarExpressions.Bindings.initialLimits RNil
 
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/EventStream.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/EventStream.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/EventStream.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.EventStream
   ( scalarAccountCategory
+  , scalarAccountCommandCategory
   , scalarAccountEventStream
   , scalarAccountEventStreamDef
   , ScalarAccountEventStream
@@ -21,9 +22,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-scalarAccountCategory :: Stream.StreamCategory a
+scalarAccountCategory :: Stream.StreamCategory ScalarAccountEventStreamDef
 scalarAccountCategory = Stream.categoryUnsafe "scalarAccount"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+scalarAccountCommandCategory :: Stream.StreamCategory ScalarAccountCommand
+scalarAccountCommandCategory = Stream.categoryUnsafe "scalarAccount"
+
 type ScalarAccountEventStreamDef =
   EventStream (HsPred ScalarAccountRegs ScalarAccountCommand) ScalarAccountRegs ScalarAccountVertex ScalarAccountCommand ScalarAccountEvent
 
@@ -33,21 +38,21 @@
 scalarAccountEventStreamDef :: ScalarAccountEventStreamDef
 scalarAccountEventStreamDef =
   EventStream
-    { transducer = scalarAccountTransducer
-    , initialState = ScalarAccountOpen
-    , initialRegisters = initialScalarAccountRegs
-    , eventCodec = scalarAccountCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Every 1
-    -- The snapshot discriminator composes: the spec's state-codec version (bump it
-    -- in the spec's `state-codec version=` clause), keiki's register and
-    -- control-state shape hashes, and this fold fingerprint derived from the
-    -- spec's transition surface (guards, writes, emits, states, register
-    -- initials, referenced rules). Spec-visible fold changes invalidate old
-    -- snapshots automatically. Version-2 Hole-owned transitions additionally
-    -- compose their explicit hand-owned FoldVersion tokens here; bump the
-    -- corresponding token whenever that Hole behavior changes.
-    , stateCodec = Just (withFoldFingerprint scalarAccountFoldFingerprint (defaultStateCodec 1))
+    { transducer = scalarAccountTransducer,
+      initialState = ScalarAccountOpen,
+      initialRegisters = initialScalarAccountRegs,
+      eventCodec = scalarAccountCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Every 1,
+      -- The snapshot discriminator composes: the spec's state-codec version (bump it
+      -- in the spec's `state-codec version=` clause), keiki's register and
+      -- control-state shape hashes, and this fold fingerprint derived from the
+      -- spec's transition surface (guards, writes, emits, states, register
+      -- initials, referenced rules). Spec-visible fold changes invalidate old
+      -- snapshots automatically. Version-2 Hole-owned transitions additionally
+      -- compose their explicit hand-owned FoldVersion tokens here; bump the
+      -- corresponding token whenever that Hole behavior changes.
+      stateCodec = Just (withFoldFingerprint scalarAccountFoldFingerprint (defaultStateCodec 1))
     }
 
 scalarAccountSnapshotFixture :: (Int, Text)
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Expressions.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Expressions.hs
deleted file mode 100644
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Expressions.hs
+++ /dev/null
@@ -1,60 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.AggregateScalarExpressions.ScalarAccount.Expressions
-  ( transition1OpenAdjustGuard
-  , transition1OpenAdjustWriteBalance
-  , transition1OpenAdjustWriteReserved
-  , transition1OpenAdjustWriteMachine
-  , transition1OpenAdjustWriteLabel
-  , transition1OpenAdjustWriteActive
-  , transition1OpenAdjustWriteMode
-  , transition1OpenAdjustWriteRequestId
-  , transition1OpenAdjustWriteOpenedAt
-  , transition1OpenAdjustWriteLimits
-  ) where
-
-import Generated.AggregateScalarExpressions.ScalarAccount.Domain
-import Keiki.Builder qualified as B
-import Keiki.Core qualified as K
-import Keiki.Generics (RegFieldsOf)
-import Data.Text (Text)
-import Data.Time.Calendar (fromGregorian)
-import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)
-import Numeric.Natural (Natural)
-import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId (..))
-import Generated.AggregateScalarExpressions.StructuralProjections qualified as StructuralProjections
-import Generated.AggregateScalarExpressions.Nominals qualified as GeneratedNominals
-import ScalarExpressions.Domain qualified
-
-transition1OpenAdjustGuard :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.HsPred ScalarAccountRegs ScalarAccountCommand
-transition1OpenAdjustGuard d = K.PAnd (K.PAnd (K.PAnd (K.PAnd (K.PAnd (K.PAnd (K.PCmp K.CmpGe (K.tadd (d.balance) (B.reg @"balance")) (K.lit (-100 :: Integer))) (K.PCmp K.CmpLe (K.tadd (B.reg @"reserved") (d.requested)) (B.reg @"capacity"))) (K.PCmp K.CmpGe (d.observedAt) (B.reg @"openedAt"))) (K.PCmp K.CmpGe (K.inpProj StructuralProjections.structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness inCtorAdjust (#limits :: K.Index (RegFieldsOf AdjustData) ScalarExpressions.Domain.Limits)) (K.regProj StructuralProjections.structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness (#limits :: K.Index ScalarAccountRegs ScalarExpressions.Domain.Limits)))) (K.PEq (d.active) (K.lit False))) (K.PEq (K.inpProj GeneratedNominals.accountModeEqualityWitness inCtorAdjust (#mode :: K.Index (RegFieldsOf AdjustData) AccountMode)) (K.regProj GeneratedNominals.accountModeEqualityWitness (#mode :: K.Index ScalarAccountRegs AccountMode)))) (K.PEq (K.inpProj GeneratedNominals.requestIdEqualityWitness inCtorAdjust (#requestId :: K.Index (RegFieldsOf AdjustData) RequestId)) (K.regProj GeneratedNominals.requestIdEqualityWitness (#requestId :: K.Index ScalarAccountRegs RequestId)))
-
-transition1OpenAdjustWriteBalance :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Integer
-transition1OpenAdjustWriteBalance d = K.tadd (B.reg @"balance") (K.tmul (d.balance) (K.lit (2 :: Integer)))
-
-transition1OpenAdjustWriteReserved :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Natural
-transition1OpenAdjustWriteReserved d = K.tadd (B.reg @"reserved") (K.tsub (d.requested) (B.reg @"capacity"))
-
-transition1OpenAdjustWriteMachine :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Int
-transition1OpenAdjustWriteMachine d = K.lit (-7 :: Int)
-
-transition1OpenAdjustWriteLabel :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Text
-transition1OpenAdjustWriteLabel d = K.lit ("adjusted" :: Text)
-
-transition1OpenAdjustWriteActive :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Bool
-transition1OpenAdjustWriteActive d = K.lit True
-
-transition1OpenAdjustWriteMode :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) AccountMode
-transition1OpenAdjustWriteMode d = K.lit Restricted
-
-transition1OpenAdjustWriteRequestId :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) RequestId
-transition1OpenAdjustWriteRequestId d = K.lit (RequestId "req_00041061050r3gg28a1c60t3gf")
-
-transition1OpenAdjustWriteOpenedAt :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) UTCTime
-transition1OpenAdjustWriteOpenedAt d = K.lit (UTCTime (fromGregorian 2026 2 3) (picosecondsToDiffTime 14706000000000000))
-
-transition1OpenAdjustWriteLimits :: B.PayloadProj ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) -> K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) ScalarExpressions.Domain.Limits
-transition1OpenAdjustWriteLimits d = d.limits
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Harness.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Harness.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Harness.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Harness.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.Harness (harnessAssertions) where
 
 import Generated.AggregateScalarExpressions.ScalarAccount.Domain
@@ -9,7 +9,7 @@
 import Generated.AggregateScalarExpressions.ScalarAccount.Transducer (scalarAccountTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, fieldWitnessAgrees, (!))
 import Keiro.Codec (eventType)
-import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId (..))
+import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId, parseRequestId)
 import Data.Aeson qualified as Aeson
 import Data.Aeson.Key qualified as AesonKey
 import Data.Aeson.KeyMap qualified as AesonKeyMap
@@ -28,10 +28,9 @@
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime(..), picosecondsToDiffTime)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions scalarAccountTransducer))
@@ -47,14 +46,14 @@
 roundTrips e = parseScalarAccountEvent (eventType scalarAccountCodec e) (encodeScalarAccountEvent e) == Right e
 
 sampleEventAdjusted :: ScalarAccountEvent
-sampleEventAdjusted = (Adjusted (AdjustedData 0 0 0 "sample-label" False Normal (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))
+sampleEventAdjusted = (Adjusted (AdjustedData 0 0 0 "sample-label" False Normal (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))
 
 sampleEventClosedEvent :: ScalarAccountEvent
 sampleEventClosedEvent = (ClosedEvent (ClosedEventData 0))
 
 acceptAdjust :: Bool
 acceptAdjust =
-  case step scalarAccountTransducer (ScalarAccountOpen, initialScalarAccountRegs) ((Adjust (AdjustData 0 0 0 "sample-label" False Normal (RequestId "") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))) of
+  case step scalarAccountTransducer (ScalarAccountOpen, initialScalarAccountRegs) ((Adjust (AdjustData 0 0 0 "sample-label" False Normal (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))) of
     Just (v, _, _) -> v == ScalarAccountReviewed
     Nothing -> False
 
@@ -62,7 +61,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayAdjust :: [(String, Bool)]
 forwardReplayAdjust =
-  case step scalarAccountTransducer (ScalarAccountOpen, initialScalarAccountRegs) ((Adjust (AdjustData 0 0 0 "sample-label" False Normal (RequestId "") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))) of
+  case step scalarAccountTransducer (ScalarAccountOpen, initialScalarAccountRegs) ((Adjust (AdjustData 0 0 0 "sample-label" False Normal (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) (snd (NonEmpty.head (fixtureCases ScalarExpressions.Bindings.limitsCases)))))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseScalarAccountEvent (eventType scalarAccountCodec event) (encodeScalarAccountEvent event)) emitted of
@@ -120,7 +119,7 @@
 
 adjustedLimitsAssertions :: [(String, Bool)]
 adjustedLimitsAssertions =
-  [ ("mapped codec round-trip: Adjusted/limits/" <> T.unpack label, roundTrips (Adjusted (AdjustedData 0 0 0 "sample-label" False Normal (RequestId "sample") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) mappedValue)))
+  [ ("mapped codec round-trip: Adjusted/limits/" <> T.unpack label, roundTrips (Adjusted (AdjustedData 0 0 0 "sample-label" False Normal (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (UTCTime (fromGregorian 2026 1 2) (picosecondsToDiffTime 11045123456789012)) mappedValue)))
   | (label, mappedValue) <- NonEmpty.toList (fixtureCases ScalarExpressions.Bindings.limitsCases)
   ]
 
@@ -131,8 +130,8 @@
 
 structuralProjectionAssertions :: [(String, Bool)]
 structuralProjectionAssertions =
-  [ ("projection witness agreement: scalar-expressions.Limits.v1/ceiling", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67ZWitness (\referenceOwner -> Generated.AggregateScalarExpressions.Structural.Shape.Limits.ceiling (bindingToShape ScalarExpressions.Bindings.limitsBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases ScalarExpressions.Bindings.limitsCases)))
-  , ("projection witness agreement: scalar-expressions.Limits.v1/minimum", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness (\referenceOwner -> Generated.AggregateScalarExpressions.Structural.Shape.Limits.minimum (bindingToShape ScalarExpressions.Bindings.limitsBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases ScalarExpressions.Bindings.limitsCases)))
+  [ ("projection witness agreement: scalar-expressions.Limits.v1/ceiling", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.limitsCeilingWitness (\referenceOwner -> Generated.AggregateScalarExpressions.Structural.Shape.Limits.ceiling (bindingToShape ScalarExpressions.Bindings.limitsBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases ScalarExpressions.Bindings.limitsCases)))
+  , ("projection witness agreement: scalar-expressions.Limits.v1/minimum", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.limitsMinimumWitness (\referenceOwner -> Generated.AggregateScalarExpressions.Structural.Shape.Limits.minimum (bindingToShape ScalarExpressions.Bindings.limitsBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases ScalarExpressions.Bindings.limitsCases)))
   ]
 
 deleteObjectField :: T.Text -> Aeson.Value -> Aeson.Value
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Projection.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Projection.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Projection.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.Projection () where
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Transducer.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Transducer.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Transducer.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/ScalarAccount/Transducer.hs
@@ -2,9 +2,10 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ScalarAccount; do not edit.
 module Generated.AggregateScalarExpressions.ScalarAccount.Transducer
   ( scalarAccountTransducer
   , scalarAccountFoldFingerprint
@@ -14,14 +15,20 @@
 
 import Generated.AggregateScalarExpressions.ScalarAccount.Domain
 import Data.Text (Text)
+import Data.Time.Calendar (fromGregorian)
+import Data.Time.Clock (UTCTime (..), picosecondsToDiffTime)
+import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId, parseRequestId)
+import Generated.AggregateScalarExpressions.StructuralProjections qualified as StructuralProjections
+import Generated.AggregateScalarExpressions.Nominals qualified as GeneratedNominals
+import ScalarExpressions.Domain qualified
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core (HsPred, SymTransducer, (.*), (.+), (.-), (.==), (.<=), (.>=), (.&&))
 import Keiki.Core qualified as K
 import Keiki.Symbolic qualified as S
 import AggregateScalarExpressions.ScalarAccount.Holes qualified as Holes
-import Generated.AggregateScalarExpressions.ScalarAccount.Expressions qualified as Expressions
 import Data.Text qualified as T
 import Keiki.Builder ((=:))
+import Keiki.Generics (RegFieldsOf)
 import Keiro.Snapshot.Codec (FoldVersion (..))
 
 scalarAccountTransducer
@@ -35,16 +42,29 @@
   B.buildTransducer ScalarAccountOpen initialScalarAccountRegs isTerminal do
     B.from ScalarAccountOpen do
       B.onCmd inCtorAdjust $ \d -> B.do
-        B.requireGuard (Expressions.transition1OpenAdjustGuard d)
-        B.slot @"balance" =: Expressions.transition1OpenAdjustWriteBalance d
-        B.slot @"reserved" =: Expressions.transition1OpenAdjustWriteReserved d
-        B.slot @"machine" =: Expressions.transition1OpenAdjustWriteMachine d
-        B.slot @"label" =: Expressions.transition1OpenAdjustWriteLabel d
-        B.slot @"active" =: Expressions.transition1OpenAdjustWriteActive d
-        B.slot @"mode" =: Expressions.transition1OpenAdjustWriteMode d
-        B.slot @"requestId" =: Expressions.transition1OpenAdjustWriteRequestId d
-        B.slot @"openedAt" =: Expressions.transition1OpenAdjustWriteOpenedAt d
-        B.slot @"limits" =: Expressions.transition1OpenAdjustWriteLimits d
+        let commandLimitsMinimum = K.inpProj StructuralProjections.limitsMinimumWitness inCtorAdjust (#limits :: K.Index (RegFieldsOf AdjustData) ScalarExpressions.Domain.Limits)
+            registerLimitsMinimum = K.regProj StructuralProjections.limitsMinimumWitness (#limits :: K.Index ScalarAccountRegs ScalarExpressions.Domain.Limits)
+            commandMode = K.inpProj GeneratedNominals.accountModeEqualityWitness inCtorAdjust (#mode :: K.Index (RegFieldsOf AdjustData) AccountMode)
+            registerMode = K.regProj GeneratedNominals.accountModeEqualityWitness (#mode :: K.Index ScalarAccountRegs AccountMode)
+            commandRequestId = K.inpProj GeneratedNominals.requestIdEqualityWitness inCtorAdjust (#requestId :: K.Index (RegFieldsOf AdjustData) RequestId)
+            registerRequestId = K.regProj GeneratedNominals.requestIdEqualityWitness (#requestId :: K.Index ScalarAccountRegs RequestId)
+        B.requireGuard $
+          (((((d.balance .+ B.reg @"balance" .>= K.lit (-100 :: Integer)
+          .&& B.reg @"reserved" .+ d.requested .<= B.reg @"capacity")
+          .&& d.observedAt .>= B.reg @"openedAt")
+          .&& commandLimitsMinimum .>= registerLimitsMinimum)
+          .&& d.active .== K.lit False)
+          .&& commandMode .== registerMode)
+          .&& commandRequestId .== registerRequestId
+        B.slot @"balance" =: (B.reg @"balance" .+ d.balance .* K.lit (2 :: Integer))
+        B.slot @"reserved" =: (B.reg @"reserved" .+ (d.requested .- B.reg @"capacity"))
+        B.slot @"machine" =: K.lit (-7 :: Int)
+        B.slot @"label" =: K.lit ("adjusted" :: Text)
+        B.slot @"active" =: K.lit True
+        B.slot @"mode" =: K.lit Restricted
+        B.slot @"requestId" =: K.lit (case parseRequestId "req_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "validated ID literal failed to parse")
+        B.slot @"openedAt" =: K.lit (UTCTime (fromGregorian 2026 2 3) (picosecondsToDiffTime 14706000000000000))
+        B.slot @"limits" =: d.limits
         B.emit wireAdjusted (AdjustedTermFields
           { balance = d.balance
           , requested = d.requested
@@ -70,7 +90,7 @@
     _ -> False
 
 scalarAccountFoldFingerprint :: Text
-scalarAccountFoldFingerprint = T.intercalate "|" ("d0897c163c958108" : [foldToken Holes.transition2ReviewedCloseHoleFoldVersion] ) where foldToken (FoldVersion token) = T.pack (show (T.length token)) <> ":" <> token
+scalarAccountFoldFingerprint = T.intercalate "|" ("60f4f059f718b2ee2bca06360ea20221" : [foldToken Holes.transition2ReviewedCloseHoleFoldVersion] ) where foldToken (FoldVersion token) = T.pack (show (T.length token)) <> ":" <> token
 
 data BehaviorOwnership = GeneratedOwned | HoleOwned
   deriving stock (Eq, Show)
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Structural/Shape/Limits.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Structural/Shape/Limits.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Structural/Shape/Limits.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/Structural/Shape/Limits.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DuplicateRecordFields #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural Limits; do not edit.
 module Generated.AggregateScalarExpressions.Structural.Shape.Limits (LimitsShape (..)) where
 
 import GHC.Generics (Generic)
diff --git a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/StructuralProjections.hs b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/StructuralProjections.hs
--- a/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/StructuralProjections.hs
+++ b/test/conformance-scalar-expressions/Generated/AggregateScalarExpressions/StructuralProjections.hs
@@ -1,12 +1,12 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context aggregate-scalar-expressions mapped structural facade; do not edit.
 -- Equality witnesses are emitted for Text, Int, Bool, Natural, and UTCTime.
 -- Int, Natural, and UTCTime belong to Keiki's ordered subset.
 module Generated.AggregateScalarExpressions.StructuralProjections
-  ( structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67ZWitness
-  , structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness
+  ( limitsCeilingWitness
+  , limitsMinimumWitness
   ) where
 
 import Data.Text (Text)
@@ -18,26 +18,26 @@
 import ScalarExpressions.Bindings qualified
 import ScalarExpressions.Domain qualified
 
-data StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z
+data LimitsCeilingProjection
 
-instance FieldProjection StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z where
-  type FieldName StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z = "/ceiling"
-  type FieldOwner StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z = ScalarExpressions.Domain.Limits
-  type FieldResult StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z = Natural
+instance FieldProjection LimitsCeilingProjection where
+  type FieldName LimitsCeilingProjection = "/ceiling"
+  type FieldOwner LimitsCeilingProjection = ScalarExpressions.Domain.Limits
+  type FieldResult LimitsCeilingProjection = Natural
   fieldShapeId _ = "scalar-expressions.Limits.v1"
   projectFieldValue _ owner = Generated.AggregateScalarExpressions.Structural.Shape.Limits.ceiling (bindingToShape ScalarExpressions.Bindings.limitsBinding owner)
 
-structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67ZWitness :: FieldWitness StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z
-structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67ZWitness = fieldWitness @StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC63ZC65ZC69ZC6cZC69ZC6eZC67Z
+limitsCeilingWitness :: FieldWitness LimitsCeilingProjection
+limitsCeilingWitness = fieldWitness @LimitsCeilingProjection
 
-data StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ
+data LimitsMinimumProjection
 
-instance FieldProjection StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ where
-  type FieldName StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ = "/minimum"
-  type FieldOwner StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ = ScalarExpressions.Domain.Limits
-  type FieldResult StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ = Integer
+instance FieldProjection LimitsMinimumProjection where
+  type FieldName LimitsMinimumProjection = "/minimum"
+  type FieldOwner LimitsMinimumProjection = ScalarExpressions.Domain.Limits
+  type FieldResult LimitsMinimumProjection = Integer
   fieldShapeId _ = "scalar-expressions.Limits.v1"
   projectFieldValue _ owner = Generated.AggregateScalarExpressions.Structural.Shape.Limits.minimum (bindingToShape ScalarExpressions.Bindings.limitsBinding owner)
 
-structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness :: FieldWitness StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ
-structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness = fieldWitness @StructuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZ
+limitsMinimumWitness :: FieldWitness LimitsMinimumProjection
+limitsMinimumWitness = fieldWitness @LimitsMinimumProjection
diff --git a/test/conformance-scalar-expressions/Main.hs b/test/conformance-scalar-expressions/Main.hs
--- a/test/conformance-scalar-expressions/Main.hs
+++ b/test/conformance-scalar-expressions/Main.hs
@@ -1,5 +1,4 @@
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE OverloadedRecordDot #-}
 {-# LANGUAGE TypeApplications #-}
 
@@ -10,7 +9,7 @@
 import Data.Time.Calendar (fromGregorian)
 import Data.Time.Clock (UTCTime (..), secondsToDiffTime)
 import AggregateScalarExpressions.ScalarAccount.BehaviorHoles (behaviorWitnesses)
-import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId (..), accountModeEqualityWitness, requestIdEqualityWitness)
+import Generated.AggregateScalarExpressions.Nominals (AccountMode (..), RequestId, accountModeEqualityWitness, parseRequestId, requestIdEqualityWitness, requestIdText)
 import Generated.AggregateScalarExpressions.ScalarAccount.BehaviorContract qualified as Behavior
 import Generated.AggregateScalarExpressions.ScalarAccount.Codec (encodeScalarAccountEvent, parseScalarAccountEvent, scalarAccountCodec)
 import Generated.AggregateScalarExpressions.ScalarAccount.Domain
@@ -20,6 +19,7 @@
 import Generated.AggregateScalarExpressions.StructuralProjections qualified as StructuralProjections
 import Keiki.Core qualified as K
 import Keiki.Generics (RegFieldsOf)
+import Keiki.Render.Pretty qualified as Pretty
 import Keiki.Symbolic qualified as S
 import Keiro.Codec (eventType)
 import Keiro.EventStream (EventStream (..), StateCodec (..))
@@ -41,13 +41,14 @@
           <> [ ("finite scalar oracle agrees with generated execution", finiteOracleAgreement)
              , ("Natural 2 - 5 is total monus zero", naturalMonusExample)
              , ("Integer/Natural oracle, concrete terms, and symbolic formulas agree", arithmeticAgreement)
+             , ("Keiki 0.8 pretty predicate/update pin the authoritative tree", scalarTreeBaseline)
              , ("one-way projected scalar paths remain conservatively unverified", repeatedPathAgreement)
              , ("one-way generated projection and opaque Hole remain unverified", verificationReport == expectedVerificationReport)
              , ("same-declaration nominal guards take both concrete branches", nominalGuardBranches)
              , ("finite generated enum equality is exact symbolically", exactEnumProof)
-             , ("generated enum exactness laws hold and legacy ID claims no inverse", generatedNominalProjectionLaws)
-             , ("legacy Text-backed generated ID equality stays conservative symbolically", conservativeIdProof)
-             , ("Keiki 0.7 detailed step and replay attribute the generated edge", detailedAttributionAgreement)
+             , ("generated enum and TypeID exactness laws hold", generatedNominalProjectionLaws)
+             , ("generated TypeID equality is exact symbolically", conservativeIdProof)
+             , ("Keiki 0.8 detailed step and replay attribute the generated edge", detailedAttributionAgreement)
              , ("generated behavior contract reconciles its create-once pending rows", length (Behavior.reportPending behaviorReport) == 6 && null (Behavior.reportMissing behaviorReport))
              , ("Hole envelope preserves declared event and target", holeEnvelopeAgreement)
              , ("encoded replay and full replay after snapshot invalidation agree", fullReplayAgreement)
@@ -57,6 +58,16 @@
     putStrLn ((if passed then "PASS  " else "FAIL  ") <> label)
   unless (all snd checks) exitFailure
 
+scalarTreeBaseline :: Bool
+scalarTreeBaseline =
+  case K.edgesOut scalarAccountTransducer ScalarAccountOpen of
+    K.Edge predicate update _ _ _ : _ ->
+      Pretty.prettyPred predicate
+        == "(Adjust && (((((((Adjust.balance + balance) >= -100 && (reserved + Adjust.requested) <= capacity) && Adjust.observedAt >= openedAt) && Adjust.limits./minimum >= limits./minimum) && Adjust.active == False) && Adjust.mode.AccountMode == mode.AccountMode) && Adjust.requestId.RequestId == requestId.RequestId))"
+        && Pretty.prettyUpdate update
+          == "limits := Adjust.limits, openedAt := 2026-02-03 04:05:06 UTC, requestId := RequestId \"req_01h455vb4pex5vsknk084sn02q\", mode := Restricted, active := True, label := \"adjusted\", machine := -7, reserved := (reserved + (Adjust.requested - capacity)), balance := (balance + (Adjust.balance * 2)), (keep)"
+    [] -> False
+
 expectedVerificationReport :: [(T.Text, BehaviorOwnership, S.PredicateVerification)]
 expectedVerificationReport =
   [ ("transition1OpenAdjust", GeneratedOwned, S.UnverifiedOpaque)
@@ -82,7 +93,7 @@
       , label = "input-label"
       , active = activeValue
       , mode = Normal
-      , requestId = RequestId ""
+      , requestId = requestIdValue
       , observedAt = observedAtValue
       , limits = Domain.Limits minimumValue 13
       }
@@ -120,7 +131,7 @@
         && registers K.! #label == "adjusted"
         && registers K.! #active
         && registers K.! #mode == Restricted
-        && registers K.! #requestId == RequestId "req_00041061050r3gg28a1c60t3gf"
+        && registers K.! #requestId == requestIdValue
         && registers K.! #openedAt == writtenTime
         && registers K.! #limits == Domain.Limits minimumValue 13
         && events == expectedEvents
@@ -136,7 +147,7 @@
           , label = "input-label"
           , active = activeValue
           , mode = Normal
-          , requestId = RequestId ""
+          , requestId = requestIdValue
           , observedAt = observedAtValue
           , limits = Domain.Limits minimumValue 13
           }
@@ -157,7 +168,7 @@
 nominalGuardBranches =
   accepts (mkAdjust 0 2 False commandTime 0)
     && not (accepts (withMode Restricted (mkAdjust 0 2 False commandTime 0)))
-    && not (accepts (withRequestId (RequestId "req_other") (mkAdjust 0 2 False commandTime 0)))
+    && not (accepts (withRequestId otherRequestIdValue (mkAdjust 0 2 False commandTime 0)))
   where
     accepts command = case K.step scalarAccountTransducer (ScalarAccountOpen, initialScalarAccountRegs) command of
       Just {} -> True
@@ -177,14 +188,27 @@
 conservativeGeneratedIdProjection = do
   let projected = K.regProj requestIdEqualityWitness (#requestId :: K.Index ScalarAccountRegs RequestId)
       contradiction = K.PAnd (K.PEq projected (K.lit ("req_a" :: T.Text))) (K.PEq projected (K.lit ("req_b" :: T.Text)))
-  (== S.UnverifiedOpaque) <$> S.verifyPredicate contradiction
+  (== S.VerifiedUnsatisfiable) <$> S.verifyPredicate contradiction
 
 generatedNominalProjectionLaws :: Bool
 generatedNominalProjectionLaws =
   K.checkFieldProjectionOwner accountModeEqualityWitness Normal == Right ()
     && K.checkFieldProjectionKey accountModeEqualityWitness "restricted" == Right Restricted
-    && K.checkFieldProjectionOwner requestIdEqualityWitness (RequestId "") == Left K.ProjectionWitnessIsUnconstrained
+    && K.checkFieldProjectionOwner requestIdEqualityWitness requestIdValue == Right ()
+    && K.checkFieldProjectionKey requestIdEqualityWitness (requestIdText otherRequestIdValue) == Right otherRequestIdValue
 
+requestIdValue :: RequestId
+requestIdValue = checkedRequestId "req_01h455vb4pex5vsknk084sn02q"
+
+otherRequestIdValue :: RequestId
+otherRequestIdValue = checkedRequestId "req_01h455vb4pex5vsknk084sn02r"
+
+checkedRequestId :: T.Text -> RequestId
+checkedRequestId raw =
+  case parseRequestId raw of
+    Right parsed -> parsed
+    Left problem -> error (show problem)
+
 scalarArithmeticAgreement :: IO Bool
 scalarArithmeticAgreement = do
   integerResults <- forM [(left, right) | left <- [-4 .. 4], right <- [-4 .. 4]] $ \(left, right) -> do
@@ -214,7 +238,7 @@
   let projected :: K.Term ScalarAccountRegs ScalarAccountCommand (RegFieldsOf AdjustData) Integer
       projected =
         K.inpProj
-          StructuralProjections.structuralProjectionC4cZC69ZC6dZC69ZC74ZC73ZC2fZC6dZC69ZC6eZC69ZC6dZC75ZC6dZWitness
+          StructuralProjections.limitsMinimumWitness
           inCtorAdjust
           (#limits :: K.Index (RegFieldsOf AdjustData) Domain.Limits)
       contradiction :: K.HsPred ScalarAccountRegs ScalarAccountCommand
diff --git a/test/conformance-skeletons/Main.hs b/test/conformance-skeletons/Main.hs
--- a/test/conformance-skeletons/Main.hs
+++ b/test/conformance-skeletons/Main.hs
@@ -2,12 +2,12 @@
 
 import Control.Monad (unless)
 import SkelAggregate.Generated.MyService.Thing.Harness (harnessAssertions)
-import SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowFacts (workflowFacts)
+import SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowFacts (WorkflowFacts (workflowFactBody), workflowFacts)
 
 main :: IO ()
 main = do
     mapM_ assertHarness harnessAssertions
-    unless (not (null workflowFacts)) (fail "workflow skeleton emitted no facts")
+    unless (not (null (workflowFactBody workflowFacts))) (fail "workflow skeleton emitted no facts")
     putStrLn "PASS  every committed skeleton scaffold compiles"
     putStrLn "PASS  aggregate skeleton harness"
     putStrLn "PASS  workflow skeleton facts"
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals.hs
@@ -1,19 +1,19 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelAggregate.Generated.MyService.Nominals where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service generated nominal declarations; do not edit.
+module SkelAggregate.Generated.MyService.Nominals
+  ( ThingId
+  , parseThingId
+  , mkThingId
+  , thingIdText
+  ) where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Shape (CanonicalTypeName)
-
-newtype ThingId = ThingId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
+import SkelAggregate.Generated.MyService.Nominals.Internal (ThingId, mkThingId, parseThingId, thingIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 instance CanonicalTypeName ThingId
-
-thingIdText :: ThingId -> Text
-thingIdText (ThingId value) = value
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals/Internal.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Nominals/Internal.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service generated nominal ID internals; do not edit.
+module SkelAggregate.Generated.MyService.Nominals.Internal
+  ( ThingId
+  , parseThingId
+  , mkThingId
+  , thingIdText
+  , unsafeThingIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype ThingId = ThingId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON ThingId where
+  toJSON = toJSON . thingIdText
+
+instance FromJSON ThingId where
+  parseJSON = withText "ThingId" (either (fail . T.unpack) pure . parseThingId)
+
+parseThingId :: Text -> Either Text ThingId
+parseThingId input = case validateIdDomainText (typeIdV7Domain "thing") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (ThingId input)
+
+mkThingId :: Text -> Either Text ThingId
+mkThingId = parseThingId
+
+thingIdText :: ThingId -> Text
+thingIdText (ThingId value) = value
+
+unsafeThingIdFromLegacyText :: Text -> ThingId
+unsafeThingIdFromLegacyText = ThingId
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/ReplayAudit.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/ReplayAudit.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/ReplayAudit.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/BehaviorContract.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/BehaviorContract.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/BehaviorContract.hs
@@ -0,0 +1,281 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
+module SkelAggregate.Generated.MyService.Thing.BehaviorContract where
+
+import SkelAggregate.Generated.MyService.Thing.Codec (encodeThingEvent, parseThingEvent, thingCodec)
+import SkelAggregate.Generated.MyService.Thing.Domain
+import SkelAggregate.Generated.MyService.Thing.Transducer (thingTransducer)
+import Data.Aeson (ToJSON (..), object, (.=))
+import Data.List (sortOn)
+import Data.List.NonEmpty (NonEmpty)
+import Data.List.NonEmpty qualified as NonEmpty
+import Data.Map.Strict qualified as Map
+import Data.Text (Text)
+import Data.Text qualified as T
+import Keiki.Core qualified as K (EdgeMode (..), EdgeRef (..), RegFile, ReplayAttribution (..), ReplayEventSpan (..), ReplaySuccess (..), StepFailure (..), StepSuccess (..), applyEventsDetailedEither, stepDetailedEither, (!))
+import Keiro.Codec qualified as Codec (Codec (eventType), EventType (..))
+
+newtype BehaviorKey = BehaviorKey { unBehaviorKey :: Text }
+  deriving stock (Eq, Ord, Show)
+
+data ObligationKind = LiveTransition | RequiredRejection | ReplayTransition
+  deriving stock (Eq, Ord, Show)
+
+data EvidenceLevel = GeneratedAuthoritative | HoleWitnessed | LegacyRuntimeWitness
+  deriving stock (Eq, Ord, Show)
+
+data GuardCoverage = GuardTotal | GuardPartial | GuardUnknown | GuardNotApplicable
+  deriving stock (Eq, Ord, Show)
+
+data BehaviorRequirement = BehaviorRequirement
+  { requirementKey :: !BehaviorKey
+  , requirementKind :: !ObligationKind
+  , requirementEvidence :: !EvidenceLevel
+  , requirementGuardCoverage :: !GuardCoverage
+  , requirementSource :: !ThingVertex
+  , requirementCommandName :: !Text
+  , requirementExpectedEdge :: !(Maybe (K.EdgeRef ThingVertex))
+  , requirementTarget :: !(Maybe ThingVertex)
+  , requirementEventKinds :: ![Text]
+  , requirementLine :: !Int
+  }
+  deriving stock (Eq, Show)
+
+data RejectionClass = RejectNoOutgoingEdges | RejectNoMatchingEdge
+  deriving stock (Eq, Show)
+
+data LiveExpectation
+  = Emits (NonEmpty ThingEvent)
+  | Rejects RejectionClass
+  | NoOp
+  deriving stock (Eq, Show)
+
+data BehaviorWitness
+  = Pending BehaviorKey
+  | LiveWitness
+      { witnessKey :: BehaviorKey
+      , witnessHistory :: [ThingEvent]
+      , witnessCommand :: ThingCommand
+      , witnessExpected :: LiveExpectation
+      }
+  | ReplayWitness
+      { witnessKey :: BehaviorKey
+      , witnessHistoryPrefix :: [ThingEvent]
+      , witnessObservedChunk :: [ThingEvent]
+      }
+  deriving stock (Eq, Show)
+
+data BehaviorFailure = BehaviorFailure
+  { failureKey :: !BehaviorKey
+  , failureCode :: !Text
+  , failureDetail :: !Text
+  }
+  deriving stock (Eq, Show)
+
+instance ToJSON BehaviorFailure where
+  toJSON failure = object
+    [ "key" .= unBehaviorKey (failureKey failure)
+    , "code" .= failureCode failure
+    , "detail" .= failureDetail failure
+    ]
+
+data BehaviorConformanceReport = BehaviorConformanceReport
+  { reportRequired :: ![BehaviorKey]
+  , reportFilled :: ![BehaviorKey]
+  , reportPending :: ![BehaviorKey]
+  , reportMissing :: ![BehaviorKey]
+  , reportDuplicate :: ![BehaviorKey]
+  , reportStale :: ![BehaviorKey]
+  , reportFailed :: ![BehaviorFailure]
+  , reportVerified :: ![BehaviorKey]
+  , reportUnverified :: ![BehaviorKey]
+  }
+  deriving stock (Eq, Show)
+
+instance ToJSON BehaviorConformanceReport where
+  toJSON report = object
+    [ "schema" .= ("keiro/behavior-conformance/1" :: Text)
+    , "required" .= keyTexts (reportRequired report)
+    , "filled" .= keyTexts (reportFilled report)
+    , "pending" .= keyTexts (reportPending report)
+    , "missing" .= keyTexts (reportMissing report)
+    , "duplicate" .= keyTexts (reportDuplicate report)
+    , "stale" .= keyTexts (reportStale report)
+    , "failed" .= reportFailed report
+    , "verified" .= keyTexts (reportVerified report)
+    , "unverified" .= keyTexts (reportUnverified report)
+    ]
+
+behaviorRequirements :: [BehaviorRequirement]
+behaviorRequirements =
+  [ BehaviorRequirement (BehaviorKey "behavior-v1-9879aa7cdadb6004") RequiredRejection GeneratedAuthoritative GuardNotApplicable ThingDone "DoThing" Nothing Nothing [] 9
+  , BehaviorRequirement (BehaviorKey "behavior-v1-c1b356b00484077a") LiveTransition GeneratedAuthoritative GuardTotal ThingPending "DoThing" (Just (K.EdgeRef ThingPending 0)) (Just ThingDone) ["ThingCompleted"] 14
+  ]
+
+behaviorCoverageReport :: [BehaviorWitness] -> BehaviorConformanceReport
+behaviorCoverageReport witnesses =
+  BehaviorConformanceReport
+    { reportRequired = sortedKeys (Map.keys requiredByKey)
+    , reportFilled = sortedKeys [key | (key, [witness]) <- Map.toList witnessGroups, Map.member key requiredByKey, not (isPending witness)]
+    , reportPending = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, Map.member key requiredByKey, any isPending rows]
+    , reportMissing = sortedKeys [key | key <- Map.keys requiredByKey, Map.notMember key witnessGroups]
+    , reportDuplicate = sortedKeys [key | (key, rows) <- Map.toList witnessGroups, length rows > 1]
+    , reportStale = sortedKeys [key | key <- Map.keys witnessGroups, Map.notMember key requiredByKey]
+    , reportFailed = sortOn (unBehaviorKey . failureKey) failures
+    , reportVerified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, proofStrength requirement]
+    , reportUnverified = sortedKeys [requirementKey requirement | (requirement, Right ()) <- executions, not (proofStrength requirement)]
+    }
+ where
+  requiredByKey = Map.fromList [(requirementKey requirement, requirement) | requirement <- behaviorRequirements]
+  witnessGroups = Map.fromListWith (flip (<>)) [(behaviorWitnessKey witness, [witness]) | witness <- witnesses]
+  executions =
+    [ (requirement, runWitness requirement witness)
+    | (key, [witness]) <- Map.toList witnessGroups
+    , not (isPending witness)
+    , Just requirement <- [Map.lookup key requiredByKey]
+    ]
+  failures = [failure | (_, Left failure) <- executions]
+
+behaviorConformancePassed :: BehaviorConformanceReport -> Bool
+behaviorConformancePassed = behaviorConformancePassedWith False
+
+behaviorConformancePassedWith :: Bool -> BehaviorConformanceReport -> Bool
+behaviorConformancePassedWith failOnUnverified report =
+  null (reportPending report)
+    && null (reportMissing report)
+    && null (reportDuplicate report)
+    && null (reportStale report)
+    && null (reportFailed report)
+    && (not failOnUnverified || null (reportUnverified report))
+
+renderBehaviorConformanceText :: BehaviorConformanceReport -> Text
+renderBehaviorConformanceText report = T.unlines
+  [ "behavior conformance: Thing"
+  , "schema: keiro/behavior-conformance/1"
+  , countLine "required" (reportRequired report)
+  , countLine "filled" (reportFilled report)
+  , countLine "pending" (reportPending report)
+  , countLine "missing" (reportMissing report)
+  , countLine "duplicate" (reportDuplicate report)
+  , countLine "stale" (reportStale report)
+  , "failed: " <> tshow (length (reportFailed report))
+  , countLine "verified" (reportVerified report)
+  , countLine "unverified" (reportUnverified report)
+  ] <> T.unlines ["FAIL " <> unBehaviorKey (failureKey failure) <> " [" <> failureCode failure <> "] " <> failureDetail failure | failure <- reportFailed report]
+
+runWitness :: BehaviorRequirement -> BehaviorWitness -> Either BehaviorFailure ()
+runWitness requirement witness = case witness of
+  Pending _ -> failure requirement "pending" "witness is still Pending"
+  LiveWitness _ history command expectation -> runLive requirement history command expectation
+  ReplayWitness _ prefix chunk -> runReplay requirement prefix chunk
+
+runLive :: BehaviorRequirement -> [ThingEvent] -> ThingCommand -> LiveExpectation -> Either BehaviorFailure ()
+runLive requirement history command expectation = do
+  settled <- settleHistory requirement "history" history
+  ensure requirement (K.replaySuccessState settled == requirementSource requirement) "history-wrong-source" "history does not settle at the required source vertex"
+  ensure requirement (commandKind command == requirementCommandName requirement) "command-mismatch" "witness command constructor does not match the required state/command cell"
+  case requirementKind requirement of
+    ReplayTransition -> failure requirement "witness-kind" "a replay-only requirement needs ReplayWitness"
+    RequiredRejection -> runRejection requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation
+    LiveTransition -> runAcceptance requirement (K.replaySuccessState settled, K.replaySuccessRegs settled) command expectation
+
+runRejection requirement seed command expectation = case expectation of
+  Emits _ -> failure requirement "expectation-kind" "a rejection requirement cannot expect emitted events"
+  NoOp -> failure requirement "expectation-kind" "a rejection requirement cannot expect an accepted no-op"
+  Rejects expectedClass -> case K.stepDetailedEither thingTransducer seed command of
+    Left K.NoOutgoingEdges {} -> ensure requirement (expectedClass == RejectNoOutgoingEdges) "rejection-class" "expected NoMatchingEdge but runtime returned NoOutgoingEdges"
+    Left K.NoMatchingEdge {} -> ensure requirement (expectedClass == RejectNoMatchingEdge) "rejection-class" "expected NoOutgoingEdges but runtime returned NoMatchingEdge"
+    Left K.AmbiguousEdges {} -> failure requirement "ambiguous-edges" "AmbiguousEdges can never satisfy a rejection witness"
+    Right _ -> failure requirement "unexpected-acceptance" "runtime accepted a command required to reject"
+
+runAcceptance requirement seed command expectation = case expectation of
+  Rejects _ -> failure requirement "expectation-kind" "a live-transition requirement needs Emits or NoOp"
+  NoOp -> case K.stepDetailedEither thingTransducer seed command of
+    Left stepFailure -> failure requirement "unexpected-rejection" (tshow stepFailure)
+    Right success -> do
+      checkAcceptedEnvelope requirement success
+      ensure requirement (null (K.stepSuccessOutputs success)) "noop-emitted" "NoOp emitted one or more events"
+      ensure requirement (K.stepSuccessState success == fst seed) "noop-vertex-change" "NoOp changed the control vertex"
+      ensure requirement (regsEqual (K.stepSuccessRegs success) (snd seed)) "noop-register-change" "NoOp changed one or more registers"
+  Emits expectedEvents -> case K.stepDetailedEither thingTransducer seed command of
+    Left stepFailure -> failure requirement "unexpected-rejection" (tshow stepFailure)
+    Right success -> do
+      checkAcceptedEnvelope requirement success
+      let expected = NonEmpty.toList expectedEvents
+          actual = K.stepSuccessOutputs success
+      ensure requirement (actual == expected) "event-value-mismatch" "runtime event values differ from the exact witness expectation"
+      ensure requirement (map eventKind actual == requirementEventKinds requirement) "event-envelope-mismatch" "runtime event kinds differ from the declared ordered envelope"
+      decoded <- either (failure requirement "emitted-codec-decode") Right (decodeEvents actual)
+      replayed <- case K.applyEventsDetailedEither thingTransducer seed decoded of
+        Left replayFailure -> failure requirement "emitted-replay-failed" (tshow replayFailure)
+        Right replaySuccess -> Right replaySuccess
+      ensure requirement (K.replaySuccessState replayed == K.stepSuccessState success) "forward-replay-vertex" "decoded emissions replay to a different vertex"
+      ensure requirement (regsEqual (K.replaySuccessRegs replayed) (K.stepSuccessRegs success)) "forward-replay-registers" "decoded emissions replay to different registers"
+      checkSingleAttribution requirement K.Live (length decoded) (K.replaySuccessTrace replayed)
+
+checkAcceptedEnvelope requirement success = do
+  ensure requirement (K.stepSuccessMode success == K.Live) "forward-mode" "forward execution selected a non-live edge"
+  ensure requirement (Just (K.stepSuccessEdge success) == requirementExpectedEdge requirement) "edge-attribution" "runtime selected a different guarded sibling"
+  ensure requirement (Just (K.stepSuccessState success) == requirementTarget requirement) "target-mismatch" "runtime reached a different target vertex"
+
+runReplay :: BehaviorRequirement -> [ThingEvent] -> [ThingEvent] -> Either BehaviorFailure ()
+runReplay requirement prefix chunk = case requirementKind requirement of
+  ReplayTransition -> do
+    settled <- settleHistory requirement "history-prefix" prefix
+    ensure requirement (K.replaySuccessState settled == requirementSource requirement) "history-wrong-source" "history prefix does not settle at the replay edge source"
+    ensure requirement (not (null chunk)) "empty-replay-chunk" "a replay-only edge has no observable empty chunk"
+    decoded <- either (failure requirement "replay-chunk-codec-decode") Right (decodeEvents chunk)
+    replayed <- case K.applyEventsDetailedEither thingTransducer (K.replaySuccessState settled, K.replaySuccessRegs settled) decoded of
+      Left replayFailure -> failure requirement "replay-chunk-failed" (tshow replayFailure)
+      Right replaySuccess -> Right replaySuccess
+    ensure requirement (Just (K.replaySuccessState replayed) == requirementTarget requirement) "target-mismatch" "replay chunk reached a different target vertex"
+    checkSingleAttribution requirement K.ReplayOnly (length decoded) (K.replaySuccessTrace replayed)
+  _ -> failure requirement "witness-kind" "ReplayWitness supplied for a non-replay requirement"
+
+checkSingleAttribution requirement expectedMode eventCount trace = case trace of
+  [attribution] -> do
+    ensure requirement (Just (K.replayAttributionEdge attribution) == requirementExpectedEdge requirement) "replay-edge-attribution" "replay selected a different edge"
+    ensure requirement (K.replayAttributionMode attribution == expectedMode) "replay-mode-attribution" "replay selected the wrong live/replay-only phase"
+    ensure requirement (K.replayAttributionSource attribution == requirementSource requirement) "replay-source-attribution" "replay attribution starts at the wrong source"
+    ensure requirement (Just (K.replayAttributionTarget attribution) == requirementTarget requirement) "replay-target-attribution" "replay attribution ends at the wrong target"
+    ensure requirement (K.replayAttributionSpan attribution == K.ReplayEventSpan 0 eventCount) "replay-span-attribution" "replay attribution did not consume the exact chunk"
+  _ -> failure requirement "replay-trace-cardinality" "expected exactly one completed-edge attribution"
+
+settleHistory requirement label history = do
+  decoded <- either (failure requirement (label <> "-codec-decode")) Right (decodeEvents history)
+  case K.applyEventsDetailedEither thingTransducer (ThingPending, initialThingRegs) decoded of
+    Left replayFailure -> failure requirement (label <> "-replay-failed") (tshow replayFailure)
+    Right replaySuccess -> Right replaySuccess
+
+decodeEvents :: [ThingEvent] -> Either Text [ThingEvent]
+decodeEvents = traverse (\event -> parseThingEvent (Codec.eventType thingCodec event) (encodeThingEvent event))
+
+commandKind command = case command of
+  DoThing _ -> "DoThing"
+
+eventKind event = case Codec.eventType thingCodec event of Codec.EventType tag -> tag
+
+regsEqual :: K.RegFile ThingRegs -> K.RegFile ThingRegs -> Bool
+regsEqual left right = (left K.! #thingId) == (right K.! #thingId)
+
+proofStrength requirement =
+  requirementEvidence requirement == GeneratedAuthoritative
+    && requirementGuardCoverage requirement `elem` [GuardTotal, GuardNotApplicable]
+
+behaviorWitnessKey witness = case witness of
+  Pending key -> key
+  LiveWitness { witnessKey = key } -> key
+  ReplayWitness { witnessKey = key } -> key
+
+isPending Pending {} = True
+isPending _ = False
+
+ensure requirement condition code detail = if condition then Right () else failure requirement code detail
+failure requirement code detail = Left (BehaviorFailure (requirementKey requirement) code detail)
+sortedKeys = sortOn unBehaviorKey
+keyTexts = map unBehaviorKey
+countLine label values = label <> ": " <> tshow (length values)
+tshow :: Show value => value -> Text
+tshow = T.pack . show
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Codec.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Codec.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Codec.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Codec.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
 module SkelAggregate.Generated.MyService.Thing.Codec (
     thingCodec,
     parseThingEvent,
@@ -7,9 +7,10 @@
 ) where
 
 import SkelAggregate.Generated.MyService.Thing.Domain
-import SkelAggregate.Generated.MyService.Nominals (ThingId (..), thingIdText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import SkelAggregate.Generated.MyService.Nominals (ThingId, thingIdText)
+import SkelAggregate.Generated.MyService.Nominals.Internal (unsafeThingIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -46,8 +47,12 @@
     go o = do
       case tag of
         "ThingCompleted" ->
-          ThingCompleted <$> (ThingCompletedData <$> (ThingId <$> o .: "thingId") <*> o .: "attempt")
-        _ -> fail "unknown event type"
+          ThingCompleted
+            <$> ( ThingCompletedData
+                    <$> (unsafeThingIdFromLegacyText <$> o .: "thingId")
+                    <*> o .: "attempt"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: ThingCompleted")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Domain.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Domain.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Domain.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
 module SkelAggregate.Generated.MyService.Thing.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import SkelAggregate.Generated.MyService.Nominals (ThingId (..))
+import SkelAggregate.Generated.MyService.Nominals (ThingId, parseThingId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ThingVertex = ThingPending | ThingDone
@@ -36,13 +35,11 @@
 
 type ThingRegs =
   '[ '("thingId", ThingId)
-   , '("state", ThingVertex)
    ]
 
 initialThingRegs :: RegFile ThingRegs
 initialThingRegs =
-  RCons (Proxy @"thingId") (ThingId "") $
-  RCons (Proxy @"state") ThingPending RNil
+  RCons (Proxy @"thingId") (case parseThingId "thing_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RNil
 
 $(deriveAggregateCtorsAll ''ThingCommand ''ThingRegs)
 
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/EventStream.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/EventStream.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/EventStream.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
 module SkelAggregate.Generated.MyService.Thing.EventStream
   ( thingCategory
+  , thingCommandCategory
   , thingEventStream
   , thingEventStreamDef
   , ThingEventStream
@@ -9,7 +10,7 @@
 
 import SkelAggregate.Generated.MyService.Thing.Domain
 import SkelAggregate.Generated.MyService.Thing.Codec (thingCodec)
-import SkelAggregate.MyService.Thing.Holes (thingTransducer)
+import SkelAggregate.Generated.MyService.Thing.Transducer (thingFoldFingerprint, thingTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-thingCategory :: Stream.StreamCategory a
+thingCategory :: Stream.StreamCategory ThingEventStreamDef
 thingCategory = Stream.categoryUnsafe "thing"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+thingCommandCategory :: Stream.StreamCategory ThingCommand
+thingCommandCategory = Stream.categoryUnsafe "thing"
+
 type ThingEventStreamDef =
   EventStream (HsPred ThingRegs ThingCommand) ThingRegs ThingVertex ThingCommand ThingEvent
 
@@ -30,13 +35,13 @@
 thingEventStreamDef :: ThingEventStreamDef
 thingEventStreamDef =
   EventStream
-    { transducer = thingTransducer
-    , initialState = ThingPending
-    , initialRegisters = initialThingRegs
-    , eventCodec = thingCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = thingTransducer,
+      initialState = ThingPending,
+      initialRegisters = initialThingRegs,
+      eventCodec = thingCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 thingEventStream :: ThingEventStream
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Harness.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Harness.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Harness.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Harness.hs
@@ -1,19 +1,18 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
 module SkelAggregate.Generated.MyService.Thing.Harness (harnessAssertions) where
 
 import SkelAggregate.Generated.MyService.Thing.Domain
 import SkelAggregate.Generated.MyService.Thing.Codec (encodeThingEvent, parseThingEvent, thingCodec)
-import SkelAggregate.MyService.Thing.Holes (thingTransducer)
+import SkelAggregate.Generated.MyService.Thing.Transducer (thingTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
-import SkelAggregate.Generated.MyService.Nominals (ThingId (..))
+import SkelAggregate.Generated.MyService.Nominals (ThingId, parseThingId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions thingTransducer))
@@ -27,11 +26,11 @@
 roundTrips e = parseThingEvent (eventType thingCodec e) (encodeThingEvent e) == Right e
 
 sampleEventThingCompleted :: ThingEvent
-sampleEventThingCompleted = (ThingCompleted (ThingCompletedData (ThingId "sample") 0))
+sampleEventThingCompleted = (ThingCompleted (ThingCompletedData (case parseThingId "thing_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0))
 
 acceptDoThing :: Bool
 acceptDoThing =
-  case step thingTransducer (ThingPending, initialThingRegs) ((DoThing (DoThingData (ThingId "sample") 0))) of
+  case step thingTransducer (ThingPending, initialThingRegs) ((DoThing (DoThingData (case parseThingId "thing_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0))) of
     Just (v, _, _) -> v == ThingDone
     Nothing -> False
 
@@ -39,7 +38,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayDoThing :: [(String, Bool)]
 forwardReplayDoThing =
-  case step thingTransducer (ThingPending, initialThingRegs) ((DoThing (DoThingData (ThingId "sample") 0))) of
+  case step thingTransducer (ThingPending, initialThingRegs) ((DoThing (DoThingData (case parseThingId "thing_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseThingEvent (eventType thingCodec event) (encodeThingEvent event)) emitted of
@@ -50,7 +49,6 @@
             Right (replayVertex, replayRegs) ->
               [ (prefix <> "final vertex", replayVertex == forwardVertex)
               , (prefix <> "register thingId", (replayRegs ! #thingId) == (forwardRegs ! #thingId))
-              , (prefix <> "register state", (replayRegs ! #state) == (forwardRegs ! #state))
               ]
   where
     prefix = "forward/replay equality: DoThing from ThingPending -- "
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Projection.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Projection.hs
--- a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Projection.hs
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
 module SkelAggregate.Generated.MyService.Thing.Projection () where
diff --git a/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Transducer.hs b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelAggregate/Generated/MyService/Thing/Transducer.hs
@@ -0,0 +1,57 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Thing; do not edit.
+module SkelAggregate.Generated.MyService.Thing.Transducer
+  ( thingTransducer
+  , thingFoldFingerprint
+  , BehaviorOwnership (..)
+  , thingPredicateVerifications
+  ) where
+
+import SkelAggregate.Generated.MyService.Thing.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import SkelAggregate.MyService.Thing.Holes qualified as Holes
+
+thingTransducer
+  :: SymTransducer
+       (HsPred ThingRegs ThingCommand)
+       ThingRegs
+       ThingVertex
+       ThingCommand
+       ThingEvent
+thingTransducer =
+  B.buildTransducer ThingPending initialThingRegs isTerminal do
+    B.from ThingPending do
+      B.onCmd inCtorDoThing $ \d -> B.do
+        B.emit wireThingCompleted (Holes.transition1PendingDoThingOutput1ThingCompleted d)
+        B.goto ThingDone
+ where
+  isTerminal = \case
+    ThingDone -> True
+    _ -> False
+
+thingFoldFingerprint :: Text
+thingFoldFingerprint = "79409d44f75b30358aac38c78863ca4e"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+thingPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+thingPredicateVerifications = sequence
+  [ verifyTransition "transition1PendingDoThing" GeneratedOwned ThingPending 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut thingTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-skeletons/SkelAggregate/MyService/Thing/BehaviorHoles.hs b/test/conformance-skeletons/SkelAggregate/MyService/Thing/BehaviorHoles.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelAggregate/MyService/Thing/BehaviorHoles.hs
@@ -0,0 +1,10 @@
+-- Consumer-owned behavioral witnesses. Created once; never overwritten.
+module SkelAggregate.MyService.Thing.BehaviorHoles (behaviorWitnesses) where
+
+import SkelAggregate.Generated.MyService.Thing.BehaviorContract
+
+behaviorWitnesses :: [BehaviorWitness]
+behaviorWitnesses =
+  [ Pending (BehaviorKey "behavior-v1-9879aa7cdadb6004")
+  , Pending (BehaviorKey "behavior-v1-c1b356b00484077a")
+  ]
diff --git a/test/conformance-skeletons/SkelAggregate/MyService/Thing/Holes.hs b/test/conformance-skeletons/SkelAggregate/MyService/Thing/Holes.hs
--- a/test/conformance-skeletons/SkelAggregate/MyService/Thing/Holes.hs
+++ b/test/conformance-skeletons/SkelAggregate/MyService/Thing/Holes.hs
@@ -1,44 +1,22 @@
-{-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
--- overwrites it. Fill the transducer body (and any other holes) against the
--- generated signatures, then run the harness to confirm behaviour.
-module SkelAggregate.MyService.Thing.Holes (
-    thingTransducer,
-    -- (no projection)
-) where
+-- HAND-OWNED language-4 hook. The generated transducer owns the transition
+-- lifecycle; this module supplies only the explicit event-field mapping.
+module SkelAggregate.MyService.Thing.Holes
+  ( transition1PendingDoThingOutput1ThingCompleted
+  ) where
 
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, lit)
+import Keiki.Generics (RegFieldsOf)
 import SkelAggregate.Generated.MyService.Thing.Domain
 
--- HOLE: the transducer body. Reproduce the structure below, replacing each
--- `-- HOLE` line with the keiki symbolic operators it describes.
-thingTransducer ::
-    SymTransducer
-        (HsPred ThingRegs ThingCommand)
-        ThingRegs
-        ThingVertex
-        ThingCommand
-        ThingEvent
-thingTransducer =
-    B.buildTransducer ThingPending initialThingRegs isTerminal do
-        B.from ThingPending do
-            B.onCmd inCtorDoThing $ \d -> B.do
-                B.slot @"state" =: lit ThingDone
-                B.emit
-                    wireThingCompleted
-                    ThingCompletedTermFields
-                        { thingId = d.thingId
-                        , attempt = d.attempt
-                        }
-                B.goto ThingDone
-  where
-    isTerminal = \case
-        ThingDone -> True
-        _ -> False
+transition1PendingDoThingOutput1ThingCompleted
+  :: B.PayloadProj ThingRegs ThingCommand (RegFieldsOf DoThingData)
+  -> ThingCompletedTermFields ThingRegs ThingCommand (RegFieldsOf DoThingData)
+transition1PendingDoThingOutput1ThingCompleted d =
+  ThingCompletedTermFields
+    { thingId = d.thingId
+    , attempt = d.attempt
+    }
diff --git a/test/conformance-skeletons/SkelContract/Generated/MyService/MyContract/Contract.hs b/test/conformance-skeletons/SkelContract/Generated/MyService/MyContract/Contract.hs
--- a/test/conformance-skeletons/SkelContract/Generated/MyService/MyContract/Contract.hs
+++ b/test/conformance-skeletons/SkelContract/Generated/MyService/MyContract/Contract.hs
@@ -1,54 +1,69 @@
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+{-# LANGUAGE TypeApplications #-}
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelContract.Generated.MyService.MyContract.Contract (
-    MyContractPayload (..),
-    ThingHappenedData (..),
-    messageTypeOf,
-    encodeMyContractPayload,
-    parseMyContractPayload,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from contract myContract; do not edit.
+module SkelContract.Generated.MyService.MyContract.Contract
+  ( MyContractPayload (..)
+  , ThingHappenedData (..)
+  , eventsTopic
+  , messageTypeOf
+  , encodeMyContractPayload
+  , parseMyContractPayload
+  ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.KindID (KindID)
+import qualified Data.KindID as KindID
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
+import Keiro.Codec.IdDomain (parseKindIdV7Value)
 
 -- topic constants
 eventsTopic :: Text
 eventsTopic = "my-service.events"
 
 -- the closed payload set (discriminated by "messageType")
-data ThingHappenedData = ThingHappenedData {thingId :: !Text, detail :: !Text}
-    deriving stock (Eq, Show)
+data ThingHappenedData = ThingHappenedData {thingId :: !(KindID "thing"), detail :: !Text}
+  deriving stock (Eq, Show)
 
-data MyContractPayload = ThingHappened !ThingHappenedData
-    deriving stock (Eq, Show)
+data MyContractPayload
+  = ThingHappened !ThingHappenedData
+  deriving stock (Eq, Show)
 
 messageTypeOf :: MyContractPayload -> Text
 messageTypeOf = \case
-    ThingHappened{} -> "ThingHappened"
+  ThingHappened {} -> "ThingHappened"
 
 encodeMyContractPayload :: MyContractPayload -> Value
 encodeMyContractPayload = \case
-    ThingHappened payload ->
-        object
-            [ "messageType" .= ("ThingHappened" :: Text)
-            , "thingId" .= payload.thingId
-            , "detail" .= payload.detail
-            ]
+  ThingHappened payload ->
+    object
+      [ "messageType" .= ("ThingHappened" :: Text),
+        "thingId" .= KindID.toText payload.thingId,
+        "detail" .= payload.detail
+      ]
 
 parseMyContractPayload :: Value -> Either Text MyContractPayload
 parseMyContractPayload = mapLeftText . parseEither (withObject "MyContractPayload" go)
   where
     go o = do
-        kind <- o .: "messageType" :: Parser Text
-        case kind of
-            "ThingHappened" ->
-                ThingHappened <$> (ThingHappenedData <$> o .: "thingId" <*> o .: "detail")
-            _ -> fail "unknown message type"
+      kind <- explicitParseField (withText "messageType" validateMessageType) o "messageType"
+      case kind of
+        "ThingHappened" ->
+          ThingHappened
+            <$> ( ThingHappenedData
+                    <$> explicitParseField (parseKindIdV7Value @"thing") o "thingId"
+                    <*> o .: "detail"
+                )
+        _ -> fail "validated message type was not handled"
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
+
+validateMessageType :: Text -> Parser Text
+validateMessageType kind
+  | kind `elem` ["ThingHappened"] = pure kind
+  | otherwise = fail ("unknown message type " <> show kind <> "; expected one of: ThingHappened")
diff --git a/test/conformance-skeletons/SkelEmit/Generated/MyService/MyContract/Contract.hs b/test/conformance-skeletons/SkelEmit/Generated/MyService/MyContract/Contract.hs
--- a/test/conformance-skeletons/SkelEmit/Generated/MyService/MyContract/Contract.hs
+++ b/test/conformance-skeletons/SkelEmit/Generated/MyService/MyContract/Contract.hs
@@ -1,53 +1,67 @@
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+{-# LANGUAGE TypeApplications #-}
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelEmit.Generated.MyService.MyContract.Contract (
-    MyContractPayload (..),
-    ThingAcceptedData (..),
-    messageTypeOf,
-    encodeMyContractPayload,
-    parseMyContractPayload,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from contract myContract; do not edit.
+module SkelEmit.Generated.MyService.MyContract.Contract
+  ( MyContractPayload (..)
+  , ThingAcceptedData (..)
+  , eventsTopic
+  , messageTypeOf
+  , encodeMyContractPayload
+  , parseMyContractPayload
+  ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.KindID (KindID)
+import qualified Data.KindID as KindID
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
+import Keiro.Codec.IdDomain (parseKindIdV7Value)
 
 -- topic constants
 eventsTopic :: Text
 eventsTopic = "my-service.events"
 
 -- the closed payload set (discriminated by "messageType")
-data ThingAcceptedData = ThingAcceptedData {thingId :: !Text}
-    deriving stock (Eq, Show)
+data ThingAcceptedData = ThingAcceptedData {thingId :: !(KindID "thing")}
+  deriving stock (Eq, Show)
 
-data MyContractPayload = ThingAccepted !ThingAcceptedData
-    deriving stock (Eq, Show)
+data MyContractPayload
+  = ThingAccepted !ThingAcceptedData
+  deriving stock (Eq, Show)
 
 messageTypeOf :: MyContractPayload -> Text
 messageTypeOf = \case
-    ThingAccepted{} -> "ThingAccepted"
+  ThingAccepted {} -> "ThingAccepted"
 
 encodeMyContractPayload :: MyContractPayload -> Value
 encodeMyContractPayload = \case
-    ThingAccepted payload ->
-        object
-            [ "messageType" .= ("ThingAccepted" :: Text)
-            , "thingId" .= payload.thingId
-            ]
+  ThingAccepted payload ->
+    object
+      [ "messageType" .= ("ThingAccepted" :: Text),
+        "thingId" .= KindID.toText payload.thingId
+      ]
 
 parseMyContractPayload :: Value -> Either Text MyContractPayload
 parseMyContractPayload = mapLeftText . parseEither (withObject "MyContractPayload" go)
   where
     go o = do
-        kind <- o .: "messageType" :: Parser Text
-        case kind of
-            "ThingAccepted" ->
-                ThingAccepted <$> (ThingAcceptedData <$> o .: "thingId")
-            _ -> fail "unknown message type"
+      kind <- explicitParseField (withText "messageType" validateMessageType) o "messageType"
+      case kind of
+        "ThingAccepted" ->
+          ThingAccepted
+            <$> ( ThingAcceptedData
+                    <$> explicitParseField (parseKindIdV7Value @"thing") o "thingId"
+                )
+        _ -> fail "validated message type was not handled"
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
+
+validateMessageType :: Text -> Parser Text
+validateMessageType kind
+  | kind `elem` ["ThingAccepted"] = pure kind
+  | otherwise = fail ("unknown message type " <> show kind <> "; expected one of: ThingAccepted")
diff --git a/test/conformance-skeletons/SkelEmit/Generated/MyService/ThingPublisher/Publisher.hs b/test/conformance-skeletons/SkelEmit/Generated/MyService/ThingPublisher/Publisher.hs
--- a/test/conformance-skeletons/SkelEmit/Generated/MyService/ThingPublisher/Publisher.hs
+++ b/test/conformance-skeletons/SkelEmit/Generated/MyService/ThingPublisher/Publisher.hs
@@ -1,11 +1,9 @@
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelEmit.Generated.MyService.ThingPublisher.Publisher (
-    publisherOrdering,
-    publisherBackoff,
-    publisherMaxAttempts,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from publisher thingPublisher; do not edit.
+module SkelEmit.Generated.MyService.ThingPublisher.Publisher
+  ( publisherOrdering
+  , publisherBackoff
+  , publisherMaxAttempts
+  ) where
 
 import Keiro.Outbox.Types (BackoffSchedule (..), ExponentialBackoffOptions (..), OrderingPolicy (..))
 
diff --git a/test/conformance-skeletons/SkelIntake/Generated/MyService/MyContract/Contract.hs b/test/conformance-skeletons/SkelIntake/Generated/MyService/MyContract/Contract.hs
--- a/test/conformance-skeletons/SkelIntake/Generated/MyService/MyContract/Contract.hs
+++ b/test/conformance-skeletons/SkelIntake/Generated/MyService/MyContract/Contract.hs
@@ -1,53 +1,67 @@
+{-# LANGUAGE DataKinds #-}
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+{-# LANGUAGE TypeApplications #-}
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelIntake.Generated.MyService.MyContract.Contract (
-    MyContractPayload (..),
-    ThingHappenedData (..),
-    messageTypeOf,
-    encodeMyContractPayload,
-    parseMyContractPayload,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from contract myContract; do not edit.
+module SkelIntake.Generated.MyService.MyContract.Contract
+  ( MyContractPayload (..)
+  , ThingHappenedData (..)
+  , eventsTopic
+  , messageTypeOf
+  , encodeMyContractPayload
+  , parseMyContractPayload
+  ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
+import Data.KindID (KindID)
+import qualified Data.KindID as KindID
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
+import Keiro.Codec.IdDomain (parseKindIdV7Value)
 
 -- topic constants
 eventsTopic :: Text
 eventsTopic = "my-service.events"
 
 -- the closed payload set (discriminated by "messageType")
-data ThingHappenedData = ThingHappenedData {thingId :: !Text}
-    deriving stock (Eq, Show)
+data ThingHappenedData = ThingHappenedData {thingId :: !(KindID "thing")}
+  deriving stock (Eq, Show)
 
-data MyContractPayload = ThingHappened !ThingHappenedData
-    deriving stock (Eq, Show)
+data MyContractPayload
+  = ThingHappened !ThingHappenedData
+  deriving stock (Eq, Show)
 
 messageTypeOf :: MyContractPayload -> Text
 messageTypeOf = \case
-    ThingHappened{} -> "ThingHappened"
+  ThingHappened {} -> "ThingHappened"
 
 encodeMyContractPayload :: MyContractPayload -> Value
 encodeMyContractPayload = \case
-    ThingHappened payload ->
-        object
-            [ "messageType" .= ("ThingHappened" :: Text)
-            , "thingId" .= payload.thingId
-            ]
+  ThingHappened payload ->
+    object
+      [ "messageType" .= ("ThingHappened" :: Text),
+        "thingId" .= KindID.toText payload.thingId
+      ]
 
 parseMyContractPayload :: Value -> Either Text MyContractPayload
 parseMyContractPayload = mapLeftText . parseEither (withObject "MyContractPayload" go)
   where
     go o = do
-        kind <- o .: "messageType" :: Parser Text
-        case kind of
-            "ThingHappened" ->
-                ThingHappened <$> (ThingHappenedData <$> o .: "thingId")
-            _ -> fail "unknown message type"
+      kind <- explicitParseField (withText "messageType" validateMessageType) o "messageType"
+      case kind of
+        "ThingHappened" ->
+          ThingHappened
+            <$> ( ThingHappenedData
+                    <$> explicitParseField (parseKindIdV7Value @"thing") o "thingId"
+                )
+        _ -> fail "validated message type was not handled"
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
+
+validateMessageType :: Text -> Parser Text
+validateMessageType kind
+  | kind `elem` ["ThingHappened"] = pure kind
+  | otherwise = fail ("unknown message type " <> show kind <> "; expected one of: ThingHappened")
diff --git a/test/conformance-skeletons/SkelIntake/Generated/MyService/ThingInbox/Inbox.hs b/test/conformance-skeletons/SkelIntake/Generated/MyService/ThingInbox/Inbox.hs
--- a/test/conformance-skeletons/SkelIntake/Generated/MyService/ThingInbox/Inbox.hs
+++ b/test/conformance-skeletons/SkelIntake/Generated/MyService/ThingInbox/Inbox.hs
@@ -1,38 +1,80 @@
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelIntake.Generated.MyService.ThingInbox.Inbox (
-    InboxAck (..),
-    inboxDedupePolicy,
-    inboxPersistence,
-    inboxDisposition,
-) where
+{-# LANGUAGE OverloadedStrings #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from intake thingInbox; do not edit.
+module SkelIntake.Generated.MyService.ThingInbox.Inbox
+  ( InboxFailure (..)
+  , ThingInboxOutcome (..)
+  , ThingInboxDisposition (..)
+  , inboxDedupePolicy
+  , inboxPersistence
+  , inboxDispositionFor
+  , inboxDisposition
+  ) where
 
-import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..))
+import Data.Text (Text)
+import Keiro.Inbox.Types (InboxDedupePolicy (..), InboxPersistence (..), InboxResult (..), RetryDelay (..))
 
 -- The dedupe policy (hole-kind 4), lowered to the live InboxDedupePolicy.
 inboxDedupePolicy :: InboxDedupePolicy
 inboxDedupePolicy = PreferIntegrationMessageId
 
-{- | Success-path envelope retention passed to runInboxTransactionWith.
-Failures always retain their full operator-facing dead-letter envelope.
-Dedupe-only success rows decode with an empty payload.
--}
+-- | Success-path envelope retention passed to runInboxTransactionWith.
+-- Failures always retain their full operator-facing dead-letter envelope.
+-- Dedupe-only success rows decode with an empty payload.
 inboxPersistence :: InboxPersistence
 inboxPersistence = PersistFullEnvelope
 
--- The service's ack decision for each inbox classification.
-data InboxAck = InboxAckOk | InboxRetry | InboxDeadLetter
-    deriving stock (Eq, Show)
+-- Runtime failure detail retained when the inbox wrapper reports a failed handler attempt.
+data InboxFailure = InboxFailure
+  { inboxFailureReason :: !Text
+  , inboxFailureAttempt :: !(Maybe Int)
+  }
+  deriving stock (Eq, Show)
 
--- The disposition table (hole-kind 2) over the LIVE Keiro.Inbox.Types.InboxResult.
--- duplicate => ackOk and previouslyFailed => deadLetter are the dangerous
--- inversions the spec states explicitly.
-inboxDisposition :: InboxResult a -> InboxAck
+-- Every classification named by the spec. Keeping this closed makes the
+-- generated table exhaustive and gives handler holes typed inputs.
+data ThingInboxOutcome
+  = ThingInboxProcessed
+  | ThingInboxDuplicate
+  | ThingInboxInProgress
+  | ThingInboxPreviouslyFailed
+  | ThingInboxDecodeFailed
+  | ThingInboxDedupeFailed
+  | ThingInboxStoreFailed
+  deriving stock (Eq, Show)
+
+-- The service's declared acknowledgement decision, including its details.
+data ThingInboxDisposition
+  = InboxAccept
+  | InboxRetryAfter !RetryDelay !(Maybe InboxFailure)
+  | InboxDeadLetter !(Maybe Text) !(Maybe InboxFailure)
+  deriving stock (Eq, Show)
+
+-- The complete disposition table (hole-kind 2).
+inboxDispositionFor :: ThingInboxOutcome -> ThingInboxDisposition
+inboxDispositionFor outcome = case outcome of
+  ThingInboxProcessed -> InboxAccept
+  ThingInboxDuplicate -> InboxAccept
+  ThingInboxInProgress -> InboxRetryAfter (RetryDelay 5) Nothing
+  ThingInboxPreviouslyFailed -> InboxDeadLetter (Just "previous inbox failure") Nothing
+  ThingInboxDecodeFailed -> InboxDeadLetter Nothing Nothing
+  ThingInboxDedupeFailed -> InboxDeadLetter Nothing Nothing
+  ThingInboxStoreFailed -> InboxRetryAfter (RetryDelay 5) Nothing
+
+-- Lower the LIVE Keiro.Inbox.Types.InboxResult without an open fallback.
+inboxDisposition :: InboxResult a -> ThingInboxDisposition
 inboxDisposition r = case r of
-    InboxProcessed _ -> InboxAckOk
-    InboxDuplicate -> InboxAckOk
-    InboxInProgress -> InboxRetry
-    InboxPreviouslyFailed _ -> InboxDeadLetter
+  InboxProcessed _ -> inboxDispositionFor ThingInboxProcessed
+  InboxDuplicate -> inboxDispositionFor ThingInboxDuplicate
+  InboxInProgress -> inboxDispositionFor ThingInboxInProgress
+  InboxPreviouslyFailed failureReason ->
+    maybe (inboxDispositionFor ThingInboxPreviouslyFailed)
+      (\reason -> attachFailure (InboxFailure reason Nothing) (inboxDispositionFor ThingInboxPreviouslyFailed))
+      failureReason
+  InboxHandlerFailed reason attempts ->
+    attachFailure (InboxFailure reason (Just attempts)) (inboxDispositionFor ThingInboxStoreFailed)
 
--- handler-level failures (not InboxResult): decodeFailed => deadLetter, dedupeFailed => deadLetter, storeFailed => retry
+attachFailure :: InboxFailure -> ThingInboxDisposition -> ThingInboxDisposition
+attachFailure failure disposition = case disposition of
+  InboxRetryAfter delay _ -> InboxRetryAfter delay (Just failure)
+  InboxDeadLetter reason _ -> InboxDeadLetter reason (Just failure)
+  InboxAccept -> InboxAccept
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Codec.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Codec.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Codec.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Codec.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module SkelProcess.Generated.MyService.Hospital.Codec (
     hospitalCodec,
     parseHospitalEvent,
@@ -7,9 +7,10 @@
 ) where
 
 import SkelProcess.Generated.MyService.Hospital.Domain
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..), hospitalIdText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import SkelProcess.Generated.MyService.Nominals (HospitalId, hospitalIdText)
+import SkelProcess.Generated.MyService.Nominals.Internal (unsafeHospitalIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -45,8 +46,11 @@
     go o = do
       case tag of
         "SurgeActivated" ->
-          SurgeActivated <$> (SurgeActivatedData <$> (HospitalId <$> o .: "hospitalId"))
-        _ -> fail "unknown event type"
+          SurgeActivated
+            <$> ( SurgeActivatedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SurgeActivated")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Domain.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Domain.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Domain.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module SkelProcess.Generated.MyService.Hospital.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..))
+import SkelProcess.Generated.MyService.Nominals (HospitalId, parseHospitalId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data HospitalVertex = HospitalOperational | HospitalSurging
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/EventStream.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/EventStream.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/EventStream.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module SkelProcess.Generated.MyService.Hospital.EventStream
   ( hospitalCategory
+  , hospitalCommandCategory
   , hospitalEventStream
   , hospitalEventStreamDef
   , HospitalEventStream
@@ -9,7 +10,7 @@
 
 import SkelProcess.Generated.MyService.Hospital.Domain
 import SkelProcess.Generated.MyService.Hospital.Codec (hospitalCodec)
-import SkelProcess.MyService.Hospital.Holes (hospitalTransducer)
+import SkelProcess.Generated.MyService.Hospital.Transducer (hospitalFoldFingerprint, hospitalTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-hospitalCategory :: Stream.StreamCategory a
+hospitalCategory :: Stream.StreamCategory HospitalEventStreamDef
 hospitalCategory = Stream.categoryUnsafe "hospital"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+hospitalCommandCategory :: Stream.StreamCategory HospitalCommand
+hospitalCommandCategory = Stream.categoryUnsafe "hospital"
+
 type HospitalEventStreamDef =
   EventStream (HsPred HospitalRegs HospitalCommand) HospitalRegs HospitalVertex HospitalCommand HospitalEvent
 
@@ -30,13 +35,13 @@
 hospitalEventStreamDef :: HospitalEventStreamDef
 hospitalEventStreamDef =
   EventStream
-    { transducer = hospitalTransducer
-    , initialState = HospitalOperational
-    , initialRegisters = initialHospitalRegs
-    , eventCodec = hospitalCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = hospitalTransducer,
+      initialState = HospitalOperational,
+      initialRegisters = initialHospitalRegs,
+      eventCodec = hospitalCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 hospitalEventStream :: HospitalEventStream
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Harness.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Harness.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Harness.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Harness.hs
@@ -1,19 +1,18 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module SkelProcess.Generated.MyService.Hospital.Harness (harnessAssertions) where
 
 import SkelProcess.Generated.MyService.Hospital.Domain
 import SkelProcess.Generated.MyService.Hospital.Codec (encodeHospitalEvent, parseHospitalEvent, hospitalCodec)
-import SkelProcess.MyService.Hospital.Holes (hospitalTransducer)
+import SkelProcess.Generated.MyService.Hospital.Transducer (hospitalTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer)
 import Keiro.Codec (eventType)
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..))
+import SkelProcess.Generated.MyService.Nominals (HospitalId, parseHospitalId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions hospitalTransducer))
@@ -27,11 +26,11 @@
 roundTrips e = parseHospitalEvent (eventType hospitalCodec e) (encodeHospitalEvent e) == Right e
 
 sampleEventSurgeActivated :: HospitalEvent
-sampleEventSurgeActivated = (SurgeActivated (SurgeActivatedData (HospitalId "sample")))
+sampleEventSurgeActivated = (SurgeActivated (SurgeActivatedData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))
 
 acceptActivateSurge :: Bool
 acceptActivateSurge =
-  case step hospitalTransducer (HospitalOperational, initialHospitalRegs) ((ActivateSurge (ActivateSurgeData (HospitalId "sample")))) of
+  case step hospitalTransducer (HospitalOperational, initialHospitalRegs) ((ActivateSurge (ActivateSurgeData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))) of
     Just (v, _, _) -> v == HospitalSurging
     Nothing -> False
 
@@ -39,7 +38,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayActivateSurge :: [(String, Bool)]
 forwardReplayActivateSurge =
-  case step hospitalTransducer (HospitalOperational, initialHospitalRegs) ((ActivateSurge (ActivateSurgeData (HospitalId "sample")))) of
+  case step hospitalTransducer (HospitalOperational, initialHospitalRegs) ((ActivateSurge (ActivateSurgeData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, _forwardRegs, emitted) ->
       case mapM (\event -> parseHospitalEvent (eventType hospitalCodec event) (encodeHospitalEvent event)) emitted of
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Projection.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Projection.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Projection.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
 module SkelProcess.Generated.MyService.Hospital.Projection () where
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Transducer.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Hospital/Transducer.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Hospital; do not edit.
+module SkelProcess.Generated.MyService.Hospital.Transducer
+  ( hospitalTransducer
+  , hospitalFoldFingerprint
+  , BehaviorOwnership (..)
+  , hospitalPredicateVerifications
+  ) where
+
+import SkelProcess.Generated.MyService.Hospital.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+hospitalTransducer
+  :: SymTransducer
+       (HsPred HospitalRegs HospitalCommand)
+       HospitalRegs
+       HospitalVertex
+       HospitalCommand
+       HospitalEvent
+hospitalTransducer =
+  B.buildTransducer HospitalOperational initialHospitalRegs isTerminal do
+    B.from HospitalOperational do
+      B.onCmd inCtorActivateSurge $ \d -> B.do
+        B.emit wireSurgeActivated (SurgeActivatedTermFields
+          { hospitalId = d.hospitalId
+          })
+        B.goto HospitalSurging
+ where
+  isTerminal = \case
+    HospitalSurging -> True
+    _ -> False
+
+hospitalFoldFingerprint :: Text
+hospitalFoldFingerprint = "6203fe3055c1796d97f93b3e2c185814"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+hospitalPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+hospitalPredicateVerifications = sequence
+  [ verifyTransition "transition1OperationalActivateSurge" GeneratedOwned HospitalOperational 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut hospitalTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/Process.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/Process.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/Process.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/Process.hs
@@ -1,4 +1,4 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from process HospitalSurge; do not edit.
 module SkelProcess.Generated.MyService.HospitalSurge.Process
   ( hospitalSurgeProcessName
   , hospitalSurgeCategory
@@ -13,6 +13,7 @@
 import Data.Time (UTCTime)
 import Data.UUID (UUID)
 import qualified Data.UUID.V5 as UUID.V5
+import SkelProcess.Generated.MyService.Surge.EventStream (SurgeEventStreamDef)
 import Keiro.Command (CommandError (..))
 import Keiro.Stream qualified as Stream
 import Keiro.Timer (TimerId (..), TimerRequest (..))
@@ -26,7 +27,7 @@
 -- The validated saga stream category (hole-kind 5: referenced, never retyped).
 -- Saga streams are '<category>-<correlationId>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because keiro-dsl check proved the literal legal.
-hospitalSurgeCategory :: Stream.StreamCategory a
+hospitalSurgeCategory :: Stream.StreamCategory SurgeEventStreamDef
 hospitalSurgeCategory = Stream.categoryUnsafe "hospitalSurge"
 
 -- Node-level worker policy lowered from the spec. Pass this value to
@@ -34,10 +35,10 @@
 hospitalSurgeProcessWorkerOptions :: WorkerOptions es msg
 hospitalSurgeProcessWorkerOptions =
   WorkerOptions
-    { poisonPolicy = PoisonHalt
-    , rejectedCommandPolicy = RejectedHalt
-    , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-    , metrics = Nothing                  -- runtime configuration; install at call site
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedHalt,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
     }
 
 -- The deterministic timer-request builder: id derived from the correlation
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/ProcessHarness.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/ProcessHarness.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/ProcessHarness.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/HospitalSurge/ProcessHarness.hs
@@ -1,13 +1,12 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from process HospitalSurge; do not edit.
 module SkelProcess.Generated.MyService.HospitalSurge.ProcessHarness (processHarnessValues) where
 
-{- | (label, value): the spec's deterministic process/timer decisions,
-lowered to plain values so a driver can assert them against a committed
-expectation. The driver's expectation is hand-written (not generated), so a
-spec change that alters a decision diverges from it and turns a specific
-assertion red — the spec->behaviour pin. (Live-runtime behavioural
-conformance of the filled ProcessManager is the M5 step.)
--}
+-- | (label, value): the spec's deterministic process/timer decisions,
+-- lowered to plain values so a driver can assert them against a committed
+-- expectation. The driver's expectation is hand-written (not generated), so a
+-- spec change that alters a decision diverges from it and turns a specific
+-- assertion red — the spec->behaviour pin. (Live-runtime behavioural
+-- conformance of the filled ProcessManager is the M5 step.)
 processHarnessValues :: [(String, String)]
 processHarnessValues =
   [ ("fireAtField", "observedAt")
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals.hs
@@ -1,28 +1,25 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelProcess.Generated.MyService.Nominals where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service generated nominal declarations; do not edit.
+module SkelProcess.Generated.MyService.Nominals
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  ) where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Shape (CanonicalTypeName)
-
-newtype CommandId = CommandId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
+import SkelProcess.Generated.MyService.Nominals.Internal (CommandId, mkCommandId, parseCommandId, commandIdText, HospitalId, mkHospitalId, parseHospitalId, hospitalIdText)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 instance CanonicalTypeName CommandId
 
-commandIdText :: CommandId -> Text
-commandIdText (CommandId value) = value
-
-newtype HospitalId = HospitalId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
-
 instance CanonicalTypeName HospitalId
-
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId value) = value
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals/Internal.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Nominals/Internal.hs
@@ -0,0 +1,66 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service generated nominal ID internals; do not edit.
+module SkelProcess.Generated.MyService.Nominals.Internal
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , unsafeCommandIdFromLegacyText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CommandId = CommandId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CommandId where
+  toJSON = toJSON . commandIdText
+
+instance FromJSON CommandId where
+  parseJSON = withText "CommandId" (either (fail . T.unpack) pure . parseCommandId)
+
+parseCommandId :: Text -> Either Text CommandId
+parseCommandId input = case validateIdDomainText (typeIdV7Domain "cmd") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CommandId input)
+
+mkCommandId :: Text -> Either Text CommandId
+mkCommandId = parseCommandId
+
+commandIdText :: CommandId -> Text
+commandIdText (CommandId value) = value
+
+unsafeCommandIdFromLegacyText :: Text -> CommandId
+unsafeCommandIdFromLegacyText = CommandId
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/ReplayAudit.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/ReplayAudit.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/ReplayAudit.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Codec.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Codec.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Codec.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Codec.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module SkelProcess.Generated.MyService.Surge.Codec (
     surgeCodec,
     parseSurgeEvent,
@@ -7,9 +7,10 @@
 ) where
 
 import SkelProcess.Generated.MyService.Surge.Domain
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..), hospitalIdText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import SkelProcess.Generated.MyService.Nominals (HospitalId, hospitalIdText)
+import SkelProcess.Generated.MyService.Nominals.Internal (unsafeHospitalIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -55,10 +56,20 @@
     go o = do
       case tag of
         "SurgeThresholdNoted" ->
-          SurgeThresholdNoted <$> (SurgeThresholdNotedData <$> (HospitalId <$> o .: "hospitalId") <*> o .: "availableIcuBeds" <*> o .: "redDemand" <*> o .: "timerId")
+          SurgeThresholdNoted
+            <$> ( SurgeThresholdNotedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> o .: "availableIcuBeds"
+                    <*> o .: "redDemand"
+                    <*> o .: "timerId"
+                )
         "SurgeTimerMarked" ->
-          SurgeTimerMarked <$> (SurgeTimerMarkedData <$> (HospitalId <$> o .: "hospitalId") <*> o .: "timerId")
-        _ -> fail "unknown event type"
+          SurgeTimerMarked
+            <$> ( SurgeTimerMarkedData
+                    <$> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> o .: "timerId"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: SurgeThresholdNoted, SurgeTimerMarked")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Domain.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Domain.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Domain.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module SkelProcess.Generated.MyService.Surge.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..))
+import SkelProcess.Generated.MyService.Nominals (HospitalId, parseHospitalId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data SurgeVertex = SurgeIdle | SurgeFired
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/EventStream.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/EventStream.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/EventStream.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module SkelProcess.Generated.MyService.Surge.EventStream
   ( surgeCategory
+  , surgeCommandCategory
   , surgeEventStream
   , surgeEventStreamDef
   , SurgeEventStream
@@ -9,7 +10,7 @@
 
 import SkelProcess.Generated.MyService.Surge.Domain
 import SkelProcess.Generated.MyService.Surge.Codec (surgeCodec)
-import SkelProcess.MyService.Surge.Holes (surgeTransducer)
+import SkelProcess.Generated.MyService.Surge.Transducer (surgeFoldFingerprint, surgeTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-surgeCategory :: Stream.StreamCategory a
+surgeCategory :: Stream.StreamCategory SurgeEventStreamDef
 surgeCategory = Stream.categoryUnsafe "surge"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+surgeCommandCategory :: Stream.StreamCategory SurgeCommand
+surgeCommandCategory = Stream.categoryUnsafe "surge"
+
 type SurgeEventStreamDef =
   EventStream (HsPred SurgeRegs SurgeCommand) SurgeRegs SurgeVertex SurgeCommand SurgeEvent
 
@@ -30,13 +35,13 @@
 surgeEventStreamDef :: SurgeEventStreamDef
 surgeEventStreamDef =
   EventStream
-    { transducer = surgeTransducer
-    , initialState = SurgeIdle
-    , initialRegisters = initialSurgeRegs
-    , eventCodec = surgeCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = surgeTransducer,
+      initialState = SurgeIdle,
+      initialRegisters = initialSurgeRegs,
+      eventCodec = surgeCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 surgeEventStream :: SurgeEventStream
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Harness.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Harness.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Harness.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Harness.hs
@@ -1,19 +1,18 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module SkelProcess.Generated.MyService.Surge.Harness (harnessAssertions) where
 
 import SkelProcess.Generated.MyService.Surge.Domain
 import SkelProcess.Generated.MyService.Surge.Codec (encodeSurgeEvent, parseSurgeEvent, surgeCodec)
-import SkelProcess.MyService.Surge.Holes (surgeTransducer)
+import SkelProcess.Generated.MyService.Surge.Transducer (surgeTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer)
 import Keiro.Codec (eventType)
-import SkelProcess.Generated.MyService.Nominals (HospitalId (..))
+import SkelProcess.Generated.MyService.Nominals (HospitalId, parseHospitalId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions surgeTransducer))
@@ -30,20 +29,20 @@
 roundTrips e = parseSurgeEvent (eventType surgeCodec e) (encodeSurgeEvent e) == Right e
 
 sampleEventSurgeThresholdNoted :: SurgeEvent
-sampleEventSurgeThresholdNoted = (SurgeThresholdNoted (SurgeThresholdNotedData (HospitalId "sample") 0 0 "sample-timerId"))
+sampleEventSurgeThresholdNoted = (SurgeThresholdNoted (SurgeThresholdNotedData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0 0 "sample-timerId"))
 
 sampleEventSurgeTimerMarked :: SurgeEvent
-sampleEventSurgeTimerMarked = (SurgeTimerMarked (SurgeTimerMarkedData (HospitalId "sample") "sample-timerId"))
+sampleEventSurgeTimerMarked = (SurgeTimerMarked (SurgeTimerMarkedData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") "sample-timerId"))
 
 acceptNoteSurgeThreshold :: Bool
 acceptNoteSurgeThreshold =
-  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((NoteSurgeThreshold (NoteSurgeThresholdData (HospitalId "sample") 0 0 "sample-timerId"))) of
+  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((NoteSurgeThreshold (NoteSurgeThresholdData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0 0 "sample-timerId"))) of
     Just (v, _, _) -> v == SurgeIdle
     Nothing -> False
 
 acceptMarkSurgeTimerFired :: Bool
 acceptMarkSurgeTimerFired =
-  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((MarkSurgeTimerFired (MarkSurgeTimerFiredData (HospitalId "sample") "sample-timerId"))) of
+  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((MarkSurgeTimerFired (MarkSurgeTimerFiredData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") "sample-timerId"))) of
     Just (v, _, _) -> v == SurgeFired
     Nothing -> False
 
@@ -51,7 +50,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayNoteSurgeThreshold :: [(String, Bool)]
 forwardReplayNoteSurgeThreshold =
-  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((NoteSurgeThreshold (NoteSurgeThresholdData (HospitalId "sample") 0 0 "sample-timerId"))) of
+  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((NoteSurgeThreshold (NoteSurgeThresholdData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") 0 0 "sample-timerId"))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, _forwardRegs, emitted) ->
       case mapM (\event -> parseSurgeEvent (eventType surgeCodec event) (encodeSurgeEvent event)) emitted of
@@ -69,7 +68,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayMarkSurgeTimerFired :: [(String, Bool)]
 forwardReplayMarkSurgeTimerFired =
-  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((MarkSurgeTimerFired (MarkSurgeTimerFiredData (HospitalId "sample") "sample-timerId"))) of
+  case step surgeTransducer (SurgeIdle, initialSurgeRegs) ((MarkSurgeTimerFired (MarkSurgeTimerFiredData (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") "sample-timerId"))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, _forwardRegs, emitted) ->
       case mapM (\event -> parseSurgeEvent (eventType surgeCodec event) (encodeSurgeEvent event)) emitted of
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Projection.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Projection.hs
--- a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Projection.hs
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
 module SkelProcess.Generated.MyService.Surge.Projection () where
diff --git a/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Transducer.hs b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelProcess/Generated/MyService/Surge/Transducer.hs
@@ -0,0 +1,68 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Surge; do not edit.
+module SkelProcess.Generated.MyService.Surge.Transducer
+  ( surgeTransducer
+  , surgeFoldFingerprint
+  , BehaviorOwnership (..)
+  , surgePredicateVerifications
+  ) where
+
+import SkelProcess.Generated.MyService.Surge.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+surgeTransducer
+  :: SymTransducer
+       (HsPred SurgeRegs SurgeCommand)
+       SurgeRegs
+       SurgeVertex
+       SurgeCommand
+       SurgeEvent
+surgeTransducer =
+  B.buildTransducer SurgeIdle initialSurgeRegs isTerminal do
+    B.from SurgeIdle do
+      B.onCmd inCtorNoteSurgeThreshold $ \d -> B.do
+        B.emit wireSurgeThresholdNoted (SurgeThresholdNotedTermFields
+          { hospitalId = d.hospitalId
+          , availableIcuBeds = d.availableIcuBeds
+          , redDemand = d.redDemand
+          , timerId = d.timerId
+          })
+        B.goto SurgeIdle
+      B.onCmd inCtorMarkSurgeTimerFired $ \d -> B.do
+        B.emit wireSurgeTimerMarked (SurgeTimerMarkedTermFields
+          { hospitalId = d.hospitalId
+          , timerId = d.timerId
+          })
+        B.goto SurgeFired
+ where
+  isTerminal = \case
+    SurgeFired -> True
+    _ -> False
+
+surgeFoldFingerprint :: Text
+surgeFoldFingerprint = "d84ee3eac65a407f23e4a4443cafab93"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+surgePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+surgePredicateVerifications = sequence
+  [ verifyTransition "transition1IdleNoteSurgeThreshold" GeneratedOwned SurgeIdle 0
+  , verifyTransition "transition2IdleMarkSurgeTimerFired" GeneratedOwned SurgeIdle 1
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut surgeTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModel.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModel.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModel.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModel.hs
@@ -1,62 +1,61 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModel (
-    acceptedTransferNeedsReadModel,
-    acceptedTransferNeedsQualifiedTable,
-    registerAcceptedTransferNeeds,
-    startAcceptedTransferNeedsRebuild,
-    finishAcceptedTransferNeedsRebuild,
-    abandonAcceptedTransferNeedsRebuild,
-    acceptedTransferNeedsAsyncProjection,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel accepted_transfer_needs; do not edit.
+module SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModel
+  ( acceptedTransferNeedsReadModel
+  , acceptedTransferNeedsQualifiedTable
+  , registerAcceptedTransferNeeds
+  , startAcceptedTransferNeedsRebuild
+  , finishAcceptedTransferNeedsRebuild
+  , abandonAcceptedTransferNeedsRebuild
+  , acceptedTransferNeedsAsyncProjection
+  ) where
 
 import Data.Functor (void)
 import Effectful (Eff, (:>))
+import SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModelTable (acceptedTransferNeedsQualifiedTable)
+import SkelQueue.MyService.Accepted_transfer_needs.ReadModelHoles (AcceptedTransferNeedsQueryInput, AcceptedTransferNeedsQueryResult, acceptedTransferNeedsQuery, applyAcceptedTransferNeeds)
 import Keiro.Projection (AsyncProjection (..))
 import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)
 import Keiro.ReadModel.Rebuild qualified as Rebuild
 import Kiroku.Store.Effect (Store)
 import Kiroku.Store.Types (GlobalPosition, RecordedEvent (..))
-import SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModelTable (acceptedTransferNeedsQualifiedTable)
-import SkelQueue.MyService.Accepted_transfer_needs.ReadModelHoles (AcceptedTransferNeedsQueryInput, AcceptedTransferNeedsQueryResult, acceptedTransferNeedsQuery, applyAcceptedTransferNeeds)
 
 acceptedTransferNeedsReadModel :: ReadModel AcceptedTransferNeedsQueryInput AcceptedTransferNeedsQueryResult
 acceptedTransferNeedsReadModel =
-    ReadModel
-        { name = "my-service-accepted-transfer-needs"
-        , tableName = "accepted_transfer_needs"
-        , schema = "my_service"
-        , subscriptionName = "my-service-accepted-transfer-needs-sub"
-        , version = 1
-        , shapeHash = "fnv1a:fec517dae7760b8a"
-        , defaultConsistency = Eventual
-        , strongScope = EntireLog
-        , query = acceptedTransferNeedsQuery
-        }
+  ReadModel
+    { name = "my-service-accepted-transfer-needs"
+    , tableName = "accepted_transfer_needs"
+    , schema = "my_service"
+    , subscriptionName = "my-service-accepted-transfer-needs-sub"
+    , version = 1
+    , shapeHash = "fnv1a:fec517dae7760b8a"
+    , defaultConsistency = Eventual
+    , strongScope = EntireLog
+    , query = acceptedTransferNeedsQuery
+    }
 
 -- Call once at projection startup before serving queries.
 registerAcceptedTransferNeeds :: (Store :> es) => Eff es ()
 registerAcceptedTransferNeeds =
-    void (registerReadModel "my-service-accepted-transfer-needs" 1 "fnv1a:fec517dae7760b8a")
+  void (registerReadModel "my-service-accepted-transfer-needs" 1 "fnv1a:fec517dae7760b8a")
 
 startAcceptedTransferNeedsRebuild :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata
 startAcceptedTransferNeedsRebuild =
-    Rebuild.startRebuild acceptedTransferNeedsReadModel ["my-service-accepted-transfer-needs-async"]
+  Rebuild.startRebuild acceptedTransferNeedsReadModel ["my-service-accepted-transfer-needs-async"]
 
 finishAcceptedTransferNeedsRebuild :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)
 finishAcceptedTransferNeedsRebuild =
-    Rebuild.finishRebuild acceptedTransferNeedsReadModel ["my-service-accepted-transfer-needs-async"]
+  Rebuild.finishRebuild acceptedTransferNeedsReadModel ["my-service-accepted-transfer-needs-async"]
 
 abandonAcceptedTransferNeedsRebuild :: (Store :> es) => Eff es ReadModelMetadata
 abandonAcceptedTransferNeedsRebuild = Rebuild.abandonRebuild acceptedTransferNeedsReadModel
 
 acceptedTransferNeedsAsyncProjection :: AsyncProjection
 acceptedTransferNeedsAsyncProjection =
-    AsyncProjection
-        { name = "my-service-accepted-transfer-needs-async"
-        , readModelName = "my-service-accepted-transfer-needs"
-        , subscriptionName = "my-service-accepted-transfer-needs-sub"
-        , applyRecorded = applyAcceptedTransferNeeds
-        , idempotencyKey = \recorded -> recorded.eventId
-        }
+  AsyncProjection
+    { name = "my-service-accepted-transfer-needs-async"
+    , readModelName = "my-service-accepted-transfer-needs"
+    , subscriptionName = "my-service-accepted-transfer-needs-sub"
+    , applyRecorded = applyAcceptedTransferNeeds
+    , idempotencyKey = \recorded -> recorded.eventId
+    }
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelHarness.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelHarness.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelHarness.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelHarness.hs
@@ -1,19 +1,19 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel accepted_transfer_needs; do not edit.
 module SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModelHarness (readModelFacts, runReadModelFacts) where
 
 -- | (fact, expected from notation, actual shared derivation/lowering).
 readModelFacts :: [(String, String, String)]
 readModelFacts =
-    [ ("registryName", "my-service-accepted-transfer-needs", "my-service-accepted-transfer-needs")
-    , ("subscriptionName", "my-service-accepted-transfer-needs-sub", "my-service-accepted-transfer-needs-sub")
-    , ("shapeHash", "fnv1a:fec517dae7760b8a", "fnv1a:fec517dae7760b8a")
-    , ("asyncProjectionName", "my-service-accepted-transfer-needs-async", "my-service-accepted-transfer-needs-async")
-    , ("consistency", "Eventual", "Eventual")
-    , ("strongScope", "EntireLog", "EntireLog")
-    ]
+  [ ("registryName", "my-service-accepted-transfer-needs", "my-service-accepted-transfer-needs")
+  , ("subscriptionName", "my-service-accepted-transfer-needs-sub", "my-service-accepted-transfer-needs-sub")
+  , ("shapeHash", "fnv1a:fec517dae7760b8a", "fnv1a:fec517dae7760b8a")
+  , ("asyncProjectionName", "my-service-accepted-transfer-needs-async", "my-service-accepted-transfer-needs-async")
+  , ("consistency", "Eventual", "Eventual")
+  , ("strongScope", "EntireLog", "EntireLog")
+  ]
 
 runReadModelFacts :: IO Bool
 runReadModelFacts = do
-    let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
-    mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
-    pure (null failures)
+  let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
+  mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
+  pure (null failures)
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelTable.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelTable.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelTable.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Accepted_transfer_needs/ReadModelTable.hs
@@ -1,5 +1,4 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel accepted_transfer_needs; do not edit.
 module SkelQueue.Generated.MyService.Accepted_transfer_needs.ReadModelTable (acceptedTransferNeedsQualifiedTable) where
 
 import Data.Text (Text)
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/Queue.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/Queue.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/Queue.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/Queue.hs
@@ -1,20 +1,17 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module SkelQueue.Generated.MyService.Reservation_work.Queue
+  ( ReservationWorkItem (..)
+  , encodeReservationWorkItem
+  , parseReservationWorkItem
+  , queuePhysical, queueDlq, queueTable
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelQueue.Generated.MyService.Reservation_work.Queue (
-    ReservationWorkItem (..),
-    encodeReservationWorkItem,
-    parseReservationWorkItem,
-    queuePhysical,
-    queueDlq,
-    queueTable,
-) where
+  ) where
 
 import Data.Aeson (Value, object, withObject, (.:), (.=))
 import Data.Aeson.Types (parseEither)
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 
 queuePhysical, queueDlq, queueTable :: Text
 queuePhysical = "my_service_reservation_work"
@@ -22,17 +19,17 @@
 queueTable = "pgmq.q_my_service_reservation_work"
 
 data ReservationWorkItem = ReservationWorkItem
-    { reservationId :: !Text
-    , hospitalId :: !Text
-    }
-    deriving stock (Eq, Show)
+  { reservationId :: !Text
+  , hospitalId :: !Text
+  }
+  deriving stock (Eq, Show)
 
 encodeReservationWorkItem :: ReservationWorkItem -> Value
 encodeReservationWorkItem p =
-    object
-        [ "reservation_id" .= p.reservationId
-        , "hospital_id" .= p.hospitalId
-        ]
+  object
+    [ "reservation_id" .= p.reservationId
+    , "hospital_id" .= p.hospitalId
+    ]
 
 parseReservationWorkItem :: Value -> Either Text ReservationWorkItem
 parseReservationWorkItem = mapLeftText . parseEither (withObject "ReservationWorkItem" go)
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueueCodec.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueueCodec.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueueCodec.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueueCodec.hs
@@ -1,15 +1,12 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-
-{- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
-
-Deploy workers before producers when raising its schema version. Do not
-adopt this codec on a non-empty bare-payload queue without draining it
-(or supplying a transitional codec), or in-flight messages will
-dead-letter. This is telemetry-neutral:
-docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
-spans and acknowledgement vocabulary.
--}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+-- | Versioned job payload envelope: @{\"v\",\"t\",\"data\"}@.
+--
+-- Deploy workers before producers when raising its schema version. Do not
+-- adopt this codec on a non-empty bare-payload queue without draining it
+-- (or supplying a transitional codec), or in-flight messages will
+-- dead-letter. This is telemetry-neutral:
+-- docs/adr/0001-keiro-pgmq-job-processing-telemetry-contract.md owns
+-- spans and acknowledgement vocabulary.
 module SkelQueue.Generated.MyService.Reservation_work.QueueCodec (reservationWorkPayloadCodec, reservationWorkJobCodec) where
 
 import Data.List.NonEmpty (NonEmpty (..))
@@ -19,14 +16,14 @@
 
 reservationWorkPayloadCodec :: Codec ReservationWorkItem
 reservationWorkPayloadCodec =
-    Codec
-        { eventTypes = EventType "ReservationWorkItem" :| []
-        , eventType = \_ -> EventType "ReservationWorkItem"
-        , schemaVersion = 1
-        , encode = encodeReservationWorkItem
-        , decode = \_ -> parseReservationWorkItem
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ReservationWorkItem" :| []
+    , eventType = \_ -> EventType "ReservationWorkItem"
+    , schemaVersion = 1
+    , encode = encodeReservationWorkItem
+    , decode = \_ -> parseReservationWorkItem
+    , upcasters = []
+    }
 
 reservationWorkJobCodec :: JobCodec ReservationWorkItem
 reservationWorkJobCodec = keiroJobCodec reservationWorkPayloadCodec
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueuePolicy.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueuePolicy.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueuePolicy.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Reservation_work/QueuePolicy.hs
@@ -1,15 +1,10 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelQueue.Generated.MyService.Reservation_work.QueuePolicy (
-    retryPolicy,
-    jobOutcomeFor,
-    jobOrdering,
-    jobTuningFor,
-    queueProvision,
-)
-where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workqueue reservation_work; do not edit.
+module SkelQueue.Generated.MyService.Reservation_work.QueuePolicy
+  ( ReservationWorkOutcome (..)
+  , retryPolicy, jobOutcomeFor
+  , jobOrdering, jobTuningFor, queueProvision
+  ) where
 
-import Data.Text (Text)
 import Keiro.PGMQ.Job (JobOrdering (..), JobOutcome (..), JobTuning, PartitionSpec (..), QueueProvision, RetryDelay (..), RetryPolicy (..), partitionedProvision, standardProvision, unloggedProvision, withFifoIndexProvision, withOrdering)
 
 jobOrdering :: JobOrdering
@@ -25,18 +20,24 @@
 
 retryPolicy :: RetryPolicy
 retryPolicy =
-    RetryPolicy
-        { maxRetries = 3
-        , defaultRetryDelay = RetryDelay 5
-        , useDeadLetter = True
-        }
+  RetryPolicy
+    { maxRetries = 3
+    , defaultRetryDelay = RetryDelay 5
+    , useDeadLetter = True
+    }
 
 -- The consumer JobOutcome disposition over the spec's named domain outcomes,
 -- lowered to the live Keiro.PGMQ.Job.JobOutcome.
-jobOutcomeFor :: Text -> JobOutcome
+data ReservationWorkOutcome
+  = StoreFailure
+  | CommandRejected
+  | DecodeFailure
+  | OnCodecReject
+  deriving stock (Eq, Show)
+
+jobOutcomeFor :: ReservationWorkOutcome -> JobOutcome
 jobOutcomeFor o = case o of
-    "storeFailure" -> Retry (RetryDelay 5)
-    "commandRejected" -> Dead "dead-lettered"
-    "decodeFailure" -> Dead "dead-lettered"
-    "onCodecReject" -> Dead "dead-lettered"
-    _ -> Retry (RetryDelay 5)
+  StoreFailure -> Retry (RetryDelay 5)
+  CommandRejected -> Dead "dead-lettered"
+  DecodeFailure -> Dead "dead-lettered"
+  OnCodecReject -> Dead "dead-lettered"
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModel.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModel.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModel.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModel.hs
@@ -1,62 +1,61 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelQueue.Generated.MyService.Transfer_decisions.ReadModel (
-    transferDecisionsReadModel,
-    transferDecisionsQualifiedTable,
-    registerTransferDecisions,
-    startTransferDecisionsRebuild,
-    finishTransferDecisionsRebuild,
-    abandonTransferDecisionsRebuild,
-    transferDecisionsAsyncProjection,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
+module SkelQueue.Generated.MyService.Transfer_decisions.ReadModel
+  ( transferDecisionsReadModel
+  , transferDecisionsQualifiedTable
+  , registerTransferDecisions
+  , startTransferDecisionsRebuild
+  , finishTransferDecisionsRebuild
+  , abandonTransferDecisionsRebuild
+  , transferDecisionsAsyncProjection
+  ) where
 
 import Data.Functor (void)
 import Effectful (Eff, (:>))
+import SkelQueue.Generated.MyService.Transfer_decisions.ReadModelTable (transferDecisionsQualifiedTable)
+import SkelQueue.MyService.Transfer_decisions.ReadModelHoles (TransferDecisionsQueryInput, TransferDecisionsQueryResult, transferDecisionsQuery, applyTransferDecisions)
 import Keiro.Projection (AsyncProjection (..))
 import Keiro.ReadModel (ConsistencyMode (..), ReadModel (..), ReadModelMetadata, StrongScope (..), registerReadModel)
 import Keiro.ReadModel.Rebuild qualified as Rebuild
 import Kiroku.Store.Effect (Store)
 import Kiroku.Store.Types (GlobalPosition, RecordedEvent (..))
-import SkelQueue.Generated.MyService.Transfer_decisions.ReadModelTable (transferDecisionsQualifiedTable)
-import SkelQueue.MyService.Transfer_decisions.ReadModelHoles (TransferDecisionsQueryInput, TransferDecisionsQueryResult, applyTransferDecisions, transferDecisionsQuery)
 
 transferDecisionsReadModel :: ReadModel TransferDecisionsQueryInput TransferDecisionsQueryResult
 transferDecisionsReadModel =
-    ReadModel
-        { name = "my-service-transfer-decisions"
-        , tableName = "transfer_decisions"
-        , schema = "my_service"
-        , subscriptionName = "my-service-transfer-decisions-sub"
-        , version = 1
-        , shapeHash = "fnv1a:d44d218822582783"
-        , defaultConsistency = Eventual
-        , strongScope = EntireLog
-        , query = transferDecisionsQuery
-        }
+  ReadModel
+    { name = "my-service-transfer-decisions"
+    , tableName = "transfer_decisions"
+    , schema = "my_service"
+    , subscriptionName = "my-service-transfer-decisions-sub"
+    , version = 1
+    , shapeHash = "fnv1a:d44d218822582783"
+    , defaultConsistency = Eventual
+    , strongScope = EntireLog
+    , query = transferDecisionsQuery
+    }
 
 -- Call once at projection startup before serving queries.
 registerTransferDecisions :: (Store :> es) => Eff es ()
 registerTransferDecisions =
-    void (registerReadModel "my-service-transfer-decisions" 1 "fnv1a:d44d218822582783")
+  void (registerReadModel "my-service-transfer-decisions" 1 "fnv1a:d44d218822582783")
 
 startTransferDecisionsRebuild :: (Store :> es) => GlobalPosition -> Eff es ReadModelMetadata
 startTransferDecisionsRebuild =
-    Rebuild.startRebuild transferDecisionsReadModel ["my-service-transfer-decisions-async"]
+  Rebuild.startRebuild transferDecisionsReadModel ["my-service-transfer-decisions-async"]
 
 finishTransferDecisionsRebuild :: (Store :> es) => GlobalPosition -> Eff es (Either Rebuild.RebuildError ReadModelMetadata)
 finishTransferDecisionsRebuild =
-    Rebuild.finishRebuild transferDecisionsReadModel ["my-service-transfer-decisions-async"]
+  Rebuild.finishRebuild transferDecisionsReadModel ["my-service-transfer-decisions-async"]
 
 abandonTransferDecisionsRebuild :: (Store :> es) => Eff es ReadModelMetadata
 abandonTransferDecisionsRebuild = Rebuild.abandonRebuild transferDecisionsReadModel
 
 transferDecisionsAsyncProjection :: AsyncProjection
 transferDecisionsAsyncProjection =
-    AsyncProjection
-        { name = "my-service-transfer-decisions-async"
-        , readModelName = "my-service-transfer-decisions"
-        , subscriptionName = "my-service-transfer-decisions-sub"
-        , applyRecorded = applyTransferDecisions
-        , idempotencyKey = \recorded -> recorded.eventId
-        }
+  AsyncProjection
+    { name = "my-service-transfer-decisions-async"
+    , readModelName = "my-service-transfer-decisions"
+    , subscriptionName = "my-service-transfer-decisions-sub"
+    , applyRecorded = applyTransferDecisions
+    , idempotencyKey = \recorded -> recorded.eventId
+    }
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelHarness.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelHarness.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelHarness.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelHarness.hs
@@ -1,19 +1,19 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
 module SkelQueue.Generated.MyService.Transfer_decisions.ReadModelHarness (readModelFacts, runReadModelFacts) where
 
 -- | (fact, expected from notation, actual shared derivation/lowering).
 readModelFacts :: [(String, String, String)]
 readModelFacts =
-    [ ("registryName", "my-service-transfer-decisions", "my-service-transfer-decisions")
-    , ("subscriptionName", "my-service-transfer-decisions-sub", "my-service-transfer-decisions-sub")
-    , ("shapeHash", "fnv1a:d44d218822582783", "fnv1a:d44d218822582783")
-    , ("asyncProjectionName", "my-service-transfer-decisions-async", "my-service-transfer-decisions-async")
-    , ("consistency", "Eventual", "Eventual")
-    , ("strongScope", "EntireLog", "EntireLog")
-    ]
+  [ ("registryName", "my-service-transfer-decisions", "my-service-transfer-decisions")
+  , ("subscriptionName", "my-service-transfer-decisions-sub", "my-service-transfer-decisions-sub")
+  , ("shapeHash", "fnv1a:d44d218822582783", "fnv1a:d44d218822582783")
+  , ("asyncProjectionName", "my-service-transfer-decisions-async", "my-service-transfer-decisions-async")
+  , ("consistency", "Eventual", "Eventual")
+  , ("strongScope", "EntireLog", "EntireLog")
+  ]
 
 runReadModelFacts :: IO Bool
 runReadModelFacts = do
-    let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
-    mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
-    pure (null failures)
+  let failures = [(fact, expected, actual) | (fact, expected, actual) <- readModelFacts, expected /= actual]
+  mapM_ (\(fact, expected, actual) -> putStrLn ("FAIL  " <> fact <> " expected=" <> show expected <> " actual=" <> show actual)) failures
+  pure (null failures)
diff --git a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelTable.hs b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelTable.hs
--- a/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelTable.hs
+++ b/test/conformance-skeletons/SkelQueue/Generated/MyService/Transfer_decisions/ReadModelTable.hs
@@ -1,5 +1,4 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from readmodel transfer_decisions; do not edit.
 module SkelQueue.Generated.MyService.Transfer_decisions.ReadModelTable (transferDecisionsQualifiedTable) where
 
 import Data.Text (Text)
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Codec.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Codec.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Codec.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Codec.hs
@@ -1,49 +1,56 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module SkelRouter.Generated.MyService.Page.Codec (
     pageCodec,
     parsePageEvent,
     encodePageEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import SkelRouter.Generated.MyService.Page.Domain
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
-import SkelRouter.Generated.MyService.Page.Domain
 
+
+
+
+
 pageCodec :: Codec PageEvent
 pageCodec =
-    Codec
-        { eventTypes = EventType "PageSent" :| []
-        , eventType = \case
-            PageSent{} -> EventType "PageSent"
-        , schemaVersion = 1
-        , encode = encodePageEvent
-        , decode = parsePageEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "PageSent" :| []
+    , eventType = \case
+        PageSent{} -> EventType "PageSent"
+    , schemaVersion = 1
+    , encode = encodePageEvent
+    , decode = parsePageEvent
+    , upcasters = []
+    }
 
 encodePageEvent :: PageEvent -> Value
 encodePageEvent = \case
-    PageSent payload ->
-        object
-            [ "kind" .= ("PageSent" :: Text)
-            , "incidentId" .= payload.incidentId
-            , "responderId" .= payload.responderId
-            ]
+  PageSent payload ->
+    object
+      [ "kind" .= ("PageSent" :: Text)
+      , "incidentId" .= payload.incidentId
+      , "responderId" .= payload.responderId
+      ]
 
 parsePageEvent :: EventType -> Value -> Either Text PageEvent
 parsePageEvent (EventType tag) = mapLeftText . parseEither (withObject "PageEvent" go)
   where
     go o = do
-        case tag of
-            "PageSent" ->
-                PageSent <$> (PageSentData <$> o .: "incidentId" <*> o .: "responderId")
-            _ -> fail "unknown event type"
+      case tag of
+        "PageSent" ->
+          PageSent
+            <$> ( PageSentData
+                    <$> o .: "incidentId"
+                    <*> o .: "responderId"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: PageSent")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Domain.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Domain.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Domain.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Domain.hs
@@ -2,9 +2,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module SkelRouter.Generated.MyService.Page.Domain where
 
 import Data.Proxy (Proxy (..))
@@ -14,33 +12,35 @@
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data PageVertex = PagePending | PageDelivered
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
 data SendPageData = SendPageData
-    { incidentId :: !Text
-    , responderId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { incidentId :: !Text
+  , responderId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data PageCommand = SendPage !SendPageData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data PageSentData = PageSentData
-    { incidentId :: !Text
-    , responderId :: !Text
-    }
-    deriving stock (Generic, Eq, Show)
+  { incidentId :: !Text
+  , responderId :: !Text
+  }
+  deriving stock (Generic, Eq, Show)
 
 data PageEvent = PageSent !PageSentData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 type PageRegs =
-    '[]
+  '[]
 
 initialPageRegs :: RegFile PageRegs
 initialPageRegs =
-    RNil
+  RNil
 
 $(deriveAggregateCtorsAll ''PageCommand ''PageRegs)
+
+
 
 $(deriveWireCtorsAll ''PageEvent)
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/EventStream.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/EventStream.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/EventStream.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelRouter.Generated.MyService.Page.EventStream (
-    pageCategory,
-    pageEventStream,
-    pageEventStreamDef,
-    PageEventStream,
-    PageEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
+module SkelRouter.Generated.MyService.Page.EventStream
+  ( pageCategory
+  , pageCommandCategory
+  , pageEventStream
+  , pageEventStreamDef
+  , PageEventStream
+  , PageEventStreamDef
+  ) where
 
+import SkelRouter.Generated.MyService.Page.Domain
+import SkelRouter.Generated.MyService.Page.Codec (pageCodec)
+import SkelRouter.Generated.MyService.Page.Transducer (pageFoldFingerprint, pageTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import SkelRouter.Generated.MyService.Page.Codec (pageCodec)
-import SkelRouter.Generated.MyService.Page.Domain
-import SkelRouter.MyService.Page.Holes (pageTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-pageCategory :: Stream.StreamCategory a
+pageCategory :: Stream.StreamCategory PageEventStreamDef
 pageCategory = Stream.categoryUnsafe "page"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+pageCommandCategory :: Stream.StreamCategory PageCommand
+pageCommandCategory = Stream.categoryUnsafe "page"
+
 type PageEventStreamDef =
-    EventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
+  EventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
 
 type PageEventStream =
-    ValidatedEventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
+  ValidatedEventStream (HsPred PageRegs PageCommand) PageRegs PageVertex PageCommand PageEvent
 
 pageEventStreamDef :: PageEventStreamDef
 pageEventStreamDef =
-    EventStream
-        { transducer = pageTransducer
-        , initialState = PagePending
-        , initialRegisters = initialPageRegs
-        , eventCodec = pageCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = pageTransducer,
+      initialState = PagePending,
+      initialRegisters = initialPageRegs,
+      eventCodec = pageCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 pageEventStream :: PageEventStream
 pageEventStream =
-    mkEventStreamOrThrow "Page" pageEventStreamDef
+  mkEventStreamOrThrow "Page" pageEventStreamDef
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Harness.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Harness.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Harness.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Harness.hs
@@ -1,27 +1,25 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module SkelRouter.Generated.MyService.Page.Harness (harnessAssertions) where
 
+import SkelRouter.Generated.MyService.Page.Domain
+import SkelRouter.Generated.MyService.Page.Codec (encodePageEvent, parsePageEvent, pageCodec)
+import SkelRouter.Generated.MyService.Page.Transducer (pageTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer)
 import Keiro.Codec (eventType)
-import SkelRouter.Generated.MyService.Page.Codec (encodePageEvent, pageCodec, parsePageEvent)
-import SkelRouter.Generated.MyService.Page.Domain
-import SkelRouter.MyService.Page.Holes (pageTransducer)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions pageTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: PageSent", roundTrips sampleEventPageSent)
-    , ("accepts SendPage from PagePending", acceptSendPage)
-    ]
-        ++ forwardReplaySendPage
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions pageTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: PageSent", roundTrips sampleEventPageSent)
+  , ("accepts SendPage from PagePending", acceptSendPage)
+  ]
+  ++ forwardReplaySendPage
 
 roundTrips :: PageEvent -> Bool
 roundTrips e = parsePageEvent (eventType pageCodec e) (encodePageEvent e) == Right e
@@ -31,24 +29,24 @@
 
 acceptSendPage :: Bool
 acceptSendPage =
-    case step pageTransducer (PagePending, initialPageRegs) ((SendPage (SendPageData "sample-incidentId" "sample-responderId"))) of
-        Just (v, _, _) -> v == PageDelivered
-        Nothing -> False
+  case step pageTransducer (PagePending, initialPageRegs) ((SendPage (SendPageData "sample-incidentId" "sample-responderId"))) of
+    Just (v, _, _) -> v == PageDelivered
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplaySendPage :: [(String, Bool)]
 forwardReplaySendPage =
-    case step pageTransducer (PagePending, initialPageRegs) ((SendPage (SendPageData "sample-incidentId" "sample-responderId"))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, _forwardRegs, emitted) ->
-            case mapM (\event -> parsePageEvent (eventType pageCodec event) (encodePageEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither pageTransducer (PagePending, initialPageRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, _replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            ]
+  case step pageTransducer (PagePending, initialPageRegs) ((SendPage (SendPageData "sample-incidentId" "sample-responderId"))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, _forwardRegs, emitted) ->
+      case mapM (\event -> parsePageEvent (eventType pageCodec event) (encodePageEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither pageTransducer (PagePending, initialPageRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, _replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              ]
   where
     prefix = "forward/replay equality: SendPage from PagePending -- "
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Projection.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Projection.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Projection.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
 module SkelRouter.Generated.MyService.Page.Projection () where
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Transducer.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/Page/Transducer.hs
@@ -0,0 +1,59 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Page; do not edit.
+module SkelRouter.Generated.MyService.Page.Transducer
+  ( pageTransducer
+  , pageFoldFingerprint
+  , BehaviorOwnership (..)
+  , pagePredicateVerifications
+  ) where
+
+import SkelRouter.Generated.MyService.Page.Domain
+import Data.Text (Text)
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+
+pageTransducer
+  :: SymTransducer
+       (HsPred PageRegs PageCommand)
+       PageRegs
+       PageVertex
+       PageCommand
+       PageEvent
+pageTransducer =
+  B.buildTransducer PagePending initialPageRegs isTerminal do
+    B.from PagePending do
+      B.onCmd inCtorSendPage $ \d -> B.do
+        B.emit wirePageSent (PageSentTermFields
+          { incidentId = d.incidentId
+          , responderId = d.responderId
+          })
+        B.goto PageDelivered
+ where
+  isTerminal = \case
+    PageDelivered -> True
+    _ -> False
+
+pageFoldFingerprint :: Text
+pageFoldFingerprint = "32a87df625245b05c0307606293a5650"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+pagePredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+pagePredicateVerifications = sequence
+  [ verifyTransition "transition1PendingSendPage" GeneratedOwned PagePending 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut pageTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/Router.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/Router.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/Router.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/Router.hs
@@ -1,9 +1,8 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelRouter.Generated.MyService.PagingRouter.Router (
-    pagingRouterName,
-    pagingRouterWorkerOptions,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
+module SkelRouter.Generated.MyService.PagingRouter.Router
+  ( pagingRouterName
+  , pagingRouterWorkerOptions
+  ) where
 
 import Data.Text (Text)
 import Keiro.ProcessManager (PoisonPolicy (..), RejectedCommandPolicy (..), WorkerOptions (..))
@@ -21,9 +20,9 @@
 -- Keiro.Router.runRouterWorkerWith; do not silently use defaultWorkerOptions.
 pagingRouterWorkerOptions :: WorkerOptions es msg
 pagingRouterWorkerOptions =
-    WorkerOptions
-        { poisonPolicy = PoisonHalt
-        , rejectedCommandPolicy = RejectedHalt
-        , transientRetryDelay = RetryDelay 5 -- matches defaultWorkerOptions; runtime tuning
-        , metrics = Nothing -- runtime configuration; install at call site
-        }
+  WorkerOptions
+    { poisonPolicy = PoisonHalt,
+      rejectedCommandPolicy = RejectedHalt,
+      transientRetryDelay = RetryDelay 5, -- matches defaultWorkerOptions; runtime tuning
+      metrics = Nothing -- runtime configuration; install at call site
+    }
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/RouterHarness.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/RouterHarness.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/RouterHarness.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/PagingRouter/RouterHarness.hs
@@ -1,16 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from router PagingRouter; do not edit.
 module SkelRouter.Generated.MyService.PagingRouter.RouterHarness (routerHarnessValues) where
 
 routerHarnessValues :: [(String, String)]
 routerHarnessValues =
-    [ ("routerName", "paging-router")
-    , ("keyField", "incidentId")
-    , ("resolveSource", "hole")
-    , ("resolveRow", "responderId")
-    , ("dispatchCommand", "SendPage")
-    , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
-    , ("onDuplicate", "AckOk")
-    , ("onFailed", "Retry")
-    , ("rejectedPolicy", "halt")
-    , ("poisonPolicy", "halt")
-    ]
+  [ ("routerName", "paging-router")
+  , ("keyField", "incidentId")
+  , ("resolveSource", "hole")
+  , ("resolveRow", "responderId")
+  , ("dispatchCommand", "SendPage")
+  , ("dispatchIdInputs", "(name, key, sourceEventId, targetStreamName, occurrence)")
+  , ("onDuplicate", "AckOk")
+  , ("onFailed", "Retry")
+  , ("rejectedPolicy", "halt")
+  , ("poisonPolicy", "halt")
+  ]
diff --git a/test/conformance-skeletons/SkelRouter/Generated/MyService/ReplayAudit.hs b/test/conformance-skeletons/SkelRouter/Generated/MyService/ReplayAudit.hs
--- a/test/conformance-skeletons/SkelRouter/Generated/MyService/ReplayAudit.hs
+++ b/test/conformance-skeletons/SkelRouter/Generated/MyService/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context my-service replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -10,16 +9,16 @@
 --   * any non-zero audit exit blocks deployment.
 module SkelRouter.Generated.MyService.ReplayAudit (auditTargets) where
 
+import SkelRouter.Generated.MyService.Page.EventStream qualified as Page
 import Keiro.ReplayAudit (AuditTarget (..), SomeAuditTarget (..), streamInCategory)
 import Keiro.Stream qualified as Stream
-import SkelRouter.Generated.MyService.Page.EventStream qualified as Page
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Page.pageEventStream
-            , category = Stream.categoryText Page.pageCategory
-            , mkStream = streamInCategory (Stream.categoryText Page.pageCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Page.pageEventStream
+        , category = Stream.categoryText Page.pageCategory
+        , mkStream = streamInCategory (Stream.categoryText Page.pageCategory)
+        }
+  ]
diff --git a/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowFacts.hs b/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowFacts.hs
--- a/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowFacts.hs
+++ b/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowFacts.hs
@@ -1,17 +1,26 @@
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workflow HospitalTransferReservation; do not edit.
+module SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowFacts (WorkflowFacts (..), workflowFacts) where
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowFacts (workflowFacts) where
+-- | The workflow's deterministic decisions, pinned as typed pure facts.
+-- A driver asserts them against a hand-written expectation, so a spec
+-- change (e.g. renaming an await) reddens a specific assertion.
+data WorkflowFacts = WorkflowFacts
+  { workflowFactName :: !String
+  , workflowFactIdVia :: !String
+  , workflowFactIdField :: !String
+  , workflowFactBody :: ![String]
+  , workflowFactAwaitLabels :: ![String]
+  , workflowFactPatchIds :: ![String]
+  }
+  deriving stock (Eq, Show)
 
-{- | (label, value): the workflow's deterministic decisions, pinned as pure
-facts. A driver asserts them against a hand-written expectation, so a spec
-change (e.g. renaming an await) reddens a specific assertion.
--}
-workflowFacts :: [(String, String)]
+workflowFacts :: WorkflowFacts
 workflowFacts =
-    [ ("name", "hospital-transfer-reservation")
-    , ("idVia", "idText")
-    , ("idField", "reservationId")
-    , ("body", "step:create-transfer-hold,await:reservation-confirmation,step:summarize-reservation")
-    , ("awaits", "reservation-confirmation")
-    , ("patches", "")
-    ]
+  WorkflowFacts
+    { workflowFactName = "hospital-transfer-reservation"
+    , workflowFactIdVia = "idText"
+    , workflowFactIdField = "reservationId"
+    , workflowFactBody = ["step:create-transfer-hold", "await:reservation-confirmation", "step:summarize-reservation"]
+    , workflowFactAwaitLabels = ["reservation-confirmation"]
+    , workflowFactPatchIds = []
+    }
diff --git a/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowRuntime.hs b/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowRuntime.hs
--- a/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowRuntime.hs
+++ b/test/conformance-skeletons/SkelWorkflow/Generated/MyService/HospitalTransferReservation/WorkflowRuntime.hs
@@ -1,13 +1,12 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowRuntime (
-    workflowName,
-    awaitAwakeableId,
-    awaitLabels,
-    declaredPatches,
-    declaredPatchStepNames,
-    withDeclaredPatches,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workflow HospitalTransferReservation; do not edit.
+module SkelWorkflow.Generated.MyService.HospitalTransferReservation.WorkflowRuntime
+  ( workflowName
+  , awaitAwakeableId
+  , awaitLabels
+  , declaredPatches
+  , declaredPatchStepNames
+  , withDeclaredPatches
+  ) where
 
 import Data.Set (Set)
 import Data.Set qualified as Set
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Nominals.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Nominals.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal declarations; do not edit.
+module Generated.HospitalCapacity.Nominals
+  ( BedType (..)
+  , bedTypeText
+  , CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , DivertStatus (..)
+  , divertStatusText
+  , DivertStatusEqualityProjection
+  , divertStatusEqualityWitness
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , PatientAcuity (..)
+  , patientAcuityText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.HospitalCapacity.Nominals.Internal (CommandId, mkCommandId, parseCommandId, commandIdText, HospitalId, mkHospitalId, parseHospitalId, hospitalIdText, TransferReservationId, mkTransferReservationId, parseTransferReservationId, transferReservationIdText)
+import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
+import Data.List.NonEmpty (NonEmpty (..))
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+data BedType = Icu | MedicalSurgical
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName BedType
+
+bedTypeText :: BedType -> Text
+bedTypeText = \case
+  Icu -> "icu"
+  MedicalSurgical -> "medical-surgical"
+
+instance CanonicalTypeName CommandId
+
+data DivertStatus = Open | PartialDivert | TotalDivert
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName DivertStatus
+
+divertStatusText :: DivertStatus -> Text
+divertStatusText = \case
+  Open -> "open"
+  PartialDivert -> "partial-divert"
+  TotalDivert -> "total-divert"
+
+data DivertStatusEqualityProjection
+
+instance FieldProjection DivertStatusEqualityProjection where
+  type FieldName DivertStatusEqualityProjection = "DivertStatus"
+  type FieldOwner DivertStatusEqualityProjection = DivertStatus
+  type FieldResult DivertStatusEqualityProjection = Text
+  fieldShapeId _ = "nominal-equality|name=DivertStatus|contract=keiro-dsl/nominal-equality/1|key=Text|domain=finite-text:open,partial-divert,total-divert|owner=generated"
+  projectFieldValue _ = divertStatusText
+
+instance ExactFieldProjection DivertStatusEqualityProjection where
+  fieldProjectionDomain _ = finiteProjectionDomain ("open" :| ["partial-divert", "total-divert"])
+  reconstructFieldOwner _ = \case
+    "open" -> Just Open
+    "partial-divert" -> Just PartialDivert
+    "total-divert" -> Just TotalDivert
+    _ -> Nothing
+
+divertStatusEqualityWitness :: FieldWitness DivertStatusEqualityProjection
+divertStatusEqualityWitness = exactFieldWitness @DivertStatusEqualityProjection
+
+instance CanonicalTypeName HospitalId
+
+data PatientAcuity = RedTag | YellowTag | GreenTag
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName PatientAcuity
+
+patientAcuityText :: PatientAcuity -> Text
+patientAcuityText = \case
+  RedTag -> "red"
+  YellowTag -> "yellow"
+  GreenTag -> "green"
+
+instance CanonicalTypeName TransferReservationId
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Nominals/Internal.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Nominals/Internal.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal ID internals; do not edit.
+module Generated.HospitalCapacity.Nominals.Internal
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , unsafeCommandIdFromLegacyText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  , unsafeTransferReservationIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CommandId = CommandId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CommandId where
+  toJSON = toJSON . commandIdText
+
+instance FromJSON CommandId where
+  parseJSON = withText "CommandId" (either (fail . T.unpack) pure . parseCommandId)
+
+parseCommandId :: Text -> Either Text CommandId
+parseCommandId input = case validateIdDomainText (typeIdV7Domain "cmd") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CommandId input)
+
+mkCommandId :: Text -> Either Text CommandId
+mkCommandId = parseCommandId
+
+commandIdText :: CommandId -> Text
+commandIdText (CommandId value) = value
+
+unsafeCommandIdFromLegacyText :: Text -> CommandId
+unsafeCommandIdFromLegacyText = CommandId
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
+
+newtype TransferReservationId = TransferReservationId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON TransferReservationId where
+  toJSON = toJSON . transferReservationIdText
+
+instance FromJSON TransferReservationId where
+  parseJSON = withText "TransferReservationId" (either (fail . T.unpack) pure . parseTransferReservationId)
+
+parseTransferReservationId :: Text -> Either Text TransferReservationId
+parseTransferReservationId input = case validateIdDomainText (typeIdV7Domain "rsv") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (TransferReservationId input)
+
+mkTransferReservationId :: Text -> Either Text TransferReservationId
+mkTransferReservationId = parseTransferReservationId
+
+transferReservationIdText :: TransferReservationId -> Text
+transferReservationIdText (TransferReservationId value) = value
+
+unsafeTransferReservationIdFromLegacyText :: Text -> TransferReservationId
+unsafeTransferReservationIdFromLegacyText = TransferReservationId
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/ReplayAudit.hs b/test/conformance-snapshot/Generated/HospitalCapacity/ReplayAudit.hs
--- a/test/conformance-snapshot/Generated/HospitalCapacity/ReplayAudit.hs
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Reservation.reservationEventStream
-            , category = Stream.categoryText Reservation.reservationCategory
-            , mkStream = streamInCategory (Stream.categoryText Reservation.reservationCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Reservation.reservationEventStream
+        , category = Stream.categoryText Reservation.reservationCategory
+        , mkStream = streamInCategory (Stream.categoryText Reservation.reservationCategory)
+        }
+  ]
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Codec.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Codec.hs
--- a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Codec.hs
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Codec.hs
@@ -1,83 +1,94 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Codec (
     reservationCodec,
     parseReservationEvent,
     encodeReservationEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.HospitalCapacity.Reservation.Domain
+import Generated.HospitalCapacity.Nominals (CommandId, commandIdText, DivertStatus (..), divertStatusText, HospitalId, hospitalIdText, PatientAcuity (..), patientAcuityText, TransferReservationId, transferReservationIdText)
+import Generated.HospitalCapacity.Nominals.Internal (unsafeCommandIdFromLegacyText, unsafeHospitalIdFromLegacyText, unsafeTransferReservationIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.HospitalCapacity.Reservation.Domain
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
 
-parsePatientAcuity :: Text -> Parser PatientAcuity
-parsePatientAcuity = \case
-    "red" -> pure RedTag
-    "yellow" -> pure YellowTag
-    "green" -> pure GreenTag
-    _ -> fail "unknown PatientAcuity"
 
-parseBedType :: Text -> Parser BedType
-parseBedType = \case
-    "icu" -> pure Icu
-    "medical-surgical" -> pure MedicalSurgical
-    _ -> fail "unknown BedType"
-
 parseDivertStatus :: Text -> Parser DivertStatus
 parseDivertStatus = \case
-    "open" -> pure Open
-    "partial-divert" -> pure PartialDivert
-    "total-divert" -> pure TotalDivert
-    _ -> fail "unknown DivertStatus"
+  "open" -> pure Open
+  "partial-divert" -> pure PartialDivert
+  "total-divert" -> pure TotalDivert
+  tag -> fail ("unknown DivertStatus " <> show tag <> "; expected one of: open, partial-divert, total-divert")
 
+parsePatientAcuity :: Text -> Parser PatientAcuity
+parsePatientAcuity = \case
+  "red" -> pure RedTag
+  "yellow" -> pure YellowTag
+  "green" -> pure GreenTag
+  tag -> fail ("unknown PatientAcuity " <> show tag <> "; expected one of: red, yellow, green")
+
+
 reservationCodec :: Codec ReservationEvent
 reservationCodec =
-    Codec
-        { eventTypes = EventType "TransferReservationCreated" :| [EventType "TransferReservationConfirmed"]
-        , eventType = \case
-            TransferReservationCreated{} -> EventType "TransferReservationCreated"
-            TransferReservationConfirmed{} -> EventType "TransferReservationConfirmed"
-        , schemaVersion = 1
-        , encode = encodeReservationEvent
-        , decode = parseReservationEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "TransferReservationCreated" :| [EventType "TransferReservationConfirmed"]
+    , eventType = \case
+        TransferReservationCreated{} -> EventType "TransferReservationCreated"
+        TransferReservationConfirmed{} -> EventType "TransferReservationConfirmed"
+    , schemaVersion = 1
+    , encode = encodeReservationEvent
+    , decode = parseReservationEvent
+    , upcasters = []
+    }
 
 encodeReservationEvent :: ReservationEvent -> Value
 encodeReservationEvent = \case
-    TransferReservationCreated payload ->
-        object
-            [ "kind" .= ("TransferReservationCreated" :: Text)
-            , "reservationId" .= transferReservationIdText payload.reservationId
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            , "commandId" .= commandIdText payload.commandId
-            , "patientAcuity" .= patientAcuityText payload.patientAcuity
-            , "divertStatus" .= divertStatusText payload.divertStatus
-            , "lifeCriticalOverride" .= payload.lifeCriticalOverride
-            ]
-    TransferReservationConfirmed payload ->
-        object
-            [ "kind" .= ("TransferReservationConfirmed" :: Text)
-            , "reservationId" .= transferReservationIdText payload.reservationId
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            , "commandId" .= commandIdText payload.commandId
-            ]
+  TransferReservationCreated payload ->
+    object
+      [ "kind" .= ("TransferReservationCreated" :: Text)
+      , "reservationId" .= transferReservationIdText payload.reservationId
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "commandId" .= commandIdText payload.commandId
+      , "patientAcuity" .= patientAcuityText payload.patientAcuity
+      , "divertStatus" .= divertStatusText payload.divertStatus
+      , "lifeCriticalOverride" .= payload.lifeCriticalOverride
+      ]
+  TransferReservationConfirmed payload ->
+    object
+      [ "kind" .= ("TransferReservationConfirmed" :: Text)
+      , "reservationId" .= transferReservationIdText payload.reservationId
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "commandId" .= commandIdText payload.commandId
+      ]
 
 parseReservationEvent :: EventType -> Value -> Either Text ReservationEvent
 parseReservationEvent (EventType tag) = mapLeftText . parseEither (withObject "ReservationEvent" go)
   where
     go o = do
-        case tag of
-            "TransferReservationCreated" ->
-                TransferReservationCreated <$> (TransferReservationCreatedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId") <*> (o .: "patientAcuity" >>= parsePatientAcuity) <*> (o .: "divertStatus" >>= parseDivertStatus) <*> o .: "lifeCriticalOverride")
-            "TransferReservationConfirmed" ->
-                TransferReservationConfirmed <$> (TransferReservationConfirmedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId"))
-            _ -> fail "unknown event type"
+      case tag of
+        "TransferReservationCreated" ->
+          TransferReservationCreated
+            <$> ( TransferReservationCreatedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                    <*> explicitParseField (withText "PatientAcuity" parsePatientAcuity) o "patientAcuity"
+                    <*> explicitParseField (withText "DivertStatus" parseDivertStatus) o "divertStatus"
+                    <*> o .: "lifeCriticalOverride"
+                )
+        "TransferReservationConfirmed" ->
+          TransferReservationConfirmed
+            <$> ( TransferReservationConfirmedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: TransferReservationCreated, TransferReservationConfirmed")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Domain.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Domain.hs
--- a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Domain.hs
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Domain.hs
@@ -3,9 +3,7 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Domain where
 
 import Data.Aeson (FromJSON, ToJSON)
@@ -13,129 +11,72 @@
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 import Keiki.Shape (CanonicalStateShape, CanonicalTypeName)
-
-newtype TransferReservationId = TransferReservationId Text
-    deriving stock (Generic, Eq, Ord, Show)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName TransferReservationId
-
-transferReservationIdText :: TransferReservationId -> Text
-transferReservationIdText (TransferReservationId t) = t
-
-newtype HospitalId = HospitalId Text
-    deriving stock (Generic, Eq, Ord, Show)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName HospitalId
-
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId t) = t
-
-newtype CommandId = CommandId Text
-    deriving stock (Generic, Eq, Ord, Show)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName CommandId
-
-commandIdText :: CommandId -> Text
-commandIdText (CommandId t) = t
-
-data PatientAcuity = RedTag | YellowTag | GreenTag
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName PatientAcuity
-
-patientAcuityText :: PatientAcuity -> Text
-patientAcuityText = \case
-    RedTag -> "red"
-    YellowTag -> "yellow"
-    GreenTag -> "green"
-
-data BedType = Icu | MedicalSurgical
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName BedType
-
-bedTypeText :: BedType -> Text
-bedTypeText = \case
-    Icu -> "icu"
-    MedicalSurgical -> "medical-surgical"
-
-data DivertStatus = Open | PartialDivert | TotalDivert
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-    deriving anyclass (ToJSON, FromJSON)
-instance CanonicalTypeName DivertStatus
-
-divertStatusText :: DivertStatus -> Text
-divertStatusText = \case
-    Open -> "open"
-    PartialDivert -> "partial-divert"
-    TotalDivert -> "total-divert"
+import Generated.HospitalCapacity.Nominals (CommandId, parseCommandId, DivertStatus (..), HospitalId, parseHospitalId, PatientAcuity (..), TransferReservationId, parseTransferReservationId)
+import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ReservationVertex = ReservationUnrequested | ReservationHeld | ReservationConfirmed | ReservationExpired | ReservationAdmitted | ReservationReleased
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-    deriving anyclass (ToJSON, FromJSON)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
 instance CanonicalStateShape ReservationVertex
 instance CanonicalTypeName ReservationVertex
 
 data RequestTransferReservationData = RequestTransferReservationData
-    { reservationId :: !TransferReservationId
-    , hospitalId :: !HospitalId
-    , commandId :: !CommandId
-    , patientAcuity :: !PatientAcuity
-    , divertStatus :: !DivertStatus
-    , lifeCriticalOverride :: !Bool
-    }
-    deriving stock (Generic, Eq, Show)
+  { reservationId :: !TransferReservationId
+  , hospitalId :: !HospitalId
+  , commandId :: !CommandId
+  , patientAcuity :: !PatientAcuity
+  , divertStatus :: !DivertStatus
+  , lifeCriticalOverride :: !Bool
+  }
+  deriving stock (Generic, Eq, Show)
 
 data ConfirmReservationData = ConfirmReservationData
-    { reservationId :: !TransferReservationId
-    , hospitalId :: !HospitalId
-    , commandId :: !CommandId
-    }
-    deriving stock (Generic, Eq, Show)
+  { reservationId :: !TransferReservationId
+  , hospitalId :: !HospitalId
+  , commandId :: !CommandId
+  }
+  deriving stock (Generic, Eq, Show)
 
-data ReservationCommand
-    = RequestTransferReservation !RequestTransferReservationData
-    | ConfirmReservation !ConfirmReservationData
-    deriving stock (Generic, Eq, Show)
+data ReservationCommand = RequestTransferReservation !RequestTransferReservationData
+  | ConfirmReservation !ConfirmReservationData
+  deriving stock (Generic, Eq, Show)
 
 data TransferReservationCreatedData = TransferReservationCreatedData
-    { reservationId :: !TransferReservationId
-    , hospitalId :: !HospitalId
-    , commandId :: !CommandId
-    , patientAcuity :: !PatientAcuity
-    , divertStatus :: !DivertStatus
-    , lifeCriticalOverride :: !Bool
-    }
-    deriving stock (Generic, Eq, Show)
+  { reservationId :: !TransferReservationId
+  , hospitalId :: !HospitalId
+  , commandId :: !CommandId
+  , patientAcuity :: !PatientAcuity
+  , divertStatus :: !DivertStatus
+  , lifeCriticalOverride :: !Bool
+  }
+  deriving stock (Generic, Eq, Show)
 
 data TransferReservationConfirmedData = TransferReservationConfirmedData
-    { reservationId :: !TransferReservationId
-    , hospitalId :: !HospitalId
-    , commandId :: !CommandId
-    }
-    deriving stock (Generic, Eq, Show)
+  { reservationId :: !TransferReservationId
+  , hospitalId :: !HospitalId
+  , commandId :: !CommandId
+  }
+  deriving stock (Generic, Eq, Show)
 
-data ReservationEvent
-    = TransferReservationCreated !TransferReservationCreatedData
-    | TransferReservationConfirmed !TransferReservationConfirmedData
-    deriving stock (Generic, Eq, Show)
+data ReservationEvent = TransferReservationCreated !TransferReservationCreatedData
+  | TransferReservationConfirmed !TransferReservationConfirmedData
+  deriving stock (Generic, Eq, Show)
 
 type ReservationRegs =
-    '[ '("reservationId", TransferReservationId)
-     , '("hospitalId", HospitalId)
-     , '("patientAcuity", PatientAcuity)
-     , '("reservationState", ReservationVertex)
-     ]
+  '[ '("reservationId", TransferReservationId)
+   , '("hospitalId", HospitalId)
+   , '("patientAcuity", PatientAcuity)
+   ]
 
 initialReservationRegs :: RegFile ReservationRegs
 initialReservationRegs =
-    RCons (Proxy @"reservationId") (TransferReservationId "") $
-        RCons (Proxy @"hospitalId") (HospitalId "") $
-            RCons (Proxy @"patientAcuity") GreenTag $
-                RCons (Proxy @"reservationState") ReservationUnrequested RNil
+  RCons (Proxy @"reservationId") (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"hospitalId") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"patientAcuity") GreenTag RNil
 
 $(deriveAggregateCtorsAll ''ReservationCommand ''ReservationRegs)
+
+
 
 $(deriveWireCtorsAll ''ReservationEvent)
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/EventStream.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/EventStream.hs
--- a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/EventStream.hs
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/EventStream.hs
@@ -1,58 +1,63 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation.EventStream (
-    reservationCategory,
-    reservationEventStream,
-    reservationEventStreamDef,
-    ReservationEventStream,
-    ReservationEventStreamDef,
-    reservationSnapshotFixture,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.EventStream
+  ( reservationCategory
+  , reservationCommandCategory
+  , reservationEventStream
+  , reservationEventStreamDef
+  , ReservationEventStream
+  , ReservationEventStreamDef
+  , reservationSnapshotFixture
+  ) where
 
-import Data.Text (Text)
-import Generated.HospitalCapacity.Reservation.Codec (reservationCodec)
 import Generated.HospitalCapacity.Reservation.Domain
-import HospitalCapacity.Reservation.Holes (reservationTransducer)
+import Generated.HospitalCapacity.Reservation.Codec (reservationCodec)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationFoldFingerprint, reservationTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
+import Data.Text (Text)
 import Keiro.Snapshot.Codec (defaultStateCodec, withFoldFingerprint)
 import Keiro.Stream qualified as Stream
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-reservationCategory :: Stream.StreamCategory a
+reservationCategory :: Stream.StreamCategory ReservationEventStreamDef
 reservationCategory = Stream.categoryUnsafe "reservation"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+reservationCommandCategory :: Stream.StreamCategory ReservationCommand
+reservationCommandCategory = Stream.categoryUnsafe "reservation"
+
 type ReservationEventStreamDef =
-    EventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
+  EventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
 
 type ReservationEventStream =
-    ValidatedEventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
+  ValidatedEventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
 
 reservationEventStreamDef :: ReservationEventStreamDef
 reservationEventStreamDef =
-    EventStream
-        { transducer = reservationTransducer
-        , initialState = ReservationUnrequested
-        , initialRegisters = initialReservationRegs
-        , eventCodec = reservationCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Every 100
-        , -- The snapshot discriminator composes: the spec's state-codec version (bump it
-          -- in the spec's `state-codec version=` clause), keiki's register and
-          -- control-state shape hashes, and this fold fingerprint derived from the
-          -- spec's transition surface (guards, writes, emits, states, register
-          -- initials, referenced rules). Spec-visible fold changes invalidate old
-          -- snapshots automatically. Fold changes made ONLY in the hand-owned Holes
-          -- module are invisible here: bump `state-codec version=` manually or old
-          -- snapshots will be served stale.
-          stateCodec = Just (withFoldFingerprint "2367ef6fadf0e751" (defaultStateCodec 1))
-        }
+  EventStream
+    { transducer = reservationTransducer,
+      initialState = ReservationUnrequested,
+      initialRegisters = initialReservationRegs,
+      eventCodec = reservationCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Every 100,
+      -- The snapshot discriminator composes: the spec's state-codec version (bump it
+      -- in the spec's `state-codec version=` clause), keiki's register and
+      -- control-state shape hashes, and this fold fingerprint derived from the
+      -- spec's transition surface (guards, writes, emits, states, register
+      -- initials, referenced rules). Spec-visible fold changes invalidate old
+      -- snapshots automatically. Version-2 Hole-owned transitions additionally
+      -- compose their explicit hand-owned FoldVersion tokens here; bump the
+      -- corresponding token whenever that Hole behavior changes.
+      stateCodec = Just (withFoldFingerprint reservationFoldFingerprint (defaultStateCodec 1))
+    }
 
 reservationSnapshotFixture :: (Int, Text)
-reservationSnapshotFixture = (1, "7eb3a94f62f947231375d44083e2a1c8029d91ffe0329107d55092ed3430efcc")
+reservationSnapshotFixture = (1, "7a181ceb7d798d883d28c85201c5c1692bd314a7b489da9128bff91e0f38cd28")
 
 reservationEventStream :: ReservationEventStream
 reservationEventStream =
-    mkEventStreamOrThrow "Reservation" reservationEventStreamDef
+  mkEventStreamOrThrow "Reservation" reservationEventStreamDef
diff --git a/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Transducer.hs b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-snapshot/Generated/HospitalCapacity/Reservation/Transducer.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.Transducer
+  ( reservationTransducer
+  , reservationFoldFingerprint
+  , BehaviorOwnership (..)
+  , reservationPredicateVerifications
+  ) where
+
+import Generated.HospitalCapacity.Reservation.Domain
+import Data.Text (Text)
+import Generated.HospitalCapacity.Nominals (DivertStatus (..))
+import Generated.HospitalCapacity.Nominals qualified as GeneratedNominals
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer, (.==), (./=), (.||))
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import HospitalCapacity.Reservation.Holes qualified as Holes
+import Keiki.Generics (RegFieldsOf)
+
+reservationTransducer
+  :: SymTransducer
+       (HsPred ReservationRegs ReservationCommand)
+       ReservationRegs
+       ReservationVertex
+       ReservationCommand
+       ReservationEvent
+reservationTransducer =
+  B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
+    B.from ReservationUnrequested do
+      B.onCmd inCtorRequestTransferReservation $ \d -> B.do
+        let commandDivertStatus = K.inpProj GeneratedNominals.divertStatusEqualityWitness inCtorRequestTransferReservation (#divertStatus :: K.Index (RegFieldsOf RequestTransferReservationData) DivertStatus)
+        B.requireGuard $
+          commandDivertStatus ./= K.lit ("total-divert" :: Text)
+          .|| d.lifeCriticalOverride .== K.lit True
+        B.emit wireTransferReservationCreated (TransferReservationCreatedTermFields
+          { reservationId = d.reservationId
+          , hospitalId = d.hospitalId
+          , commandId = d.commandId
+          , patientAcuity = d.patientAcuity
+          , divertStatus = d.divertStatus
+          , lifeCriticalOverride = d.lifeCriticalOverride
+          })
+        B.goto ReservationHeld
+    B.from ReservationHeld do
+      B.onCmd inCtorConfirmReservation $ \d -> B.do
+        B.emit wireTransferReservationConfirmed (Holes.transition2HeldConfirmReservationOutput1TransferReservationConfirmed d)
+        B.goto ReservationConfirmed
+ where
+  isTerminal = \case
+    ReservationExpired -> True
+    ReservationAdmitted -> True
+    ReservationReleased -> True
+    _ -> False
+
+reservationFoldFingerprint :: Text
+reservationFoldFingerprint = "5e09ffcba6b155a8269b34b1e232d0c6"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+reservationPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+reservationPredicateVerifications = sequence
+  [ verifyTransition "transition1UnrequestedRequestTransferReservation" GeneratedOwned ReservationUnrequested 0
+  , verifyTransition "transition2HeldConfirmReservation" GeneratedOwned ReservationHeld 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut reservationTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-snapshot/HospitalCapacity/Reservation/Holes.hs b/test/conformance-snapshot/HospitalCapacity/Reservation/Holes.hs
--- a/test/conformance-snapshot/HospitalCapacity/Reservation/Holes.hs
+++ b/test/conformance-snapshot/HospitalCapacity/Reservation/Holes.hs
@@ -1,67 +1,26 @@
-{-# LANGUAGE BlockArguments #-}
+-- Snapshot conformance retains only the DB-coupled projection boundary;
+-- stable generated code owns the aggregate transducer.
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
--- overwrites it. Its transducer body has been filled by hand to match the
--- captured HospitalCapacity/Reservation reference, against the generated
--- signatures. The harness pins this behaviour.
-module HospitalCapacity.Reservation.Holes (
-    reservationTransducer,
+module HospitalCapacity.Reservation.Holes
+  ( transition2HeldConfirmReservationOutput1TransferReservationConfirmed,
     applyTransfer_decisions,
-) where
+  )
+where
 
 import Generated.HospitalCapacity.Reservation.Domain
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, lit, (./=), (.==), (.||))
+import Keiki.Generics (RegFieldsOf)
 
-reservationTransducer ::
-    SymTransducer
-        (HsPred ReservationRegs ReservationCommand)
-        ReservationRegs
-        ReservationVertex
-        ReservationCommand
-        ReservationEvent
-reservationTransducer =
-    B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
-        B.from ReservationUnrequested do
-            B.onCmd inCtorRequestTransferReservation $ \d -> B.do
-                B.requireGuard (d.divertStatus ./= lit TotalDivert .|| d.lifeCriticalOverride .== lit True)
-                B.slot @"reservationState" =: lit ReservationHeld
-                B.emit
-                    wireTransferReservationCreated
-                    TransferReservationCreatedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        , patientAcuity = d.patientAcuity
-                        , divertStatus = d.divertStatus
-                        , lifeCriticalOverride = d.lifeCriticalOverride
-                        }
-                B.goto ReservationHeld
-        B.from ReservationHeld do
-            B.onCmd inCtorConfirmReservation $ \d -> B.do
-                B.slot @"reservationState" =: lit ReservationConfirmed
-                B.emit
-                    wireTransferReservationConfirmed
-                    TransferReservationConfirmedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        }
-                B.goto ReservationConfirmed
-  where
-    isTerminal = \case
-        ReservationExpired -> True
-        ReservationAdmitted -> True
-        ReservationReleased -> True
-        _ -> False
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed :: B.PayloadProj ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData) -> TransferReservationConfirmedTermFields ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData)
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed command =
+  TransferReservationConfirmedTermFields
+    { reservationId = command.reservationId,
+      hospitalId = command.hospitalId,
+      commandId = command.commandId
+    }
 
--- HOLE (DB-coupled, out of scope for EP-1): the read-model SQL for the
--- transfer_decisions projection. The pure event->status mapping is generated as
--- transfer_decisionsStatusFor. Left as a typed hole; the harness does not pin it.
 applyTransfer_decisions :: ReservationEvent -> recorded -> txn ()
 applyTransfer_decisions _event _recorded = error "HOLE: fill transfer_decisions projection apply"
diff --git a/test/conformance-snapshot/Main.hs b/test/conformance-snapshot/Main.hs
--- a/test/conformance-snapshot/Main.hs
+++ b/test/conformance-snapshot/Main.hs
@@ -8,6 +8,7 @@
 import Data.Proxy (Proxy (..))
 import Generated.HospitalCapacity.Reservation.Domain (ReservationRegs, ReservationVertex)
 import Generated.HospitalCapacity.Reservation.EventStream (reservationEventStream, reservationEventStreamDef, reservationSnapshotFixture)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationFoldFingerprint)
 import Keiki.Shape qualified as Shape
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..), StateCodec (..))
 import System.Exit (exitFailure)
@@ -26,7 +27,7 @@
                 hashDerived = shapeHash liveCodec == Shape.regFileShapeHash (Proxy @ReservationRegs)
                 stateShapeDerived =
                     stateShapeHash liveCodec
-                        == Shape.stateShapeHash (Proxy @ReservationVertex) <> ";fold=2367ef6fadf0e751"
+                        == Shape.stateShapeHash (Proxy @ReservationVertex) <> ";fold=" <> reservationFoldFingerprint
                 policyOk = case snapshotPolicy reservationEventStreamDef of
                     Every interval -> interval == 100
                     _ -> False
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Codec.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Codec.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Codec.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Codec.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
 module Generated.StructuralConformance.ArtifactCatalog.Codec (
     artifactCatalogCodec,
     parseArtifactCatalogEvent,
@@ -15,19 +14,19 @@
     decodeArtifactMetadataMapped,
 ) where
 
+import Generated.StructuralConformance.ArtifactCatalog.Domain
 import Control.Monad (unless)
 import Data.Aeson (Value (..), object, parseJSON, toJSON, withObject, withText, (.:), (.=))
 import Data.Aeson.Key qualified as Key
 import Data.Aeson.KeyMap qualified as KeyMap
-import Data.Aeson.Types (Parser, parseEither)
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.StructuralConformance.ArtifactCatalog.Domain
-import Keiro.Codec (Codec (..), EventType (..))
+import qualified Data.Text as T
 import Keiro.Codec.Structural (bindingFromShape, bindingToShape)
+import Keiro.Codec (Codec (..), EventType (..))
 
 import Conformance.Structural.Bindings qualified
 import Conformance.Structural.Domain qualified
@@ -36,6 +35,8 @@
 import Generated.StructuralConformance.Structural.Shape.ArtifactLocation qualified
 import Generated.StructuralConformance.Structural.Shape.ArtifactMetadata qualified
 
+
+
 encodeArtifactInfoMapped :: Conformance.Structural.Domain.ArtifactInfo -> Value
 encodeArtifactInfoMapped = encodeArtifactInfoShape . bindingToShape Conformance.Structural.Bindings.artifactInfoBinding
 
@@ -47,29 +48,29 @@
 
 encodeArtifactInfoShape :: Generated.StructuralConformance.Structural.Shape.ArtifactInfo.ArtifactInfoShape -> Value
 encodeArtifactInfoShape shape =
-    object
-        [ "artifact_key" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey shape)
-        , "display_name" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName shape)
-        , "artifact_hash" .= maybe Null (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactHash shape)
-        , "artifact_kind" .= encodeArtifactKindShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKind shape)
-        , "location" .= encodeArtifactLocationShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.location shape)
-        , "metadata" .= encodeArtifactMetadataShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.metadata shape)
-        , "active" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.active shape)
-        , "tags" .= toJSON (map (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.tags shape))
-        ]
+  object
+      [ "artifact_key" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey shape)
+      , "display_name" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName shape)
+      , "artifact_hash" .= maybe Null (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactHash shape)
+      , "artifact_kind" .= encodeArtifactKindShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKind shape)
+      , "location" .= encodeArtifactLocationShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.location shape)
+      , "metadata" .= encodeArtifactMetadataShape (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.metadata shape)
+      , "active" .= toJSON (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.active shape)
+      , "tags" .= toJSON (map (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactInfo.tags shape))
+      ]
 
 parseArtifactInfoShape :: Value -> Parser Generated.StructuralConformance.Structural.Shape.ArtifactInfo.ArtifactInfoShape
 parseArtifactInfoShape = withObject "ArtifactInfoShape" $ \objectValue -> do
-    rejectUnknownFields "ArtifactInfo" ["artifact_key", "display_name", "artifact_hash", "artifact_kind", "location", "metadata", "active", "tags"] objectValue
-    Generated.StructuralConformance.Structural.Shape.ArtifactInfo.ArtifactInfo
-        <$> ((objectValue .: "artifact_key" :: Parser Value) >>= (parseJSON))
-        <*> ((objectValue .: "display_name" :: Parser Value) >>= (parseJSON))
-        <*> (case KeyMap.lookup (Key.fromText "artifact_hash") objectValue of Nothing -> pure Nothing; Just presentValue -> (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other) presentValue)
-        <*> (case KeyMap.lookup (Key.fromText "artifact_kind") objectValue of Nothing -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide; Just presentValue -> (parseArtifactKindShape) presentValue)
-        <*> ((objectValue .: "location" :: Parser Value) >>= (parseArtifactLocationShape))
-        <*> ((objectValue .: "metadata" :: Parser Value) >>= (parseArtifactMetadataShape))
-        <*> (case KeyMap.lookup (Key.fromText "active") objectValue of Nothing -> pure False; Just presentValue -> (parseJSON) presentValue)
-        <*> (case KeyMap.lookup (Key.fromText "tags") objectValue of Nothing -> pure []; Just presentValue -> (\value -> (parseJSON value :: Parser [Value]) >>= traverse (parseJSON)) presentValue)
+  rejectUnknownFields "ArtifactInfo" ["artifact_key", "display_name", "artifact_hash", "artifact_kind", "location", "metadata", "active", "tags"] objectValue
+  Generated.StructuralConformance.Structural.Shape.ArtifactInfo.ArtifactInfo
+    <$> explicitParseField (parseJSON) objectValue "artifact_key"
+    <*> explicitParseField (parseJSON) objectValue "display_name"
+    <*> (case KeyMap.lookup (Key.fromText "artifact_hash") objectValue of Nothing -> pure Nothing; Just _ -> explicitParseField (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other) objectValue "artifact_hash")
+    <*> (case KeyMap.lookup (Key.fromText "artifact_kind") objectValue of Nothing -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide; Just _ -> explicitParseField (parseArtifactKindShape) objectValue "artifact_kind")
+    <*> explicitParseField (parseArtifactLocationShape) objectValue "location"
+    <*> explicitParseField (parseArtifactMetadataShape) objectValue "metadata"
+    <*> (case KeyMap.lookup (Key.fromText "active") objectValue of Nothing -> pure False; Just _ -> explicitParseField (parseJSON) objectValue "active")
+    <*> (case KeyMap.lookup (Key.fromText "tags") objectValue of Nothing -> pure []; Just _ -> explicitParseField (\value -> (parseJSON value :: Parser [Value]) >>= traverse (parseJSON)) objectValue "tags")
 
 encodeArtifactKindMapped :: Conformance.Structural.Domain.ArtifactKind -> Value
 encodeArtifactKindMapped = encodeArtifactKindShape . bindingToShape Conformance.Structural.Bindings.artifactKindBinding
@@ -82,14 +83,14 @@
 
 encodeArtifactKindShape :: Generated.StructuralConformance.Structural.Shape.ArtifactKind.ArtifactKindShape -> Value
 encodeArtifactKindShape = \case
-    Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide -> String "guide"
-    Generated.StructuralConformance.Structural.Shape.ArtifactKind.Reference -> String "reference"
+  Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide -> String "guide"
+  Generated.StructuralConformance.Structural.Shape.ArtifactKind.Reference -> String "reference"
 
 parseArtifactKindShape :: Value -> Parser Generated.StructuralConformance.Structural.Shape.ArtifactKind.ArtifactKindShape
 parseArtifactKindShape = withText "ArtifactKindShape" $ \tag -> case tag of
-    "guide" -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide
-    "reference" -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Reference
-    _ -> fail "unknown ArtifactKind wire value"
+  "guide" -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Guide
+  "reference" -> pure Generated.StructuralConformance.Structural.Shape.ArtifactKind.Reference
+  tag -> fail ("unknown ArtifactKind wire value " <> show tag <> "; expected one of: guide, reference")
 
 encodeArtifactLocationMapped :: Conformance.Structural.Domain.ArtifactLocation -> Value
 encodeArtifactLocationMapped = encodeArtifactLocationShape . bindingToShape Conformance.Structural.Bindings.artifactLocationBinding
@@ -102,52 +103,57 @@
 
 encodeArtifactLocationShape :: Generated.StructuralConformance.Structural.Shape.ArtifactLocation.ArtifactLocationShape -> Value
 encodeArtifactLocationShape = \case
-    Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalFile payload ->
-        object
-            [ "tag" .= ("local_file" :: Text)
-            , "contents" .= toJSON (payload)
-            ]
-    Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalDir payload ->
-        object
-            [ "tag" .= ("local_dir" :: Text)
-            , "contents" .= toJSON (payload)
-            ]
-    Generated.StructuralConformance.Structural.Shape.ArtifactLocation.RepoPath payload ->
-        object
-            [ "tag" .= ("repo_path" :: Text)
-            , "contents" .= toJSON (payload)
-            ]
-    Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocUrl payload ->
-        object
-            [ "tag" .= ("url" :: Text)
-            , "contents" .= toJSON (payload)
-            ]
-    Generated.StructuralConformance.Structural.Shape.ArtifactLocation.Canonical ->
-        object
-            [ "tag" .= ("canonical" :: Text)
-            ]
+  Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalFile payload ->
+    object
+      [ "tag" .= ("local_file" :: Text)
+      , "contents" .= toJSON (payload)
+      ]
+  Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalDir payload ->
+    object
+      [ "tag" .= ("local_dir" :: Text)
+      , "contents" .= toJSON (payload)
+      ]
+  Generated.StructuralConformance.Structural.Shape.ArtifactLocation.RepoPath payload ->
+    object
+      [ "tag" .= ("repo_path" :: Text)
+      , "contents" .= toJSON (payload)
+      ]
+  Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocUrl payload ->
+    object
+      [ "tag" .= ("url" :: Text)
+      , "contents" .= toJSON (payload)
+      ]
+  Generated.StructuralConformance.Structural.Shape.ArtifactLocation.Canonical ->
+    object
+      [ "tag" .= ("canonical" :: Text)
+      ]
 
 parseArtifactLocationShape :: Value -> Parser Generated.StructuralConformance.Structural.Shape.ArtifactLocation.ArtifactLocationShape
 parseArtifactLocationShape = withObject "ArtifactLocationShape" $ \objectValue -> do
-    tag <- objectValue .: "tag" :: Parser Text
-    case tag of
-        "local_file" -> do
-            rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
-            Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalFile <$> (objectValue .: "contents" >>= (parseJSON))
-        "local_dir" -> do
-            rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
-            Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalDir <$> (objectValue .: "contents" >>= (parseJSON))
-        "repo_path" -> do
-            rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
-            Generated.StructuralConformance.Structural.Shape.ArtifactLocation.RepoPath <$> (objectValue .: "contents" >>= (parseJSON))
-        "url" -> do
-            rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
-            Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocUrl <$> (objectValue .: "contents" >>= (parseJSON))
-        "canonical" -> do
-            rejectUnknownFields "ArtifactLocation" ["tag"] objectValue
-            pure Generated.StructuralConformance.Structural.Shape.ArtifactLocation.Canonical
-        _ -> fail "unknown ArtifactLocation union tag"
+  tag <- explicitParseField (withText "ArtifactLocation tag" validateArtifactLocationTag) objectValue "tag"
+  case tag of
+    "local_file" -> do
+      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
+      Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalFile <$> explicitParseField (parseJSON) objectValue "contents"
+    "local_dir" -> do
+      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
+      Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocalDir <$> explicitParseField (parseJSON) objectValue "contents"
+    "repo_path" -> do
+      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
+      Generated.StructuralConformance.Structural.Shape.ArtifactLocation.RepoPath <$> explicitParseField (parseJSON) objectValue "contents"
+    "url" -> do
+      rejectUnknownFields "ArtifactLocation" ["tag", "contents"] objectValue
+      Generated.StructuralConformance.Structural.Shape.ArtifactLocation.LocUrl <$> explicitParseField (parseJSON) objectValue "contents"
+    "canonical" -> do
+      rejectUnknownFields "ArtifactLocation" ["tag"] objectValue
+      pure Generated.StructuralConformance.Structural.Shape.ArtifactLocation.Canonical
+    _ -> fail "validated union tag was not handled"
 
+validateArtifactLocationTag :: Text -> Parser Text
+validateArtifactLocationTag tag
+  | tag `elem` ["local_file", "local_dir", "repo_path", "url", "canonical"] = pure tag
+  | otherwise = fail ("unknown ArtifactLocation union tag " <> show tag <> "; expected one of: local_file, local_dir, repo_path, url, canonical")
+
 encodeArtifactMetadataMapped :: Conformance.Structural.Domain.ArtifactMetadata -> Value
 encodeArtifactMetadataMapped = encodeArtifactMetadataShape . bindingToShape Conformance.Structural.Bindings.artifactMetadataBinding
 
@@ -159,59 +165,67 @@
 
 encodeArtifactMetadataShape :: Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadataShape -> Value
 encodeArtifactMetadataShape shape =
-    object
-        [ "note" .= maybe Null (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.note shape)
-        ]
+  object
+      [ "note" .= maybe Null (\item -> toJSON (item)) (Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.note shape)
+      ]
 
 parseArtifactMetadataShape :: Value -> Parser Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadataShape
 parseArtifactMetadataShape = withObject "ArtifactMetadataShape" $ \objectValue -> do
-    Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadata
-        <$> ((objectValue .: "note" :: Parser Value) >>= (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other))
+  Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadata
+    <$> explicitParseField (\value -> case value of Null -> pure Nothing; other -> Just <$> parseJSON other) objectValue "note"
 
 artifactCatalogCodec :: Codec ArtifactCatalogEvent
 artifactCatalogCodec =
-    Codec
-        { eventTypes = EventType "ArtifactRecorded" :| [EventType "ArtifactAccepted"]
-        , eventType = \case
-            ArtifactRecorded{} -> EventType "ArtifactRecorded"
-            ArtifactAccepted{} -> EventType "ArtifactAccepted"
-        , schemaVersion = 1
-        , encode = encodeArtifactCatalogEvent
-        , decode = parseArtifactCatalogEvent
-        , upcasters = []
-        }
+  Codec
+    { eventTypes = EventType "ArtifactRecorded" :| [EventType "ArtifactAccepted"]
+    , eventType = \case
+        ArtifactRecorded{} -> EventType "ArtifactRecorded"
+        ArtifactAccepted{} -> EventType "ArtifactAccepted"
+    , schemaVersion = 1
+    , encode = encodeArtifactCatalogEvent
+    , decode = parseArtifactCatalogEvent
+    , upcasters = []
+    }
 
 encodeArtifactCatalogEvent :: ArtifactCatalogEvent -> Value
 encodeArtifactCatalogEvent = \case
-    ArtifactRecorded payload ->
-        object
-            [ "kind" .= ("ArtifactRecorded" :: Text)
-            , "artifact" .= encodeArtifactInfoMapped payload.artifact
-            , "geometry" .= toJSON payload.geometry
-            , "accepted" .= payload.accepted
-            ]
-    ArtifactAccepted payload ->
-        object
-            [ "kind" .= ("ArtifactAccepted" :: Text)
-            , "accepted" .= payload.accepted
-            ]
+  ArtifactRecorded payload ->
+    object
+      [ "kind" .= ("ArtifactRecorded" :: Text)
+      , "artifact" .= encodeArtifactInfoMapped payload.artifact
+      , "geometry" .= toJSON payload.geometry
+      , "accepted" .= payload.accepted
+      ]
+  ArtifactAccepted payload ->
+    object
+      [ "kind" .= ("ArtifactAccepted" :: Text)
+      , "accepted" .= payload.accepted
+      ]
 
 parseArtifactCatalogEvent :: EventType -> Value -> Either Text ArtifactCatalogEvent
 parseArtifactCatalogEvent (EventType tag) = mapLeftText . parseEither (withObject "ArtifactCatalogEvent" go)
   where
     go o = do
-        case tag of
-            "ArtifactRecorded" ->
-                ArtifactRecorded <$> (ArtifactRecordedData <$> (o .: "artifact" >>= parseArtifactInfoMapped) <*> o .: "geometry" <*> o .: "accepted")
-            "ArtifactAccepted" ->
-                ArtifactAccepted <$> (ArtifactAcceptedData <$> o .: "accepted")
-            _ -> fail "unknown event type"
+      case tag of
+        "ArtifactRecorded" ->
+          ArtifactRecorded
+            <$> ( ArtifactRecordedData
+                    <$> explicitParseField parseArtifactInfoMapped o "artifact"
+                    <*> o .: "geometry"
+                    <*> o .: "accepted"
+                )
+        "ArtifactAccepted" ->
+          ArtifactAccepted
+            <$> ( ArtifactAcceptedData
+                    <$> o .: "accepted"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: ArtifactRecorded, ArtifactAccepted")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
 
 rejectUnknownFields :: String -> [Text] -> KeyMap.KeyMap Value -> Parser ()
 rejectUnknownFields label allowed objectValue =
-    unless (null extras) (fail (label <> " contains unknown fields: " <> show extras))
+  unless (null extras) (fail (label <> " contains unknown fields: " <> show extras))
   where
     extras = filter (`notElem` allowed) (map Key.toText (KeyMap.keys objectValue))
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Domain.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Domain.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Domain.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Domain.hs
@@ -2,61 +2,60 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
 module Generated.StructuralConformance.ArtifactCatalog.Domain where
 
-import Conformance.Structural.Bindings qualified
-import Conformance.Structural.Domain qualified
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
+import Conformance.Structural.Bindings qualified
+import Conformance.Structural.Domain qualified
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ArtifactCatalogVertex = ArtifactCatalogEmpty | ArtifactCatalogObserved
-    deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
 data ObserveArtifactData = ObserveArtifactData
-    { artifact :: !Conformance.Structural.Domain.ArtifactInfo
-    , geometry :: !Conformance.Structural.Domain.Geometry
-    , accepted :: !Bool
-    }
-    deriving stock (Generic, Eq, Show)
+  { artifact :: !Conformance.Structural.Domain.ArtifactInfo
+  , geometry :: !Conformance.Structural.Domain.Geometry
+  , accepted :: !Bool
+  }
+  deriving stock (Generic, Eq, Show)
 
 data ArtifactCatalogCommand = ObserveArtifact !ObserveArtifactData
-    deriving stock (Generic, Eq, Show)
+  deriving stock (Generic, Eq, Show)
 
 data ArtifactRecordedData = ArtifactRecordedData
-    { artifact :: !Conformance.Structural.Domain.ArtifactInfo
-    , geometry :: !Conformance.Structural.Domain.Geometry
-    , accepted :: !Bool
-    }
-    deriving stock (Generic, Eq, Show)
+  { artifact :: !Conformance.Structural.Domain.ArtifactInfo
+  , geometry :: !Conformance.Structural.Domain.Geometry
+  , accepted :: !Bool
+  }
+  deriving stock (Generic, Eq, Show)
 
 data ArtifactAcceptedData = ArtifactAcceptedData
-    { accepted :: !Bool
-    }
-    deriving stock (Generic, Eq, Show)
+  { accepted :: !Bool
+  }
+  deriving stock (Generic, Eq, Show)
 
-data ArtifactCatalogEvent
-    = ArtifactRecorded !ArtifactRecordedData
-    | ArtifactAccepted !ArtifactAcceptedData
-    deriving stock (Generic, Eq, Show)
+data ArtifactCatalogEvent = ArtifactRecorded !ArtifactRecordedData
+  | ArtifactAccepted !ArtifactAcceptedData
+  deriving stock (Generic, Eq, Show)
 
 type ArtifactCatalogRegs =
-    '[ '("currentArtifact", Conformance.Structural.Domain.ArtifactInfo)
-     , '("currentGeometry", Conformance.Structural.Domain.Geometry)
-     , '("acceptedCount", Int)
-     ]
+  '[ '("currentArtifact", Conformance.Structural.Domain.ArtifactInfo)
+   , '("currentGeometry", Conformance.Structural.Domain.Geometry)
+   , '("acceptedCount", Int)
+   ]
 
 initialArtifactCatalogRegs :: RegFile ArtifactCatalogRegs
 initialArtifactCatalogRegs =
-    RCons (Proxy @"currentArtifact") Conformance.Structural.Bindings.emptyArtifactInfo $
-        RCons (Proxy @"currentGeometry") Conformance.Structural.Bindings.emptyGeometry $
-            RCons (Proxy @"acceptedCount") 0 RNil
+  RCons (Proxy @"currentArtifact") Conformance.Structural.Bindings.emptyArtifactInfo $
+  RCons (Proxy @"currentGeometry") Conformance.Structural.Bindings.emptyGeometry $
+  RCons (Proxy @"acceptedCount") 0 RNil
 
 $(deriveAggregateCtorsAll ''ArtifactCatalogCommand ''ArtifactCatalogRegs)
+
+
 
 $(deriveWireCtorsAll ''ArtifactCatalogEvent)
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/EventStream.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/EventStream.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/EventStream.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/EventStream.hs
@@ -1,44 +1,49 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.StructuralConformance.ArtifactCatalog.EventStream (
-    artifactCatalogCategory,
-    artifactCatalogEventStream,
-    artifactCatalogEventStreamDef,
-    ArtifactCatalogEventStream,
-    ArtifactCatalogEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
+module Generated.StructuralConformance.ArtifactCatalog.EventStream
+  ( artifactCatalogCategory
+  , artifactCatalogCommandCategory
+  , artifactCatalogEventStream
+  , artifactCatalogEventStreamDef
+  , ArtifactCatalogEventStream
+  , ArtifactCatalogEventStreamDef
+  ) where
 
-import Generated.StructuralConformance.ArtifactCatalog.Codec (artifactCatalogCodec)
 import Generated.StructuralConformance.ArtifactCatalog.Domain
+import Generated.StructuralConformance.ArtifactCatalog.Codec (artifactCatalogCodec)
+import Generated.StructuralConformance.ArtifactCatalog.Transducer (artifactCatalogFoldFingerprint, artifactCatalogTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
 import Keiro.Stream qualified as Stream
-import StructuralConformance.ArtifactCatalog.Holes (artifactCatalogTransducer)
 
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-artifactCatalogCategory :: Stream.StreamCategory a
+artifactCatalogCategory :: Stream.StreamCategory ArtifactCatalogEventStreamDef
 artifactCatalogCategory = Stream.categoryUnsafe "artifactCatalog"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+artifactCatalogCommandCategory :: Stream.StreamCategory ArtifactCatalogCommand
+artifactCatalogCommandCategory = Stream.categoryUnsafe "artifactCatalog"
+
 type ArtifactCatalogEventStreamDef =
-    EventStream (HsPred ArtifactCatalogRegs ArtifactCatalogCommand) ArtifactCatalogRegs ArtifactCatalogVertex ArtifactCatalogCommand ArtifactCatalogEvent
+  EventStream (HsPred ArtifactCatalogRegs ArtifactCatalogCommand) ArtifactCatalogRegs ArtifactCatalogVertex ArtifactCatalogCommand ArtifactCatalogEvent
 
 type ArtifactCatalogEventStream =
-    ValidatedEventStream (HsPred ArtifactCatalogRegs ArtifactCatalogCommand) ArtifactCatalogRegs ArtifactCatalogVertex ArtifactCatalogCommand ArtifactCatalogEvent
+  ValidatedEventStream (HsPred ArtifactCatalogRegs ArtifactCatalogCommand) ArtifactCatalogRegs ArtifactCatalogVertex ArtifactCatalogCommand ArtifactCatalogEvent
 
 artifactCatalogEventStreamDef :: ArtifactCatalogEventStreamDef
 artifactCatalogEventStreamDef =
-    EventStream
-        { transducer = artifactCatalogTransducer
-        , initialState = ArtifactCatalogEmpty
-        , initialRegisters = initialArtifactCatalogRegs
-        , eventCodec = artifactCatalogCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = artifactCatalogTransducer,
+      initialState = ArtifactCatalogEmpty,
+      initialRegisters = initialArtifactCatalogRegs,
+      eventCodec = artifactCatalogCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 artifactCatalogEventStream :: ArtifactCatalogEventStream
 artifactCatalogEventStream =
-    mkEventStreamOrThrow "ArtifactCatalog" artifactCatalogEventStreamDef
+  mkEventStreamOrThrow "ArtifactCatalog" artifactCatalogEventStreamDef
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Harness.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Harness.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Harness.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Harness.hs
@@ -1,12 +1,14 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE TypeApplications #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
 module Generated.StructuralConformance.ArtifactCatalog.Harness (harnessAssertions) where
 
-import Conformance.Structural.Bindings qualified
-import Conformance.Structural.Domain qualified
+import Generated.StructuralConformance.ArtifactCatalog.Domain
+import Generated.StructuralConformance.ArtifactCatalog.Codec (encodeArtifactCatalogEvent, parseArtifactCatalogEvent, artifactCatalogCodec, encodeArtifactInfoMapped, decodeArtifactInfoMapped, encodeArtifactKindMapped, decodeArtifactKindMapped, encodeArtifactLocationMapped, decodeArtifactLocationMapped, encodeArtifactMetadataMapped, decodeArtifactMetadataMapped)
+import Generated.StructuralConformance.ArtifactCatalog.Transducer (artifactCatalogTransducer)
+import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, fieldWitnessAgrees, (!))
+import Keiro.Codec (eventType)
 import Data.Aeson qualified as Aeson
 import Data.Aeson.Key qualified as AesonKey
 import Data.Aeson.KeyMap qualified as AesonKeyMap
@@ -16,33 +18,29 @@
 import Data.Maybe (isJust, isNothing)
 import Data.Proxy (Proxy (..))
 import Data.Text qualified as T
-import Generated.StructuralConformance.ArtifactCatalog.Codec (artifactCatalogCodec, decodeArtifactInfoMapped, decodeArtifactKindMapped, decodeArtifactLocationMapped, decodeArtifactMetadataMapped, encodeArtifactCatalogEvent, encodeArtifactInfoMapped, encodeArtifactKindMapped, encodeArtifactLocationMapped, encodeArtifactMetadataMapped, parseArtifactCatalogEvent)
-import Generated.StructuralConformance.ArtifactCatalog.Domain
+import Keiki.Shape (CanonicalTypeName (..))
+import Keiro.Codec.Structural (FixtureCases (..), bindingDomainRoundTrip, bindingShapeRoundTrip, bindingToShape)
+import Conformance.Structural.Bindings qualified
 import Generated.StructuralConformance.Structural.Shape.ArtifactInfo qualified
 import Generated.StructuralConformance.Structural.Shape.ArtifactKind qualified
 import Generated.StructuralConformance.Structural.Shape.ArtifactLocation qualified
 import Generated.StructuralConformance.Structural.Shape.ArtifactMetadata qualified
+import Conformance.Structural.Domain qualified
 import Generated.StructuralConformance.StructuralProjections qualified as StructuralProjections
-import Keiki.Core (applyEventsEither, defaultValidationOptions, fieldWitnessAgrees, step, validateTransducer, (!))
-import Keiki.Shape (CanonicalTypeName (..))
-import Keiro.Codec (eventType)
-import Keiro.Codec.Structural (FixtureCases (..), bindingDomainRoundTrip, bindingShapeRoundTrip, bindingToShape)
-import StructuralConformance.ArtifactCatalog.Holes (artifactCatalogTransducer)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions artifactCatalogTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: ArtifactRecorded", roundTrips sampleEventArtifactRecorded)
-    , ("golden round-trip: ArtifactAccepted", roundTrips sampleEventArtifactAccepted)
-    , ("accepts ObserveArtifact from ArtifactCatalogEmpty", acceptObserveArtifact)
-    ]
-        ++ mappedConformanceAssertions
-        ++ forwardReplayObserveArtifact
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions artifactCatalogTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: ArtifactRecorded", roundTrips sampleEventArtifactRecorded)
+  , ("golden round-trip: ArtifactAccepted", roundTrips sampleEventArtifactAccepted)
+  , ("accepts ObserveArtifact from ArtifactCatalogEmpty", acceptObserveArtifact)
+  ]
+  ++ mappedConformanceAssertions
+  ++ forwardReplayObserveArtifact
 
 roundTrips :: ArtifactCatalogEvent -> Bool
 roundTrips e = parseArtifactCatalogEvent (eventType artifactCatalogCodec e) (encodeArtifactCatalogEvent e) == Right e
@@ -55,113 +53,113 @@
 
 acceptObserveArtifact :: Bool
 acceptObserveArtifact =
-    case step artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) ((ObserveArtifact (ObserveArtifactData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False))) of
-        Just (v, _, _) -> v == ArtifactCatalogObserved
-        Nothing -> False
+  case step artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) ((ObserveArtifact (ObserveArtifactData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False))) of
+    Just (v, _, _) -> v == ArtifactCatalogObserved
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayObserveArtifact :: [(String, Bool)]
 forwardReplayObserveArtifact =
-    case step artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) ((ObserveArtifact (ObserveArtifactData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, forwardRegs, emitted) ->
-            case mapM (\event -> parseArtifactCatalogEvent (eventType artifactCatalogCodec event) (encodeArtifactCatalogEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            , (prefix <> "register currentArtifact", (replayRegs ! #currentArtifact) == (forwardRegs ! #currentArtifact))
-                            , (prefix <> "register currentGeometry", (replayRegs ! #currentGeometry) == (forwardRegs ! #currentGeometry))
-                            , (prefix <> "register acceptedCount", (replayRegs ! #acceptedCount) == (forwardRegs ! #acceptedCount))
-                            ]
+  case step artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) ((ObserveArtifact (ObserveArtifactData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, forwardRegs, emitted) ->
+      case mapM (\event -> parseArtifactCatalogEvent (eventType artifactCatalogCodec event) (encodeArtifactCatalogEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither artifactCatalogTransducer (ArtifactCatalogEmpty, initialArtifactCatalogRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              , (prefix <> "register currentArtifact", (replayRegs ! #currentArtifact) == (forwardRegs ! #currentArtifact))
+              , (prefix <> "register currentGeometry", (replayRegs ! #currentGeometry) == (forwardRegs ! #currentGeometry))
+              , (prefix <> "register acceptedCount", (replayRegs ! #acceptedCount) == (forwardRegs ! #acceptedCount))
+              ]
   where
     prefix = "forward/replay equality: ObserveArtifact from ArtifactCatalogEmpty -- "
 
 mappedConformanceAssertions :: [(String, Bool)]
 mappedConformanceAssertions =
-    concat
-        [ artifactInfoBindingAssertions
-        , artifactKindBindingAssertions
-        , artifactLocationBindingAssertions
-        , artifactMetadataBindingAssertions
-        , vendorGeometryOpaqueAssertions
-        , [("fixture coverage: conformance.structural.ArtifactInfo.v1", coverageArtifactInfo)]
-        , [("fixture coverage: conformance.structural.ArtifactKind.v1", coverageArtifactKind)]
-        , [("fixture coverage: conformance.structural.ArtifactLocation.v1", coverageArtifactLocation)]
-        , [("fixture coverage: conformance.structural.ArtifactMetadata.v1", coverageArtifactMetadata)]
-        , artifactRecordedArtifactAssertions
-        , artifactRecordedGeometryAssertions
-        , structuralWirePolicyAssertions
-        , structuralProjectionAssertions
-        ]
+  concat
+    [ artifactInfoBindingAssertions
+    , artifactKindBindingAssertions
+    , artifactLocationBindingAssertions
+    , artifactMetadataBindingAssertions
+    , vendorGeometryOpaqueAssertions
+    , [("fixture coverage: conformance.structural.ArtifactInfo.v1", coverageArtifactInfo)]
+    , [("fixture coverage: conformance.structural.ArtifactKind.v1", coverageArtifactKind)]
+    , [("fixture coverage: conformance.structural.ArtifactLocation.v1", coverageArtifactLocation)]
+    , [("fixture coverage: conformance.structural.ArtifactMetadata.v1", coverageArtifactMetadata)]
+    , artifactRecordedArtifactAssertions
+    , artifactRecordedGeometryAssertions
+    , structuralWirePolicyAssertions
+    , structuralProjectionAssertions
+    ]
 
 validFixtureLabels :: NonEmpty.NonEmpty (T.Text, value) -> Bool
 validFixtureLabels cases =
-    all (not . T.null) labels && length labels == length (nub labels)
+  all (not . T.null) labels && length labels == length (nub labels)
   where
     labels = map fst (NonEmpty.toList cases)
 
 artifactInfoBindingAssertions :: [(String, Bool)]
 artifactInfoBindingAssertions =
-    ("fixture labels: conformance.structural.ArtifactInfo.v1", validFixtureLabels cases)
-        : ("canonical identity: conformance.structural.ArtifactInfo.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactInfo) == "conformance.structural.ArtifactInfo.v1")
-        : concat
-            [ [ ("binding domain round-trip: conformance.structural.ArtifactInfo.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactInfoBinding value)
-              , ("binding shape round-trip: conformance.structural.ArtifactInfo.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactInfoBinding (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding value))
-              ]
-            | (label, value) <- NonEmpty.toList cases
-            ]
+  ("fixture labels: conformance.structural.ArtifactInfo.v1", validFixtureLabels cases) :
+  ("canonical identity: conformance.structural.ArtifactInfo.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactInfo) == "conformance.structural.ArtifactInfo.v1") :
+  concat
+    [ [ ("binding domain round-trip: conformance.structural.ArtifactInfo.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactInfoBinding value)
+      , ("binding shape round-trip: conformance.structural.ArtifactInfo.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactInfoBinding (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding value))
+      ]
+    | (label, value) <- NonEmpty.toList cases
+    ]
   where
     cases = fixtureCases Conformance.Structural.Bindings.artifactInfoCases
 
 artifactKindBindingAssertions :: [(String, Bool)]
 artifactKindBindingAssertions =
-    ("fixture labels: conformance.structural.ArtifactKind.v1", validFixtureLabels cases)
-        : ("canonical identity: conformance.structural.ArtifactKind.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactKind) == "conformance.structural.ArtifactKind.v1")
-        : concat
-            [ [ ("binding domain round-trip: conformance.structural.ArtifactKind.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactKindBinding value)
-              , ("binding shape round-trip: conformance.structural.ArtifactKind.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactKindBinding (bindingToShape Conformance.Structural.Bindings.artifactKindBinding value))
-              ]
-            | (label, value) <- NonEmpty.toList cases
-            ]
+  ("fixture labels: conformance.structural.ArtifactKind.v1", validFixtureLabels cases) :
+  ("canonical identity: conformance.structural.ArtifactKind.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactKind) == "conformance.structural.ArtifactKind.v1") :
+  concat
+    [ [ ("binding domain round-trip: conformance.structural.ArtifactKind.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactKindBinding value)
+      , ("binding shape round-trip: conformance.structural.ArtifactKind.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactKindBinding (bindingToShape Conformance.Structural.Bindings.artifactKindBinding value))
+      ]
+    | (label, value) <- NonEmpty.toList cases
+    ]
   where
     cases = fixtureCases Conformance.Structural.Bindings.artifactKindCases
 
 artifactLocationBindingAssertions :: [(String, Bool)]
 artifactLocationBindingAssertions =
-    ("fixture labels: conformance.structural.ArtifactLocation.v1", validFixtureLabels cases)
-        : ("canonical identity: conformance.structural.ArtifactLocation.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactLocation) == "conformance.structural.ArtifactLocation.v1")
-        : concat
-            [ [ ("binding domain round-trip: conformance.structural.ArtifactLocation.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactLocationBinding value)
-              , ("binding shape round-trip: conformance.structural.ArtifactLocation.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactLocationBinding (bindingToShape Conformance.Structural.Bindings.artifactLocationBinding value))
-              ]
-            | (label, value) <- NonEmpty.toList cases
-            ]
+  ("fixture labels: conformance.structural.ArtifactLocation.v1", validFixtureLabels cases) :
+  ("canonical identity: conformance.structural.ArtifactLocation.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactLocation) == "conformance.structural.ArtifactLocation.v1") :
+  concat
+    [ [ ("binding domain round-trip: conformance.structural.ArtifactLocation.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactLocationBinding value)
+      , ("binding shape round-trip: conformance.structural.ArtifactLocation.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactLocationBinding (bindingToShape Conformance.Structural.Bindings.artifactLocationBinding value))
+      ]
+    | (label, value) <- NonEmpty.toList cases
+    ]
   where
     cases = fixtureCases Conformance.Structural.Bindings.artifactLocationCases
 
 artifactMetadataBindingAssertions :: [(String, Bool)]
 artifactMetadataBindingAssertions =
-    ("fixture labels: conformance.structural.ArtifactMetadata.v1", validFixtureLabels cases)
-        : ("canonical identity: conformance.structural.ArtifactMetadata.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactMetadata) == "conformance.structural.ArtifactMetadata.v1")
-        : concat
-            [ [ ("binding domain round-trip: conformance.structural.ArtifactMetadata.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactMetadataBinding value)
-              , ("binding shape round-trip: conformance.structural.ArtifactMetadata.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactMetadataBinding (bindingToShape Conformance.Structural.Bindings.artifactMetadataBinding value))
-              ]
-            | (label, value) <- NonEmpty.toList cases
-            ]
+  ("fixture labels: conformance.structural.ArtifactMetadata.v1", validFixtureLabels cases) :
+  ("canonical identity: conformance.structural.ArtifactMetadata.v1", canonicalTypeName (Proxy @Conformance.Structural.Domain.ArtifactMetadata) == "conformance.structural.ArtifactMetadata.v1") :
+  concat
+    [ [ ("binding domain round-trip: conformance.structural.ArtifactMetadata.v1/" <> T.unpack label, bindingDomainRoundTrip Conformance.Structural.Bindings.artifactMetadataBinding value)
+      , ("binding shape round-trip: conformance.structural.ArtifactMetadata.v1/" <> T.unpack label, bindingShapeRoundTrip Conformance.Structural.Bindings.artifactMetadataBinding (bindingToShape Conformance.Structural.Bindings.artifactMetadataBinding value))
+      ]
+    | (label, value) <- NonEmpty.toList cases
+    ]
   where
     cases = fixtureCases Conformance.Structural.Bindings.artifactMetadataCases
 
 vendorGeometryOpaqueAssertions :: [(String, Bool)]
 vendorGeometryOpaqueAssertions =
-    ("opaque boundary fixtures: vendor.geometry.json@3", validFixtureLabels cases)
-        : [ ("opaque codec round-trip: vendor.geometry.json@3/" <> T.unpack caseLabel, case Aeson.fromJSON (Aeson.toJSON value) of Aeson.Success decoded -> decoded == value; Aeson.Error _ -> False)
-          | (caseLabel, value) <- NonEmpty.toList cases
-          ]
+  ("opaque boundary fixtures: vendor.geometry.json@3", validFixtureLabels cases) :
+  [ ("opaque codec round-trip: vendor.geometry.json@3/" <> T.unpack caseLabel, case Aeson.fromJSON (Aeson.toJSON value) of Aeson.Success decoded -> decoded == value; Aeson.Error _ -> False)
+  | (caseLabel, value) <- NonEmpty.toList cases
+  ]
   where
     cases = fixtureCases Conformance.Structural.Bindings.geometryCases
 
@@ -187,44 +185,44 @@
 
 artifactRecordedArtifactAssertions :: [(String, Bool)]
 artifactRecordedArtifactAssertions =
-    [ ("mapped codec round-trip: ArtifactRecorded/artifact/" <> T.unpack label, roundTrips (ArtifactRecorded (ArtifactRecordedData mappedValue (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False)))
-    | (label, mappedValue) <- NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)
-    ]
+  [ ("mapped codec round-trip: ArtifactRecorded/artifact/" <> T.unpack label, roundTrips (ArtifactRecorded (ArtifactRecordedData mappedValue (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.geometryCases))) False)))
+  | (label, mappedValue) <- NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)
+  ]
 
 artifactRecordedGeometryAssertions :: [(String, Bool)]
 artifactRecordedGeometryAssertions =
-    [ ("mapped codec round-trip: ArtifactRecorded/geometry/" <> T.unpack label, roundTrips (ArtifactRecorded (ArtifactRecordedData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) mappedValue False)))
-    | (label, mappedValue) <- NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.geometryCases)
-    ]
+  [ ("mapped codec round-trip: ArtifactRecorded/geometry/" <> T.unpack label, roundTrips (ArtifactRecorded (ArtifactRecordedData (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))) mappedValue False)))
+  | (label, mappedValue) <- NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.geometryCases)
+  ]
 
 structuralWirePolicyAssertions :: [(String, Bool)]
 structuralWirePolicyAssertions =
-    [ ("wire policy missing default: conformance.structural.ArtifactInfo.v1/artifact_hash", case decodeArtifactInfoMapped (deleteObjectField "artifact_hash" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "artifact_hash" (encodeArtifactInfoMapped decoded) == Just (Aeson.Null))
-    , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/artifact_hash", isRight (decodeArtifactInfoMapped (insertObjectField "artifact_hash" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
-    , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/artifact_kind", case decodeArtifactInfoMapped (deleteObjectField "artifact_kind" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "artifact_kind" (encodeArtifactInfoMapped decoded) == Just (Aeson.String "guide"))
-    , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/artifact_kind", isLeft (decodeArtifactInfoMapped (insertObjectField "artifact_kind" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
-    , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/active", case decodeArtifactInfoMapped (deleteObjectField "active" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "active" (encodeArtifactInfoMapped decoded) == Just (Aeson.Bool False))
-    , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/active", isLeft (decodeArtifactInfoMapped (insertObjectField "active" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
-    , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/tags", case decodeArtifactInfoMapped (deleteObjectField "tags" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "tags" (encodeArtifactInfoMapped decoded) == Just (Aeson.toJSON ([] :: [Aeson.Value])))
-    , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/tags", isLeft (decodeArtifactInfoMapped (insertObjectField "tags" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
-    , ("wire policy unknown fields: conformance.structural.ArtifactInfo.v1", all (\(_, value) -> isLeft (decodeArtifactInfoMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactInfoMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
-    , ("wire enum arm: conformance.structural.ArtifactKind.v1/guide", any (\(_, value) -> encodeArtifactKindMapped value == Aeson.String "guide" && decodeArtifactKindMapped (Aeson.String "guide") == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactKindCases)))
-    , ("wire enum arm: conformance.structural.ArtifactKind.v1/reference", any (\(_, value) -> encodeArtifactKindMapped value == Aeson.String "reference" && decodeArtifactKindMapped (Aeson.String "reference") == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactKindCases)))
-    , ("wire enum unknown tag: conformance.structural.ArtifactKind.v1", isLeft (decodeArtifactKindMapped (Aeson.String "__keiro_unknown")))
-    , ("wire union arm: conformance.structural.ArtifactLocation.v1/local_file", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "local_file") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire union arm: conformance.structural.ArtifactLocation.v1/local_dir", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "local_dir") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire union arm: conformance.structural.ArtifactLocation.v1/repo_path", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "repo_path") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire union arm: conformance.structural.ArtifactLocation.v1/url", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "url") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire union arm: conformance.structural.ArtifactLocation.v1/canonical", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "canonical") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire policy unknown fields: conformance.structural.ArtifactLocation.v1", all (\(_, value) -> isLeft (decodeArtifactLocationMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactLocationMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
-    , ("wire policy unknown fields: conformance.structural.ArtifactMetadata.v1", all (\(_, value) -> isRight (decodeArtifactMetadataMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactMetadataMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactMetadataCases)))
-    ]
+  [ ("wire policy missing default: conformance.structural.ArtifactInfo.v1/artifact_hash", case decodeArtifactInfoMapped (deleteObjectField "artifact_hash" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "artifact_hash" (encodeArtifactInfoMapped decoded) == Just (Aeson.Null))
+  , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/artifact_hash", isRight (decodeArtifactInfoMapped (insertObjectField "artifact_hash" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
+  , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/artifact_kind", case decodeArtifactInfoMapped (deleteObjectField "artifact_kind" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "artifact_kind" (encodeArtifactInfoMapped decoded) == Just (Aeson.String "guide"))
+  , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/artifact_kind", isLeft (decodeArtifactInfoMapped (insertObjectField "artifact_kind" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
+  , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/active", case decodeArtifactInfoMapped (deleteObjectField "active" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "active" (encodeArtifactInfoMapped decoded) == Just (Aeson.Bool False))
+  , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/active", isLeft (decodeArtifactInfoMapped (insertObjectField "active" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
+  , ("wire policy missing default: conformance.structural.ArtifactInfo.v1/tags", case decodeArtifactInfoMapped (deleteObjectField "tags" (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases))))) of Left _ -> False; Right decoded -> objectField "tags" (encodeArtifactInfoMapped decoded) == Just (Aeson.toJSON ([] :: [Aeson.Value])))
+  , ("wire policy explicit null: conformance.structural.ArtifactInfo.v1/tags", isLeft (decodeArtifactInfoMapped (insertObjectField "tags" Aeson.Null (encodeArtifactInfoMapped (snd (NonEmpty.head (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))))))
+  , ("wire policy unknown fields: conformance.structural.ArtifactInfo.v1", all (\(_, value) -> isLeft (decodeArtifactInfoMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactInfoMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
+  , ("wire enum arm: conformance.structural.ArtifactKind.v1/guide", any (\(_, value) -> encodeArtifactKindMapped value == Aeson.String "guide" && decodeArtifactKindMapped (Aeson.String "guide") == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactKindCases)))
+  , ("wire enum arm: conformance.structural.ArtifactKind.v1/reference", any (\(_, value) -> encodeArtifactKindMapped value == Aeson.String "reference" && decodeArtifactKindMapped (Aeson.String "reference") == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactKindCases)))
+  , ("wire enum unknown tag: conformance.structural.ArtifactKind.v1", isLeft (decodeArtifactKindMapped (Aeson.String "__keiro_unknown")))
+  , ("wire union arm: conformance.structural.ArtifactLocation.v1/local_file", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "local_file") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire union arm: conformance.structural.ArtifactLocation.v1/local_dir", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "local_dir") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire union arm: conformance.structural.ArtifactLocation.v1/repo_path", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "repo_path") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire union arm: conformance.structural.ArtifactLocation.v1/url", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "url") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire union arm: conformance.structural.ArtifactLocation.v1/canonical", any (\(_, value) -> objectField "tag" (encodeArtifactLocationMapped value) == Just (Aeson.String "canonical") && decodeArtifactLocationMapped (encodeArtifactLocationMapped value) == Right value) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire policy unknown fields: conformance.structural.ArtifactLocation.v1", all (\(_, value) -> isLeft (decodeArtifactLocationMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactLocationMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactLocationCases)))
+  , ("wire policy unknown fields: conformance.structural.ArtifactMetadata.v1", all (\(_, value) -> isRight (decodeArtifactMetadataMapped (insertObjectField "__keiro_unknown" (Aeson.Bool True) (encodeArtifactMetadataMapped value)))) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactMetadataCases)))
+  ]
 
 structuralProjectionAssertions :: [(String, Bool)]
 structuralProjectionAssertions =
-    [ ("projection witness agreement: conformance.structural.ArtifactInfo.v1/artifact_key", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness (\referenceOwner -> Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
-    , ("projection witness agreement: conformance.structural.ArtifactInfo.v1/display_name", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65ZWitness (\referenceOwner -> Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
-    ]
+  [ ("projection witness agreement: conformance.structural.ArtifactInfo.v1/artifact_key", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.artifactInfoArtifactKeyWitness (\referenceOwner -> Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
+  , ("projection witness agreement: conformance.structural.ArtifactInfo.v1/display_name", all (\(_, owner) -> fieldWitnessAgrees StructuralProjections.artifactInfoDisplayNameWitness (\referenceOwner -> Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding referenceOwner)) owner) (NonEmpty.toList (fixtureCases Conformance.Structural.Bindings.artifactInfoCases)))
+  ]
 
 deleteObjectField :: T.Text -> Aeson.Value -> Aeson.Value
 deleteObjectField key (Aeson.Object objectValue) = Aeson.Object (AesonKeyMap.delete (AesonKey.fromText key) objectValue)
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Projection.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Projection.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Projection.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
 module Generated.StructuralConformance.ArtifactCatalog.Projection () where
diff --git a/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Transducer.hs b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-structural/Generated/StructuralConformance/ArtifactCatalog/Transducer.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ArtifactCatalog; do not edit.
+module Generated.StructuralConformance.ArtifactCatalog.Transducer
+  ( artifactCatalogTransducer
+  , artifactCatalogFoldFingerprint
+  , BehaviorOwnership (..)
+  , artifactCatalogPredicateVerifications
+  ) where
+
+import Generated.StructuralConformance.ArtifactCatalog.Domain
+import Data.Text (Text)
+import Conformance.Structural.Domain qualified
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import StructuralConformance.ArtifactCatalog.Holes qualified as Holes
+import Keiki.Builder ((=:))
+
+artifactCatalogTransducer
+  :: SymTransducer
+       (HsPred ArtifactCatalogRegs ArtifactCatalogCommand)
+       ArtifactCatalogRegs
+       ArtifactCatalogVertex
+       ArtifactCatalogCommand
+       ArtifactCatalogEvent
+artifactCatalogTransducer =
+  B.buildTransducer ArtifactCatalogEmpty initialArtifactCatalogRegs isTerminal do
+    B.from ArtifactCatalogEmpty do
+      B.onCmd inCtorObserveArtifact $ \d -> B.do
+        B.slot @"currentArtifact" =: d.artifact
+        B.slot @"currentGeometry" =: d.geometry
+        B.slot @"acceptedCount" =: B.reg @"acceptedCount"
+        B.emit wireArtifactRecorded (ArtifactRecordedTermFields
+          { artifact = d.artifact
+          , geometry = d.geometry
+          , accepted = d.accepted
+          })
+        B.emit wireArtifactAccepted (Holes.transition1EmptyObserveArtifactOutput2ArtifactAccepted d)
+        B.goto ArtifactCatalogObserved
+ where
+  isTerminal = \case
+    ArtifactCatalogObserved -> True
+    _ -> False
+
+artifactCatalogFoldFingerprint :: Text
+artifactCatalogFoldFingerprint = "e31d3344ae234ff96862f11e9c823f9d"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+artifactCatalogPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+artifactCatalogPredicateVerifications = sequence
+  [ verifyTransition "transition1EmptyObserveArtifact" GeneratedOwned ArtifactCatalogEmpty 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut artifactCatalogTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-structural/Generated/StructuralConformance/ReplayAudit.hs b/test/conformance-structural/Generated/StructuralConformance/ReplayAudit.hs
--- a/test/conformance-structural/Generated/StructuralConformance/ReplayAudit.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context structural-conformance replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = ArtifactCatalog.artifactCatalogEventStream
-            , category = Stream.categoryText ArtifactCatalog.artifactCatalogCategory
-            , mkStream = streamInCategory (Stream.categoryText ArtifactCatalog.artifactCatalogCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = ArtifactCatalog.artifactCatalogEventStream
+        , category = Stream.categoryText ArtifactCatalog.artifactCatalogCategory
+        , mkStream = streamInCategory (Stream.categoryText ArtifactCatalog.artifactCatalogCategory)
+        }
+  ]
diff --git a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactInfo.hs b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactInfo.hs
--- a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactInfo.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactInfo.hs
@@ -1,7 +1,6 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DuplicateRecordFields #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural ArtifactInfo; do not edit.
 module Generated.StructuralConformance.Structural.Shape.ArtifactInfo (ArtifactInfoShape (..)) where
 
 import Data.Text (Text)
@@ -11,13 +10,13 @@
 import Generated.StructuralConformance.Structural.Shape.ArtifactMetadata qualified
 
 data ArtifactInfoShape = ArtifactInfo
-    { artifactKey :: !Text
-    , displayName :: !Text
-    , artifactHash :: !(Maybe (Text))
-    , artifactKind :: !Generated.StructuralConformance.Structural.Shape.ArtifactKind.ArtifactKindShape
-    , location :: !Generated.StructuralConformance.Structural.Shape.ArtifactLocation.ArtifactLocationShape
-    , metadata :: !Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadataShape
-    , active :: !Bool
-    , tags :: !([Text])
-    }
-    deriving stock (Eq, Generic, Show)
+  { artifactKey :: !Text
+  , displayName :: !Text
+  , artifactHash :: !(Maybe (Text))
+  , artifactKind :: !Generated.StructuralConformance.Structural.Shape.ArtifactKind.ArtifactKindShape
+  , location :: !Generated.StructuralConformance.Structural.Shape.ArtifactLocation.ArtifactLocationShape
+  , metadata :: !Generated.StructuralConformance.Structural.Shape.ArtifactMetadata.ArtifactMetadataShape
+  , active :: !Bool
+  , tags :: !([Text])
+  }
+  deriving stock (Eq, Generic, Show)
diff --git a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactKind.hs b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactKind.hs
--- a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactKind.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactKind.hs
@@ -1,9 +1,8 @@
 {-# LANGUAGE DeriveGeneric #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural ArtifactKind; do not edit.
 module Generated.StructuralConformance.Structural.Shape.ArtifactKind (ArtifactKindShape (..)) where
 
 import GHC.Generics (Generic)
 
 data ArtifactKindShape = Guide | Reference
-    deriving stock (Eq, Generic, Show)
+  deriving stock (Eq, Generic, Show)
diff --git a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactLocation.hs b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactLocation.hs
--- a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactLocation.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactLocation.hs
@@ -1,15 +1,13 @@
 {-# LANGUAGE DeriveGeneric #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural ArtifactLocation; do not edit.
 module Generated.StructuralConformance.Structural.Shape.ArtifactLocation (ArtifactLocationShape (..)) where
 
 import Data.Text (Text)
 import GHC.Generics (Generic)
 
-data ArtifactLocationShape
-    = LocalFile !Text
-    | LocalDir !Text
-    | RepoPath !Text
-    | LocUrl !Text
-    | Canonical
-    deriving stock (Eq, Generic, Show)
+data ArtifactLocationShape = LocalFile !Text
+  | LocalDir !Text
+  | RepoPath !Text
+  | LocUrl !Text
+  | Canonical
+  deriving stock (Eq, Generic, Show)
diff --git a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactMetadata.hs b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactMetadata.hs
--- a/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactMetadata.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/Structural/Shape/ArtifactMetadata.hs
@@ -1,13 +1,12 @@
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE DuplicateRecordFields #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from mapped structural ArtifactMetadata; do not edit.
 module Generated.StructuralConformance.Structural.Shape.ArtifactMetadata (ArtifactMetadataShape (..)) where
 
 import Data.Text (Text)
 import GHC.Generics (Generic)
 
 data ArtifactMetadataShape = ArtifactMetadata
-    { note :: !(Maybe (Text))
-    }
-    deriving stock (Eq, Generic, Show)
+  { note :: !(Maybe (Text))
+  }
+  deriving stock (Eq, Generic, Show)
diff --git a/test/conformance-structural/Generated/StructuralConformance/StructuralProjections.hs b/test/conformance-structural/Generated/StructuralConformance/StructuralProjections.hs
--- a/test/conformance-structural/Generated/StructuralConformance/StructuralProjections.hs
+++ b/test/conformance-structural/Generated/StructuralConformance/StructuralProjections.hs
@@ -1,43 +1,43 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE TypeApplications #-}
 {-# LANGUAGE TypeFamilies #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
--- Equality witnesses are emitted for Text, Int, Bool, and UTCTime.
--- Only Int and UTCTime belong to Keiki's v1 ordered subset.
-module Generated.StructuralConformance.StructuralProjections (
-    structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness,
-    structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65ZWitness,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context structural-conformance mapped structural facade; do not edit.
+-- Equality witnesses are emitted for Text, Int, Bool, Natural, and UTCTime.
+-- Int, Natural, and UTCTime belong to Keiki's ordered subset.
+module Generated.StructuralConformance.StructuralProjections
+  ( artifactInfoArtifactKeyWitness
+  , artifactInfoDisplayNameWitness
+  ) where
 
-import Conformance.Structural.Bindings qualified
-import Conformance.Structural.Domain qualified
 import Data.Text (Text)
 import Data.Time (UTCTime)
-import Generated.StructuralConformance.Structural.Shape.ArtifactInfo qualified
-import Keiki.Core (FieldProjection (..), FieldWitness, fieldWitness)
+import Numeric.Natural (Natural)
 import Keiro.Codec.Structural (bindingToShape)
+import Keiki.Core (FieldProjection (..), FieldWitness, fieldWitness)
+import Conformance.Structural.Bindings qualified
+import Conformance.Structural.Domain qualified
+import Generated.StructuralConformance.Structural.Shape.ArtifactInfo qualified
 
-data StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z
+data ArtifactInfoArtifactKeyProjection
 
-instance FieldProjection StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z where
-    type FieldName StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z = "/artifact_key"
-    type FieldOwner StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z = Conformance.Structural.Domain.ArtifactInfo
-    type FieldResult StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z = Text
-    fieldShapeId _ = "conformance.structural.ArtifactInfo.v1"
-    projectFieldValue _ owner = Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding owner)
+instance FieldProjection ArtifactInfoArtifactKeyProjection where
+  type FieldName ArtifactInfoArtifactKeyProjection = "/artifact_key"
+  type FieldOwner ArtifactInfoArtifactKeyProjection = Conformance.Structural.Domain.ArtifactInfo
+  type FieldResult ArtifactInfoArtifactKeyProjection = Text
+  fieldShapeId _ = "conformance.structural.ArtifactInfo.v1"
+  projectFieldValue _ owner = Generated.StructuralConformance.Structural.Shape.ArtifactInfo.artifactKey (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding owner)
 
-structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness :: FieldWitness StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z
-structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness = fieldWitness @StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79Z
+artifactInfoArtifactKeyWitness :: FieldWitness ArtifactInfoArtifactKeyProjection
+artifactInfoArtifactKeyWitness = fieldWitness @ArtifactInfoArtifactKeyProjection
 
-data StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z
+data ArtifactInfoDisplayNameProjection
 
-instance FieldProjection StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z where
-    type FieldName StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z = "/display_name"
-    type FieldOwner StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z = Conformance.Structural.Domain.ArtifactInfo
-    type FieldResult StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z = Text
-    fieldShapeId _ = "conformance.structural.ArtifactInfo.v1"
-    projectFieldValue _ owner = Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding owner)
+instance FieldProjection ArtifactInfoDisplayNameProjection where
+  type FieldName ArtifactInfoDisplayNameProjection = "/display_name"
+  type FieldOwner ArtifactInfoDisplayNameProjection = Conformance.Structural.Domain.ArtifactInfo
+  type FieldResult ArtifactInfoDisplayNameProjection = Text
+  fieldShapeId _ = "conformance.structural.ArtifactInfo.v1"
+  projectFieldValue _ owner = Generated.StructuralConformance.Structural.Shape.ArtifactInfo.displayName (bindingToShape Conformance.Structural.Bindings.artifactInfoBinding owner)
 
-structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65ZWitness :: FieldWitness StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z
-structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65ZWitness = fieldWitness @StructuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC64ZC69ZC73ZC70ZC6cZC61ZC79ZC5fZC6eZC61ZC6dZC65Z
+artifactInfoDisplayNameWitness :: FieldWitness ArtifactInfoDisplayNameProjection
+artifactInfoDisplayNameWitness = fieldWitness @ArtifactInfoDisplayNameProjection
diff --git a/test/conformance-structural/Main.hs b/test/conformance-structural/Main.hs
--- a/test/conformance-structural/Main.hs
+++ b/test/conformance-structural/Main.hs
@@ -1,15 +1,17 @@
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
 
 module Main (main) where
 
 import Conformance.Structural.Bindings qualified as Bindings
 import Control.Monad (forM_, unless)
+import Data.Aeson (Value (..), object, (.=))
 import Data.Aeson qualified as Aeson
+import Data.Aeson.KeyMap qualified as KeyMap
 import Data.ByteString.Lazy qualified as LazyByteString
 import Data.ByteString.Lazy.Char8 qualified as LazyChar8
 import Data.List.NonEmpty qualified as NonEmpty
-import Generated.StructuralConformance.ArtifactCatalog.Codec (encodeArtifactCatalogEvent)
+import Data.Text qualified as T
+import Generated.StructuralConformance.ArtifactCatalog.Codec (decodeArtifactInfoMapped, decodeArtifactKindMapped, decodeArtifactLocationMapped, encodeArtifactCatalogEvent, encodeArtifactInfoMapped)
 import Generated.StructuralConformance.ArtifactCatalog.Domain (ArtifactCatalogCommand, ArtifactCatalogEvent (..), ArtifactCatalogRegs, ArtifactRecordedData (..), inCtorObserveArtifact)
 import Generated.StructuralConformance.ArtifactCatalog.Harness (harnessAssertions)
 import Generated.StructuralConformance.StructuralProjections qualified as StructuralProjections
@@ -21,7 +23,7 @@
 main :: IO ()
 main = do
     goldenAssertions <- loadGoldenAssertions
-    let assertions = harnessAssertions <> projectionAssertions <> goldenAssertions
+    let assertions = harnessAssertions <> projectionAssertions <> decodeDiagnosticAssertions <> goldenAssertions
     forM_ assertions $ \(label, ok) ->
         putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
     let failed = [label | (label, ok) <- assertions, not ok]
@@ -41,7 +43,38 @@
         )
     ]
   where
-    artifactKeyWitness = StructuralProjections.structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness
+    artifactKeyWitness = StructuralProjections.artifactInfoArtifactKeyWitness
+
+decodeDiagnosticAssertions :: [(String, Bool)]
+decodeDiagnosticAssertions =
+    [ ( "mapped enum failure names the value and expected set"
+      , leftContains ["unlisted", "guide", "reference"] (decodeArtifactKindMapped (String "unlisted"))
+      )
+    , ( "mapped union failure points at the tag and names every arm"
+      , leftContains
+            ["$.tag", "remote", "local_file", "local_dir", "repo_path", "url", "canonical"]
+            (decodeArtifactLocationMapped (object ["tag" .= ("remote" :: T.Text)]))
+      )
+    , ( "nested mapped failure retains the complete field path"
+      , leftContains ["$.location.contents"] nestedFailure
+      )
+    ]
+  where
+    artifact = snd (NonEmpty.head (fixtureCases Bindings.artifactInfoCases))
+    nestedFailure = case encodeArtifactInfoMapped artifact of
+        Object fields ->
+            decodeArtifactInfoMapped
+                ( Object
+                    ( KeyMap.insert
+                        "location"
+                        (object ["tag" .= ("local_file" :: T.Text), "contents" .= (7 :: Int)])
+                        fields
+                    )
+                )
+        _ -> error "ArtifactInfo mapped encoder did not produce an object"
+    leftContains fragments result = case result of
+        Left problem -> all (`T.isInfixOf` problem) fragments
+        Right _ -> False
 
 loadGoldenAssertions :: IO [(String, Bool)]
 loadGoldenAssertions = do
diff --git a/test/conformance-structural/StructuralConformance/ArtifactCatalog/Holes.hs b/test/conformance-structural/StructuralConformance/ArtifactCatalog/Holes.hs
--- a/test/conformance-structural/StructuralConformance/ArtifactCatalog/Holes.hs
+++ b/test/conformance-structural/StructuralConformance/ArtifactCatalog/Holes.hs
@@ -1,46 +1,19 @@
-{-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
--- overwrites it. Fill the transducer body (and any other holes) against the
--- generated signatures, then run the harness to confirm behaviour.
-module StructuralConformance.ArtifactCatalog.Holes (
-    artifactCatalogTransducer,
-) where
+-- HAND-OWNED language-4 event mapping. Generated code owns the aggregate
+-- lifecycle, guards, writes, and transition envelope.
+module StructuralConformance.ArtifactCatalog.Holes
+  ( transition1EmptyObserveArtifactOutput2ArtifactAccepted
+  ) where
 
 import Generated.StructuralConformance.ArtifactCatalog.Domain
-import Generated.StructuralConformance.StructuralProjections qualified as StructuralProjections
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, inpProj, lit)
+import Keiki.Generics (RegFieldsOf)
 
--- HOLE: the transducer body. Reproduce the structure below, replacing each
--- `-- HOLE` line with the keiki symbolic operators it describes.
-artifactCatalogTransducer ::
-    SymTransducer
-        (HsPred ArtifactCatalogRegs ArtifactCatalogCommand)
-        ArtifactCatalogRegs
-        ArtifactCatalogVertex
-        ArtifactCatalogCommand
-        ArtifactCatalogEvent
-artifactCatalogTransducer =
-    B.buildTransducer ArtifactCatalogEmpty initialArtifactCatalogRegs isTerminal do
-        B.from ArtifactCatalogEmpty do
-            B.onCmd inCtorObserveArtifact $ \d -> B.do
-                B.requireEq
-                    (inpProj StructuralProjections.structuralProjectionC41ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC49ZC6eZC66ZC6fZC2fZC61ZC72ZC74ZC69ZC66ZC61ZC63ZC74ZC5fZC6bZC65ZC79ZWitness inCtorObserveArtifact #artifact)
-                    (lit "artifact-local-file")
-                B.slot @"currentArtifact" =: d.artifact
-                B.slot @"currentGeometry" =: d.geometry
-                B.slot @"acceptedCount" =: B.reg @"acceptedCount"
-                B.emit wireArtifactRecorded ArtifactRecordedTermFields{artifact = d.artifact, geometry = d.geometry, accepted = d.accepted}
-                B.emit wireArtifactAccepted ArtifactAcceptedTermFields{accepted = d.accepted}
-                B.goto ArtifactCatalogObserved
-  where
-    isTerminal = \case
-        ArtifactCatalogObserved -> True
-        _ -> False
+transition1EmptyObserveArtifactOutput2ArtifactAccepted
+  :: B.PayloadProj ArtifactCatalogRegs ArtifactCatalogCommand (RegFieldsOf ObserveArtifactData)
+  -> ArtifactAcceptedTermFields ArtifactCatalogRegs ArtifactCatalogCommand (RegFieldsOf ObserveArtifactData)
+transition1EmptyObserveArtifactOutput2ArtifactAccepted d =
+  ArtifactAcceptedTermFields {accepted = d.accepted}
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Nominals.hs b/test/conformance-v2/Generated/HospitalCapacity/Nominals.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-v2/Generated/HospitalCapacity/Nominals.hs
@@ -0,0 +1,100 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
+{-# LANGUAGE DeriveAnyClass #-}
+{-# LANGUAGE DeriveGeneric #-}
+{-# LANGUAGE LambdaCase #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal declarations; do not edit.
+module Generated.HospitalCapacity.Nominals
+  ( BedType (..)
+  , bedTypeText
+  , CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , DivertStatus (..)
+  , divertStatusText
+  , DivertStatusEqualityProjection
+  , divertStatusEqualityWitness
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , PatientAcuity (..)
+  , patientAcuityText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  ) where
+
+import Data.Aeson (FromJSON, ToJSON)
+import Data.Text (Text)
+import GHC.Generics (Generic)
+import Keiki.Shape (CanonicalTypeName)
+import Generated.HospitalCapacity.Nominals.Internal (CommandId, mkCommandId, parseCommandId, commandIdText, HospitalId, mkHospitalId, parseHospitalId, hospitalIdText, TransferReservationId, mkTransferReservationId, parseTransferReservationId, transferReservationIdText)
+import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
+import Data.List.NonEmpty (NonEmpty (..))
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
+
+data BedType = Icu | MedicalSurgical
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName BedType
+
+bedTypeText :: BedType -> Text
+bedTypeText = \case
+  Icu -> "icu"
+  MedicalSurgical -> "medical-surgical"
+
+instance CanonicalTypeName CommandId
+
+data DivertStatus = Open | PartialDivert | TotalDivert
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName DivertStatus
+
+divertStatusText :: DivertStatus -> Text
+divertStatusText = \case
+  Open -> "open"
+  PartialDivert -> "partial-divert"
+  TotalDivert -> "total-divert"
+
+data DivertStatusEqualityProjection
+
+instance FieldProjection DivertStatusEqualityProjection where
+  type FieldName DivertStatusEqualityProjection = "DivertStatus"
+  type FieldOwner DivertStatusEqualityProjection = DivertStatus
+  type FieldResult DivertStatusEqualityProjection = Text
+  fieldShapeId _ = "nominal-equality|name=DivertStatus|contract=keiro-dsl/nominal-equality/1|key=Text|domain=finite-text:open,partial-divert,total-divert|owner=generated"
+  projectFieldValue _ = divertStatusText
+
+instance ExactFieldProjection DivertStatusEqualityProjection where
+  fieldProjectionDomain _ = finiteProjectionDomain ("open" :| ["partial-divert", "total-divert"])
+  reconstructFieldOwner _ = \case
+    "open" -> Just Open
+    "partial-divert" -> Just PartialDivert
+    "total-divert" -> Just TotalDivert
+    _ -> Nothing
+
+divertStatusEqualityWitness :: FieldWitness DivertStatusEqualityProjection
+divertStatusEqualityWitness = exactFieldWitness @DivertStatusEqualityProjection
+
+instance CanonicalTypeName HospitalId
+
+data PatientAcuity = RedTag | YellowTag | GreenTag
+  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
+  deriving anyclass (ToJSON, FromJSON)
+
+instance CanonicalTypeName PatientAcuity
+
+patientAcuityText :: PatientAcuity -> Text
+patientAcuityText = \case
+  RedTag -> "red"
+  YellowTag -> "yellow"
+  GreenTag -> "green"
+
+instance CanonicalTypeName TransferReservationId
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Nominals/Internal.hs b/test/conformance-v2/Generated/HospitalCapacity/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-v2/Generated/HospitalCapacity/Nominals/Internal.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal ID internals; do not edit.
+module Generated.HospitalCapacity.Nominals.Internal
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , unsafeCommandIdFromLegacyText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  , unsafeTransferReservationIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CommandId = CommandId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CommandId where
+  toJSON = toJSON . commandIdText
+
+instance FromJSON CommandId where
+  parseJSON = withText "CommandId" (either (fail . T.unpack) pure . parseCommandId)
+
+parseCommandId :: Text -> Either Text CommandId
+parseCommandId input = case validateIdDomainText (typeIdV7Domain "cmd") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CommandId input)
+
+mkCommandId :: Text -> Either Text CommandId
+mkCommandId = parseCommandId
+
+commandIdText :: CommandId -> Text
+commandIdText (CommandId value) = value
+
+unsafeCommandIdFromLegacyText :: Text -> CommandId
+unsafeCommandIdFromLegacyText = CommandId
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
+
+newtype TransferReservationId = TransferReservationId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON TransferReservationId where
+  toJSON = toJSON . transferReservationIdText
+
+instance FromJSON TransferReservationId where
+  parseJSON = withText "TransferReservationId" (either (fail . T.unpack) pure . parseTransferReservationId)
+
+parseTransferReservationId :: Text -> Either Text TransferReservationId
+parseTransferReservationId input = case validateIdDomainText (typeIdV7Domain "rsv") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (TransferReservationId input)
+
+mkTransferReservationId :: Text -> Either Text TransferReservationId
+mkTransferReservationId = parseTransferReservationId
+
+transferReservationIdText :: TransferReservationId -> Text
+transferReservationIdText (TransferReservationId value) = value
+
+unsafeTransferReservationIdFromLegacyText :: Text -> TransferReservationId
+unsafeTransferReservationIdFromLegacyText = TransferReservationId
diff --git a/test/conformance-v2/Generated/HospitalCapacity/ReplayAudit.hs b/test/conformance-v2/Generated/HospitalCapacity/ReplayAudit.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/ReplayAudit.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/ReplayAudit.hs
@@ -1,6 +1,5 @@
 {-# LANGUAGE GADTs #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
@@ -16,10 +15,10 @@
 
 auditTargets :: [SomeAuditTarget]
 auditTargets =
-    [ SomeAuditTarget
-        AuditTarget
-            { eventStream = Reservation.reservationEventStream
-            , category = Stream.categoryText Reservation.reservationCategory
-            , mkStream = streamInCategory (Stream.categoryText Reservation.reservationCategory)
-            }
-    ]
+  [ SomeAuditTarget
+      AuditTarget
+        { eventStream = Reservation.reservationEventStream
+        , category = Stream.categoryText Reservation.reservationCategory
+        , mkStream = streamInCategory (Stream.categoryText Reservation.reservationCategory)
+        }
+  ]
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Codec.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Codec.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Codec.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Codec.hs
@@ -1,53 +1,50 @@
+{-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Codec (
     reservationCodec,
     parseReservationEvent,
     encodeReservationEvent,
 ) where
 
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.HospitalCapacity.Reservation.Domain
+import Generated.HospitalCapacity.Nominals (CommandId, commandIdText, DivertStatus (..), divertStatusText, HospitalId, hospitalIdText, PatientAcuity (..), patientAcuityText, TransferReservationId, transferReservationIdText)
+import Generated.HospitalCapacity.Nominals.Internal (unsafeCommandIdFromLegacyText, unsafeHospitalIdFromLegacyText, unsafeTransferReservationIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
-import Data.Text qualified as T
-import Generated.HospitalCapacity.Reservation.Domain
-import HospitalCapacity.Reservation.Holes (upcastTransferReservationCreatedV1)
+import qualified Data.Text as T
 import Keiro.Codec (Codec (..), EventType (..))
+import HospitalCapacity.Reservation.Holes (upcastTransferReservationCreatedV1)
 
+parseDivertStatus :: Text -> Parser DivertStatus
+parseDivertStatus = \case
+  "open" -> pure Open
+  "partial-divert" -> pure PartialDivert
+  "total-divert" -> pure TotalDivert
+  tag -> fail ("unknown DivertStatus " <> show tag <> "; expected one of: open, partial-divert, total-divert")
+
 parsePatientAcuity :: Text -> Parser PatientAcuity
 parsePatientAcuity = \case
-    "red" -> pure RedTag
-    "yellow" -> pure YellowTag
-    "green" -> pure GreenTag
-    _ -> fail "unknown PatientAcuity"
+  "red" -> pure RedTag
+  "yellow" -> pure YellowTag
+  "green" -> pure GreenTag
+  tag -> fail ("unknown PatientAcuity " <> show tag <> "; expected one of: red, yellow, green")
 
-parseBedType :: Text -> Parser BedType
-parseBedType = \case
-    "icu" -> pure Icu
-    "medical-surgical" -> pure MedicalSurgical
-    _ -> fail "unknown BedType"
 
-parseDivertStatus :: Text -> Parser DivertStatus
-parseDivertStatus = \case
-    "open" -> pure Open
-    "partial-divert" -> pure PartialDivert
-    "total-divert" -> pure TotalDivert
-    _ -> fail "unknown DivertStatus"
-
 reservationCodec :: Codec ReservationEvent
 reservationCodec =
-    Codec
-        { eventTypes = EventType "TransferReservationCreated" :| [EventType "TransferReservationConfirmed"]
-        , eventType = \case
-            TransferReservationCreated{} -> EventType "TransferReservationCreated"
-            TransferReservationConfirmed{} -> EventType "TransferReservationConfirmed"
-        , schemaVersion = 2
-        , encode = encodeReservationEvent
-        , decode = parseReservationEvent
-        , upcasters = [(1, upcastRungV1)]
-        }
+  Codec
+    { eventTypes = EventType "TransferReservationCreated" :| [EventType "TransferReservationConfirmed"]
+    , eventType = \case
+        TransferReservationCreated{} -> EventType "TransferReservationCreated"
+        TransferReservationConfirmed{} -> EventType "TransferReservationConfirmed"
+    , schemaVersion = 2
+    , encode = encodeReservationEvent
+    , decode = parseReservationEvent
+    , upcasters = [(1, upcastRungV1)]
+    }
 
 upcastRungV1 :: EventType -> Value -> Either Text Value
 upcastRungV1 (EventType "TransferReservationCreated") value = upcastTransferReservationCreatedV1 value
@@ -57,35 +54,49 @@
 
 encodeReservationEvent :: ReservationEvent -> Value
 encodeReservationEvent = \case
-    TransferReservationCreated payload ->
-        object
-            [ "kind" .= ("TransferReservationCreated" :: Text)
-            , "reservationId" .= transferReservationIdText payload.reservationId
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            , "commandId" .= commandIdText payload.commandId
-            , "patientAcuity" .= patientAcuityText payload.patientAcuity
-            , "divertStatus" .= divertStatusText payload.divertStatus
-            , "lifeCriticalOverride" .= payload.lifeCriticalOverride
-            , "triageNote" .= payload.triageNote
-            ]
-    TransferReservationConfirmed payload ->
-        object
-            [ "kind" .= ("TransferReservationConfirmed" :: Text)
-            , "reservationId" .= transferReservationIdText payload.reservationId
-            , "hospitalId" .= hospitalIdText payload.hospitalId
-            , "commandId" .= commandIdText payload.commandId
-            ]
+  TransferReservationCreated payload ->
+    object
+      [ "kind" .= ("TransferReservationCreated" :: Text)
+      , "reservationId" .= transferReservationIdText payload.reservationId
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "commandId" .= commandIdText payload.commandId
+      , "patientAcuity" .= patientAcuityText payload.patientAcuity
+      , "divertStatus" .= divertStatusText payload.divertStatus
+      , "lifeCriticalOverride" .= payload.lifeCriticalOverride
+      , "triageNote" .= payload.triageNote
+      ]
+  TransferReservationConfirmed payload ->
+    object
+      [ "kind" .= ("TransferReservationConfirmed" :: Text)
+      , "reservationId" .= transferReservationIdText payload.reservationId
+      , "hospitalId" .= hospitalIdText payload.hospitalId
+      , "commandId" .= commandIdText payload.commandId
+      ]
 
 parseReservationEvent :: EventType -> Value -> Either Text ReservationEvent
 parseReservationEvent (EventType tag) = mapLeftText . parseEither (withObject "ReservationEvent" go)
   where
     go o = do
-        case tag of
-            "TransferReservationCreated" ->
-                TransferReservationCreated <$> (TransferReservationCreatedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId") <*> (o .: "patientAcuity" >>= parsePatientAcuity) <*> (o .: "divertStatus" >>= parseDivertStatus) <*> o .: "lifeCriticalOverride" <*> o .: "triageNote")
-            "TransferReservationConfirmed" ->
-                TransferReservationConfirmed <$> (TransferReservationConfirmedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId"))
-            _ -> fail "unknown event type"
+      case tag of
+        "TransferReservationCreated" ->
+          TransferReservationCreated
+            <$> ( TransferReservationCreatedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                    <*> explicitParseField (withText "PatientAcuity" parsePatientAcuity) o "patientAcuity"
+                    <*> explicitParseField (withText "DivertStatus" parseDivertStatus) o "divertStatus"
+                    <*> o .: "lifeCriticalOverride"
+                    <*> o .: "triageNote"
+                )
+        "TransferReservationConfirmed" ->
+          TransferReservationConfirmed
+            <$> ( TransferReservationConfirmedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: TransferReservationCreated, TransferReservationConfirmed")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Domain.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Domain.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Domain.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Domain.hs
@@ -2,60 +2,16 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
+import Generated.HospitalCapacity.Nominals (CommandId, parseCommandId, DivertStatus (..), HospitalId, parseHospitalId, PatientAcuity (..), TransferReservationId, parseTransferReservationId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
-newtype TransferReservationId = TransferReservationId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-transferReservationIdText :: TransferReservationId -> Text
-transferReservationIdText (TransferReservationId t) = t
-
-newtype HospitalId = HospitalId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId t) = t
-
-newtype CommandId = CommandId Text
-  deriving stock (Generic, Eq, Ord, Show)
-
-commandIdText :: CommandId -> Text
-commandIdText (CommandId t) = t
-
-data PatientAcuity = RedTag | YellowTag | GreenTag
-  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-
-patientAcuityText :: PatientAcuity -> Text
-patientAcuityText = \case
-  RedTag -> "red"
-  YellowTag -> "yellow"
-  GreenTag -> "green"
-
-data BedType = Icu | MedicalSurgical
-  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-
-bedTypeText :: BedType -> Text
-bedTypeText = \case
-  Icu -> "icu"
-  MedicalSurgical -> "medical-surgical"
-
-data DivertStatus = Open | PartialDivert | TotalDivert
-  deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
-
-divertStatusText :: DivertStatus -> Text
-divertStatusText = \case
-  Open -> "open"
-  PartialDivert -> "partial-divert"
-  TotalDivert -> "total-divert"
-
 data ReservationVertex = ReservationUnrequested | ReservationHeld | ReservationConfirmed | ReservationExpired | ReservationAdmitted | ReservationReleased
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
 
@@ -106,15 +62,13 @@
   '[ '("reservationId", TransferReservationId)
    , '("hospitalId", HospitalId)
    , '("patientAcuity", PatientAcuity)
-   , '("reservationState", ReservationVertex)
    ]
 
 initialReservationRegs :: RegFile ReservationRegs
 initialReservationRegs =
-  RCons (Proxy @"reservationId") (TransferReservationId "") $
-  RCons (Proxy @"hospitalId") (HospitalId "") $
-  RCons (Proxy @"patientAcuity") GreenTag $
-  RCons (Proxy @"reservationState") ReservationUnrequested RNil
+  RCons (Proxy @"reservationId") (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"hospitalId") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"patientAcuity") GreenTag RNil
 
 $(deriveAggregateCtorsAll ''ReservationCommand ''ReservationRegs)
 
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/EventStream.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/EventStream.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/Reservation/EventStream.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/EventStream.hs
@@ -1,15 +1,16 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation.EventStream (
-    reservationCategory,
-    reservationEventStream,
-    reservationEventStreamDef,
-    ReservationEventStream,
-    ReservationEventStreamDef,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.EventStream
+  ( reservationCategory
+  , reservationCommandCategory
+  , reservationEventStream
+  , reservationEventStreamDef
+  , ReservationEventStream
+  , ReservationEventStreamDef
+  ) where
 
-import Generated.HospitalCapacity.Reservation.Codec (reservationCodec)
 import Generated.HospitalCapacity.Reservation.Domain
-import HospitalCapacity.Reservation.Holes (reservationTransducer)
+import Generated.HospitalCapacity.Reservation.Codec (reservationCodec)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationFoldFingerprint, reservationTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,27 +19,31 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-reservationCategory :: Stream.StreamCategory a
+reservationCategory :: Stream.StreamCategory ReservationEventStreamDef
 reservationCategory = Stream.categoryUnsafe "reservation"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+reservationCommandCategory :: Stream.StreamCategory ReservationCommand
+reservationCommandCategory = Stream.categoryUnsafe "reservation"
+
 type ReservationEventStreamDef =
-    EventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
+  EventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
 
 type ReservationEventStream =
-    ValidatedEventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
+  ValidatedEventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
 
 reservationEventStreamDef :: ReservationEventStreamDef
 reservationEventStreamDef =
-    EventStream
-        { transducer = reservationTransducer
-        , initialState = ReservationUnrequested
-        , initialRegisters = initialReservationRegs
-        , eventCodec = reservationCodec
-        , resolveStreamName = Stream.streamName
-        , snapshotPolicy = Never
-        , stateCodec = Nothing
-        }
+  EventStream
+    { transducer = reservationTransducer,
+      initialState = ReservationUnrequested,
+      initialRegisters = initialReservationRegs,
+      eventCodec = reservationCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
+    }
 
 reservationEventStream :: ReservationEventStream
 reservationEventStream =
-    mkEventStreamOrThrow "Reservation" reservationEventStreamDef
+  mkEventStreamOrThrow "Reservation" reservationEventStreamDef
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Harness.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Harness.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Harness.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Harness.hs
@@ -1,76 +1,67 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Harness (harnessAssertions) where
 
-import Data.Aeson (eitherDecodeStrict)
-import Data.Text.Encoding (encodeUtf8)
-import Generated.HospitalCapacity.Reservation.Codec (encodeReservationEvent, parseReservationEvent, reservationCodec)
 import Generated.HospitalCapacity.Reservation.Domain
-import HospitalCapacity.Reservation.Holes (reservationTransducer)
+import Generated.HospitalCapacity.Reservation.Codec (encodeReservationEvent, parseReservationEvent, reservationCodec)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (EventType (..), decodeRaw, eventType)
+import Generated.HospitalCapacity.Nominals (CommandId, parseCommandId, DivertStatus (..), HospitalId, parseHospitalId, PatientAcuity (..), TransferReservationId, parseTransferReservationId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
-    [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions reservationTransducer))
-    , ("clock-free: spec samples no wall clock", True)
-    , ("golden round-trip: TransferReservationCreated", roundTrips sampleEventTransferReservationCreated)
-    , ("golden round-trip: TransferReservationConfirmed", roundTrips sampleEventTransferReservationConfirmed)
-    , ("accepts RequestTransferReservation from ReservationUnrequested", acceptRequestTransferReservation)
-    ]
-        ++ forwardReplayRequestTransferReservation
-        ++ [ ("golden TransferReservationCreated.v1 decodes", upcastsTransferReservationCreated)
-           ]
+  [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions reservationTransducer))
+  , ("clock-free: spec samples no wall clock", True)
+  , ("golden round-trip: TransferReservationCreated", roundTrips sampleEventTransferReservationCreated)
+  , ("golden round-trip: TransferReservationConfirmed", roundTrips sampleEventTransferReservationConfirmed)
+  , ("accepts RequestTransferReservation from ReservationUnrequested", acceptRequestTransferReservation)
+  ]
+  ++ forwardReplayRequestTransferReservation
+  ++ [ ("upcast TransferReservationCreated chain wired (current-shape stand-in; add a golden payload)", upcastsTransferReservationCreated)
+     ]
 
 roundTrips :: ReservationEvent -> Bool
 roundTrips e = parseReservationEvent (eventType reservationCodec e) (encodeReservationEvent e) == Right e
 
 sampleEventTransferReservationCreated :: ReservationEvent
-sampleEventTransferReservationCreated = (TransferReservationCreated (TransferReservationCreatedData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False "sample-triageNote"))
+sampleEventTransferReservationCreated = (TransferReservationCreated (TransferReservationCreatedData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False "sample-triageNote"))
 
 sampleEventTransferReservationConfirmed :: ReservationEvent
-sampleEventTransferReservationConfirmed = (TransferReservationConfirmed (TransferReservationConfirmedData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample")))
+sampleEventTransferReservationConfirmed = (TransferReservationConfirmed (TransferReservationConfirmedData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))
 
 acceptRequestTransferReservation :: Bool
 acceptRequestTransferReservation =
-    case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False))) of
-        Just (v, _, _) -> v == ReservationHeld
-        Nothing -> False
+  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False))) of
+    Just (v, _, _) -> v == ReservationHeld
+    Nothing -> False
 
 -- forward/replay equality (plan 147): cross the persisted codec boundary,
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRequestTransferReservation :: [(String, Bool)]
 forwardReplayRequestTransferReservation =
-    case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False))) of
-        Nothing -> [(prefix <> "forward step accepted", False)]
-        Just (forwardVertex, forwardRegs, emitted) ->
-            case mapM (\event -> parseReservationEvent (eventType reservationCodec event) (encodeReservationEvent event)) emitted of
-                Left _ -> [(prefix <> "emitted chain decodes", False)]
-                Right decodedEvents ->
-                    case applyEventsEither reservationTransducer (ReservationUnrequested, initialReservationRegs) decodedEvents of
-                        Left _ -> [(prefix <> "replay succeeds", False)]
-                        Right (replayVertex, replayRegs) ->
-                            [ (prefix <> "final vertex", replayVertex == forwardVertex)
-                            , (prefix <> "register reservationId", (replayRegs ! #reservationId) == (forwardRegs ! #reservationId))
-                            , (prefix <> "register hospitalId", (replayRegs ! #hospitalId) == (forwardRegs ! #hospitalId))
-                            , (prefix <> "register patientAcuity", (replayRegs ! #patientAcuity) == (forwardRegs ! #patientAcuity))
-                            , (prefix <> "register reservationState", (replayRegs ! #reservationState) == (forwardRegs ! #reservationState))
-                            ]
+  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False))) of
+    Nothing -> [(prefix <> "forward step accepted", False)]
+    Just (forwardVertex, forwardRegs, emitted) ->
+      case mapM (\event -> parseReservationEvent (eventType reservationCodec event) (encodeReservationEvent event)) emitted of
+        Left _ -> [(prefix <> "emitted chain decodes", False)]
+        Right decodedEvents ->
+          case applyEventsEither reservationTransducer (ReservationUnrequested, initialReservationRegs) decodedEvents of
+            Left _ -> [(prefix <> "replay succeeds", False)]
+            Right (replayVertex, replayRegs) ->
+              [ (prefix <> "final vertex", replayVertex == forwardVertex)
+              , (prefix <> "register reservationId", (replayRegs ! #reservationId) == (forwardRegs ! #reservationId))
+              , (prefix <> "register hospitalId", (replayRegs ! #hospitalId) == (forwardRegs ! #hospitalId))
+              , (prefix <> "register patientAcuity", (replayRegs ! #patientAcuity) == (forwardRegs ! #patientAcuity))
+              ]
   where
     prefix = "forward/replay equality: RequestTransferReservation from ReservationUnrequested -- "
 
 upcastsTransferReservationCreated :: Bool
 upcastsTransferReservationCreated =
-    case eitherDecodeStrict (encodeUtf8 "{\n  \"kind\": \"TransferReservationCreated\",\n  \"reservationId\": \"rsv_01hzy3v7q2e8kaw2m5x0d41n9c\",\n  \"hospitalId\": \"hosp_01hzy3v7q2e8kaw2m5x0d41n9d\",\n  \"commandId\": \"cmd_01hzy3v7q2e8kaw2m5x0d41n9e\",\n  \"patientAcuity\": \"red\",\n  \"divertStatus\": \"open\",\n  \"lifeCriticalOverride\": true\n}\n") of
-        Left _ -> False
-        Right payload ->
-            either
-                (const False)
-                (const True)
-                (decodeRaw reservationCodec (EventType "TransferReservationCreated") 1 payload)
+  either (const False) (const True)
+    (decodeRaw reservationCodec (EventType "TransferReservationCreated") 1 (encodeReservationEvent sampleEventTransferReservationCreated))
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Projection.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Projection.hs
--- a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Projection.hs
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Projection.hs
@@ -1,14 +1,13 @@
 {-# LANGUAGE OverloadedRecordDot #-}
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Reservation.Projection (
-    transfer_decisionsProjection,
-    transfer_decisionsStatusFor,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.Projection
+  ( transfer_decisionsProjection
+  , transfer_decisionsStatusFor
+  ) where
 
-import Data.Text (Text)
 import Generated.HospitalCapacity.Reservation.Domain
 import HospitalCapacity.Reservation.Holes (applyTransfer_decisions)
+import Data.Text (Text)
 import Keiro.Projection (InlineProjection (..))
 
 -- The deterministic event->status mapping (hole-kind 3, /mapping/), derived
@@ -17,12 +16,12 @@
 -- WARNING: no readmodel node declares 'transfer_decisions'; unqualified SQL depends on search_path.
 transfer_decisionsStatusFor :: ReservationEvent -> Maybe Text
 transfer_decisionsStatusFor = \case
-    TransferReservationCreated{} -> Just "held"
-    TransferReservationConfirmed{} -> Just "confirmed"
+  TransferReservationCreated {} -> Just "held"
+  TransferReservationConfirmed {} -> Just "confirmed"
 
 transfer_decisionsProjection :: InlineProjection ReservationEvent
 transfer_decisionsProjection =
-    InlineProjection
-        { name = "hospital-capacity-transfer_decisions-inline"
-        , apply = applyTransfer_decisions
-        }
+  InlineProjection
+    { name = "hospital-capacity-transfer_decisions-inline"
+    , apply = applyTransfer_decisions
+    }
diff --git a/test/conformance-v2/Generated/HospitalCapacity/Reservation/Transducer.hs b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-v2/Generated/HospitalCapacity/Reservation/Transducer.hs
@@ -0,0 +1,72 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.Transducer
+  ( reservationTransducer
+  , reservationFoldFingerprint
+  , BehaviorOwnership (..)
+  , reservationPredicateVerifications
+  ) where
+
+import Generated.HospitalCapacity.Reservation.Domain
+import Data.Text (Text)
+import Generated.HospitalCapacity.Nominals (DivertStatus (..))
+import Generated.HospitalCapacity.Nominals qualified as GeneratedNominals
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer, (.==), (./=), (.||))
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import HospitalCapacity.Reservation.Holes qualified as Holes
+import Keiki.Generics (RegFieldsOf)
+
+reservationTransducer
+  :: SymTransducer
+       (HsPred ReservationRegs ReservationCommand)
+       ReservationRegs
+       ReservationVertex
+       ReservationCommand
+       ReservationEvent
+reservationTransducer =
+  B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
+    B.from ReservationUnrequested do
+      B.onCmd inCtorRequestTransferReservation $ \d -> B.do
+        let commandDivertStatus = K.inpProj GeneratedNominals.divertStatusEqualityWitness inCtorRequestTransferReservation (#divertStatus :: K.Index (RegFieldsOf RequestTransferReservationData) DivertStatus)
+        B.requireGuard $
+          commandDivertStatus ./= K.lit ("total-divert" :: Text)
+          .|| d.lifeCriticalOverride .== K.lit True
+        B.emit wireTransferReservationCreated (Holes.transition1UnrequestedRequestTransferReservationOutput1TransferReservationCreated d)
+        B.goto ReservationHeld
+    B.from ReservationHeld do
+      B.onCmd inCtorConfirmReservation $ \d -> B.do
+        B.emit wireTransferReservationConfirmed (Holes.transition2HeldConfirmReservationOutput1TransferReservationConfirmed d)
+        B.goto ReservationConfirmed
+ where
+  isTerminal = \case
+    ReservationExpired -> True
+    ReservationAdmitted -> True
+    ReservationReleased -> True
+    _ -> False
+
+reservationFoldFingerprint :: Text
+reservationFoldFingerprint = "742bcb162938503b2d6ee4ff05b877ea"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+reservationPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+reservationPredicateVerifications = sequence
+  [ verifyTransition "transition1UnrequestedRequestTransferReservation" GeneratedOwned ReservationUnrequested 0
+  , verifyTransition "transition2HeldConfirmReservation" GeneratedOwned ReservationHeld 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut reservationTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance-v2/HospitalCapacity/Reservation/Holes.hs b/test/conformance-v2/HospitalCapacity/Reservation/Holes.hs
--- a/test/conformance-v2/HospitalCapacity/Reservation/Holes.hs
+++ b/test/conformance-v2/HospitalCapacity/Reservation/Holes.hs
@@ -1,76 +1,49 @@
-{-# LANGUAGE BlockArguments #-}
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- HAND-OWNED hole module, filled by hand for the v2 (evolved) conformance
--- aggregate. The transducer body matches the v1 reference; the v2 event carries
--- a new `triageNote` field sourced as a literal here, and the upcaster defaults
--- `triageNote` on v1-on-disk payloads that lack it.
-module HospitalCapacity.Reservation.Holes (
-    reservationTransducer,
+-- Stable generated code owns the aggregate transducer. This hand-owned module
+-- retains the projection boundary and the historical schema-v1 upcaster.
+module HospitalCapacity.Reservation.Holes
+  ( transition1UnrequestedRequestTransferReservationOutput1TransferReservationCreated,
+    transition2HeldConfirmReservationOutput1TransferReservationConfirmed,
     applyTransfer_decisions,
     upcastTransferReservationCreatedV1,
-) where
+  )
+where
 
 import Data.Aeson (Value (..))
 import Data.Aeson.KeyMap qualified as KM
 import Data.Text (Text)
 import Generated.HospitalCapacity.Reservation.Domain
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, lit, (./=), (.==), (.||))
+import Keiki.Core (lit)
+import Keiki.Generics (RegFieldsOf)
 
-reservationTransducer ::
-    SymTransducer
-        (HsPred ReservationRegs ReservationCommand)
-        ReservationRegs
-        ReservationVertex
-        ReservationCommand
-        ReservationEvent
-reservationTransducer =
-    B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
-        B.from ReservationUnrequested do
-            B.onCmd inCtorRequestTransferReservation $ \d -> B.do
-                B.requireGuard (d.divertStatus ./= lit TotalDivert .|| d.lifeCriticalOverride .== lit True)
-                B.slot @"reservationState" =: lit ReservationHeld
-                B.emit
-                    wireTransferReservationCreated
-                    TransferReservationCreatedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        , patientAcuity = d.patientAcuity
-                        , divertStatus = d.divertStatus
-                        , lifeCriticalOverride = d.lifeCriticalOverride
-                        , triageNote = lit ""
-                        }
-                B.goto ReservationHeld
-        B.from ReservationHeld do
-            B.onCmd inCtorConfirmReservation $ \d -> B.do
-                B.slot @"reservationState" =: lit ReservationConfirmed
-                B.emit
-                    wireTransferReservationConfirmed
-                    TransferReservationConfirmedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        }
-                B.goto ReservationConfirmed
-  where
-    isTerminal = \case
-        ReservationExpired -> True
-        ReservationAdmitted -> True
-        ReservationReleased -> True
-        _ -> False
+transition1UnrequestedRequestTransferReservationOutput1TransferReservationCreated :: B.PayloadProj ReservationRegs ReservationCommand (RegFieldsOf RequestTransferReservationData) -> TransferReservationCreatedTermFields ReservationRegs ReservationCommand (RegFieldsOf RequestTransferReservationData)
+transition1UnrequestedRequestTransferReservationOutput1TransferReservationCreated command =
+  TransferReservationCreatedTermFields
+    { reservationId = command.reservationId,
+      hospitalId = command.hospitalId,
+      commandId = command.commandId,
+      patientAcuity = command.patientAcuity,
+      divertStatus = command.divertStatus,
+      lifeCriticalOverride = command.lifeCriticalOverride,
+      triageNote = lit ""
+    }
 
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed :: B.PayloadProj ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData) -> TransferReservationConfirmedTermFields ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData)
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed command =
+  TransferReservationConfirmedTermFields
+    { reservationId = command.reservationId,
+      hospitalId = command.hospitalId,
+      commandId = command.commandId
+    }
+
 applyTransfer_decisions :: ReservationEvent -> recorded -> txn ()
 applyTransfer_decisions _event _recorded = error "HOLE: fill transfer_decisions projection apply"
 
--- Bring a v1 TransferReservationCreated payload up to v2 by defaulting the new
--- `triageNote` field when it is absent (a v1-on-disk payload lacks it).
 upcastTransferReservationCreatedV1 :: Value -> Either Text Value
-upcastTransferReservationCreatedV1 v = case v of
-    Object o -> Right (Object (KM.insertWith (\_new old -> old) "triageNote" (String "") o))
-    _ -> Left "upcastTransferReservationCreatedV1: expected a JSON object"
+upcastTransferReservationCreatedV1 value = case value of
+  Object fields -> Right (Object (KM.insertWith (\_new old -> old) "triageNote" (String "") fields))
+  _ -> Left "upcastTransferReservationCreatedV1: expected a JSON object"
diff --git a/test/conformance-workflow-full/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs b/test/conformance-workflow-full/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
--- a/test/conformance-workflow-full/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
+++ b/test/conformance-workflow-full/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
@@ -1,13 +1,12 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowRuntime (
-    workflowName,
-    awaitAwakeableId,
-    awaitLabels,
-    declaredPatches,
-    declaredPatchStepNames,
-    withDeclaredPatches,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workflow HospitalTransferReservation; do not edit.
+module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowRuntime
+  ( workflowName
+  , awaitAwakeableId
+  , awaitLabels
+  , declaredPatches
+  , declaredPatchStepNames
+  , withDeclaredPatches
+  ) where
 
 import Data.Set (Set)
 import Data.Set qualified as Set
diff --git a/test/conformance-workflow-runtime/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs b/test/conformance-workflow-runtime/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
--- a/test/conformance-workflow-runtime/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
+++ b/test/conformance-workflow-runtime/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowRuntime.hs
@@ -1,13 +1,12 @@
-
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowRuntime (
-    workflowName,
-    awaitAwakeableId,
-    awaitLabels,
-    declaredPatches,
-    declaredPatchStepNames,
-    withDeclaredPatches,
-) where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workflow HospitalTransferReservation; do not edit.
+module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowRuntime
+  ( workflowName
+  , awaitAwakeableId
+  , awaitLabels
+  , declaredPatches
+  , declaredPatchStepNames
+  , withDeclaredPatches
+  ) where
 
 import Data.Set (Set)
 import Data.Set qualified as Set
diff --git a/test/conformance-workflow/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowFacts.hs b/test/conformance-workflow/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowFacts.hs
--- a/test/conformance-workflow/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowFacts.hs
+++ b/test/conformance-workflow/Generated/HospitalCapacity/HospitalTransferReservation/WorkflowFacts.hs
@@ -1,17 +1,26 @@
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from workflow HospitalTransferReservation; do not edit.
+module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowFacts (WorkflowFacts (..), workflowFacts) where
 
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.HospitalTransferReservation.WorkflowFacts (workflowFacts) where
+-- | The workflow's deterministic decisions, pinned as typed pure facts.
+-- A driver asserts them against a hand-written expectation, so a spec
+-- change (e.g. renaming an await) reddens a specific assertion.
+data WorkflowFacts = WorkflowFacts
+  { workflowFactName :: !String
+  , workflowFactIdVia :: !String
+  , workflowFactIdField :: !String
+  , workflowFactBody :: ![String]
+  , workflowFactAwaitLabels :: ![String]
+  , workflowFactPatchIds :: ![String]
+  }
+  deriving stock (Eq, Show)
 
-{- | (label, value): the workflow's deterministic decisions, pinned as pure
-facts. A driver asserts them against a hand-written expectation, so a spec
-change (e.g. renaming an await) reddens a specific assertion.
--}
-workflowFacts :: [(String, String)]
+workflowFacts :: WorkflowFacts
 workflowFacts =
-    [ ("name", "hospital-transfer-reservation")
-    , ("idVia", "idText")
-    , ("idField", "reservationId")
-    , ("body", "step:create-transfer-hold,patch:fraud-check-v2(step:fraud-check),await:reservation-confirmation,step:release-or-retain-capacity,step:summarize-reservation,continueAsNew:RolloverSeed")
-    , ("awaits", "reservation-confirmation")
-    , ("patches", "fraud-check-v2")
-    ]
+  WorkflowFacts
+    { workflowFactName = "hospital-transfer-reservation"
+    , workflowFactIdVia = "idText"
+    , workflowFactIdField = "reservationId"
+    , workflowFactBody = ["step:create-transfer-hold", "patch:fraud-check-v2(step:fraud-check)", "await:reservation-confirmation", "step:release-or-retain-capacity", "step:summarize-reservation", "continueAsNew:RolloverSeed"]
+    , workflowFactAwaitLabels = ["reservation-confirmation"]
+    , workflowFactPatchIds = ["fraud-check-v2"]
+    }
diff --git a/test/conformance-workflow/Main.hs b/test/conformance-workflow/Main.hs
--- a/test/conformance-workflow/Main.hs
+++ b/test/conformance-workflow/Main.hs
@@ -6,22 +6,37 @@
 module Main (main) where
 
 import Control.Monad (forM_, unless)
-import Generated.HospitalCapacity.HospitalTransferReservation.WorkflowFacts (workflowFacts)
+import Generated.HospitalCapacity.HospitalTransferReservation.WorkflowFacts (WorkflowFacts (..), workflowFacts)
 import System.Exit (exitFailure)
 
-expected :: [(String, String)]
+expected :: WorkflowFacts
 expected =
-    [ ("name", "hospital-transfer-reservation")
-    , ("idVia", "idText")
-    , ("idField", "reservationId")
-    , ("body", "step:create-transfer-hold,patch:fraud-check-v2(step:fraud-check),await:reservation-confirmation,step:release-or-retain-capacity,step:summarize-reservation,continueAsNew:RolloverSeed")
-    , ("awaits", "reservation-confirmation") -- the signal operation's id must match this
-    , ("patches", "fraud-check-v2")
-    ]
+    WorkflowFacts
+        { workflowFactName = "hospital-transfer-reservation"
+        , workflowFactIdVia = "idText"
+        , workflowFactIdField = "reservationId"
+        , workflowFactBody =
+            [ "step:create-transfer-hold"
+            , "patch:fraud-check-v2(step:fraud-check)"
+            , "await:reservation-confirmation"
+            , "step:release-or-retain-capacity"
+            , "step:summarize-reservation"
+            , "continueAsNew:RolloverSeed"
+            ]
+        , workflowFactAwaitLabels = ["reservation-confirmation"]
+        , workflowFactPatchIds = ["fraud-check-v2"]
+        }
 
 main :: IO ()
 main = do
-    let results = [(label, lookup label workflowFacts == Just want) | (label, want) <- expected]
+    let results =
+            [ ("name", workflowFactName workflowFacts == workflowFactName expected)
+            , ("idVia", workflowFactIdVia workflowFacts == workflowFactIdVia expected)
+            , ("idField", workflowFactIdField workflowFacts == workflowFactIdField expected)
+            , ("body", workflowFactBody workflowFacts == workflowFactBody expected)
+            , ("awaits", workflowFactAwaitLabels workflowFacts == workflowFactAwaitLabels expected)
+            , ("patches", workflowFactPatchIds workflowFacts == workflowFactPatchIds expected)
+            ]
     forM_ results $ \(label, ok) -> putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
     let failed = [label | (label, ok) <- results, not ok]
     unless (null failed) $ putStrLn ("workflow facts: failed " <> show failed) >> exitFailure
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals.hs
@@ -4,37 +4,52 @@
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.WorkspaceNominalProof.Nominals where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context workspace-nominal-proof generated nominal declarations; do not edit.
+module Generated.WorkspaceNominalProof.Nominals
+  ( ProjectId
+  , parseProjectId
+  , mkProjectId
+  , projectIdText
+  , ProjectIdEqualityProjection
+  , projectIdEqualityWitness
+  , ProjectPhase (..)
+  , projectPhaseText
+  , ProjectPhaseEqualityProjection
+  , projectPhaseEqualityWitness
+  , UnusedNominal (..)
+  , unusedNominalText
+  ) where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Shape (CanonicalTypeName)
+import Generated.WorkspaceNominalProof.Nominals.Internal (ProjectId, mkProjectId, parseProjectId, projectIdText)
 import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
 import Data.List.NonEmpty (NonEmpty (..))
-import Keiki.ProjectionDomain (finiteProjectionDomain)
-
-newtype ProjectId = ProjectId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 instance CanonicalTypeName ProjectId
 
-projectIdText :: ProjectId -> Text
-projectIdText (ProjectId value) = value
-
 data ProjectIdEqualityProjection
 
 instance FieldProjection ProjectIdEqualityProjection where
   type FieldName ProjectIdEqualityProjection = "ProjectId"
   type FieldOwner ProjectIdEqualityProjection = ProjectId
   type FieldResult ProjectIdEqualityProjection = Text
-  fieldShapeId _ = "nominal-equality|name=ProjectId|contract=keiro-dsl/nominal-equality/1|key=Text|domain=legacy-unrestricted-text|owner=generated"
+  fieldShapeId _ = "nominal-equality|name=ProjectId|contract=keiro-dsl/nominal-equality/2|key=Text|domain=typeid-v7-text:proj:keiro-dsl/id-domain/typeid-v7/1|owner=generated"
   projectFieldValue _ = projectIdText
 
+projectIdIdDomainPattern :: TextPattern
+projectIdIdDomainPattern = either (error . show) id (idDomainTextPattern (typeIdV7Domain "proj"))
+
+instance ExactFieldProjection ProjectIdEqualityProjection where
+  fieldProjectionDomain _ = textProjectionDomain projectIdIdDomainPattern
+  reconstructFieldOwner _ = either (const Nothing) Just . parseProjectId
+
 projectIdEqualityWitness :: FieldWitness ProjectIdEqualityProjection
-projectIdEqualityWitness = fieldWitness @ProjectIdEqualityProjection
+projectIdEqualityWitness = exactFieldWitness @ProjectIdEqualityProjection
 
 data ProjectPhase = Draft | Active
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals/Internal.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Nominals/Internal.hs
@@ -0,0 +1,38 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context workspace-nominal-proof generated nominal ID internals; do not edit.
+module Generated.WorkspaceNominalProof.Nominals.Internal
+  ( ProjectId
+  , parseProjectId
+  , mkProjectId
+  , projectIdText
+  , unsafeProjectIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype ProjectId = ProjectId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON ProjectId where
+  toJSON = toJSON . projectIdText
+
+instance FromJSON ProjectId where
+  parseJSON = withText "ProjectId" (either (fail . T.unpack) pure . parseProjectId)
+
+parseProjectId :: Text -> Either Text ProjectId
+parseProjectId input = case validateIdDomainText (typeIdV7Domain "proj") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (ProjectId input)
+
+mkProjectId :: Text -> Either Text ProjectId
+mkProjectId = parseProjectId
+
+projectIdText :: ProjectId -> Text
+projectIdText (ProjectId value) = value
+
+unsafeProjectIdFromLegacyText :: Text -> ProjectId
+unsafeProjectIdFromLegacyText = ProjectId
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/BehaviorContract.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/BehaviorContract.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/BehaviorContract.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/BehaviorContract.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.BehaviorContract where
 
 import Generated.WorkspaceNominalProof.Project.Codec (encodeProjectEvent, parseProjectEvent, projectCodec)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Codec.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Codec.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Codec.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Codec.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.Codec (
     projectCodec,
     parseProjectEvent,
@@ -8,9 +8,10 @@
 ) where
 
 import Generated.WorkspaceNominalProof.Project.Domain
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), projectIdText, ProjectPhase (..), projectPhaseText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, projectIdText, ProjectPhase (..), projectPhaseText)
+import Generated.WorkspaceNominalProof.Nominals.Internal (unsafeProjectIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -21,7 +22,7 @@
 parseProjectPhase = \case
   "draft" -> pure Draft
   "active" -> pure Active
-  _ -> fail "unknown ProjectPhase"
+  tag -> fail ("unknown ProjectPhase " <> show tag <> "; expected one of: draft, active")
 
 
 projectCodec :: Codec ProjectEvent
@@ -58,10 +59,18 @@
     go o = do
       case tag of
         "ProjectRegistered" ->
-          ProjectRegistered <$> (ProjectRegisteredData <$> (ProjectId <$> o .: "projectId") <*> (o .: "phase" >>= parseProjectPhase))
+          ProjectRegistered
+            <$> ( ProjectRegisteredData
+                    <$> (unsafeProjectIdFromLegacyText <$> o .: "projectId")
+                    <*> explicitParseField (withText "ProjectPhase" parseProjectPhase) o "phase"
+                )
         "ArchivalRecorded" ->
-          ArchivalRecorded <$> (ArchivalRecordedData <$> (ProjectId <$> o .: "projectId") <*> (o .: "phase" >>= parseProjectPhase))
-        _ -> fail "unknown event type"
+          ArchivalRecorded
+            <$> ( ArchivalRecordedData
+                    <$> (unsafeProjectIdFromLegacyText <$> o .: "projectId")
+                    <*> explicitParseField (withText "ProjectPhase" parseProjectPhase) o "phase"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: ProjectRegistered, ArchivalRecorded")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Domain.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Domain.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Domain.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ProjectVertex = ProjectEmpty | ProjectLive | ProjectArchived
@@ -55,7 +54,7 @@
 
 initialProjectRegs :: RegFile ProjectRegs
 initialProjectRegs =
-  RCons (Proxy @"projectId") (ProjectId "") $
+  RCons (Proxy @"projectId") (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
   RCons (Proxy @"phase") Draft RNil
 
 $(deriveAggregateCtorsAll ''ProjectCommand ''ProjectRegs)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/EventStream.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/EventStream.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/EventStream.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.EventStream
   ( projectCategory
+  , projectCommandCategory
   , projectEventStream
   , projectEventStreamDef
   , ProjectEventStream
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-projectCategory :: Stream.StreamCategory a
+projectCategory :: Stream.StreamCategory ProjectEventStreamDef
 projectCategory = Stream.categoryUnsafe "project"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+projectCommandCategory :: Stream.StreamCategory ProjectCommand
+projectCommandCategory = Stream.categoryUnsafe "project"
+
 type ProjectEventStreamDef =
   EventStream (HsPred ProjectRegs ProjectCommand) ProjectRegs ProjectVertex ProjectCommand ProjectEvent
 
@@ -30,13 +35,13 @@
 projectEventStreamDef :: ProjectEventStreamDef
 projectEventStreamDef =
   EventStream
-    { transducer = projectTransducer
-    , initialState = ProjectEmpty
-    , initialRegisters = initialProjectRegs
-    , eventCodec = projectCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = projectTransducer,
+      initialState = ProjectEmpty,
+      initialRegisters = initialProjectRegs,
+      eventCodec = projectCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 projectEventStream :: ProjectEventStream
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Expressions.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Expressions.hs
deleted file mode 100644
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Expressions.hs
+++ /dev/null
@@ -1,31 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.WorkspaceNominalProof.Project.Expressions
-  ( transition1EmptyRegisterProjectGuard
-  , transition1EmptyRegisterProjectWriteProjectId
-  , transition1EmptyRegisterProjectWritePhase
-  , transition2LiveArchiveProjectGuard
-  ) where
-
-import Generated.WorkspaceNominalProof.Project.Domain
-import Keiki.Builder qualified as B
-import Keiki.Core qualified as K
-import Keiki.Generics (RegFieldsOf)
-import Data.Text (Text)
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
-import Generated.WorkspaceNominalProof.Nominals qualified as GeneratedNominals
-
-transition1EmptyRegisterProjectGuard :: B.PayloadProj ProjectRegs ProjectCommand (RegFieldsOf RegisterProjectData) -> K.HsPred ProjectRegs ProjectCommand
-transition1EmptyRegisterProjectGuard d = K.PAnd (K.PEq (K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorRegisterProject (#projectId :: K.Index (RegFieldsOf RegisterProjectData) ProjectId)) (K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectRegs ProjectId))) (K.PEq (K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorRegisterProject (#phase :: K.Index (RegFieldsOf RegisterProjectData) ProjectPhase)) (K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectRegs ProjectPhase)))
-
-transition1EmptyRegisterProjectWriteProjectId :: B.PayloadProj ProjectRegs ProjectCommand (RegFieldsOf RegisterProjectData) -> K.Term ProjectRegs ProjectCommand (RegFieldsOf RegisterProjectData) ProjectId
-transition1EmptyRegisterProjectWriteProjectId d = d.projectId
-
-transition1EmptyRegisterProjectWritePhase :: B.PayloadProj ProjectRegs ProjectCommand (RegFieldsOf RegisterProjectData) -> K.Term ProjectRegs ProjectCommand (RegFieldsOf RegisterProjectData) ProjectPhase
-transition1EmptyRegisterProjectWritePhase d = d.phase
-
-transition2LiveArchiveProjectGuard :: B.PayloadProj ProjectRegs ProjectCommand (RegFieldsOf ArchiveProjectData) -> K.HsPred ProjectRegs ProjectCommand
-transition2LiveArchiveProjectGuard d = K.PAnd (K.PEq (K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorArchiveProject (#projectId :: K.Index (RegFieldsOf ArchiveProjectData) ProjectId)) (K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectRegs ProjectId))) (K.PEq (K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorArchiveProject (#phase :: K.Index (RegFieldsOf ArchiveProjectData) ProjectPhase)) (K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectRegs ProjectPhase)))
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Harness.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Harness.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Harness.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Harness.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.Harness (harnessAssertions) where
 
 import Generated.WorkspaceNominalProof.Project.Domain
@@ -8,12 +8,11 @@
 import Generated.WorkspaceNominalProof.Project.Transducer (projectTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions projectTransducer))
@@ -28,14 +27,14 @@
 roundTrips e = parseProjectEvent (eventType projectCodec e) (encodeProjectEvent e) == Right e
 
 sampleEventProjectRegistered :: ProjectEvent
-sampleEventProjectRegistered = (ProjectRegistered (ProjectRegisteredData (ProjectId "sample") Draft))
+sampleEventProjectRegistered = (ProjectRegistered (ProjectRegisteredData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))
 
 sampleEventArchivalRecorded :: ProjectEvent
-sampleEventArchivalRecorded = (ArchivalRecorded (ArchivalRecordedData (ProjectId "sample") Draft))
+sampleEventArchivalRecorded = (ArchivalRecorded (ArchivalRecordedData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))
 
 acceptRegisterProject :: Bool
 acceptRegisterProject =
-  case step projectTransducer (ProjectEmpty, initialProjectRegs) ((RegisterProject (RegisterProjectData (ProjectId "") Draft))) of
+  case step projectTransducer (ProjectEmpty, initialProjectRegs) ((RegisterProject (RegisterProjectData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))) of
     Just (v, _, _) -> v == ProjectLive
     Nothing -> False
 
@@ -43,7 +42,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRegisterProject :: [(String, Bool)]
 forwardReplayRegisterProject =
-  case step projectTransducer (ProjectEmpty, initialProjectRegs) ((RegisterProject (RegisterProjectData (ProjectId "") Draft))) of
+  case step projectTransducer (ProjectEmpty, initialProjectRegs) ((RegisterProject (RegisterProjectData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseProjectEvent (eventType projectCodec event) (encodeProjectEvent event)) emitted of
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Projection.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Projection.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Projection.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.Projection () where
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Transducer.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Transducer.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Transducer.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/Project/Transducer.hs
@@ -2,9 +2,10 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Project; do not edit.
 module Generated.WorkspaceNominalProof.Project.Transducer
   ( projectTransducer
   , projectFoldFingerprint
@@ -14,12 +15,14 @@
 
 import Generated.WorkspaceNominalProof.Project.Domain
 import Data.Text (Text)
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals qualified as GeneratedNominals
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core (HsPred, SymTransducer, (.==), (.&&))
 import Keiki.Core qualified as K
 import Keiki.Symbolic qualified as S
-import Generated.WorkspaceNominalProof.Project.Expressions qualified as Expressions
 import Keiki.Builder ((=:))
+import Keiki.Generics (RegFieldsOf)
 
 projectTransducer
   :: SymTransducer
@@ -32,9 +35,15 @@
   B.buildTransducer ProjectEmpty initialProjectRegs isTerminal do
     B.from ProjectEmpty do
       B.onCmd inCtorRegisterProject $ \d -> B.do
-        B.requireGuard (Expressions.transition1EmptyRegisterProjectGuard d)
-        B.slot @"projectId" =: Expressions.transition1EmptyRegisterProjectWriteProjectId d
-        B.slot @"phase" =: Expressions.transition1EmptyRegisterProjectWritePhase d
+        let commandProjectId = K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorRegisterProject (#projectId :: K.Index (RegFieldsOf RegisterProjectData) ProjectId)
+            registerProjectId = K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectRegs ProjectId)
+            commandPhase = K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorRegisterProject (#phase :: K.Index (RegFieldsOf RegisterProjectData) ProjectPhase)
+            registerPhase = K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectRegs ProjectPhase)
+        B.requireGuard $
+          commandProjectId .== registerProjectId
+          .&& commandPhase .== registerPhase
+        B.slot @"projectId" =: d.projectId
+        B.slot @"phase" =: d.phase
         B.emit wireProjectRegistered (ProjectRegisteredTermFields
           { projectId = d.projectId
           , phase = d.phase
@@ -42,7 +51,13 @@
         B.goto ProjectLive
     B.from ProjectLive do
       B.onCmd inCtorArchiveProject $ \d -> B.do
-        B.requireGuard (Expressions.transition2LiveArchiveProjectGuard d)
+        let commandProjectId = K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorArchiveProject (#projectId :: K.Index (RegFieldsOf ArchiveProjectData) ProjectId)
+            registerProjectId = K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectRegs ProjectId)
+            commandPhase = K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorArchiveProject (#phase :: K.Index (RegFieldsOf ArchiveProjectData) ProjectPhase)
+            registerPhase = K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectRegs ProjectPhase)
+        B.requireGuard $
+          commandProjectId .== registerProjectId
+          .&& commandPhase .== registerPhase
         B.emit wireArchivalRecorded (ArchivalRecordedTermFields
           { projectId = d.projectId
           , phase = d.phase
@@ -54,7 +69,7 @@
     _ -> False
 
 projectFoldFingerprint :: Text
-projectFoldFingerprint = "6ee6400f21b05845"
+projectFoldFingerprint = "45ade9770d9fd050b296ae227bd65aa2"
 
 data BehaviorOwnership = GeneratedOwned | HoleOwned
   deriving stock (Eq, Show)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/BehaviorContract.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
 {-# OPTIONS_GHC -Wno-missing-signatures -Wno-name-shadowing #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.BehaviorContract where
 
 import Generated.WorkspaceNominalProof.ProjectArtifact.Codec (encodeProjectArtifactEvent, parseProjectArtifactEvent, projectArtifactCodec)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Codec.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.Codec (
     projectArtifactCodec,
     parseProjectArtifactEvent,
@@ -8,9 +8,10 @@
 ) where
 
 import Generated.WorkspaceNominalProof.ProjectArtifact.Domain
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), projectIdText, ProjectPhase (..), projectPhaseText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, projectIdText, ProjectPhase (..), projectPhaseText)
+import Generated.WorkspaceNominalProof.Nominals.Internal (unsafeProjectIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -21,7 +22,7 @@
 parseProjectPhase = \case
   "draft" -> pure Draft
   "active" -> pure Active
-  _ -> fail "unknown ProjectPhase"
+  tag -> fail ("unknown ProjectPhase " <> show tag <> "; expected one of: draft, active")
 
 
 projectArtifactCodec :: Codec ProjectArtifactEvent
@@ -51,8 +52,12 @@
     go o = do
       case tag of
         "ArtifactRecorded" ->
-          ArtifactRecorded <$> (ArtifactRecordedData <$> (ProjectId <$> o .: "projectId") <*> (o .: "phase" >>= parseProjectPhase))
-        _ -> fail "unknown event type"
+          ArtifactRecorded
+            <$> ( ArtifactRecordedData
+                    <$> (unsafeProjectIdFromLegacyText <$> o .: "projectId")
+                    <*> explicitParseField (withText "ProjectPhase" parseProjectPhase) o "phase"
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: ArtifactRecorded")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ProjectArtifactVertex = ProjectArtifactEmpty | ProjectArtifactRecorded
@@ -41,7 +40,7 @@
 
 initialProjectArtifactRegs :: RegFile ProjectArtifactRegs
 initialProjectArtifactRegs =
-  RCons (Proxy @"projectId") (ProjectId "") $
+  RCons (Proxy @"projectId") (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
   RCons (Proxy @"phase") Draft RNil
 
 $(deriveAggregateCtorsAll ''ProjectArtifactCommand ''ProjectArtifactRegs)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.EventStream
   ( projectArtifactCategory
+  , projectArtifactCommandCategory
   , projectArtifactEventStream
   , projectArtifactEventStreamDef
   , ProjectArtifactEventStream
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-projectArtifactCategory :: Stream.StreamCategory a
+projectArtifactCategory :: Stream.StreamCategory ProjectArtifactEventStreamDef
 projectArtifactCategory = Stream.categoryUnsafe "projectArtifact"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+projectArtifactCommandCategory :: Stream.StreamCategory ProjectArtifactCommand
+projectArtifactCommandCategory = Stream.categoryUnsafe "projectArtifact"
+
 type ProjectArtifactEventStreamDef =
   EventStream (HsPred ProjectArtifactRegs ProjectArtifactCommand) ProjectArtifactRegs ProjectArtifactVertex ProjectArtifactCommand ProjectArtifactEvent
 
@@ -30,13 +35,13 @@
 projectArtifactEventStreamDef :: ProjectArtifactEventStreamDef
 projectArtifactEventStreamDef =
   EventStream
-    { transducer = projectArtifactTransducer
-    , initialState = ProjectArtifactEmpty
-    , initialRegisters = initialProjectArtifactRegs
-    , eventCodec = projectArtifactCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = projectArtifactTransducer,
+      initialState = ProjectArtifactEmpty,
+      initialRegisters = initialProjectArtifactRegs,
+      eventCodec = projectArtifactCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 projectArtifactEventStream :: ProjectArtifactEventStream
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Expressions.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Expressions.hs
deleted file mode 100644
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Expressions.hs
+++ /dev/null
@@ -1,27 +0,0 @@
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
-{-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.WorkspaceNominalProof.ProjectArtifact.Expressions
-  ( transition1EmptyRecordArtifactGuard
-  , transition1EmptyRecordArtifactWriteProjectId
-  , transition1EmptyRecordArtifactWritePhase
-  ) where
-
-import Generated.WorkspaceNominalProof.ProjectArtifact.Domain
-import Keiki.Builder qualified as B
-import Keiki.Core qualified as K
-import Keiki.Generics (RegFieldsOf)
-import Data.Text (Text)
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
-import Generated.WorkspaceNominalProof.Nominals qualified as GeneratedNominals
-
-transition1EmptyRecordArtifactGuard :: B.PayloadProj ProjectArtifactRegs ProjectArtifactCommand (RegFieldsOf RecordArtifactData) -> K.HsPred ProjectArtifactRegs ProjectArtifactCommand
-transition1EmptyRecordArtifactGuard d = K.PAnd (K.PEq (K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorRecordArtifact (#projectId :: K.Index (RegFieldsOf RecordArtifactData) ProjectId)) (K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectArtifactRegs ProjectId))) (K.PEq (K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorRecordArtifact (#phase :: K.Index (RegFieldsOf RecordArtifactData) ProjectPhase)) (K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectArtifactRegs ProjectPhase)))
-
-transition1EmptyRecordArtifactWriteProjectId :: B.PayloadProj ProjectArtifactRegs ProjectArtifactCommand (RegFieldsOf RecordArtifactData) -> K.Term ProjectArtifactRegs ProjectArtifactCommand (RegFieldsOf RecordArtifactData) ProjectId
-transition1EmptyRecordArtifactWriteProjectId d = d.projectId
-
-transition1EmptyRecordArtifactWritePhase :: B.PayloadProj ProjectArtifactRegs ProjectArtifactCommand (RegFieldsOf RecordArtifactData) -> K.Term ProjectArtifactRegs ProjectArtifactCommand (RegFieldsOf RecordArtifactData) ProjectPhase
-transition1EmptyRecordArtifactWritePhase d = d.phase
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Harness.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.Harness (harnessAssertions) where
 
 import Generated.WorkspaceNominalProof.ProjectArtifact.Domain
@@ -8,12 +8,11 @@
 import Generated.WorkspaceNominalProof.ProjectArtifact.Transducer (projectArtifactTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions projectArtifactTransducer))
@@ -27,11 +26,11 @@
 roundTrips e = parseProjectArtifactEvent (eventType projectArtifactCodec e) (encodeProjectArtifactEvent e) == Right e
 
 sampleEventArtifactRecorded :: ProjectArtifactEvent
-sampleEventArtifactRecorded = (ArtifactRecorded (ArtifactRecordedData (ProjectId "sample") Draft))
+sampleEventArtifactRecorded = (ArtifactRecorded (ArtifactRecordedData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))
 
 acceptRecordArtifact :: Bool
 acceptRecordArtifact =
-  case step projectArtifactTransducer (ProjectArtifactEmpty, initialProjectArtifactRegs) ((RecordArtifact (RecordArtifactData (ProjectId "") Draft))) of
+  case step projectArtifactTransducer (ProjectArtifactEmpty, initialProjectArtifactRegs) ((RecordArtifact (RecordArtifactData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))) of
     Just (v, _, _) -> v == ProjectArtifactRecorded
     Nothing -> False
 
@@ -39,7 +38,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRecordArtifact :: [(String, Bool)]
 forwardReplayRecordArtifact =
-  case step projectArtifactTransducer (ProjectArtifactEmpty, initialProjectArtifactRegs) ((RecordArtifact (RecordArtifactData (ProjectId "") Draft))) of
+  case step projectArtifactTransducer (ProjectArtifactEmpty, initialProjectArtifactRegs) ((RecordArtifact (RecordArtifactData (case parseProjectId "proj_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") Draft))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseProjectArtifactEvent (eventType projectArtifactCodec event) (encodeProjectArtifactEvent event)) emitted of
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Projection.hs
@@ -1,2 +1,2 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.Projection () where
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ProjectArtifact/Transducer.hs
@@ -2,9 +2,10 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE GADTs #-}
 {-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
 {-# LANGUAGE QualifiedDo #-}
 {-# LANGUAGE TypeApplications #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate ProjectArtifact; do not edit.
 module Generated.WorkspaceNominalProof.ProjectArtifact.Transducer
   ( projectArtifactTransducer
   , projectArtifactFoldFingerprint
@@ -14,12 +15,14 @@
 
 import Generated.WorkspaceNominalProof.ProjectArtifact.Domain
 import Data.Text (Text)
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, parseProjectId, ProjectPhase (..))
+import Generated.WorkspaceNominalProof.Nominals qualified as GeneratedNominals
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer)
+import Keiki.Core (HsPred, SymTransducer, (.==), (.&&))
 import Keiki.Core qualified as K
 import Keiki.Symbolic qualified as S
-import Generated.WorkspaceNominalProof.ProjectArtifact.Expressions qualified as Expressions
 import Keiki.Builder ((=:))
+import Keiki.Generics (RegFieldsOf)
 
 projectArtifactTransducer
   :: SymTransducer
@@ -32,9 +35,15 @@
   B.buildTransducer ProjectArtifactEmpty initialProjectArtifactRegs isTerminal do
     B.from ProjectArtifactEmpty do
       B.onCmd inCtorRecordArtifact $ \d -> B.do
-        B.requireGuard (Expressions.transition1EmptyRecordArtifactGuard d)
-        B.slot @"projectId" =: Expressions.transition1EmptyRecordArtifactWriteProjectId d
-        B.slot @"phase" =: Expressions.transition1EmptyRecordArtifactWritePhase d
+        let commandProjectId = K.inpProj GeneratedNominals.projectIdEqualityWitness inCtorRecordArtifact (#projectId :: K.Index (RegFieldsOf RecordArtifactData) ProjectId)
+            registerProjectId = K.regProj GeneratedNominals.projectIdEqualityWitness (#projectId :: K.Index ProjectArtifactRegs ProjectId)
+            commandPhase = K.inpProj GeneratedNominals.projectPhaseEqualityWitness inCtorRecordArtifact (#phase :: K.Index (RegFieldsOf RecordArtifactData) ProjectPhase)
+            registerPhase = K.regProj GeneratedNominals.projectPhaseEqualityWitness (#phase :: K.Index ProjectArtifactRegs ProjectPhase)
+        B.requireGuard $
+          commandProjectId .== registerProjectId
+          .&& commandPhase .== registerPhase
+        B.slot @"projectId" =: d.projectId
+        B.slot @"phase" =: d.phase
         B.emit wireArtifactRecorded (ArtifactRecordedTermFields
           { projectId = d.projectId
           , phase = d.phase
@@ -46,7 +55,7 @@
     _ -> False
 
 projectArtifactFoldFingerprint :: Text
-projectArtifactFoldFingerprint = "f9264cc50ea1c28f"
+projectArtifactFoldFingerprint = "a437868e9773ccac1561d3cdef96dbde"
 
 data BehaviorOwnership = GeneratedOwned | HoleOwned
   deriving stock (Eq, Show)
diff --git a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ReplayAudit.hs b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ReplayAudit.hs
--- a/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ReplayAudit.hs
+++ b/test/conformance-workspace-nominals/Generated/WorkspaceNominalProof/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context workspace-nominal-proof replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance-workspace-nominals/Main.hs b/test/conformance-workspace-nominals/Main.hs
--- a/test/conformance-workspace-nominals/Main.hs
+++ b/test/conformance-workspace-nominals/Main.hs
@@ -1,9 +1,9 @@
 {-# LANGUAGE DataKinds #-}
-{-# LANGUAGE OverloadedLabels #-}
 
 module Main (main) where
 
-import Generated.WorkspaceNominalProof.Nominals (ProjectId (..), ProjectPhase (..))
+import Data.Text qualified as T
+import Generated.WorkspaceNominalProof.Nominals (ProjectId, ProjectPhase (..), parseProjectId)
 import Generated.WorkspaceNominalProof.Project.Codec qualified as ProjectCodec
 import Generated.WorkspaceNominalProof.Project.Domain qualified as Project
 import Generated.WorkspaceNominalProof.Project.Harness qualified as ProjectHarness
@@ -22,7 +22,7 @@
     else fail "workspace nominal conformance failed"
 
 projectPayload :: Project.ProjectRegisteredData
-projectPayload = Project.ProjectRegisteredData (ProjectId "proj_shared") Active
+projectPayload = Project.ProjectRegisteredData projectIdValue Active
 
 artifactPayload :: Artifact.ArtifactRecordedData
 artifactPayload = toArtifactPayload projectPayload
@@ -35,7 +35,7 @@
 sharedNominalIdentity =
   case artifactPayload of
     Artifact.ArtifactRecordedData projectId phase ->
-      projectId == ProjectId "proj_shared" && phase == Active
+      projectId == projectIdValue && phase == Active
 
 projectRoundTrip :: Bool
 projectRoundTrip =
@@ -56,11 +56,11 @@
 generatedFleetAgreement :: Bool
 generatedFleetAgreement = projectRing && artifactRing
   where
-    matchingId = ProjectId ""
+    matchingId = projectIdValue
     matchingPhase = Draft
     register = Project.RegisterProject (Project.RegisterProjectData matchingId matchingPhase)
     archive = Project.ArchiveProject (Project.ArchiveProjectData matchingId matchingPhase)
-    mismatchedArchive = Project.ArchiveProject (Project.ArchiveProjectData (ProjectId "different") matchingPhase)
+    mismatchedArchive = Project.ArchiveProject (Project.ArchiveProjectData otherProjectIdValue matchingPhase)
     projectRing = case K.step projectTransducer (Project.ProjectEmpty, Project.initialProjectRegs) register of
       Nothing -> False
       Just (live, registers, _) ->
@@ -70,7 +70,7 @@
             Just (archived, _, _) -> archived == Project.ProjectArchived
             Nothing -> False
     artifactCommand = Artifact.RecordArtifact (Artifact.RecordArtifactData matchingId matchingPhase)
-    artifactMismatch = Artifact.RecordArtifact (Artifact.RecordArtifactData (ProjectId "different") matchingPhase)
+    artifactMismatch = Artifact.RecordArtifact (Artifact.RecordArtifactData otherProjectIdValue matchingPhase)
     artifactRing =
       case K.step projectArtifactTransducer (Artifact.ProjectArtifactEmpty, Artifact.initialProjectArtifactRegs) artifactCommand of
         Just (recorded, _, _) ->
@@ -79,3 +79,15 @@
         Nothing -> False
     rejects Nothing = True
     rejects Just {} = False
+
+projectIdValue :: ProjectId
+projectIdValue = checkedProjectId "proj_01h455vb4pex5vsknk084sn02q"
+
+otherProjectIdValue :: ProjectId
+otherProjectIdValue = checkedProjectId "proj_01h455vb4pex5vsknk084sn02r"
+
+checkedProjectId :: String -> ProjectId
+checkedProjectId raw =
+  case parseProjectId (T.pack raw) of
+    Right parsed -> parsed
+    Left problem -> error (show problem)
diff --git a/test/conformance/Generated/HospitalCapacity/Nominals.hs b/test/conformance/Generated/HospitalCapacity/Nominals.hs
--- a/test/conformance/Generated/HospitalCapacity/Nominals.hs
+++ b/test/conformance/Generated/HospitalCapacity/Nominals.hs
@@ -1,13 +1,42 @@
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE TypeApplications #-}
+{-# LANGUAGE TypeFamilies #-}
 {-# LANGUAGE DeriveAnyClass #-}
 {-# LANGUAGE DeriveGeneric #-}
 {-# LANGUAGE LambdaCase #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
-module Generated.HospitalCapacity.Nominals where
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal declarations; do not edit.
+module Generated.HospitalCapacity.Nominals
+  ( BedType (..)
+  , bedTypeText
+  , CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , DivertStatus (..)
+  , divertStatusText
+  , DivertStatusEqualityProjection
+  , divertStatusEqualityWitness
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , PatientAcuity (..)
+  , patientAcuityText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  ) where
 
 import Data.Aeson (FromJSON, ToJSON)
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Shape (CanonicalTypeName)
+import Generated.HospitalCapacity.Nominals.Internal (CommandId, mkCommandId, parseCommandId, commandIdText, HospitalId, mkHospitalId, parseHospitalId, hospitalIdText, TransferReservationId, mkTransferReservationId, parseTransferReservationId, transferReservationIdText)
+import Keiki.Core (ExactFieldProjection (..), FieldProjection (..), FieldWitness, exactFieldWitness, fieldWitness)
+import Data.List.NonEmpty (NonEmpty (..))
+import Keiki.ProjectionDomain (TextPattern, finiteProjectionDomain, textProjectionDomain)
+import Keiro.Codec.IdDomain (idDomainTextPattern, typeIdV7Domain)
 
 data BedType = Icu | MedicalSurgical
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
@@ -20,15 +49,8 @@
   Icu -> "icu"
   MedicalSurgical -> "medical-surgical"
 
-newtype CommandId = CommandId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
-
 instance CanonicalTypeName CommandId
 
-commandIdText :: CommandId -> Text
-commandIdText (CommandId value) = value
-
 data DivertStatus = Open | PartialDivert | TotalDivert
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
   deriving anyclass (ToJSON, FromJSON)
@@ -41,15 +63,28 @@
   PartialDivert -> "partial-divert"
   TotalDivert -> "total-divert"
 
-newtype HospitalId = HospitalId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
+data DivertStatusEqualityProjection
 
-instance CanonicalTypeName HospitalId
+instance FieldProjection DivertStatusEqualityProjection where
+  type FieldName DivertStatusEqualityProjection = "DivertStatus"
+  type FieldOwner DivertStatusEqualityProjection = DivertStatus
+  type FieldResult DivertStatusEqualityProjection = Text
+  fieldShapeId _ = "nominal-equality|name=DivertStatus|contract=keiro-dsl/nominal-equality/1|key=Text|domain=finite-text:open,partial-divert,total-divert|owner=generated"
+  projectFieldValue _ = divertStatusText
 
-hospitalIdText :: HospitalId -> Text
-hospitalIdText (HospitalId value) = value
+instance ExactFieldProjection DivertStatusEqualityProjection where
+  fieldProjectionDomain _ = finiteProjectionDomain ("open" :| ["partial-divert", "total-divert"])
+  reconstructFieldOwner _ = \case
+    "open" -> Just Open
+    "partial-divert" -> Just PartialDivert
+    "total-divert" -> Just TotalDivert
+    _ -> Nothing
 
+divertStatusEqualityWitness :: FieldWitness DivertStatusEqualityProjection
+divertStatusEqualityWitness = exactFieldWitness @DivertStatusEqualityProjection
+
+instance CanonicalTypeName HospitalId
+
 data PatientAcuity = RedTag | YellowTag | GreenTag
   deriving stock (Generic, Eq, Ord, Show, Enum, Bounded)
   deriving anyclass (ToJSON, FromJSON)
@@ -62,11 +97,4 @@
   YellowTag -> "yellow"
   GreenTag -> "green"
 
-newtype TransferReservationId = TransferReservationId Text
-  deriving stock (Generic, Eq, Ord, Show)
-  deriving anyclass (ToJSON, FromJSON)
-
 instance CanonicalTypeName TransferReservationId
-
-transferReservationIdText :: TransferReservationId -> Text
-transferReservationIdText (TransferReservationId value) = value
diff --git a/test/conformance/Generated/HospitalCapacity/Nominals/Internal.hs b/test/conformance/Generated/HospitalCapacity/Nominals/Internal.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance/Generated/HospitalCapacity/Nominals/Internal.hs
@@ -0,0 +1,94 @@
+{-# LANGUAGE DeriveGeneric #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity generated nominal ID internals; do not edit.
+module Generated.HospitalCapacity.Nominals.Internal
+  ( CommandId
+  , parseCommandId
+  , mkCommandId
+  , commandIdText
+  , unsafeCommandIdFromLegacyText
+  , HospitalId
+  , parseHospitalId
+  , mkHospitalId
+  , hospitalIdText
+  , unsafeHospitalIdFromLegacyText
+  , TransferReservationId
+  , parseTransferReservationId
+  , mkTransferReservationId
+  , transferReservationIdText
+  , unsafeTransferReservationIdFromLegacyText
+  ) where
+
+import Data.Aeson (FromJSON (..), ToJSON (..), withText)
+import Data.Text (Text)
+import Data.Text qualified as T
+import GHC.Generics (Generic)
+import Keiro.Codec.IdDomain (typeIdV7Domain, validateIdDomainText)
+
+newtype CommandId = CommandId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON CommandId where
+  toJSON = toJSON . commandIdText
+
+instance FromJSON CommandId where
+  parseJSON = withText "CommandId" (either (fail . T.unpack) pure . parseCommandId)
+
+parseCommandId :: Text -> Either Text CommandId
+parseCommandId input = case validateIdDomainText (typeIdV7Domain "cmd") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (CommandId input)
+
+mkCommandId :: Text -> Either Text CommandId
+mkCommandId = parseCommandId
+
+commandIdText :: CommandId -> Text
+commandIdText (CommandId value) = value
+
+unsafeCommandIdFromLegacyText :: Text -> CommandId
+unsafeCommandIdFromLegacyText = CommandId
+
+newtype HospitalId = HospitalId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON HospitalId where
+  toJSON = toJSON . hospitalIdText
+
+instance FromJSON HospitalId where
+  parseJSON = withText "HospitalId" (either (fail . T.unpack) pure . parseHospitalId)
+
+parseHospitalId :: Text -> Either Text HospitalId
+parseHospitalId input = case validateIdDomainText (typeIdV7Domain "hosp") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (HospitalId input)
+
+mkHospitalId :: Text -> Either Text HospitalId
+mkHospitalId = parseHospitalId
+
+hospitalIdText :: HospitalId -> Text
+hospitalIdText (HospitalId value) = value
+
+unsafeHospitalIdFromLegacyText :: Text -> HospitalId
+unsafeHospitalIdFromLegacyText = HospitalId
+
+newtype TransferReservationId = TransferReservationId Text
+  deriving stock (Generic, Eq, Ord, Show)
+
+instance ToJSON TransferReservationId where
+  toJSON = toJSON . transferReservationIdText
+
+instance FromJSON TransferReservationId where
+  parseJSON = withText "TransferReservationId" (either (fail . T.unpack) pure . parseTransferReservationId)
+
+parseTransferReservationId :: Text -> Either Text TransferReservationId
+parseTransferReservationId input = case validateIdDomainText (typeIdV7Domain "rsv") input of
+  Left reason -> Left (T.pack (show reason))
+  Right () -> Right (TransferReservationId input)
+
+mkTransferReservationId :: Text -> Either Text TransferReservationId
+mkTransferReservationId = parseTransferReservationId
+
+transferReservationIdText :: TransferReservationId -> Text
+transferReservationIdText (TransferReservationId value) = value
+
+unsafeTransferReservationIdFromLegacyText :: Text -> TransferReservationId
+unsafeTransferReservationIdFromLegacyText = TransferReservationId
diff --git a/test/conformance/Generated/HospitalCapacity/ReplayAudit.hs b/test/conformance/Generated/HospitalCapacity/ReplayAudit.hs
--- a/test/conformance/Generated/HospitalCapacity/ReplayAudit.hs
+++ b/test/conformance/Generated/HospitalCapacity/ReplayAudit.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE GADTs #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from context hospital-capacity replay-audit assembly; do not edit.
 --
 -- Deployment contract:
 --   * replay-neutral diff: no data audit is required;
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/Codec.hs b/test/conformance/Generated/HospitalCapacity/Reservation/Codec.hs
--- a/test/conformance/Generated/HospitalCapacity/Reservation/Codec.hs
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/Codec.hs
@@ -1,6 +1,6 @@
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Codec (
     reservationCodec,
     parseReservationEvent,
@@ -8,9 +8,10 @@
 ) where
 
 import Generated.HospitalCapacity.Reservation.Domain
-import Generated.HospitalCapacity.Nominals (CommandId (..), commandIdText, DivertStatus (..), divertStatusText, HospitalId (..), hospitalIdText, PatientAcuity (..), patientAcuityText, TransferReservationId (..), transferReservationIdText)
-import Data.Aeson (Value, object, withObject, (.:), (.=))
-import Data.Aeson.Types (Parser, parseEither)
+import Generated.HospitalCapacity.Nominals (CommandId, commandIdText, DivertStatus (..), divertStatusText, HospitalId, hospitalIdText, PatientAcuity (..), patientAcuityText, TransferReservationId, transferReservationIdText)
+import Generated.HospitalCapacity.Nominals.Internal (unsafeCommandIdFromLegacyText, unsafeHospitalIdFromLegacyText, unsafeTransferReservationIdFromLegacyText)
+import Data.Aeson (Value, object, withObject, withText, (.:), (.=))
+import Data.Aeson.Types (Parser, explicitParseField, parseEither)
 import Data.List.NonEmpty (NonEmpty (..))
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -22,14 +23,14 @@
   "open" -> pure Open
   "partial-divert" -> pure PartialDivert
   "total-divert" -> pure TotalDivert
-  _ -> fail "unknown DivertStatus"
+  tag -> fail ("unknown DivertStatus " <> show tag <> "; expected one of: open, partial-divert, total-divert")
 
 parsePatientAcuity :: Text -> Parser PatientAcuity
 parsePatientAcuity = \case
   "red" -> pure RedTag
   "yellow" -> pure YellowTag
   "green" -> pure GreenTag
-  _ -> fail "unknown PatientAcuity"
+  tag -> fail ("unknown PatientAcuity " <> show tag <> "; expected one of: red, yellow, green")
 
 
 reservationCodec :: Codec ReservationEvent
@@ -71,10 +72,23 @@
     go o = do
       case tag of
         "TransferReservationCreated" ->
-          TransferReservationCreated <$> (TransferReservationCreatedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId") <*> (o .: "patientAcuity" >>= parsePatientAcuity) <*> (o .: "divertStatus" >>= parseDivertStatus) <*> o .: "lifeCriticalOverride")
+          TransferReservationCreated
+            <$> ( TransferReservationCreatedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                    <*> explicitParseField (withText "PatientAcuity" parsePatientAcuity) o "patientAcuity"
+                    <*> explicitParseField (withText "DivertStatus" parseDivertStatus) o "divertStatus"
+                    <*> o .: "lifeCriticalOverride"
+                )
         "TransferReservationConfirmed" ->
-          TransferReservationConfirmed <$> (TransferReservationConfirmedData <$> (TransferReservationId <$> o .: "reservationId") <*> (HospitalId <$> o .: "hospitalId") <*> (CommandId <$> o .: "commandId"))
-        _ -> fail "unknown event type"
+          TransferReservationConfirmed
+            <$> ( TransferReservationConfirmedData
+                    <$> (unsafeTransferReservationIdFromLegacyText <$> o .: "reservationId")
+                    <*> (unsafeHospitalIdFromLegacyText <$> o .: "hospitalId")
+                    <*> (unsafeCommandIdFromLegacyText <$> o .: "commandId")
+                )
+        _ -> fail ("unknown event type " <> show tag <> "; expected one of: TransferReservationCreated, TransferReservationConfirmed")
 
 mapLeftText :: Either String b -> Either Text b
 mapLeftText = either (Left . T.pack) Right
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/Domain.hs b/test/conformance/Generated/HospitalCapacity/Reservation/Domain.hs
--- a/test/conformance/Generated/HospitalCapacity/Reservation/Domain.hs
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/Domain.hs
@@ -2,15 +2,14 @@
 {-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE TemplateHaskell #-}
 {-# LANGUAGE TypeApplications #-}
-{-# OPTIONS_GHC -Wno-unused-top-binds #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Domain where
 
 import Data.Proxy (Proxy (..))
 import Data.Text (Text)
 import GHC.Generics (Generic)
 import Keiki.Core (RegFile (..))
-import Generated.HospitalCapacity.Nominals (CommandId (..), DivertStatus (..), HospitalId (..), PatientAcuity (..), TransferReservationId (..))
+import Generated.HospitalCapacity.Nominals (CommandId, parseCommandId, DivertStatus (..), HospitalId, parseHospitalId, PatientAcuity (..), TransferReservationId, parseTransferReservationId)
 import Keiki.Generics.TH (deriveAggregateCtorsAll, deriveWireCtorsAll)
 
 data ReservationVertex = ReservationUnrequested | ReservationHeld | ReservationConfirmed | ReservationExpired | ReservationAdmitted | ReservationReleased
@@ -62,15 +61,13 @@
   '[ '("reservationId", TransferReservationId)
    , '("hospitalId", HospitalId)
    , '("patientAcuity", PatientAcuity)
-   , '("reservationState", ReservationVertex)
    ]
 
 initialReservationRegs :: RegFile ReservationRegs
 initialReservationRegs =
-  RCons (Proxy @"reservationId") (TransferReservationId "") $
-  RCons (Proxy @"hospitalId") (HospitalId "") $
-  RCons (Proxy @"patientAcuity") GreenTag $
-  RCons (Proxy @"reservationState") ReservationUnrequested RNil
+  RCons (Proxy @"reservationId") (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"hospitalId") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") $
+  RCons (Proxy @"patientAcuity") GreenTag RNil
 
 $(deriveAggregateCtorsAll ''ReservationCommand ''ReservationRegs)
 
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/EventStream.hs b/test/conformance/Generated/HospitalCapacity/Reservation/EventStream.hs
--- a/test/conformance/Generated/HospitalCapacity/Reservation/EventStream.hs
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/EventStream.hs
@@ -1,6 +1,7 @@
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.EventStream
   ( reservationCategory
+  , reservationCommandCategory
   , reservationEventStream
   , reservationEventStreamDef
   , ReservationEventStream
@@ -9,7 +10,7 @@
 
 import Generated.HospitalCapacity.Reservation.Domain
 import Generated.HospitalCapacity.Reservation.Codec (reservationCodec)
-import HospitalCapacity.Reservation.Holes (reservationTransducer)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationFoldFingerprint, reservationTransducer)
 import Keiki.Core (HsPred)
 import Keiro.EventStream (EventStream (..), SnapshotPolicy (..))
 import Keiro.EventStream.Validate (ValidatedEventStream, mkEventStreamOrThrow)
@@ -18,9 +19,13 @@
 -- The validated aggregate stream category (hole-kind 5: referenced, never retyped).
 -- Entity streams are '<category>-<id>' via Keiro.Stream.entityStream.
 -- categoryUnsafe is safe here because this generated literal passed the DSL category proof.
-reservationCategory :: Stream.StreamCategory a
+reservationCategory :: Stream.StreamCategory ReservationEventStreamDef
 reservationCategory = Stream.categoryUnsafe "reservation"
 
+-- The same category text, typed for command envelopes such as PMCommand.
+reservationCommandCategory :: Stream.StreamCategory ReservationCommand
+reservationCommandCategory = Stream.categoryUnsafe "reservation"
+
 type ReservationEventStreamDef =
   EventStream (HsPred ReservationRegs ReservationCommand) ReservationRegs ReservationVertex ReservationCommand ReservationEvent
 
@@ -30,13 +35,13 @@
 reservationEventStreamDef :: ReservationEventStreamDef
 reservationEventStreamDef =
   EventStream
-    { transducer = reservationTransducer
-    , initialState = ReservationUnrequested
-    , initialRegisters = initialReservationRegs
-    , eventCodec = reservationCodec
-    , resolveStreamName = Stream.streamName
-    , snapshotPolicy = Never
-    , stateCodec = Nothing
+    { transducer = reservationTransducer,
+      initialState = ReservationUnrequested,
+      initialRegisters = initialReservationRegs,
+      eventCodec = reservationCodec,
+      resolveStreamName = Stream.streamName,
+      snapshotPolicy = Never,
+      stateCodec = Nothing
     }
 
 reservationEventStream :: ReservationEventStream
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/Harness.hs b/test/conformance/Generated/HospitalCapacity/Reservation/Harness.hs
--- a/test/conformance/Generated/HospitalCapacity/Reservation/Harness.hs
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/Harness.hs
@@ -1,19 +1,18 @@
 {-# LANGUAGE DataKinds #-}
 {-# LANGUAGE OverloadedLabels #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Harness (harnessAssertions) where
 
 import Generated.HospitalCapacity.Reservation.Domain
 import Generated.HospitalCapacity.Reservation.Codec (encodeReservationEvent, parseReservationEvent, reservationCodec)
-import HospitalCapacity.Reservation.Holes (reservationTransducer)
+import Generated.HospitalCapacity.Reservation.Transducer (reservationTransducer)
 import Keiki.Core (applyEventsEither, defaultValidationOptions, step, validateTransducer, (!))
 import Keiro.Codec (eventType)
-import Generated.HospitalCapacity.Nominals (CommandId (..), DivertStatus (..), HospitalId (..), PatientAcuity (..), TransferReservationId (..))
+import Generated.HospitalCapacity.Nominals (CommandId, parseCommandId, DivertStatus (..), HospitalId, parseHospitalId, PatientAcuity (..), TransferReservationId, parseTransferReservationId)
 
-{- | (label, passed). A driver runs these and exits non-zero on any False,
-naming the failing assertion. Filling a hole wrongly turns a specific
-entry False; the scaffold cannot.
--}
+-- | (label, passed). A driver runs these and exits non-zero on any False,
+-- naming the failing assertion. Filling a hole wrongly turns a specific
+-- entry False; the scaffold cannot.
 harnessAssertions :: [(String, Bool)]
 harnessAssertions =
   [ ("validateTransducer is empty", null (validateTransducer defaultValidationOptions reservationTransducer))
@@ -28,14 +27,14 @@
 roundTrips e = parseReservationEvent (eventType reservationCodec e) (encodeReservationEvent e) == Right e
 
 sampleEventTransferReservationCreated :: ReservationEvent
-sampleEventTransferReservationCreated = (TransferReservationCreated (TransferReservationCreatedData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False))
+sampleEventTransferReservationCreated = (TransferReservationCreated (TransferReservationCreatedData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False))
 
 sampleEventTransferReservationConfirmed :: ReservationEvent
-sampleEventTransferReservationConfirmed = (TransferReservationConfirmed (TransferReservationConfirmedData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample")))
+sampleEventTransferReservationConfirmed = (TransferReservationConfirmed (TransferReservationConfirmedData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse")))
 
 acceptRequestTransferReservation :: Bool
 acceptRequestTransferReservation =
-  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False))) of
+  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False))) of
     Just (v, _, _) -> v == ReservationHeld
     Nothing -> False
 
@@ -43,7 +42,7 @@
 -- replay the emitted chain, and compare the final vertex and every register.
 forwardReplayRequestTransferReservation :: [(String, Bool)]
 forwardReplayRequestTransferReservation =
-  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (TransferReservationId "sample") (HospitalId "sample") (CommandId "sample") RedTag Open False))) of
+  case step reservationTransducer (ReservationUnrequested, initialReservationRegs) ((RequestTransferReservation (RequestTransferReservationData (case parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") (case parseCommandId "cmd_01h455vb4pex5vsknk084sn02q" of Right parsed -> parsed; Left _ -> error "generated valid ID sample failed to parse") RedTag Open False))) of
     Nothing -> [(prefix <> "forward step accepted", False)]
     Just (forwardVertex, forwardRegs, emitted) ->
       case mapM (\event -> parseReservationEvent (eventType reservationCodec event) (encodeReservationEvent event)) emitted of
@@ -56,7 +55,6 @@
               , (prefix <> "register reservationId", (replayRegs ! #reservationId) == (forwardRegs ! #reservationId))
               , (prefix <> "register hospitalId", (replayRegs ! #hospitalId) == (forwardRegs ! #hospitalId))
               , (prefix <> "register patientAcuity", (replayRegs ! #patientAcuity) == (forwardRegs ! #patientAcuity))
-              , (prefix <> "register reservationState", (replayRegs ! #reservationState) == (forwardRegs ! #reservationState))
               ]
   where
     prefix = "forward/replay equality: RequestTransferReservation from ReservationUnrequested -- "
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/Projection.hs b/test/conformance/Generated/HospitalCapacity/Reservation/Projection.hs
--- a/test/conformance/Generated/HospitalCapacity/Reservation/Projection.hs
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/Projection.hs
@@ -1,5 +1,5 @@
 {-# LANGUAGE OverloadedRecordDot #-}
--- @generated by keiro-dsl; do not edit. Regenerated from the .keiro spec.
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
 module Generated.HospitalCapacity.Reservation.Projection
   ( transfer_decisionsProjection
   , transfer_decisionsStatusFor
diff --git a/test/conformance/Generated/HospitalCapacity/Reservation/Transducer.hs b/test/conformance/Generated/HospitalCapacity/Reservation/Transducer.hs
new file mode 100644
--- /dev/null
+++ b/test/conformance/Generated/HospitalCapacity/Reservation/Transducer.hs
@@ -0,0 +1,79 @@
+{-# LANGUAGE BlockArguments #-}
+{-# LANGUAGE DataKinds #-}
+{-# LANGUAGE GADTs #-}
+{-# LANGUAGE OverloadedRecordDot #-}
+{-# LANGUAGE OverloadedLabels #-}
+{-# LANGUAGE QualifiedDo #-}
+{-# LANGUAGE TypeApplications #-}
+-- @generated by keiro-dsl 0.8.0.0 (language keiro-dsl 4) from aggregate Reservation; do not edit.
+module Generated.HospitalCapacity.Reservation.Transducer
+  ( reservationTransducer
+  , reservationFoldFingerprint
+  , BehaviorOwnership (..)
+  , reservationPredicateVerifications
+  ) where
+
+import Generated.HospitalCapacity.Reservation.Domain
+import Data.Text (Text)
+import Generated.HospitalCapacity.Nominals (DivertStatus (..))
+import Generated.HospitalCapacity.Nominals qualified as GeneratedNominals
+import Keiki.Builder qualified as B
+import Keiki.Core (HsPred, SymTransducer, (.==), (./=), (.||))
+import Keiki.Core qualified as K
+import Keiki.Symbolic qualified as S
+import HospitalCapacity.Reservation.Holes qualified as Holes
+import Keiki.Generics (RegFieldsOf)
+
+reservationTransducer
+  :: SymTransducer
+       (HsPred ReservationRegs ReservationCommand)
+       ReservationRegs
+       ReservationVertex
+       ReservationCommand
+       ReservationEvent
+reservationTransducer =
+  B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
+    B.from ReservationUnrequested do
+      B.onCmd inCtorRequestTransferReservation $ \d -> B.do
+        let commandDivertStatus = K.inpProj GeneratedNominals.divertStatusEqualityWitness inCtorRequestTransferReservation (#divertStatus :: K.Index (RegFieldsOf RequestTransferReservationData) DivertStatus)
+        B.requireGuard $
+          commandDivertStatus ./= K.lit ("total-divert" :: Text)
+          .|| d.lifeCriticalOverride .== K.lit True
+        B.emit wireTransferReservationCreated (TransferReservationCreatedTermFields
+          { reservationId = d.reservationId
+          , hospitalId = d.hospitalId
+          , commandId = d.commandId
+          , patientAcuity = d.patientAcuity
+          , divertStatus = d.divertStatus
+          , lifeCriticalOverride = d.lifeCriticalOverride
+          })
+        B.goto ReservationHeld
+    B.from ReservationHeld do
+      B.onCmd inCtorConfirmReservation $ \d -> B.do
+        B.emit wireTransferReservationConfirmed (Holes.transition2HeldConfirmReservationOutput1TransferReservationConfirmed d)
+        B.goto ReservationConfirmed
+ where
+  isTerminal = \case
+    ReservationExpired -> True
+    ReservationAdmitted -> True
+    ReservationReleased -> True
+    _ -> False
+
+reservationFoldFingerprint :: Text
+reservationFoldFingerprint = "5e09ffcba6b155a8269b34b1e232d0c6"
+
+data BehaviorOwnership = GeneratedOwned | HoleOwned
+  deriving stock (Eq, Show)
+
+-- Every checked transition predicate is audited through Keiki's conservative
+-- symbolic verifier. Opaque Hole terms remain explicitly unverified.
+reservationPredicateVerifications :: IO [(Text, BehaviorOwnership, S.PredicateVerification)]
+reservationPredicateVerifications = sequence
+  [ verifyTransition "transition1UnrequestedRequestTransferReservation" GeneratedOwned ReservationUnrequested 0
+  , verifyTransition "transition2HeldConfirmReservation" GeneratedOwned ReservationHeld 0
+  ]
+ where
+  verifyTransition label owner source edgeIndex =
+    case drop edgeIndex (K.edgesOut reservationTransducer source) of
+      K.Edge predicate _ _ _ _ : _ -> (\result -> (label, owner, result)) <$> S.verifyPredicate predicate
+      [] -> pure (label, owner, S.UnverifiedSolverFailure "generated transition edge missing")
diff --git a/test/conformance/HospitalCapacity/Reservation/Holes.hs b/test/conformance/HospitalCapacity/Reservation/Holes.hs
--- a/test/conformance/HospitalCapacity/Reservation/Holes.hs
+++ b/test/conformance/HospitalCapacity/Reservation/Holes.hs
@@ -1,68 +1,27 @@
-{-# LANGUAGE BlockArguments #-}
+-- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
+-- overwrites it. Stable generated code owns the aggregate transducer; this
+-- module retains only the DB-coupled projection implementation boundary.
 {-# LANGUAGE DataKinds #-}
+{-# LANGUAGE DuplicateRecordFields #-}
 {-# LANGUAGE OverloadedRecordDot #-}
-{-# LANGUAGE QualifiedDo #-}
-{-# LANGUAGE TypeApplications #-}
 
--- This is a HAND-OWNED hole module. keiro-dsl creates it once and never
--- overwrites it. Its transducer body has been filled by hand to match the
--- captured HospitalCapacity/Reservation reference, against the generated
--- signatures. The harness pins this behaviour.
-module HospitalCapacity.Reservation.Holes (
-    reservationTransducer,
+module HospitalCapacity.Reservation.Holes
+  ( transition2HeldConfirmReservationOutput1TransferReservationConfirmed,
     applyTransfer_decisions,
-) where
+  )
+where
 
 import Generated.HospitalCapacity.Reservation.Domain
-import Generated.HospitalCapacity.Nominals (DivertStatus (..))
-import Keiki.Builder ((=:))
 import Keiki.Builder qualified as B
-import Keiki.Core (HsPred, SymTransducer, lit, (./=), (.==), (.||))
+import Keiki.Generics (RegFieldsOf)
 
-reservationTransducer ::
-    SymTransducer
-        (HsPred ReservationRegs ReservationCommand)
-        ReservationRegs
-        ReservationVertex
-        ReservationCommand
-        ReservationEvent
-reservationTransducer =
-    B.buildTransducer ReservationUnrequested initialReservationRegs isTerminal do
-        B.from ReservationUnrequested do
-            B.onCmd inCtorRequestTransferReservation $ \d -> B.do
-                B.requireGuard (d.divertStatus ./= lit TotalDivert .|| d.lifeCriticalOverride .== lit True)
-                B.slot @"reservationState" =: lit ReservationHeld
-                B.emit
-                    wireTransferReservationCreated
-                    TransferReservationCreatedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        , patientAcuity = d.patientAcuity
-                        , divertStatus = d.divertStatus
-                        , lifeCriticalOverride = d.lifeCriticalOverride
-                        }
-                B.goto ReservationHeld
-        B.from ReservationHeld do
-            B.onCmd inCtorConfirmReservation $ \d -> B.do
-                B.slot @"reservationState" =: lit ReservationConfirmed
-                B.emit
-                    wireTransferReservationConfirmed
-                    TransferReservationConfirmedTermFields
-                        { reservationId = d.reservationId
-                        , hospitalId = d.hospitalId
-                        , commandId = d.commandId
-                        }
-                B.goto ReservationConfirmed
-  where
-    isTerminal = \case
-        ReservationExpired -> True
-        ReservationAdmitted -> True
-        ReservationReleased -> True
-        _ -> False
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed :: B.PayloadProj ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData) -> TransferReservationConfirmedTermFields ReservationRegs ReservationCommand (RegFieldsOf ConfirmReservationData)
+transition2HeldConfirmReservationOutput1TransferReservationConfirmed command =
+  TransferReservationConfirmedTermFields
+    { reservationId = command.reservationId,
+      hospitalId = command.hospitalId,
+      commandId = command.commandId
+    }
 
--- HOLE (DB-coupled, out of scope for EP-1): the read-model SQL for the
--- transfer_decisions projection. The pure event->status mapping is generated as
--- transfer_decisionsStatusFor. Left as a typed hole; the harness does not pin it.
 applyTransfer_decisions :: ReservationEvent -> recorded -> txn ()
 applyTransfer_decisions _event _recorded = error "HOLE: fill transfer_decisions projection apply"
diff --git a/test/conformance/Main.hs b/test/conformance/Main.hs
--- a/test/conformance/Main.hs
+++ b/test/conformance/Main.hs
@@ -1,26 +1,53 @@
-{- | Conformance driver for the captured HospitalCapacity/Reservation aggregate.
-It runs the spec-derived harness emitted by 'Keiro.Dsl.Harness.harnessFor'
-(the @Generated.…Harness@ module) over the hand-filled @Holes.hs@, printing
-each labelled assertion and exiting non-zero if any is False. Compiling this
-component at all proves the scaffolded Generated modules + filled holes build
-against keiki/keiro; running it proves the filled transducer is valid, every
-event round-trips, and the guarded transition behaves as specified.
-
-The mutation check (flip @./=@ to @.==@ in Holes, rebuild) turns the
-"accepts RequestTransferReservation …" assertion red, proving the harness —
-not the scaffold — pins behaviour.
--}
+-- | Conformance driver for the captured HospitalCapacity/Reservation aggregate.
+-- It runs the spec-derived harness emitted by 'Keiro.Dsl.Harness.harnessFor'
+-- (the @Generated.…Harness@ module) over the hand-filled @Holes.hs@, printing
+-- each labelled assertion and exiting non-zero if any is False. Compiling this
+-- component at all proves the scaffolded Generated modules + filled holes build
+-- against keiki/keiro; running it proves the filled transducer is valid, every
+-- event round-trips, and the guarded transition behaves as specified.
+--
+-- The mutation check (temporarily flip @./=@ to @.==@ in the generated
+-- transducer, rebuild, then restore) turns the
+-- "accepts RequestTransferReservation …" assertion red, proving the harness —
+-- not the scaffold — pins behaviour.
 module Main (main) where
 
 import Control.Monad (forM_, unless)
+import Data.Aeson (encode, object)
+import Data.ByteString.Lazy (ByteString)
+import Generated.HospitalCapacity.Nominals (CommandId, DivertStatus (..), HospitalId, PatientAcuity (..), TransferReservationId, parseCommandId, parseHospitalId, parseTransferReservationId)
+import Generated.HospitalCapacity.Reservation.Codec (encodeReservationEvent, parseReservationEvent)
+import Generated.HospitalCapacity.Reservation.Domain (ReservationEvent (..), TransferReservationCreatedData (..))
 import Generated.HospitalCapacity.Reservation.Harness (harnessAssertions)
+import Keiro.Codec (EventType (..))
 import System.Exit (exitFailure)
 
 main :: IO ()
 main = do
-    forM_ harnessAssertions $ \(label, ok) ->
-        putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
-    let failed = [label | (label, ok) <- harnessAssertions, not ok]
-    unless (null failed) $ do
-        putStrLn ("harness: " <> show (length failed) <> " assertion(s) failed")
-        exitFailure
+  forM_ harnessAssertions $ \(label, ok) ->
+    putStrLn ((if ok then "PASS  " else "FAIL  ") <> label)
+  let failed = [label | (label, ok) <- harnessAssertions, not ok]
+      sample =
+        TransferReservationCreated
+          (TransferReservationCreatedData transferReservationIdValue hospitalIdValue commandIdValue RedTag Open False)
+      expectedBytes :: ByteString
+      expectedBytes = "{\"commandId\":\"cmd_01h455vb4pex5vsknk084sn02q\",\"divertStatus\":\"open\",\"hospitalId\":\"hosp_01h455vb4pex5vsknk084sn02q\",\"kind\":\"TransferReservationCreated\",\"lifeCriticalOverride\":false,\"patientAcuity\":\"red\",\"reservationId\":\"rsv_01h455vb4pex5vsknk084sn02q\"}"
+      wireBytesOk = encode (encodeReservationEvent sample) == expectedBytes
+      acceptedOutcomeOk = parseReservationEvent (EventType "TransferReservationCreated") (encodeReservationEvent sample) == Right sample
+      rejectedOutcomeOk = case parseReservationEvent (EventType "UnknownEvent") (object []) of
+        Left _ -> True
+        Right _ -> False
+  putStrLn ("event bytes pinned: " <> show wireBytesOk)
+  putStrLn ("event decoder acceptance/rejection pinned: " <> show (acceptedOutcomeOk && rejectedOutcomeOk))
+  unless (null failed && wireBytesOk && acceptedOutcomeOk && rejectedOutcomeOk) $ do
+    putStrLn ("harness: " <> show (length failed) <> " assertion(s) failed")
+    exitFailure
+
+transferReservationIdValue :: TransferReservationId
+transferReservationIdValue = either (error . show) id (parseTransferReservationId "rsv_01h455vb4pex5vsknk084sn02q")
+
+hospitalIdValue :: HospitalId
+hospitalIdValue = either (error . show) id (parseHospitalId "hosp_01h455vb4pex5vsknk084sn02q")
+
+commandIdValue :: CommandId
+commandIdValue = either (error . show) id (parseCommandId "cmd_01h455vb4pex5vsknk084sn02q")
