diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # Changelog for agentic-openai
 
+## 0.2.0.4 - 2026-10-06
+
+* Follows agentic's record field changes; no API change.
+
 ## 0.2.0.3 - 2026-10-06
 
 * Follows agentic's renames: `endpoint` is `Text`.
diff --git a/agentic-openai.cabal b/agentic-openai.cabal
--- a/agentic-openai.cabal
+++ b/agentic-openai.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               agentic-openai
-version:            0.2.0.3
+version:            0.2.0.4
 synopsis:           OpenAI as the System Two provider for agentic
 description:
   Runs agentic's draft steps on OpenAI models through the Responses API, with strict structured outputs and tools, and can stand in as System One.
diff --git a/src/Agentic/OpenAI.hs b/src/Agentic/OpenAI.hs
--- a/src/Agentic/OpenAI.hs
+++ b/src/Agentic/OpenAI.hs
@@ -141,15 +141,15 @@
   A.Object $
     [("model", A.String cfg.model)]
       <> maybe [] (\s -> [("instructions", A.String s)]) cfg.system
-      <> [("tools", A.Array (map tool (tools c))) | not (null (tools c))]
-      <> [ ("input", A.Array (task : concatMap exchange (history c)))
+      <> [("tools", A.Array (map tool c.tools)) | not (null c.tools)]
+      <> [ ("input", A.Array (task : concatMap exchange c.history))
          , ( "text"
            , A.Object
                [ ( "format"
                  , A.Object
                      [ ("type", A.String "json_schema")
-                     , ("name", A.String (schemaName (outputSchema c)))
-                     , ("schema", objectSchema (outputSchema c))
+                     , ("name", A.String (schemaName c.outputSchema))
+                     , ("schema", objectSchema c.outputSchema)
                      , ("strict", A.Bool True)
                      ]
                  )
@@ -161,16 +161,16 @@
       <> maybe [] (\e -> [("reasoning", A.Object [("effort", A.String (effortName e))])]) cfg.effort
       <> maybe [] (\n -> [("max_output_tokens", A.Integer (toInteger n))]) cfg.maxTokens
   where
-    task = message "user" (instructionText (instruction c) <> inputText)
-    inputText = case input c of
+    task = message "user" (c.instruction.text <> inputText)
+    inputText = case c.input of
       A.Null -> ""
       s -> "\n\nInput:\n" <> A.renderJson s
     tool spec =
       A.Object
         [ ("type", A.String "function")
-        , ("name", A.String (specName spec))
-        , ("description", A.String (specDescription spec))
-        , ("parameters", objectSchema (specInput spec))
+        , ("name", A.String spec.name)
+        , ("description", A.String spec.description)
+        , ("parameters", objectSchema spec.input)
         , ("strict", A.Bool True)
         ]
     -- A turn's raw value is its list of output items, which go back as input.
@@ -235,13 +235,13 @@
     -- A reply that isn't JSON goes back to the core as text; the output
     -- contract then rejects it and the model gets another go.
     final text = case decodeJson text of
-      Just v -> unwrap (outputSchema c) v
+      Just v -> unwrap c.outputSchema v
       Nothing -> A.String text
     json t = maybe (A.String t) id (decodeJson t)
     decodeJson t = fromAeson <$> J.decode (TL.encodeUtf8 (TL.fromStrict t))
     unwrapInput name v = maybe v (`unwrap` v) (inputSchema name)
     inputSchema :: Text -> Maybe Schema
-    inputSchema name = case [specInput s | s <- tools c, specName s == name] of
+    inputSchema name = case [s.input | s <- c.tools, s.name == name] of
       s : _ -> Just s
       [] -> Nothing
 
diff --git a/test-live/Live.hs b/test-live/Live.hs
--- a/test-live/Live.hs
+++ b/test-live/Live.hs
@@ -15,9 +15,9 @@
   deriving (Generic, Show, Contract)
 
 data BetterJoke
-  = DadJoke {setup' :: Text, punchline' :: Text}
+  = DadJoke {setup :: Text, punchline :: Text}
   | OneLiner {line :: Text}
-  | KnockKnock {whosThere :: Text, punchline' :: Text}
+  | KnockKnock {whosThere :: Text, punchline :: Text}
   deriving (Generic, Show, Contract)
 
 data Groan = Mild | Solid | Unbearable
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -29,8 +29,8 @@
     , instruction = "Suggest 3 dinosaurs"
     , input = Null
     , inputSchema = schemaOf SNull
-    , tools = [ToolSpec "search" "Search the fossil database" (codecSchema (contract @Text))]
-    , outputSchema = codecSchema (contract @[Text])
+    , tools = [ToolSpec "search" "Search the fossil database" ((contract @Text).schema)]
+    , outputSchema = (contract @[Text]).schema
     , history = []
     }
 
@@ -57,7 +57,7 @@
         )
 
   it "names the schema after its type, and shares repeated types through $defs" $ do
-    let format = at "format" (at "text" (body conversation {outputSchema = codecSchema (contract @Board)}))
+    let format = at "format" (at "text" (body conversation {outputSchema = (contract @Board).schema}))
         schema = at "schema" format
     at "name" format `shouldBe` J.String "Board"
     at "top" (at "properties" schema) `shouldBe` fromJust (J.decode "{\"$ref\":\"#/$defs/Row\"}")
@@ -90,11 +90,11 @@
         )
 
   it "reads function calls, parsing and unwrapping their arguments" $
-    fmap action (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"reasoning\"},{\"type\":\"function_call\",\"call_id\":\"c1\",\"name\":\"search\",\"arguments\":\"{\\\"value\\\":\\\"rex\\\"}\"}]}")))
+    fmap (.action) (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"reasoning\"},{\"type\":\"function_call\",\"call_id\":\"c1\",\"name\":\"search\",\"arguments\":\"{\\\"value\\\":\\\"rex\\\"}\"}]}")))
       `shouldBe` Right (CallTools [ToolCall "c1" "search" (String "rex")])
 
   it "reads a final answer from the message text" $
-    fmap action (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}]}")))
+    fmap (.action) (decoded (fromJust (J.decode "{\"status\":\"completed\",\"output\":[{\"type\":\"message\",\"content\":[{\"type\":\"output_text\",\"text\":\"{\\\"value\\\":[\\\"Stegosaurus\\\"]}\"}]}]}")))
       `shouldBe` Right (Respond (Array [String "Stegosaurus"]))
 
   it "reports refusals and truncation as errors" $ do
