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-trace-otel.cabal b/baikai-trace-otel.cabal
--- a/baikai-trace-otel.cabal
+++ b/baikai-trace-otel.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.4
 name: baikai-trace-otel
-version: 0.4.0.2
+version: 0.4.1.0
 synopsis: OpenTelemetry TraceSink for baikai.
 description:
   Provides an opt-in OpenTelemetry adapter for the baikai 'TraceSink'
@@ -60,7 +60,7 @@
   exposed-modules: Baikai.Trace.Sink.OpenTelemetry
   build-depends:
     aeson ^>=2.2,
-    baikai ^>=0.8.0,
+    baikai ^>=0.9.0,
     base >=4.20 && <5,
     containers ^>=0.7,
     hs-opentelemetry-api >=1.0 && <1.1,
@@ -83,7 +83,7 @@
 
   build-depends:
     aeson ^>=2.2,
-    baikai ^>=0.8.0,
+    baikai ^>=0.9.0,
     baikai-trace-otel,
     base,
     generic-lens,
diff --git a/src/Baikai/Trace/Sink/OpenTelemetry.hs b/src/Baikai/Trace/Sink/OpenTelemetry.hs
--- a/src/Baikai/Trace/Sink/OpenTelemetry.hs
+++ b/src/Baikai/Trace/Sink/OpenTelemetry.hs
@@ -146,7 +146,7 @@
   -- evidence, and, because 'Otel.addAttributes' replaces an existing key
   -- and evidence is pushed before the terminal, overwrote the genuinely
   -- observed value on every call that did.
-  CallFinished {eventId, timestamp, latencyMs, inputTokens, outputTokens, usd, costBasis, usageAvailability} ->
+  CallFinished {eventId, timestamp, latencyMs, inputTokens, outputTokens, usd, costBasis, usageAvailability, structuredData} ->
     case Map.lookup eventId m of
       -- A terminal without a live span is unreachable for normal withTraceStream
       -- usage because each traced call drives a fresh fold. Keep the silent drop so
@@ -155,19 +155,20 @@
       Just sp -> do
         let attrs :: HashMap.HashMap Text Attr.Attribute
             attrs =
-              maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_inputTokens (fromIntegral n :: Int64)) inputTokens $
-                maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_outputTokens (fromIntegral n :: Int64)) outputTokens $
-                  maybe id (\b -> HashMap.insert "baikai.cost.basis" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON b))))) costBasis $
-                    maybe id (\a -> HashMap.insert "baikai.usage.availability" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON a))))) usageAvailability $
-                      maybe id (\s -> HashMap.insert "baikai.cost.usd" (Attr.toAttribute (Scientific.toRealFloat s :: Double))) usd $
-                        HashMap.fromList
-                          [ ("baikai.latency_ms", Attr.toAttribute latencyMs)
-                          ]
+              maybe id (\ds -> HashMap.insert "baikai.response.structured_data" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON ds))))) structuredData $
+                maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_inputTokens (fromIntegral n :: Int64)) inputTokens $
+                  maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_outputTokens (fromIntegral n :: Int64)) outputTokens $
+                    maybe id (\b -> HashMap.insert "baikai.cost.basis" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON b))))) costBasis $
+                      maybe id (\a -> HashMap.insert "baikai.usage.availability" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON a))))) usageAvailability $
+                        maybe id (\s -> HashMap.insert "baikai.cost.usd" (Attr.toAttribute (Scientific.toRealFloat s :: Double))) usd $
+                          HashMap.fromList
+                            [ ("baikai.latency_ms", Attr.toAttribute latencyMs)
+                            ]
         Otel.addAttributes sp attrs
         Otel.setStatus sp Otel.Ok
         Otel.endSpan sp (Just (utcToTimestamp timestamp))
         pure (Map.delete eventId m)
-  CallFailed {eventId, timestamp, latencyMs, errorMessage, inputTokens, outputTokens, usd, costBasis, usageAvailability} ->
+  CallFailed {eventId, timestamp, latencyMs, errorMessage, inputTokens, outputTokens, usd, costBasis, usageAvailability, structuredData} ->
     case Map.lookup eventId m of
       -- A terminal without a live span is unreachable for normal withTraceStream
       -- usage because each traced call drives a fresh fold. Keep the silent drop so
@@ -175,15 +176,16 @@
       Nothing -> pure m
       Just sp -> do
         Otel.addAttributes sp $
