diff --git a/changelog.md b/changelog.md
--- a/changelog.md
+++ b/changelog.md
@@ -1,21 +1,34 @@
+# 0.4.1.0
+
+- **Bug fix**: `Data.RAList.Co` — off-by-one in all index operations (`!!`, `lookup`, `lookupM`, `lookupWithDefault`, `lookupCC`). Index formula was `length - n` instead of `length - 1 - n`.
+- **Bug fix**: `Data.RAList.Co.adjust` — infinite recursion (called itself instead of `QRA.adjust`).
+- **Bug fix**: `Data.RAList.Co.genericReplicate` — infinite recursion (called itself instead of `QRA.genericReplicate`).
+- Widened dependency bounds for GHC 9.14 (base < 6, deepseq < 1.7, transformers < 0.7, hspec < 2.12).
+- Added test coverage for `Data.RAList.Co` (35 new tests).
+
 # 0.4.0.0
 
-* flipped the argument order for `Co.lookup` to be consistent between
-both flavors of RAList apis
+- Breaking change to api to make api flavors more consistent.
 
 # 0.3.0.0
-* changed type from
-  `lookupM :: forall (m :: * -> *) a. Monad m => Word64 -> Top a -> m a` to `lookupM :: forall a. Word64 -> Top a -> Either String a`
-* added `Data.RAList.Flip`, which flips the counting by having the zeroth index be the last element of the skew binary list,
-rather than the head element.
 
+- Added `Data.RAList.Co` module for reversed (co-indexed) access lists.
+- Added filter/catMaybe/wither family of operations.
+- Added NFData/NFData1 instances.
+- Added indexed traversal instances (FunctorWithIndex, FoldableWithIndex, TraversableWithIndex).
+
 # 0.2.1.1
-Fix bug in log n time drop
 
+- Documentation tweaks.
+
 # 0.2.1.0
-Added missing traversable instance
 
+- Various improvements.
+
 # 0.2.0.0
-updated version of ralist
-includes bug fixes, api cleanup,
-test suite and logarithmic drop contributed by Nell White
+
+- Major rework by Carter Schonwald.
+
+# 0.1.0.0
+
+- Initial release by Lennart Augustsson.
diff --git a/ralist.cabal b/ralist.cabal
--- a/ralist.cabal
+++ b/ralist.cabal
@@ -1,6 +1,6 @@
 cabal-version:    3.0
 Name:           ralist
-Version:        0.4.0.0
+Version:        0.4.1.0
 License:        BSD-3-Clause
 license-file: LICENSE
 Author:         Lennart Augustsson, Carter Schonwald
@@ -16,7 +16,7 @@
 -- URL for the project homepage or repository.
 homepage:            http://github.com/cartazio/ralist
 
-tested-with: GHC==8.10.2, GHC==8.8.4, GHC==8.6.5
+tested-with: GHC==9.14.1, GHC==8.10.2, GHC==8.8.4, GHC==8.6.5
 extra-source-files:   changelog.md
                       LICENSE
 
@@ -30,10 +30,10 @@
 Library
   Build-Depends:
     base >= 4.12 && < 6
-    ,indexed-traversable ^>= 0.1
-    , transformers ^>= 0.5
+    ,indexed-traversable >= 0.1 && < 0.2
+    , transformers >= 0.5 && < 0.7
     -- only needed for one spot in .co
-    ,deepseq ^>= 1.4.4.0
+    ,deepseq >= 1.4.4.0 && < 1.7
   Exposed-modules:
        Data.RAList
        ,Data.RAList.Co
@@ -59,7 +59,7 @@
   build-depends:
       base,
       ralist,
-      hspec >= 2.2 && < 2.8
+      hspec >= 2.2 && < 2.12
 
 
 benchmark benchmarking
diff --git a/src/Data/RAList/Co.hs b/src/Data/RAList/Co.hs
--- a/src/Data/RAList/Co.hs
+++ b/src/Data/RAList/Co.hs
@@ -322,23 +322,23 @@
 (!!) :: RAList a -> Word64 -> a
 rls  !! n |  n <  0 = error "Data.RAList.Flip.!!: negative index"
                         | n >= (wLength  rls)  = error "Data.RAList.Flip.!!: index too large"
