diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -7,6 +7,91 @@
 
 ## [Unreleased]
 
+## [baikai 0.9.0.0] - 2026-10-25
+
+### Added
+
+- `Baikai.Judgment` compiles closed-set schemas into ordered questions, refuses
+  generation controls and validates neutral answers without normalizing provider
+  probabilities or deriving confidence and expected scores.
+- `UserData`, `AssistantData`/`DataContent`, user-data message helpers, atomic
+  data events, and named full-response JSON codecs preserve structured answers.
+- `TypeSafeSystemOne`, catalog-owned `OutputKind`/`isJudgmentOnly`, `InputData`,
+  and `typesafe_jev_latest`/`typesafe_jev_preview`. Legacy model JSON defaults to
+  generative output. Register the new `baikai-typesafe` provider explicitly.
+
+### Changed (breaking)
+
+- Public content/event sums and model, error, trace and log records gain fields
+  or constructors. Update exhaustive matches and record construction on upgrade.
+- Terminal traces and both call-log paths preserve structured answer payloads.
+  Evidence schema 2.6 includes structured response commitments and typed request
+  problems; existing digest rules are unchanged.
+
+## [baikai-typesafe 0.1.0.0] - 2026-10-25
+
+### Added
+
+- Initial explicitly registered TypeSafe System One provider using the released
+  `typesafe-hs ^>=0.1.0.0` wire client. Strict request and response validation,
+  atomic synthetic streaming, truthful call evidence, bounded HTTP calls and an
+  opt-in live test. No retry policy is embedded.
+- Installation, schema, refusal and consumer migration guide, plus CAP-27.
+
+## [baikai-claude 0.7.2.0] - 2026-10-25
+
+### Changed
+
+- Require `baikai ^>=0.9.0`, encode user data as canonical JSON text, and refuse
+  structured assistant history before dispatch with a typed field path.
+
+### Fixed
+
+- The offline cancellation fixture waits until the response reader is active
+  before cancelling, eliminating a fixed-startup-delay race under build load.
+
+## [baikai-openai 0.7.2.0] - 2026-10-25
+
+### Changed
+
+- Require `baikai ^>=0.9.0`, encode user data as canonical JSON text, and refuse
+  structured assistant history before dispatch with a typed field path across
+  Chat Completions, Responses and batch CLI calls.
+
+### Fixed
+
+- The offline cancellation fixture waits until the response reader is active
+  before cancelling, eliminating a fixed-startup-delay race under build load.
+
+## [baikai-trace-otel 0.4.1.0] - 2026-10-25
+
+### Added
+
+- Export structured answer data as canonical JSON under
+  `baikai.response.structured_data`, on success and failure terminals.
+
+### Changed
+
+- Require `baikai ^>=0.9.0` for the expanded trace protocol.
+
+## [baikai-effectful 0.4.0.4] - 2026-10-25
+
+### Changed
+
+- Require `baikai ^>=0.9.0`. Dependency-only update; no package API change.
+
+## [baikai-kit 0.4.0.2] - 2026-10-25
+
+### Changed
+
+- Require `baikai ^>=0.9.0`. Dependency-only update; no package API change.
+
+## [baikai-agent 0.2.0.3] - 2026-10-25
+
+### Changed
+
+- Require `baikai ^>=0.9.0`. Dependency-only update; no package API change.
+
 ## [baikai 0.8.0.0] - 2026-10-10
 
 ### Added
diff --git a/baikai-openai.cabal b/baikai-openai.cabal
--- a/baikai-openai.cabal
+++ b/baikai-openai.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name: baikai-openai
-version: 0.7.1.1
+version: 0.7.2.0
 synopsis: OpenAI providers for the baikai abstraction
 description:
   OpenAI-compatible backends for baikai: Chat Completions over SSE against OpenAI and
@@ -77,7 +77,7 @@
   autogen-modules: Paths_baikai_openai
   build-depends:
     aeson ^>=2.2,
