diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,29 @@
 # Changelog
 
+## 0.5.0.0 — 2026-09-18
+
+### Breaking Changes
+
+* Requires the `pg-migrate` 1.2 series (`pg-migrate`, `pg-migrate-embed`,
+  `pg-migrate-import-codd`, `pg-migrate-cli` `^>=1.2.0.0`, was `^>=1.1.0.0`).
+  `Kiroku.Store.Migrations` re-exports `MigrationComponent`, `MigrationPlan`,
+  `DefinitionError` and `PlanError` from `pg-migrate`, so this major bump
+  follows the dependency's. pg-migrate 1.2 has no API or behavior changes
+  relative to 1.1: consumers only need to move their own `pg-migrate` bounds
+  to `^>=1.2`. The migration payloads, their checksums and the
+  `kiroku-store-migrate` executable are unchanged.
+
+### Other Changes
+
+* The test-suite requires `ephemeral-pg >=0.3.1 && <0.4` and
+  `pg-migrate-test-support ^>=1.2.0.0`, and builds on its
+  `defaultEphemeralConfig` so orphaned clusters from killed runs are reaped.
+* The UUIDv7 test now requires the PostgreSQL 18 builtin to exist exactly when
+  the server major is 18, and reports which route supplied `kiroku.uuidv7()`.
+  A run can no longer pass while exercising a different major than intended.
+* The README documents running the suites against both PostgreSQL 17 and 18
+  (`just test-matrix`, `just test-pg <major>`).
+
 ## 0.4.0.0 — 2026-08-16
 
 ### Breaking Changes
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -105,9 +105,37 @@
 source-ledger preservation. The relation contract suite also proves the frozen
 `kiroku.subscription_checkpoints_v1` catalog, non-null value semantics,
 owner-rights privilege isolation, structural read-only behavior, downstream
-view survival, and indexed query plan. `cabal test kiroku-store:kiroku-store-test`
-consumes the same native plan through `kiroku-test-support` and proves the
-complete store behavior, including append and read scenarios.
+view survival, and indexed query plan. It also applies the plan's pending tail
+in a session that never ran `0001` and cannot reach the Kiroku schema through
+`search_path`, which is the upgrade shape BUG-1 lived in.
+`cabal test kiroku-store:kiroku-store-test` consumes the same native plan
+through `kiroku-test-support` and proves the complete store behavior, including
+append and read scenarios.
+
+### Both PostgreSQL majors
+
+The suites reach PostgreSQL through `ephemeral-pg`, which runs whichever server
+is on `PATH`, and the default dev shell carries only PostgreSQL 18. Migration
+behavior differs by major -- `uuidv7()` is a builtin on 18 and comes from
+`0001`'s fallback on 17 -- so a single-major run cannot cover this package.
+Run both:
+
+```bash
+just test-matrix        # every suite, on PostgreSQL 17 and then 18
+just test-pg 17         # one major
+```
+
+Each recipe enters the matching `nix develop .#postgresql<major>` shell and
+refuses to run if that major is not the one on `PATH`. The suite reports the
+route it exercised, so a run states which half of the matrix it covered:
+
+```
+PostgreSQL 17 UUIDv7 generator: kiroku.uuidv7() via 0001's fallback
+PostgreSQL 18 UUIDv7 generator: kiroku.uuidv7() via 0010's alias for the builtin
+```
+
+Run the matrix before releasing this package, and after any migration change
+that touches version-dependent behavior.
 
 Migration `0010` adds replay-history retention leases, the per-schema
 coordinator, an indexed active-lease predicate, and statement-level
diff --git a/kiroku-store-migrations.cabal b/kiroku-store-migrations.cabal
--- a/kiroku-store-migrations.cabal
+++ b/kiroku-store-migrations.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               kiroku-store-migrations
-version:            0.4.0.0
+version:            0.5.0.0
 synopsis:           Schema migrations for kiroku-store
 description:
   Native pg-migrate component, Codd history mapping, and migration executable
@@ -52,9 +52,9 @@
     , bytestring              >=0.11     && <0.13
     , containers              >=0.6      && <0.8
     , filepath                >=1.4      && <1.6
-    , pg-migrate              ^>=1.1.0.0
-    , pg-migrate-embed        ^>=1.1.0.0
-    , pg-migrate-import-codd  ^>=1.1.0.0
+    , pg-migrate              ^>=1.2.0.0
+    , pg-migrate-embed        ^>=1.2.0.0
+    , pg-migrate-import-codd  ^>=1.2.0.0
     , template-haskell        >=2.20     && <2.24
     , text                    >=2.0      && <2.2
 
@@ -70,8 +70,8 @@
     , hasql                    >=1.10     && <1.11
     , kiroku-store-migrations
     , optparse-applicative     >=0.17     && <0.20
-    , pg-migrate               ^>=1.1.0.0
-    , pg-migrate-cli           ^>=1.1.0.0
+    , pg-migrate               ^>=1.2.0.0
+    , pg-migrate-cli           ^>=1.2.0.0
     , text                     >=2.0      && <2.2
 
 test-suite kiroku-store-migrations-test
@@ -85,14 +85,14 @@
     , base                     >=4.18     && <5
     , bytestring               >=0.11     && <0.13
     , directory                >=1.3
-    , ephemeral-pg             >=0.2      && <0.3
+    , ephemeral-pg             >=0.3.1    && <0.4
     , filepath                 >=1.4      && <1.6
     , hasql                    >=1.10     && <1.11
     , hspec                    >=2.10     && <2.12
     , kiroku-store-migrations
