diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,3 +1,10 @@
+0.9.4
+===
+
+- GHC 9.14.1 support
+- simplified Cabal stanzas to best practice template
+- relaxed dependency bounds via allow-newer for GHC 9.14 compatibility
+
 0.9.3
 ===
 * stdBox, toLineBox, fromLineBox added to Box.IO
diff --git a/box.cabal b/box.cabal
--- a/box.cabal
+++ b/box.cabal
@@ -1,6 +1,6 @@
 cabal-version: 3.0
 name: box
-version: 0.9.3.3
+version: 0.9.4.0
 license: BSD-3-Clause
 license-file: LICENSE
 copyright: Tony Day (c) 2017
@@ -15,10 +15,9 @@
 
 build-type: Simple
 tested-with:
-  ghc ==9.6.7
-  ghc ==9.8.4
-  ghc ==9.10.2
+  ghc ==9.10.3
   ghc ==9.12.2
+  ghc ==9.14.1
 
 extra-doc-files:
   ChangeLog.md
@@ -36,29 +35,9 @@
     -Wincomplete-uni-patterns
     -Wredundant-constraints
 
-common ghc2024-additions
-  default-extensions:
-    DataKinds
-    DerivingStrategies
-    DisambiguateRecordFields
-    ExplicitNamespaces
-    GADTs
-    LambdaCase
-    MonoLocalBinds
-    RoleAnnotations
-
-common ghc2024-stanza
-  if impl(ghc >=9.10)
-    default-language:
-      GHC2024
-  else
-    import: ghc2024-additions
-    default-language:
-      GHC2021
-
 library
   import: ghc-options-stanza
-  import: ghc2024-stanza
+  default-language: GHC2024
   hs-source-dirs: src
   build-depends:
     async >=2.2 && <2.3,
@@ -88,13 +67,3 @@
     Box.Queue
     Box.Time
 
-test-suite doctests
-  import: ghc2024-stanza
-  main-is: doctests.hs
-  hs-source-dirs: test
-  build-depends:
-    base >=4.14 && <5,
-    doctest-parallel >=0.3 && <0.5,
-
-  ghc-options: -threaded
-  type: exitcode-stdio-1.0
diff --git a/readme.md b/readme.md
--- a/readme.md
+++ b/readme.md
@@ -1,11 +1,10 @@
-# box 
-
-[![img](https://img.shields.io/hackage/v/box.svg)](https://hackage.haskell.org/package/box) [![img](https://github.com/tonyday567/box/workflows/haskell-ci/badge.svg)](https://github.com/tonyday567/box/actions?query=workflow%3Ahaskell-ci)
+[![img](https://img.shields.io/hackage/v/box.svg)](https://hackage.haskell.org/package/box) [![img](https://github.com/tonyday567/box/actions/workflows/haskell-ci.yml/badge.svg)](https://github.com/tonyday567/box/actions/workflows/haskell-ci.yml)
 
 A profunctor effect system.
 
 > What is all this stuff around me; this stream of experiences that I seem to be having all the time? Throughout history there have been people who say it is all illusion. ~ S Blackmore
 
+
 # Usage
 
     :set -XOverloadedStrings
@@ -43,6 +42,7 @@
 
 # Library Design
 
+
 ### Resource Coinduction
 
 Haskell has an affinity with [coinductive functions](https://www.reddit.com/r/haskell/comments/j3kbge/comment/g7foelq/?utm_source=share&utm_medium=web2x&context=3); functions should expose destructors and allow for infinite data.
@@ -59,6 +59,7 @@
 
 These are the destructors that need to be transparently exposed if effects are to be good citizens in Haskell.
 
+
 ### What is a Box?
 
 A Box is simply the product of a consumer destructor and a producer destructor.
@@ -68,6 +69,7 @@
         emitter :: Emitter m e
       }
 
+
 ### Committer
 
 The library denotes a consumer by wrapping a consumption destructor and calling it a Committer. Like much of base, there is failure hidden in the getLine example type. A better approach, for a consumer, is to signal whether consumption actually occurred.
@@ -93,6 +95,7 @@
     echoC :: Committer IO Text
     echoC = contramap (Text.unpack . ("echo: "<>)) stdC
 
+
 ### Emitter
 
 The library denotes a producer by wrapping a production destructor and calling it an Emitter.
@@ -146,6 +149,7 @@
 
     fuse (pure . pure) stdIO
 
+
 ### Continuation
 
 As with many operators in the library, `qList` is actually a continuation:
@@ -210,6 +214,7 @@
     b
     c
 
+
 # Explicit Continuation
 
 Yield-style streaming libraries are [coroutines](https://rubenpieters.github.io/assets/papers/JFP20-pipes.pdf), sum types that embed and mix continuation logic in with other stuff like effect decontruction. `box` sticks to a corner case of a product type representing a consumer and producer. The major drawback of eschewing coroutines is that continuations become explicit and difficult to hide. One example; taking the first n elements of an Emitter:
@@ -227,18 +232,22 @@
     glueES :: (Monad m) => s -> Committer m a -> Emitter (StateT s m) a -> m ()
     glueES s c e = flip evalStateT s $ glue (foist lift c) e
 
+
 # Future directions
 
 The design and concepts contained within the box library is a hodge-podge, but an interesting mess, being at quite a busy confluence of recent developments.
 
+
 ## Optics
 
 A Box is an adapter in the [language of optics](http://www.cs.ox.ac.uk/people/jeremy.gibbons/publications/poptics.pdf) and the relationship between a resource&rsquo;s committer and emitter could be modelled by other optics.
 
+
 ## Categorical Profunctor
 
 The deprecation of Box.Functor awaits the development of [categorical functors](https://github.com/haskell/core-libraries-committee/issues/91#issuecomment-1325337471). Similarly to Filterable the type of a Box could be something like `FunctorOf Op(Kleisli Maybe) (Kleisli Maybe) (->)`. Or it could be something like the SISO type in [Programming with Monoidal Profunctors and Semiarrows](https://papers.ssrn.com/sol3/papers.cfm?abstract_id=4496714).
 
+
 ## Wider Types
 
 Alternatively, the types could be widened:
@@ -267,6 +276,7 @@
     type CommitterB m a = Committer (MaybeT m) a
     type EmitterB m a = Emitter (MaybeT m) a
     type BoxB m b a = Box (MaybeT m) (MaybeT m) b a
+
 
 ## Introduce a [nucleus](https://golem.ph.utexas.edu/category/2013/08/the_nucleus_of_a_profunctor_so.html)
 
diff --git a/test/doctests.hs b/test/doctests.hs
deleted file mode 100644
--- a/test/doctests.hs
+++ /dev/null
@@ -1,7 +0,0 @@
-module Main where
-
-import System.Environment (getArgs)
-import Test.DocTest (mainFromCabal)
-
-main :: IO ()
-main = mainFromCabal "box" =<< getArgs
