packages feed

keiro-migrations (empty) → 0.2.0.0

raw patch · 318 files changed

+7353/−0 lines, 318 filesdep +aesondep +asyncdep +attoparsec

Dependencies added: aeson, async, attoparsec, base, bytestring, codd, codd-extras, containers, contravariant-extras, directory, ephemeral-pg, file-embed, filepath, generic-lens, hasql, hasql-pool, hspec, keiro-migrations, kiroku-store, kiroku-store-migrations, lens, optparse-applicative, pg-migrate, pg-migrate-cli, pg-migrate-embed, pg-migrate-import-codd, pg-migrate-test-support, template-haskell, temporary, text, time, vector

Files

+ CHANGELOG.md view
@@ -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.
+ README.md view
@@ -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+```
+ app/Main.hs view
@@ -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
+ app/WriteExpectedSchema.hs view
@@ -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)
+ expected-schema/v18/db-settings view
@@ -0,0 +1,8 @@+{+    "collate": "c",+    "ctype": "c",+    "encoding": "utf8",+    "owner": "keiro",+    "public_privileges": "[[\"cT\", \"keiro\"]]",+    "settings": null+}
+ expected-schema/v18/roles/keiro view
@@ -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+}
+ expected-schema/v18/schemas/keiro/objrep view
@@ -0,0 +1,11 @@+{+    "owner": "keiro",+    "privileges": {+        "keiro": [+            [+                "CU",+                "keiro"+            ]+        ]+    }+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/awakeable_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/completed_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/owner_workflow_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/payload view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_awakeable_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_owner_workflow_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_chk view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/constraints/keiro_awakeables_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_owner_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pending_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/indexes/keiro_awakeables_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_awakeables/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attempt_count view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/attributes view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/causation_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/completed_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/content_type view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/correlation_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/dedupe_key view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/destination view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/event_type view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/failed_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_offset view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_partition view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/kafka_topic view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/last_error view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/message_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/occurred_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/payload_bytes view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/received_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_fingerprint view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_registry view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_subject view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/schema_version_ref view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_event_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/source_global_position view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/traceparent view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/cols/tracestate view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_attempt_count_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_content_type_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_dedupe_key_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_payload_bytes_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_received_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_source_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/constraints/keiro_inbox_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_backlog_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_completed_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/indexes/keiro_inbox_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_inbox/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attempt_count view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/attributes view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/causation_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/content_type view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/correlation_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/destination view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/event_type view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/last_error view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/message_key view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/next_attempt_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/occurred_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/outbox_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/payload_bytes view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/published_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_fingerprint view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_registry view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_subject view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/schema_version_ref view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_event_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/source_global_position view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/traceparent view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/tracestate view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_attempt_count_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_content_type_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_destination_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_event_type_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_message_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_next_attempt_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_occurred_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_outbox_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_payload_bytes_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_schema_version_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_message_id_key view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_source_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/constraints/keiro_outbox_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_claim_order_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_head_of_line_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pending_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_sent_gc_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_message_id_key view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/indexes/keiro_outbox_source_order_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_outbox/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/applied_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/event_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/cols/projection_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_applied_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_event_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/constraints/keiro_projection_dedup_projection_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_applied_at_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/indexes/keiro_projection_dedup_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_projection_dedup/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/last_built_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/shape_hash view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/cols/version view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_shape_hash_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/constraints/keiro_read_models_version_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/indexes/keiro_read_models_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_read_models/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/regfile_shape_hash view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/state_codec_version view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/stream_version view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_regfile_shape_hash_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_codec_version_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_state_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_stream_version_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/constraints/keiro_snapshots_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_compat_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/indexes/keiro_snapshots_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_snapshots/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/bucket view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/heartbeat_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/lease_expires_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/owner_worker_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/shard_count view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/subscription_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_bucket_range_chk view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_count_chk view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_shard_count_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_subscription_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/constraints/keiro_subscription_shards_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_lease_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_owner_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/indexes/keiro_subscription_shards_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_subscription_shards/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/attempts view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/correlation_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fire_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/fired_event_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/last_error view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/payload view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/process_manager_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/timer_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_attempts_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_correlation_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_fire_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_payload_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_process_manager_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_timer_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/constraints/keiro_timers_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_due_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/indexes/keiro_timers_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_timers/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/await_step view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/child_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/completed_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/parent_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/result view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_await_step_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_child_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_parent_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_chk view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/constraints/keiro_workflow_children_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_parent_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/indexes/keiro_workflow_children_running_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_children/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/generation view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/recorded_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/result view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/step_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/cols/workflow_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_generation_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_recorded_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_result_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_step_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/constraints/keiro_workflow_steps_workflow_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/indexes/keiro_workflow_steps_workflow_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflow_steps/objrep view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/attempts view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/completed_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/created_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/generation view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/last_error view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/lease_expires_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/leased_by view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/next_attempt_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/status view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/updated_at view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/wake_after view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_id view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/cols/workflow_name view
@@ -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"+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_attempts_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_created_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_generation_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_chk view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_status_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_updated_at_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_id_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/constraints/keiro_workflows_workflow_name_not_null view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_active_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_gc_idx view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/indexes/keiro_workflows_pkey view
@@ -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+}
+ expected-schema/v18/schemas/keiro/tables/keiro_workflows/objrep view
@@ -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+}
+ keiro-migrations.cabal view
@@ -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
+ migrations.lock view
@@ -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
+ migrations/0001-keiro-bootstrap.sql view
@@ -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);
+ migrations/0002-keiro-outbox.sql view
@@ -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;
+ migrations/0003-keiro-inbox.sql view
@@ -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';
+ migrations/0004-keiro-timer-recovery.sql view
@@ -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');
+ migrations/0005-keiro-workflow-steps.sql view
@@ -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);
+ migrations/0006-keiro-awakeables.sql view
@@ -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);
+ migrations/0007-keiro-workflow-children.sql view
@@ -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';
+ migrations/0008-keiro-workflow-generation.sql view
@@ -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);
+ migrations/0009-keiro-subscription-shards.sql view
@@ -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);
+ migrations/0010-keiro-messaging-crash-recovery.sql view
@@ -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');
+ migrations/0011-keiro-workflows-instances.sql view
@@ -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'));
+ migrations/0012-keiro-workflow-gc-index.sql view
@@ -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);
+ migrations/0013-keiro-workflows-wake-after.sql view
@@ -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;
+ migrations/0014-keiro-projection-dedup.sql view
@@ -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);
+ migrations/0015-keiro-outbox-claim-order-index.sql view
@@ -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');
+ migrations/0016-keiro-inbox-drop-received-idx.sql view
@@ -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;
+ migrations/0017-schema-management-comment.sql view
@@ -0,0 +1,2 @@+COMMENT ON SCHEMA keiro IS+  'Managed by pg-migrate component keiro through 0017-schema-management-comment';
+ migrations/0018.sql view
@@ -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);
+ migrations/manifest view
@@ -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
+ sql-migrations/2026-05-17-13-58-15-keiro-bootstrap.sql view
@@ -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);
+ sql-migrations/2026-05-19-12-55-02-keiro-outbox.sql view
@@ -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;
+ sql-migrations/2026-05-19-13-05-23-keiro-inbox.sql view
@@ -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';
+ sql-migrations/2026-06-03-05-14-28-keiro-timer-recovery.sql view
@@ -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');
+ sql-migrations/2026-06-03-16-10-05-keiro-workflow-steps.sql view
@@ -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);
+ sql-migrations/2026-06-03-18-19-41-keiro-awakeables.sql view
@@ -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);
+ sql-migrations/2026-06-03-19-49-23-keiro-workflow-children.sql view
@@ -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';
+ sql-migrations/2026-06-04-02-12-28-keiro-workflow-generation.sql view
@@ -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);
+ sql-migrations/2026-06-04-03-53-34-keiro-subscription-shards.sql view
@@ -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);
+ sql-migrations/2026-06-15-13-22-31-keiro-messaging-crash-recovery.sql view
@@ -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');
+ sql-migrations/2026-06-15-15-07-25-keiro-workflows-instances.sql view
@@ -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'));
+ sql-migrations/2026-06-15-17-53-48-keiro-workflow-gc-index.sql view
@@ -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);
+ sql-migrations/2026-06-15-18-01-33-keiro-workflows-wake-after.sql view
@@ -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;
+ sql-migrations/2026-06-15-21-49-37-keiro-projection-dedup.sql view
@@ -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);
+ sql-migrations/2026-07-02-00-15-48-keiro-outbox-claim-order-index.sql view
@@ -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');
+ sql-migrations/2026-07-02-00-58-54-keiro-inbox-drop-received-idx.sql view
@@ -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;
+ src/Keiro/Migrations.hs view
@@ -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])
+ src/Keiro/Migrations/ExpectedSchema.hs view
@@ -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
+ src/Keiro/Migrations/History/Codd.hs view
@@ -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
+ src/Keiro/Migrations/Internal/Definition.hs view
@@ -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
+ src/Keiro/Migrations/Internal/EmbedFile.hs view
@@ -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))))
+ src/Keiro/Migrations/LegacyCodd.hs view
@@ -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
+ src/Keiro/Migrations/New.hs view
@@ -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+        }
+ test-legacy/Main.hs view
@@ -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)))
+ test/Main.hs view
@@ -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