diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,33 @@
 
 ## [Unreleased]
 
+## 0.22.0.0 — 2026-10-27
+
+### Breaking Changes
+
+- Require [Kiroku Store migrations](mori://shinzui/kiroku/packages/kiroku-store-migrations)
+  `^>=0.7.2.0`, adding migration 0016 and raising the composed plan to 48 entries.
+  Stop affected subscription workers for cutover and restart with
+  [Kiroku Store](mori://shinzui/kiroku/packages/kiroku-store) 0.12 before using
+  namespace targets; older binaries must not manage namespace bindings.
+
+### New Features
+
+- Export verifyExpectedSchemaSession for fail-closed verification on an existing pool, preserving managed-object handling.
+
+### Bug Fixes
+
+- Preserve multiline generated/default expressions in schema fingerprints so
+  pooled and full-snapshot comparisons agree.
+
+### Other Changes
+
+- Keep workflow classification and browsing on the read side. No inspection-specific generated column, index, or migration changes ordinary writer work.
+
+
+- Compare live schema observations in PostgreSQL and return differences only,
+  retaining per-call verification, registry permissions, and drift diagnostics.
+
 ## 0.21.0.0 — 2026-10-14
 
 ### Bug Fixes
diff --git a/keiro-migrations.cabal b/keiro-migrations.cabal
--- a/keiro-migrations.cabal
+++ b/keiro-migrations.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: keiro-migrations
-version: 0.21.0.0
+version: 0.22.0.0
 synopsis: Schema migrations for keiro
 description:
   Embedded PostgreSQL schema migrations and a migration runner for the Keiro
@@ -333,7 +333,7 @@
     bytestring >=0.11 && <0.13,
     containers >=0.6 && <0.8,
     hasql >=1.10 && <1.11,
-    kiroku-store-migrations ^>=0.7.1.0,
+    kiroku-store-migrations ^>=0.7.2.0,
     pg-migrate ^>=1.2.0.0,
     pg-migrate-embed ^>=1.2.0.0,
     pg-migrate-import-codd ^>=1.2.0.0,
@@ -369,7 +369,7 @@
     bytestring >=0.11 && <0.13,
     hasql >=1.10 && <1.11,
     keiro-migrations,
-    kiroku-store-migrations ^>=0.7.1.0,
+    kiroku-store-migrations ^>=0.7.2.0,
     optparse-applicative >=0.17 && <0.20,
     pg-migrate ^>=1.2.0.0,
     pg-migrate-cli ^>=1.2.0.0,
@@ -415,7 +415,7 @@
     hasql >=1.10 && <1.11,
     hspec >=2.10 && <2.12,
     keiro-migrations,
-    kiroku-store-migrations ^>=0.7.1.0,
+    kiroku-store-migrations ^>=0.7.2.0,
     pg-migrate ^>=1.2.0.0,
     pg-migrate-import-codd ^>=1.2.0.0,
     pg-migrate-test-support ^>=1.2.0.0,
@@ -451,8 +451,8 @@
     hasql-pool >=1.2 && <1.5,
     hspec >=2.10,
     keiro-migrations,
-    kiroku-store ^>=0.11.0.0,
-    kiroku-store-migrations ^>=0.7.1.0,
+    kiroku-store ^>=0.12.0.0,
+    kiroku-store-migrations ^>=0.7.2.0,
     lens >=5.2 && <5.4,
     temporary,
     text >=2.0 && <2.2,
diff --git a/src/Keiro/Migrations/SchemaCheck.hs b/src/Keiro/Migrations/SchemaCheck.hs
--- a/src/Keiro/Migrations/SchemaCheck.hs
+++ b/src/Keiro/Migrations/SchemaCheck.hs
@@ -9,6 +9,7 @@
     renderSchemaDrift,
     snapshotSchema,
     verifyExpectedSchema,
+    verifyExpectedSchemaSession,
   )
 where
 
@@ -118,7 +119,7 @@
 snapshotSchema schema =
   Text.unlines <$> Session.statement schema schemaSnapshotStatement
 
--- | PostgreSQL 18 snapshot regenerated through migration 0031.
+-- | PostgreSQL 18 snapshot regenerated through migration 0032.
 expectedSchemaSnapshot :: Text
 expectedSchemaSnapshot =
   $(embedTextFile "expected-schema/native/keiro-v18.txt")
@@ -139,26 +140,27 @@
       pure (Left (ConnectionAcquisitionFailed connectionError))
     Right connection -> do
       result <-
-        Connection.use connection liveSnapshotSession
+        Connection.use connection verifyExpectedSchemaSession
           `finally` Connection.release connection
       pure $ case result of
         Left sessionError -> Left (DatabaseSessionFailed sessionError)
-        Right (Left migrationError) -> Left migrationError
-        Right (Right (managed, actual)) ->
-          Right (compareSchemaSnapshotWithManaged managed expectedSchemaSnapshot actual)
-  where
-    liveSnapshotSession :: Session (Either MigrationError (Set Text, Text))
-    liveSnapshotSession = do
-      serverVersionNumber <- Session.statement () serverVersionStatement
-      let majorVersion = fromIntegral serverVersionNumber `div` 10000
-      if majorVersion == (18 :: Int)
-        then do
-          privateSchema <- snapshotSchema "keiro"
-          publicSchema <- snapshotSchema "keiro_read"
-          managed <- managedObjectSnapshotKeys
-          pure (Right (managed, privateSchema <> publicSchema))
-        else pure (Left (UnsupportedPostgresVersion majorVersion))
+        Right result -> result
 
+-- | Verify the live schema on an existing connection or pool. This retains the
+-- CLI's PostgreSQL version check and runtime-managed object handling.
+verifyExpectedSchemaSession :: Session (Either MigrationError [SchemaDrift])
+verifyExpectedSchemaSession = do
+  serverVersionNumber <- Session.statement () serverVersionStatement
+  let majorVersion = fromIntegral serverVersionNumber `div` 10000
+  if majorVersion == (18 :: Int)
+    then do
+      readable <- Session.statement () registryReadableStatement
+      -- Compare every live catalog observation in PostgreSQL. Returning only
+      -- differences avoids rebuilding two complete Text/Map snapshots per poll.
+      -- No live result is cached, and registry visibility stays role-dependent.
+      Right <$> Session.statement () (if readable then managedSchemaDriftStatement else unmanagedSchemaDriftStatement)
+    else pure (Left (UnsupportedPostgresVersion majorVersion))
+
 -- | Snapshot keys of the objects Keiro's runtime created and recorded in
 -- @keiro.keiro_managed_read_objects@ with @managed_by = 'keiro'@. Every state
 -- counts: retirement marks a managed object retired without dropping it. Rows
@@ -243,73 +245,130 @@
 schemaSnapshotStatement :: Statement Text [Text]
 schemaSnapshotStatement =
   Statement.preparable
-    """
-    WITH configured AS MATERIALIZED (
-      SELECT set_config('search_path', 'pg_catalog', true) AS search_path
-    )
-    SELECT line
-    FROM configured
-    CROSS JOIN LATERAL (
-      SELECT 'table' || E'\t' || c.relname || E'\t' || 'kind=r' AS line
-        FROM pg_class c
-        JOIN pg_namespace n ON n.oid = c.relnamespace
-        WHERE n.nspname = $1
-          AND c.relkind = 'r'
-          AND configured.search_path = 'pg_catalog'
-      UNION ALL
-      SELECT 'column' || E'\t' || c.relname || '.' || a.attname || E'\t'
-             || format_type(a.atttypid, a.atttypmod)
-             || CASE WHEN a.attnotnull THEN ' not null' ELSE '' END
-             || coalesce(' default ' || pg_get_expr(d.adbin, d.adrelid), '')
-        FROM pg_attribute a
-        JOIN pg_class c ON c.oid = a.attrelid
-        JOIN pg_namespace n ON n.oid = c.relnamespace
-        LEFT JOIN pg_attrdef d
-          ON d.adrelid = a.attrelid AND d.adnum = a.attnum
-        WHERE n.nspname = $1
-          AND c.relkind = 'r'
-          AND a.attnum > 0
-          AND NOT a.attisdropped
-          AND configured.search_path = 'pg_catalog'
-      UNION ALL
-      SELECT 'constraint' || E'\t' || rel.relname || '.' || con.conname || E'\t'
-             || pg_get_constraintdef(con.oid)
-        FROM pg_constraint con
-        JOIN pg_class rel ON rel.oid = con.conrelid
-        JOIN pg_namespace n ON n.oid = rel.relnamespace
-        WHERE n.nspname = $1
-          AND configured.search_path = 'pg_catalog'
-      UNION ALL
-      SELECT 'index' || E'\t' || ci.relname || E'\t'
-             || pg_get_indexdef(i.indexrelid)
-        FROM pg_index i
-        JOIN pg_class ci ON ci.oid = i.indexrelid
-        JOIN pg_class ct ON ct.oid = i.indrelid
-        JOIN pg_namespace n ON n.oid = ct.relnamespace
-        WHERE n.nspname = $1
-          AND configured.search_path = 'pg_catalog'
-      UNION ALL
-      SELECT 'view' || E'\t' || n.nspname || '.' || c.relname || E'\t'
-             || 'columns='
-             || string_agg(
-                  a.attnum::text || ':' || a.attname || ' '
-                    || format_type(a.atttypid, a.atttypmod),
-                  ',' ORDER BY a.attnum
-                ) AS line
-        FROM pg_class c
-        JOIN pg_namespace n ON n.oid = c.relnamespace
-        JOIN pg_attribute a ON a.attrelid = c.oid
-        WHERE n.nspname = $1
-          AND c.relkind = 'v'
-          AND a.attnum > 0
-          AND NOT a.attisdropped
-          AND configured.search_path = 'pg_catalog'
-        GROUP BY n.nspname, c.relname
-    ) snapshot
-    ORDER BY line COLLATE "C"
-    """
+    schemaSnapshotSql
     (Encoders.param (Encoders.nonNullable Encoders.text))
     (Decoders.rowList (Decoders.column (Decoders.nonNullable Decoders.text)))
+
+schemaSnapshotSql :: Text
+schemaSnapshotSql =
+  """
+  WITH configured AS MATERIALIZED (
+    SELECT set_config('search_path', 'pg_catalog', true) AS search_path
+  )
+  SELECT line
+  FROM configured
+  CROSS JOIN LATERAL (
+    SELECT 'table' || E'\t' || c.relname || E'\t' || 'kind=r' AS line
+      FROM pg_class c
+      JOIN pg_namespace n ON n.oid = c.relnamespace
+      WHERE n.nspname = $1
+        AND c.relkind = 'r'
+        AND configured.search_path = 'pg_catalog'
+    UNION ALL
+    SELECT 'column' || E'\t' || c.relname || '.' || a.attname || E'\t'
+           || format_type(a.atttypid, a.atttypmod)
+           || CASE WHEN a.attnotnull THEN ' not null' ELSE '' END
+           -- JSON quoting keeps a multiline generated expression on one snapshot line.
+           || CASE a.attgenerated
+                WHEN 's' THEN ' generated always as ' || to_json(pg_get_expr(d.adbin, d.adrelid))::text || ' stored'
+                WHEN 'v' THEN ' generated always as ' || to_json(pg_get_expr(d.adbin, d.adrelid))::text || ' virtual'
+                ELSE coalesce(' default ' || replace(replace(replace(pg_get_expr(d.adbin, d.adrelid),
+                       chr(92), chr(92) || chr(92)), chr(10), chr(92) || 'n'), chr(13), chr(92) || 'r'), '')
+              END
+      FROM pg_attribute a
+      JOIN pg_class c ON c.oid = a.attrelid
+      JOIN pg_namespace n ON n.oid = c.relnamespace
+      LEFT JOIN pg_attrdef d
+        ON d.adrelid = a.attrelid AND d.adnum = a.attnum
+      WHERE n.nspname = $1
+        AND c.relkind = 'r'
+        AND a.attnum > 0
+        AND NOT a.attisdropped
+        AND configured.search_path = 'pg_catalog'
+    UNION ALL
+    SELECT 'constraint' || E'\t' || rel.relname || '.' || con.conname || E'\t'
+           || pg_get_constraintdef(con.oid)
+      FROM pg_constraint con
+      JOIN pg_class rel ON rel.oid = con.conrelid
+      JOIN pg_namespace n ON n.oid = rel.relnamespace
+      WHERE n.nspname = $1
+        AND configured.search_path = 'pg_catalog'
+    UNION ALL
+    SELECT 'index' || E'\t' || ci.relname || E'\t'
+           || pg_get_indexdef(i.indexrelid)
+      FROM pg_index i
+      JOIN pg_class ci ON ci.oid = i.indexrelid
+      JOIN pg_class ct ON ct.oid = i.indrelid
+      JOIN pg_namespace n ON n.oid = ct.relnamespace
+      WHERE n.nspname = $1
+        AND configured.search_path = 'pg_catalog'
+    UNION ALL
+    SELECT 'view' || E'\t' || n.nspname || '.' || c.relname || E'\t'
+           || 'columns='
+           || string_agg(
+                a.attnum::text || ':' || a.attname || ' '
+                  || format_type(a.atttypid, a.atttypmod),
+                ',' ORDER BY a.attnum
+              ) AS line
+      FROM pg_class c
+      JOIN pg_namespace n ON n.oid = c.relnamespace
+      JOIN pg_attribute a ON a.attrelid = c.oid
+      WHERE n.nspname = $1
+        AND c.relkind = 'v'
+        AND a.attnum > 0
+        AND NOT a.attisdropped
+        AND configured.search_path = 'pg_catalog'
+      GROUP BY n.nspname, c.relname
+  ) snapshot
+  ORDER BY line COLLATE "C"
+  """
+
+-- These two prepared statements embed only the immutable expected snapshot.
+-- Their SQL is constructed once, not serialized as a large parameter per poll.
+managedSchemaDriftStatement :: Statement () [SchemaDrift]
+{-# NOINLINE managedSchemaDriftStatement #-}
+managedSchemaDriftStatement = schemaDriftStatement managedKeysSql
+  where
+    managedKeysSql =
+      "SELECT ('view' || E'\\t' || object_schema || '.' || object_name) COLLATE \"C\" AS key "
+        <> "FROM keiro.keiro_managed_read_objects WHERE managed_by = 'keiro' AND object_kind = 'binding-view'"
+
+unmanagedSchemaDriftStatement :: Statement () [SchemaDrift]
+{-# NOINLINE unmanagedSchemaDriftStatement #-}
+unmanagedSchemaDriftStatement = schemaDriftStatement "SELECT NULL::text COLLATE \"C\" AS key WHERE false"
+
+schemaDriftStatement :: Text -> Statement () [SchemaDrift]
+schemaDriftStatement managedSql = Statement.preparable sql Encoders.noParams decoder
+  where
+    -- E literals preserve quotes, backslashes and embedded control characters
+    -- independently of standard_conforming_strings. No runtime input enters SQL.
+    literal value = "E'" <> Text.replace "'" "''" (Text.replace "\\" "\\\\" value) <> "'"
+    expectedRows =
+      Text.intercalate "," ["(" <> Text.intercalate "," (map literal [key, line, definition]) <> ")" | (key, (line, definition)) <- Map.toAscList (snapshotObjects expectedSchemaSnapshot)]
+    sql =
+      "WITH live_lines AS MATERIALIZED (SELECT schemas.priority, observed.line FROM "
+        <> "(VALUES ('keiro'::text,1),('keiro_read'::text,2)) AS schemas(name,priority) CROSS JOIN LATERAL ("
+        <> Text.replace "$1" "schemas.name" schemaSnapshotSql
+        <> ") AS observed), actual_keys AS (SELECT priority,line COLLATE \"C\" AS line, "
+        <> "(split_part(line,E'\\t',1)||E'\\t'||split_part(line,E'\\t',2)) COLLATE \"C\" AS key FROM live_lines), "
+        -- Match Map.fromList over the two sorted snapshots: the public schema
+        -- wins duplicate keys, then the final C-sorted line within that schema.
+        <> "actual AS (SELECT DISTINCT ON (key) key,line,substring(line FROM length(key)+2) COLLATE \"C\" AS definition "
+        <> "FROM actual_keys ORDER BY key,priority DESC,line DESC), "
+        <> "expected AS (SELECT key COLLATE \"C\" AS key,line,definition COLLATE \"C\" AS definition FROM (VALUES "
+        <> expectedRows
+        <> ") AS embedded(key,line,definition)), managed AS ("
+        <> managedSql
+        <> ") SELECT coalesce(e.key,a.key),e.line,a.line,e.definition,a.definition FROM expected e FULL JOIN actual a USING(key) "
+        <> "WHERE a.key IS NULL OR (e.key IS NULL AND NOT EXISTS (SELECT 1 FROM managed m WHERE m.key=a.key)) "
+        <> "OR (e.key IS NOT NULL AND a.key IS NOT NULL AND e.definition IS DISTINCT FROM a.definition) ORDER BY coalesce(e.key,a.key) COLLATE \"C\""
+    decoder = Decoders.rowList (toDrift <$> required <*> optional <*> optional <*> optional <*> optional)
+    required = Decoders.column (Decoders.nonNullable Decoders.text)
+    optional = Decoders.column (Decoders.nullable Decoders.text)
+    toDrift _ (Just line) Nothing _ _ = MissingObject line
+    toDrift _ Nothing (Just line) _ _ = UnexpectedObject line
+    toDrift key (Just _) (Just _) (Just expected) (Just actual) = ChangedObject key expected actual
+    toDrift _ _ _ _ _ = error "schema comparison returned an impossible null combination"
 
 serverVersionStatement :: Statement () Int32
 serverVersionStatement =
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -79,7 +79,7 @@
           ] -> do
             componentNameText kirokuName `shouldBe` "kiroku"
             kirokuDependencies `shouldBe` mempty
-            length kirokuEntries `shouldBe` 15
+            length kirokuEntries `shouldBe` 16
             componentNameText keiroName `shouldBe` "keiro"
             dependencyName <- requireRight (componentName "kiroku")
             keiroDependencies `shouldBe` Set.singleton dependencyName
@@ -186,7 +186,7 @@
             plan
             >>= requireRight
         Keiro.pendingMigrations handshake `shouldBe` planMigrationIds plan
-        length (Keiro.pendingMigrations handshake) `shouldBe` 47
+        length (Keiro.pendingMigrations handshake) `shouldBe` 48
         Keiro.ledgerIssues handshake `shouldBe` []
         handshakePassed handshake `shouldBe` False
 
@@ -211,7 +211,7 @@
         _ <- runMigrationPlan defaultRunOptions settings kirokuOnly >>= requireRight
         handshake <-
           missingMigrations defaultRunOptions provider plan >>= requireRight
-        Keiro.pendingMigrations handshake `shouldBe` drop 15 (planMigrationIds plan)
+        Keiro.pendingMigrations handshake `shouldBe` drop 16 (planMigrationIds plan)
         length (Keiro.pendingMigrations handshake) `shouldBe` 32
         Keiro.ledgerIssues handshake `shouldBe` []
         handshakePassed handshake `shouldBe` False
@@ -277,6 +277,9 @@
                 """
             )
         drifts <- verifyExpectedSchema settings >>= requireRight
+        withConnection settings $ \connection -> do
+          actual <- useSession connection ((<>) <$> snapshotSchema "keiro" <*> snapshotSchema "keiro_read")
+          drifts `shouldBe` compareSchemaSnapshot expectedSchemaSnapshot actual
         let rendered = renderSchemaDrift <$> drifts
         rendered
           `shouldSatisfy` any
@@ -355,18 +358,20 @@
             demoDrift = [UnexpectedObject "view\tkeiro.external_read_demo_v1_binding\tcolumns=1:id bigint,2:total bigint"]
             checkRole expectedKeys expectedDrifts =
               withConnection settings $ \connection -> do
-                (keys, drifts) <-
+                (keys, drifts, optimized) <-
                   useSession
                     connection
                     ( do
                         Session.script "SET ROLE keiro_verify_probe"
                         managed <- managedObjectSnapshotKeys
                         actual <- (<>) <$> snapshotSchema "keiro" <*> snapshotSchema "keiro_read"
-                        pure (managed, compareSchemaSnapshotWithManaged managed expectedSchemaSnapshot actual)
+                        optimized <- verifyExpectedSchemaSession
+                        pure (managed, compareSchemaSnapshotWithManaged managed expectedSchemaSnapshot actual, optimized)
                     )
                     `finally` useSession connection (Session.script "RESET ROLE")
                 keys `shouldBe` expectedKeys
                 drifts `shouldBe` expectedDrifts
+                requireRight optimized >>= (`shouldBe` expectedDrifts)
         _ <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
         withConnection settings $ \connection -> do
           useSession connection (Session.script managedBindingFixtureSql)
@@ -395,6 +400,26 @@
           useSession connection (Session.script "GRANT USAGE ON SCHEMA keiro TO keiro_verify_probe")
           checkRole demoKeys []
 
+    it "preserves snapshot comparison for duplicate keys, quoting, and changed defaults" $ do
+      plan <- requirePlan
+      result <- withMigratedDatabase plan $ \connection -> do
+        useSession connection $
+          Session.script
+            """
+            CREATE TABLE keiro.schema_collision (shared_value text DEFAULT E'quote''slash\\\\tab\\t');
+            CREATE TABLE keiro_read.schema_collision (shared_value bigint DEFAULT 42);
+            ALTER TABLE keiro.keiro_workflows ALTER COLUMN attempts SET DEFAULT 3;
+            ALTER TABLE keiro.keiro_workflows ALTER COLUMN last_error SET DEFAULT E'quote''slash\\\\tab\\t';
+            """
+        actual <- useSession connection ((<>) <$> snapshotSchema "keiro" <*> snapshotSchema "keiro_read")
+        optimized <- useSession connection verifyExpectedSchemaSession >>= requireRight
+        optimized `shouldBe` compareSchemaSnapshot expectedSchemaSnapshot actual
+        useSession connection (Session.script "ALTER TABLE keiro.keiro_workflows ALTER COLUMN last_error SET DEFAULT E'first\\nsecond\\rtail'")
+        multiline <- useSession connection ((<>) <$> snapshotSchema "keiro" <*> snapshotSchema "keiro_read")
+        multilineOptimized <- useSession connection verifyExpectedSchemaSession >>= requireRight
+        multilineOptimized `shouldBe` compareSchemaSnapshot expectedSchemaSnapshot multiline
+      either (expectationFailure . show) pure result
+
     it "falls back to ordinary drift when the registry is absent" $ do
       plan <- requirePlan
       withKeiroPg $ \database -> do
@@ -423,12 +448,12 @@
         assertSchema connection
         let provider = providerFor connection
         rerun <- runMigrationPlanWith defaultRunOptions provider plan >>= requireRight
-        reportOutcomes rerun `shouldBe` replicate 47 AlreadyApplied
+        reportOutcomes rerun `shouldBe` replicate 48 AlreadyApplied
         verified <- verifyMigrationPlanWith defaultRunOptions provider plan >>= requireRight
         case verified of
           VerificationReport verificationIssues applied pending unknown -> do
             verificationIssues `shouldBe` []
-            length applied `shouldBe` 47
+            length applied `shouldBe` 48
             pending `shouldBe` []
             unknown `shouldBe` []
       either (expectationFailure . show) pure result
@@ -477,7 +502,7 @@
             (runMigrationPlan defaultRunOptions settings plan >>= requireRight)
             (runMigrationPlan defaultRunOptions settings plan >>= requireRight)
         sort [reportOutcomes first, reportOutcomes second]
-          `shouldBe` sort [replicate 47 AppliedNow, replicate 47 AlreadyApplied]
+          `shouldBe` sort [replicate 48 AppliedNow, replicate 48 AlreadyApplied]
 
     it "upgrades singleton read-model rows into deterministic rebuild groups" $ do
       fullPlan <- requirePlan
@@ -811,7 +836,7 @@
         up <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
         reportOutcomes up
           `shouldBe` replicate 7 AlreadyApplied
-            <> replicate 8 AppliedNow
+            <> replicate 9 AppliedNow
             <> replicate 16 AlreadyApplied
             <> replicate 16 AppliedNow
 
@@ -1248,14 +1273,14 @@
     up <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
     reportOutcomes up
       `shouldBe` replicate 7 AlreadyApplied
-        <> replicate 8 AppliedNow
+        <> replicate 9 AppliedNow
         <> replicate 16 AlreadyApplied
         <> replicate 16 AppliedNow
     verifiedAfterCanaries <- verifyMigrationPlan defaultRunOptions settings plan >>= requireRight
     case verifiedAfterCanaries of
       VerificationReport verificationIssues _ _ _ -> verificationIssues `shouldBe` []
     rerun <- runMigrationPlan defaultRunOptions settings plan >>= requireRight
-    reportOutcomes rerun `shouldBe` replicate 47 AlreadyApplied
+    reportOutcomes rerun `shouldBe` replicate 48 AlreadyApplied
     second <-
       importCoddHistory defaultImportOptions config provider plan frameworkCoddHistoryMappings
         >>= requireRight
@@ -1265,7 +1290,7 @@
       sourceRows <- useSession connection (Session.statement () (sourceRowCountStatement sourceSchema))
       sourceRows `shouldBe` 23
       facts <- useSession connection (Session.statement () importFactsStatement)
-      facts `shouldBe` (47, 23, True)
+      facts `shouldBe` (48, 23, True)
 
 postCoddImportPendingIssues :: IO [VerificationIssue]
 postCoddImportPendingIssues =
@@ -1283,6 +1308,7 @@
         ("kiroku", "0013"),
         ("kiroku", "0014"),
         ("kiroku", "0015"),
+        ("kiroku", "0016"),
         ("keiro", "0017-schema-management-comment"),
         ("keiro", "0018"),
         ("keiro", "0019-keiro-snapshots-state-shape-hash"),
@@ -1550,7 +1576,7 @@
       (to_regclass('keiro_read.projection_group_status_v1') IS NOT NULL),
       (obj_description(to_regnamespace('keiro_read'), 'pg_namespace') =
         'Versioned, owner-rights read contracts for out-of-process Keiro consumers.'),
-      (obj_description(to_regnamespace('kiroku'), 'pg_namespace') = 'Managed by pg-migrate component kiroku through 0015'),
+      (obj_description(to_regnamespace('kiroku'), 'pg_namespace') = 'Managed by pg-migrate component kiroku through 0016'),
       (to_regclass('kiroku.ix_streams_browse_name') IS NOT NULL),
       (obj_description(to_regnamespace('keiro'), 'pg_namespace') = 'Managed by pg-migrate component keiro through 0017-schema-management-comment')
     ) AS checks(ok)