-          maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_inputTokens (fromIntegral n :: Int64)) inputTokens $
-            maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_outputTokens (fromIntegral n :: Int64)) outputTokens $
-              maybe id (\s -> HashMap.insert "baikai.cost.usd" (Attr.toAttribute (Scientific.toRealFloat s :: Double))) usd $
-                maybe id (\b -> HashMap.insert "baikai.cost.basis" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON b))))) costBasis $
-                  maybe id (\a -> HashMap.insert "baikai.usage.availability" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON a))))) usageAvailability $
-                    HashMap.fromList
-                      [ ("baikai.latency_ms", Attr.toAttribute latencyMs),
-                        ("baikai.error", Attr.toAttribute errorMessage)
-                      ]
+          maybe id (\ds -> HashMap.insert "baikai.response.structured_data" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON ds))))) structuredData $
+            maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_inputTokens (fromIntegral n :: Int64)) inputTokens $
+              maybe id (\n -> AttrMap.insertByKey SC.genAi_usage_outputTokens (fromIntegral n :: Int64)) outputTokens $
+                maybe id (\s -> HashMap.insert "baikai.cost.usd" (Attr.toAttribute (Scientific.toRealFloat s :: Double))) usd $
+                  maybe id (\b -> HashMap.insert "baikai.cost.basis" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON b))))) costBasis $
+                    maybe id (\a -> HashMap.insert "baikai.usage.availability" (Attr.toAttribute (Text.decodeUtf8 (Ev.canonicalEncode (Aeson.toJSON a))))) usageAvailability $
+                      HashMap.fromList
+                        [ ("baikai.latency_ms", Attr.toAttribute latencyMs),
+                          ("baikai.error", Attr.toAttribute errorMessage)
+                        ]
         Otel.setStatus sp (Otel.Error errorMessage)
         Otel.endSpan sp (Just (utcToTimestamp timestamp))
         pure (Map.delete eventId m)
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -3,7 +3,7 @@
 module Main (main) where
 
 import Baikai.Api (Api (..))
-import Baikai.Content (AssistantContent (..), TextContent (..))
+import Baikai.Content (AssistantContent (..), DataContent (..), TextContent (..))
 import Baikai.Context (Context (..), emptyContext)
 import Baikai.Cost qualified as Cost
 import Baikai.Error (BaikaiError, providerError)
@@ -37,6 +37,7 @@
 import Control.Concurrent (threadDelay)
 import Control.Exception (throwIO)
 import Control.Lens ((&), (.~), (^.))
+import Control.Monad (forM_)
 import Data.Aeson qualified as Aeson
 import Data.Generics.Labels ()
 import Data.HashMap.Strict qualified as HashMap
@@ -62,7 +63,8 @@
   defaultMain $
     testGroup
       "baikai-trace-otel"
-      [ successSpanTest,
+      [ structuredDataSpanTest,
+        successSpanTest,
         observedModelSpanTest,
         failureSpanTest,
         partialBillingSpanTest,
@@ -550,3 +552,22 @@
           "gen_ai.response.model must be absent when observedModel is Unobserved"
           (not (HashMap.member "gen_ai.response.model" attrs))
       _ -> assertFailure "expected exactly one span"
+
+structuredDataSpanTest :: TestTree
+structuredDataSpanTest = testCase "structured data reaches the actual OTel exporter without evidence opt-in" $
+  forM_ [False, True] $ \failed -> do
+    let a = Custom (if failed then "baikai-otel-failed-data" else "baikai-otel-data")
+        dat = DataContent (Aeson.object ["urgent" Aeson..= True]) Nothing Nothing (Just "provider_classification")
+        response = stubResponse a & #message . #content .~ V.singleton (AssistantData dat)
+        handler _ _ _ = pure (if failed then response & #message . #stopReason .~ ErrorReason & #errorInfo .~ Just (providerError "failed") else response)
+    registerApiProvider (apiProviderWith a (liftCompleteToStream handler) handler)
+    (tracer, getSpans) <- newTracerWithInMemory
+    _ <- withTrace (otelSink tracer) (stubModel a) stubContext stubOptions
+    spans <- getSpans
+    case spans of
+      [sp] -> do
+        hot <- spanHotSnapshot sp
+        let attrs = Attr.getAttributeMap (Otel.hotAttributes hot)
+        HashMap.lookup "baikai.response.structured_data" attrs
+          @?= Just (Attr.toAttribute (TextEncoding.decodeUtf8 (canonicalEncode (Aeson.toJSON (V.singleton dat)))))
+      _ -> assertFailure ("expected one span, got " <> show (length spans))
