diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -6,10 +6,10 @@
 operations against a real or isolated provider registry.
 
 It is the *seam*, not the framework: it adds **no** retries, backoff, rate limiting,
-budgets, caching, or error remapping. Failures propagate exactly as baikai produces them —
-the blocking path throws `Baikai.Error.BaikaiError`; the streaming paths surface baikai's
-terminal `EventError` event in-band. Policy belongs one layer up, written *in terms of* this
-effect.
+budgets, caching, or error remapping. Failures propagate exactly as baikai produces them:
+blocking completions return the same error-shaped `Response` as `completeRequest`, and
+streaming paths surface baikai's terminal `EventError` event in-band. Policy belongs one
+layer up, written *in terms of* this effect.
 
 ## The effect
 
@@ -24,7 +24,7 @@
 
 | Operation                         | Meaning                                                        |
 | --------------------------------- | ------------------------------------------------------------- |
-| `complete m c o`                  | Blocking completion → `Response`. Throws `BaikaiError`.       |
+| `complete m c o`                  | Blocking completion → `Response`, including error-shaped responses. |
 | `streamCollect m c o`             | Drain the stream into the full `[AssistantMessageEvent]`.     |
 | `streamEach m c o k`              | Run callback `k` once per event, in order, inside `Eff`.      |
 
diff --git a/baikai-effectful.cabal b/baikai-effectful.cabal
--- a/baikai-effectful.cabal
+++ b/baikai-effectful.cabal
@@ -1,6 +1,6 @@
 cabal-version:   3.4
 name:            baikai-effectful
-version:         0.2.0.0
+version:         0.3.0.0
 synopsis:        effectful binding for the baikai AI-provider transport
 description:
   A thin, policy-free effectful binding over baikai's transport. Provides the dynamic
@@ -35,7 +35,7 @@
   hs-source-dirs:  src
   exposed-modules: Baikai.Effectful
   build-depends:
-    , baikai          ^>=0.2.0
+    , baikai          ^>=0.3.0
     , base            >=4.20   && <5
     , effectful-core
     , streamly        >=0.11   && <0.13
diff --git a/src/Baikai/Effectful.hs b/src/Baikai/Effectful.hs
--- a/src/Baikai/Effectful.hs
+++ b/src/Baikai/Effectful.hs
@@ -9,7 +9,8 @@
 -- baikai's transport functions, plus two interpreters that run those operations
 -- against a real (global) or isolated provider registry. It carries NO policy:
 -- failures propagate exactly as baikai produces them — the blocking path
--- ('complete') throws 'Baikai.Error.BaikaiError'; the streaming paths
+-- ('complete') returns an error-shaped 'Response' whose
+-- 'Baikai.responseError' is populated; the streaming paths
 -- ('streamCollect', 'streamEach') surface baikai's terminal @EventError@ in-band.
 -- Retries, caching, budgets, rate limiting, and error remapping belong one layer
 -- up, in terms of this effect.
@@ -46,8 +47,8 @@
 -- | The provider-neutral baikai transport effect. Operations mirror baikai's
 -- transport functions; the interpreter selects the provider registry.
 data Baikai :: Effect where
-  -- | A blocking completion. Mirrors 'Baikai.completeRequest' — throws
-  -- 'Baikai.Error.BaikaiError' on failure.
+  -- | A blocking completion. Mirrors 'Baikai.completeRequest' — failures
+  -- are returned in-band as an error-shaped 'Response'.
   Complete :: Model -> Context -> Options -> Baikai m Response
   -- | A streaming completion materialized into the full event list. Mirrors
   -- 'Baikai.streamRequest' drained to a list; a terminal @EventError@ appears
@@ -59,8 +60,8 @@
 
 type instance DispatchOf Baikai = 'Dynamic
 
--- | Issue a blocking completion. Throws baikai's 'Baikai.Error.BaikaiError' on
--- failure, just like 'Baikai.completeRequest' — this binding does not catch it.
+-- | Issue a blocking completion. Provider failures are returned in-band
+-- as an error-shaped 'Response', just like 'Baikai.completeRequest'.
 complete :: (Baikai :> es) => Model -> Context -> Options -> Eff es Response
 complete m c o = send (Complete m c o)
 
diff --git a/test/LiveSpec.hs b/test/LiveSpec.hs
--- a/test/LiveSpec.hs
+++ b/test/LiveSpec.hs
@@ -15,7 +15,6 @@
 import Data.Text qualified as T
 import Data.Vector qualified as V
 import Effectful (runEff)
