diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,20 @@
 # Changelog for agentic
 
+## 0.2.0.3 - 2026-10-06
+
+* `fromBasisPoints` is renamed `toProbability`, the inverse of `probability`:
+  it takes a probability such as 0.9. `fromBasisPoints` now takes basis points,
+  the inverse of `basisPoints`.
+* A step's input is called its input everywhere, not its state: the
+  `Conversation` fields are `input`, `inputSchema` and `outputSchema`,
+  `JudgeRequest`'s is `requestInput`, and `JudgeInfo`'s is `judgeInput`.
+* Attaching a description is `documented…` throughout: `documentSchema` is
+  `documentedSchema`, and `described` is `documentedOptions`.
+* `Agentic.Scripted.alwaysYes` is `fixedAnswers`; it gives yes/no questions
+  whatever probability it's given.
+* `Agentic.Describe.toValue` is `descriptionValue`.
+* `endpoint` takes `Text`, like the other settings.
+
 ## 0.2.0.2 - 2026-10-01
 
 * The core now builds and runs under MicroHs as well as GHC. Generic
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -147,10 +147,10 @@
 like `between 1 10`, are checked locally, and a failed one goes back to the
 model to try again.
 
-This is the core design assumption: the types ARE the prompt. The state's types
+This is the core design assumption: the types ARE the prompt. The input's types
 are part of what the model reads, and field names carry meaning. A meeting note
 wrapped in a record with `setup` and `punchline` fields looks like a joke before
-the model reads a word. So give each step the state it should judge, and no more.
+the model reads a word. So give each step the input it should judge, and no more.
 
 And describe an enumeration once - Claude and Jev both see the same wording (see
 `Options` below).
@@ -183,7 +183,7 @@
 data Groan = Mild | Solid | Unbearable deriving (Generic, Show)
 
 instance Options Groan where
-  options = described "How much the audience groans"
+  options = documentedOptions "How much the audience groans"
     [ option Mild       "A polite smile; most people didn't notice"
     , option Solid      "An audible groan from most of the room"
     , option Unbearable "People get up and leave" ]
@@ -355,9 +355,9 @@
   n9 --> output
 ```
 
-`describe` returns a plain `Description` you can walk yourself, and `toValue`
-turns it into JSON for UIs and other agents. The tree hides unnamed glue between
-steps, but never a branch.
+`describe` returns a plain `Description` you can walk yourself, and
+`descriptionValue` turns it into JSON for UIs and other agents. The tree hides
+unnamed glue between steps, but never a branch.
 
 ### Naming things
 
@@ -468,7 +468,7 @@
 
 The library never tells the model how to format its reply - the providers'
 strict structured outputs take care of that. What the model gets is meaning: the
-instruction, the state, and your contracts' descriptions.
+instruction, the input, and your contracts' descriptions.
 
 And there are no sessions to manage. Anything a later step needs goes through
 the types. Memory across runs is yours to own - put it in the flow's types, or
@@ -502,7 +502,7 @@
 testRuntime :: IO (Runtime IO)
 testRuntime = do
   two <- scripted [respond joke]
-  pure runtime { systemOne = alwaysYes 0.95, systemTwo = two }
+  pure runtime { systemOne = fixedAnswers 0.95, systemTwo = two }
 ```
 
 ## History
diff --git a/agentic.cabal b/agentic.cabal
--- a/agentic.cabal
+++ b/agentic.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               agentic
-version:            0.2.0.2
+version:            0.2.0.3
 synopsis:           Composable, inspectable agentic workflows mixing LLMs and Jev
 description:
   Typed agentic workflows built from Arrow combinators. A flow is a description: you can describe it as a tree, Mermaid or Graphviz before running anything, then interpret it against System One (Jev) and System Two (an LLM) providers. This is the core: flows, contracts, questions, the runtime and the interpreter. It depends only on base and text.
diff --git a/src/Agentic/Contract.hs b/src/Agentic/Contract.hs
--- a/src/Agentic/Contract.hs
+++ b/src/Agentic/Contract.hs
@@ -43,7 +43,7 @@
   , OptionSet (..)
   , Option (..)
   , option
