diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,7 +4,25 @@
 [Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
 the [Haskell Package Versioning Policy](https://pvp.haskell.org/).
 
-## [Unreleased]
+## [0.11.0.0] - 2026-08-05
+
+### Breaking Changes
+
+- Requires `keiki >=0.9 && <0.10` and `keiki-codec-json >=0.9 && <0.10`,
+  replacing the 0.8 bounds. Keiki 0.9 makes `InCtor` and `WireCtor` read-only
+  construction patterns; manual values must use `unavailableInCtor` and
+  `unavailableWireCtor`, or use Generic/TH producers when trusted structural
+  evidence is required. Validation can report fewer name-collision false
+  positives when trusted structural heads differ or exact integral constraints
+  prove replay candidates disjoint. The JSON event and snapshot formats are
+  unchanged.
+
+### Added
+
+- `Keiro.Timer.Schema.TimerStatus` now derives `Enum` and `Bounded`, so a
+  consumer can enumerate the complete stored lifecycle instead of restating
+  it. Stored representations are unchanged. keiro-dsl's cross-package
+  timer-vocabulary test relies on this.
 
 ## 0.10.0.0 — 2026-08-03
 
diff --git a/keiro.cabal b/keiro.cabal
--- a/keiro.cabal
+++ b/keiro.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.0
 name:            keiro
-version:         0.10.0.0
+version:         0.11.0.0
 synopsis:        Event sourcing framework and workflow engine
 description:
   A library that composes kiroku, keiki, and shibuya into an
@@ -121,9 +121,9 @@
     , hs-opentelemetry-api                   >=1.0       && <1.1
     , hs-opentelemetry-propagator-w3c        >=1.0       && <1.1
     , hs-opentelemetry-semantic-conventions  >=1.40      && <2
-    , keiki                                  >=0.8       && <0.9
-    , keiki-codec-json                       >=0.8       && <0.9
-    , keiro-core                             ^>=0.10.0.0
+    , keiki                                  >=0.9       && <0.10
+    , keiki-codec-json                       >=0.9       && <0.10
+    , keiro-core                             ^>=0.11.0.0
     , kiroku-store                           >=0.3       && <0.4
     , lens                                   >=5.2       && <5.4
     , mmzk-typeid                            >=0.7       && <0.8
@@ -191,7 +191,7 @@
     , hs-opentelemetry-api  >=1.0       && <1.1
     , hs-opentelemetry-sdk  >=1.0       && <1.1
     , keiro
-    , keiro-core            ^>=0.10.0.0
+    , keiro-core            ^>=0.11.0.0
     , keiro-test-support
     , kiroku-store          >=0.3       && <0.4
     , tasty-bench           >=0.4
diff --git a/src/Keiro/Timer/Schema.hs b/src/Keiro/Timer/Schema.hs
--- a/src/Keiro/Timer/Schema.hs
+++ b/src/Keiro/Timer/Schema.hs
@@ -65,7 +65,9 @@
   | Fired
   | Cancelled
   | Dead
-  deriving stock (Generic, Eq, Show)
+  -- 'Enum'/'Bounded' let a consumer enumerate the complete lifecycle rather than
+  -- restate it; keiro-dsl's cross-package vocabulary test relies on this.
+  deriving stock (Generic, Eq, Show, Enum, Bounded)
 
 -- | A timer row as stored: the original 'TimerRequest' fields plus the live
 -- 'status', the 'attempts' count (incremented on each claim), and the
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -11250,57 +11250,62 @@
 
 sAddCtor :: InCtor SkipCommand AddFields
 sAddCtor =
-  InCtor
-    { icName = "SAdd",
-      icMatch = \case
+  Keiki.unavailableInCtor
+    "SAdd"
+    ( \case
         SAdd amount -> Just (RCons Proxy amount RNil)
-        SSkip -> Nothing,
-      icBuild = \case
+        SSkip -> Nothing
+    )
+    ( \case
         RCons _ amount RNil -> SAdd amount
-    }
+    )
 
 sSkipCtor :: InCtor SkipCommand '[]
 sSkipCtor =
-  InCtor
-    { icName = "SSkip",
-      icMatch = \case
+  Keiki.unavailableInCtor
+    "SSkip"
+    ( \case
         SAdd {} -> Nothing
-        SSkip -> Just RNil,
-      icBuild = \case
+        SSkip -> Just RNil
+    )
+    ( \case
         RNil -> SSkip
-    }
+    )
 
 addCtor :: InCtor CounterCommand AddFields
 addCtor =
-  InCtor
-    { icName = "Add",
-      icMatch = \case
-        Add amount -> Just (RCons Proxy amount RNil),
-      icBuild = \case
+  Keiki.unavailableInCtor
+    "Add"
+    ( \case
+        Add amount -> Just (RCons Proxy amount RNil)
+    )
+    ( \case
         RCons _ amount RNil -> Add amount
-    }
+    )
 
 counterAddedCtor :: WireCtor CounterEvent (Int, ())
 counterAddedCtor =
-  WireCtor
-    { wcName = "CounterAdded",
-      wcMatch = \case
+  Keiki.unavailableWireCtor
+    "CounterAdded"
+    ( \case
         CounterAdded amount -> Just (amount, ())
-        CounterAudited {} -> Nothing,
-      wcBuild = \case
+        CounterAudited {} -> Nothing
+    )
+    ( \case
         (amount, ()) -> CounterAdded amount
-    }
+    )
 
 counterAuditedCtor :: WireCtor CounterEvent (Int, ())
 counterAuditedCtor =
-  WireCtor
-    { wcName = "CounterAudited",
-      wcMatch = \case
+  Keiki.unavailableWireCtor
+    "CounterAudited"
+    ( \case
         CounterAudited amount -> Just (amount, ())
-        CounterAdded {} -> Nothing,
-      wcBuild = \case
+        CounterAdded {} -> Nothing
+    )
+    ( \case
         (amount, ()) -> CounterAudited amount
-    }
+    )
 
 counterCodec :: Codec CounterEvent
 counterCodec =
@@ -11352,23 +11357,25 @@
 
 confirmDivertCtor :: InCtor DivertCommand DivertFields
 confirmDivertCtor =
-  InCtor
-    { icName = "ConfirmDivert",
-      icMatch = \case
-        ConfirmDivert acuityBlack -> Just (RCons Proxy acuityBlack RNil),
-      icBuild = \case
+  Keiki.unavailableInCtor
+    "ConfirmDivert"
+    ( \case
+        ConfirmDivert acuityBlack -> Just (RCons Proxy acuityBlack RNil)
+    )
+    ( \case
         RCons _ acuityBlack RNil -> ConfirmDivert acuityBlack
-    }
+    )
 
 divertConfirmedCtor :: WireCtor DivertEvent (Bool, ())
 divertConfirmedCtor =
-  WireCtor
-    { wcName = "DivertConfirmed",
-      wcMatch = \case
-        DivertConfirmed acuityBlack -> Just (acuityBlack, ()),
-      wcBuild = \case
+  Keiki.unavailableWireCtor
+    "DivertConfirmed"
+    ( \case
+        DivertConfirmed acuityBlack -> Just (acuityBlack, ())
+    )
+    ( \case
         (acuityBlack, ()) -> DivertConfirmed acuityBlack
-    }
+    )
 
 divertCodec :: Codec DivertEvent
 divertCodec =
