diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,112 @@
+# Changelog
+
+All notable changes to `keiro-migrations` are recorded here. The format follows
+[Keep a Changelog](https://keepachangelog.com/), and the package follows the
+[Haskell Package Versioning Policy](https://pvp.haskell.org/).
+
+## [Unreleased]
+
+_No unreleased changes._
+
+## 0.2.0.0 — 2026-07-13
+
+### Added
+
+- Appended `0018`, creating `keiro.keiro_dead_letters`. It durably records
+  process-manager and router dispatches that reached a target stream and were
+  rejected. These rows are distinct from `kiroku.dead_letters`: the source
+  subscription event is successfully handled and checkpointed only after every
+  rejected dispatch has been recorded. The `keiro` package reads and replays them
+  through `Keiro.DeadLetter`.
+- Appended `0017-schema-management-comment`, an observable non-destructive
+  native-runner canary. The combined-history fixture proves Kiroku `0008`
+  completes before Keiro `0017`, then strict verification and reruns succeed.
+
+### Breaking Changes
+
+- Replaced the public Codd runner surface with native `pg-migrate` APIs.
+  `keiroMigrations` now returns the `keiro` `MigrationComponent`, and
+  `frameworkMigrationPlan` composes concrete Kiroku and Keiro components in
+  dependency order. The standard `keiro-migrate` CLI now uses the `pgmigrate`
+  ledger.
+- Renamed the sixteen embedded migrations to stable component-local identifiers
+  under `migrations/manifest` while preserving every legacy SQL payload byte.
+  Timestamped filenames and `migrations.lock` remain import evidence only.
+- **Keiro's framework tables moved out of the `kiroku` schema into a new,
+  dedicated `keiro` PostgreSQL schema that Keiro creates and owns.** The
+  bootstrap migration now issues `CREATE SCHEMA IF NOT EXISTS keiro`, and every
+  migration creates its objects schema-qualified as `keiro.<table>` with **no**
+  `SET search_path` pin and no explanatory comment. The `keiro-migrate new`
+  scaffolder emits that qualified, comment-free template.
+- The codd expected-schema drift gate is now scoped to the `keiro` namespace
+  (`CODD_SCHEMAS=keiro`), contains only `keiro_*` objects, and is **portable**:
+  the captured database role and owner are a deterministic pinned `keiro`
+  identity rather than the local operating-system user, so
+  `cabal test keiro-migrations-test` passes on any machine and in CI.
+- Read-model and projection tables now support a **configurable schema**: an
+  application declares the schema its read-model/projection tables live in (a
+  `schema` field on `ReadModel` plus the `Keiro.Connection` helpers), instead of
+  implicitly inheriting the store connection's `search_path`. This is
+  Haskell-level configuration only — no new database column and no new migration.
+
+### Upgrade
+
+- Added atomic Codd history import for the shared Kiroku/Keiro ledger. Both
+  components' exact payload maps, manifests, and 23 mappings are validated in
+  one adapter call; strict verification succeeds without replaying target SQL.
+- Moved Codd expected-schema, remediation, and ledger-fixup behavior behind the
+  manual `legacy-codd-tools` flag. The normal library, executable, and shared
+  test fixture no longer depend on `codd`, `codd-extras`, `file-embed`, or
+  `postgresql-simple` for migration execution.
+- Added integrity gates for shipped migrations: `migrations.lock`,
+  `keiro-migrate lock`, embed-parity checks, body linting, combined
+  Kiroku+Keiro ledger timestamp uniqueness, a codd v5 ledger canary, and
+  regression tests for both the ledger realignment fixup and the alpha
+  remediation runbook.
+- Hardened the apply path: unknown `keiro-migrate` arguments now exit 2 with
+  usage, `up` is an explicit apply synonym, `KEIRO_MIGRATE_NO_CHECK=false`
+  remains checked, schema drift under the checked path exits nonzero, embedded
+  migrations force codd's single-try retry policy, and concurrent applies
+  serialize with the shared Kiroku advisory lock.
+- Added operator tooling: `keiro-migrate verify` strict-checks a live database
+  against the expected-schema snapshot embedded in the binary,
+  `keiro-migrate status` reports applied and pending combined-ledger entries,
+  and `Keiro.Migrations.missingMigrations` lets applications fail fast at
+  startup when Kiroku or Keiro framework migrations have not been applied.
+- Added `docs/user/migration-ownership.md`, the canonical guide for
+  framework-owned vs application-owned migrations, combined-ledger composition,
+  application migration guards, runtime grants, and operator checks.
+- A database first migrated by `0.1.0.0` has its `keiro_*` tables in `kiroku`. It
+  requires a **one-time remediation** before running these migrations: follow
+  [Upgrading To The Keiro Schema](../docs/user/upgrading-to-the-keiro-schema.md),
+  which wraps the tested script
+  `keiro-migrations/remediation/2026-07-05-relocate-keiro-tables-to-keiro-schema.sql`.
+  The script relocates the tables (`ALTER TABLE ... SET SCHEMA keiro`) in one
+  transaction and needs no codd-ledger change (migration filenames are unchanged,
+  so codd re-runs nothing). Fresh and ephemeral databases need no remediation.
+
+### Recommended Version Bump
+
+- The next release is **`0.2.0.0`**. This is a breaking change; the package
+  follows the Haskell PVP, where the leading `A.B` components form the "major"
+  version and must increment on any breaking change, so `0.1` → `0.2` is the
+  minimal PVP-correct major bump for a pre-1.0 package.
+
+## 0.1.0.0 — 2026-07-05
+
+Initial Hackage release.
+
+### New Features
+
+- Added embedded codd migrations and the `keiro-migrate` executable for Keiro
+  schema installation and upgrades.
+- Added framework schema for read models, snapshots, timers, outbox, inbox,
+  subscriptions, projections, durable workflows, workflow children, awakeables,
+  workflow instances, recovery indexes, and maintenance helpers.
+- Added expected-schema drift checks and a local-development no-check migration
+  runner.
+
+### Bug Fixes
+
+- Aligned with kiroku schema dependencies and added messaging crash-recovery
+  schema updates.
diff --git a/README.md b/README.md
new file mode 100644
--- /dev/null
+++ b/README.md
@@ -0,0 +1,100 @@
+# Keiro database migrations
+
+`keiro-migrations` exports a native `pg-migrate` component named `keiro`. The
+component owns sixteen embedded SQL migrations and declares one dependency,
+`kiroku`. Applications compose Kiroku first and Keiro second; Keiro never embeds
+or copies Kiroku's SQL.
+
+```haskell
+import Keiro.Migrations (frameworkMigrationPlan, keiroMigrations)
+import Kiroku.Store.Migrations qualified as Kiroku
+
+plan = do
+  kiroku <- Kiroku.kirokuMigrations
+  keiro <- keiroMigrations
+  frameworkMigrationPlan kiroku keiro
+```
+
+The `keiro-migrate` executable builds that plan and exposes the standard
+`pg-migrate` commands. Set `DATABASE_URL` or pass the connection option shown by
+the command help:
+
+```bash
+cabal run keiro-migrate -- plan
+cabal run keiro-migrate -- status
+cabal run keiro-migrate -- verify
+cabal run keiro-migrate -- up
+```
+
+`status` and `verify` are read-only. `up` serializes concurrent callers with the
+`pg-migrate` advisory lock, applies all Kiroku migrations before Keiro, and
+reports already-applied migrations without replaying them.
+
+## Authoring native migrations
+
+The ordered manifest is `keiro-migrations/migrations/manifest`. Migration names
+are stable component-local identifiers such as `0016-keiro-inbox-drop-received-idx`;
+the timestamped files under `sql-migrations/` and `migrations.lock` remain
+immutable legacy evidence for old Codd databases.
+
+Create new work through the standard CLI and review the manifest append together
+with the new SQL file:
+
+```bash
+cabal run keiro-migrate -- new \
+  --manifest keiro-migrations/migrations/manifest \
+  --description "add order summary"
+cabal run keiro-migrate -- check keiro-migrations/migrations/manifest
+```
+
+Never edit a migration that has shipped. Add a new forward migration for every
+schema correction.
+
+## Importing the shared Codd ledger
+
+`Keiro.Migrations.History.Codd` combines Kiroku's seven mappings with Keiro's
+sixteen mappings. `frameworkCoddSourceConfig` selects both histories from the
+shared `codd.sql_migrations` or legacy `codd_schema.sql_migrations` ledger,
+checks the original SHA-256 manifests and payload bytes, and produces one atomic
+history import:
+
+```haskell
+config <-
+  either (fail . show) pure $
+    frameworkCoddSourceConfig provider True reason Confirmed
+
+result <-
+  importCoddHistory
+    defaultImportOptions
+    config
+    provider
+    plan
+    frameworkCoddHistoryMappings
+```
+
+Run the import while legacy migration writers are quiescent, then run strict
+`verify`. A partial row, missing payload, changed manifest, or unexpected row in
+strict mode fails before either target component is imported.
+
+Databases first migrated by `keiro-migrations 0.1.0.0` may have `keiro_*`
+tables under `kiroku`. Follow
+[Upgrading To The Keiro Schema](../docs/user/upgrading-to-the-keiro-schema.md)
+before importing their history.
+
+## Legacy transition tools
+
+Codd expected-schema snapshots, remediation drills, and sentinel-ledger fixup
+tests remain available only through the manual `legacy-codd-tools` flag. They do
+not enter the normal library, executable, or test-support dependency closure:
+
+```bash
+cabal test -flegacy-codd-tools \
+  keiro-migrations:keiro-migrations-legacy-test
+cabal run -flegacy-codd-tools keiro-write-expected-schema
+```
+
+The normal migration suite is:
+
+```bash
+cabal test keiro-migrations:keiro-migrations-test
+```
diff --git a/app/Main.hs b/app/Main.hs
new file mode 100644
--- /dev/null
+++ b/app/Main.hs
@@ -0,0 +1,48 @@
+module Main (main) where
+
+import Data.Aeson qualified as Aeson
+import Data.ByteString.Lazy.Char8 qualified as LazyByteString
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text.IO
+import Database.PostgreSQL.Migrate (defaultRunOptions)
+import Database.PostgreSQL.Migrate.CLI
+import Hasql.Connection.Settings qualified as Settings
+import Keiro.Migrations (frameworkMigrationPlan, keiroMigrations)
+import Kiroku.Store.Migrations qualified as Kiroku
+import Options.Applicative
+import System.Environment (lookupEnv)
+import System.Exit qualified as Exit
+
+main :: IO ()
+main = do
+    kiroku <- either (fail . show) pure Kiroku.kirokuMigrations
+    keiro <- either (fail . show) pure keiroMigrations
+    plan <- either (fail . show) pure (frameworkMigrationPlan kiroku keiro)
+    command <-
+        execParser
+            ( info
+                (migrationCommandParser plan <**> helper)
+                (fullDesc <> progDesc "Manage the Kiroku and Keiro migration components")
+            )
+    defaultDatabaseUrl <- lookupEnv "DATABASE_URL"
+    let defaultSettings =
+            Settings.connectionString (Text.pack (maybe "" id defaultDatabaseUrl))
+        environment = cliEnvironment defaultSettings plan defaultRunOptions
+    outcome <- runMigrationCommand environment command
+    case commandOutputFormat command of
+        TextOutput -> Text.IO.putStrLn (renderMigrationCommandText outcome)
+        JsonOutput -> LazyByteString.putStrLn (Aeson.encode (renderMigrationCommandJson outcome))
+    Exit.exitWith
+        (case exitClass outcome of ExitSuccess -> Exit.ExitSuccess; _ -> Exit.ExitFailure 1)
+
+commandOutputFormat :: MigrationCommand -> OutputFormat
+commandOutputFormat command =
+    case command of
+        Plan PlanOptions{output = OutputOptions format} -> format
+        List ListOptions{output = OutputOptions format} -> format
+        Check CheckOptions{output = OutputOptions format} -> format
+        Status StatusOptions{output = OutputOptions format} -> format
+        Verify VerifyOptions{output = OutputOptions format} -> format
+        Up UpOptions{output = OutputOptions format} -> format
+        Repair RepairOptions{output = OutputOptions format} -> format
+        New NewOptions{output = OutputOptions format} -> format
diff --git a/app/WriteExpectedSchema.hs b/app/WriteExpectedSchema.hs
new file mode 100644
--- /dev/null
+++ b/app/WriteExpectedSchema.hs
@@ -0,0 +1,13 @@
+module Main (
+    main,
+)
+where
+
+import Codd.Extras.WriteSchema (writeExpectedSchemaMain)
+import Data.Time (secondsToDiffTime)
+import Keiro.Migrations.LegacyCodd (runAllKeiroMigrationsNoCheck)
+
+main :: IO ()
+main =
+    writeExpectedSchemaMain "keiro" ["keiro"] "keiro-migrations/expected-schema" $ \settings ->
+        runAllKeiroMigrationsNoCheck settings (secondsToDiffTime 5)
diff --git a/expected-schema/v18/db-settings b/expected-schema/v18/db-settings
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/db-settings
@@ -0,0 +1,8 @@
+{
+    "collate": "c",
+    "ctype": "c",
+    "encoding": "utf8",
+    "owner": "keiro",
+    "public_privileges": "[[\"cT\", \"keiro\"]]",
+    "settings": null
+}
diff --git a/expected-schema/v18/roles/keiro b/expected-schema/v18/roles/keiro
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/roles/keiro
@@ -0,0 +1,12 @@
+{
+    "bypassrls": true,
+    "canlogin": true,
+    "config": null,
+    "createdb": true,
+    "createrole": true,
+    "db_privileges": "[[\"cCT\", \"keiro\"]]",
+    "inherit": true,
+    "membership": [],
+    "replication": true,
+    "superuser": true
+}
diff --git a/expected-schema/v18/schemas/keiro/objrep b/expected-schema/v18/schemas/keiro/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/objrep
@@ -0,0 +1,11 @@
+{
+    "owner": "keiro",
+    "privileges": {
+        "keiro": [
+            [
+                "CU",
+                "keiro"
+            ]
+        ]
+    }
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/awakeable_id b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/awakeable_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/awakeable_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/completed_at b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/completed_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/completed_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_id b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_name b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/payload b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/payload
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/payload
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'pending'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_awakeable_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_awakeable_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_awakeable_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL awakeable_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL owner_workflow_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL owner_workflow_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (awakeable_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_awakeables_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_chk b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_chk
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_chk
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "CHECK (status = ANY (ARRAY['pending'::text, 'completed'::text, 'cancelled'::text]))",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "c",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_owner_idx b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_owner_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_owner_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_awakeables_owner_idx ON keiro.keiro_awakeables USING btree (owner_workflow_name, owner_workflow_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pending_idx b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pending_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pending_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_awakeables_pending_idx ON keiro.keiro_awakeables USING btree (status) WHERE (status = 'pending'::text)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_awakeables_pkey ON keiro.keiro_awakeables USING btree (awakeable_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_awakeables/objrep
@@ -0,0 +1,32 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "awakeable_id",
+        "owner_workflow_name",
+        "owner_workflow_id",
+        "status",
+        "payload",
+        "created_at",
+        "updated_at",
+        "completed_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_awakeables",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attempt_count b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attempt_count
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attempt_count
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attributes b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attributes
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attributes
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/causation_id b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/causation_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/causation_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/completed_at b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/completed_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/completed_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/content_type b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/content_type
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/content_type
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/correlation_id b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/correlation_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/correlation_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/dedupe_key b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/dedupe_key
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/dedupe_key
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/destination b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/destination
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/destination
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/event_type b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/event_type
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/event_type
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/failed_at b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/failed_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/failed_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_offset b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_offset
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_offset
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_partition b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_partition
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_partition
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_topic b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_topic
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_topic
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/last_error b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/last_error
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/last_error
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/message_id b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/message_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/message_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/occurred_at b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/occurred_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/occurred_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/payload_bytes b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/payload_bytes
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/payload_bytes
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bytea"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/received_at b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/received_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/received_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_fingerprint b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_fingerprint
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_fingerprint
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_id b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_registry b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_registry
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_registry
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_subject b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_subject
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_subject
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version_ref b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version_ref
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version_ref
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_event_id b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_event_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_event_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_global_position b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_global_position
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_global_position
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'processing'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/traceparent b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/traceparent
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/traceparent
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/tracestate b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/tracestate
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/tracestate
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_attempt_count_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_attempt_count_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_attempt_count_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL attempt_count",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_content_type_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_content_type_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_content_type_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL content_type",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_dedupe_key_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_dedupe_key_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_dedupe_key_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL dedupe_key",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_payload_bytes_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_payload_bytes_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_payload_bytes_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL payload_bytes",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (source, dedupe_key)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_inbox_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_received_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_received_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_received_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL received_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_source_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_source_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_source_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL source",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_backlog_idx b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_backlog_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_backlog_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_inbox_backlog_idx ON keiro.keiro_inbox USING btree (status) WHERE (status = ANY (ARRAY['processing'::text, 'failed'::text]))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_completed_idx b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_completed_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_completed_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_inbox_completed_idx ON keiro.keiro_inbox USING btree (completed_at) WHERE (status = 'completed'::text)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_inbox_pkey ON keiro.keiro_inbox USING btree (source, dedupe_key)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_inbox/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_inbox/objrep
@@ -0,0 +1,54 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "source",
+        "dedupe_key",
+        "message_id",
+        "source_event_id",
+        "source_global_position",
+        "destination",
+        "event_type",
+        "schema_version",
+        "content_type",
+        "schema_registry",
+        "schema_subject",
+        "schema_version_ref",
+        "schema_id",
+        "schema_fingerprint",
+        "causation_id",
+        "correlation_id",
+        "traceparent",
+        "tracestate",
+        "kafka_topic",
+        "kafka_partition",
+        "kafka_offset",
+        "payload_bytes",
+        "attributes",
+        "occurred_at",
+        "status",
+        "received_at",
+        "completed_at",
+        "failed_at",
+        "last_error",
+        "attempt_count"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_inbox",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attempt_count b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attempt_count
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attempt_count
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attributes b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attributes
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attributes
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/causation_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/causation_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/causation_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/content_type b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/content_type
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/content_type
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/correlation_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/correlation_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/correlation_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/destination b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/destination
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/destination
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/event_type b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/event_type
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/event_type
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/last_error b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/last_error
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/last_error
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_key b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_key
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_key
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/next_attempt_at b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/next_attempt_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/next_attempt_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/occurred_at b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/occurred_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/occurred_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/outbox_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/outbox_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/outbox_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/payload_bytes b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/payload_bytes
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/payload_bytes
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bytea"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/published_at b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/published_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/published_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_fingerprint b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_fingerprint
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_fingerprint
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_registry b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_registry
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_registry
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_subject b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_subject
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_subject
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version_ref b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version_ref
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version_ref
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_event_id b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_event_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_event_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_global_position b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_global_position
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_global_position
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'pending'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/traceparent b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/traceparent
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/traceparent
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/tracestate b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/tracestate
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/tracestate
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_attempt_count_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_attempt_count_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_attempt_count_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL attempt_count",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_content_type_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_content_type_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_content_type_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL content_type",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_destination_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_destination_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_destination_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL destination",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_event_type_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_event_type_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_event_type_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL event_type",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_message_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_message_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_message_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL message_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_next_attempt_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_next_attempt_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_next_attempt_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL next_attempt_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_occurred_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_occurred_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_occurred_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL occurred_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_outbox_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_outbox_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_outbox_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL outbox_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_payload_bytes_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_payload_bytes_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_payload_bytes_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL payload_bytes",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (outbox_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_outbox_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_schema_version_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_schema_version_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_schema_version_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL schema_version",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_message_id_key b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_message_id_key
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_message_id_key
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "UNIQUE (source, message_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_outbox_source_message_id_key",
+    "type": "u",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL source",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_claim_order_idx b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_claim_order_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_claim_order_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_outbox_claim_order_idx ON keiro.keiro_outbox USING btree (created_at, outbox_id) WHERE (status = ANY (ARRAY['pending'::text, 'failed'::text]))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_head_of_line_idx b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_head_of_line_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_head_of_line_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_outbox_head_of_line_idx ON keiro.keiro_outbox USING btree (source, message_key, created_at) WHERE ((status <> ALL (ARRAY['sent'::text, 'dead'::text])) AND (message_key IS NOT NULL))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pending_idx b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pending_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pending_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_outbox_pending_idx ON keiro.keiro_outbox USING btree (status, next_attempt_at, created_at)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_outbox_pkey ON keiro.keiro_outbox USING btree (outbox_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_sent_gc_idx b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_sent_gc_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_sent_gc_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_outbox_sent_gc_idx ON keiro.keiro_outbox USING btree (published_at) WHERE (status = 'sent'::text)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_message_id_key b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_message_id_key
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_message_id_key
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_outbox_source_message_id_key ON keiro.keiro_outbox USING btree (source, message_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_order_idx b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_order_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_order_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_outbox_source_order_idx ON keiro.keiro_outbox USING btree (source, created_at, outbox_id) WHERE (status <> ALL (ARRAY['sent'::text, 'dead'::text]))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_outbox/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_outbox/objrep
@@ -0,0 +1,53 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "outbox_id",
+        "message_id",
+        "source",
+        "destination",
+        "message_key",
+        "event_type",
+        "schema_version",
+        "content_type",
+        "schema_registry",
+        "schema_subject",
+        "schema_version_ref",
+        "schema_id",
+        "schema_fingerprint",
+        "source_event_id",
+        "source_global_position",
+        "causation_id",
+        "correlation_id",
+        "traceparent",
+        "tracestate",
+        "payload_bytes",
+        "attributes",
+        "occurred_at",
+        "status",
+        "attempt_count",
+        "next_attempt_at",
+        "last_error",
+        "published_at",
+        "created_at",
+        "updated_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_outbox",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/applied_at b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/applied_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/applied_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/event_id b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/event_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/event_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/projection_name b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/projection_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/projection_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_applied_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_applied_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_applied_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL applied_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_event_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_event_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_event_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL event_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (projection_name, event_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_projection_dedup_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_projection_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_projection_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_projection_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL projection_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_applied_at_idx b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_applied_at_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_applied_at_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_projection_dedup_applied_at_idx ON keiro.keiro_projection_dedup USING btree (applied_at)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_projection_dedup_pkey ON keiro.keiro_projection_dedup USING btree (projection_name, event_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/objrep
@@ -0,0 +1,27 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "projection_name",
+        "event_id",
+        "applied_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_projection_dedup",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/last_built_at b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/last_built_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/last_built_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/name b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/shape_hash b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/shape_hash
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/shape_hash
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/version b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/version
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/version
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (name)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_read_models_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_shape_hash_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_shape_hash_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_shape_hash_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL shape_hash",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_version_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_version_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_version_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL version",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/indexes/keiro_read_models_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/indexes/keiro_read_models_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/indexes/keiro_read_models_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_read_models_pkey ON keiro.keiro_read_models USING btree (name)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_read_models/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_read_models/objrep
@@ -0,0 +1,30 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "name",
+        "version",
+        "shape_hash",
+        "last_built_at",
+        "status",
+        "updated_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_read_models",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/regfile_shape_hash b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/regfile_shape_hash
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/regfile_shape_hash
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state_codec_version b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state_codec_version
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state_codec_version
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_id b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_version b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_version
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_version
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (stream_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_snapshots_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_regfile_shape_hash_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_regfile_shape_hash_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_regfile_shape_hash_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL regfile_shape_hash",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_codec_version_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_codec_version_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_codec_version_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL state_codec_version",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL state",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL stream_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_version_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_version_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_version_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL stream_version",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_compat_idx b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_compat_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_compat_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_snapshots_compat_idx ON keiro.keiro_snapshots USING btree (stream_id, state_codec_version, regfile_shape_hash, stream_version DESC)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_snapshots_pkey ON keiro.keiro_snapshots USING btree (stream_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_snapshots/objrep
@@ -0,0 +1,31 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "stream_id",
+        "stream_version",
+        "state",
+        "state_codec_version",
+        "regfile_shape_hash",
+        "created_at",
+        "updated_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_snapshots",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/bucket b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/bucket
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/bucket
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "integer"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/heartbeat_at b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/heartbeat_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/heartbeat_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/lease_expires_at b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/lease_expires_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/lease_expires_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/owner_worker_id b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/owner_worker_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/owner_worker_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/shard_count b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/shard_count
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/shard_count
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "integer"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/subscription_name b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/subscription_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/subscription_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL bucket",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_range_chk b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_range_chk
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_range_chk
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "CHECK (bucket >= 0 AND bucket < shard_count)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "c",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_count_chk b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_count_chk
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_count_chk
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "CHECK (shard_count >= 1)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "c",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (subscription_name, bucket)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_subscription_shards_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_shard_count_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_shard_count_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_shard_count_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL shard_count",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_subscription_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_subscription_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_subscription_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL subscription_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_lease_idx b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_lease_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_lease_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_subscription_shards_lease_idx ON keiro.keiro_subscription_shards USING btree (subscription_name, lease_expires_at)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_owner_idx b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_owner_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_owner_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_subscription_shards_owner_idx ON keiro.keiro_subscription_shards USING btree (subscription_name, owner_worker_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_subscription_shards_pkey ON keiro.keiro_subscription_shards USING btree (subscription_name, bucket)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/objrep
@@ -0,0 +1,31 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "subscription_name",
+        "bucket",
+        "shard_count",
+        "owner_worker_id",
+        "lease_expires_at",
+        "heartbeat_at",
+        "updated_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_subscription_shards",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/attempts b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/attempts
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/attempts
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "bigint"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/correlation_id b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/correlation_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/correlation_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fire_at b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fire_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fire_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fired_event_id b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fired_event_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fired_event_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/last_error b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/last_error
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/last_error
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/payload b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/payload
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/payload
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/process_manager_name b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/process_manager_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/process_manager_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'scheduled'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/timer_id b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/timer_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/timer_id
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "uuid"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_attempts_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_attempts_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_attempts_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL attempts",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_correlation_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_correlation_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_correlation_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL correlation_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_fire_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_fire_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_fire_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL fire_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_payload_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_payload_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_payload_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL payload",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (timer_id)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_timers_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_process_manager_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_process_manager_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_process_manager_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL process_manager_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_timer_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_timer_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_timer_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL timer_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_due_idx b/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_due_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_due_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_timers_due_idx ON keiro.keiro_timers USING btree (status, fire_at, process_manager_name) WHERE (status = ANY (ARRAY['scheduled'::text, 'firing'::text]))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_timers_pkey ON keiro.keiro_timers USING btree (timer_id)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_timers/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_timers/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_timers/objrep
@@ -0,0 +1,35 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "timer_id",
+        "process_manager_name",
+        "correlation_id",
+        "fire_at",
+        "payload",
+        "status",
+        "attempts",
+        "fired_event_id",
+        "created_at",
+        "updated_at",
+        "last_error"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_timers",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/await_step b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/await_step
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/await_step
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_id b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_name b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/completed_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/completed_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/completed_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_id b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_name b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/result b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/result
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/result
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'running'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_await_step_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_await_step_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_await_step_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL await_step",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL child_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL child_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL parent_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL parent_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (child_id, child_name)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_workflow_children_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_chk b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_chk
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_chk
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "CHECK (status = ANY (ARRAY['running'::text, 'completed'::text, 'cancelled'::text, 'failed'::text]))",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "c",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_parent_idx b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_parent_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_parent_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_workflow_children_parent_idx ON keiro.keiro_workflow_children USING btree (parent_id, parent_name)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_workflow_children_pkey ON keiro.keiro_workflow_children USING btree (child_id, child_name)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_running_idx b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_running_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_running_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_workflow_children_running_idx ON keiro.keiro_workflow_children USING btree (status) WHERE (status = 'running'::text)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/objrep
@@ -0,0 +1,34 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "child_id",
+        "child_name",
+        "parent_id",
+        "parent_name",
+        "await_step",
+        "status",
+        "result",
+        "created_at",
+        "updated_at",
+        "completed_at"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_workflow_children",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/generation b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/generation
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/generation
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "integer"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/recorded_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/recorded_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/recorded_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/result b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/result
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/result
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "jsonb"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/step_name b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/step_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/step_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_id b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_name b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_generation_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_generation_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_generation_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL generation",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (workflow_id, workflow_name, generation, step_name)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_workflow_steps_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_recorded_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_recorded_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_recorded_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL recorded_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_result_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_result_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_result_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL result",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_step_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_step_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_step_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL step_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL workflow_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL workflow_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_workflow_steps_pkey ON keiro.keiro_workflow_steps USING btree (workflow_id, workflow_name, generation, step_name)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_workflow_idx b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_workflow_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_workflow_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_workflow_steps_workflow_idx ON keiro.keiro_workflow_steps USING btree (workflow_id, workflow_name, generation)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/objrep
@@ -0,0 +1,30 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "workflow_id",
+        "workflow_name",
+        "step_name",
+        "result",
+        "recorded_at",
+        "generation"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_workflow_steps",
+    "shared": false
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/attempts b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/attempts
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/attempts
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "integer"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/completed_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/completed_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/completed_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/created_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/created_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/created_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/generation b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/generation
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/generation
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "0",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "integer"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/last_error b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/last_error
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/last_error
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/lease_expires_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/lease_expires_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/lease_expires_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/leased_by b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/leased_by
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/leased_by
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/next_attempt_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/next_attempt_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/next_attempt_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/status b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/status
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/status
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": "'running'::text",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/updated_at b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/updated_at
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/updated_at
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": "now()",
+    "generated": "",
+    "hasdefault": true,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/wake_after b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/wake_after
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/wake_after
@@ -0,0 +1,13 @@
+{
+    "collation": null,
+    "collation_nsp": null,
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": false,
+    "privileges": null,
+    "type": "timestamp with time zone"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_id b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_id
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_id
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_name b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_name
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_name
@@ -0,0 +1,13 @@
+{
+    "collation": "default",
+    "collation_nsp": "pg_catalog",
+    "default": null,
+    "generated": "",
+    "hasdefault": false,
+    "identity": "",
+    "inhcount": 0,
+    "local": true,
+    "notnull": true,
+    "privileges": null,
+    "type": "text"
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_attempts_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_attempts_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_attempts_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL attempts",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_created_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_created_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_created_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL created_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_generation_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_generation_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_generation_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL generation",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_pkey
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "PRIMARY KEY (workflow_id, workflow_name)",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": true,
+    "parent_constraint": null,
+    "supporting_index": "keiro_workflows_pkey",
+    "type": "p",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_chk b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_chk
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_chk
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "CHECK (status = ANY (ARRAY['running'::text, 'suspended'::text, 'completed'::text, 'cancelled'::text, 'failed'::text]))",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "c",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL status",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_updated_at_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_updated_at_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_updated_at_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL updated_at",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_id_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_id_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_id_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL workflow_id",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_name_not_null b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_name_not_null
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_name_not_null
@@ -0,0 +1,16 @@
+{
+    "deferrable": false,
+    "deferred": false,
+    "definition": "NOT NULL workflow_name",
+    "fk_deltype": " ",
+    "fk_matchtype": " ",
+    "fk_ref_table": null,
+    "fk_updtype": " ",
+    "inhcount": 0,
+    "local": true,
+    "noinherit": false,
+    "parent_constraint": null,
+    "supporting_index": null,
+    "type": "n",
+    "validated": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_active_idx b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_active_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_active_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_workflows_active_idx ON keiro.keiro_workflows USING btree (status) WHERE (status = ANY (ARRAY['running'::text, 'suspended'::text]))",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_gc_idx b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_gc_idx
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_gc_idx
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE INDEX keiro_workflows_gc_idx ON keiro.keiro_workflows USING btree (status, completed_at)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": false,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": false,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_pkey b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_pkey
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_pkey
@@ -0,0 +1,21 @@
+{
+    "accessmethod": "btree",
+    "amoptions": null,
+    "compositetype": null,
+    "definition": "CREATE UNIQUE INDEX keiro_workflows_pkey ON keiro.keiro_workflows USING btree (workflow_id, workflow_name)",
+    "exclusion": false,
+    "forcerowsecurity": false,
+    "immediate": true,
+    "kind": "i",
+    "nullsnotdistinct": false,
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "primary": true,
+    "replident": "n",
+    "rowsecurity": false,
+    "rowtype": null,
+    "shared": false,
+    "unique": true,
+    "valid": true
+}
diff --git a/expected-schema/v18/schemas/keiro/tables/keiro_workflows/objrep b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/objrep
new file mode 100644
--- /dev/null
+++ b/expected-schema/v18/schemas/keiro/tables/keiro_workflows/objrep
@@ -0,0 +1,37 @@
+{
+    "accessmethod": "heap",
+    "amoptions": null,
+    "columns_in_order": [
+        "workflow_id",
+        "workflow_name",
+        "generation",
+        "status",
+        "attempts",
+        "last_error",
+        "next_attempt_at",
+        "leased_by",
+        "lease_expires_at",
+        "created_at",
+        "updated_at",
+        "completed_at",
+        "wake_after"
+    ],
+    "compositetype": null,
+    "forcerowsecurity": false,
+    "kind": "r",
+    "owner": "keiro",
+    "partition": false,
+    "persistence": "p",
+    "privileges": {
+        "keiro": [
+            [
+                "damxrtDw",
+                "keiro"
+            ]
+        ]
+    },
+    "replident": "d",
+    "rowsecurity": false,
+    "rowtype": "keiro_workflows",
+    "shared": false
+}
diff --git a/keiro-migrations.cabal b/keiro-migrations.cabal
new file mode 100644
--- /dev/null
+++ b/keiro-migrations.cabal
@@ -0,0 +1,439 @@
+cabal-version:      3.0
+name:               keiro-migrations
+version:            0.2.0.0
+synopsis:           Schema migrations for keiro
+description:
+  Embedded PostgreSQL schema migrations and a migration runner for the Keiro
+  event-sourcing framework.
+
+license:            BSD-3-Clause
+author:             Nadeem Bitar
+maintainer:         nadeem@gmail.com
+copyright:          2026 Nadeem Bitar
+category:           Database
+build-type:         Simple
+tested-with:        GHC >=9.12 && <9.13
+extra-doc-files:
+  CHANGELOG.md
+  README.md
+
+extra-source-files:
+  expected-schema/v18/db-settings
+  expected-schema/v18/roles/keiro
+  expected-schema/v18/schemas/keiro/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/awakeable_id
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/completed_at
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_id
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_name
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/payload
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_awakeable_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_chk
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_owner_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pending_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_awakeables/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attempt_count
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attributes
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/causation_id
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/completed_at
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/content_type
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/correlation_id
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/dedupe_key
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/destination
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/event_type
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/failed_at
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_offset
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_partition
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_topic
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/last_error
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/message_id
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/occurred_at
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/payload_bytes
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/received_at
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_fingerprint
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_id
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_registry
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_subject
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version_ref
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_event_id
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_global_position
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/traceparent
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/tracestate
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_attempt_count_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_content_type_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_dedupe_key_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_payload_bytes_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_received_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_source_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_backlog_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_completed_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_inbox/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attempt_count
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attributes
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/causation_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/content_type
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/correlation_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/destination
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/event_type
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/last_error
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_key
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/next_attempt_at
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/occurred_at
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/outbox_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/payload_bytes
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/published_at
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_fingerprint
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_registry
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_subject
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version_ref
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_event_id
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_global_position
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/traceparent
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/tracestate
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_attempt_count_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_content_type_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_destination_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_event_type_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_message_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_next_attempt_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_occurred_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_outbox_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_payload_bytes_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_schema_version_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_message_id_key
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_claim_order_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_head_of_line_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pending_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_sent_gc_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_message_id_key
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_order_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_outbox/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/applied_at
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/event_id
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/projection_name
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_applied_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_event_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_projection_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_applied_at_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/last_built_at
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/name
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/shape_hash
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/version
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_shape_hash_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_version_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/indexes/keiro_read_models_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_read_models/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/regfile_shape_hash
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state_codec_version
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_id
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_version
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_regfile_shape_hash_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_codec_version_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_version_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_compat_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_snapshots/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/bucket
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/heartbeat_at
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/lease_expires_at
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/owner_worker_id
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/shard_count
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/subscription_name
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_range_chk
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_count_chk
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_shard_count_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_subscription_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_lease_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_owner_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/attempts
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/correlation_id
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fire_at
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fired_event_id
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/last_error
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/payload
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/process_manager_name
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/timer_id
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_attempts_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_correlation_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_fire_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_payload_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_process_manager_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_timer_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_due_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_timers/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/await_step
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_id
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_name
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/completed_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_id
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_name
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/result
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_await_step_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_chk
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_parent_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_running_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/generation
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/recorded_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/result
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/step_name
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_id
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_name
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_generation_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_recorded_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_result_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_step_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_workflow_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/objrep
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/attempts
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/completed_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/created_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/generation
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/last_error
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/lease_expires_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/leased_by
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/next_attempt_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/status
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/updated_at
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/wake_after
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_id
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_name
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_attempts_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_created_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_generation_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_chk
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_updated_at_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_id_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_name_not_null
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_active_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_gc_idx
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_pkey
+  expected-schema/v18/schemas/keiro/tables/keiro_workflows/objrep
+  migrations/*.sql
+  migrations/manifest
+  migrations.lock
+  sql-migrations/*.sql
+
+source-repository head
+  type:     git
+  location: https://github.com/shinzui/keiro.git
+
+flag legacy-codd-tools
+  description:
+    Build transitional Codd expected-schema, remediation, and ledger-fixup
+    targets. The normal pg-migrate closure excludes these dependencies.
+
+  default:     False
+  manual:      True
+
+common common
+  default-language:   GHC2024
+  default-extensions:
+    DeriveAnyClass
+    DuplicateRecordFields
+    MultilineStrings
+    OverloadedLabels
+    OverloadedStrings
+
+library
+  import:          common
+  exposed-modules:
+    Keiro.Migrations
+    Keiro.Migrations.History.Codd
+
+  other-modules:
+    Keiro.Migrations.Internal.Definition
+    Keiro.Migrations.Internal.EmbedFile
+
+  hs-source-dirs:  src
+  build-depends:
+    , base                     >=4.18     && <5
+    , bytestring               >=0.11     && <0.13
+    , containers               >=0.6      && <0.8
+    , kiroku-store-migrations  ^>=0.2.0.0
+    , pg-migrate               ^>=1.0.0.0
+    , pg-migrate-embed         ^>=1.0.0.0
+    , pg-migrate-import-codd   ^>=1.0.0.0
+    , template-haskell         >=2.20     && <2.24
+    , text                     >=2.0      && <2.2
+
+  if flag(legacy-codd-tools)
+    exposed-modules:
+      Keiro.Migrations.ExpectedSchema
+      Keiro.Migrations.LegacyCodd
+      Keiro.Migrations.New
+
+    build-depends:
+      , codd         >=0.1.8  && <0.2
+      , codd-extras  >=0.1    && <0.2
+      , directory    >=1.3
+      , file-embed   >=0.0.15 && <0.0.17
+      , filepath     >=1.4    && <1.6
+      , time         >=1.12   && <1.15
+
+executable keiro-migrate
+  import:         common
+  main-is:        Main.hs
+  hs-source-dirs: app
+  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+    , aeson                    >=2.1      && <2.3
+    , base                     >=4.18     && <5
+    , bytestring               >=0.11     && <0.13
+    , hasql                    >=1.10     && <1.11
+    , keiro-migrations
+    , kiroku-store-migrations  ^>=0.2.0.0
+    , optparse-applicative     >=0.17     && <0.20
+    , pg-migrate               ^>=1.0.0.0
+    , pg-migrate-cli           ^>=1.0.0.0
+    , text                     >=2.0      && <2.2
+
+executable keiro-write-expected-schema
+  import:         common
+  main-is:        WriteExpectedSchema.hs
+  hs-source-dirs: app
+  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
+
+  if !flag(legacy-codd-tools)
+    buildable: False
+
+  build-depends:
+    , base
+    , codd-extras:ephemeral  >=0.1 && <0.2
+    , keiro-migrations
+    , time
+
+test-suite keiro-migrations-test
+  import:         common
+  type:           exitcode-stdio-1.0
+  main-is:        Main.hs
+  hs-source-dirs: test
+  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+    , async
+    , base                     >=4.18     && <5
+    , bytestring               >=0.11     && <0.13
+    , containers               >=0.6      && <0.8
+    , directory                >=1.3
+    , ephemeral-pg             >=0.2      && <0.3
+    , filepath                 >=1.4      && <1.6
+    , hasql                    >=1.10     && <1.11
+    , hspec                    >=2.10     && <2.12
+    , keiro-migrations
+    , kiroku-store-migrations  ^>=0.2.0.0
+    , pg-migrate               ^>=1.0.0.0
+    , pg-migrate-import-codd   ^>=1.0.0.0
+    , pg-migrate-test-support  ^>=1.0.0.0
+    , text                     >=2.0      && <2.2
+
+test-suite keiro-migrations-legacy-test
+  import:         common
+  type:           exitcode-stdio-1.0
+  main-is:        Main.hs
+  hs-source-dirs: test-legacy
+  ghc-options:    -threaded -rtsopts -with-rtsopts=-N
+
+  if !flag(legacy-codd-tools)
+    buildable: False
+
+  build-depends:
+    , aeson
+    , async
+    , attoparsec
+    , base                     >=4.18     && <5
+    , codd
+    , codd-extras
+    , containers
+    , contravariant-extras
+    , directory
+    , ephemeral-pg             >=0.2
+    , filepath
+    , generic-lens             >=2.2
+    , hasql                    >=1.10
+    , hasql-pool               >=1.2
+    , hspec                    >=2.10
+    , keiro-migrations
+    , kiroku-store             >=0.3      && <0.4
+    , kiroku-store-migrations  ^>=0.2.0.0
+    , lens                     >=5.2
+    , temporary
+    , text                     >=2.0
+    , time                     >=1.12
+    , vector                   >=0.13
diff --git a/migrations.lock b/migrations.lock
new file mode 100644
--- /dev/null
+++ b/migrations.lock
@@ -0,0 +1,16 @@
+1d15faa7cfb474455c3a1193b454ce493d49589b0f9f62b0b8beff65a00f6403  2026-05-17-13-58-15-keiro-bootstrap.sql
+4c9c645e7ac9ebc9228638a89580613bd0058444e20047486d4bd7696b9ddb0a  2026-05-19-12-55-02-keiro-outbox.sql
+71aabbfd93e66b324691ca856ca73245d62d84d984ca162e1388726127daa5e5  2026-05-19-13-05-23-keiro-inbox.sql
+42f1bbd4fb23b27b2be434f25b559b2e17870f1d8c6efbcf2ae6a702c6136f98  2026-06-03-05-14-28-keiro-timer-recovery.sql
+e967d41342cddc8cfe8381e5f562603dbc9a10e61232458e24c5cb291cfe9047  2026-06-03-16-10-05-keiro-workflow-steps.sql
+3b20ba3981ae718cfbb9ed68318131a50ca5ad715b459868b79ac1f4c138fc5c  2026-06-03-18-19-41-keiro-awakeables.sql
+975c0e6480fa01f2af1e2c45a4396e2588af4560c9803b55e8b574ea2e69b5b2  2026-06-03-19-49-23-keiro-workflow-children.sql
+f05e8254de4c328f9319db4db8b9a78d6fba552c245acf0e24c6c450d8d71559  2026-06-04-02-12-28-keiro-workflow-generation.sql
+1a93b4fdad24624b168a0aeeb12c659c59764660cc11fa564e77aa4b32a54fca  2026-06-04-03-53-34-keiro-subscription-shards.sql
+49a963745a637c8e0248b5b958a719ccc772dbd02ca77cf706fee5de8d8d1a85  2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql
+7c329c9f439d2bab927debf5dc8b96acb438b4e240ef16292723398ddf005fea  2026-06-15-15-07-25-keiro-workflows-instances.sql
+77c394334c64ff0631eb1c831f60629123ae255f1be7fb1716748b6ff9d0b701  2026-06-15-17-53-48-keiro-workflow-gc-index.sql
+a2ea08ce3debec078e89490403241d9fd4e5d4ff3ea64b2c01ab236fb2dda313  2026-06-15-18-01-33-keiro-workflows-wake-after.sql
+ed5bef75d195df0335b86d36256fd4f1dab30cb1eab4e05f0a321bf8d989503a  2026-06-15-21-49-37-keiro-projection-dedup.sql
+fd65320576b06e8d30a8308ac790be463c09906e74d81a6fd535e7745ef943c5  2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql
+50b169c9bc98e7ab6845c74b105d13990af3cdb2e587445fd869e942c28df81d  2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql
diff --git a/migrations/0001-keiro-bootstrap.sql b/migrations/0001-keiro-bootstrap.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0001-keiro-bootstrap.sql
@@ -0,0 +1,44 @@
+-- Keiro framework bootstrap migration (codd).
+--
+-- Keiro owns the dedicated `keiro` schema. codd applies this file in its own
+-- session, so the schema is created here and every object is written fully
+-- qualified as keiro.<name> — no session search-path pin is used anywhere.
+CREATE SCHEMA IF NOT EXISTS keiro;
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_snapshots (
+  stream_id BIGINT PRIMARY KEY,
+  stream_version BIGINT NOT NULL,
+  state JSONB NOT NULL,
+  state_codec_version BIGINT NOT NULL,
+  regfile_shape_hash TEXT NOT NULL,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS keiro_snapshots_compat_idx
+  ON keiro.keiro_snapshots (stream_id, state_codec_version, regfile_shape_hash, stream_version DESC);
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_read_models (
+  name TEXT PRIMARY KEY,
+  version BIGINT NOT NULL,
+  shape_hash TEXT NOT NULL,
+  last_built_at TIMESTAMPTZ,
+  status TEXT NOT NULL,
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_timers (
+  timer_id UUID PRIMARY KEY,
+  process_manager_name TEXT NOT NULL,
+  correlation_id TEXT NOT NULL,
+  fire_at TIMESTAMPTZ NOT NULL,
+  payload JSONB NOT NULL,
+  status TEXT NOT NULL DEFAULT 'scheduled',
+  attempts BIGINT NOT NULL DEFAULT 0,
+  fired_event_id UUID,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS keiro_timers_due_idx
+  ON keiro.keiro_timers (status, fire_at, process_manager_name);
diff --git a/migrations/0002-keiro-outbox.sql b/migrations/0002-keiro-outbox.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0002-keiro-outbox.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_outbox (
+  outbox_id UUID PRIMARY KEY,
+  message_id TEXT NOT NULL,
+  source TEXT NOT NULL,
+  destination TEXT NOT NULL,
+  message_key TEXT,
+  event_type TEXT NOT NULL,
+  schema_version BIGINT NOT NULL,
+  content_type TEXT NOT NULL,
+  schema_registry TEXT,
+  schema_subject TEXT,
+  schema_version_ref BIGINT,
+  schema_id BIGINT,
+  schema_fingerprint TEXT,
+  source_event_id UUID,
+  source_global_position BIGINT,
+  causation_id UUID,
+  correlation_id UUID,
+  traceparent TEXT,
+  tracestate TEXT,
+  payload_bytes BYTEA NOT NULL,
+  attributes JSONB,
+  occurred_at TIMESTAMPTZ NOT NULL,
+  status TEXT NOT NULL DEFAULT 'pending',
+  attempt_count BIGINT NOT NULL DEFAULT 0,
+  next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  last_error TEXT,
+  published_at TIMESTAMPTZ,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  UNIQUE (source, message_id)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_outbox_pending_idx
+  ON keiro.keiro_outbox (status, next_attempt_at, created_at);
+
+CREATE INDEX IF NOT EXISTS keiro_outbox_head_of_line_idx
+  ON keiro.keiro_outbox (source, message_key, created_at)
+  WHERE status NOT IN ('sent', 'dead') AND message_key IS NOT NULL;
diff --git a/migrations/0003-keiro-inbox.sql b/migrations/0003-keiro-inbox.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0003-keiro-inbox.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_inbox (
+  source TEXT NOT NULL,
+  dedupe_key TEXT NOT NULL,
+  message_id TEXT,
+  source_event_id UUID,
+  source_global_position BIGINT,
+  destination TEXT,
+  event_type TEXT,
+  schema_version BIGINT,
+  content_type TEXT NOT NULL,
+  schema_registry TEXT,
+  schema_subject TEXT,
+  schema_version_ref BIGINT,
+  schema_id BIGINT,
+  schema_fingerprint TEXT,
+  causation_id UUID,
+  correlation_id UUID,
+  traceparent TEXT,
+  tracestate TEXT,
+  kafka_topic TEXT,
+  kafka_partition BIGINT,
+  kafka_offset BIGINT,
+  payload_bytes BYTEA NOT NULL,
+  attributes JSONB,
+  occurred_at TIMESTAMPTZ,
+  status TEXT NOT NULL DEFAULT 'processing',
+  received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at TIMESTAMPTZ,
+  failed_at TIMESTAMPTZ,
+  last_error TEXT,
+  PRIMARY KEY (source, dedupe_key)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_inbox_received_idx
+  ON keiro.keiro_inbox (received_at);
+
+CREATE INDEX IF NOT EXISTS keiro_inbox_completed_idx
+  ON keiro.keiro_inbox (completed_at)
+  WHERE status = 'completed';
diff --git a/migrations/0004-keiro-timer-recovery.sql b/migrations/0004-keiro-timer-recovery.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0004-keiro-timer-recovery.sql
@@ -0,0 +1,8 @@
+ALTER TABLE keiro.keiro_timers
+  ADD COLUMN IF NOT EXISTS last_error TEXT;
+
+DROP INDEX IF EXISTS keiro.keiro_timers_due_idx;
+
+CREATE INDEX IF NOT EXISTS keiro_timers_due_idx
+  ON keiro.keiro_timers (status, fire_at, process_manager_name)
+  WHERE status IN ('scheduled', 'firing');
diff --git a/migrations/0005-keiro-workflow-steps.sql b/migrations/0005-keiro-workflow-steps.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0005-keiro-workflow-steps.sql
@@ -0,0 +1,21 @@
+-- The fast-lookup index of journaled workflow steps. The journal stream
+-- (wf:<name>-<id>) is the source of truth for replay; this table is a derived
+-- view kept in sync inside the same transaction as each journal append. It
+-- lets the runtime hot path and the EP-42 resume worker look up a step (or
+-- discover unfinished workflows) without rescanning the journal stream.
+--
+-- The reserved step name '__workflow_completed__' is written when a workflow
+-- finishes (see Keiro.Workflow.Types.completedStepName); its absence is how
+-- findUnfinishedWorkflowIds distinguishes an in-flight workflow from a
+-- completed one.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflow_steps (
+  workflow_id    text        NOT NULL,
+  workflow_name  text        NOT NULL,
+  step_name      text        NOT NULL,
+  result         jsonb       NOT NULL,
+  recorded_at    timestamptz NOT NULL DEFAULT now(),
+  PRIMARY KEY (workflow_id, step_name)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_workflow_steps_workflow_idx
+  ON keiro.keiro_workflow_steps (workflow_id);
diff --git a/migrations/0006-keiro-awakeables.sql b/migrations/0006-keiro-awakeables.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0006-keiro-awakeables.sql
@@ -0,0 +1,33 @@
+-- The keiro_awakeables table: durable promises an external system resolves.
+--
+-- A workflow's `awakeable` allocates a deterministic id, inserts a 'pending'
+-- row here, and suspends; an external caller later runs `signalAwakeable`,
+-- which flips the row to 'completed' (storing the payload) and appends a
+-- StepRecorded "awk:<uuid>" to the owning workflow's journal so the next
+-- runWorkflow takes the awaitStep hit path. `cancelAwakeable` flips a still
+-- 'pending' row to 'cancelled'; a subsequent run then throws.
+--
+-- The journal stream (wf:<name>-<id>) remains the source of truth for replay;
+-- this table is the external-completion handshake plus operator-visible state
+-- (a stuck 'pending' row is a workflow waiting on a callback that never came).
+CREATE TABLE IF NOT EXISTS keiro.keiro_awakeables (
+  awakeable_id        UUID PRIMARY KEY,
+  owner_workflow_name TEXT        NOT NULL,
+  owner_workflow_id   TEXT        NOT NULL,
+  status              TEXT        NOT NULL DEFAULT 'pending',
+  payload             JSONB,
+  created_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at        TIMESTAMPTZ,
+  CONSTRAINT keiro_awakeables_status_chk
+    CHECK (status IN ('pending', 'completed', 'cancelled'))
+);
+
+-- Gauge support (EP-44 keiro.workflow.awakeables.pending) and operator triage.
+CREATE INDEX IF NOT EXISTS keiro_awakeables_pending_idx
+  ON keiro.keiro_awakeables (status)
+  WHERE status = 'pending';
+
+-- Find all awakeables owned by one workflow instance (operator repair, EP-42/EP-43).
+CREATE INDEX IF NOT EXISTS keiro_awakeables_owner_idx
+  ON keiro.keiro_awakeables (owner_workflow_name, owner_workflow_id);
diff --git a/migrations/0007-keiro-workflow-children.sql b/migrations/0007-keiro-workflow-children.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0007-keiro-workflow-children.sql
@@ -0,0 +1,39 @@
+-- The keiro_workflow_children table: durable parent->child workflow links.
+--
+-- A parent workflow's `spawnChild` records the child in the parent's journal
+-- as a StepRecorded "child:<childId>" (so a replay short-circuits the spawn)
+-- and inserts a 'running' row here linking the child's (id, name) back to the
+-- parent's (id, name) plus the parent-journal step the parent awaits
+-- ("child:<childId>:result"). When the child completes, `childCompletionHook`
+-- flips the row to 'completed' (storing the child's result) and appends that
+-- await step to the parent's journal so the parent's `awaitChild` resolves.
+-- `cancelChild` flips a still-'running' row to 'cancelled' and writes a
+-- WorkflowCancelled marker to the child's journal so the child stops.
+--
+-- The journal streams (wf:<name>-<id>) remain the source of truth for replay;
+-- this table is the parent<->child relation plus operator-visible state and
+-- the discovery seed that lets the resume worker drive a zero-step child.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflow_children (
+  child_id      TEXT        NOT NULL,
+  child_name    TEXT        NOT NULL,
+  parent_id     TEXT        NOT NULL,
+  parent_name   TEXT        NOT NULL,
+  await_step    TEXT        NOT NULL,   -- "child:<childId>:result" in the parent journal
+  status        TEXT        NOT NULL DEFAULT 'running',
+  result        JSONB,
+  created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at  TIMESTAMPTZ,
+  PRIMARY KEY (child_id, child_name),
+  CONSTRAINT keiro_workflow_children_status_chk
+    CHECK (status IN ('running', 'completed', 'cancelled'))
+);
+
+-- List all children of one parent (operator inspection, awaitChild arm re-assertion).
+CREATE INDEX IF NOT EXISTS keiro_workflow_children_parent_idx
+  ON keiro.keiro_workflow_children (parent_id, parent_name);
+
+-- Discovery for the resume worker: children still running (zero-step children too).
+CREATE INDEX IF NOT EXISTS keiro_workflow_children_running_idx
+  ON keiro.keiro_workflow_children (status)
+  WHERE status = 'running';
diff --git a/migrations/0008-keiro-workflow-generation.sql b/migrations/0008-keiro-workflow-generation.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0008-keiro-workflow-generation.sql
@@ -0,0 +1,25 @@
+-- Continue-as-new (EP-48) rotates a long-running workflow onto a fresh journal
+-- *generation* so its history stays bounded. The logical identity
+-- (workflow_id, workflow_name) is stable; the generation discriminates the
+-- physical journal stream wf:<name>-<id>#<gen>. Generation 0 is the pre-rotation
+-- default, so every existing row and every never-rotating workflow is unaffected.
+ALTER TABLE keiro.keiro_workflow_steps
+  ADD COLUMN IF NOT EXISTS generation integer NOT NULL DEFAULT 0;
+
+-- Fold the generation (and workflow_name) into the key so two generations of the
+-- same logical workflow do not collide on a reserved step name (e.g. the terminal
+-- markers). Adding columns to the key is a strict relaxation: no existing row can
+-- violate the wider key. The as-shipped key is (workflow_id, step_name) — plan 47
+-- (which would have keyed on (workflow_id, workflow_name, step_name)) has NOT landed,
+-- so this migration re-keys with workflow_name AND generation in one step, doing both
+-- jobs at once. The base table's primary-key constraint is named
+-- keiro_workflow_steps_pkey (Postgres default <table>_pkey).
+ALTER TABLE keiro.keiro_workflow_steps DROP CONSTRAINT keiro_workflow_steps_pkey;
+ALTER TABLE keiro.keiro_workflow_steps
+  ADD PRIMARY KEY (workflow_id, workflow_name, generation, step_name);
+
+-- Support the current-generation lookup (MAX(generation) per id+name). Replaces the
+-- old (workflow_id)-only lookup index.
+DROP INDEX IF EXISTS keiro.keiro_workflow_steps_workflow_idx;
+CREATE INDEX IF NOT EXISTS keiro_workflow_steps_workflow_idx
+  ON keiro.keiro_workflow_steps (workflow_id, workflow_name, generation);
diff --git a/migrations/0009-keiro-subscription-shards.sql b/migrations/0009-keiro-subscription-shards.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0009-keiro-subscription-shards.sql
@@ -0,0 +1,38 @@
+-- The keiro_subscription_shards table: cooperative ownership of category
+-- subscription buckets (EP-51).
+--
+-- A "bucket" is a kiroku consumer-group member index in [0, shard_count): the
+-- stream key (originating stream_id) hashes to one bucket via
+--   (((hashtextextended(stream_id::text, 0) % shard_count) + shard_count) % shard_count)
+-- exactly as kiroku's readCategoryForwardConsumerGroupStmt does. This table does
+-- NOT re-hash anything; it records WHO owns each bucket right now, as a renewable
+-- lease. A live worker renews lease_expires_at on a heartbeat; a dead worker stops
+-- renewing, its lease expires, and another worker re-claims the bucket (failover).
+--
+-- One row per (subscription_name, bucket). owner_worker_id NULL means unowned
+-- (free to claim). The journal/checkpoints stay in kiroku's `subscriptions` table
+-- keyed (subscription_name, consumer_group_member); this table only governs
+-- assignment, never event position.
+CREATE TABLE IF NOT EXISTS keiro.keiro_subscription_shards (
+  subscription_name  TEXT        NOT NULL,
+  bucket             INT         NOT NULL,        -- kiroku consumer-group member index
+  shard_count        INT         NOT NULL,        -- N; fixed per subscription_name
+  owner_worker_id    UUID,                        -- NULL = unowned / claimable
+  lease_expires_at   TIMESTAMPTZ,                 -- NULL when unowned
+  heartbeat_at       TIMESTAMPTZ,                 -- last renewal (observability)
+  updated_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
+  PRIMARY KEY (subscription_name, bucket),
+  CONSTRAINT keiro_subscription_shards_bucket_range_chk
+    CHECK (bucket >= 0 AND bucket < shard_count),
+  CONSTRAINT keiro_subscription_shards_count_chk
+    CHECK (shard_count >= 1)
+);
+
+-- Fast lookup of an owner's currently-held buckets (renew path) and of
+-- claimable buckets (claim path filters on lease_expires_at).
+CREATE INDEX IF NOT EXISTS keiro_subscription_shards_owner_idx
+  ON keiro.keiro_subscription_shards (subscription_name, owner_worker_id);
+
+-- Find expired/unowned buckets cheaply during a claim sweep.
+CREATE INDEX IF NOT EXISTS keiro_subscription_shards_lease_idx
+  ON keiro.keiro_subscription_shards (subscription_name, lease_expires_at);
diff --git a/migrations/0010-keiro-messaging-crash-recovery.sql b/migrations/0010-keiro-messaging-crash-recovery.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0010-keiro-messaging-crash-recovery.sql
@@ -0,0 +1,21 @@
+-- messaging crash recovery
+
+-- H5: per-message failure accounting for the inbox poison-message path.
+ALTER TABLE keiro.keiro_inbox
+  ADD COLUMN IF NOT EXISTS attempt_count BIGINT NOT NULL DEFAULT 0;
+
+-- H3: lets the backlog gauge count rows without a sequential scan.
+CREATE INDEX IF NOT EXISTS keiro_inbox_backlog_idx
+  ON keiro.keiro_inbox (status)
+  WHERE status IN ('processing', 'failed');
+
+-- H4: lets garbageCollectSent find expired sent rows without a sequential scan.
+CREATE INDEX IF NOT EXISTS keiro_outbox_sent_gc_idx
+  ON keiro.keiro_outbox (published_at)
+  WHERE status = 'sent';
+
+-- L8: supports the PerSourceStream head-of-line predicate (any key, including
+-- NULL); the existing keiro_outbox_head_of_line_idx excludes NULL keys.
+CREATE INDEX IF NOT EXISTS keiro_outbox_source_order_idx
+  ON keiro.keiro_outbox (source, created_at, outbox_id)
+  WHERE status NOT IN ('sent', 'dead');
diff --git a/migrations/0011-keiro-workflows-instances.sql b/migrations/0011-keiro-workflows-instances.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0011-keiro-workflows-instances.sql
@@ -0,0 +1,76 @@
+-- Workflow instance state used by the resume worker and by later discovery /
+-- pruning work. The journal remains the source of truth; this table is a
+-- transactional summary maintained beside journal-index writes.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflows (
+  workflow_id      TEXT        NOT NULL,
+  workflow_name    TEXT        NOT NULL,
+  generation       INTEGER     NOT NULL DEFAULT 0,
+  status           TEXT        NOT NULL DEFAULT 'running',
+  attempts         INTEGER     NOT NULL DEFAULT 0,
+  last_error       TEXT,
+  next_attempt_at  TIMESTAMPTZ,
+  leased_by        TEXT,
+  lease_expires_at TIMESTAMPTZ,
+  created_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at     TIMESTAMPTZ,
+  PRIMARY KEY (workflow_id, workflow_name),
+  CONSTRAINT keiro_workflows_status_chk
+    CHECK (status IN ('running', 'suspended', 'completed', 'cancelled', 'failed'))
+);
+
+CREATE INDEX IF NOT EXISTS keiro_workflows_active_idx
+  ON keiro.keiro_workflows (status)
+  WHERE status IN ('running', 'suspended');
+
+WITH current_gen AS (
+  SELECT workflow_id, workflow_name, MAX(generation) AS generation
+  FROM keiro.keiro_workflow_steps
+  GROUP BY workflow_id, workflow_name
+),
+terminal AS (
+  SELECT
+    cg.workflow_id,
+    cg.workflow_name,
+    cg.generation,
+    CASE
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_completed__'
+      ) THEN 'completed'
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_cancelled__'
+      ) THEN 'cancelled'
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_failed__'
+      ) THEN 'failed'
+      ELSE 'running'
+    END AS status
+  FROM current_gen cg
+)
+INSERT INTO keiro.keiro_workflows (workflow_id, workflow_name, generation, status, completed_at)
+SELECT workflow_id, workflow_name, generation, status,
+       CASE WHEN status IN ('completed', 'cancelled', 'failed') THEN now() ELSE NULL END
+FROM terminal
+ON CONFLICT (workflow_id, workflow_name) DO NOTHING;
+
+INSERT INTO keiro.keiro_workflows (workflow_id, workflow_name, generation, status)
+SELECT child_id, child_name, 0, 'running'
+FROM keiro.keiro_workflow_children
+WHERE status = 'running'
+ON CONFLICT (workflow_id, workflow_name) DO NOTHING;
+
+ALTER TABLE keiro.keiro_workflow_children DROP CONSTRAINT IF EXISTS keiro_workflow_children_status_chk;
+ALTER TABLE keiro.keiro_workflow_children ADD CONSTRAINT keiro_workflow_children_status_chk
+  CHECK (status IN ('running', 'completed', 'cancelled', 'failed'));
diff --git a/migrations/0012-keiro-workflow-gc-index.sql b/migrations/0012-keiro-workflow-gc-index.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0012-keiro-workflow-gc-index.sql
@@ -0,0 +1,3 @@
+-- GC eligibility scan: terminal instances ordered by terminal age.
+CREATE INDEX IF NOT EXISTS keiro_workflows_gc_idx
+  ON keiro.keiro_workflows (status, completed_at);
diff --git a/migrations/0013-keiro-workflows-wake-after.sql b/migrations/0013-keiro-workflows-wake-after.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0013-keiro-workflows-wake-after.sql
@@ -0,0 +1,3 @@
+-- Self-expiring resume hint for workflows parked only on a sleep timer.
+ALTER TABLE keiro.keiro_workflows
+  ADD COLUMN IF NOT EXISTS wake_after TIMESTAMPTZ;
diff --git a/migrations/0014-keiro-projection-dedup.sql b/migrations/0014-keiro-projection-dedup.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0014-keiro-projection-dedup.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_projection_dedup (
+  projection_name TEXT        NOT NULL,
+  event_id        UUID        NOT NULL,
+  applied_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
+  PRIMARY KEY (projection_name, event_id)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_projection_dedup_applied_at_idx
+  ON keiro.keiro_projection_dedup (applied_at);
diff --git a/migrations/0015-keiro-outbox-claim-order-index.sql b/migrations/0015-keiro-outbox-claim-order-index.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0015-keiro-outbox-claim-order-index.sql
@@ -0,0 +1,6 @@
+-- Serves the outbox claim query's candidate scan: ordered walk by
+-- (created_at, outbox_id) over claimable rows, stopping at the batch limit.
+-- next_attempt_at is a residual filter, usually already satisfied for pending rows.
+CREATE INDEX IF NOT EXISTS keiro_outbox_claim_order_idx
+  ON keiro.keiro_outbox (created_at, outbox_id)
+  WHERE status IN ('pending', 'failed');
diff --git a/migrations/0016-keiro-inbox-drop-received-idx.sql b/migrations/0016-keiro-inbox-drop-received-idx.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0016-keiro-inbox-drop-received-idx.sql
@@ -0,0 +1,4 @@
+-- keiro_inbox_received_idx served only the listInbox test helper's ordering
+-- but was maintained on every consumed message. Retention GC uses
+-- keiro_inbox_completed_idx; the backlog gauge uses keiro_inbox_backlog_idx.
+DROP INDEX IF EXISTS keiro.keiro_inbox_received_idx;
diff --git a/migrations/0017-schema-management-comment.sql b/migrations/0017-schema-management-comment.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0017-schema-management-comment.sql
@@ -0,0 +1,2 @@
+COMMENT ON SCHEMA keiro IS
+  'Managed by pg-migrate component keiro through 0017-schema-management-comment';
diff --git a/migrations/0018.sql b/migrations/0018.sql
new file mode 100644
--- /dev/null
+++ b/migrations/0018.sql
@@ -0,0 +1,28 @@
+-- keiro dead letters
+
+-- Durable records for process-manager and router dispatches that reached a
+-- target stream but were rejected. These rows are distinct from
+-- kiroku.dead_letters: the source subscription event is successfully handled
+-- and checkpointed after every rejected dispatch has been recorded here.
+CREATE TABLE IF NOT EXISTS keiro.keiro_dead_letters (
+  dead_letter_id          BIGSERIAL   PRIMARY KEY,
+  dispatcher_kind         TEXT        NOT NULL,
+  dispatcher_name         TEXT        NOT NULL,
+  correlation_id          TEXT        NOT NULL,
+  source_event_id         UUID        NOT NULL,
+  source_global_position  BIGINT      NOT NULL,
+  emit_index              INT         NOT NULL,
+  target_stream_name      TEXT        NOT NULL,
+  error_class             TEXT        NOT NULL,
+  error_detail            TEXT        NOT NULL,
+  attempt_count           INT         NOT NULL,
+  created_at              TIMESTAMPTZ NOT NULL DEFAULT now(),
+  CONSTRAINT keiro_dead_letters_dispatcher_kind_chk
+    CHECK (dispatcher_kind IN ('process-manager', 'router')),
+  CONSTRAINT keiro_dead_letters_attempt_count_chk
+    CHECK (attempt_count >= 1),
+  UNIQUE (dispatcher_name, source_event_id, emit_index)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_dead_letters_dispatcher_created_at_idx
+  ON keiro.keiro_dead_letters (dispatcher_name, created_at DESC);
diff --git a/migrations/manifest b/migrations/manifest
new file mode 100644
--- /dev/null
+++ b/migrations/manifest
@@ -0,0 +1,18 @@
+0001-keiro-bootstrap.sql
+0002-keiro-outbox.sql
+0003-keiro-inbox.sql
+0004-keiro-timer-recovery.sql
+0005-keiro-workflow-steps.sql
+0006-keiro-awakeables.sql
+0007-keiro-workflow-children.sql
+0008-keiro-workflow-generation.sql
+0009-keiro-subscription-shards.sql
+0010-keiro-messaging-crash-recovery.sql
+0011-keiro-workflows-instances.sql
+0012-keiro-workflow-gc-index.sql
+0013-keiro-workflows-wake-after.sql
+0014-keiro-projection-dedup.sql
+0015-keiro-outbox-claim-order-index.sql
+0016-keiro-inbox-drop-received-idx.sql
+0017-schema-management-comment.sql
+0018.sql
diff --git a/sql-migrations/2026-05-17-13-58-15-keiro-bootstrap.sql b/sql-migrations/2026-05-17-13-58-15-keiro-bootstrap.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-05-17-13-58-15-keiro-bootstrap.sql
@@ -0,0 +1,44 @@
+-- Keiro framework bootstrap migration (codd).
+--
+-- Keiro owns the dedicated `keiro` schema. codd applies this file in its own
+-- session, so the schema is created here and every object is written fully
+-- qualified as keiro.<name> — no session search-path pin is used anywhere.
+CREATE SCHEMA IF NOT EXISTS keiro;
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_snapshots (
+  stream_id BIGINT PRIMARY KEY,
+  stream_version BIGINT NOT NULL,
+  state JSONB NOT NULL,
+  state_codec_version BIGINT NOT NULL,
+  regfile_shape_hash TEXT NOT NULL,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS keiro_snapshots_compat_idx
+  ON keiro.keiro_snapshots (stream_id, state_codec_version, regfile_shape_hash, stream_version DESC);
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_read_models (
+  name TEXT PRIMARY KEY,
+  version BIGINT NOT NULL,
+  shape_hash TEXT NOT NULL,
+  last_built_at TIMESTAMPTZ,
+  status TEXT NOT NULL,
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE TABLE IF NOT EXISTS keiro.keiro_timers (
+  timer_id UUID PRIMARY KEY,
+  process_manager_name TEXT NOT NULL,
+  correlation_id TEXT NOT NULL,
+  fire_at TIMESTAMPTZ NOT NULL,
+  payload JSONB NOT NULL,
+  status TEXT NOT NULL DEFAULT 'scheduled',
+  attempts BIGINT NOT NULL DEFAULT 0,
+  fired_event_id UUID,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
+
+CREATE INDEX IF NOT EXISTS keiro_timers_due_idx
+  ON keiro.keiro_timers (status, fire_at, process_manager_name);
diff --git a/sql-migrations/2026-05-19-12-55-02-keiro-outbox.sql b/sql-migrations/2026-05-19-12-55-02-keiro-outbox.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-05-19-12-55-02-keiro-outbox.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_outbox (
+  outbox_id UUID PRIMARY KEY,
+  message_id TEXT NOT NULL,
+  source TEXT NOT NULL,
+  destination TEXT NOT NULL,
+  message_key TEXT,
+  event_type TEXT NOT NULL,
+  schema_version BIGINT NOT NULL,
+  content_type TEXT NOT NULL,
+  schema_registry TEXT,
+  schema_subject TEXT,
+  schema_version_ref BIGINT,
+  schema_id BIGINT,
+  schema_fingerprint TEXT,
+  source_event_id UUID,
+  source_global_position BIGINT,
+  causation_id UUID,
+  correlation_id UUID,
+  traceparent TEXT,
+  tracestate TEXT,
+  payload_bytes BYTEA NOT NULL,
+  attributes JSONB,
+  occurred_at TIMESTAMPTZ NOT NULL,
+  status TEXT NOT NULL DEFAULT 'pending',
+  attempt_count BIGINT NOT NULL DEFAULT 0,
+  next_attempt_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  last_error TEXT,
+  published_at TIMESTAMPTZ,
+  created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  UNIQUE (source, message_id)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_outbox_pending_idx
+  ON keiro.keiro_outbox (status, next_attempt_at, created_at);
+
+CREATE INDEX IF NOT EXISTS keiro_outbox_head_of_line_idx
+  ON keiro.keiro_outbox (source, message_key, created_at)
+  WHERE status NOT IN ('sent', 'dead') AND message_key IS NOT NULL;
diff --git a/sql-migrations/2026-05-19-13-05-23-keiro-inbox.sql b/sql-migrations/2026-05-19-13-05-23-keiro-inbox.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-05-19-13-05-23-keiro-inbox.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_inbox (
+  source TEXT NOT NULL,
+  dedupe_key TEXT NOT NULL,
+  message_id TEXT,
+  source_event_id UUID,
+  source_global_position BIGINT,
+  destination TEXT,
+  event_type TEXT,
+  schema_version BIGINT,
+  content_type TEXT NOT NULL,
+  schema_registry TEXT,
+  schema_subject TEXT,
+  schema_version_ref BIGINT,
+  schema_id BIGINT,
+  schema_fingerprint TEXT,
+  causation_id UUID,
+  correlation_id UUID,
+  traceparent TEXT,
+  tracestate TEXT,
+  kafka_topic TEXT,
+  kafka_partition BIGINT,
+  kafka_offset BIGINT,
+  payload_bytes BYTEA NOT NULL,
+  attributes JSONB,
+  occurred_at TIMESTAMPTZ,
+  status TEXT NOT NULL DEFAULT 'processing',
+  received_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at TIMESTAMPTZ,
+  failed_at TIMESTAMPTZ,
+  last_error TEXT,
+  PRIMARY KEY (source, dedupe_key)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_inbox_received_idx
+  ON keiro.keiro_inbox (received_at);
+
+CREATE INDEX IF NOT EXISTS keiro_inbox_completed_idx
+  ON keiro.keiro_inbox (completed_at)
+  WHERE status = 'completed';
diff --git a/sql-migrations/2026-06-03-05-14-28-keiro-timer-recovery.sql b/sql-migrations/2026-06-03-05-14-28-keiro-timer-recovery.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-03-05-14-28-keiro-timer-recovery.sql
@@ -0,0 +1,8 @@
+ALTER TABLE keiro.keiro_timers
+  ADD COLUMN IF NOT EXISTS last_error TEXT;
+
+DROP INDEX IF EXISTS keiro.keiro_timers_due_idx;
+
+CREATE INDEX IF NOT EXISTS keiro_timers_due_idx
+  ON keiro.keiro_timers (status, fire_at, process_manager_name)
+  WHERE status IN ('scheduled', 'firing');
diff --git a/sql-migrations/2026-06-03-16-10-05-keiro-workflow-steps.sql b/sql-migrations/2026-06-03-16-10-05-keiro-workflow-steps.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-03-16-10-05-keiro-workflow-steps.sql
@@ -0,0 +1,21 @@
+-- The fast-lookup index of journaled workflow steps. The journal stream
+-- (wf:<name>-<id>) is the source of truth for replay; this table is a derived
+-- view kept in sync inside the same transaction as each journal append. It
+-- lets the runtime hot path and the EP-42 resume worker look up a step (or
+-- discover unfinished workflows) without rescanning the journal stream.
+--
+-- The reserved step name '__workflow_completed__' is written when a workflow
+-- finishes (see Keiro.Workflow.Types.completedStepName); its absence is how
+-- findUnfinishedWorkflowIds distinguishes an in-flight workflow from a
+-- completed one.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflow_steps (
+  workflow_id    text        NOT NULL,
+  workflow_name  text        NOT NULL,
+  step_name      text        NOT NULL,
+  result         jsonb       NOT NULL,
+  recorded_at    timestamptz NOT NULL DEFAULT now(),
+  PRIMARY KEY (workflow_id, step_name)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_workflow_steps_workflow_idx
+  ON keiro.keiro_workflow_steps (workflow_id);
diff --git a/sql-migrations/2026-06-03-18-19-41-keiro-awakeables.sql b/sql-migrations/2026-06-03-18-19-41-keiro-awakeables.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-03-18-19-41-keiro-awakeables.sql
@@ -0,0 +1,33 @@
+-- The keiro_awakeables table: durable promises an external system resolves.
+--
+-- A workflow's `awakeable` allocates a deterministic id, inserts a 'pending'
+-- row here, and suspends; an external caller later runs `signalAwakeable`,
+-- which flips the row to 'completed' (storing the payload) and appends a
+-- StepRecorded "awk:<uuid>" to the owning workflow's journal so the next
+-- runWorkflow takes the awaitStep hit path. `cancelAwakeable` flips a still
+-- 'pending' row to 'cancelled'; a subsequent run then throws.
+--
+-- The journal stream (wf:<name>-<id>) remains the source of truth for replay;
+-- this table is the external-completion handshake plus operator-visible state
+-- (a stuck 'pending' row is a workflow waiting on a callback that never came).
+CREATE TABLE IF NOT EXISTS keiro.keiro_awakeables (
+  awakeable_id        UUID PRIMARY KEY,
+  owner_workflow_name TEXT        NOT NULL,
+  owner_workflow_id   TEXT        NOT NULL,
+  status              TEXT        NOT NULL DEFAULT 'pending',
+  payload             JSONB,
+  created_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at          TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at        TIMESTAMPTZ,
+  CONSTRAINT keiro_awakeables_status_chk
+    CHECK (status IN ('pending', 'completed', 'cancelled'))
+);
+
+-- Gauge support (EP-44 keiro.workflow.awakeables.pending) and operator triage.
+CREATE INDEX IF NOT EXISTS keiro_awakeables_pending_idx
+  ON keiro.keiro_awakeables (status)
+  WHERE status = 'pending';
+
+-- Find all awakeables owned by one workflow instance (operator repair, EP-42/EP-43).
+CREATE INDEX IF NOT EXISTS keiro_awakeables_owner_idx
+  ON keiro.keiro_awakeables (owner_workflow_name, owner_workflow_id);
diff --git a/sql-migrations/2026-06-03-19-49-23-keiro-workflow-children.sql b/sql-migrations/2026-06-03-19-49-23-keiro-workflow-children.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-03-19-49-23-keiro-workflow-children.sql
@@ -0,0 +1,39 @@
+-- The keiro_workflow_children table: durable parent->child workflow links.
+--
+-- A parent workflow's `spawnChild` records the child in the parent's journal
+-- as a StepRecorded "child:<childId>" (so a replay short-circuits the spawn)
+-- and inserts a 'running' row here linking the child's (id, name) back to the
+-- parent's (id, name) plus the parent-journal step the parent awaits
+-- ("child:<childId>:result"). When the child completes, `childCompletionHook`
+-- flips the row to 'completed' (storing the child's result) and appends that
+-- await step to the parent's journal so the parent's `awaitChild` resolves.
+-- `cancelChild` flips a still-'running' row to 'cancelled' and writes a
+-- WorkflowCancelled marker to the child's journal so the child stops.
+--
+-- The journal streams (wf:<name>-<id>) remain the source of truth for replay;
+-- this table is the parent<->child relation plus operator-visible state and
+-- the discovery seed that lets the resume worker drive a zero-step child.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflow_children (
+  child_id      TEXT        NOT NULL,
+  child_name    TEXT        NOT NULL,
+  parent_id     TEXT        NOT NULL,
+  parent_name   TEXT        NOT NULL,
+  await_step    TEXT        NOT NULL,   -- "child:<childId>:result" in the parent journal
+  status        TEXT        NOT NULL DEFAULT 'running',
+  result        JSONB,
+  created_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at    TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at  TIMESTAMPTZ,
+  PRIMARY KEY (child_id, child_name),
+  CONSTRAINT keiro_workflow_children_status_chk
+    CHECK (status IN ('running', 'completed', 'cancelled'))
+);
+
+-- List all children of one parent (operator inspection, awaitChild arm re-assertion).
+CREATE INDEX IF NOT EXISTS keiro_workflow_children_parent_idx
+  ON keiro.keiro_workflow_children (parent_id, parent_name);
+
+-- Discovery for the resume worker: children still running (zero-step children too).
+CREATE INDEX IF NOT EXISTS keiro_workflow_children_running_idx
+  ON keiro.keiro_workflow_children (status)
+  WHERE status = 'running';
diff --git a/sql-migrations/2026-06-04-02-12-28-keiro-workflow-generation.sql b/sql-migrations/2026-06-04-02-12-28-keiro-workflow-generation.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-04-02-12-28-keiro-workflow-generation.sql
@@ -0,0 +1,25 @@
+-- Continue-as-new (EP-48) rotates a long-running workflow onto a fresh journal
+-- *generation* so its history stays bounded. The logical identity
+-- (workflow_id, workflow_name) is stable; the generation discriminates the
+-- physical journal stream wf:<name>-<id>#<gen>. Generation 0 is the pre-rotation
+-- default, so every existing row and every never-rotating workflow is unaffected.
+ALTER TABLE keiro.keiro_workflow_steps
+  ADD COLUMN IF NOT EXISTS generation integer NOT NULL DEFAULT 0;
+
+-- Fold the generation (and workflow_name) into the key so two generations of the
+-- same logical workflow do not collide on a reserved step name (e.g. the terminal
+-- markers). Adding columns to the key is a strict relaxation: no existing row can
+-- violate the wider key. The as-shipped key is (workflow_id, step_name) — plan 47
+-- (which would have keyed on (workflow_id, workflow_name, step_name)) has NOT landed,
+-- so this migration re-keys with workflow_name AND generation in one step, doing both
+-- jobs at once. The base table's primary-key constraint is named
+-- keiro_workflow_steps_pkey (Postgres default <table>_pkey).
+ALTER TABLE keiro.keiro_workflow_steps DROP CONSTRAINT keiro_workflow_steps_pkey;
+ALTER TABLE keiro.keiro_workflow_steps
+  ADD PRIMARY KEY (workflow_id, workflow_name, generation, step_name);
+
+-- Support the current-generation lookup (MAX(generation) per id+name). Replaces the
+-- old (workflow_id)-only lookup index.
+DROP INDEX IF EXISTS keiro.keiro_workflow_steps_workflow_idx;
+CREATE INDEX IF NOT EXISTS keiro_workflow_steps_workflow_idx
+  ON keiro.keiro_workflow_steps (workflow_id, workflow_name, generation);
diff --git a/sql-migrations/2026-06-04-03-53-34-keiro-subscription-shards.sql b/sql-migrations/2026-06-04-03-53-34-keiro-subscription-shards.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-04-03-53-34-keiro-subscription-shards.sql
@@ -0,0 +1,38 @@
+-- The keiro_subscription_shards table: cooperative ownership of category
+-- subscription buckets (EP-51).
+--
+-- A "bucket" is a kiroku consumer-group member index in [0, shard_count): the
+-- stream key (originating stream_id) hashes to one bucket via
+--   (((hashtextextended(stream_id::text, 0) % shard_count) + shard_count) % shard_count)
+-- exactly as kiroku's readCategoryForwardConsumerGroupStmt does. This table does
+-- NOT re-hash anything; it records WHO owns each bucket right now, as a renewable
+-- lease. A live worker renews lease_expires_at on a heartbeat; a dead worker stops
+-- renewing, its lease expires, and another worker re-claims the bucket (failover).
+--
+-- One row per (subscription_name, bucket). owner_worker_id NULL means unowned
+-- (free to claim). The journal/checkpoints stay in kiroku's `subscriptions` table
+-- keyed (subscription_name, consumer_group_member); this table only governs
+-- assignment, never event position.
+CREATE TABLE IF NOT EXISTS keiro.keiro_subscription_shards (
+  subscription_name  TEXT        NOT NULL,
+  bucket             INT         NOT NULL,        -- kiroku consumer-group member index
+  shard_count        INT         NOT NULL,        -- N; fixed per subscription_name
+  owner_worker_id    UUID,                        -- NULL = unowned / claimable
+  lease_expires_at   TIMESTAMPTZ,                 -- NULL when unowned
+  heartbeat_at       TIMESTAMPTZ,                 -- last renewal (observability)
+  updated_at         TIMESTAMPTZ NOT NULL DEFAULT now(),
+  PRIMARY KEY (subscription_name, bucket),
+  CONSTRAINT keiro_subscription_shards_bucket_range_chk
+    CHECK (bucket >= 0 AND bucket < shard_count),
+  CONSTRAINT keiro_subscription_shards_count_chk
+    CHECK (shard_count >= 1)
+);
+
+-- Fast lookup of an owner's currently-held buckets (renew path) and of
+-- claimable buckets (claim path filters on lease_expires_at).
+CREATE INDEX IF NOT EXISTS keiro_subscription_shards_owner_idx
+  ON keiro.keiro_subscription_shards (subscription_name, owner_worker_id);
+
+-- Find expired/unowned buckets cheaply during a claim sweep.
+CREATE INDEX IF NOT EXISTS keiro_subscription_shards_lease_idx
+  ON keiro.keiro_subscription_shards (subscription_name, lease_expires_at);
diff --git a/sql-migrations/2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql b/sql-migrations/2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql
@@ -0,0 +1,21 @@
+-- messaging crash recovery
+
+-- H5: per-message failure accounting for the inbox poison-message path.
+ALTER TABLE keiro.keiro_inbox
+  ADD COLUMN IF NOT EXISTS attempt_count BIGINT NOT NULL DEFAULT 0;
+
+-- H3: lets the backlog gauge count rows without a sequential scan.
+CREATE INDEX IF NOT EXISTS keiro_inbox_backlog_idx
+  ON keiro.keiro_inbox (status)
+  WHERE status IN ('processing', 'failed');
+
+-- H4: lets garbageCollectSent find expired sent rows without a sequential scan.
+CREATE INDEX IF NOT EXISTS keiro_outbox_sent_gc_idx
+  ON keiro.keiro_outbox (published_at)
+  WHERE status = 'sent';
+
+-- L8: supports the PerSourceStream head-of-line predicate (any key, including
+-- NULL); the existing keiro_outbox_head_of_line_idx excludes NULL keys.
+CREATE INDEX IF NOT EXISTS keiro_outbox_source_order_idx
+  ON keiro.keiro_outbox (source, created_at, outbox_id)
+  WHERE status NOT IN ('sent', 'dead');
diff --git a/sql-migrations/2026-06-15-15-07-25-keiro-workflows-instances.sql b/sql-migrations/2026-06-15-15-07-25-keiro-workflows-instances.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-15-15-07-25-keiro-workflows-instances.sql
@@ -0,0 +1,76 @@
+-- Workflow instance state used by the resume worker and by later discovery /
+-- pruning work. The journal remains the source of truth; this table is a
+-- transactional summary maintained beside journal-index writes.
+CREATE TABLE IF NOT EXISTS keiro.keiro_workflows (
+  workflow_id      TEXT        NOT NULL,
+  workflow_name    TEXT        NOT NULL,
+  generation       INTEGER     NOT NULL DEFAULT 0,
+  status           TEXT        NOT NULL DEFAULT 'running',
+  attempts         INTEGER     NOT NULL DEFAULT 0,
+  last_error       TEXT,
+  next_attempt_at  TIMESTAMPTZ,
+  leased_by        TEXT,
+  lease_expires_at TIMESTAMPTZ,
+  created_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
+  updated_at       TIMESTAMPTZ NOT NULL DEFAULT now(),
+  completed_at     TIMESTAMPTZ,
+  PRIMARY KEY (workflow_id, workflow_name),
+  CONSTRAINT keiro_workflows_status_chk
+    CHECK (status IN ('running', 'suspended', 'completed', 'cancelled', 'failed'))
+);
+
+CREATE INDEX IF NOT EXISTS keiro_workflows_active_idx
+  ON keiro.keiro_workflows (status)
+  WHERE status IN ('running', 'suspended');
+
+WITH current_gen AS (
+  SELECT workflow_id, workflow_name, MAX(generation) AS generation
+  FROM keiro.keiro_workflow_steps
+  GROUP BY workflow_id, workflow_name
+),
+terminal AS (
+  SELECT
+    cg.workflow_id,
+    cg.workflow_name,
+    cg.generation,
+    CASE
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_completed__'
+      ) THEN 'completed'
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_cancelled__'
+      ) THEN 'cancelled'
+      WHEN EXISTS (
+        SELECT 1 FROM keiro.keiro_workflow_steps s
+        WHERE s.workflow_id = cg.workflow_id
+          AND s.workflow_name = cg.workflow_name
+          AND s.generation = cg.generation
+          AND s.step_name = '__workflow_failed__'
+      ) THEN 'failed'
+      ELSE 'running'
+    END AS status
+  FROM current_gen cg
+)
+INSERT INTO keiro.keiro_workflows (workflow_id, workflow_name, generation, status, completed_at)
+SELECT workflow_id, workflow_name, generation, status,
+       CASE WHEN status IN ('completed', 'cancelled', 'failed') THEN now() ELSE NULL END
+FROM terminal
+ON CONFLICT (workflow_id, workflow_name) DO NOTHING;
+
+INSERT INTO keiro.keiro_workflows (workflow_id, workflow_name, generation, status)
+SELECT child_id, child_name, 0, 'running'
+FROM keiro.keiro_workflow_children
+WHERE status = 'running'
+ON CONFLICT (workflow_id, workflow_name) DO NOTHING;
+
+ALTER TABLE keiro.keiro_workflow_children DROP CONSTRAINT IF EXISTS keiro_workflow_children_status_chk;
+ALTER TABLE keiro.keiro_workflow_children ADD CONSTRAINT keiro_workflow_children_status_chk
+  CHECK (status IN ('running', 'completed', 'cancelled', 'failed'));
diff --git a/sql-migrations/2026-06-15-17-53-48-keiro-workflow-gc-index.sql b/sql-migrations/2026-06-15-17-53-48-keiro-workflow-gc-index.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-15-17-53-48-keiro-workflow-gc-index.sql
@@ -0,0 +1,3 @@
+-- GC eligibility scan: terminal instances ordered by terminal age.
+CREATE INDEX IF NOT EXISTS keiro_workflows_gc_idx
+  ON keiro.keiro_workflows (status, completed_at);
diff --git a/sql-migrations/2026-06-15-18-01-33-keiro-workflows-wake-after.sql b/sql-migrations/2026-06-15-18-01-33-keiro-workflows-wake-after.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-15-18-01-33-keiro-workflows-wake-after.sql
@@ -0,0 +1,3 @@
+-- Self-expiring resume hint for workflows parked only on a sleep timer.
+ALTER TABLE keiro.keiro_workflows
+  ADD COLUMN IF NOT EXISTS wake_after TIMESTAMPTZ;
diff --git a/sql-migrations/2026-06-15-21-49-37-keiro-projection-dedup.sql b/sql-migrations/2026-06-15-21-49-37-keiro-projection-dedup.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-06-15-21-49-37-keiro-projection-dedup.sql
@@ -0,0 +1,9 @@
+CREATE TABLE IF NOT EXISTS keiro.keiro_projection_dedup (
+  projection_name TEXT        NOT NULL,
+  event_id        UUID        NOT NULL,
+  applied_at      TIMESTAMPTZ NOT NULL DEFAULT now(),
+  PRIMARY KEY (projection_name, event_id)
+);
+
+CREATE INDEX IF NOT EXISTS keiro_projection_dedup_applied_at_idx
+  ON keiro.keiro_projection_dedup (applied_at);
diff --git a/sql-migrations/2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql b/sql-migrations/2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql
@@ -0,0 +1,6 @@
+-- Serves the outbox claim query's candidate scan: ordered walk by
+-- (created_at, outbox_id) over claimable rows, stopping at the batch limit.
+-- next_attempt_at is a residual filter, usually already satisfied for pending rows.
+CREATE INDEX IF NOT EXISTS keiro_outbox_claim_order_idx
+  ON keiro.keiro_outbox (created_at, outbox_id)
+  WHERE status IN ('pending', 'failed');
diff --git a/sql-migrations/2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql b/sql-migrations/2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql
new file mode 100644
--- /dev/null
+++ b/sql-migrations/2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql
@@ -0,0 +1,4 @@
+-- keiro_inbox_received_idx served only the listInbox test helper's ordering
+-- but was maintained on every consumed message. Retention GC uses
+-- keiro_inbox_completed_idx; the backlog gauge uses keiro_inbox_backlog_idx.
+DROP INDEX IF EXISTS keiro.keiro_inbox_received_idx;
diff --git a/src/Keiro/Migrations.hs b/src/Keiro/Migrations.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations.hs
@@ -0,0 +1,31 @@
+module Keiro.Migrations (
+    DefinitionError,
+    MigrationComponent,
+    MigrationPlan,
+    PlanError,
+    frameworkMigrationPlan,
+    keiroMigrations,
+) where
+
+import Data.List.NonEmpty (NonEmpty (..))
+import Database.PostgreSQL.Migrate (
+    DefinitionError,
+    MigrationComponent,
+    MigrationPlan,
+    PlanError,
+    migrationPlan,
+ )
+import Keiro.Migrations.Internal.Definition (keiroMigrations)
+
+{- | Compose the concrete Kiroku and Keiro components in dependency order.
+
+The first argument must be Kiroku's component and the second must be Keiro's.
+The native planner validates both identity and ordering, so swapped or unrelated
+components fail with a structured 'PlanError'.
+-}
+frameworkMigrationPlan ::
+    MigrationComponent ->
+    MigrationComponent ->
+    Either PlanError MigrationPlan
+frameworkMigrationPlan kiroku keiro =
+    migrationPlan (kiroku :| [keiro])
diff --git a/src/Keiro/Migrations/ExpectedSchema.hs b/src/Keiro/Migrations/ExpectedSchema.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/ExpectedSchema.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Keiro.Migrations.ExpectedSchema (
+    expectedSchemaFiles,
+    withMaterializedExpectedSchema,
+) where
+
+import Codd.Extras.ExpectedSchema qualified as ExpectedSchema
+import Data.ByteString (ByteString)
+import Data.FileEmbed (embedDir)
+
+-- Embedded expected schema (touch this comment after regenerating expected-schema
+-- so Template Haskell refreshes the executable snapshot).
+expectedSchemaFiles :: [(FilePath, ByteString)]
+expectedSchemaFiles = $(embedDir "expected-schema")
+
+withMaterializedExpectedSchema :: (FilePath -> IO a) -> IO a
+withMaterializedExpectedSchema action =
+    ExpectedSchema.withMaterializedExpectedSchema "keiro-expected-schema" expectedSchemaFiles action
diff --git a/src/Keiro/Migrations/History/Codd.hs b/src/Keiro/Migrations/History/Codd.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/History/Codd.hs
@@ -0,0 +1,127 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Keiro.Migrations.History.Codd (
+    frameworkCoddHistoryMappings,
+    frameworkCoddSourceConfig,
+    keiroCoddHistoryMappings,
+    keiroCoddManifestText,
+    keiroCoddSourcePayloads,
+    keiroLegacyMigrationNames,
+) where
+
+import Data.Bifunctor (first)
+import Data.ByteString (ByteString)
+import Data.Foldable (toList)
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.Map.Strict qualified as Map
+import Data.Text (Text)
+import Database.PostgreSQL.Migrate (
+    Confirmation,
+    ConnectionProvider,
+    EvidenceRequirement (Evidence),
+    HistoryMapping,
+    PayloadRelation (SamePayload),
+    historyMapping,
+    migrationId,
+ )
+import Database.PostgreSQL.Migrate.History.Codd (
+    CoddDefinitionError,
+    CoddSourceConfig,
+    coddEvidenceKey,
+    coddSourceConfig,
+    parseCoddManifest,
+ )
+import Keiro.Migrations.Internal.Definition (embeddedMigrationEntries)
+import Keiro.Migrations.Internal.EmbedFile (embedTextFile)
+import Kiroku.Store.Migrations.History.Codd qualified as Kiroku
+
+keiroLegacyMigrationNames :: NonEmpty FilePath
+keiroLegacyMigrationNames =
+    "2026-05-17-13-58-15-keiro-bootstrap.sql"
+        :| [ "2026-05-19-12-55-02-keiro-outbox.sql"
+           , "2026-05-19-13-05-23-keiro-inbox.sql"
+           , "2026-06-03-05-14-28-keiro-timer-recovery.sql"
+           , "2026-06-03-16-10-05-keiro-workflow-steps.sql"
+           , "2026-06-03-18-19-41-keiro-awakeables.sql"
+           , "2026-06-03-19-49-23-keiro-workflow-children.sql"
+           , "2026-06-04-02-12-28-keiro-workflow-generation.sql"
+           , "2026-06-04-03-53-34-keiro-subscription-shards.sql"
+           , "2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql"
+           , "2026-06-15-15-07-25-keiro-workflows-instances.sql"
+           , "2026-06-15-17-53-48-keiro-workflow-gc-index.sql"
+           , "2026-06-15-18-01-33-keiro-workflows-wake-after.sql"
+           , "2026-06-15-21-49-37-keiro-projection-dedup.sql"
+           , "2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql"
+           , "2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql"
+           ]
+
+keiroCoddHistoryMappings :: NonEmpty HistoryMapping
+keiroCoddHistoryMappings =
+    zipWithNonEmpty mapping keiroLegacyMigrationNames nativeMigrationNames
+  where
+    mapping sourceFilename targetName =
+        historyMapping
+            (definitionInvariant (migrationId "keiro" targetName))
+            (Evidence sourceKey)
+            (SamePayload sourceKey)
+      where
+        sourceKey = definitionInvariant (first show (coddEvidenceKey sourceFilename))
+
+frameworkCoddHistoryMappings :: NonEmpty HistoryMapping
+frameworkCoddHistoryMappings =
+    Kiroku.kirokuCoddHistoryMappings <> keiroCoddHistoryMappings
+
+frameworkCoddSourceConfig ::
+    ConnectionProvider ->
+    Bool ->
+    Text ->
+    Confirmation ->
+    Either CoddDefinitionError CoddSourceConfig
+frameworkCoddSourceConfig sourceProvider strictSource reason confirmation =
+    coddSourceConfig
+        sourceProvider
+        (Kiroku.kirokuLegacyMigrationNames <> keiroLegacyMigrationNames)
+        strictSource
+        (Kiroku.kirokuCoddSourcePayloads <> keiroCoddSourcePayloads)
+        (Just combinedManifest)
+        reason
+        confirmation
+  where
+    combinedManifest =
+        definitionInvariant
+            (parseCoddManifest (Kiroku.kirokuCoddManifestText <> keiroCoddManifestText))
+
+nativeMigrationNames :: NonEmpty Text
+nativeMigrationNames =
+    "0001-keiro-bootstrap"
+        :| [ "0002-keiro-outbox"
+           , "0003-keiro-inbox"
+           , "0004-keiro-timer-recovery"
+           , "0005-keiro-workflow-steps"
+           , "0006-keiro-awakeables"
+           , "0007-keiro-workflow-children"
+           , "0008-keiro-workflow-generation"
+           , "0009-keiro-subscription-shards"
+           , "0010-keiro-messaging-crash-recovery"
+           , "0011-keiro-workflows-instances"
+           , "0012-keiro-workflow-gc-index"
+           , "0013-keiro-workflows-wake-after"
+           , "0014-keiro-projection-dedup"
+           , "0015-keiro-outbox-claim-order-index"
+           , "0016-keiro-inbox-drop-received-idx"
+           ]
+
+keiroCoddSourcePayloads :: Map.Map FilePath ByteString
+keiroCoddSourcePayloads =
+    Map.fromList
+        (zip (toList keiroLegacyMigrationNames) (snd <$> toList embeddedMigrationEntries))
+
+keiroCoddManifestText :: Text
+keiroCoddManifestText = $(embedTextFile "migrations.lock")
+
+zipWithNonEmpty :: (a -> b -> c) -> NonEmpty a -> NonEmpty b -> NonEmpty c
+zipWithNonEmpty combine (firstA :| restA) (firstB :| restB) =
+    combine firstA firstB :| zipWith combine restA restB
+
+definitionInvariant :: (Show error) => Either error value -> value
+definitionInvariant = either (error . ("invalid checked-in Keiro migration definition: " <>) . show) id
diff --git a/src/Keiro/Migrations/Internal/Definition.hs b/src/Keiro/Migrations/Internal/Definition.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/Internal/Definition.hs
@@ -0,0 +1,27 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Keiro.Migrations.Internal.Definition (
+    embeddedMigrationEntries,
+    keiroMigrations,
+) where
+
+import Data.ByteString (ByteString)
+import Data.List.NonEmpty (NonEmpty)
+import Data.Set qualified as Set
+import Database.PostgreSQL.Migrate (
+    DefinitionError,
+    MigrationComponent,
+    migrationComponentFromEmbeddedSql,
+ )
+import Database.PostgreSQL.Migrate.Embed (embedMigrationManifest)
+
+embeddedMigrationEntries :: NonEmpty (FilePath, ByteString)
+embeddedMigrationEntries =
+    $(embedMigrationManifest "migrations/manifest")
+
+keiroMigrations :: Either DefinitionError MigrationComponent
+keiroMigrations =
+    migrationComponentFromEmbeddedSql
+        "keiro"
+        (Set.singleton "kiroku")
+        embeddedMigrationEntries
diff --git a/src/Keiro/Migrations/Internal/EmbedFile.hs b/src/Keiro/Migrations/Internal/EmbedFile.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/Internal/EmbedFile.hs
@@ -0,0 +1,19 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Keiro.Migrations.Internal.EmbedFile (embedTextFile) where
+
+import Data.ByteString qualified as ByteString
+import Data.Text qualified as Text
+import Data.Text.Encoding qualified as Text.Encoding
+import Language.Haskell.TH (Exp (..), Lit (..), Q)
+import Language.Haskell.TH.Syntax qualified as TH
+
+embedTextFile :: FilePath -> Q Exp
+embedTextFile inputPath = do
+    path <- TH.makeRelativeToProject inputPath
+    TH.addDependentFile path
+    bytes <- TH.runIO (ByteString.readFile path)
+    case Text.Encoding.decodeUtf8' bytes of
+        Left decodeError -> fail ("invalid UTF-8 in " <> path <> ": " <> show decodeError)
+        Right contents ->
+            pure (AppE (VarE 'Text.pack) (LitE (StringL (Text.unpack contents))))
diff --git a/src/Keiro/Migrations/LegacyCodd.hs b/src/Keiro/Migrations/LegacyCodd.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/LegacyCodd.hs
@@ -0,0 +1,118 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+{- | Transitional Codd-only tools retained for expected-schema snapshots,
+remediation drills, and historical ledger fixups.
+
+Normal migration execution uses 'Keiro.Migrations' and does not build this
+module unless the @legacy-codd-tools@ Cabal flag is enabled.
+-}
+module Keiro.Migrations.LegacyCodd (
+    LedgerSchema (..),
+    MigrationStatus (..),
+    VerifyOutcome (..),
+    embeddedMigrationNames,
+    embeddedMigrationSources,
+    kirokuEmbeddedMigrationNames,
+    migrationStatus,
+    missingMigrations,
+    runAllKeiroMigrations,
+    runAllKeiroMigrationsNoCheck,
+    runKirokuMigrationsNoCheck,
+    verifySchema,
+) where
+
+import Codd (ApplyResult, CoddSettings (..), VerifySchemas)
+import Codd.Extras.Ledger (LedgerSchema (..), MigrationStatus (..), VerifyOutcome (..))
+import Codd.Extras.MigrationSet (MigrationSet)
+import Codd.Extras.MigrationSet qualified as MigrationSet
+import Control.Monad (void)
+import Data.ByteString (ByteString)
+import Data.FileEmbed (embedDir)
+import Data.Foldable (toList)
+import Data.Map.Strict qualified as Map
+import Data.Time (DiffTime)
+import Keiro.Migrations.ExpectedSchema (expectedSchemaFiles)
+import Keiro.Migrations.History.Codd (keiroLegacyMigrationNames)
+import Kiroku.Store.Migrations.History.Codd qualified as Kiroku
+
+runAllKeiroMigrations :: CoddSettings -> DiffTime -> VerifySchemas -> IO ApplyResult
+runAllKeiroMigrations settings connectTimeout verifySchemas =
+    MigrationSet.applyMigrationSets settings connectTimeout verifySchemas frameworkMigrationSets
+
+runAllKeiroMigrationsNoCheck :: CoddSettings -> DiffTime -> IO ()
+runAllKeiroMigrationsNoCheck settings connectTimeout =
+    void $ MigrationSet.applyMigrationSetsNoCheck settings connectTimeout frameworkMigrationSets
+
+runKirokuMigrationsNoCheck :: CoddSettings -> DiffTime -> IO ()
+runKirokuMigrationsNoCheck settings connectTimeout =
+    void $ MigrationSet.applyMigrationSetNoCheck settings connectTimeout kirokuMigrationSet
+
+verifySchema :: CoddSettings -> DiffTime -> IO VerifyOutcome
+verifySchema =
+    MigrationSet.verifyExpectedSchema
+        expectedLedgerNames
+        MigrationSet.ExpectedSchema
+            { MigrationSet.label = "keiro-expected-schema"
+            , MigrationSet.files = expectedSchemaFiles
+            }
+
+missingMigrations :: CoddSettings -> DiffTime -> IO [FilePath]
+missingMigrations settings connectTimeout =
+    MigrationSet.missingMigrationsForNames expectedLedgerNames (migsConnString settings) connectTimeout
+
+migrationStatus :: CoddSettings -> DiffTime -> IO MigrationStatus
+migrationStatus settings connectTimeout =
+    MigrationSet.migrationStatusForNames expectedLedgerNames (migsConnString settings) connectTimeout
+
+frameworkMigrationSets :: [MigrationSet]
+frameworkMigrationSets =
+    [ kirokuMigrationSet
+    , keiroMigrationSet
+    ]
+
+kirokuMigrationSet :: MigrationSet
+kirokuMigrationSet =
+    MigrationSet.MigrationSet
+        { MigrationSet.label = "Kiroku legacy evidence"
+        , MigrationSet.files = sourceFiles Kiroku.kirokuLegacyMigrationNames Kiroku.kirokuCoddSourcePayloads
+        }
+
+keiroMigrationSet :: MigrationSet
+keiroMigrationSet =
+    MigrationSet.MigrationSet
+        { MigrationSet.label = "Keiro legacy evidence"
+        , MigrationSet.files = embeddedMigrationFiles
+        }
+
+sourceFiles ::
+    (Foldable collection) =>
+    collection FilePath ->
+    Map.Map FilePath ByteString ->
+    [(FilePath, ByteString)]
+sourceFiles names payloads =
+    [ (name, requirePayload name)
+    | name <- toList names
+    ]
+  where
+    requirePayload name =
+        case Map.lookup name payloads of
+            Just payload -> payload
+            Nothing -> error ("missing checked-in legacy payload for " <> name)
+
+embeddedMigrationFiles :: [(FilePath, ByteString)]
+embeddedMigrationFiles = $(embedDir "sql-migrations")
+
+embeddedMigrationSources :: [(FilePath, ByteString)]
+embeddedMigrationSources = embeddedMigrationFiles
+
+embeddedMigrationNames :: [FilePath]
+embeddedMigrationNames =
+    MigrationSet.migrationNames keiroMigrationSet
+
+kirokuEmbeddedMigrationNames :: [FilePath]
+kirokuEmbeddedMigrationNames =
+    MigrationSet.migrationNames kirokuMigrationSet
+
+expectedLedgerNames :: [FilePath]
+expectedLedgerNames =
+    MigrationSet.migrationNamesForSets frameworkMigrationSets
diff --git a/src/Keiro/Migrations/New.hs b/src/Keiro/Migrations/New.hs
new file mode 100644
--- /dev/null
+++ b/src/Keiro/Migrations/New.hs
@@ -0,0 +1,51 @@
+{- | Generate a new timestamped Keiro migration skeleton.
+
+The embedded migrations are ordered by their @YYYY-MM-DD-HH-MM-SS-slug.sql@
+file names (lexicographic order == chronological order because every field is
+fixed-width and zero-padded). 'newMigrationFile' stamps the /real/ current UTC
+time, so two migrations authored at different moments can never collide and
+always sort in authoring order -- no hand-assigned slots to coordinate.
+-}
+module Keiro.Migrations.New (
+    newMigrationFile,
+    defaultMigrationsDir,
+    migrationFileName,
+    migrationSlug,
+    migrationTemplate,
+) where
+
+import Codd.Extras.New qualified as New
+import Data.Time (UTCTime)
+
+defaultMigrationsDir :: FilePath
+defaultMigrationsDir = New.defaultMigrationsDir
+
+newMigrationFile :: FilePath -> String -> IO FilePath
+newMigrationFile = New.newMigrationFile migrationFileConfig
+
+migrationFileName :: UTCTime -> String -> FilePath
+migrationFileName = New.migrationFileName migrationFileConfig
+
+migrationSlug :: String -> String
+migrationSlug = New.migrationSlug (Just "keiro")
+
+migrationTemplate :: String -> String
+migrationTemplate description =
+    unlines
+        [ "-- " <> description
+        , "--"
+        , "-- Create objects fully qualified in the keiro schema (no session path pin)."
+        , "-- Example:"
+        , "--   CREATE TABLE IF NOT EXISTS keiro.keiro_example ("
+        , "--     id UUID PRIMARY KEY"
+        , "--   );"
+        , ""
+        , "-- TODO: write the migration body. Prefer idempotent DDL (IF NOT EXISTS)."
+        ]
+
+migrationFileConfig :: New.MigrationFileConfig
+migrationFileConfig =
+    New.MigrationFileConfig
+        { New.migrationSlugPrefix = Just "keiro"
+        , New.migrationTemplate = migrationTemplate
+        }
diff --git a/test-legacy/Main.hs b/test-legacy/Main.hs
new file mode 100644
--- /dev/null
+++ b/test-legacy/Main.hs
@@ -0,0 +1,689 @@
+module Main (
+    main,
+)
+where
+
+import Codd (ApplyResult (..), CoddSettings (..), VerifySchemas (LaxCheck, StrictCheck))
+import Codd.Extras.Guards
+import Codd.Parsing (connStringParser)
+import Codd.Types (ConnectionString, SchemaAlgo (..), SchemaSelection (..), SqlSchema (..), TxnIsolationLvl (..), singleTryPolicy)
+import Contravariant.Extras (contrazip3)
+import Control.Concurrent.Async (concurrently)
+import Control.Exception (finally)
+import Control.Monad (filterM)
+import Data.Attoparsec.Text (endOfInput, parseOnly)
+import Data.Int (Int32)
+import Data.List (isSuffixOf, sort)
+import Data.Text (Text)
+import Data.Text qualified as T
+import Data.Text.IO qualified as TIO
+import Data.Time (UTCTime (..), fromGregorian, secondsToDiffTime)
+import EphemeralPg qualified as Pg
+import Hasql.Connection.Settings qualified as Conn
+import Hasql.Decoders qualified as D
+import Hasql.Encoders qualified as E
+import Hasql.Pool qualified as Pool
+import Hasql.Pool.Config qualified as Pool.Config
+import Hasql.Session qualified as Session
+import Hasql.Statement (Statement, preparable)
+import Keiro.Migrations.LegacyCodd (
+    MigrationStatus (..),
+    VerifyOutcome (..),
+    embeddedMigrationNames,
+    embeddedMigrationSources,
+    kirokuEmbeddedMigrationNames,
+    migrationStatus,
+    missingMigrations,
+    runAllKeiroMigrations,
+    runAllKeiroMigrationsNoCheck,
+    runKirokuMigrationsNoCheck,
+    verifySchema,
+ )
+import Keiro.Migrations.New (migrationFileName, migrationSlug, newMigrationFile)
+import System.Directory (doesDirectoryExist, doesFileExist, listDirectory)
+import System.FilePath (takeFileName)
+import System.IO.Temp (withSystemTempDirectory)
+import Test.Hspec
+
+{- | Pin the ephemeral PostgreSQL superuser to the fixed name @keiro@ so the
+captured snapshot identity (roles, database owner, per-object owners) is
+deterministic across machines and CI rather than the local OS username. This is
+the portability fix for the strict drift gate.
+-}
+keiroPgConfig :: Pg.Config
+keiroPgConfig = Pg.defaultConfig{Pg.user = "keiro"}
+
+{- | Start a cached ephemeral server whose PostgreSQL superuser is the fixed
+name @keiro@. Mirrors 'Pg.withCached' but pins the user; 'Pg.withCachedConfig'
+is not exported, so we use 'Pg.startCached' + 'finally'.
+-}
+withKeiroPg :: (Pg.Database -> IO a) -> IO (Either Pg.StartError a)
+withKeiroPg action = do
+    started <- Pg.startCached keiroPgConfig Pg.defaultCacheConfig
+    case started of
+        Left err -> pure (Left err)
+        Right db -> Right <$> (action db `finally` Pg.stop db)
+
+main :: IO ()
+main =
+    hspec $ do
+        migrationFileNameSpec
+        migrationIntegritySpec
+        scaffolderSpec
+        migrationUpgradeSpec
+        describe "Keiro codd migrations" $ do
+            it "applies Kiroku and Keiro migrations to a fresh database and is repeatable" $ do
+                result <- withKeiroPg $ \db -> do
+                    let connStr = Pg.connectionString db
+                        coddSettings = testCoddSettings connStr "keiro-migrations/expected-schema"
+
+                    runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                    assertTablesExist connStr "kiroku" kirokuTables
+                    assertTablesExist connStr "keiro" keiroTables
+                    assertTablesAbsent connStr "kiroku" keiroTables
+                    assertTablesAbsent connStr "public" keiroTables
+                    assertTablesAbsent connStr "public" kirokuTables
+
+                    runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                    assertTablesExist connStr "kiroku" kirokuTables
+                    assertTablesExist connStr "keiro" keiroTables
+                    assertTablesAbsent connStr "kiroku" keiroTables
+                    assertTablesAbsent connStr "public" keiroTables
+                    assertColumnExists connStr "keiro" "keiro_timers" "last_error"
+
+                case result of
+                    Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                    Right () -> pure ()
+
+            it "matches the checked-in expected schema" $ do
+                expectedSchemaDir <- findExpectedSchemaDir
+                result <- withKeiroPg $ \db -> do
+                    let coddSettings = testCoddSettings (Pg.connectionString db) expectedSchemaDir
+                    runAllKeiroMigrations coddSettings (secondsToDiffTime 5) StrictCheck
+
+                case result of
+                    Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                    Right (SchemasMatch _) -> pure ()
+                    Right SchemasNotVerified -> expectationFailure "StrictCheck did not verify schemas"
+                    Right (SchemasDiffer _) -> expectationFailure "StrictCheck returned a schema mismatch without throwing"
+
+            it "reports schema drift under LaxCheck" $ do
+                expectedSchemaDir <- findExpectedSchemaDir
+                result <- withKeiroPg $ \db -> do
+                    let connStr = Pg.connectionString db
+                        coddSettings = testCoddSettings connStr expectedSchemaDir
+                    runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                    runDb
+                        connStr
+                        "drift drill"
+                        (Session.script "ALTER TABLE keiro.keiro_timers ALTER COLUMN last_error SET NOT NULL;")
+                    runAllKeiroMigrations coddSettings (secondsToDiffTime 5) LaxCheck
+
+                case result of
+                    Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                    Right (SchemasDiffer _) -> pure ()
+                    Right SchemasNotVerified -> expectationFailure "LaxCheck did not verify schemas"
+                    Right (SchemasMatch _) -> expectationFailure "LaxCheck did not report schema drift"
+
+{- | Guard against the recurring mistake of hand-assigning rounded, sentinel
+migration timestamps (e.g. @2026-05-17-00-00-00-...@, @...-01-00-00-...@).
+Migrations must be created with @keiro-migrate new@ ("Keiro.Migrations.New"),
+which stamps the real current UTC time to the second, so filenames sort in true
+authoring order and never collide in codd's timestamp-keyed ledger.
+-}
+migrationFileNameSpec :: Spec
+migrationFileNameSpec =
+    describe "migration file names" $ do
+        it "carry real UTC authoring timestamps, not hand-assigned sentinels" $ do
+            files <- migrationFiles
+            files `shouldNotBe` []
+            sentinelViolations files `shouldHaveNoViolations` "sentinel timestamp violations"
+
+        it "have unique, strictly increasing timestamps" $ do
+            files <- migrationFiles
+            duplicateTimestampViolations files `shouldHaveNoViolations` "duplicate timestamp violations"
+
+migrationIntegritySpec :: Spec
+migrationIntegritySpec =
+    describe "migration integrity guards" $ do
+        it "embeds exactly the checked-in sql-migrations directory" $ do
+            diskNames <- sort <$> migrationFiles
+            embeddedMigrationNames `shouldBe` diskNames
+
+        it "matches the checked-in SHA-256 manifest" $ do
+            manifestPath <- findLockfile
+            parsed <- parseChecksumManifest <$> TIO.readFile manifestPath
+            case parsed of
+                Left err -> expectationFailure (T.unpack err)
+                Right manifest ->
+                    checksumViolations manifest embeddedMigrationSources
+                        `shouldHaveNoViolations` "checksum manifest violations"
+
+        it "keeps future migration bodies schema-qualified and codd-safe" $ do
+            lintViolations
+                LintConfig
+                    { requiredQualifier = "keiro."
+                    , exemptFiles = []
+                    }
+                embeddedMigrationSources
+                `shouldHaveNoViolations` "migration body lint violations"
+
+        it "keeps timestamps unique across the combined Kiroku and Keiro ledger" $ do
+            duplicateTimestampViolations expectedLedgerNames
+                `shouldHaveNoViolations` "combined-ledger duplicate timestamp violations"
+
+        it "records every embedded Kiroku and Keiro migration in the codd v5 ledger" $ do
+            result <- withKeiroPg $ \db -> do
+                let connStr = Pg.connectionString db
+                    coddSettings = testCoddSettings connStr "keiro-migrations/expected-schema"
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                schema <- detectLedgerSchema connStr
+                schema `shouldBe` "codd"
+                names <- ledgerNames connStr schema
+                names `shouldBe` map T.pack expectedLedgerNames
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "serializes concurrent combined applies with the shared advisory lock" $ do
+            result <- withKeiroPg $ \db -> do
+                let connStr = Pg.connectionString db
+                    coddSettings = testCoddSettings connStr "keiro-migrations/expected-schema"
+                concurrently
+                    (runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5))
+                    (runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5))
+                schema <- detectLedgerSchema connStr
+                names <- ledgerNames connStr schema
+                names `shouldBe` map T.pack expectedLedgerNames
+                count <- ledgerRowCount connStr schema
+                count `shouldBe` fromIntegral (length expectedLedgerNames)
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "reports every embedded migration as pending on an empty database" $ do
+            result <- withKeiroPg $ \db -> do
+                let coddSettings = testCoddSettings (Pg.connectionString db) "keiro-migrations/expected-schema"
+                verifySchema coddSettings (secondsToDiffTime 5) `shouldReturn` VerifyPending expectedLedgerNames
+                status <- migrationStatus coddSettings (secondsToDiffTime 5)
+                statusApplied status `shouldBe` []
+                statusPending status `shouldBe` expectedLedgerNames
+                missingMigrations coddSettings (secondsToDiffTime 5) `shouldReturn` expectedLedgerNames
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "reports only Keiro migrations as pending after Kiroku-only apply" $ do
+            result <- withKeiroPg $ \db -> do
+                let coddSettings = testCoddSettings (Pg.connectionString db) "keiro-migrations/expected-schema"
+                runKirokuMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                verifySchema coddSettings (secondsToDiffTime 5) `shouldReturn` VerifyPending embeddedMigrationNames
+                status <- migrationStatus coddSettings (secondsToDiffTime 5)
+                map fst (statusApplied status) `shouldBe` kirokuEmbeddedMigrationNames
+                statusPending status `shouldBe` embeddedMigrationNames
+                missingMigrations coddSettings (secondsToDiffTime 5) `shouldReturn` embeddedMigrationNames
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "verifies the embedded Keiro expected schema after combined apply" $ do
+            result <- withKeiroPg $ \db -> do
+                let coddSettings = testCoddSettings (Pg.connectionString db) "keiro-migrations/expected-schema"
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                verifySchema coddSettings (secondsToDiffTime 5) `shouldReturn` VerifySucceeded
+                status <- migrationStatus coddSettings (secondsToDiffTime 5)
+                map fst (statusApplied status) `shouldBe` expectedLedgerNames
+                statusPending status `shouldBe` []
+                missingMigrations coddSettings (secondsToDiffTime 5) `shouldReturn` []
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "reports Keiro schema drift without applying migrations" $ do
+            result <- withKeiroPg $ \db -> do
+                let connStr = Pg.connectionString db
+                    coddSettings = testCoddSettings connStr "keiro-migrations/expected-schema"
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                beforeCount <- ledgerRowCount connStr "codd"
+                runDb connStr "verify drift mutation" (Session.script "CREATE TABLE keiro.verify_drift (id int);")
+                verifySchema coddSettings (secondsToDiffTime 5) `shouldReturn` VerifyFailed
+                afterCount <- ledgerRowCount connStr "codd"
+                afterCount `shouldBe` beforeCount
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+        it "realigns historical sentinel ledger rows before a repeat migrate" $ do
+            fixupPath <- findLedgerFixup
+            fixupScript <- TIO.readFile fixupPath
+            result <- withKeiroPg $ \db -> do
+                let connStr = Pg.connectionString db
+                    coddSettings = testCoddSettings connStr "keiro-migrations/expected-schema"
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                schema <- detectLedgerSchema connStr
+                rewindKeiroLedgerToSentinelNames connStr schema
+                sentinelNames <- ledgerNames connStr schema
+                sentinelNames `shouldSatisfy` any (`elem` map oldLedgerName keiroLedgerRemaps)
+                runDb connStr "keiro ledger fixup script" (Session.script fixupScript)
+                fixedNames <- ledgerNames connStr schema
+                fixedNames `shouldBe` map T.pack expectedLedgerNames
+                beforeCount <- ledgerRowCount connStr schema
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                afterCount <- ledgerRowCount connStr schema
+                afterCount `shouldBe` beforeCount
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+{- | Prove the scaffolder (`Keiro.Migrations.New`) is the producer that satisfies
+the reactive filename guard. The deterministic check proves the slug convention;
+the temp-dir check proves the live writer creates a schema-qualified template.
+-}
+scaffolderSpec :: Spec
+scaffolderSpec =
+    describe "migration scaffolder" $ do
+        it "stamps a real, non-sentinel UTC timestamp and a keiro-prefixed slug" $ do
+            let sampled = UTCTime (fromGregorian 2026 7 5) (secondsToDiffTime (19 * 3600 + 9 * 60 + 18))
+                name = migrationFileName sampled "Add widget index"
+            takeFileName name `shouldBe` name
+            isTimestampShaped (take timestampWidth name) `shouldBe` True
+            handAssignedTimestamp name `shouldBe` False
+            migrationSlug "Add widget index" `shouldBe` "keiro-add-widget-index"
+
+        it "writes a well-named file into a temp dir with a qualified template" $
+            withSystemTempDirectory "keiro-scaffolder" $ \dir -> do
+                path <- newMigrationFile dir "add widget index"
+                let base = takeFileName path
+                isTimestampShaped (take timestampWidth base) `shouldBe` True
+                length base `shouldSatisfy` (> timestampWidth)
+                body <- TIO.readFile path
+                (".sql" `isSuffixOf` path) `shouldBe` True
+                ("keiro.keiro_example" `T.isInfixOf` body) `shouldBe` True
+                ("search_path" `T.isInfixOf` body) `shouldBe` False
+
+migrationUpgradeSpec :: Spec
+migrationUpgradeSpec =
+    describe "keiro migration upgrade artifacts" $ do
+        it "remediates a 0.1.0.0-style kiroku-schema layout without losing rows" $ do
+            remediationPath <- findRemediationScript
+            remediationScript <- TIO.readFile remediationPath
+            expectedSchemaDir <- findExpectedSchemaDir
+            result <- withKeiroPg $ \db -> do
+                let connStr = Pg.connectionString db
+                    coddSettings = testCoddSettings connStr expectedSchemaDir
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                schema <- detectLedgerSchema connStr
+                moveKeiroTablesBackToKiroku connStr
+                seedRemediationRows connStr
+                runDb connStr "keiro schema remediation script" (Session.script remediationScript)
+
+                assertTablesExist connStr "keiro" keiroTables
+                assertTablesAbsent connStr "kiroku" keiroTables
+                assertSnapshotRowSurvived connStr
+                assertTimerRowSurvived connStr
+
+                beforeCount <- ledgerRowCount connStr schema
+                runAllKeiroMigrationsNoCheck coddSettings (secondsToDiffTime 5)
+                afterCount <- ledgerRowCount connStr schema
+                afterCount `shouldBe` beforeCount
+
+                strictResult <- runAllKeiroMigrations coddSettings (secondsToDiffTime 5) StrictCheck
+                case strictResult of
+                    SchemasMatch _ -> pure ()
+                    SchemasNotVerified -> expectationFailure "StrictCheck did not verify remediated schemas"
+                    SchemasDiffer _ -> expectationFailure "StrictCheck returned a schema mismatch after remediation"
+
+                runDb connStr "idempotent keiro schema remediation script" (Session.script remediationScript)
+                assertTablesExist connStr "keiro" keiroTables
+                assertTablesAbsent connStr "kiroku" keiroTables
+                assertSnapshotRowSurvived connStr
+                assertTimerRowSurvived connStr
+            case result of
+                Left err -> expectationFailure ("Failed to start ephemeral PostgreSQL: " <> show err)
+                Right () -> pure ()
+
+-- | The migration @.sql@ files, wherever the suite is run from.
+migrationFiles :: IO [FilePath]
+migrationFiles = do
+    dir <- findMigrationsDir
+    filter (".sql" `isSuffixOf`) <$> listDirectory dir
+
+findMigrationsDir :: IO FilePath
+findMigrationsDir = do
+    let candidates = ["keiro-migrations/sql-migrations", "sql-migrations"]
+    existing <- filterM doesDirectoryExist candidates
+    case existing of
+        dir : _ -> pure dir
+        [] ->
+            expectationFailure "Could not find keiro-migrations/sql-migrations"
+                >> pure "keiro-migrations/sql-migrations"
+
+findExpectedSchemaDir :: IO FilePath
+findExpectedSchemaDir = do
+    let candidates =
+            [ "keiro-migrations/expected-schema"
+            , "expected-schema"
+            ]
+    existing <- filterM doesDirectoryExist candidates
+    case existing of
+        dir : _ -> pure dir
+        [] ->
+            expectationFailure "Could not find keiro-migrations/expected-schema"
+                >> pure "keiro-migrations/expected-schema"
+
+findLockfile :: IO FilePath
+findLockfile = findExistingFile ["keiro-migrations/migrations.lock", "migrations.lock"]
+
+findLedgerFixup :: IO FilePath
+findLedgerFixup =
+    findExistingFile
+        [ "keiro-migrations/ledger-fixups/2026-07-05-realign-keiro-migration-timestamps.sql"
+        , "ledger-fixups/2026-07-05-realign-keiro-migration-timestamps.sql"
+        ]
+
+findRemediationScript :: IO FilePath
+findRemediationScript =
+    findExistingFile
+        [ "keiro-migrations/remediation/2026-07-05-relocate-keiro-tables-to-keiro-schema.sql"
+        , "remediation/2026-07-05-relocate-keiro-tables-to-keiro-schema.sql"
+        ]
+
+findExistingFile :: [FilePath] -> IO FilePath
+findExistingFile candidates = do
+    existing <- filterM doesFileExist candidates
+    case existing of
+        path : _ -> pure path
+        [] -> expectationFailure ("Could not find any of: " <> show candidates) >> pure fallback
+  where
+    fallback =
+        case candidates of
+            path : _ -> path
+            [] -> "."
+
+expectedLedgerNames :: [FilePath]
+expectedLedgerNames = sort (kirokuEmbeddedMigrationNames <> embeddedMigrationNames)
+
+-- | Kiroku's own event-store tables, which remain in the kiroku schema.
+kirokuTables :: [Text]
+kirokuTables =
+    [ "events"
+    , "stream_events"
+    , "streams"
+    , "subscriptions"
+    ]
+
+-- | Keiro's framework tables, which live in the dedicated keiro schema.
+keiroTables :: [Text]
+keiroTables =
+    [ "keiro_awakeables"
+    , "keiro_inbox"
+    , "keiro_outbox"
+    , "keiro_projection_dedup"
+    , "keiro_read_models"
+    , "keiro_snapshots"
+    , "keiro_subscription_shards"
+    , "keiro_timers"
+    , "keiro_workflow_children"
+    , "keiro_workflow_steps"
+    , "keiro_workflows"
+    ]
+
+testCoddSettings :: Text -> FilePath -> CoddSettings
+testCoddSettings connStr expectedSchemaDir =
+    CoddSettings
+        { migsConnString = parseConnString connStr
+        , sqlMigrations = []
+        , onDiskReps = Left expectedSchemaDir
+        , namespacesToCheck = IncludeSchemas [SqlSchema "keiro"]
+        , extraRolesToCheck = []
+        , retryPolicy = singleTryPolicy
+        , txnIsolationLvl = DbDefault
+        , schemaAlgoOpts = SchemaAlgo False False False
+        }
+
+parseConnString :: Text -> ConnectionString
+parseConnString connStr =
+    case parseOnly (connStringParser <* endOfInput) connStr of
+        Left err -> error ("Could not parse ephemeral PostgreSQL connection string for codd: " <> err)
+        Right parsed -> parsed
+
+shouldHaveNoViolations :: [Text] -> String -> Expectation
+shouldHaveNoViolations [] _ = pure ()
+shouldHaveNoViolations violations label =
+    expectationFailure (label <> ":\n" <> T.unpack (T.unlines violations))
+
+runDb :: Text -> String -> Session.Session a -> IO a
+runDb connStr label session = do
+    pool <- Pool.acquire poolConfig
+    result <- Pool.use pool session
+    Pool.release pool
+    case result of
+        Left err -> expectationFailure (label <> " failed: " <> show err) >> fail label
+        Right value -> pure value
+  where
+    poolConfig =
+        Pool.Config.settings
+            [ Pool.Config.staticConnectionSettings (Conn.connectionString connStr)
+            , Pool.Config.size 1
+            ]
+
+detectLedgerSchema :: Text -> IO Text
+detectLedgerSchema connStr = do
+    (hasCodd, hasCoddSchema) <- runDb connStr "ledger schema detection" (Session.statement () ledgerSchemaStmt)
+    case (hasCodd, hasCoddSchema) of
+        (True, False) -> pure "codd"
+        (False, True) -> pure "codd_schema"
+        (False, False) -> expectationFailure "codd ledger table was not found" >> pure "codd"
+        (True, True) -> expectationFailure "both codd and codd_schema ledger tables exist" >> pure "codd"
+
+ledgerSchemaStmt :: Statement () (Bool, Bool)
+ledgerSchemaStmt =
+    preparable
+        "SELECT to_regclass('codd.sql_migrations') IS NOT NULL, to_regclass('codd_schema.sql_migrations') IS NOT NULL"
+        E.noParams
+        (D.singleRow ((,) <$> D.column (D.nonNullable D.bool) <*> D.column (D.nonNullable D.bool)))
+
+ledgerNames :: Text -> Text -> IO [Text]
+ledgerNames connStr "codd" = runDb connStr "codd ledger names" (Session.statement () ledgerNamesCoddStmt)
+ledgerNames connStr "codd_schema" = runDb connStr "codd_schema ledger names" (Session.statement () ledgerNamesCoddSchemaStmt)
+ledgerNames _ schema = expectationFailure ("unknown ledger schema " <> T.unpack schema) >> pure []
+
+ledgerNamesCoddStmt :: Statement () [Text]
+ledgerNamesCoddStmt =
+    preparable
+        "SELECT name::text FROM codd.sql_migrations ORDER BY name"
+        E.noParams
+        (D.rowList (D.column (D.nonNullable D.text)))
+
+ledgerNamesCoddSchemaStmt :: Statement () [Text]
+ledgerNamesCoddSchemaStmt =
+    preparable
+        "SELECT name::text FROM codd_schema.sql_migrations ORDER BY name"
+        E.noParams
+        (D.rowList (D.column (D.nonNullable D.text)))
+
+ledgerRowCount :: Text -> Text -> IO Int32
+ledgerRowCount connStr "codd" = runDb connStr "codd ledger row count" (Session.statement () ledgerCountCoddStmt)
+ledgerRowCount connStr "codd_schema" = runDb connStr "codd_schema ledger row count" (Session.statement () ledgerCountCoddSchemaStmt)
+ledgerRowCount _ schema = expectationFailure ("unknown ledger schema " <> T.unpack schema) >> pure 0
+
+ledgerCountCoddStmt :: Statement () Int32
+ledgerCountCoddStmt =
+    preparable
+        "SELECT count(*)::int FROM codd.sql_migrations"
+        E.noParams
+        (D.singleRow (D.column (D.nonNullable D.int4)))
+
+ledgerCountCoddSchemaStmt :: Statement () Int32
+ledgerCountCoddSchemaStmt =
+    preparable
+        "SELECT count(*)::int FROM codd_schema.sql_migrations"
+        E.noParams
+        (D.singleRow (D.column (D.nonNullable D.int4)))
+
+data KeiroLedgerRemap = KeiroLedgerRemap
+    { newLedgerName :: Text
+    , oldLedgerName :: Text
+    , oldLedgerTimestamp :: Text
+    }
+    deriving stock (Eq, Show)
+
+keiroLedgerRemaps :: [KeiroLedgerRemap]
+keiroLedgerRemaps =
+    [ KeiroLedgerRemap "2026-05-17-13-58-15-keiro-bootstrap.sql" "2026-05-17-00-00-00-keiro-bootstrap.sql" "2026-05-17 00:00:00+00"
+    , KeiroLedgerRemap "2026-05-19-12-55-02-keiro-outbox.sql" "2026-05-17-01-00-00-keiro-outbox.sql" "2026-05-17 01:00:00+00"
+    , KeiroLedgerRemap "2026-05-19-13-05-23-keiro-inbox.sql" "2026-05-17-02-00-00-keiro-inbox.sql" "2026-05-17 02:00:00+00"
+    , KeiroLedgerRemap "2026-06-03-05-14-28-keiro-timer-recovery.sql" "2026-05-17-03-00-00-keiro-timer-recovery.sql" "2026-05-17 03:00:00+00"
+    , KeiroLedgerRemap "2026-06-03-16-10-05-keiro-workflow-steps.sql" "2026-06-03-00-00-00-keiro-workflow-steps.sql" "2026-06-03 00:00:00+00"
+    , KeiroLedgerRemap "2026-06-03-18-19-41-keiro-awakeables.sql" "2026-06-03-01-00-00-keiro-awakeables.sql" "2026-06-03 01:00:00+00"
+    , KeiroLedgerRemap "2026-06-03-19-49-23-keiro-workflow-children.sql" "2026-06-03-02-00-00-keiro-workflow-children.sql" "2026-06-03 02:00:00+00"
+    , KeiroLedgerRemap "2026-06-04-02-12-28-keiro-workflow-generation.sql" "2026-06-05-00-00-00-keiro-workflow-generation.sql" "2026-06-05 00:00:00+00"
+    , KeiroLedgerRemap "2026-06-04-03-53-34-keiro-subscription-shards.sql" "2026-06-05-01-00-00-keiro-subscription-shards.sql" "2026-06-05 01:00:00+00"
+    , KeiroLedgerRemap "2026-06-15-15-07-25-keiro-workflows-instances.sql" "2026-06-11-00-00-04-keiro-workflows-instances.sql" "2026-06-11 00:00:04+00"
+    , KeiroLedgerRemap "2026-06-15-17-53-48-keiro-workflow-gc-index.sql" "2026-06-15-22-10-00-keiro-workflow-gc-index.sql" "2026-06-15 22:10:00+00"
+    , KeiroLedgerRemap "2026-06-15-18-01-33-keiro-workflows-wake-after.sql" "2026-06-15-22-20-00-keiro-workflows-wake-after.sql" "2026-06-15 22:20:00+00"
+    , KeiroLedgerRemap "2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql" "2026-07-02-00-12-00-keiro-outbox-claim-order-index.sql" "2026-07-02 00:12:00+00"
+    , KeiroLedgerRemap "2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql" "2026-07-02-00-55-00-keiro-inbox-drop-received-idx.sql" "2026-07-02 00:55:00+00"
+    ]
+
+rewindKeiroLedgerToSentinelNames :: Text -> Text -> IO ()
+rewindKeiroLedgerToSentinelNames connStr schema =
+    runDb connStr "keiro ledger rewind to sentinel names" (Session.script script)
+  where
+    qname = schema <> ".sql_migrations"
+    -- Kept in sync with ledger-fixups/2026-07-05-realign-keiro-migration-timestamps.sql.
+    script =
+        T.unlines
+            [ "UPDATE " <> qname <> " SET name = '" <> oldName <> "', migration_timestamp = '" <> oldTimestamp <> "' WHERE name = '" <> newName <> "';"
+            | KeiroLedgerRemap newName oldName oldTimestamp <- keiroLedgerRemaps
+            ]
+
+moveKeiroTablesBackToKiroku :: Text -> IO ()
+moveKeiroTablesBackToKiroku connStr =
+    runDb connStr "move keiro tables back to kiroku schema" (Session.script script)
+  where
+    tableArray = T.intercalate ", " ["'" <> table <> "'" | table <- keiroTables]
+    script =
+        T.unlines
+            [ "DO $$"
+            , "DECLARE"
+            , "  t text;"
+            , "  tables text[] := ARRAY[" <> tableArray <> "];"
+            , "BEGIN"
+            , "  FOREACH t IN ARRAY tables LOOP"
+            , "    IF to_regclass('keiro.' || t) IS NOT NULL THEN"
+            , "      EXECUTE format('ALTER TABLE keiro.%I SET SCHEMA kiroku', t);"
+            , "    END IF;"
+            , "  END LOOP;"
+            , "END"
+            , "$$;"
+            , "DROP SCHEMA IF EXISTS keiro;"
+            ]
+
+seedRemediationRows :: Text -> IO ()
+seedRemediationRows connStr =
+    runDb connStr "seed 0.1.0.0-layout keiro rows" (Session.script script)
+  where
+    script =
+        """
+        INSERT INTO kiroku.keiro_snapshots
+          (stream_id, stream_version, state, state_codec_version, regfile_shape_hash)
+        VALUES
+          (4242, 7, '{"ok": true}'::jsonb, 3, 'shape-abc');
+
+        INSERT INTO kiroku.keiro_timers
+          (timer_id, process_manager_name, correlation_id, fire_at, payload, status)
+        VALUES
+          ('00000000-0000-4000-8000-000000000001', 'remediation-test', 'corr-1',
+           '2026-07-06 00:00:00+00', '{"wake": true}'::jsonb, 'scheduled');
+        """
+
+assertSnapshotRowSurvived :: Text -> IO ()
+assertSnapshotRowSurvived connStr = do
+    present <- runDb connStr "snapshot survival query" (Session.statement () snapshotSurvivedStmt)
+    present `shouldBe` True
+
+snapshotSurvivedStmt :: Statement () Bool
+snapshotSurvivedStmt =
+    preparable
+        """
+        SELECT EXISTS (
+          SELECT 1
+          FROM keiro.keiro_snapshots
+          WHERE stream_id = 4242
+            AND stream_version = 7
+            AND state = '{"ok": true}'::jsonb
+            AND state_codec_version = 3
+            AND regfile_shape_hash = 'shape-abc'
+        )
+        """
+        E.noParams
+        (D.singleRow (D.column (D.nonNullable D.bool)))
+
+assertTimerRowSurvived :: Text -> IO ()
+assertTimerRowSurvived connStr = do
+    present <- runDb connStr "timer survival query" (Session.statement () timerSurvivedStmt)
+    present `shouldBe` True
+
+timerSurvivedStmt :: Statement () Bool
+timerSurvivedStmt =
+    preparable
+        """
+        SELECT EXISTS (
+          SELECT 1
+          FROM keiro.keiro_timers
+          WHERE timer_id = '00000000-0000-4000-8000-000000000001'
+            AND process_manager_name = 'remediation-test'
+            AND correlation_id = 'corr-1'
+            AND payload = '{"wake": true}'::jsonb
+            AND status = 'scheduled'
+        )
+        """
+        E.noParams
+        (D.singleRow (D.column (D.nonNullable D.bool)))
+
+assertTablesExist :: Text -> Text -> [Text] -> IO ()
+assertTablesExist connStr schema tables = do
+    actualTables <- runDb connStr "table verification query" (Session.statement schema schemaTablesStmt)
+    let missing = filter (`notElem` actualTables) tables
+    missing `shouldBe` []
+
+assertTablesAbsent :: Text -> Text -> [Text] -> IO ()
+assertTablesAbsent connStr schema tables = do
+    actualTables <- runDb connStr "table verification query" (Session.statement schema schemaTablesStmt)
+    let present = filter (`elem` actualTables) tables
+    present `shouldBe` []
+
+assertColumnExists :: Text -> Text -> Text -> Text -> IO ()
+assertColumnExists connStr schema table column = do
+    present <- runDb connStr "column verification query" (Session.statement (schema, table, column) columnExistsStmt)
+    present `shouldBe` True
+
+columnExistsStmt :: Statement (Text, Text, Text) Bool
+columnExistsStmt =
+    preparable
+        """
+        SELECT EXISTS (
+          SELECT 1 FROM information_schema.columns
+          WHERE table_schema = $1 AND table_name = $2 AND column_name = $3
+        )
+        """
+        ( contrazip3
+            (E.param (E.nonNullable E.text))
+            (E.param (E.nonNullable E.text))
+            (E.param (E.nonNullable E.text))
+        )
+        (D.singleRow (D.column (D.nonNullable D.bool)))
+
+schemaTablesStmt :: Statement Text [Text]
+schemaTablesStmt =
+    preparable
+        """
+        SELECT table_name::text
+        FROM information_schema.tables
+        WHERE table_schema = $1
+          AND table_type = 'BASE TABLE'
+        ORDER BY table_name
+        """
+        (E.param (E.nonNullable E.text))
+        (D.rowList (D.column (D.nonNullable D.text)))
diff --git a/test/Main.hs b/test/Main.hs
new file mode 100644
--- /dev/null
+++ b/test/Main.hs
@@ -0,0 +1,426 @@
+{-# LANGUAGE MultilineStrings #-}
+
+module Main (main) where
+
+import Control.Concurrent.Async (concurrently)
+import Control.Exception (finally)
+import Control.Monad (forM_)
+import Data.ByteString (ByteString)
+import Data.ByteString qualified as ByteString
+import Data.Either (isLeft)
+import Data.Foldable (toList)
+import Data.Int (Int64)
+import Data.List (sort)
+import Data.List.NonEmpty (NonEmpty (..))
+import Data.Map.Strict qualified as Map
+import Data.Set qualified as Set
+import Data.Text (Text)
+import Data.Text qualified as Text
+import Data.Text.Encoding qualified as Text.Encoding
+import Data.Text.IO qualified as Text.IO
+import Database.PostgreSQL.Migrate
+import Database.PostgreSQL.Migrate.History.Codd
+import Database.PostgreSQL.Migrate.Internal (
+    ComponentDescription (..),
+    PlanDescription (..),
+    componentNameText,
+    migrationChecksumBytes,
+    planDescription,
+ )
+import Database.PostgreSQL.Migrate.Test (withMigratedDatabase)
+import EphemeralPg qualified as Pg
+import Hasql.Connection qualified as Connection
+import Hasql.Connection.Settings qualified as Settings
+import Hasql.Decoders qualified as Decoders
+import Hasql.Encoders qualified as Encoders
+import Hasql.Session qualified as Session
+import Hasql.Statement (Statement)
+import Hasql.Statement qualified as Statement
+import Keiro.Migrations
+import Keiro.Migrations.History.Codd
+import Kiroku.Store.Migrations qualified as Kiroku
+import Kiroku.Store.Migrations.History.Codd qualified as Kiroku.Codd
+import Numeric qualified
+import System.Directory (doesDirectoryExist, doesFileExist)
+import System.FilePath ((</>))
+import Test.Hspec
+
+main :: IO ()
+main = hspec $ do
+    describe "native Keiro migration definition" $ do
+        it "tracks eighteen native files in manifest order" $ do
+            directory <- findMigrationsDirectory
+            manifest <- Text.lines <$> Text.IO.readFile (directory </> "manifest")
+            manifest `shouldBe` Text.pack <$> nativeMigrationFiles
+
+        it "preserves every legacy payload byte recorded by migrations.lock" $ do
+            directory <- findMigrationsDirectory
+            lockPath <- findLockfile
+            lockEntries <- parseLockfile <$> Text.IO.readFile lockPath
+            forM_ (zip (toList keiroLegacyMigrationNames) nativeMigrationFiles) $ \(legacyName, nativeName) -> do
+                bytes <- ByteString.readFile (directory </> nativeName)
+                lookup legacyName lockEntries `shouldBe` Just (checksumText bytes)
+
+        it "builds component keiro with dependency kiroku and eighteen migrations" $ do
+            plan <- requirePlan
+            let PlanDescription components = planDescription plan
+            case toList components of
+                [ ComponentDescription{name = kirokuName, dependencies = kirokuDependencies, migrations = kirokuEntries}
+                    , ComponentDescription{name = keiroName, dependencies = keiroDependencies, migrations = keiroEntries}
+                    ] -> do
+                        componentNameText kirokuName `shouldBe` "kiroku"
+                        kirokuDependencies `shouldBe` mempty
+                        length kirokuEntries `shouldBe` 8
+                        componentNameText keiroName `shouldBe` "keiro"
+                        dependencyName <- requireRight (componentName "kiroku")
+                        keiroDependencies `shouldBe` Set.singleton dependencyName
+                        length keiroEntries `shouldBe` 18
+                actual -> expectationFailure ("unexpected plan description: " <> show actual)
+            validateHistoryMappingTargets plan frameworkCoddHistoryMappings `shouldBe` Right ()
+
+        it "rejects missing and reversed Kiroku dependencies" $ do
+            kiroku <- requireRight Kiroku.kirokuMigrations
+            keiro <- requireRight keiroMigrations
+            migrationPlan (keiro :| []) `shouldSatisfy` isLeft
+            frameworkMigrationPlan keiro kiroku `shouldSatisfy` isLeft
+
+    describe "fresh native databases" $ do
+        it "applies Kiroku then Keiro, verifies strictly, and is repeatable" $ do
+            plan <- requirePlan
+            result <- withMigratedDatabase plan $ \connection -> do
+                assertSchema connection
+                let provider = providerFor connection
+                rerun <- runMigrationPlanWith defaultRunOptions provider plan >>= requireRight
+                reportOutcomes rerun `shouldBe` replicate 26 AlreadyApplied
+                verified <- verifyMigrationPlanWith defaultRunOptions provider plan >>= requireRight
+                case verified of
+                    VerificationReport verificationIssues applied pending unknown -> do
+                        verificationIssues `shouldBe` []
+                        length applied `shouldBe` 26
+                        pending `shouldBe` []
+                        unknown `shouldBe` []
+            either (expectationFailure . show) pure result
+
+        it "serializes concurrent composed applies" $ do
+            plan <- requirePlan
+            withKeiroPg $ \database -> do
+                let settings = Pg.connectionSettings database
+                (first, second) <-
+                    concurrently
+                        (runMigrationPlan defaultRunOptions settings plan >>= requireRight)
+                        (runMigrationPlan defaultRunOptions settings plan >>= requireRight)
+                sort [reportOutcomes first, reportOutcomes second]
+                    `shouldBe` sort [replicate 26 AppliedNow, replicate 26 AlreadyApplied]
+
+    describe "combined Codd history import" $ do
+        it "imports a shared Codd V5 ledger atomically without replaying target SQL" $
+            importFixture "codd"
+
+        it "imports the legacy codd_schema ledger shape" $
+            importFixture "codd_schema"
+
+        it "rejects one partial source row before creating the target ledger" $ do
+            plan <- requirePlan
+            withKeiroPg $ \database -> do
+                let settings = Pg.connectionSettings database
+                    provider = connectionProviderFromSettings settings
+                withConnection settings $ \connection -> do
+                    applyLegacyPayloads connection
+                    installCoddLedger connection "codd" True False
+                config <-
+                    requireRight
+                        (frameworkCoddSourceConfig provider True "partial fixture must fail" Confirmed)
+                imported <-
+                    importCoddHistory defaultImportOptions config provider plan frameworkCoddHistoryMappings
+                imported `shouldSatisfy` \case
+                    Left CoddPartialMigration{} -> True
+                    _ -> False
+                withConnection settings $ \connection -> do
+                    targetExists <- useSession connection (Session.statement "pgmigrate" schemaExistsStatement)
+                    targetExists `shouldBe` False
+
+        it "rejects unselected shared-ledger rows in strict mode" $ do
+            plan <- requirePlan
+            withKeiroPg $ \database -> do
+                let settings = Pg.connectionSettings database
+                    provider = connectionProviderFromSettings settings
+                withConnection settings $ \connection -> do
+                    applyLegacyPayloads connection
+                    installCoddLedger connection "codd" False True
+                config <-
+                    requireRight
+                        (frameworkCoddSourceConfig provider True "strict source fixture" Confirmed)
+                imported <-
+                    importCoddHistory defaultImportOptions config provider plan frameworkCoddHistoryMappings
+                imported `shouldSatisfy` \case
+                    Left CoddStrictSourceHasUnselected{} -> True
+                    _ -> False
+
+importFixture :: Text -> Expectation
+importFixture sourceSchema = do
+    plan <- requirePlan
+    withKeiroPg $ \database -> do
+        let settings = Pg.connectionSettings database
+            provider = connectionProviderFromSettings settings
+        withConnection settings $ \connection -> do
+            applyLegacyPayloads connection
+            installCoddLedger connection sourceSchema False False
+        config <-
+            requireRight
+                (frameworkCoddSourceConfig provider True "verified Keiro shared-ledger cutover" Confirmed)
+        first <-
+            importCoddHistory defaultImportOptions config provider plan frameworkCoddHistoryMappings
+                >>= requireRight
+        importOutcomes first `shouldBe` replicate 23 Imported
+        kirokuCanaryId <- requireRight (migrationId "kiroku" "0008-schema-management-comment")
+        keiroCanaryId <- requireRight (migrationId "keiro" "0017-schema-management-comment")
+        keiroDeadLettersId <- requireRight (migrationId "keiro" "0018")
+        verifiedBeforeCanaries <- verifyMigrationPlan defaultRunOptions settings plan >>= requireRight
+        case verifiedBeforeCanaries of
+            VerificationReport verificationIssues _ _ _ ->
+                verificationIssues
+                    `shouldBe` [ PendingMigration kirokuCanaryId
+                               , PendingMigration keiroCanaryId
+                               , PendingMigration keiroDeadLettersId
+                               ]
+        up <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
+        reportOutcomes up
+            `shouldBe` replicate 7 AlreadyApplied
+                <> [AppliedNow]
+                <> replicate 16 AlreadyApplied
+                <> [AppliedNow, AppliedNow]
+        verifiedAfterCanaries <- verifyMigrationPlan defaultRunOptions settings plan >>= requireRight
+        case verifiedAfterCanaries of
+            VerificationReport verificationIssues _ _ _ -> verificationIssues `shouldBe` []
+        rerun <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
+        reportOutcomes rerun `shouldBe` replicate 26 AlreadyApplied
+        second <-
+            importCoddHistory defaultImportOptions config provider plan frameworkCoddHistoryMappings
+                >>= requireRight
+        importOutcomes second `shouldBe` replicate 23 AlreadyImported
+        withConnection settings $ \connection -> do
+            assertSchema connection
+            sourceRows <- useSession connection (Session.statement () (sourceRowCountStatement sourceSchema))
+            sourceRows `shouldBe` 23
+            facts <- useSession connection (Session.statement () importFactsStatement)
+            facts `shouldBe` (26, 23, True)
+
+nativeMigrationFiles :: [FilePath]
+nativeMigrationFiles =
+    [ "0001-keiro-bootstrap.sql"
+    , "0002-keiro-outbox.sql"
+    , "0003-keiro-inbox.sql"
+    , "0004-keiro-timer-recovery.sql"
+    , "0005-keiro-workflow-steps.sql"
+    , "0006-keiro-awakeables.sql"
+    , "0007-keiro-workflow-children.sql"
+    , "0008-keiro-workflow-generation.sql"
+    , "0009-keiro-subscription-shards.sql"
+    , "0010-keiro-messaging-crash-recovery.sql"
+    , "0011-keiro-workflows-instances.sql"
+    , "0012-keiro-workflow-gc-index.sql"
+    , "0013-keiro-workflows-wake-after.sql"
+    , "0014-keiro-projection-dedup.sql"
+    , "0015-keiro-outbox-claim-order-index.sql"
+    , "0016-keiro-inbox-drop-received-idx.sql"
+    , "0017-schema-management-comment.sql"
+    , "0018.sql"
+    ]
+
+findMigrationsDirectory :: IO FilePath
+findMigrationsDirectory =
+    findDirectory ["keiro-migrations/migrations", "migrations"]
+
+findLockfile :: IO FilePath
+findLockfile =
+    findFile ["keiro-migrations/migrations.lock", "migrations.lock"]
+
+findDirectory :: [FilePath] -> IO FilePath
+findDirectory candidates = do
+    existing <- filterM doesDirectoryExist candidates
+    case existing of
+        directory : _ -> pure directory
+        [] -> expectationFailure ("could not find directory: " <> show candidates) >> pure "."
+
+findFile :: [FilePath] -> IO FilePath
+findFile candidates = do
+    existing <- filterM doesFileExist candidates
+    case existing of
+        path : _ -> pure path
+        [] -> expectationFailure ("could not find file: " <> show candidates) >> pure "."
+
+filterM :: (value -> IO Bool) -> [value] -> IO [value]
+filterM predicate = foldr step (pure [])
+  where
+    step value remaining = do
+        matches <- predicate value
+        values <- remaining
+        pure (if matches then value : values else values)
+
+parseLockfile :: Text -> [(FilePath, Text)]
+parseLockfile contents =
+    [ (Text.unpack filename, checksum)
+    | line <- Text.lines contents
+    , [checksum, filename] <- [Text.words line]
+    ]
+
+checksumText :: ByteString -> Text
+checksumText =
+    Text.pack
+        . concatMap renderByte
+        . ByteString.unpack
+        . migrationChecksumBytes
+        . migrationFingerprint
+  where
+    renderByte byte =
+        case Numeric.showHex byte "" of
+            [digit] -> ['0', digit]
+            digits -> digits
+
+requirePlan :: IO MigrationPlan
+requirePlan = do
+    kiroku <- requireRight Kiroku.kirokuMigrations
+    keiro <- requireRight keiroMigrations
+    requireRight (frameworkMigrationPlan kiroku keiro)
+
+requireRight :: (Show error) => Either error value -> IO value
+requireRight = either failure pure
+
+failure :: (Show value) => value -> IO result
+failure value = expectationFailure (show value) >> fail (show value)
+
+providerFor :: Connection.Connection -> ConnectionProvider
+providerFor connection = connectionProvider (\action -> Right <$> action connection)
+
+reportOutcomes :: MigrationReport -> [MigrationOutcome]
+reportOutcomes MigrationReport{results} = outcome <$> toList results
+
+importOutcomes :: HistoryImportReport -> [HistoryImportOutcome]
+importOutcomes HistoryImportReport{importResults} = importOutcome <$> toList importResults
+
+keiroPgConfig :: Pg.Config
+keiroPgConfig = Pg.defaultConfig{Pg.user = "keiro"}
+
+withKeiroPg :: (Pg.Database -> IO ()) -> IO ()
+withKeiroPg action = do
+    started <- Pg.startCached keiroPgConfig Pg.defaultCacheConfig
+    case started of
+        Left startError -> expectationFailure (show startError)
+        Right database -> action database `finally` Pg.stop database
+
+withConnection :: Settings.Settings -> (Connection.Connection -> IO value) -> IO value
+withConnection settings action = do
+    acquired <- Connection.acquire settings
+    connection <- requireRight acquired
+    action connection `finally` Connection.release connection
+
+useSession :: Connection.Connection -> Session.Session value -> IO value
+useSession connection session =
+    Connection.use connection session >>= requireRight
+
+assertSchema :: Connection.Connection -> Expectation
+assertSchema connection = do
+    healthy <- useSession connection (Session.statement () schemaFactsStatement)
+    healthy `shouldBe` True
+
+schemaFactsStatement :: Statement () Bool
+schemaFactsStatement =
+    Statement.preparable
+        """
+        SELECT bool_and(ok)
+        FROM (VALUES
+          (to_regnamespace('kiroku') IS NOT NULL),
+          (to_regclass('kiroku.events') IS NOT NULL),
+          (to_regnamespace('keiro') IS NOT NULL),
+          (to_regclass('keiro.keiro_inbox') IS NOT NULL),
+          (to_regclass('keiro.keiro_outbox') IS NOT NULL),
+          (to_regclass('keiro.keiro_timers') IS NOT NULL),
+          (to_regclass('keiro.keiro_workflows') IS NOT NULL),
+          (obj_description(to_regnamespace('kiroku'), 'pg_namespace') = 'Managed by pg-migrate component kiroku through 0008-schema-management-comment'),
+          (obj_description(to_regnamespace('keiro'), 'pg_namespace') = 'Managed by pg-migrate component keiro through 0017-schema-management-comment')
+        ) AS checks(ok)
+        """
+        Encoders.noParams
+        (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.bool)))
+
+applyLegacyPayloads :: Connection.Connection -> IO ()
+applyLegacyPayloads connection = do
+    apply Kiroku.Codd.kirokuLegacyMigrationNames Kiroku.Codd.kirokuCoddSourcePayloads
+    apply keiroLegacyMigrationNames keiroCoddSourcePayloads
+  where
+    apply names payloads =
+        forM_ names $ \name ->
+            case Map.lookup name payloads of
+                Nothing -> failure ("missing source payload " <> name)
+                Just bytes -> useSession connection (Session.script (Text.Encoding.decodeUtf8 bytes))
+
+installCoddLedger :: Connection.Connection -> Text -> Bool -> Bool -> IO ()
+installCoddLedger connection sourceSchema partial includeExtra =
+    useSession connection (Session.script (coddFixtureSql sourceSchema partial includeExtra))
+
+coddFixtureSql :: Text -> Bool -> Bool -> Text
+coddFixtureSql sourceSchema partial includeExtra =
+    Text.unlines
+        [ "CREATE SCHEMA " <> sourceSchema <> ";"
+        , "CREATE TABLE " <> sourceSchema <> ".sql_migrations ("
+        , "  id serial NOT NULL, migration_timestamp timestamptz NOT NULL,"
+        , "  applied_at timestamptz, name text NOT NULL, application_duration interval,"
+        , "  num_applied_statements int, no_txn_failed_at timestamptz, txnid bigint, connid int"
+        , ");"
+        , "INSERT INTO " <> sourceSchema <> ".sql_migrations"
+        , "  (migration_timestamp, applied_at, name, application_duration, num_applied_statements, no_txn_failed_at, txnid, connid) VALUES"
+        , Text.intercalate ",\n" (zipWith renderRow [1 :: Int ..] filenames) <> ";"
+        ]
+  where
+    selected = toList Kiroku.Codd.kirokuLegacyMigrationNames <> toList keiroLegacyMigrationNames
+    filenames = selected <> ["application-owned-extra.sql" | includeExtra]
+    renderRow index filename =
+        "('2026-01-01 00:00:00+00'::timestamptz + interval '"
+            <> Text.pack (show index)
+            <> " seconds', "
+            <> appliedAt index
+            <> ", '"
+            <> Text.pack filename
+            <> "', interval '1 second', 1, "
+            <> failureAt index
+            <> ", 1, 1)"
+    appliedAt index
+        | partial && index == 11 = "NULL"
+        | otherwise = "'2026-01-01 00:01:00+00'::timestamptz + interval '" <> Text.pack (show index) <> " seconds'"
+    failureAt index
+        | partial && index == 11 = "'2026-01-01 00:02:00+00'::timestamptz"
+        | otherwise = "NULL"
+
+schemaExistsStatement :: Statement Text Bool
+schemaExistsStatement =
+    Statement.preparable
+        "SELECT to_regnamespace($1) IS NOT NULL"
+        (Encoders.param (Encoders.nonNullable Encoders.text))
+        (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.bool)))
+
+sourceRowCountStatement :: Text -> Statement () Int64
+sourceRowCountStatement sourceSchema =
+    Statement.unpreparable
+        ("SELECT count(*) FROM " <> sourceSchema <> ".sql_migrations")
+        Encoders.noParams
+        (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.int8)))
+
+importFactsStatement :: Statement () (Int64, Int64, Bool)
+importFactsStatement =
+    Statement.preparable
+        """
+        SELECT
+          (SELECT count(*) FROM pgmigrate.migrations),
+          (SELECT count(*) FROM pgmigrate.history_imports),
+          (SELECT bool_and(source_evidence #>> '{satisfying_evidence,0,details,adapter}' = 'codd') FROM pgmigrate.history_imports)
+        """
+        Encoders.noParams
+        ( Decoders.singleRow
+            ( (,,)
+                <$> column Decoders.int8
+                <*> column Decoders.int8
+                <*> column Decoders.bool
+            )
+        )
+  where
+    column = Decoders.column . Decoders.nonNullable
