diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# v1.0.0.0
+
+## Breaking
+
+- Add the `resStatus` field to `Adapter`
+
 # v0.1.0.2
 
 Polish the docs.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -94,10 +94,9 @@
 - OIDs are a plain `Word32` and row/column/parameter indices are a plain
   `Int32`, instead of the C-specific newtypes of the original.
 - There's no `invalidOid` constant. It's just `0`.
-- Ambiguous, rarely-useful helpers (e.g. `resStatus`) are omitted, as is
-  `libpqVersion`.
-- `unescapeBytea` is a field of `Adapter` rather than a
-  connection-independent top-level function, since its implementation is
+- `libpqVersion` is omitted.
+- `unescapeBytea` and `resStatus` are fields of `Adapter` rather than
+  connection-independent top-level functions, since their implementations are
   adapter-specific.
 
 [libpq]: https://www.postgresql.org/docs/current/libpq.html
diff --git a/pqi.cabal b/pqi.cabal
--- a/pqi.cabal
+++ b/pqi.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: pqi
-version: 0.1.0.2
+version: 1.0.0.0
 category: Database, PostgreSQL
 synopsis: Driver-agnostic interface to the PostgreSQL libpq API
 description:
diff --git a/src/library/Pqi.hs b/src/library/Pqi.hs
--- a/src/library/Pqi.hs
+++ b/src/library/Pqi.hs
@@ -1,48 +1,36 @@
--- | A driver-agnostic reproduction of the [@postgresql-libpq@](https://hackage.haskell.org/package/postgresql-libpq) API (version
--- @0.11@, the pipelining-capable release).
+-- | A driver-agnostic reproduction of the [@postgresql-libpq@](https://hackage.haskell.org/package/postgresql-libpq) @0.11@ API
+-- (the pipelining-capable release).
 --
--- The connection is reified as a single concrete record type, 'Connection',
--- whose fields are the closures that implement each capability. Result
--- accessors live in the independent 'Result' record, and cancellation
--- handles in the 'Cancel' record. A 'Connection' produces 'Result's and
--- 'Cancel's directly (via its 'exec', 'getResult', 'getCancel', etc.
--- fields) — there is no type-level indirection: the whole package defines
--- exactly one 'Connection', one 'Result', and one 'Cancel' type.
+-- t'Connection', t'Result' and t'Cancel' are records of @IO@ closures, each
+-- already closed over the handle it needs (a C @PGconn@ pointer, a native
+-- socket, ...). Exactly one of each type exists in the package: no classes,
+-- no type parameters.
 --
--- Each field of these records is a closure that has already captured
--- whatever underlying handle (e.g. a C @PGconn@ pointer, or a native
--- socket) it needs; from the caller's perspective a 'Connection' is simply
--- a bundle of @IO@ actions. This trades the old class-based polymorphism
--- for a concrete, monomorphic value that can be passed around, stored, and
--- constructed by whichever adapter package is in use.
+-- Connections come from adapter packages — @pqi-ffi@ (C @libpq@ via
+-- @postgresql-libpq@) and @pqi-native@ (pure-Haskell wire protocol) — each
+-- exporting one top-level t'Adapter' value. An adapter must be byte-identical
+-- to @libpq@ on every protocol-derived value; @pqi-conformance@ enforces that
+-- differentially.
 --
--- Adapter packages (e.g. @pqi-ffi@, @pqi-native@) are responsible for
--- constructing 'Connection' values — this package does not provide any
--- @connectdb@\/@connectStart@\/@newNullConnection@-style constructors of
--- its own, since those don't have a connection to close over yet. Instead,
--- each adapter package exports a single top-level value of type 'Adapter',
--- bundling its connection-establishing functions together so that callers
--- who need to be adapter-agnostic (e.g. a differential test harness, or a
--- consumer that lets its own users pick an adapter) can hold onto one value
--- rather than a family of adapter-qualified functions.
+-- == Differences from @postgresql-libpq@
 --
--- Function names, argument order, and semantics mirror the API of the C library binding
--- @postgresql-libpq@.
--- The only deliberate departures are:
+-- Everything else — names, argument order, semantics — mirrors
+-- @Database.PostgreSQL.LibPQ@.
 --
--- * @Connection@, @Result@, and @Cancel@ are plain records of closures
---   rather than a class-parameterised type and its associated types.
+-- * Connection acquisition lives in t'Adapter': @connectdb@, @connectStart@
+--   and @newNullConnection@ are its fields, not top-level functions.
 --
--- * OIDs are a plain 'Word32' and row\/column\/parameter indices and LoFds are a
---   plain 'Int32', rather than the C-specific newtypes of the original.
+-- * @unescapeBytea@ and @resStatus@ are t'Adapter' fields too. They take no
+--   connection, but their implementations are adapter-specific.
 --
--- * Ambiguous, rarely-useful helpers (e.g. @resStatus@) are omitted,
---   @libpqVersion@ is omitted too.
+-- * t'Connection', t'Result' and t'Cancel' are records of closures rather than
+--   opaque handles fed to top-level functions. Call sites are unchanged:
+--   @exec connection sql@ selects a field and applies it.
 --
--- * There's no @invalidOid@ constant. It's just 0.
+-- * @Oid@ is 'Word32'; @Row@, @Column@, @LoFd@ are 'Int32'. No @invalidOid@
+--   constant — it is @0@.
 --
--- * @unescapeBytea@ is a field of 'Adapter' rather than a connection-independent
---   top-level function, since its implementation is adapter-specific.
+-- * @libpqVersion@ is omitted.
 module Pqi
   ( -- * Adapter
     Adapter (..),
@@ -115,17 +103,17 @@
     NonfatalError
   | -- | A fatal error occurred.
     FatalError
-  | -- | The @'Result'@ contains a single result tuple from the current command.
+  | -- | The t'Result' contains a single result tuple from the current command.
     -- This status occurs only when single-row mode has been selected for the
     -- query.
     SingleTuple
-  | -- | The @'Result'@ represents a synchronization point in pipeline mode,
-    -- requested by @'pipelineSync'@. This status occurs only in pipeline mode.
+  | -- | The t'Result' represents a synchronization point in pipeline mode,
+    -- requested by 'pipelineSync'. This status occurs only in pipeline mode.
     PipelineSync
-  | -- | The @'Result'@ represents a pipeline that has received an error from
-    -- the server. @'getResult'@ must be called repeatedly, and each time it will
+  | -- | The t'Result' represents a pipeline that has received an error from
+    -- the server. 'getResult' must be called repeatedly, and each time it will
     -- return this status code until the end of the current pipeline, at which
-    -- point it will return @'PipelineSync'@ and normal processing can resume.
+    -- point it will return 'PipelineSync' and normal processing can resume.
     PipelineAbort
   deriving stock (Eq, Ord, Show, Enum, Bounded)
 
@@ -239,25 +227,22 @@
 
 -- * Result inspection
 
--- | Result-accessor closures, independent of the connection that produced
--- the result. This allows row decoders and projection functions (such as
--- 'observeResult' in @pqi-conformance@) to operate on any result value
--- without knowing the originating connection or adapter.
+-- | Result-accessor closures, closed over an adapter's own result
+-- representation (e.g. a C @PGresult@ pointer).
 --
--- There is exactly one 'Result' type in the whole @pqi@ package; adapters
--- construct values of this type by closing each field over their own
--- underlying result representation (e.g. a C @PGresult@ pointer).
+-- Carries no reference to the connection that produced it, so decoders can
+-- consume a result without knowing its origin.
 data Result = Result
   { -- | The status of the result.
     resultStatus :: IO ExecStatus,
-    -- | The flat error message associated with the result, if any. Best-effort;
-    -- see the note on 'errorMessage'.
+    -- | The flat error message of the result, if any. Formatted locally by the
+    -- driver, so adapters are not expected to agree byte for byte; use
+    -- 'resultErrorField' where exactness matters.
     resultErrorMessage :: IO (Maybe ByteString),
     -- | A single structured field of the result's error report.
     resultErrorField :: FieldCode -> IO (Maybe ByteString),
-    -- | Free the result. Adapters that manage results with the garbage collector
-    -- may implement this as a no-op; for the C-backed adapter it frees the
-    -- underlying @PGresult@, after which the result must not be used.
+    -- | Free the result, after which it must not be used. A no-op in adapters
+    -- that leave results to the garbage collector.
     unsafeFreeResult :: IO (),
     -- | Number of rows (tuples) in the result.
     ntuples :: IO Int32,
@@ -281,14 +266,10 @@
     -- | Server-side storage size of the given column's type, or a negative value
     -- for variable size.
     fsize :: Int32 -> IO Int,
-    -- | Value at @(row, column)@, or @'Nothing'@ for SQL @NULL@. In the old
-    -- class-based API this delegated to @'getvalue''@ by default; since a
-    -- record has no notion of default methods, adapters that want that
-    -- behaviour should set this field to the same closure as 'getvalue''
-    -- when constructing the 'Result'.
+    -- | Value at @(row, column)@, or 'Nothing' for SQL @NULL@. May alias the
+    -- result's storage, which 'unsafeFreeResult' invalidates.
     getvalue :: Int32 -> Int32 -> IO (Maybe ByteString),
-    -- | Like 'getvalue', but always returns a copy that remains valid after the
-    -- result is freed.
+    -- | Like 'getvalue', but always a copy, valid after the result is freed.
     getvalue' :: Int32 -> Int32 -> IO (Maybe ByteString),
     -- | Whether the value at @(row, column)@ is SQL @NULL@.
     getisnull :: Int32 -> Int32 -> IO Bool,
@@ -307,11 +288,8 @@
 
 -- * Cancellation
 
--- | A cancellation handle, isolated from the connection that produced it.
---
--- There is exactly one 'Cancel' type in the whole @pqi@ package; adapters
--- construct values of this type by closing over their own underlying
--- cancellation handle.
+-- | A cancellation handle, isolated from the connection that produced it,
+-- hence usable from another thread while that connection is busy.
 data Cancel = Cancel
   { -- | Request cancellation of the in-progress command via the handle.
     cancel :: IO (Either ByteString ())
@@ -319,18 +297,13 @@
 
 -- * Connection
 
--- | The single flat capability record: closing, inspecting, querying,
--- escaping, async commands, pipelining, cancellation handle creation,
--- notifications, copy, large objects, and control.
---
--- There is exactly one 'Connection' type in the whole @pqi@ package.
--- Adapter packages (e.g. @pqi-ffi@, @pqi-native@) construct values of this
--- type from their own top-level @connectdb@\/@connectStart@ functions,
--- closing each field over their own underlying connection representation
--- (e.g. a C @PGconn@ pointer, or a native socket). This module only
--- defines the shape; it does not construct any connections.
+-- | One flat capability record: closing, inspecting, querying, escaping,
+-- async commands, pipelining, cancellation handles, notifications, copy,
+-- large objects, control.
 --
--- See the field-level documentation for the semantics of each capability.
+-- Produced by the 'connectdb'\/'connectStart'\/'newNullConnection' fields of
+-- an t'Adapter', which close each field over their own connection
+-- representation (a C @PGconn@ pointer, a native socket, ...).
 data Connection = Connection
   { -- | Drive an asynchronous connection attempt forward.
     connectPoll :: IO PollingStatus,
@@ -366,11 +339,8 @@
     protocolVersion :: IO Int,
     -- | The server version, as an integer of the form @MMmmpp@.
     serverVersion :: IO Int,
-    -- | The most recent error message, if any.
-    --
-    -- Note: unlike the structured fields available via @'resultErrorField'@, the
-    -- flat message text is formatted locally by the driver, so adapters are not
-    -- expected to produce byte-identical strings.
+    -- | The most recent error message, if any. Formatted locally by the driver,
+    -- so adapters are not expected to agree byte for byte.
     errorMessage :: IO (Maybe ByteString),
     -- | The file descriptor of the connection socket.
     socket :: IO (Maybe Fd),
@@ -384,18 +354,18 @@
     -- | Submit a command and wait for the result.
     exec :: ByteString -> IO (Maybe Result),
     -- | Submit a parameterized command. Each parameter is given as
-    -- @(type oid, value, format)@, or @'Nothing'@ for SQL @NULL@. The final
-    -- @'Format'@ selects the result format.
+    -- @(type oid, value, format)@, or 'Nothing' for SQL @NULL@. The final
+    -- 'Format' selects the result format.
     execParams ::
       ByteString ->
       [Maybe (Word32, ByteString, Format)] ->
       Format ->
       IO (Maybe Result),
     -- | Prepare a named statement. The OID list, when supplied, fixes parameter
-    -- types; @'Nothing'@ leaves them to be inferred.
+    -- types; 'Nothing' leaves them to be inferred.
     prepare :: ByteString -> ByteString -> Maybe [Word32] -> IO (Maybe Result),
     -- | Execute a previously prepared statement. Each parameter is
-    -- @(value, format)@, or @'Nothing'@ for SQL @NULL@.
+    -- @(value, format)@, or 'Nothing' for SQL @NULL@.
     execPrepared ::
       ByteString ->
       [Maybe (ByteString, Format)] ->
@@ -414,21 +384,21 @@
     escapeIdentifier :: ByteString -> IO (Maybe ByteString),
     -- | Submit a command without waiting for the result.
     sendQuery :: ByteString -> IO Bool,
-    -- | Asynchronous @'execParams'@.
+    -- | Asynchronous 'execParams'.
     sendQueryParams :: ByteString -> [Maybe (Word32, ByteString, Format)] -> Format -> IO Bool,
-    -- | Asynchronous @'prepare'@.
+    -- | Asynchronous 'prepare'.
     sendPrepare :: ByteString -> ByteString -> Maybe [Word32] -> IO Bool,
-    -- | Asynchronous @'execPrepared'@.
+    -- | Asynchronous 'execPrepared'.
     sendQueryPrepared :: ByteString -> [Maybe (ByteString, Format)] -> Format -> IO Bool,
-    -- | Asynchronous @'describePrepared'@.
+    -- | Asynchronous 'describePrepared'.
     sendDescribePrepared :: ByteString -> IO Bool,
-    -- | Asynchronous @'describePortal'@.
+    -- | Asynchronous 'describePortal'.
     sendDescribePortal :: ByteString -> IO Bool,
     -- | Collect the next result from an asynchronous command.
     getResult :: IO (Maybe Result),
     -- | Read input from the server into the driver's buffer.
     consumeInput :: IO Bool,
-    -- | Whether a command is busy (a @'getResult'@ would block).
+    -- | Whether a command is busy (a 'getResult' would block).
     isBusy :: IO Bool,
     -- | Set the non-blocking flag of the connection.
     setnonblocking :: Bool -> IO Bool,
@@ -452,17 +422,17 @@
     getCancel :: IO (Maybe Cancel),
     -- | Return the next notification from the queue, if any.
     notifies :: IO (Maybe Notify),
-    -- | Stop accumulating notices for retrieval via @'getNotice'@.
+    -- | Stop accumulating notices for retrieval via 'getNotice'.
     disableNoticeReporting :: IO (),
-    -- | Start accumulating notices for retrieval via @'getNotice'@.
+    -- | Start accumulating notices for retrieval via 'getNotice'.
     enableNoticeReporting :: IO (),
     -- | Retrieve the next accumulated notice, if any.
     getNotice :: IO (Maybe ByteString),
     -- | Send data on a @COPY FROM STDIN@ connection.
     putCopyData :: ByteString -> IO CopyInResult,
-    -- | Signal the end of @COPY FROM STDIN@; @'Just'@ aborts with the given error.
+    -- | Signal the end of @COPY FROM STDIN@; 'Just' aborts with the given error.
     putCopyEnd :: Maybe ByteString -> IO CopyInResult,
-    -- | Receive data on a @COPY TO STDOUT@ connection. The @'Bool'@ selects
+    -- | Receive data on a @COPY TO STDOUT@ connection. The 'Bool' selects
     -- non-blocking mode.
     getCopyData :: Bool -> IO CopyOutResult,
     -- | Create a new large object.
@@ -502,20 +472,14 @@
 -- * Adapter
 
 -- | An adapter package's connection-establishing functions, bundled into one
--- value.
+-- value. Each adapter package (e.g. @pqi-ffi@, @pqi-native@) exports exactly
+-- one value of this type, conventionally named @adapter@.
 --
--- 'Connection', 'Result', and 'Cancel' are per-connection and per-result;
--- they carry no information about which adapter produced them, so they
--- cannot themselves stand in for "the FFI adapter" or "the native adapter"
--- the way a driver-parameterised type could. 'Adapter' fills that gap: it is
--- the one value that names an adapter and knows how to bring a 'Connection'
--- into being, so a caller that must remain adapter-agnostic (a differential
--- test harness comparing two adapters, or a library that lets its users pick
--- an adapter at runtime) can hold onto a single 'Adapter' value instead of a
+-- This is the only value that identifies an adapter: t'Connection', t'Result'
+-- and t'Cancel' say nothing about which adapter produced them. Callers that
+-- must stay adapter-agnostic — a differential test harness, a library letting
+-- its users pick a driver at runtime — pass an t'Adapter' around instead of a
 -- family of adapter-qualified top-level functions.
---
--- Each adapter package (e.g. @pqi-ffi@, @pqi-native@) exports exactly one
--- top-level value of this type, conventionally named @adapter@.
 data Adapter = Adapter
   { -- | A short identifier for the adapter (e.g. @\"pqi-ffi\"@), for use in
     -- test descriptions, logs, and error messages.
@@ -528,5 +492,8 @@
     newNullConnection :: IO Connection,
     -- | Convert the textual representation of a @bytea@ value, as produced
     -- by the server, back into raw bytes, as @PQunescapeBytea@.
-    unescapeBytea :: ByteString -> IO (Maybe ByteString)
+    unescapeBytea :: ByteString -> IO (Maybe ByteString),
+    -- | Render an 'ExecStatus' as the string describing its status code
+    -- (e.g. @\"PGRES_TUPLES_OK\"@), as @PQresStatus@.
+    resStatus :: ExecStatus -> IO ByteString
   }
