diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,9 @@
 # Changelog for hs-opentelemetry-instrumentation-conduit
 
+## 1.0.0.0 - 2026-05-29
+
+- Promoted to 1.0.0.0 for the hs-opentelemetry 1.0 release.
+
 ## 0.1.0.2
 
 - Relax `hs-opentelemetry-api` bounds to support 0.3.x
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright Ian Duncan (c) 2021
+Copyright Ian Duncan (c) 2021-2026
 
 All rights reserved.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,1 +1,3 @@
 # hs-opentelemetry-instrumentation-conduit
+
+[![hs-opentelemetry-instrumentation-conduit](https://img.shields.io/hackage/v/hs-opentelemetry-instrumentation-conduit?style=flat-square&logo=haskell&label=hs-opentelemetry-instrumentation-conduit&labelColor=5D4F85)](https://hackage.haskell.org/package/hs-opentelemetry-instrumentation-conduit)
diff --git a/hs-opentelemetry-instrumentation-conduit.cabal b/hs-opentelemetry-instrumentation-conduit.cabal
--- a/hs-opentelemetry-instrumentation-conduit.cabal
+++ b/hs-opentelemetry-instrumentation-conduit.cabal
@@ -1,20 +1,22 @@
 cabal-version: 1.12
 
--- This file has been generated from package.yaml by hpack version 0.37.0.
+-- This file has been generated from package.yaml by hpack version 0.38.3.
 --
 -- see: https://github.com/sol/hpack
 
-name:               hs-opentelemetry-instrumentation-conduit
-version:            0.1.0.2
-description:        Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/conduit#readme>
-homepage:           https://github.com/iand675/hs-opentelemetry#readme
-bug-reports:        https://github.com/iand675/hs-opentelemetry/issues
-author:             Ian Duncan, Jade Lovelace
-maintainer:         ian@iankduncan.com
-copyright:          2024 Ian Duncan, Mercury Technologies
-license:            BSD3
-license-file:       LICENSE
-build-type:         Simple
+name:           hs-opentelemetry-instrumentation-conduit
+version:        1.0.0.0
+synopsis:       OpenTelemetry instrumentation for Conduit streaming pipelines
+description:    Please see the README on GitHub at <https://github.com/iand675/hs-opentelemetry/tree/main/instrumentation/conduit#readme>
+category:       OpenTelemetry, Conduit, Streaming
+homepage:       https://github.com/iand675/hs-opentelemetry#readme
+bug-reports:    https://github.com/iand675/hs-opentelemetry/issues
+author:         Ian Duncan, Jade Lovelace
+maintainer:     ian@iankduncan.com
+copyright:      2024 Ian Duncan, Mercury Technologies
+license:        BSD3
+license-file:   LICENSE
+build-type:     Simple
 extra-source-files:
     README.md
     ChangeLog.md
@@ -34,6 +36,28 @@
   build-depends:
       base >=4.7 && <5
     , conduit
-    , hs-opentelemetry-api ==0.3.*
+    , hs-opentelemetry-api ==1.0.*
+    , hs-opentelemetry-semantic-conventions >=1.40 && <2
     , text
+  default-language: Haskell2010
+
+test-suite hs-opentelemetry-instrumentation-conduit-test
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  other-modules:
+      Paths_hs_opentelemetry_instrumentation_conduit
+  hs-source-dirs:
+      test
+  ghc-options: -threaded -rtsopts -with-rtsopts=-N
+  build-depends:
+      base >=4.7 && <5
+    , conduit
+    , hs-opentelemetry-api ==1.0.*
+    , hs-opentelemetry-exporter-in-memory ==1.0.*
+    , hs-opentelemetry-instrumentation-conduit ==1.0.*
+    , hs-opentelemetry-sdk ==1.0.*
+    , hspec
+    , resourcet
+    , text
+    , vector
   default-language: Haskell2010
diff --git a/src/OpenTelemetry/Instrumentation/Conduit.hs b/src/OpenTelemetry/Instrumentation/Conduit.hs
--- a/src/OpenTelemetry/Instrumentation/Conduit.hs
+++ b/src/OpenTelemetry/Instrumentation/Conduit.hs
@@ -1,13 +1,44 @@
 {-# LANGUAGE OverloadedLists #-}
 {-# LANGUAGE OverloadedStrings #-}
 
+{- |
+Module      : OpenTelemetry.Instrumentation.Conduit
+Copyright   : (c) Ian Duncan, 2021-2026
+License     : BSD-3
+Description : Trace conduit pipeline stages as spans
+Stability   : experimental
+
+= Overview
+
+Wraps individual conduit pipeline stages in trace spans so you can see
+how time is spent across your streaming pipeline. Use 'inSpan' to run a
+sub-pipeline under a span; exceptions are recorded on the span and rethrown.
+
+= Quick example
+
+@
+import Conduit
+import Data.ByteString (ByteString)
+import OpenTelemetry.Instrumentation.Conduit (inSpan)
+import OpenTelemetry.Trace.Core (defaultSpanArguments)
+
+myPipeline :: Tracer -> ConduitT ByteString Void IO ()
+myPipeline tracer =
+  sourceFile "input.csv"
+    .| inSpan tracer "parse" defaultSpanArguments (\_ -> mapC parseRow)
+    .| inSpan tracer "transform" defaultSpanArguments (\_ -> mapC transformRow)
+    .| sinkFile "output.json"
+@
+-}
 module OpenTelemetry.Instrumentation.Conduit where
 
 import Conduit
 import Control.Exception (SomeException, throwIO)
 import Data.Text (Text)
 import GHC.Stack (HasCallStack)
+import OpenTelemetry.Attributes.Key (unkey)
 import OpenTelemetry.Context.ThreadLocal
+import qualified OpenTelemetry.SemanticConventions as SC
 import OpenTelemetry.Trace.Core hiding (getTracer)
 
 
@@ -26,5 +57,5 @@
     $ \span_ -> do
       catchC (f span_) $ \e -> do
         liftIO $ do
-          recordException span_ [("exception.escaped", toAttribute True)] Nothing (e :: SomeException)
+          recordException span_ [(unkey SC.exception_escaped, toAttribute True)] Nothing (e :: SomeException)
           throwIO e
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,65 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+
+module Main where
+
+import Conduit
+import Control.Exception (SomeException, throwIO, try)
+import Control.Monad.IO.Class (liftIO)
+import Data.IORef
+import qualified Data.Vector as V
+import OpenTelemetry.Exporter.InMemory.Span (inMemoryListExporter)
+import OpenTelemetry.Instrumentation.Conduit (inSpan)
+import OpenTelemetry.Trace.Core (Event (..))
+import OpenTelemetry.Trace.Core hiding (inSpan)
+import OpenTelemetry.Util (appendOnlyBoundedCollectionValues)
+import System.IO.Error (userError)
+import Test.Hspec
+
+
+main :: IO ()
+main = hspec spec
+
+
+withTracer :: (Tracer -> IO a) -> IO ([ImmutableSpan], a)
+withTracer action = do
+  (processor, ref) <- inMemoryListExporter
+  tp <- createTracerProvider [processor] emptyTracerProviderOptions
+  let tracer = makeTracer tp "test-conduit" tracerOptions
+  result <- action tracer
+  _ <- shutdownTracerProvider tp Nothing
+  spans <- readIORef ref
+  pure (spans, result)
+
+
+firstSpan :: [ImmutableSpan] -> IO ImmutableSpan
+firstSpan (s : _) = pure s
+firstSpan [] = expectationFailure "No spans recorded" >> pure (error "unreachable")
+
+
+spec :: Spec
+spec = describe "Conduit instrumentation" $ do
+  it "creates a span wrapping a conduit pipeline" $ do
+    (spans, result) <- withTracer $ \t ->
+      runConduitRes $
+        inSpan t "process-items" defaultSpanArguments $ \_s ->
+          yieldMany [1 :: Int, 2, 3] .| sinkList
+    result `shouldBe` [1, 2, 3]
+    s <- firstSpan spans
+    hot <- readIORef (spanHot s)
+    hotName hot `shouldBe` "process-items"
+
+  it "records exception on conduit failure" $ do
+    (spans, result) <- withTracer $ \t -> do
+      r <- try $
+        runConduitRes $
+          inSpan t "failing-conduit" defaultSpanArguments $ \_s ->
+            yieldMany [1 :: Int, 2, 3] .| mapMC (\_ -> liftIO $ throwIO $ userError "conduit-boom") .| sinkList
+      pure (r :: Either SomeException [Int])
+    case result of
+      Left _ -> pure ()
+      Right _ -> expectationFailure "expected exception"
+    s <- firstSpan spans
+    hot <- readIORef (spanHot s)
+    let events = V.toList $ appendOnlyBoundedCollectionValues $ hotEvents hot
+    any (\e -> eventName e == "exception") events `shouldBe` True