-  , described
+  , documentedOptions
   , Enumeration (..)
   , enumeration
 #ifndef __MHS__
@@ -192,7 +192,7 @@
           | otherwise -> Left ("missing field " <> name)
     )
   where
-    schema = maybe id documentSchema d (codecSchema c)
+    schema = maybe id documentedSchema d (codecSchema c)
     nullable = case shape (codecSchema c) of
       SNullable _ -> True
       _ -> False
@@ -206,7 +206,7 @@
 record :: Text -> ObjectCodec a a -> Codec a
 record d o =
   Codec
-    (documentSchema' (nonEmpty d) (schemaOf (SObject (objectFields o))))
+    (documentedSchema' (nonEmpty d) (schemaOf (SObject (objectFields o))))
     (Object . objectEncode o)
     ( \case
         Object kvs -> objectDecode o kvs
@@ -246,7 +246,7 @@
 sumCodec d cases
   | all (null . caseFields) cases =
       Codec
-        (documentSchema' d (schemaOf (SEnum [(caseTag c, caseDoc c) | c <- cases])))
+        (documentedSchema' d (schemaOf (SEnum [(caseTag c, caseDoc c) | c <- cases])))
         (\a -> maybe Null (String . caseTag) (matching a))
         ( \case
             String t | Just c <- byTag t -> caseDecode c []
@@ -254,7 +254,7 @@
         )
   | otherwise =
       Codec
-        (documentSchema' d (schemaOf (SSum [Variant (caseTag c) (caseDoc c) (caseFields c) | c <- cases])))
+        (documentedSchema' d (schemaOf (SSum [Variant (caseTag c) (caseDoc c) (caseFields c) | c <- cases])))
         ( \a -> case [(caseTag c, kvs) | c <- cases, Just kvs <- [caseEncode c a]] of
             (t, kvs) : _ -> Object (("tag", String t) : kvs)
             [] -> Null
@@ -274,7 +274,7 @@
 
 -- | Describe the whole type.
 documented :: Text -> Codec a -> Codec a
-documented d c = c {codecSchema = documentSchema d (codecSchema c)}
+documented d c = c {codecSchema = documentedSchema d (codecSchema c)}
 
 -- | Describe one field of a record (or of any constructor of a sum). Naming a
 -- field that doesn't exist is an error when the schema is first used.
@@ -289,7 +289,7 @@
       _ -> error ("Agentic.Contract.field: no field named " <> T.unpack name)
     named f = fieldName f == name
     describeField f
-      | named f = f {fieldSchema = documentSchema d (fieldSchema f)}
+      | named f = f {fieldSchema = documentedSchema d (fieldSchema f)}
       | otherwise = f
 
 -- | A constraint the wire schemas can't express. It's stated to the model and
@@ -310,8 +310,8 @@
     ("between " <> T.pack (show lo) <> " and " <> T.pack (show hi))
     (\a -> a >= lo && a <= hi)
 
-documentSchema' :: Maybe Text -> Schema -> Schema
-documentSchema' = maybe id documentSchema
+documentedSchema' :: Maybe Text -> Schema -> Schema
+documentedSchema' = maybe id documentedSchema
 
 nonEmpty :: Text -> Maybe Text
 nonEmpty t = if T.null t then Nothing else Just t
@@ -434,8 +434,9 @@
 option :: Show a => a -> Text -> Option a
 option v d = Option v (label v) (nonEmpty d)
 
-described :: Text -> [Option a] -> OptionSet a
-described d = OptionSet (nonEmpty d)
+-- | Options with a description of the whole set.
+documentedOptions :: Text -> [Option a] -> OptionSet a
+documentedOptions d = OptionSet (nonEmpty d)
 
 -- | An option's label: what the model sees and answers with. For an
 -- enumeration, 'show' gives the constructor's name.
@@ -454,7 +455,7 @@
 enumeration :: forall a. (Options a, Eq a) => Codec a
 enumeration =
   Codec
-    (documentSchema' (optionsDoc set) (schemaOf (SEnum [(optionLabel o, optionDoc o) | o <- opts])))
+    (documentedSchema' (optionsDoc set) (schemaOf (SEnum [(optionLabel o, optionDoc o) | o <- opts])))
     (\a -> maybe Null (String . optionLabel) (find ((== a) . optionValue) opts))
     ( \case
         String t | Just o <- find ((== t) . optionLabel) opts -> Right (optionValue o)
diff --git a/src/Agentic/Describe.hs b/src/Agentic/Describe.hs
--- a/src/Agentic/Describe.hs
+++ b/src/Agentic/Describe.hs
@@ -13,14 +13,14 @@
   , NodeKind (..)
   , Edge (..)
   , EdgeStyle (..)
-  , toValue
+  , descriptionValue
   ) where
 
 import Agentic.Contract (Codec (..))
 import Agentic.Core
 import Agentic.Questions (QuestionSpec (..), Questions (..))
 import Agentic.Schema (Schema, typeLabel)
-import Agentic.Value (Value (..))
+import Agentic.Value (Value (..), renderJson)
 import Data.List (mapAccumL)
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -53,7 +53,7 @@
       , draftTools :: [ToolInfo]
       }
   | JudgeInfo
-      { judgeState :: Schema
+      { judgeInput :: Schema
       , judgeQuestions :: [QuestionSpec]
       }
 
@@ -413,8 +413,9 @@
       Flow -> []
       Uses -> ["style=dotted", "arrowhead=none"]
       Again -> ["style=dashed"]
-    str t = "\"" <> inner t <> "\""
-    inner = concatMapText (\case '"' -> "\\\""; '\\' -> "\\\\"; c -> T.singleton c)
+    -- JSON's string escapes are also DOT's.
+    str = renderJson . String
+    inner = T.pack . init . drop 1 . T.unpack . str
 
 -- | Is the node with this id inside the box with that id?
 inBox :: Text -> Text -> [Item] -> Bool
@@ -449,20 +450,20 @@
 -- JSON
 
 -- | The description as a JSON-shaped value, for UIs and other agents.
-toValue :: Description -> Value
-toValue = \case
+descriptionValue :: Description -> Value
+descriptionValue = \case
   Leaf info -> leaf info
-  Sequence ds -> node "sequence" [("steps", Array (map toValue ds))]
-  Together ds -> node "together" [("steps", Array (map toValue ds))]
-  Halves l r -> node "halves" [("first", toValue l), ("second", toValue r)]
-  Branch l r -> node "branch" [("left", toValue l), ("right", toValue r)]
-  ForEach d -> node "each" [("step", toValue d)]
-  Repeated d -> node "repeat" [("step", toValue d)]
+  Sequence ds -> node "sequence" [("steps", Array (map descriptionValue ds))]
+  Together ds -> node "together" [("steps", Array (map descriptionValue ds))]
+  Halves l r -> node "halves" [("first", descriptionValue l), ("second", descriptionValue r)]
+  Branch l r -> node "branch" [("left", descriptionValue l), ("right", descriptionValue r)]
+  ForEach d -> node "each" [("step", descriptionValue d)]
+  Repeated d -> node "repeat" [("step", descriptionValue d)]
   Annotated n d ->
     node "note" $
       [("name", String (noteName n))]
         <> maybe [] (\t -> [("description", String t)]) (noteDescription n)
-        <> [("step", toValue d)]
+        <> [("step", descriptionValue d)]
   where
     node kind fields = Object (("kind", String kind) : fields)
     leaf = \case
@@ -478,7 +479,7 @@
           , ("tools", Array (map tool tools))
           ]
       JudgeInfo input qs ->
-        node "judge" [("state", String (typeLabel input)), ("questions", Array (map question qs))]
+        node "judge" [("input", String (typeLabel input)), ("questions", Array (map question qs))]
     tool t =
       Object
         [ ("name", String (infoName t))
@@ -490,10 +491,6 @@
       AskYesNo q -> Object [("type", String "yesNo"), ("question", String q)]
       AskChoice q opts -> Object [("type", String "choice"), ("question", String q), ("options", Array [String l | (l, _) <- opts])]
       AskScore q levels -> Object [("type", String "score"), ("question", String q), ("levels", Array [String l | (l, _) <- levels])]
-
--- | 'T.concatMap', which MicroHs's "Data.Text" doesn't provide.
-concatMapText :: (Char -> Text) -> Text -> Text
-concatMapText f = T.concat . map f . T.unpack
 
 -- | Apply a function to a pair's second half. (MicroHs has no Functor instance
 -- for pairs.)
diff --git a/src/Agentic/Interpret.hs b/src/Agentic/Interpret.hs
--- a/src/Agentic/Interpret.hs
+++ b/src/Agentic/Interpret.hs
@@ -52,15 +52,15 @@
             answers <- askSystemOne (systemOne rt) request
             emit path (Judged request answers)
             answered (decodeAnswers qs answers)
-      Draft input out instruction tools -> do
+      Draft inCodec out instruction tools -> do
         let conversation =
               Conversation
                 { path = path
                 , instruction = instruction
-                , state = encode input x
-                , stateSchema = codecSchema input
+                , input = encode inCodec x
+                , inputSchema = codecSchema inCodec
                 , tools = map toolSpec tools
-                , output = codecSchema out
+                , outputSchema = codecSchema out
                 , history = []
                 }
         emit path (Drafting conversation)
@@ -83,15 +83,13 @@
     runTool :: [Note] -> [Tool m] -> ToolCall -> m ToolResult
     runTool path tools call = do
       emit path (ToolCalled call)
-      result <- case find ((== callName call) . nameOf) tools of
+      result <- case find ((== callName call) . toolName) tools of
         Nothing -> pure (ToolFailed ("there is no tool named " <> callName call))
         Just (Tool name _ input out body) -> case decode input (callInput call) of
           Left problem -> pure (ToolFailed ("invalid input: " <> problem))
           Right i -> ToolOk . encode out <$> go (path <> [Note name Nothing]) body i
       emit path (ToolReturned (callId call) result)
       pure result
-      where
-        nameOf (Tool name _ _ _ _) = name
 
 toolSpec :: Tool m -> ToolSpec
 toolSpec (Tool name description input _ _) = ToolSpec name description (codecSchema input)
diff --git a/src/Agentic/Questions.hs b/src/Agentic/Questions.hs
--- a/src/Agentic/Questions.hs
+++ b/src/Agentic/Questions.hs
@@ -1,7 +1,7 @@
 {-# LANGUAGE AllowAmbiguousTypes #-}
 
--- | Questions for a System One model such as Jev. Following Jev's terms, a step
--- asks t'Questions' about its input, the /state/.
+-- | Questions for a System One model such as Jev: a step asks t'Questions'
+-- about its input.
 module Agentic.Questions
   ( -- * Questions
     Questions (..)
@@ -11,8 +11,9 @@
     -- * Answers
   , Probability
   , probability
-  , fromBasisPoints
+  , toProbability
   , basisPoints
+  , fromBasisPoints
   , YesNo (..)
   , Choice (..)
   , Score (..)
@@ -59,12 +60,15 @@
 probability (Probability bp) = fromIntegral bp / 10000
 
 -- | Convert a provider's probability, rounding once (half to even).
-fromBasisPoints :: Double -> Probability
-fromBasisPoints d = clamp (round (d * 10000))
+toProbability :: Double -> Probability
+toProbability d = clamp (round (d * 10000))
 
 basisPoints :: Probability -> Int
 basisPoints (Probability bp) = bp
 
+fromBasisPoints :: Int -> Probability
+fromBasisPoints = clamp
+
 -- ---------------------------------------------------------------------------
 -- Answers
 
@@ -90,7 +94,7 @@
 -- Answers have contracts, so a judgement can be a tool's output.
 
 instance Contract Probability where
-  contract = mapCodec fromBasisPoints probability (contract @Double)
+  contract = mapCodec toProbability probability (contract @Double)
 
 instance Contract YesNo where
   contract = record "A yes/no judgement" (YesNo <$> required "yes" "The probability that the answer is yes" yes)
@@ -130,9 +134,9 @@
     -- ^ The position, each level's probability (by index), and the confidence.
   deriving (Eq, Show)
 
--- | What a System One provider receives: the encoded state and the questions.
+-- | What a System One provider receives: the encoded input and the questions.
 data JudgeRequest = JudgeRequest
-  { requestState :: Value
+  { requestInput :: Value
   , requestQuestions :: [QuestionSpec]
   }
   deriving (Eq, Ord, Show)
@@ -140,7 +144,7 @@
 -- ---------------------------------------------------------------------------
 -- Questions
 
--- | One or more questions about the same state, sent as one request. Combine
+-- | One or more questions about the same input, sent as one request. Combine
 -- them applicatively:
 --
 -- > judge (Review <$> funny <*> groan)
@@ -181,7 +185,7 @@
   where
     opts = optionList (options @a)
 
--- | Place the state on an 'Options' type's levels, lowest first.
+-- | Place the input on an 'Options' type's levels, lowest first.
 score :: forall a. Options a => Text -> Questions (Score a)
 score q = single (AskScore q (labels opts)) $ \case
   ScoreAnswer pos ps conf ->
diff --git a/src/Agentic/Runtime.hs b/src/Agentic/Runtime.hs
--- a/src/Agentic/Runtime.hs
+++ b/src/Agentic/Runtime.hs
@@ -43,11 +43,11 @@
   { path :: [Note]
     -- ^ Where this step is in the flow.
   , instruction :: Instruction
-  , state :: Value
+  , input :: Value
     -- ^ The step's input, encoded by its contract.
-  , stateSchema :: Schema
+  , inputSchema :: Schema
   , tools :: [ToolSpec]
-  , output :: Schema
+  , outputSchema :: Schema
     -- ^ The schema of the step's result.
   , history :: [Exchange]
     -- ^ Earlier turns of this step, oldest first. Append-only.
diff --git a/src/Agentic/Schema.hs b/src/Agentic/Schema.hs
--- a/src/Agentic/Schema.hs
+++ b/src/Agentic/Schema.hs
@@ -7,7 +7,7 @@
   , Variant (..)
   , Format (..)
   , schemaOf
-  , documentSchema
+  , documentedSchema
   , typeLabel
   , titled
   ) where
@@ -60,8 +60,8 @@
 schemaOf :: Shape -> Schema
 schemaOf = Schema Nothing Nothing []
 
-documentSchema :: Text -> Schema -> Schema
-documentSchema d s = s {doc = Just d}
+documentedSchema :: Text -> Schema -> Schema
+documentedSchema d s = s {doc = Just d}
 
 -- | Name the schema's type, unless it already has a name.
 titled :: Text -> Schema -> Schema
diff --git a/src/Agentic/Scripted.hs b/src/Agentic/Scripted.hs
--- a/src/Agentic/Scripted.hs
+++ b/src/Agentic/Scripted.hs
@@ -7,7 +7,7 @@
   , callTools
     -- * System One
   , answering
-  , alwaysYes
+  , fixedAnswers
   ) where
 
 import Agentic.Contract (Codec (..), Contract (..))
@@ -44,8 +44,8 @@
 
 -- | Yes/no questions get probability @p@; choices and scores pick the first
 -- option with certainty.
-alwaysYes :: Applicative m => Probability -> SystemOne m
-alwaysYes p = answering $ \case
+fixedAnswers :: Applicative m => Probability -> SystemOne m
+fixedAnswers p = answering $ \case
   AskYesNo _ -> YesNoAnswer p
   AskChoice _ ((l, _) : _) -> ChoiceAnswer l [(l, 1)] 1
   AskChoice _ [] -> ChoiceAnswer "" [] 0
diff --git a/src/Agentic/Settings.hs b/src/Agentic/Settings.hs
--- a/src/Agentic/Settings.hs
+++ b/src/Agentic/Settings.hs
@@ -28,7 +28,7 @@
   key :: Text -> c -> c
 
 class HasEndpoint c where
-  endpoint :: String -> c -> c
+  endpoint :: Text -> c -> c
 
 -- | How long to wait for a response, in seconds.
 class HasTimeout c where
diff --git a/src/Agentic/ViaLLM.hs b/src/Agentic/ViaLLM.hs
--- a/src/Agentic/ViaLLM.hs
+++ b/src/Agentic/ViaLLM.hs
@@ -22,10 +22,10 @@
         Conversation
           { path = []
           , instruction = Instruction "Answer each question about the input. Give every probability as a number from 0 to 1."
-          , state = requestState request
-          , stateSchema = schemaOf SNull
+          , input = requestInput request
+          , inputSchema = schemaOf SNull
           , tools = []
-          , output = schemaOf (SObject [Field qid (questionSchema q) True | (qid, q) <- qs])
+          , outputSchema = schemaOf (SObject [Field qid (questionSchema q) True | (qid, q) <- qs])
           , history = []
           }
   turn <- askSystemTwo two conversation
@@ -40,12 +40,12 @@
 questionSchema :: QuestionSpec -> Schema
 questionSchema = \case
   AskYesNo q ->
-    documentSchema q (schemaOf (SObject [Field "probabilityYes" (documentSchema "The probability that the answer is yes" (schemaOf SNumber)) True]))
+    documentedSchema q (schemaOf (SObject [Field "probabilityYes" (documentedSchema "The probability that the answer is yes" (schemaOf SNumber)) True]))
   AskChoice q opts -> distribution (q <> " Give each option's probability; they should sum to 1.") opts
   AskScore q levels -> distribution (q <> " The options are ordered levels, lowest first. Give each level's probability; they should sum to 1.") levels
   where
     distribution q opts =
-      documentSchema q (schemaOf (SObject [Field l (documentSchema (maybe l id d) (schemaOf SNumber)) True | (l, d) <- opts]))
+      documentedSchema q (schemaOf (SObject [Field l (documentedSchema (maybe l id d) (schemaOf SNumber)) True | (l, d) <- opts]))
 
 answer :: QuestionSpec -> Value -> Either Text Answer
 answer spec v = case (spec, v) of
@@ -64,6 +64,6 @@
   where
     get k kvs = maybe (Left ("missing " <> k)) Right (lookupField k kvs)
     number = \case
-      Number d -> Right (fromBasisPoints d)
-      Integer n -> Right (fromBasisPoints (fromInteger n))
+      Number d -> Right (toProbability d)
+      Integer n -> Right (toProbability (fromInteger n))
       other -> Left ("expected a probability, got " <> renderJson other)
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -27,7 +27,7 @@
 
 instance Options Groan where
   options =
-    described
+    documentedOptions
       "How much the audience groans"
       [ option Mild "A polite smile"
       , option Solid "An audible groan"
@@ -45,8 +45,8 @@
 data Review = Review {funnyAnswer :: YesNo, groanAnswer :: Score Groan}
   deriving (Show, Eq)
 
-described' :: Codec Joke
-described' =
+documentedJoke :: Codec Joke
+documentedJoke =
   record "A joke, split into its parts" $
     Joke
       <$> required "genre" "The style of joke" genre
@@ -69,7 +69,7 @@
 testRuntime :: [Action] -> Probability -> IO (Runtime IO)
 testRuntime turns p = do
   two <- scripted turns
-  pure runtime {systemOne = alwaysYes p, systemTwo = two}
+  pure runtime {systemOne = fixedAnswers p, systemTwo = two}
 
 roundTrips :: (Eq a, Show a) => Codec a -> a -> Expectation
 roundTrips c a = decode c (encode c a) `shouldBe` Right a
@@ -93,7 +93,7 @@
       title (codecSchema (contract @Joke)) `shouldBe` Just "Joke"
 
     it "keeps descriptions written in the codec" $
-      case shape (codecSchema described') of
+      case shape (codecSchema documentedJoke) of
         SObject fs -> map (doc . fieldSchema) fs `shouldBe` map Just ["The style of joke", "The setup line", "The line that lands it"]
         other -> expectationFailure (show other)
 
