packages feed

kioku-migrations 0.4.1.0 → 0.5.0.0

raw patch · 7 files changed

+461/−12 lines, 7 filesdep ~keiro-migrationsdep ~kioku-migrationsPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: keiro-migrations, kioku-migrations

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,38 @@ # Changelog +## 0.5.0.0 — 2026-08-22++### Breaking Changes++- Corrected the released payload of `0011-kioku-memory-space-partition.sql` so its final statement+  is `RESET search_path;`. Its exact-byte SHA-256 changes from+  `eee9cd252b32b563c50f8457596347fff1b2e4d3ea4dafe5b45043e991624192` to+  `6c83d3f01f784d0d9395953d5bb1763b8eea6cd9439073df42f79775a85197a9`. Databases that+  already applied `0011` under 0.4.0.0 or 0.4.1.0 must run+  `ledger-fixups/2026-08-19-rebaseline-0011-checksum.sql` once before the corrected `up` or+  `verify`; databases where `0011` is pending apply it normally. The next release must use the+  0.5.0.0 series.+- Raised `keiro-migrations` to `^>=0.14.0.0`. Its appended `keiro/0031` migration adds bounded+  terminal rejected-outbox audit fields and updates ordering indexes. The composed plan now+  contains 55 migrations: Kiroku 11, Keiro 31, and Kioku 13.++### Added++- Added `0013-partition-aware-fts-index.sql`. When `btree_gin` is installed or can be installed,+  it replaces the content-only full-text GIN with an active-only GIN over+  `(memory_space_id, namespace, content_tsv)`, preventing one space's recall cost from growing+  with matching content in unrelated spaces. If extension installation is unavailable, the+  migration succeeds and retains the old index as a correctness-preserving fallback.+- Added the exact-checksum, idempotent 0.4.x ledger re-baseline at+  `ledger-fixups/2026-08-19-rebaseline-0011-checksum.sql` and tests for mismatch recovery,+  repeated execution, and a missing custom ledger configuration.++### Fixed++- Migration `0011` now restores the configured database or role `search_path` before its own+  transaction commits. The composed-plan regression applies all 55 framework migrations and an+  unqualified host migration on one connection with a nonstandard `host_app, pg_catalog` default.+ ## 0.4.1.0 — 2026-08-18  ### Changed
kioku-migrations.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               kioku-migrations-version:            0.4.1.0+version:            0.5.0.0 synopsis:           Schema migrations for kioku description:   Owns kioku schema evolution as a native pg-migrate component and composes@@ -21,6 +21,7 @@  extra-source-files:   codd-upgrade/*.sql+  ledger-fixups/*.sql   migrations/*.sql   migrations/manifest   migrations.lock@@ -58,7 +59,7 @@     , containers               >=0.6       && <0.8     , hasql                    >=1.10      && <1.11     , hasql-transaction        >=1.2       && <1.3-    , keiro-migrations         ^>=0.13.0.0+    , keiro-migrations         ^>=0.14.0.0     , kiroku-store-migrations  ^>=0.4.0.0     , pg-migrate               ^>=1.1.0.0     , pg-migrate-embed         ^>=1.1.0.0@@ -75,7 +76,7 @@     , base                     >=4.18     && <5     , ephemeral-pg             ^>=0.2.2.0     , hasql                    >=1.10     && <1.11-    , kioku-migrations         ^>=0.4.1.0+    , kioku-migrations         ^>=0.5.0.0     , pg-migrate-test-support  ^>=1.1.0.0     , text                     >=2.0      && <2.2 @@ -89,9 +90,11 @@   -- the bridge must keep working until the last codd-era database crosses over.   ghc-options:    -threaded -rtsopts -with-rtsopts=-N -Wno-deprecations   build-depends:-    , base                           >=4.18     && <5+    , base                           >=4.18      && <5+    , containers                     >=0.6       && <0.8     , hasql                          >=1.6-    , kioku-migrations               ^>=0.4.1.0+    , keiro-migrations               ^>=0.14.0.0+    , kioku-migrations               ^>=0.5.0.0     , kioku-migrations:test-support     , kiroku-store-migrations        ^>=0.4.0.0     , pg-migrate                     ^>=1.1.0.0@@ -99,4 +102,4 @@     , pg-migrate-import-codd         ^>=1.1.0.0     , tasty                          >=1.5     , tasty-hunit                    >=0.10-    , text                           >=2.0      && <2.2+    , text                           >=2.0       && <2.2
+ ledger-fixups/2026-08-19-rebaseline-0011-checksum.sql view
@@ -0,0 +1,84 @@+-- Ledger re-baseline for the corrected payload of Kioku migration 0011 (BUG-1).+--+-- kioku-migrations 0.4.0.0 and 0.4.1.0 shipped a payload whose plain+-- `SET search_path TO kiroku, pg_catalog` survived transaction commit on the+-- connection pg-migrate reuses for a composed plan. A later host component on+-- that connection could therefore fail to resolve its own unqualified tables.+-- The corrected 0011 explicitly resets the session value before committing.+--+-- pg-migrate verifies the exact SHA-256 of every applied payload. Correcting+-- 0011 therefore changes its checksum, and a database that already applied the+-- withdrawn payload will report MigrationChecksumMismatch until its stored+-- checksum is re-baselined. This script performs that one re-baseline and+-- nothing else.+--+-- WHEN TO RUN: after taking a verified backup, once per long-lived database+-- that ALREADY APPLIED kioku/0011 under kioku-migrations 0.4.0.0 or 0.4.1.0,+-- BEFORE the first `up` or `verify` using the corrected package. A database+-- where 0011 is still pending does not need this script; it applies the+-- corrected payload normally. Fresh and ephemeral databases do not need it.+--+-- WHY NO SCHEMA MIGRATION FOLLOWS: the withdrawn and corrected payloads have+-- identical durable schema and data effects. The only withdrawn effect was a+-- connection-local search_path value, and that ceased to exist when the old+-- runner connection closed.+--+-- SAFETY: the UPDATE matches only the applied kioku/0011 row carrying the exact+-- withdrawn checksum. It is idempotent: a second run, a pending migration, an+-- already-corrected row, or any other checksum changes zero rows. This narrow+-- correction is not permission to bypass any other checksum mismatch.+--+-- LEDGER LOCATION: pg-migrate's default ledger schema is `pgmigrate`. If the+-- host configured a different schema through LedgerConfig, change the schema+-- name in the to_regclass call below and nowhere else.++BEGIN;++DO $$+DECLARE+  ledger_table regclass;+  withdrawn_checksum bytea :=+    decode('eee9cd252b32b563c50f8457596347fff1b2e4d3ea4dafe5b45043e991624192', 'hex');+  corrected_checksum bytea :=+    decode('6c83d3f01f784d0d9395953d5bb1763b8eea6cd9439073df42f79775a85197a9', 'hex');+  rebaselined integer;+BEGIN+  ledger_table := to_regclass('pgmigrate.migrations');++  IF ledger_table IS NULL THEN+    RAISE EXCEPTION+      'Could not find pgmigrate.migrations; edit this script if LedgerConfig uses a different schema';+  END IF;++  EXECUTE format(+    'UPDATE %s SET checksum = $1+       WHERE component = ''kioku''+         AND migration = ''0011-kioku-memory-space-partition''+         AND status = ''applied''+         AND checksum = $2',+    ledger_table+  )+  USING corrected_checksum, withdrawn_checksum;++  GET DIAGNOSTICS rebaselined = ROW_COUNT;++  IF rebaselined = 0 THEN+    RAISE NOTICE+      'no applied kioku/0011 row carried the withdrawn checksum; nothing to re-baseline';+  ELSIF rebaselined = 1 THEN+    RAISE NOTICE 're-baselined the applied kioku/0011 checksum';+  ELSE+    RAISE EXCEPTION 're-baselined % kioku/0011 rows; expected at most one', rebaselined;+  END IF;+END $$;++-- Sanity check: kioku/0011 must now carry the corrected checksum, or be absent+-- because this database has not reached 0011 yet. Expect one row with ok =+-- true, or zero rows.+--   SELECT encode(checksum, 'hex') =+--          '6c83d3f01f784d0d9395953d5bb1763b8eea6cd9439073df42f79775a85197a9' AS ok+--   FROM pgmigrate.migrations+--   WHERE component = 'kioku'+--     AND migration = '0011-kioku-memory-space-partition';++COMMIT;
migrations/0011-kioku-memory-space-partition.sql view
@@ -224,3 +224,10 @@ -- kioku_turns_session_idx was of UNIQUE (session_id, turn_index) before the schema-hardening -- migration dropped it. Keeping it would be pure write amplification. DROP INDEX IF EXISTS kioku_scenes_scope_idx;++-- pg-migrate reuses one connection for the complete composed plan, so the plain SET at the+-- start of this migration would survive transaction commit. Migration 0010 may already have+-- supplied the same leaked value, which is why changing only this migration's opening SET to+-- SET LOCAL would not restore the host configuration. Reset the session value explicitly so+-- migration 0012 and every later component inherit the configured database or role default.+RESET search_path;
+ migrations/0013-partition-aware-fts-index.sql view
@@ -0,0 +1,46 @@+-- Migration: partition-aware-fts-index+-- Created: 2026-08-21 UTC+--+-- Every full-text recall statement constrains an active row by memory space and namespace+-- before matching content_tsv. btree_gin supplies GIN operator classes for the two text+-- columns, so one partial index can enforce that complete candidate boundary instead of+-- enumerating matches from unrelated tenants through the historical content-only GIN.+--+-- This is an optional access-path improvement. Some managed PostgreSQL roles cannot install+-- extensions, so the extension attempt is isolated in a PL/pgSQL subtransaction. The old+-- content-only GIN is dropped only after the replacement is visible in the catalog; otherwise+-- it remains as the correctness-preserving fallback.++DO $$+DECLARE+  btree_gin_available boolean := false;+BEGIN+  BEGIN+    CREATE EXTENSION IF NOT EXISTS btree_gin;+  EXCEPTION+    WHEN OTHERS THEN+      RAISE NOTICE+        'btree_gin extension is unavailable (%: %); retaining kioku_memories_tsv_idx',+        SQLSTATE,+        SQLERRM;+  END;++  SELECT EXISTS (+    SELECT 1+    FROM pg_catalog.pg_extension+    WHERE extname = 'btree_gin'+  ) INTO btree_gin_available;++  IF btree_gin_available THEN+    CREATE INDEX IF NOT EXISTS kioku_memories_space_namespace_tsv_idx+      ON kioku.memories USING gin (memory_space_id, namespace, content_tsv)+      WHERE status = 'active';++    IF to_regclass('kioku.kioku_memories_space_namespace_tsv_idx') IS NOT NULL THEN+      DROP INDEX IF EXISTS kioku.kioku_memories_tsv_idx;+    END IF;+  ELSE+    RAISE NOTICE+      'btree_gin is not installed; retaining kioku_memories_tsv_idx as the full-text fallback';+  END IF;+END $$;
migrations/manifest view
@@ -10,3 +10,4 @@ 0010-kioku-scope-identity-recompute.sql 0011-kioku-memory-space-partition.sql 0012-relocate-projections-to-kioku-schema.sql+0013-partition-aware-fts-index.sql
test/Main.hs view
@@ -16,6 +16,7 @@ import Data.List (sort) import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe (mapMaybe)+import Data.Set qualified as Set import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding qualified as Text.Encoding@@ -31,10 +32,12 @@     MigrationPlan,     MigrationReport (..),     MigrationResult (..),+    VerificationIssue (..),     VerificationReport (..),     connectionProviderFromSettings,     defaultImportOptions,     defaultRunOptions,+    migrationComponentFromEmbeddedSql,     migrationId,     migrationPlan,     runMigrationPlan,@@ -51,7 +54,8 @@ import Hasql.Session (Session) import Hasql.Session qualified as Session import Hasql.Statement (Statement, preparable)-import Kioku.Migrations (kiokuMigrationPlan)+import Keiro.Migrations qualified as KeiroMigrations+import Kioku.Migrations (kiokuMigrationPlan, kiokuMigrations) import Kioku.Migrations.History.Codd   ( cohortCoddHistoryMappings,     cohortCoddSourceConfig,@@ -77,10 +81,24 @@           testCase "public schema (long-lived dev databases)" (assertRegistryBump "public")         ],       testCase "the full migration chain applies to a fresh database" testFreshDatabase,+      testGroup+        "migration-session isolation"+        [testCase "restores the host search path before later components" testHostSearchPathRestored],+      testGroup+        "released checksum re-baseline"+        [ testCase "restores strict verification and is idempotent" testLedgerChecksumRebaseline,+          testCase "rejects a missing default ledger table" testLedgerFixupRequiresDefaultLedger+        ],       testCase "the migration manifest is complete and valid" testManifestIntegrity,       testCase "the pinned Codd history maps 30 known plan targets" testHistoryMappings,       testCase "the pre-cutover Codd cohort imports 30 rows and applies only the forward migrations" testCoddCohortImport,       testGroup+        "the partition-aware full-text index"+        [ testCase "a normal database replaces the content-only GIN" testPartitionAwareFtsIndex,+          testCase "extension failure is handled before the fallback can be dropped" testPartitionAwareFtsFallbackOrdering,+          testCase "re-applying its body changes nothing" testPartitionAwareFtsIndexIdempotent+        ],+      testGroup         "the memory-space partition migration"         [ testCase "backfills every pre-partition row into the legacy space" testMemorySpaceBackfill,           testCase "refuses to finish when a derived row disagrees with its session" testMemorySpaceDriftAborts,@@ -221,6 +239,261 @@     E.noParams     (D.singleRow (D.column (D.nonNullable D.bool))) +-- * Migration-session isolation++-- | A host component that follows Kioku on the same pg-migrate connection must inherit the+-- database's configured search path, not session state left by a Kioku migration.+testHostSearchPathRestored :: Assertion+testHostSearchPathRestored =+  withBareDatabase \connStr -> do+    withConnection connStr \conn -> do+      run conn (Session.script hostSchemaSetup)+      databaseName <- run conn (Session.statement () quotedCurrentDatabase)+      run conn (Session.script ("ALTER DATABASE " <> databaseName <> " SET search_path TO host_app, pg_catalog"))++    plan <- hostComposedPlan+    report <-+      runMigrationPlan defaultRunOptions (Settings.connectionString connStr) plan+        >>= either (assertFailure . show) pure+    hostMigration <- either (assertFailure . show) pure (migrationId "host" "0001-host-table")+    let MigrationReport {results = migrationResults} = report+    case reverse (toList migrationResults) of+      MigrationResult {migration = finalMigration, outcome = finalOutcome} : _ -> do+        finalMigration @?= hostMigration+        finalOutcome @?= AppliedNow+      [] -> assertFailure "the composed migration plan returned no results"+    query connStr hostMigratedColumnExists >>= (@?= True)++hostComposedPlan :: IO MigrationPlan+hostComposedPlan = do+  kiroku <- either (fail . show) pure KirokuMigrations.kirokuMigrations+  keiro <- either (fail . show) pure KeiroMigrations.keiroMigrations+  kioku <- either (fail . show) pure kiokuMigrations+  host <-+    either+      (fail . show)+      pure+      ( migrationComponentFromEmbeddedSql+          "host"+          (Set.singleton "kioku")+          ( ( "0001-host-table.sql",+              Text.Encoding.encodeUtf8+                "ALTER TABLE host_table ADD COLUMN migrated boolean NOT NULL DEFAULT true;"+            )+              :| []+          )+      )+  either (fail . show) pure (migrationPlan (kiroku :| [keiro, kioku, host]))++hostSchemaSetup :: Text+hostSchemaSetup =+  """+  CREATE SCHEMA host_app;+  CREATE TABLE host_app.host_table (id bigint PRIMARY KEY);+  """++quotedCurrentDatabase :: Statement () Text+quotedCurrentDatabase =+  preparable+    "SELECT quote_ident(current_database())"+    E.noParams+    (D.singleRow (D.column (D.nonNullable D.text)))++hostMigratedColumnExists :: Statement () Bool+hostMigratedColumnExists =+  preparable+    """+    SELECT EXISTS (+      SELECT 1+      FROM information_schema.columns+      WHERE table_schema = 'host_app'+        AND table_name = 'host_table'+        AND column_name = 'migrated'+    )+    """+    E.noParams+    (D.singleRow (D.column (D.nonNullable D.bool)))++-- * Released checksum re-baseline++ledgerFixupPath :: FilePath+ledgerFixupPath = "ledger-fixups/2026-08-19-rebaseline-0011-checksum.sql"++withdrawnKioku0011Checksum :: Text+withdrawnKioku0011Checksum = "eee9cd252b32b563c50f8457596347fff1b2e4d3ea4dafe5b45043e991624192"++testLedgerChecksumRebaseline :: Assertion+testLedgerChecksumRebaseline =+  withBareDatabase \connStr -> do+    plan <- either (fail . show) pure kiokuMigrationPlan+    let settings = Settings.connectionString connStr+    initial <- runMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure+    length (appliedNow initial) @?= 55++    baselineLedger <- query connStr fullLedgerSnapshot+    fst baselineLedger @?= 55+    baselineSchema <- query connStr cohortSchemaSnapshotStatement+    fixup <- Text.IO.readFile ledgerFixupPath++    withConnection connStr \conn -> run conn (Session.statement () installWithdrawnChecksumFixture)+    query connStr kioku0011Checksum >>= (@?= withdrawnKioku0011Checksum)+    query connStr cohortSchemaSnapshotStatement >>= (@?= baselineSchema)++    mismatch <- verifyMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure+    expectedMigration <-+      either+        (assertFailure . show)+        pure+        (migrationId "kioku" "0011-kioku-memory-space-partition")+    case issues mismatch of+      [MigrationChecksumMismatch actualMigration _ _] -> actualMigration @?= expectedMigration+      other -> assertFailure ("expected one MigrationChecksumMismatch, got: " <> show other)++    withConnection connStr \conn -> run conn (Session.script fixup)+    query connStr fullLedgerSnapshot >>= (@?= baselineLedger)+    query connStr cohortSchemaSnapshotStatement >>= (@?= baselineSchema)+    verified <- verifyMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure+    issues verified @?= []++    repeated <- runMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure+    let MigrationReport {results = repeatedResults} = repeated+    length [() | MigrationResult {outcome = AlreadyApplied} <- toList repeatedResults] @?= 55+    length [() | MigrationResult {outcome = AppliedNow} <- toList repeatedResults] @?= 0++    withConnection connStr \conn -> run conn (Session.script fixup)+    query connStr fullLedgerSnapshot >>= (@?= baselineLedger)+    query connStr cohortSchemaSnapshotStatement >>= (@?= baselineSchema)++testLedgerFixupRequiresDefaultLedger :: Assertion+testLedgerFixupRequiresDefaultLedger =+  withBareDatabase \connStr -> do+    fixup <- Text.IO.readFile ledgerFixupPath+    withConnection connStr \conn -> do+      result <- Connection.use conn (Session.script fixup)+      case result of+        Left err ->+          assertBool+            ("missing-ledger error did not explain how to adapt LedgerConfig: " <> show err)+            ("Could not find pgmigrate.migrations" `Text.isInfixOf` Text.pack (show err))+        Right () -> assertFailure "ledger re-baseline unexpectedly accepted a missing ledger table"++installWithdrawnChecksumFixture :: Statement () ()+installWithdrawnChecksumFixture =+  preparable+    """+    UPDATE pgmigrate.migrations+       SET checksum = decode('eee9cd252b32b563c50f8457596347fff1b2e4d3ea4dafe5b45043e991624192', 'hex')+     WHERE component = 'kioku'+       AND migration = '0011-kioku-memory-space-partition'+       AND status = 'applied'+    """+    E.noParams+    D.noResult++kioku0011Checksum :: Statement () Text+kioku0011Checksum =+  preparable+    """+    SELECT encode(checksum, 'hex')+    FROM pgmigrate.migrations+    WHERE component = 'kioku'+      AND migration = '0011-kioku-memory-space-partition'+    """+    E.noParams+    (D.singleRow (D.column (D.nonNullable D.text)))++fullLedgerSnapshot :: Statement () (Int64, Text)+fullLedgerSnapshot =+  preparable+    """+    SELECT count(*),+           md5(string_agg(+             component || '/' || migration || ':' || encode(checksum, 'hex') || ':' || status,+             E'\n' ORDER BY component COLLATE "C", position))+    FROM pgmigrate.migrations+    """+    E.noParams+    (D.singleRow ((,) <$> required D.int8 <*> required D.text))+  where+    required = D.column . D.nonNullable++-- * The partition-aware full-text index++partitionAwareFtsMigration :: FilePath+partitionAwareFtsMigration = "0013-partition-aware-fts-index.sql"++-- | The ordinary ephemeral role can install @btree_gin@, so the preferred branch must exist as+-- a catalog fact: extension present, replacement GIN present, historical GIN absent. The index+-- definition also proves that all three candidate predicates and the active-row partial clause+-- are carried by the access path.+testPartitionAwareFtsIndex :: Assertion+testPartitionAwareFtsIndex =+  withKiokuMigratedDatabase \connStr -> do+    (extensionPresent, replacementPresent, fallbackPresent, definition) <-+      query connStr partitionAwareFtsIndexLayout+    extensionPresent @?= True+    replacementPresent @?= True+    fallbackPresent @?= False+    mapM_+      ( \fragment ->+          assertBool+            ("partition-aware GIN definition is missing " <> Text.unpack fragment <> ": " <> Text.unpack definition)+            (fragment `Text.isInfixOf` definition)+      )+      [ "USING gin (memory_space_id, namespace, content_tsv)",+        "WHERE (status = 'active'::text)"+      ]++-- | The fallback is a source-order guarantee as well as a happy-path database test. A role that+-- cannot install @btree_gin@ must reach the exception handler before any statement can remove+-- the old index, and the replacement receives a catalog check before that removal too.+testPartitionAwareFtsFallbackOrdering :: Assertion+testPartitionAwareFtsFallbackOrdering = do+  migration <- loadMigration partitionAwareFtsMigration+  let dropStatement = "DROP INDEX IF EXISTS kioku.kioku_memories_tsv_idx"+      (beforeDrop, dropAndAfter) = Text.breakOn dropStatement migration+  assertBool "the migration never names the historical fallback index" (not (Text.null dropAndAfter))+  assertBool+    "the btree_gin exception handler appears after the fallback drop"+    ("WHEN OTHERS THEN" `Text.isInfixOf` beforeDrop)+  assertBool+    "the fallback drop is not guarded by a catalog-visible replacement"+    ( "IF to_regclass('kioku.kioku_memories_space_namespace_tsv_idx') IS NOT NULL THEN"+        `Text.isInfixOf` beforeDrop+    )++testPartitionAwareFtsIndexIdempotent :: Assertion+testPartitionAwareFtsIndexIdempotent =+  withKiokuMigratedDatabase \connStr ->+    withConnection connStr \conn -> do+      migration <- loadMigration partitionAwareFtsMigration+      before <- run conn (Session.statement () partitionAwareFtsIndexLayout)+      run conn (Session.script migration)+      run conn (Session.statement () partitionAwareFtsIndexLayout) >>= (@?= before)++partitionAwareFtsIndexLayout :: Statement () (Bool, Bool, Bool, Text)+partitionAwareFtsIndexLayout =+  preparable+    """+    SELECT EXISTS (+             SELECT 1 FROM pg_catalog.pg_extension WHERE extname = 'btree_gin'+           ),+           to_regclass('kioku.kioku_memories_space_namespace_tsv_idx') IS NOT NULL,+           to_regclass('kioku.kioku_memories_tsv_idx') IS NOT NULL,+           coalesce(pg_catalog.pg_get_indexdef(+             to_regclass('kioku.kioku_memories_space_namespace_tsv_idx')::oid+           ), '')+    """+    E.noParams+    ( D.singleRow+        ( (,,,)+            <$> D.column (D.nonNullable D.bool)+            <*> D.column (D.nonNullable D.bool)+            <*> D.column (D.nonNullable D.bool)+            <*> D.column (D.nonNullable D.text)+        )+    )+ -- * The memory-space partition migration  -- | Prove the backfill on data that genuinely predates the partition.@@ -555,7 +828,7 @@     adoption <- runMigrationPlan defaultRunOptions settings full >>= either (assertFailure . show) pure     let MigrationReport {results = adoptionResults} = adoption     length [() | MigrationResult {outcome = AlreadyApplied} <- toList adoptionResults] @?= 11-    length [() | MigrationResult {outcome = AppliedNow} <- toList adoptionResults] @?= 42+    length [() | MigrationResult {outcome = AppliedNow} <- toList adoptionResults] @?= 44      -- Verified and skipped, never re-executed: the stored rows keep their checksums and their     -- original application timestamps.@@ -568,7 +841,7 @@     verification <- verifyMigrationPlan defaultRunOptions settings full >>= either (assertFailure . show) pure     let VerificationReport {issues = adoptionIssues, appliedMigrations, pendingMigrations, unknownMigrations} = verification     adoptionIssues @?= []-    length appliedMigrations @?= 53+    length appliedMigrations @?= 55     pendingMigrations @?= []     unknownMigrations @?= [] @@ -919,13 +1192,13 @@     verification <- verifyMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure     let VerificationReport {issues = verificationIssues, appliedMigrations, pendingMigrations, unknownMigrations} = verification     verificationIssues @?= []-    length appliedMigrations @?= 53+    length appliedMigrations @?= 55     pendingMigrations @?= []     unknownMigrations @?= []      repeated <- runMigrationPlan defaultRunOptions settings plan >>= either (assertFailure . show) pure     let MigrationReport {results = repeatedResults} = repeated-    length [() | MigrationResult {outcome = AlreadyApplied} <- toList repeatedResults] @?= 53+    length [() | MigrationResult {outcome = AlreadyApplied} <- toList repeatedResults] @?= 55     length [() | MigrationResult {outcome = AppliedNow} <- toList repeatedResults] @?= 0  fixtureMigrationNames :: Text -> [FilePath]@@ -1012,8 +1285,10 @@           migrationId "keiro" "0028",           migrationId "keiro" "0029",           migrationId "keiro" "0030",+          migrationId "keiro" "0031",           migrationId "kioku" "0011-kioku-memory-space-partition",-          migrationId "kioku" "0012-relocate-projections-to-kioku-schema"+          migrationId "kioku" "0012-relocate-projections-to-kioku-schema",+          migrationId "kioku" "0013-partition-aware-fts-index"         ]  expectRight :: (Show error) => Either error value -> value