-    baikai ^>=0.8.0,
+    baikai ^>=0.9.0,
     base >=4.20 && <5,
     base64-bytestring ^>=1.2,
     bytestring ^>=0.12,
@@ -111,6 +111,7 @@
     CliEvidenceSpec
     CliProcessFixture
     Contract
+    DataInputSpec
     EndpointModels
     ErrorClassSpec
     EvidenceSpec
@@ -130,7 +131,7 @@
 
   build-depends:
     aeson,
-    baikai ^>=0.8.0,
+    baikai ^>=0.9.0,
     baikai-openai,
     base >=4.20 && <5,
     bytestring,
diff --git a/src/Baikai/Provider/OpenAI/Cli.hs b/src/Baikai/Provider/OpenAI/Cli.hs
--- a/src/Baikai/Provider/OpenAI/Cli.hs
+++ b/src/Baikai/Provider/OpenAI/Cli.hs
@@ -50,6 +50,7 @@
 import Baikai.Api (Api (..))
 import Baikai.Content (AssistantContent (..), TextContent (..))
 import Baikai.Context (Context)
+import Baikai.Context qualified as Context
 import Baikai.Error (BaikaiError, processError, providerError)
 import Baikai.Evidence qualified as Ev
 import Baikai.Evidence.Build qualified as Build
@@ -70,6 +71,7 @@
 import Baikai.ThinkingLevel (ThinkingLevel, renderThinkingLevel)
 import Baikai.Usage (Usage, zeroUsage)
 import Control.Exception (IOException, SomeException, bracket, displayException, fromException, try)
+import Control.Exception qualified
 import Control.Lens ((&), (.~), (^.))
 import Control.Monad (void)
 import Data.Aeson qualified as Aeson
@@ -254,6 +256,7 @@
 
 runCodexCli :: CodexCliConfig -> Model -> Context -> Options -> IO Resp.Response
 runCodexCli cfg m ctx opts = do
+  either Control.Exception.throwIO pure (Context.refuseAssistantDataHistory ctx)
   let schema = requestedSchema opts
       -- The envelope names the schema itself where the spawned vector
       -- names its temporary file: the random path would make the
diff --git a/src/Baikai/Provider/OpenAI/Internal/Request.hs b/src/Baikai/Provider/OpenAI/Internal/Request.hs
--- a/src/Baikai/Provider/OpenAI/Internal/Request.hs
+++ b/src/Baikai/Provider/OpenAI/Internal/Request.hs
@@ -16,6 +16,8 @@
   )
 import Baikai.Content qualified as Content
 import Baikai.Context (Context (..))
+import Baikai.Context qualified as Context
+import Baikai.Error qualified as Error
 import Baikai.Message qualified as Msg
 import Baikai.Model (Model, openaiCompletionsCompatFor)
 import Baikai.Options (Options (..))
@@ -49,6 +51,7 @@
 mapRequest ::
   Model -> Context -> Options -> Either Text Chat.CreateChatCompletion
 mapRequest m ctx opts = do
+  either (Left . Error.message) pure (Context.refuseAssistantDataHistory ctx)
   let capability = openaiCompletionsCompatFor m
       forced = case opts ^. #toolChoice of
         Just Tool.ToolChoiceRequired -> True
@@ -272,6 +275,7 @@
 userContentToPart :: Content.UserContent -> Chat.Content
 userContentToPart = \case
   Content.UserText (Content.TextContent t) -> Chat.Text {Chat.text = t}
+  Content.UserData v -> Chat.Text {Chat.text = Content.canonicalDataText v}
   Content.UserImage img ->
     let encoded = Text.decodeUtf8 (Base64.encode (Content.imageData img))
         uri = "data:" <> Content.mimeType img <> ";base64," <> encoded
@@ -288,6 +292,7 @@
           Content.AssistantText (Content.TextContent t) -> Just t
           Content.AssistantThinking _ -> Nothing
           Content.AssistantToolCall _ -> Nothing
