diff --git a/Changelog.md b/Changelog.md
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,13 @@
 # Change log for unwitch project
 
+## Version 2.0.2
++ Wire up 9 missing test specs that existed but were not included in the test runner
++ Add module-level haddock to all Convert modules, Errors, and Constant
++ Add haddock to Overflows type and constructors
++ Expand property tests with 20+ new properties covering Int/Word
+  same-width conversions, Float/Double range checks, cross-sign
+  narrowing, Ratio round-trips, and Fixed round-trips
+
 ## Version 2.0.1
 + Add haddock section headers separating boxed and unboxed conversions
 + Each module with unboxed functions now has "Conversions" and
diff --git a/src/Unwitch/Constant.hs b/src/Unwitch/Constant.hs
--- a/src/Unwitch/Constant.hs
+++ b/src/Unwitch/Constant.hs
@@ -1,3 +1,4 @@
+-- | Constants used for range-checking floating-point conversions.
 module Unwitch.Constant
   ( maxIntegralRepDouble
   , maxIntegralRepFloat
diff --git a/src/Unwitch/Convert/ByteString.hs b/src/Unwitch/Convert/ByteString.hs
--- a/src/Unwitch/Convert/ByteString.hs
+++ b/src/Unwitch/Convert/ByteString.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Data.ByteString.ByteString'.
 module Unwitch.Convert.ByteString
   ( toLazyByteString
   , toShortByteString
diff --git a/src/Unwitch/Convert/Char.hs b/src/Unwitch/Convert/Char.hs
--- a/src/Unwitch/Convert/Char.hs
+++ b/src/Unwitch/Convert/Char.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Char'.
 module Unwitch.Convert.Char
   ( -- * Conversions
     toInt
diff --git a/src/Unwitch/Convert/Double.hs b/src/Unwitch/Convert/Double.hs
--- a/src/Unwitch/Convert/Double.hs
+++ b/src/Unwitch/Convert/Double.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Double'.
 module Unwitch.Convert.Double
   ( toFloat
   , toFixed
diff --git a/src/Unwitch/Convert/Fixed.hs b/src/Unwitch/Convert/Fixed.hs
--- a/src/Unwitch/Convert/Fixed.hs
+++ b/src/Unwitch/Convert/Fixed.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Data.Fixed.Fixed'.
 module Unwitch.Convert.Fixed
   ( fromInteger
   , toInteger
diff --git a/src/Unwitch/Convert/Float.hs b/src/Unwitch/Convert/Float.hs
--- a/src/Unwitch/Convert/Float.hs
+++ b/src/Unwitch/Convert/Float.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Float'.
 module Unwitch.Convert.Float
   ( toDouble
   , toRational
diff --git a/src/Unwitch/Convert/Int.hs b/src/Unwitch/Convert/Int.hs
--- a/src/Unwitch/Convert/Int.hs
+++ b/src/Unwitch/Convert/Int.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Int'.
 module Unwitch.Convert.Int
   ( -- * Conversions
     toInt8
diff --git a/src/Unwitch/Convert/Int16.hs b/src/Unwitch/Convert/Int16.hs
--- a/src/Unwitch/Convert/Int16.hs
+++ b/src/Unwitch/Convert/Int16.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Int16'.
 module Unwitch.Convert.Int16
   ( -- * Conversions
     toInt8
diff --git a/src/Unwitch/Convert/Int32.hs b/src/Unwitch/Convert/Int32.hs
--- a/src/Unwitch/Convert/Int32.hs
+++ b/src/Unwitch/Convert/Int32.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Int32'.
 module Unwitch.Convert.Int32
   ( -- * Conversions
     toInt8
diff --git a/src/Unwitch/Convert/Int64.hs b/src/Unwitch/Convert/Int64.hs
--- a/src/Unwitch/Convert/Int64.hs
+++ b/src/Unwitch/Convert/Int64.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Int64'.
 module Unwitch.Convert.Int64
   ( -- * Conversions
     toInt8
diff --git a/src/Unwitch/Convert/Int8.hs b/src/Unwitch/Convert/Int8.hs
--- a/src/Unwitch/Convert/Int8.hs
+++ b/src/Unwitch/Convert/Int8.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Int8'.
 module Unwitch.Convert.Int8
   ( -- * Conversions
     toInt16
diff --git a/src/Unwitch/Convert/Integer.hs b/src/Unwitch/Convert/Integer.hs
--- a/src/Unwitch/Convert/Integer.hs
+++ b/src/Unwitch/Convert/Integer.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Integer'.
 module Unwitch.Convert.Integer
   ( -- * Conversions
     toDouble
diff --git a/src/Unwitch/Convert/LazyByteString.hs b/src/Unwitch/Convert/LazyByteString.hs
--- a/src/Unwitch/Convert/LazyByteString.hs
+++ b/src/Unwitch/Convert/LazyByteString.hs
@@ -1,3 +1,4 @@
+-- | Conversions from lazy 'Data.ByteString.Lazy.ByteString'.
 module Unwitch.Convert.LazyByteString
   ( toByteString
   , toWord8s
diff --git a/src/Unwitch/Convert/LazyText.hs b/src/Unwitch/Convert/LazyText.hs
--- a/src/Unwitch/Convert/LazyText.hs
+++ b/src/Unwitch/Convert/LazyText.hs
@@ -1,3 +1,4 @@
+-- | Conversions from lazy 'Data.Text.Lazy.Text'.
 module Unwitch.Convert.LazyText
   ( toText
   , toString
diff --git a/src/Unwitch/Convert/Natural.hs b/src/Unwitch/Convert/Natural.hs
--- a/src/Unwitch/Convert/Natural.hs
+++ b/src/Unwitch/Convert/Natural.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Natural'.
 module Unwitch.Convert.Natural
   ( -- * Conversions
     toWord8
diff --git a/src/Unwitch/Convert/Ratio.hs b/src/Unwitch/Convert/Ratio.hs
--- a/src/Unwitch/Convert/Ratio.hs
+++ b/src/Unwitch/Convert/Ratio.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Data.Ratio.Ratio'.
 module Unwitch.Convert.Ratio
   ( unwrapIfDenominatorOne
   , fromIntegralToRatio
diff --git a/src/Unwitch/Convert/ShortByteString.hs b/src/Unwitch/Convert/ShortByteString.hs
--- a/src/Unwitch/Convert/ShortByteString.hs
+++ b/src/Unwitch/Convert/ShortByteString.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Data.ByteString.Short.ShortByteString'.
 module Unwitch.Convert.ShortByteString
   ( toByteString
   , toWord8s
diff --git a/src/Unwitch/Convert/Text.hs b/src/Unwitch/Convert/Text.hs
--- a/src/Unwitch/Convert/Text.hs
+++ b/src/Unwitch/Convert/Text.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Data.Text.Text'.
 module Unwitch.Convert.Text
   ( toLazyText
   , toString
diff --git a/src/Unwitch/Convert/Word.hs b/src/Unwitch/Convert/Word.hs
--- a/src/Unwitch/Convert/Word.hs
+++ b/src/Unwitch/Convert/Word.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Word'.
 module Unwitch.Convert.Word
   ( -- * Conversions
     toWord8
diff --git a/src/Unwitch/Convert/Word16.hs b/src/Unwitch/Convert/Word16.hs
--- a/src/Unwitch/Convert/Word16.hs
+++ b/src/Unwitch/Convert/Word16.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Word16'.
 module Unwitch.Convert.Word16
   ( -- * Conversions
     toWord8
diff --git a/src/Unwitch/Convert/Word32.hs b/src/Unwitch/Convert/Word32.hs
--- a/src/Unwitch/Convert/Word32.hs
+++ b/src/Unwitch/Convert/Word32.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Word32'.
 module Unwitch.Convert.Word32
   ( -- * Conversions
     toWord8
diff --git a/src/Unwitch/Convert/Word64.hs b/src/Unwitch/Convert/Word64.hs
--- a/src/Unwitch/Convert/Word64.hs
+++ b/src/Unwitch/Convert/Word64.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Word64'.
 module Unwitch.Convert.Word64
   ( -- * Conversions
     toWord8
diff --git a/src/Unwitch/Convert/Word8.hs b/src/Unwitch/Convert/Word8.hs
--- a/src/Unwitch/Convert/Word8.hs
+++ b/src/Unwitch/Convert/Word8.hs
@@ -1,3 +1,4 @@
+-- | Conversions from 'Word8'.
 module Unwitch.Convert.Word8
   ( -- * Conversions
     toWord16
diff --git a/src/Unwitch/Errors.hs b/src/Unwitch/Errors.hs
--- a/src/Unwitch/Errors.hs
+++ b/src/Unwitch/Errors.hs
@@ -1,8 +1,12 @@
+-- | Error types for fallible numeric conversions.
 module Unwitch.Errors
   ( Overflows(..)
   )
 where
 
-data Overflows = Overflow
-               | Underflow
+-- | Indicates that a numeric conversion failed because the source
+-- value is outside the representable range of the target type.
+data Overflows
+  = Overflow   -- ^ The source value is above the target's upper bound.
+  | Underflow  -- ^ The source value is below the target's lower bound.
   deriving (Show, Eq)
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -16,6 +16,15 @@
 import qualified Test.Convert.FloatSpec
 import qualified Test.Convert.DoubleSpec
 import qualified Test.Convert.PropertySpec
+import qualified Test.Convert.CharSpec
+import qualified Test.Convert.FixedSpec
+import qualified Test.Convert.RatioSpec
+import qualified Test.Convert.TextSpec
+import qualified Test.Convert.LazyTextSpec
+import qualified Test.Convert.ByteStringSpec
+import qualified Test.Convert.LazyByteStringSpec
+import qualified Test.Convert.ShortByteStringSpec
+import qualified Test.Convert.UnboxedSpec
 
 main :: IO ()
 main = hspec $ do
@@ -33,4 +42,13 @@
   Test.Convert.IntegerSpec.spec
   Test.Convert.FloatSpec.spec
   Test.Convert.DoubleSpec.spec
+  Test.Convert.CharSpec.spec
+  Test.Convert.FixedSpec.spec
+  Test.Convert.RatioSpec.spec
+  Test.Convert.TextSpec.spec
+  Test.Convert.LazyTextSpec.spec
+  Test.Convert.ByteStringSpec.spec
+  Test.Convert.LazyByteStringSpec.spec
+  Test.Convert.ShortByteStringSpec.spec
+  Test.Convert.UnboxedSpec.spec
   Test.Convert.PropertySpec.spec
diff --git a/test/Test/Convert/PropertySpec.hs b/test/Test/Convert/PropertySpec.hs
--- a/test/Test/Convert/PropertySpec.hs
+++ b/test/Test/Convert/PropertySpec.hs
@@ -5,6 +5,8 @@
 import Test.Hspec.QuickCheck (prop)
 import Data.Int
 import Data.Word
+import Data.Fixed (Fixed, E6)
+import Data.Ratio (Ratio)
 import qualified Unwitch.Convert.Int8 as Int8
 import qualified Unwitch.Convert.Int16 as Int16
 import qualified Unwitch.Convert.Int32 as Int32
@@ -20,6 +22,8 @@
 import qualified Unwitch.Convert.Char as Char
 import qualified Unwitch.Convert.Float as Float
 import qualified Unwitch.Convert.Double as Double
+import qualified Unwitch.Convert.Ratio as Ratio
+import qualified Unwitch.Convert.Fixed as Fixed
 import Numeric.Natural (Natural)
 
 spec :: Spec
@@ -398,6 +402,25 @@
     prop "Int64 -> Word64: Nothing iff x < 0" $ \(x :: Int64) ->
       (Int64.toWord64 x == Nothing) `shouldBe` (x < 0)
 
+    prop "Int -> Word: Nothing iff x < 0" $ \(x :: Int) ->
+      (Int.toWord x == Nothing) `shouldBe` (x < 0)
+
+  describe "Unsigned-to-signed same-width: exact failure condition" $ do
+    prop "Word8 -> Int8: Nothing iff x > 127" $ \(x :: Word8) ->
+      (Word8.toInt8 x == Nothing) `shouldBe` (x > 127)
+
+    prop "Word16 -> Int16: Nothing iff x > 32767" $ \(x :: Word16) ->
+      (Word16.toInt16 x == Nothing) `shouldBe` (x > 32767)
+
+    prop "Word32 -> Int32: Nothing iff x > 2147483647" $ \(x :: Word32) ->
+      (Word32.toInt32 x == Nothing) `shouldBe` (x > 2147483647)
+
+    prop "Word64 -> Int64: Nothing iff x > fromIntegral (maxBound :: Int64)" $ \(x :: Word64) ->
+      (Word64.toInt64 x == Nothing) `shouldBe` (x > fromIntegral (maxBound :: Int64))
+
+    prop "Word -> Int: Nothing iff x > fromIntegral (maxBound :: Int)" $ \(x :: Word) ->
+      (Word.toInt x == Nothing) `shouldBe` (x > fromIntegral (maxBound :: Int))
+
   describe "Cross-sign narrowing: exact failure condition" $ do
     prop "Int16 -> Word8: Nothing iff x < 0 || x > 255" $ \(x :: Int16) ->
       (Int16.toWord8 x == Nothing) `shouldBe` (x < 0 || x > 255)
@@ -417,6 +440,54 @@
     prop "Word64 -> Int32: Nothing iff x > 2147483647" $ \(x :: Word64) ->
       (Word64.toInt32 x == Nothing) `shouldBe` (x > 2147483647)
 
+    prop "Int -> Word8: Nothing iff x < 0 || x > 255" $ \(x :: Int) ->
+      (Int.toWord8 x == Nothing) `shouldBe` (x < 0 || x > 255)
+
+    prop "Int -> Word16: Nothing iff x < 0 || x > 65535" $ \(x :: Int) ->
+      (Int.toWord16 x == Nothing) `shouldBe` (x < 0 || x > 65535)
+
+    prop "Int -> Word32: Nothing iff x < 0 || x > 4294967295" $ \(x :: Int) ->
+      (Int.toWord32 x == Nothing) `shouldBe` (x < 0 || x > 4294967295)
+
+    prop "Word -> Int8: Nothing iff x > 127" $ \(x :: Word) ->
+      (Word.toInt8 x == Nothing) `shouldBe` (x > 127)
+
+    prop "Word -> Int16: Nothing iff x > 32767" $ \(x :: Word) ->
+      (Word.toInt16 x == Nothing) `shouldBe` (x > 32767)
+
+    prop "Word -> Int32: Nothing iff x > 2147483647" $ \(x :: Word) ->
+      (Word.toInt32 x == Nothing) `shouldBe` (x > 2147483647)
+
+  describe "Int/Word/Natural Float range check" $ do
+    prop "Int -> Float: succeeds iff abs value <= maxRepFloat" $ \(x :: Int) ->
+      let xi = fromIntegral x :: Integer
+      in isRight (Int.toFloat x) `shouldBe`
+           (xi >= -maxRepFloat && xi <= maxRepFloat)
+
+    prop "Word -> Float: succeeds iff value <= maxRepFloat" $ \(x :: Word) ->
+      isRight (Word.toFloat x) `shouldBe`
+        (fromIntegral x <= (maxRepFloat :: Integer))
+
+    prop "Natural -> Float: succeeds iff value <= maxRepFloat" $ \(w :: Word64) ->
+      let x = fromIntegral w :: Natural
+      in isRight (Natural.toFloat x) `shouldBe`
+           (fromIntegral w <= (maxRepFloat :: Integer))
+
+  describe "Int/Word/Natural Double range check" $ do
+    prop "Int -> Double: succeeds iff abs value <= maxRepDouble" $ \(x :: Int) ->
+      let xi = fromIntegral x :: Integer
+      in isRight (Int.toDouble x) `shouldBe`
+           (xi >= -maxRepDouble && xi <= maxRepDouble)
+
+    prop "Word -> Double: succeeds iff value <= maxRepDouble" $ \(x :: Word) ->
+      isRight (Word.toDouble x) `shouldBe`
+        (fromIntegral x <= (maxRepDouble :: Integer))
+
+    prop "Natural -> Double: succeeds iff value <= maxRepDouble" $ \(w :: Word64) ->
+      let x = fromIntegral w :: Natural
+      in isRight (Natural.toDouble x) `shouldBe`
+           (fromIntegral w <= (maxRepDouble :: Integer))
+
   describe "Float/Double to integer: exact success condition" $ do
     prop "Float -> Integer: succeeds iff finite and whole" $ \(x :: Float) ->
       isRight (Float.toInteger x) `shouldBe` isWholeFloat x
@@ -471,6 +542,16 @@
       in if isNaN x
          then y `shouldSatisfy` isNaN
          else y `shouldBe` x
+
+  describe "Ratio round-trip" $ do
+    prop "Int8 -> Ratio Int8 -> Int8" $ \(x :: Int8) ->
+      let r = Ratio.fromIntegralToRatio x :: Ratio Int8
+      in Ratio.unwrapIfDenominatorOne r `shouldBe` Just x
+
+  describe "Fixed round-trip" $ do
+    prop "Integer -> Fixed E6 -> Integer for whole numbers" $ \(x :: Integer) ->
+      let f = Fixed.fromInteger x :: Fixed E6
+      in Fixed.toInteger f `shouldBe` Just x
 
 maxRepFloat :: Num a => a
 maxRepFloat = 16777215
diff --git a/unwitch.cabal b/unwitch.cabal
--- a/unwitch.cabal
+++ b/unwitch.cabal
@@ -1,7 +1,7 @@
 cabal-version:      3.0
 
 name:           unwitch
-version:        2.0.1
+version:        2.0.2
 homepage:       https://github.com/jappeace/unwitch#readme
 synopsis:  converts between primitives
 description: 