-    , pg-migrate               ^>=1.1.0.0
-    , pg-migrate-import-codd   ^>=1.1.0.0
-    , pg-migrate-test-support  ^>=1.1.0.0
+    , pg-migrate               ^>=1.2.0.0
+    , pg-migrate-import-codd   ^>=1.2.0.0
+    , pg-migrate-test-support  ^>=1.2.0.0
     , temporary                >=1.3      && <1.4
     , text                     >=2.0      && <2.2
     , time                     >=1.12     && <1.15
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -21,7 +21,7 @@
 import Database.PostgreSQL.Migrate
 import Database.PostgreSQL.Migrate.History.Codd
 import Database.PostgreSQL.Migrate.Internal (migrationChecksumBytes)
-import Database.PostgreSQL.Migrate.Test (withMigratedDatabase)
+import Database.PostgreSQL.Migrate.Test (defaultEphemeralConfig, withMigratedDatabase)
 import EphemeralPg qualified as Pg
 import EphemeralPg.Config qualified as PgConfig
 import Hasql.Connection qualified as Connection
@@ -315,16 +315,27 @@
                         length applied `shouldBe` length nativeMigrationFiles
                 withConnection settings assertSchema
 
-        it "resolves the lease default through the qualified component generator" $ do
+        -- kiroku.uuidv7() is the component's version-independent generator, but
+        -- it arrives by a different route on each major: 0001's fallback on
+        -- PostgreSQL 17, 0010's alias for the builtin on PostgreSQL 18. This
+        -- case pins the route to the running server, so an acceptance run that
+        -- believes it exercised one major and actually ran the other fails here
+        -- instead of passing quietly. Both routes need a run to be covered --
+        -- see `just test-matrix`.
+        it "publishes the generator by the route its PostgreSQL major requires" $ do
             plan <- requirePlan
             result <- withMigratedDatabase plan $ \connection -> do
-                -- kiroku.uuidv7() is the component's version-independent
-                -- generator: PostgreSQL 17 gets 0001's fallback, PostgreSQL 18
-                -- gets 0010's alias for the builtin. Either way the stored
-                -- default names it, and no later migration needs search_path to
-                -- reach a UUIDv7 generator.
-                generator <- useSession connection (Session.statement () leaseDefaultStatement)
-                generator `shouldBe` (True, Just "kiroku.uuidv7()")
+                (major, builtinExists, generatorExists, leaseDefault) <-
+                    useSession connection (Session.statement () leaseDefaultStatement)
+                Text.IO.putStrLn
+                    ( "PostgreSQL "
+                        <> Text.pack (show major)
+                        <> " UUIDv7 generator: kiroku.uuidv7() via "
+                        <> (if builtinExists then "0010's alias for the builtin" else "0001's fallback")
+                    )
+                generatorExists `shouldBe` True
+                builtinExists `shouldBe` (major >= 18)
+                leaseDefault `shouldBe` Just "kiroku.uuidv7()"
             either (expectationFailure . show) pure result
 
         it "acquires a lease from a session whose search_path excludes kiroku" $ do
@@ -512,12 +523,18 @@
 importOutcomes :: HistoryImportReport -> [HistoryImportOutcome]
 importOutcomes HistoryImportReport{importResults} = importOutcome <$> toList importResults
 
-kirokuPgConfig :: Pg.Config
-kirokuPgConfig = Pg.defaultConfig{PgConfig.user = "kiroku"}
+{- | Built on pg-migrate's stable per-user temporary root, which 'withMigratedDatabase'
+also uses, so ephemeral-pg's startup sweep reclaims clusters left by killed runs.
+-}
+kirokuPgConfig :: IO Pg.Config
+kirokuPgConfig = do
+    base <- defaultEphemeralConfig
+    pure base{PgConfig.user = "kiroku"}
 
 withKirokuPg :: (Pg.Database -> IO ()) -> IO ()
 withKirokuPg action = do
-    started <- Pg.startCached kirokuPgConfig Pg.defaultCacheConfig
+    config <- kirokuPgConfig
+    started <- Pg.startCached config Pg.defaultCacheConfig
     case started of
         Left startError -> expectationFailure (show startError)
         Right database -> action database `finally` Pg.stop database
@@ -559,12 +576,17 @@
         Encoders.noParams
         (Decoders.singleRow (Decoders.column (Decoders.nonNullable Decoders.bool)))
 
--- | Whether @kiroku.uuidv7()@ exists, and the stored @lease_id@ default.
-leaseDefaultStatement :: Statement () (Bool, Maybe Text)
+{- | The server major, whether each UUIDv7 generator exists, and the stored
+@lease_id@ default. Together these pin which route published
+@kiroku.uuidv7()@ on the running server.
+-}
+leaseDefaultStatement :: Statement () (Int32, Bool, Bool, Maybe Text)
 leaseDefaultStatement =
     Statement.preparable
         """
-        SELECT to_regprocedure('kiroku.uuidv7()') IS NOT NULL,
+        SELECT pg_catalog.current_setting('server_version_num')::int / 10000,
+               to_regprocedure('pg_catalog.uuidv7()') IS NOT NULL,
+               to_regprocedure('kiroku.uuidv7()') IS NOT NULL,
                (SELECT pg_catalog.pg_get_expr(stored.adbin, stored.adrelid)
                   FROM pg_catalog.pg_attrdef AS stored
                   JOIN pg_catalog.pg_attribute AS attribute
@@ -575,8 +597,10 @@
         """
         Encoders.noParams
         ( Decoders.singleRow
-            ( (,)
-                <$> Decoders.column (Decoders.nonNullable Decoders.bool)
+            ( (,,,)
+                <$> Decoders.column (Decoders.nonNullable Decoders.int4)
+                <*> Decoders.column (Decoders.nonNullable Decoders.bool)
+                <*> Decoders.column (Decoders.nonNullable Decoders.bool)
                 <*> Decoders.column (Decoders.nullable Decoders.text)
             )
         )
