diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,18 @@
 
 ## Unreleased
 
+## 0.3.0.2 — 2026-10-16
+
+### Changed
+
+- Require the compatible Shikumi patch releases built against Baikai 0.8.
+
+- Upgrade `mori://shinzui/baikai/packages/baikai` to `>=0.8.0.0 && <0.9`.
+
+### Fixed
+
+- Make the offline test stubs count and capture concurrent LLM calls atomically, preventing lost observations in budget assertions. No library behavior changes.
+
 ## 0.3.0.1 — 2026-10-05
 
 - Move the dependency on `mori://shinzui/baikai/packages/baikai` to `>=0.7.1.0 && <0.8` and widen the `effectful` bound to `>=2.6 && <2.8`, so both effectful 2.6 and 2.7 are supported (effectful 2.7 needs `baikai-effectful` 0.4.0.2, effectful 2.6 needs 0.4.0.1). Bounds only; no source changed.
diff --git a/shikumi-optimize.cabal b/shikumi-optimize.cabal
--- a/shikumi-optimize.cabal
+++ b/shikumi-optimize.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name: shikumi-optimize
-version: 0.3.0.1
+version: 0.3.0.2
 synopsis: The optimizer framework for shikumi LM programs (EP-10)
 category: AI
 description:
@@ -76,10 +76,10 @@
     effectful >=2.6 && <2.8,
     generic-lens >=2.2 && <2.4,
     lens ^>=5.3,
-    shikumi ^>=0.4.0.0,
-    shikumi-compile ^>=0.2.1.1,
-    shikumi-eval ^>=0.3.0.1,
-    shikumi-trace ^>=0.3.0.1,
+    shikumi ^>=0.4.1.1,
+    shikumi-compile ^>=0.2.1.2,
+    shikumi-eval ^>=0.3.0.2,
+    shikumi-trace ^>=0.3.0.2,
     text ^>=2.1,
     vector >=0.13 && <0.14,
 
@@ -116,17 +116,17 @@
 
   build-depends:
     aeson,
-    baikai >=0.7.1.0 && <0.8,
+    baikai >=0.8.0.0 && <0.9,
     base,
     containers,
     effectful,
     generic-lens,
     lens,
-    shikumi ^>=0.4.0.0,
-    shikumi-compile ^>=0.2.1.1,
-    shikumi-eval ^>=0.3.0.1,
-    shikumi-optimize ^>=0.3.0.1,
-    shikumi-trace ^>=0.3.0.1,
+    shikumi ^>=0.4.1.1,
+    shikumi-compile ^>=0.2.1.2,
+    shikumi-eval ^>=0.3.0.2,
+    shikumi-optimize ^>=0.3.0.2,
+    shikumi-trace ^>=0.3.0.2,
     tasty,
     tasty-hunit,
     text,
diff --git a/test/StubLM.hs b/test/StubLM.hs
--- a/test/StubLM.hs
+++ b/test/StubLM.hs
@@ -77,7 +77,7 @@
 import Control.Lens ((&), (.~), (^.))
 import Data.Aeson (ToJSON)
 import Data.Generics.Labels ()
-import Data.IORef (IORef, modifyIORef')
+import Data.IORef (IORef, atomicModifyIORef')
 import Data.Maybe (fromMaybe)
 import Data.Set qualified as Set
 import Data.Text (Text)
@@ -189,7 +189,7 @@
 runStubLMCounting :: (IOE :> es) => IORef Int -> Eff (LLM : es) a -> Eff es a
 runStubLMCounting ref = interpret $ \_ -> \case
   Complete _ ctx _ -> do
-    liftIO (modifyIORef' ref (+ 1))
+    liftIO (atomicModifyIORef' ref (\n -> (n + 1, ())))
     pure (mkResponse (respondTo ctx))
   Stream {} -> pure []
 
@@ -199,7 +199,7 @@
 runStubLMCapturing :: (IOE :> es) => IORef [Text] -> Eff (LLM : es) a -> Eff es a
 runStubLMCapturing ref = interpret $ \_ -> \case
   Complete _ ctx _ -> do
-    liftIO (modifyIORef' ref (++ [fullRequestText ctx]))
+    liftIO (atomicModifyIORef' ref (\requests -> (requests ++ [fullRequestText ctx], ())))
     pure (mkResponse (respondTo ctx))
   Stream {} -> pure []
 
@@ -218,7 +218,7 @@
 runJointStubLMCounting :: (IOE :> es) => IORef Int -> Eff (LLM : es) a -> Eff es a
 runJointStubLMCounting ref = interpret $ \_ -> \case
   Complete _ ctx _ -> do
-    liftIO (modifyIORef' ref (+ 1))
+    liftIO (atomicModifyIORef' ref (\n -> (n + 1, ())))
     pure (mkResponse (respondWith answerJoint ctx))
   Stream {} -> pure []
 
@@ -287,7 +287,7 @@
 runGepaStubLMCapturing :: (IOE :> es) => IORef [Text] -> Eff (LLM : es) a -> Eff es a
 runGepaStubLMCapturing ref = interpret $ \_ -> \case
   Complete _ ctx _ -> do
-    liftIO (modifyIORef' ref (++ [fullRequestText ctx]))
+    liftIO (atomicModifyIORef' ref (\requests -> (requests ++ [fullRequestText ctx], ())))
     pure (mkResponse (respondGepa ctx))
   Stream {} -> pure []
 