-import StubProvider (flattenAssistantText)
 import System.Environment (lookupEnv)
 import Test.Tasty (TestTree, testGroup)
 import Test.Tasty.HUnit (assertBool, testCase)
@@ -29,8 +28,8 @@
         case live of
           Just "1" -> do
             OpenAI.register
-            let ctx = _Context & #messages .~ V.singleton (user "Reply with a single word.")
-                opts = _Options & #maxTokens .~ Just 16
+            let ctx = emptyContext & #messages .~ V.singleton (user "Reply with a single word.")
+                opts = emptyOptions & #maxTokens .~ Just 16
             out <-
               runEff . runBaikai $ do
                 r <- complete openai_gpt_4o_mini ctx opts
diff --git a/test/StubProvider.hs b/test/StubProvider.hs
--- a/test/StubProvider.hs
+++ b/test/StubProvider.hs
@@ -19,20 +19,10 @@
 
 import Baikai
 import Baikai.Prelude
-import Data.Text qualified as T
 import Data.Vector qualified as V
 import Streamly.Data.Stream (Stream)
 import Streamly.Data.Stream qualified as Stream
 
--- | Concatenate the text of every 'AssistantText' block, ignoring thinking and
--- tool-call blocks. baikai has no library equivalent — its own smoke tests define
--- this same helper locally (see @baikai-smoke/test/Smoke.hs@).
-flattenAssistantText :: V.Vector AssistantContent -> Text
-flattenAssistantText = T.concat . V.toList . V.mapMaybe textOf
-  where
-    textOf (AssistantText (TextContent t)) = Just t
-    textOf _ = Nothing
-
 -- | The 'Api' tag the stub provider serves. A 'Custom' tag needs no catalog entry.
 stubApi :: Api
 stubApi = Custom "stub"
@@ -40,7 +30,7 @@
 -- | A hand-built 'Model' whose 'api' routes to the stub provider.
 stubModel :: Model
 stubModel =
-  _Model
+  emptyModel
     & #api
     .~ stubApi
     & #modelId
@@ -52,27 +42,27 @@
 
 -- | A minimal request context (one user turn).
 stubContext :: Context
-stubContext = _Context & #messages .~ V.singleton (user "ping")
+stubContext = emptyContext & #messages .~ V.singleton (user "ping")
 
 -- | Default request options.
 stubOptions :: Options
-stubOptions = _Options
+stubOptions = emptyOptions
 
 -- | A blank assistant payload with a fixed (epoch) timestamp, reused as the base
 -- for both the streaming skeleton and the terminal message so nothing varies run
--- to run. Borrowed from baikai's '_Response' fixture base.
+-- to run. Borrowed from baikai's 'emptyResponse' fixture base.
 stubPayload :: AssistantPayload
-stubPayload = _Response ^. #message
+stubPayload = emptyResponse ^. #message
 
 -- | An assistant payload carrying the given text as its single text block.
 stubPayloadWith :: Text -> AssistantPayload
 stubPayloadWith t =
-  stubPayload & #content .~ V.singleton (AssistantText (_TextContent & #text .~ t))
+  stubPayload & #content .~ V.singleton (AssistantText (emptyTextContent & #text .~ t))
 
 -- | A fixed assistant response carrying the given text as its single text block.
 stubResponse :: Text -> Response
 stubResponse t =
-  _Response
+  emptyResponse
     & #message
     .~ stubPayloadWith t
     & #model
@@ -90,11 +80,11 @@
 -- 'EventStart' first, one text block, and one 'EventDone' last.
 stubEvents :: Text -> [AssistantMessageEvent]
 stubEvents t =
-  [ EventStart StartPayload {partial = AssistantMessage stubPayload},
+  [ EventStart StartPayload {partial = AssistantMessage stubPayload, responseId = Nothing},
     TextStart IndexPayload {contentIndex = 0},
     TextDelta DeltaPayload {contentIndex = 0, delta = t},
     TextEnd BlockEndPayload {contentIndex = 0, content = t},
-    EventDone (doneTerminal Stop (AssistantMessage (stubPayloadWith t)))
+    EventDone (doneTerminal Nothing Stop (AssistantMessage (stubPayloadWith t)))
   ]
 
 -- | The provider's streaming completion: ignore the request, emit fixed events.