+          Content.AssistantData _ -> Nothing
       )
 
 collectToolCalls :: Vector Content.AssistantContent -> Vector ToolCall.ToolCall
diff --git a/src/Baikai/Provider/OpenAI/Internal/Stream.hs b/src/Baikai/Provider/OpenAI/Internal/Stream.hs
--- a/src/Baikai/Provider/OpenAI/Internal/Stream.hs
+++ b/src/Baikai/Provider/OpenAI/Internal/Stream.hs
@@ -45,6 +45,7 @@
 import Baikai.Compat (OpenAICompletionsCompat (requiresThinkingAsText))
 import Baikai.Content qualified as Content
 import Baikai.Context (Context (..))
+import Baikai.Context qualified as Context
 import Baikai.Cost.Pricing qualified as Pricing
 import Baikai.Error (BaikaiError, contentFiltered, invalidRequest, providerError)
 import Baikai.Evidence qualified as Ev
@@ -216,35 +217,37 @@
   u -> u
 
 prepareCall :: Model -> Context -> Options -> IO (Either BaikaiError OpenAICall)
-prepareCall m ctx opts = case mapRequest m ctx opts of
-  Left e -> pure (Left (invalidRequest e))
-  Right req -> do
-    let url = resolvedBaseUrl m
-    -- Checked before the key is resolved, so a base URL baikai will not
-    -- send to never causes a credential to be read out of the
-    -- environment. The message names the problem and what to write
-    -- instead; it renders the URL without its userinfo or query, so an
-    -- error reaching a log cannot carry a key someone put in either.
-    case Url.baseUrlProblem url of
-      Just problem ->
-        pure (Left (invalidRequest ("Model.baseUrl is not usable: " <> problem)))
-      Nothing -> do
-        key <- Transport.resolveKey url opts
-        env <- Transport.getClientEnvCached url
-        let compat = openaiCompletionsCompatFor m
-            (body, translation) = streamRequestBody compat (m ^. #reasoning) opts req
-            headers = Transport.requestHeaders key m opts
-        pure
-          ( Right
-              OpenAICall
-                { clientEnv = env,
-                  requestHeaders = headers,
-                  timeoutMs = opts ^. #timeoutMs,
-                  requestBody = body,
-                  baseUrl = url,
-                  thinking = translation
-                }
-          )
+prepareCall m ctx opts
+  | Left e <- Context.refuseAssistantDataHistory ctx = pure (Left e)
+  | otherwise = case mapRequest m ctx opts of
+      Left e -> pure (Left (invalidRequest e))
+      Right req -> do
+        let url = resolvedBaseUrl m
+        -- Checked before the key is resolved, so a base URL baikai will not
+        -- send to never causes a credential to be read out of the
+        -- environment. The message names the problem and what to write
+        -- instead; it renders the URL without its userinfo or query, so an
+        -- error reaching a log cannot carry a key someone put in either.
+        case Url.baseUrlProblem url of
+          Just problem ->
+            pure (Left (invalidRequest ("Model.baseUrl is not usable: " <> problem)))
+          Nothing -> do
+            key <- Transport.resolveKey url opts
+            env <- Transport.getClientEnvCached url
+            let compat = openaiCompletionsCompatFor m
+                (body, translation) = streamRequestBody compat (m ^. #reasoning) opts req
+                headers = Transport.requestHeaders key m opts
+            pure
+              ( Right
+                  OpenAICall
+                    { clientEnv = env,
+                      requestHeaders = headers,
+                      timeoutMs = opts ^. #timeoutMs,
+                      requestBody = body,
+                      baseUrl = url,
+                      thinking = translation
+                    }
+              )
 
 -- | A loose summary of one streamed chunk. The raw 'Aeson.Value' is
 -- pre-parsed into the fields we care about; unknown fields are
diff --git a/src/Baikai/Provider/OpenAI/Responses/Assembler.hs b/src/Baikai/Provider/OpenAI/Responses/Assembler.hs
--- a/src/Baikai/Provider/OpenAI/Responses/Assembler.hs
+++ b/src/Baikai/Provider/OpenAI/Responses/Assembler.hs
@@ -287,6 +287,7 @@
 deltaEvent _ n txt = E.TextDelta (E.DeltaPayload n txt)
 
 endEvent :: Int -> C.AssistantContent -> E.AssistantMessageEvent
+endEvent n (C.AssistantData c) = E.DataEnd (E.DataEndPayload n c)
 endEvent n (C.AssistantThinking c) = E.ThinkingEnd (E.ThinkingEndPayload n c)
 endEvent n (C.AssistantToolCall c) = E.ToolCallEnd (E.ToolCallEndPayload n c)
 endEvent n (C.AssistantText c) = E.TextEnd (E.BlockEndPayload n c.text)
diff --git a/src/Baikai/Provider/OpenAI/Responses/Request.hs b/src/Baikai/Provider/OpenAI/Responses/Request.hs
--- a/src/Baikai/Provider/OpenAI/Responses/Request.hs
+++ b/src/Baikai/Provider/OpenAI/Responses/Request.hs
@@ -20,6 +20,8 @@
 import Baikai.Compat (OpenAIResponsesCompat (..))
 import Baikai.Content qualified as C
 import Baikai.Context (Context (..))
+import Baikai.Context qualified as Context
+import Baikai.Error qualified as Error
 import Baikai.Evidence qualified as E
 import Baikai.Message qualified as M
 import Baikai.Model (Model, api, maxOutputTokens, modelId, openaiResponsesCompatFor, reasoning)
@@ -55,6 +57,7 @@
 
 mapRequest :: Model -> Context -> Options -> Either Text PreparedRequest
 mapRequest m ctx opts = do
+  either (Left . Error.message) pure (Context.refuseAssistantDataHistory ctx)
   unless (normaliseApi m.api == OpenAIResponses) (Left "Responses requires Model.api = OpenAIResponses")
   let compat = openaiResponsesCompatFor m
       unsupported = [name | (name, present) <- [("stopSequences", not (null opts.stopSequences)), ("seed", isJust opts.seed), ("frequencyPenalty", isJust opts.frequencyPenalty), ("presencePenalty", isJust opts.presencePenalty)], present]
@@ -174,6 +177,7 @@
     pure [Aeson.toJSON R.Item_Input_Function_Call_Output {R.id = Nothing, R.call_id = p.toolCallId, R.output = output, R.status = Nothing}]
   where
     assistantItem = \case
+      C.AssistantData _ -> Left "Cannot replay AssistantData evidence"
       C.AssistantText t -> pure [object ["role" .= ("assistant" :: Text), "content" .= t.text]]
       C.AssistantToolCall tc -> do
         unless (not (Text.null tc.id_) && not (Text.null tc.name)) (Left "Responses tool calls need a nonempty call_id and name")
@@ -188,6 +192,7 @@
 userPart :: C.UserContent -> Value
 userPart = \case
   C.UserText t -> Aeson.toJSON (R.Input_Text t.text)
+  C.UserData v -> Aeson.toJSON (R.Input_Text (C.canonicalDataText v))
   C.UserImage img ->
     Aeson.toJSON
       R.Input_Image
diff --git a/src/Baikai/Provider/OpenAI/Responses/Stream.hs b/src/Baikai/Provider/OpenAI/Responses/Stream.hs
--- a/src/Baikai/Provider/OpenAI/Responses/Stream.hs
+++ b/src/Baikai/Provider/OpenAI/Responses/Stream.hs
@@ -10,6 +10,7 @@
 
 import Baikai.Content qualified as C
 import Baikai.Context (Context)
+import Baikai.Context qualified as Context
 import Baikai.Cost.Pricing qualified as Pricing
 import Baikai.Error (BaikaiError, invalidRequest, providerError)
 import Baikai.Evidence qualified as Ev
@@ -31,6 +32,7 @@
 import Baikai.Usage qualified as U
 import Control.Applicative ((<|>))
 import Control.Exception (SomeAsyncException, SomeException, fromException, throwIO, try)
+import Control.Exception qualified
 import Control.Lens ((&), (.~), (^.))
 import Data.Aeson (Value (..), object, (.=))
 import Data.Aeson.Key (Key)
@@ -56,6 +58,7 @@
 openaiResponsesStreamWith :: SseDriver -> Model -> Context -> Options -> Stream IO E.AssistantMessageEvent
 openaiResponsesStreamWith driver m ctx opts = Stream.concatEffect $ do
   setup <- trySync $ do
+    either Control.Exception.throwIO pure (Context.refuseAssistantDataHistory ctx)
     req <- either (throwIO . invalidRequest) pure (R.mapRequest m ctx opts)
     let url = resolvedUrl m
     case Url.baseUrlProblem url of
diff --git a/src/Baikai/Provider/OpenAI/Transport.hs b/src/Baikai/Provider/OpenAI/Transport.hs
--- a/src/Baikai/Provider/OpenAI/Transport.hs
+++ b/src/Baikai/Provider/OpenAI/Transport.hs
@@ -83,7 +83,8 @@
       httpStatus = Nothing,
       retryAfterSeconds = Nothing,
       exitCode = Nothing,
-      refusalCategory = Nothing
+      refusalCategory = Nothing,
+      requestProblem = Nothing
     }
 
 -- | Apply caller overrides over the provider's own headers.
diff --git a/test/DataInputSpec.hs b/test/DataInputSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/DataInputSpec.hs
@@ -0,0 +1,66 @@
+module DataInputSpec (tests) where
+
+import Baikai
+import Baikai.Prelude
+import Baikai.Provider.OpenAI.Cli qualified as Cli
+import Baikai.Provider.OpenAI.Internal.Request qualified as R
+import Baikai.Provider.OpenAI.Internal.Stream qualified as S
+import Baikai.Provider.OpenAI.Responses.Request qualified as RR
+import Baikai.Provider.OpenAI.Responses.Stream qualified as RS
+import Control.Exception (try)
+import Data.Aeson qualified as A
+import Data.ByteString.Lazy.Char8 qualified as BL
+import Data.IORef
+import Data.Text qualified as T
+import Data.Vector qualified as V
+import Streamly.Data.Stream qualified as Stream
+import Test.Tasty
+import Test.Tasty.HUnit
+
+modelFixture :: Model
+modelFixture = emptyModel & #modelId .~ "fixture" & #api .~ OpenAIChatCompletions
+
+dataFixture :: A.Value
+dataFixture = A.object ["z" A..= [1 :: Int, 2], "a" A..= ("日本語" :: T.Text)]
+
+history :: Context
+history = contextOf [AssistantMessage ((emptyResponse ^. #message) & #content .~ V.singleton (AssistantData (DataContent A.Null Nothing Nothing Nothing)))]
+
+tests :: TestTree
+tests =
+  testGroup
+    "structured input and history"
+    [ testCase "Responses sends structured input canonically and refuses assistant data before driver" $ do
+        req <- either (assertFailure . T.unpack) pure (RR.mapRequest (modelFixture & #api .~ OpenAIResponses) (contextOf [userData dataFixture]) emptyOptions)
+        let bytes = BL.unpack (A.encode (req ^. #requestBody))
+        assertBool "canonical data reaches Responses wire" (T.pack (BL.unpack (A.encode (canonicalDataText dataFixture))) `T.isInfixOf` T.pack bytes)
+        calls <- newIORef (0 :: Int)
+        es <- Stream.toList (RS.openaiResponsesStreamWith (\_ _ _ _ _ -> modifyIORef' calls (+ 1)) (modelFixture & #api .~ OpenAIResponses) history emptyOptions)
+        readIORef calls >>= (@?= 0)
+        case [e | EventError p <- es, Just e <- [p ^. #errorInfo]] of
+          [e] -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
+          _ -> assertFailure (show es),
+      testCase "actual wire request contains canonical JSON in block position" $ do
+        let ctx = contextOf [UserMessage (UserPayload (V.fromList [UserText (TextContent "before"), UserData dataFixture, UserText (TextContent "after")]) Nothing)]
+        req <- either (assertFailure . T.unpack) pure (R.mapRequest modelFixture ctx emptyOptions)
+        let wire = A.toJSON req
+            bytes = BL.unpack (A.encode wire)
+            jsonText = BL.unpack (A.encode (canonicalDataText dataFixture))
+        assertBool ("structured text missing: " <> bytes) (T.pack jsonText `T.isInfixOf` T.pack bytes),
+      testCase "OpenAIChatCompletions refuses assistant data with zero driver calls" $ do
+        calls <- newIORef (0 :: Int)
+        let driver _ _ _ _ _ = modifyIORef' calls (+ 1)
+        es <- Stream.toList (S.openaiChatStreamWith driver modelFixture history emptyOptions)
+        readIORef calls >>= (@?= 0)
+        case [e | EventError p <- es, Just e <- [p ^. #errorInfo]] of
+          [e] -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
+          _ -> assertFailure (show es),
+      testCase "CLI request carries JSON and refuses data history before spawning" $ do
+        let cfg = Cli.defaultCodexCliConfig & #executable .~ "/nonexistent-baikai-data-test"
+            (_, args) = Cli.codexCliCommand cfg modelFixture (contextOf [userData dataFixture]) emptyOptions
+        assertBool "canonical JSON reaches argument vector" (any (T.isInfixOf (canonicalDataText dataFixture) . T.pack) args)
+        result <- try ((Cli.codexCliProvider cfg ^. #complete) modelFixture history emptyOptions) :: IO (Either BaikaiError Response)
+        case result of
+          Left e -> e ^. #requestProblem @?= Just (UnsupportedFeature "messages[0].content[0]")
+          Right r -> assertFailure (show r)
+    ]
diff --git a/test/LifecycleSpec.hs b/test/LifecycleSpec.hs
--- a/test/LifecycleSpec.hs
+++ b/test/LifecycleSpec.hs
@@ -119,11 +119,14 @@
                 (streamWith backend (countingDriver backend reads' closedRef (Just gate)) (modelFor backend) emptyContext testOptions)
             )
         putMVar outcome (r :: Either SomeException [AssistantMessageEvent])
-    threadDelay 100000
+    -- Wait for the driver to enter its bracket and reach the blocked read.
+    -- A startup delay can cancel before a response exists under build load.
+    ready <- pollFor 200 10000 ((>= 4) <$> readIORef reads')
     throwTo tid ThreadKilled
+    r <- takeMVar outcome
+    assertBool "consumer reached the blocked response read before cancellation" ready
     released <- pollFor 100 10000 (readIORef closedRef)
     assertBool "cancellation releases the connection without a GC" released
-    r <- takeMVar outcome
     case r of
       Left e | Just ThreadKilled <- fromException e -> pure ()
       other -> assertFailure ("expected the drained thread to die by ThreadKilled, got: " <> show (fmap length other))
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -41,6 +41,7 @@
 import Data.Generics.Labels ()
 import Data.Text qualified as Text
 import Data.Vector qualified as Vector
+import DataInputSpec qualified
 import ErrorClassSpec qualified
 import EvidenceSpec qualified
 import LifecycleSpec qualified
@@ -71,7 +72,8 @@
   defaultMain $
     testGroup
       "Baikai.Provider.OpenAI"
-      [ commandRenderingTest,
+      [ DataInputSpec.tests,
+        commandRenderingTest,
         effortRenderingTests,
         safetyRefusalTest,
         refusesRejectedApprovalPoliciesTest,