-                        | otherwise =  reindex rls QRA.!! ((wLength rls)  - n )
+                        | otherwise =  reindex rls QRA.!! ((wLength rls) - 1 - n )
 {-# INLINE lookupWithDefault #-}
 lookupWithDefault :: forall t. t -> Word64 -> RAList t -> t
-lookupWithDefault = \ def ix tree -> QRA.lookupWithDefault def ((wLength tree) - ix ) $ reindex tree
+lookupWithDefault = \ def ix tree -> QRA.lookupWithDefault def ((wLength tree) - 1 - ix ) $ reindex tree
 
 
 {-# INLINE lookupM #-}
 lookupM :: forall a m . MF.MonadFail m =>  Word64 -> RAList a ->  m a
-lookupM = \ ix tree ->  QRA.lookupM  (reindex tree) ((wLength tree)  - ix)
+lookupM = \ ix tree ->  QRA.lookupM  (reindex tree) ((wLength tree) - 1 - ix)
 
 {-# INLINE lookup #-}
 lookup :: forall a. RAList a -> Word64 ->  Maybe a
-lookup =  \ (CoIndex tree) ix -> QRA.lookup  tree  ((QRA.wLength tree) - ix )
+lookup =  \ (CoIndex tree) ix -> QRA.lookup  tree  ((QRA.wLength tree) - 1 - ix )
 
 {-# INLINE lookupCC #-}
 lookupCC :: RAList a -> Word64 -> (a -> r) -> (String -> r) -> r
-lookupCC = \  tree ix f g ->  QRA.lookupCC (reindex tree) ((wLength tree) - ix ) f g
+lookupCC = \  tree ix f g ->  QRA.lookupCC (reindex tree) ((wLength tree) - 1 - ix ) f g
 
 {-# INLINE wLength #-}
 wLength:: RAList a -> Word64
@@ -396,7 +396,7 @@
 genericIndex  x i  = QRA.genericIndex (reindex x) i
 
 genericReplicate :: Integral n => n -> a -> RAList a
-genericReplicate i v = coerce $ genericReplicate i v
+genericReplicate i v = coerce $ QRA.genericReplicate i v
 
 
 update ::  Word64 -> a -> RAList a -> RAList a
@@ -404,7 +404,7 @@
 
 
 adjust :: forall a . (a->a) -> Word64 -> RAList a -> RAList a
-adjust f i l =  coerce $ adjust f i $ coerce l
+adjust f i l =  coerce $ QRA.adjust f ((wLength l) - 1 - i) $ coerce l
 
 
 unfoldr :: (b -> Maybe (a, b)) -> b -> RAList a
diff --git a/tests/hspec.hs b/tests/hspec.hs
--- a/tests/hspec.hs
+++ b/tests/hspec.hs
@@ -1,8 +1,10 @@
 module Main where
 
 import Data.RAList
+import qualified Data.RAList.Co as Co
 import Test.Hspec
 import Control.Exception (evaluate)
+import Data.Word (Word64)
 
 import Prelude hiding (
     (++), head, last, tail, init, null, length, map, reverse,
@@ -737,4 +739,119 @@
       toList (fromList [1..9]) `shouldBe` ([1..9] :: [Int])
     it "converts an empty list" $ do
       toList (empty) `shouldBe` ([] :: [Int])
+
+  -- ═══════════════════════════════════════════════════════
+  -- Data.RAList.Co tests (reversed indexing)
+  -- ═══════════════════════════════════════════════════════
+
+  let co3 = Co.fromList [1,2,3] :: Co.RAList Int
+      co9 = Co.fromList [1..9] :: Co.RAList Int
+      coEmpty = Co.Nil :: Co.RAList Int
+
+  describe "Co.cons/uncons" $ do
+    it "cons adds to an empty list" $
+      Co.cons 1 coEmpty `shouldBe` Co.fromList [1 :: Int]
+    it "cons then uncons roundtrips" $
+      Co.uncons (Co.cons 42 co3) `shouldBe` Just (42 :: Int, co3)
+    it "uncons empty is Nothing" $
+      Co.uncons coEmpty `shouldBe` (Nothing :: Maybe (Int, Co.RAList Int))
+
+  describe "Co.toList/fromList" $ do
+    it "roundtrips length 3" $
+      Co.toList co3 `shouldBe` [1,2,3 :: Int]
+    it "roundtrips length 9" $
+      Co.toList co9 `shouldBe` [1..9 :: Int]
+    it "roundtrips empty" $
+      Co.toList coEmpty `shouldBe` ([] :: [Int])
+
+  describe "Co.(!!) reversed indexing" $ do
+    -- Co indexes from the back: head is at (length-1), last is at 0
+    it "!! 0 gives last element" $
+      co9 Co.!! 0 `shouldBe` (9 :: Int)
+    it "!! (length-1) gives first element" $
+      co9 Co.!! 8 `shouldBe` (1 :: Int)
+    it "!! 4 gives middle element" $
+      co9 Co.!! 4 `shouldBe` (5 :: Int)
+
+  describe "Co.lookup" $ do
+    it "lookup 0 gives last" $
+      Co.lookup co9 0 `shouldBe` Just (9 :: Int)
+    it "lookup (length-1) gives first" $
+      Co.lookup co9 8 `shouldBe` Just (1 :: Int)
+    it "lookup out of bounds gives Nothing" $
+      Co.lookup co9 99 `shouldBe` (Nothing :: Maybe Int)
+
+  describe "Co.length" $ do
+    it "length 9" $
+      Co.wLength co9 `shouldBe` (9 :: Word64)
+    it "length empty" $
+      Co.wLength coEmpty `shouldBe` (0 :: Word64)
+
+  describe "Co.drop" $ do
+    it "drop 0 is identity" $
+      Co.toList (Co.drop 0 co9) `shouldBe` [1..9 :: Int]
+    it "drop 3 removes first 3" $
+      Co.toList (Co.drop 3 co9) `shouldBe` [4..9 :: Int]
+    it "drop all gives empty" $
+      Co.toList (Co.drop 9 co9) `shouldBe` ([] :: [Int])
+
+  describe "Co.take" $ do
+    it "take 3 keeps first 3" $
+      Co.toList (Co.take 3 co9) `shouldBe` [1,2,3 :: Int]
+    it "take 0 gives empty" $
+      Co.toList (Co.take 0 co9) `shouldBe` ([] :: [Int])
+    it "take all is identity" $
+      Co.toList (Co.take 9 co9) `shouldBe` [1..9 :: Int]
+
+  describe "Co.zip/zipWith" $ do
+    it "zip equal length" $
+      Co.toList (Co.zip co3 (Co.fromList [10,20,30])) `shouldBe`
+        [(1,10),(2,20),(3,30 :: Int)]
+    it "zipWith (+) equal length" $
+      Co.toList (Co.zipWith (+) co3 (Co.fromList [10,20,30])) `shouldBe`
+        [11,22,33 :: Int]
+    it "zip different lengths truncates from front (Co semantics)" $
+      Co.toList (Co.zip co3 co9) `shouldBe`
+        [(1,7),(2,8),(3,9 :: Int)]
+
+  describe "Co.filter/partition" $ do
+    it "filter even" $
+      Co.toList (Co.filter even co9) `shouldBe` [2,4,6,8 :: Int]
+    it "partition even" $ do
+      let (evens, odds) = Co.partition even co9
+      Co.toList evens `shouldBe` [2,4,6,8 :: Int]
+      Co.toList odds `shouldBe` [1,3,5,7,9 :: Int]
+
+  describe "Co.genericReplicate (was infinite recursion)" $ do
+    it "replicate 0" $
+      Co.toList (Co.genericReplicate (0 :: Int) 'x') `shouldBe` ""
+    it "replicate 1" $
+      Co.toList (Co.genericReplicate (1 :: Int) 'x') `shouldBe` "x"
+    it "replicate 5" $
+      Co.toList (Co.genericReplicate (5 :: Int) 'x') `shouldBe` "xxxxx"
+
+  describe "Co.adjust (was infinite recursion)" $ do
+    it "adjust at 0 modifies last element" $
+      Co.toList (Co.adjust (+100) 0 co9) `shouldBe`
+        [1,2,3,4,5,6,7,8,109 :: Int]
+    it "adjust at (length-1) modifies first element" $
+      Co.toList (Co.adjust (+100) 8 co9) `shouldBe`
+        [101,2,3,4,5,6,7,8,9 :: Int]
+    it "adjust at 4 modifies middle element" $
+      Co.toList (Co.adjust (*10) 4 co9) `shouldBe`
+        [1,2,3,4,50,6,7,8,9 :: Int]
+
+  describe "Co.update (uses adjust)" $ do
+    it "update at 0 replaces last element" $
+      Co.toList (Co.update 0 99 co9) `shouldBe`
+        [1,2,3,4,5,6,7,8,99 :: Int]
+    it "update at 8 replaces first element" $
+      Co.toList (Co.update 8 99 co9) `shouldBe`
+        [99,2,3,4,5,6,7,8,9 :: Int]
+
+  describe "Co.append" $ do
+    it "append two lists" $
+      Co.toList (co3 Co.++ Co.fromList [4,5,6]) `shouldBe` [1..6 :: Int]
+    it "append empty" $
+      Co.toList (co3 Co.++ coEmpty) `shouldBe` [1,2,3 :: Int]
 
