diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,12 @@
-## [_Unreleased_](https://github.com/freckle/freckle-app/compare/v1.17.0.0...main)
+## [_Unreleased_](https://github.com/freckle/freckle-app/compare/v1.18.1.0...main)
+
+## [v1.18.1.0](https://github.com/freckle/freckle-app/compare/v1.18.0.0...v1.18.1.0)
+
+- Add `Freckle.App.Http (setRequestMethod)`
+- Add `Freckle.App.Async` (`foldConcurrently`, `ThreadContext (..)`,
+  `getThreadContext`, `withThreadContext`)
+- Bugfix: Utilities in `Freckle.App.Async` now preserve OpenTelemetry context
+  (previously were only passing along Blammo logging context)
 
 ## [v1.18.0.0](https://github.com/freckle/freckle-app/compare/v1.17.1.0...v1.18.0.0)
 
diff --git a/freckle-app.cabal b/freckle-app.cabal
--- a/freckle-app.cabal
+++ b/freckle-app.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               freckle-app
-version:            1.18.0.0
+version:            1.18.1.0
 license:            MIT
 license-file:       LICENSE
 maintainer:         Freckle Education
@@ -83,22 +83,19 @@
 
     hs-source-dirs:     library
     other-modules:      Paths_freckle_app
-    default-language:   Haskell2010
+    default-language:   GHC2021
     default-extensions:
-        BangPatterns DataKinds DeriveAnyClass DeriveFoldable DeriveFunctor
-        DeriveGeneric DeriveLift DeriveTraversable DerivingVia
-        DerivingStrategies FlexibleContexts FlexibleInstances GADTs
-        GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses
-        NoImplicitPrelude NoMonomorphismRestriction OverloadedStrings
-        RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving
-        TypeApplications TypeFamilies
+        DataKinds DeriveAnyClass DerivingVia DerivingStrategies GADTs
+        LambdaCase NoImplicitPrelude NoMonomorphismRestriction
+        OverloadedStrings RecordWildCards TypeFamilies
 
     ghc-options:
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missing-exported-signatures
-        -Wno-missing-import-lists -Wno-missing-local-signatures
-        -Wno-missing-safe-haskell-mode -Wno-monomorphism-restriction
-        -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe
+        -Wno-missing-import-lists -Wno-missing-kind-signatures
+        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
+        -Wno-monomorphism-restriction -Wno-prepositive-qualified-module
+        -Wno-safe -Wno-unsafe
 
     build-depends:
         Blammo >=1.2.0.0,
@@ -184,48 +181,42 @@
         yesod-test >=1.6.15
 
     if impl(ghc >=9.8)
-        ghc-options: -Wno-missing-role-annotations
-
-    if impl(ghc >=9.2)
-        ghc-options: -Wno-missing-kind-signatures
+        ghc-options:
+            -Wno-missing-role-annotations -Wno-missing-poly-kind-signatures
 
 test-suite doctest
     type:               exitcode-stdio-1.0
     main-is:            Main.hs
     hs-source-dirs:     doctest
     other-modules:      Paths_freckle_app
-    default-language:   Haskell2010
+    default-language:   GHC2021
     default-extensions:
-        BangPatterns DataKinds DeriveAnyClass DeriveFoldable DeriveFunctor
-        DeriveGeneric DeriveLift DeriveTraversable DerivingVia
-        DerivingStrategies FlexibleContexts FlexibleInstances GADTs
-        GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses
-        NoImplicitPrelude NoMonomorphismRestriction OverloadedStrings
-        RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving
-        TypeApplications TypeFamilies
+        DataKinds DeriveAnyClass DerivingVia DerivingStrategies GADTs
+        LambdaCase NoImplicitPrelude NoMonomorphismRestriction
+        OverloadedStrings RecordWildCards TypeFamilies
 
     ghc-options:
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missing-exported-signatures
-        -Wno-missing-import-lists -Wno-missing-local-signatures
-        -Wno-missing-safe-haskell-mode -Wno-monomorphism-restriction
-        -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe
+        -Wno-missing-import-lists -Wno-missing-kind-signatures
+        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
+        -Wno-monomorphism-restriction -Wno-prepositive-qualified-module
+        -Wno-safe -Wno-unsafe
 
     build-depends:
         base >=4.16.4.0 && <5,
         freckle-app
 
     if impl(ghc >=9.8)
-        ghc-options: -Wno-missing-role-annotations
-
-    if impl(ghc >=9.2)
-        ghc-options: -Wno-missing-kind-signatures
+        ghc-options:
+            -Wno-missing-role-annotations -Wno-missing-poly-kind-signatures
 
 test-suite spec
     type:               exitcode-stdio-1.0
     main-is:            Main.hs
     hs-source-dirs:     tests
     other-modules:
+        Freckle.App.AsyncSpec
         Freckle.App.Bugsnag.MetaDataSpec
         Freckle.App.BugsnagSpec
         Freckle.App.CsvSpec
@@ -241,28 +232,25 @@
         Spec
         Paths_freckle_app
 
-    default-language:   Haskell2010
+    default-language:   GHC2021
     default-extensions:
-        BangPatterns DataKinds DeriveAnyClass DeriveFoldable DeriveFunctor
-        DeriveGeneric DeriveLift DeriveTraversable DerivingVia
-        DerivingStrategies FlexibleContexts FlexibleInstances GADTs
-        GeneralizedNewtypeDeriving LambdaCase MultiParamTypeClasses
-        NoImplicitPrelude NoMonomorphismRestriction OverloadedStrings
-        RankNTypes RecordWildCards ScopedTypeVariables StandaloneDeriving
-        TypeApplications TypeFamilies
+        DataKinds DeriveAnyClass DerivingVia DerivingStrategies GADTs
+        LambdaCase NoImplicitPrelude NoMonomorphismRestriction
+        OverloadedStrings RecordWildCards TypeFamilies
 
     ghc-options:
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missing-exported-signatures
-        -Wno-missing-import-lists -Wno-missing-local-signatures
-        -Wno-missing-safe-haskell-mode -Wno-monomorphism-restriction
-        -Wno-prepositive-qualified-module -Wno-safe -Wno-unsafe -threaded
-        -rtsopts -with-rtsopts=-N
+        -Wno-missing-import-lists -Wno-missing-kind-signatures
+        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
+        -Wno-monomorphism-restriction -Wno-prepositive-qualified-module
+        -Wno-safe -Wno-unsafe -threaded -rtsopts -with-rtsopts=-N
 
     build-depends:
         Blammo >=1.2.0.0,
         QuickCheck >=2.14.3,
         aeson >=2.0.3.0,
+        async >=2.2.4,
         base >=4.16.4.0 && <5,
         bugsnag >=1.1.0.0,
         bytestring >=0.11.4.0,
@@ -291,7 +279,5 @@
         zlib >=0.6.3.0
 
     if impl(ghc >=9.8)
-        ghc-options: -Wno-missing-role-annotations
-
-    if impl(ghc >=9.2)
-        ghc-options: -Wno-missing-kind-signatures
+        ghc-options:
+            -Wno-missing-role-annotations -Wno-missing-poly-kind-signatures
diff --git a/library/Freckle/App/Async.hs b/library/Freckle/App/Async.hs
--- a/library/Freckle/App/Async.hs
+++ b/library/Freckle/App/Async.hs
@@ -1,26 +1,43 @@
 module Freckle.App.Async
   ( async
+  , foldConcurrently
   , immortalCreate
   , immortalCreateLogged
-  )
-where
+  , ThreadContext (..)
+  , getThreadContext
+  , withThreadContext
+  ) where
 
 import Freckle.App.Prelude
 
-import Blammo.Logging
-import qualified Control.Immortal as Immortal
+import Blammo.Logging (Message (..), MonadLogger, MonadMask, logError, (.=))
+import Blammo.Logging qualified as Blammo
+import Control.Immortal qualified as Immortal
 import Control.Monad (forever)
-import qualified Data.Aeson.Compat as KeyMap
-import UnliftIO.Async (Async)
-import qualified UnliftIO.Async as UnliftIO
+import Data.Aeson (Value)
+import Data.Aeson.Compat (KeyMap)
+import Data.Aeson.Compat qualified as KeyMap
+import OpenTelemetry.Context qualified as OpenTelemetry
+import OpenTelemetry.Context.ThreadLocal qualified as OpenTelemetry
+import UnliftIO.Async (Async, conc, runConc)
+import UnliftIO.Async qualified as UnliftIO
 import UnliftIO.Concurrent (threadDelay)
 
 -- | 'UnliftIO.Async.async' but passing the thread context along
 async :: (MonadMask m, MonadUnliftIO m) => m a -> m (Async a)
 async f = do
-  tc <- liftIO $ KeyMap.toList <$> myThreadContext
-  UnliftIO.async $ withThreadContext tc f
+  context <- getThreadContext
+  UnliftIO.async $ withThreadContext context f
 
+-- | Run a list of actions concurrently
+--
+-- The forked threads will have the current thread context copied to them.
+foldConcurrently
+  :: (MonadUnliftIO m, MonadMask m, Monoid a, Foldable t) => t (m a) -> m a
+foldConcurrently xs = do
+  context <- getThreadContext
+  runConc $ foldMap (conc . withThreadContext context) xs
+
 -- | Wrapper around creating "Control.Immortal" processes
 --
 -- Features:
@@ -36,11 +53,11 @@
   -- ^ The action to run persistently
   -> m a
 immortalCreate onUnexpected act = do
-  tc <- liftIO $ KeyMap.toList <$> myThreadContext
+  context <- getThreadContext
 
   let
-    act' = withThreadContext tc act
-    onUnexpected' = withThreadContext tc . onUnexpected
+    act' = withThreadContext context act
+    onUnexpected' = withThreadContext context . onUnexpected
 
   void $ Immortal.create $ \thread -> do
     Immortal.onUnexpectedFinish thread onUnexpected' act'
@@ -53,3 +70,20 @@
 immortalCreateLogged = immortalCreate $ either logEx pure
  where
   logEx ex = logError $ "Unexpected Finish" :# ["exception" .= displayException ex]
+
+data ThreadContext = ThreadContext
+  { blammoContext :: KeyMap Value
+  , openTelemetryContext :: Maybe OpenTelemetry.Context
+  }
+
+getThreadContext :: MonadIO m => m ThreadContext
+getThreadContext =
+  ThreadContext
+    <$> liftIO Blammo.myThreadContext
+    <*> OpenTelemetry.lookupContext
+
+withThreadContext :: (MonadIO m, MonadMask m) => ThreadContext -> m a -> m a
+withThreadContext ThreadContext {blammoContext, openTelemetryContext} continue =
+  Blammo.withThreadContext (KeyMap.toList blammoContext) $ do
+    traverse_ @Maybe OpenTelemetry.attachContext openTelemetryContext
+    continue
diff --git a/library/Freckle/App/Bugsnag.hs b/library/Freckle/App/Bugsnag.hs
--- a/library/Freckle/App/Bugsnag.hs
+++ b/library/Freckle/App/Bugsnag.hs
@@ -19,7 +19,7 @@
 
 import Freckle.App.Prelude
 
-import qualified Control.Exception as Base (Exception)
+import Control.Exception qualified as Base (Exception)
 import Control.Lens (Lens', view)
 import Control.Monad.Catch (MonadMask)
 import Control.Monad.Reader (runReaderT)
@@ -29,9 +29,9 @@
 import Freckle.App.Async (async)
 import Freckle.App.Bugsnag.HttpException (httpExceptionBeforeNotify)
 import Freckle.App.Bugsnag.SqlError (sqlErrorBeforeNotify)
-import qualified Freckle.App.Env as Env
+import Freckle.App.Env qualified as Env
 import Network.Bugsnag hiding (notifyBugsnag, notifyBugsnagWith)
-import qualified Network.Bugsnag as Bugsnag
+import Network.Bugsnag qualified as Bugsnag
 import Yesod.Core.Lens (envL, siteL)
 import Yesod.Core.Types (HandlerData)
 
diff --git a/library/Freckle/App/Bugsnag/HttpException.hs b/library/Freckle/App/Bugsnag/HttpException.hs
--- a/library/Freckle/App/Bugsnag/HttpException.hs
+++ b/library/Freckle/App/Bugsnag/HttpException.hs
@@ -8,9 +8,9 @@
 import Freckle.App.Prelude
 
 import Data.Bugsnag (Exception (..))
-import qualified Data.ByteString.Char8 as BS8
+import Data.ByteString.Char8 qualified as BS8
 import Freckle.App.Exception.Types (AnnotatedException)
-import qualified Freckle.App.Exception.Types as Annotated
+import Freckle.App.Exception.Types qualified as Annotated
 import Network.Bugsnag
   ( BeforeNotify
   , setGroupingHash
diff --git a/library/Freckle/App/Bugsnag/MetaData.hs b/library/Freckle/App/Bugsnag/MetaData.hs
--- a/library/Freckle/App/Bugsnag/MetaData.hs
+++ b/library/Freckle/App/Bugsnag/MetaData.hs
@@ -18,7 +18,7 @@
 import Blammo.Logging (myThreadContext)
 import Control.Lens (Lens', lens, to, view, (<>~))
 import Data.Aeson (Value (..))
-import qualified Data.Aeson.KeyMap as KeyMap
+import Data.Aeson.KeyMap qualified as KeyMap
 import Data.Bugsnag (Event (..))
 import Data.String (fromString)
 import Freckle.App.Stats (HasStatsClient (..), tagsL)
diff --git a/library/Freckle/App/Bugsnag/SqlError.hs b/library/Freckle/App/Bugsnag/SqlError.hs
--- a/library/Freckle/App/Bugsnag/SqlError.hs
+++ b/library/Freckle/App/Bugsnag/SqlError.hs
@@ -17,7 +17,7 @@
   , constraintViolation
   )
 import Freckle.App.Exception.Types (AnnotatedException)
-import qualified Freckle.App.Exception.Types as Annotated
+import Freckle.App.Exception.Types qualified as Annotated
 import Network.Bugsnag
   ( BeforeNotify
   , setGroupingHash
diff --git a/library/Freckle/App/Csv.hs b/library/Freckle/App/Csv.hs
--- a/library/Freckle/App/Csv.hs
+++ b/library/Freckle/App/Csv.hs
@@ -40,9 +40,9 @@
   )
 import Data.Aeson (KeyValue (..), ToJSON (..), object, pairs, (.=))
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
-import qualified Data.Conduit.Combinators as Conduit
-import qualified Data.Conduit.Text as Conduit
+import Data.ByteString.Char8 qualified as BS8
+import Data.Conduit.Combinators qualified as Conduit
+import Data.Conduit.Text qualified as Conduit
 import Data.Csv
   ( DefaultOrdered
   , FromNamedRecord (..)
@@ -54,17 +54,17 @@
   , defaultOptions
   , headerOrder
   )
-import qualified Data.Csv.Incremental as CsvI
+import Data.Csv.Incremental qualified as CsvI
 import Data.Functor.Bind (Bind)
-import qualified Data.HashMap.Strict as HashMap
-import qualified Data.List.NonEmpty as NE
+import Data.HashMap.Strict qualified as HashMap
+import Data.List.NonEmpty qualified as NE
 import Data.Proxy (Proxy (Proxy))
 import Data.Sequence (Seq)
-import qualified Data.Sequence as Seq
+import Data.Sequence qualified as Seq
 import Data.Sequence.NonEmpty (NESeq)
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
-import qualified Data.Vector as V
+import Data.Text qualified as T
+import Data.Text.Encoding qualified as T
+import Data.Vector qualified as V
 
 -- | Treat CSV header line as 1
 --
diff --git a/library/Freckle/App/Database.hs b/library/Freckle/App/Database.hs
--- a/library/Freckle/App/Database.hs
+++ b/library/Freckle/App/Database.hs
@@ -39,14 +39,14 @@
 import Freckle.App.Prelude
 
 import Blammo.Logging
-import qualified Control.Immortal as Immortal
+import Control.Immortal qualified as Immortal
 import Control.Monad.Reader
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.HashMap.Strict as HashMap
+import Data.ByteString.Char8 qualified as BS8
+import Data.ByteString.Lazy qualified as BSL
+import Data.HashMap.Strict qualified as HashMap
 import Data.Pool
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Database.Persist.Postgresql
   ( SqlBackend
   , SqlPersistT
@@ -66,13 +66,13 @@
   )
 import Database.PostgreSQL.Simple.SqlQQ (sql)
 import Freckle.App.Env (Timeout (..))
-import qualified Freckle.App.Env as Env
+import Freckle.App.Env qualified as Env
 import Freckle.App.Exception.MonadUnliftIO
 import Freckle.App.OpenTelemetry
 import Freckle.App.Stats (HasStatsClient)
-import qualified Freckle.App.Stats as Stats
+import Freckle.App.Stats qualified as Stats
 import OpenTelemetry.Instrumentation.Persistent
-import qualified OpenTelemetry.Trace as Trace
+import OpenTelemetry.Trace qualified as Trace
 import System.Process.Typed (proc, readProcessStdout_)
 import UnliftIO.Concurrent (threadDelay)
 import UnliftIO.IORef
diff --git a/library/Freckle/App/Dotenv.hs b/library/Freckle/App/Dotenv.hs
--- a/library/Freckle/App/Dotenv.hs
+++ b/library/Freckle/App/Dotenv.hs
@@ -7,7 +7,7 @@
 
 import Freckle.App.Prelude
 
-import qualified Configuration.Dotenv as Dotenv
+import Configuration.Dotenv qualified as Dotenv
 import System.FilePath (takeDirectory, (</>))
 import UnliftIO.Directory (doesFileExist, getCurrentDirectory)
 
diff --git a/library/Freckle/App/Env.hs b/library/Freckle/App/Env.hs
--- a/library/Freckle/App/Env.hs
+++ b/library/Freckle/App/Env.hs
@@ -39,11 +39,11 @@
 
 import Control.Error.Util (note)
 import Data.Char (isDigit)
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Data.Time (defaultTimeLocale, parseTimeM)
 import Env hiding (flag)
-import qualified Env
-import qualified Prelude as Unsafe (read)
+import Env qualified
+import Prelude qualified as Unsafe (read)
 
 -- | Designates the value of a parameter when a flag is not provided.
 newtype Off a = Off a
diff --git a/library/Freckle/App/Exception.hs b/library/Freckle/App/Exception.hs
--- a/library/Freckle/App/Exception.hs
+++ b/library/Freckle/App/Exception.hs
@@ -12,7 +12,7 @@
 import Prelude
 
 import Control.Exception.Annotated (annotatedExceptionCallStack)
-import qualified Control.Exception.Annotated as AnnotatedException
+import Control.Exception.Annotated qualified as AnnotatedException
 import Control.Monad.Logger.Aeson (Message (..), (.=))
 import Data.Aeson (object)
 import Freckle.App.Exception.MonadUnliftIO
diff --git a/library/Freckle/App/Exception/MonadThrow.hs b/library/Freckle/App/Exception/MonadThrow.hs
--- a/library/Freckle/App/Exception/MonadThrow.hs
+++ b/library/Freckle/App/Exception/MonadThrow.hs
@@ -34,8 +34,8 @@
 import GHC.IO.Exception (userError)
 import GHC.Stack (withFrozenCallStack)
 
-import qualified Control.Exception.Annotated as Annotated
-import qualified Control.Monad.Catch
+import Control.Exception.Annotated qualified as Annotated
+import Control.Monad.Catch qualified
 
 -- Throws an exception, wrapped in 'AnnotatedException' which includes a call stack
 throwM :: forall e m a. (Exception e, MonadThrow m, HasCallStack) => e -> m a
diff --git a/library/Freckle/App/Exception/MonadUnliftIO.hs b/library/Freckle/App/Exception/MonadUnliftIO.hs
--- a/library/Freckle/App/Exception/MonadUnliftIO.hs
+++ b/library/Freckle/App/Exception/MonadUnliftIO.hs
@@ -35,8 +35,8 @@
 import System.IO (IO)
 import UnliftIO (MonadIO, MonadUnliftIO)
 
-import qualified Control.Exception.Annotated.UnliftIO as Annotated
-import qualified UnliftIO.Exception
+import Control.Exception.Annotated.UnliftIO qualified as Annotated
+import UnliftIO.Exception qualified
 
 -- Throws an exception, wrapped in 'AnnotatedException' which includes a call stack
 throwM :: forall e m a. (Exception e, MonadIO m, HasCallStack) => e -> m a
diff --git a/library/Freckle/App/Faktory/ProducerPool.hs b/library/Freckle/App/Faktory/ProducerPool.hs
--- a/library/Freckle/App/Faktory/ProducerPool.hs
+++ b/library/Freckle/App/Faktory/ProducerPool.hs
@@ -17,9 +17,9 @@
   , newPool
   , setNumStripes
   )
-import qualified Faktory.Producer as Faktory
-import qualified Faktory.Settings as Faktory
-import qualified Freckle.App.Env as Env
+import Faktory.Producer qualified as Faktory
+import Faktory.Settings qualified as Faktory
+import Freckle.App.Env qualified as Env
 import Yesod.Core.Lens (envL, siteL)
 import Yesod.Core.Types (HandlerData)
 
diff --git a/library/Freckle/App/Ghci.hs b/library/Freckle/App/Ghci.hs
--- a/library/Freckle/App/Ghci.hs
+++ b/library/Freckle/App/Ghci.hs
@@ -9,7 +9,7 @@
 import Database.Persist.Postgresql (runSqlPool)
 import Database.Persist.Sql (SqlBackend)
 import Freckle.App.Database (makePostgresPool)
-import qualified Freckle.App.Dotenv as Dotenv
+import Freckle.App.Dotenv qualified as Dotenv
 
 -- | Run a db action against .env
 runDB :: ReaderT SqlBackend IO b -> IO b
diff --git a/library/Freckle/App/Http.hs b/library/Freckle/App/Http.hs
--- a/library/Freckle/App/Http.hs
+++ b/library/Freckle/App/Http.hs
@@ -32,6 +32,7 @@
   , setRequestBodyJSON
   , setRequestBodyURLEncoded
   , setRequestCheckStatus
+  , setRequestMethod
   , setRequestPath
   , disableRequestDecompress
 
@@ -71,6 +72,7 @@
   , statusIsRedirection
   , statusIsClientError
   , statusIsServerError
+  , StdMethod (..)
   ) where
 
 import Freckle.App.Prelude
@@ -82,17 +84,18 @@
 import Control.Monad.Validate (ValidateT)
 import Control.Monad.Writer (WriterT)
 import Data.Aeson (FromJSON)
-import qualified Data.Aeson as Aeson
-import qualified Data.ByteString as BS
+import Data.Aeson qualified as Aeson
+import Data.ByteString qualified as BS
 import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy.Char8 as BSL8
-import qualified Data.List.NonEmpty as NE
+import Data.ByteString.Lazy.Char8 qualified as BSL8
+import Data.List.NonEmpty qualified as NE
 import Freckle.App.Http.Paginate
 import Freckle.App.Http.Retry
-import qualified Network.HTTP.Client as HTTP (Request (..))
+import Network.HTTP.Client qualified as HTTP (Request (..))
 import Network.HTTP.Conduit (HttpExceptionContent (..))
-import Network.HTTP.Simple hiding (httpLbs, httpNoBody)
-import qualified Network.HTTP.Simple as HTTP
+import Network.HTTP.Simple hiding (httpLbs, httpNoBody, setRequestMethod)
+import Network.HTTP.Simple qualified as HTTP
+import Network.HTTP.Types (StdMethod (..), renderStdMethod)
 import Network.HTTP.Types.Header (hAccept, hAuthorization)
 import Network.HTTP.Types.Status
   ( Status
@@ -250,6 +253,9 @@
 
 addBearerAuthorizationHeader :: BS.ByteString -> Request -> Request
 addBearerAuthorizationHeader = addRequestHeader hAuthorization . ("Bearer " <>)
+
+setRequestMethod :: StdMethod -> Request -> Request
+setRequestMethod method req = req {HTTP.method = renderStdMethod method}
 
 disableRequestDecompress :: Request -> Request
 disableRequestDecompress req =
diff --git a/library/Freckle/App/Http/Cache.hs b/library/Freckle/App/Http/Cache.hs
--- a/library/Freckle/App/Http/Cache.hs
+++ b/library/Freckle/App/Http/Cache.hs
@@ -16,9 +16,9 @@
 
 import Blammo.Logging (Message (..), (.=))
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.CaseInsensitive as CI
+import Data.ByteString.Char8 qualified as BS8
+import Data.ByteString.Lazy qualified as BSL
+import Data.CaseInsensitive qualified as CI
 import Data.List.Extra (firstJust)
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
@@ -28,7 +28,7 @@
 import Freckle.App.Http.Header
 import Freckle.App.Memcached
 import Network.HTTP.Client (Request, Response)
-import qualified Network.HTTP.Client as HTTP
+import Network.HTTP.Client qualified as HTTP
 import Network.HTTP.Simple
   ( addRequestHeader
   , getRequestHeader
diff --git a/library/Freckle/App/Http/Cache/Gzip.hs b/library/Freckle/App/Http/Cache/Gzip.hs
--- a/library/Freckle/App/Http/Cache/Gzip.hs
+++ b/library/Freckle/App/Http/Cache/Gzip.hs
@@ -23,11 +23,11 @@
 
 import Codec.Serialise (Serialise)
 import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Lazy qualified as BSL
 import Freckle.App.Http (disableRequestDecompress)
 import Freckle.App.Http.Header
 import Network.HTTP.Client (Request, Response)
-import qualified Network.HTTP.Client.Internal as HTTP
+import Network.HTTP.Client.Internal qualified as HTTP
 
 newtype PotentiallyGzipped a = PotentiallyGzipped
   { unwrap :: a
diff --git a/library/Freckle/App/Http/Cache/Memcached.hs b/library/Freckle/App/Http/Cache/Memcached.hs
--- a/library/Freckle/App/Http/Cache/Memcached.hs
+++ b/library/Freckle/App/Http/Cache/Memcached.hs
@@ -13,16 +13,16 @@
 
 import Blammo.Logging (MonadLogger, logDebugNS, logWarnNS)
 import Codec.Serialise (Serialise (..), deserialiseOrFail, serialise)
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Lazy qualified as BSL
 import Data.CaseInsensitive (CI)
-import qualified Data.CaseInsensitive as CI
+import Data.CaseInsensitive qualified as CI
 import Database.Memcache.Types (Value)
 import Freckle.App.Http.Cache
 import Freckle.App.Memcached
-import qualified Freckle.App.Memcached.Client as Memcached
+import Freckle.App.Memcached.Client qualified as Memcached
 import Freckle.App.OpenTelemetry (MonadTracer)
 import Network.HTTP.Client (Request)
-import qualified Network.HTTP.Client.Internal as HTTP
+import Network.HTTP.Client.Internal qualified as HTTP
 import Network.HTTP.Types.Header (ResponseHeaders)
 import Network.HTTP.Types.Status (Status (..))
 import Network.HTTP.Types.Version (HttpVersion (..))
diff --git a/library/Freckle/App/Http/Cache/State.hs b/library/Freckle/App/Http/Cache/State.hs
--- a/library/Freckle/App/Http/Cache/State.hs
+++ b/library/Freckle/App/Http/Cache/State.hs
@@ -22,7 +22,7 @@
 import Control.Monad.State
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
-import qualified Data.Text.IO as T
+import Data.Text.IO qualified as T
 import Freckle.App.Http.Cache
 import Freckle.App.Memcached.CacheKey
 import Freckle.App.Memcached.CacheTTL
diff --git a/library/Freckle/App/Http/Header.hs b/library/Freckle/App/Http/Header.hs
--- a/library/Freckle/App/Http/Header.hs
+++ b/library/Freckle/App/Http/Header.hs
@@ -10,7 +10,7 @@
 import Freckle.App.Prelude
 
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
+import Data.ByteString.Char8 qualified as BS8
 import Data.Char (isSpace)
 import Network.HTTP.Client (Request, Response, requestHeaders, responseHeaders)
 import Network.HTTP.Simple (getRequestHeader, getResponseHeader)
diff --git a/library/Freckle/App/Http/Retry.hs b/library/Freckle/App/Http/Retry.hs
--- a/library/Freckle/App/Http/Retry.hs
+++ b/library/Freckle/App/Http/Retry.hs
@@ -7,7 +7,7 @@
 import Freckle.App.Prelude
 
 import Control.Retry
-import qualified Data.ByteString.Char8 as BS8
+import Data.ByteString.Char8 qualified as BS8
 import Network.HTTP.Client (Request (..))
 import Network.HTTP.Simple
 import Network.HTTP.Types.Status (status429)
diff --git a/library/Freckle/App/Json/Empty.hs b/library/Freckle/App/Json/Empty.hs
--- a/library/Freckle/App/Json/Empty.hs
+++ b/library/Freckle/App/Json/Empty.hs
@@ -5,7 +5,12 @@
 
 import Freckle.App.Prelude
 
-import Autodocodec (Autodocodec (..), HasCodec (..), HasObjectCodec (..), object)
+import Autodocodec
+  ( Autodocodec (..)
+  , HasCodec (..)
+  , HasObjectCodec (..)
+  , object
+  )
 import Autodocodec.OpenAPI ()
 import Data.Aeson (FromJSON, ToJSON)
 import Data.OpenApi (ToSchema (..))
diff --git a/library/Freckle/App/Kafka/Consumer.hs b/library/Freckle/App/Kafka/Consumer.hs
--- a/library/Freckle/App/Kafka/Consumer.hs
+++ b/library/Freckle/App/Kafka/Consumer.hs
@@ -15,10 +15,10 @@
 import Control.Monad (forever)
 import Data.Aeson
 import Data.ByteString (ByteString)
-import qualified Data.List.NonEmpty as NE
-import qualified Data.Map.Strict as Map
-import qualified Data.Text as T
-import qualified Env
+import Data.List.NonEmpty qualified as NE
+import Data.Map.Strict qualified as Map
+import Data.Text qualified as T
+import Env qualified
 import Freckle.App.Async
 import Freckle.App.Env
 import Freckle.App.Exception (annotatedExceptionMessageFrom)
@@ -32,7 +32,7 @@
   , runConsumer
   , subscription
   )
-import qualified Kafka.Consumer as Kafka
+import Kafka.Consumer qualified as Kafka
 import UnliftIO.Exception (bracket)
 
 data KafkaConsumerConfig = KafkaConsumerConfig
diff --git a/library/Freckle/App/Kafka/Producer.hs b/library/Freckle/App/Kafka/Producer.hs
--- a/library/Freckle/App/Kafka/Producer.hs
+++ b/library/Freckle/App/Kafka/Producer.hs
@@ -18,16 +18,16 @@
 import Control.Lens (Lens', view)
 import Data.Aeson (ToJSON, encode)
 import Data.ByteString.Lazy (toStrict)
-import qualified Data.HashMap.Strict as HashMap
-import qualified Data.List.NonEmpty as NE
+import Data.HashMap.Strict qualified as HashMap
+import Data.List.NonEmpty qualified as NE
 import Data.Pool (Pool)
-import qualified Data.Pool as Pool
-import qualified Data.Text as T
+import Data.Pool qualified as Pool
+import Data.Text qualified as T
 import Freckle.App.Async (async)
-import qualified Freckle.App.Env as Env
+import Freckle.App.Env qualified as Env
 import Freckle.App.OpenTelemetry
 import Kafka.Producer
-import qualified OpenTelemetry.Trace as Trace
+import OpenTelemetry.Trace qualified as Trace
 import UnliftIO (withRunInIO)
 import Yesod.Core.Lens
 import Yesod.Core.Types (HandlerData)
@@ -92,13 +92,13 @@
   -> KafkaProducerPoolConfig
   -> IO (Pool KafkaProducer)
 createKafkaProducerPool addresses KafkaProducerPoolConfig {..} =
-  Pool.newPool
-    $ Pool.setNumStripes (Just kafkaProducerPoolConfigStripes)
-    $ Pool.defaultPoolConfig
-      mkProducer
-      closeProducer
-      (realToFrac kafkaProducerPoolConfigIdleTimeout)
-      kafkaProducerPoolConfigSize
+  Pool.newPool $
+    Pool.setNumStripes (Just kafkaProducerPoolConfigStripes) $
+      Pool.defaultPoolConfig
+        mkProducer
+        closeProducer
+        (realToFrac kafkaProducerPoolConfigIdleTimeout)
+        kafkaProducerPoolConfigSize
  where
   mkProducer =
     either
diff --git a/library/Freckle/App/Memcached.hs b/library/Freckle/App/Memcached.hs
--- a/library/Freckle/App/Memcached.hs
+++ b/library/Freckle/App/Memcached.hs
@@ -28,14 +28,14 @@
 import Codec.Serialise (Serialise, deserialiseOrFail, serialise)
 import Data.Aeson
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Lazy qualified as BSL
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
 import Freckle.App.Exception (annotatedExceptionMessage)
 import Freckle.App.Memcached.CacheKey
 import Freckle.App.Memcached.CacheTTL
 import Freckle.App.Memcached.Client (HasMemcachedClient (..))
-import qualified Freckle.App.Memcached.Client as Memcached
+import Freckle.App.Memcached.Client qualified as Memcached
 import Freckle.App.Memcached.MD5
 import Freckle.App.OpenTelemetry
 
diff --git a/library/Freckle/App/Memcached/CacheKey.hs b/library/Freckle/App/Memcached/CacheKey.hs
--- a/library/Freckle/App/Memcached/CacheKey.hs
+++ b/library/Freckle/App/Memcached/CacheKey.hs
@@ -8,7 +8,7 @@
 import Freckle.App.Prelude
 
 import Data.Char (isControl, isSpace)
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Database.Memcache.Types (Key)
 import OpenTelemetry.Trace (ToAttribute (..))
 
diff --git a/library/Freckle/App/Memcached/Client.hs b/library/Freckle/App/Memcached/Client.hs
--- a/library/Freckle/App/Memcached/Client.hs
+++ b/library/Freckle/App/Memcached/Client.hs
@@ -12,14 +12,14 @@
 import Freckle.App.Prelude
 
 import Control.Lens (Lens', view, _1)
-import qualified Data.HashMap.Strict as HashMap
-import qualified Database.Memcache.Client as Memcache
+import Data.HashMap.Strict qualified as HashMap
+import Database.Memcache.Client qualified as Memcache
 import Database.Memcache.Types (Value, Version)
 import Freckle.App.Memcached.CacheKey
 import Freckle.App.Memcached.CacheTTL
 import Freckle.App.Memcached.Servers
 import Freckle.App.OpenTelemetry
-import qualified OpenTelemetry.Trace as Trace
+import OpenTelemetry.Trace qualified as Trace
 import UnliftIO.Exception (finally)
 import Yesod.Core.Lens
 import Yesod.Core.Types (HandlerData)
diff --git a/library/Freckle/App/Memcached/MD5.hs b/library/Freckle/App/Memcached/MD5.hs
--- a/library/Freckle/App/Memcached/MD5.hs
+++ b/library/Freckle/App/Memcached/MD5.hs
@@ -6,8 +6,8 @@
 
 import Freckle.App.Prelude
 
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.Digest.Pure.MD5 as Digest
+import Data.ByteString.Lazy qualified as BSL
+import Data.Digest.Pure.MD5 qualified as Digest
 import Freckle.App.Memcached.CacheKey
 
 md5CacheKey :: Show a => a -> CacheKey
diff --git a/library/Freckle/App/Memcached/Servers.hs b/library/Freckle/App/Memcached/Servers.hs
--- a/library/Freckle/App/Memcached/Servers.hs
+++ b/library/Freckle/App/Memcached/Servers.hs
@@ -29,8 +29,8 @@
 import Freckle.App.Prelude
 
 import Control.Error.Util (note)
-import qualified Data.Text as T
-import qualified Database.Memcache.Client as Memcache
+import Data.Text qualified as T
+import Database.Memcache.Client qualified as Memcache
 import Network.URI (URI (..), URIAuth (..), parseAbsoluteURI)
 
 newtype MemcachedServers = MemcachedServers
diff --git a/library/Freckle/App/OpenTelemetry.hs b/library/Freckle/App/OpenTelemetry.hs
--- a/library/Freckle/App/OpenTelemetry.hs
+++ b/library/Freckle/App/OpenTelemetry.hs
@@ -67,14 +67,14 @@
 import Freckle.App.Prelude
 
 import Data.ByteString (ByteString)
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
 import OpenTelemetry.Context (lookupSpan)
 import OpenTelemetry.Context.ThreadLocal (getContext)
 import OpenTelemetry.Trace hiding (inSpan)
 import OpenTelemetry.Trace.Core (getSpanContext)
-import qualified OpenTelemetry.Trace.Core as Trace (SpanContext (..))
+import OpenTelemetry.Trace.Core qualified as Trace (SpanContext (..))
 import OpenTelemetry.Trace.Id
   ( Base (..)
   , SpanId
diff --git a/library/Freckle/App/OpenTelemetry/Context.hs b/library/Freckle/App/OpenTelemetry/Context.hs
--- a/library/Freckle/App/OpenTelemetry/Context.hs
+++ b/library/Freckle/App/OpenTelemetry/Context.hs
@@ -14,7 +14,7 @@
 import Data.Aeson (FromJSON, ToJSON)
 import Data.ByteString (ByteString)
 import Data.CaseInsensitive (CI)
-import qualified Data.CaseInsensitive as CI
+import Data.CaseInsensitive qualified as CI
 import Faktory.Job (Job, custom, jobOptions)
 import Faktory.Job.Custom (fromCustom, toCustom)
 import Faktory.JobOptions (JobOptions (..))
diff --git a/library/Freckle/App/OpenTelemetry/Http.hs b/library/Freckle/App/OpenTelemetry/Http.hs
--- a/library/Freckle/App/OpenTelemetry/Http.hs
+++ b/library/Freckle/App/OpenTelemetry/Http.hs
@@ -7,9 +7,9 @@
 
 import Freckle.App.Prelude
 
-import qualified Data.CaseInsensitive as CI
-import qualified Data.HashMap.Strict as HashMap
-import qualified Data.Text as T
+import Data.CaseInsensitive qualified as CI
+import Data.HashMap.Strict qualified as HashMap
+import Data.Text qualified as T
 import Data.Text.Encoding (decodeUtf8With)
 import Data.Text.Encoding.Error (lenientDecode)
 import Freckle.App.OpenTelemetry
@@ -18,7 +18,7 @@
   , clientSpanArguments
   )
 import Network.HTTP.Client (Request, Response)
-import qualified Network.HTTP.Client as HTTP
+import Network.HTTP.Client qualified as HTTP
 import Network.HTTP.Types.Status (statusCode)
 import OpenTelemetry.Attributes (Attribute, ToAttribute (..))
 
diff --git a/library/Freckle/App/OpenTelemetry/ThreadContext.hs b/library/Freckle/App/OpenTelemetry/ThreadContext.hs
--- a/library/Freckle/App/OpenTelemetry/ThreadContext.hs
+++ b/library/Freckle/App/OpenTelemetry/ThreadContext.hs
@@ -8,7 +8,7 @@
 
 import Blammo.Logging (MonadMask, withThreadContext)
 import Data.Aeson ((.=))
-import qualified Data.Aeson.Key as Key
+import Data.Aeson.Key qualified as Key
 import Data.Aeson.Types (Pair)
 import Freckle.App.OpenTelemetry (getCurrentSpanContext)
 import OpenTelemetry.Trace.Core (SpanContext (..))
diff --git a/library/Freckle/App/Prelude.hs b/library/Freckle/App/Prelude.hs
--- a/library/Freckle/App/Prelude.hs
+++ b/library/Freckle/App/Prelude.hs
@@ -88,7 +88,7 @@
     -- ** 'UTCTime'
   , getCurrentTime
 
-   -- * Exceptions
+    -- * Exceptions
   , throwM
   , throwString
   , fromJustNoteM
@@ -112,14 +112,14 @@
   , head
   , init
   , last
+#if MIN_VERSION_base(4,18,0)
+  , liftA2
+#endif
   , maximum
   , minimum
   , read
   , tail
   , (!!)
-#if MIN_VERSION_base(4,18,0)
-  , liftA2
-#endif
   )
 
 -- Commonly used types (and their commonly used functions)
@@ -135,10 +135,10 @@
 import Data.List.NonEmpty (NonEmpty)
 import Data.Map.Strict (Map)
 import Data.Set (Set)
-import Freckle.App.Exception
 import Data.Text (Text, pack, unpack)
 import Data.Time (NominalDiffTime, UTCTime, getCurrentTime)
 import Data.Vector (Vector)
+import Freckle.App.Exception
 import GHC.Generics (Generic)
 
 -- Safe alternatives to prelude functions
diff --git a/library/Freckle/App/Random.hs b/library/Freckle/App/Random.hs
--- a/library/Freckle/App/Random.hs
+++ b/library/Freckle/App/Random.hs
@@ -16,7 +16,7 @@
 import Data.STRef (newSTRef, readSTRef, writeSTRef)
 import Numeric.Natural (Natural)
 
-import qualified Data.Set as Set
+import Data.Set qualified as Set
 
 -- | A possibly-empty contiguous range of integers
 data Range i
diff --git a/library/Freckle/App/Scientist.hs b/library/Freckle/App/Scientist.hs
--- a/library/Freckle/App/Scientist.hs
+++ b/library/Freckle/App/Scientist.hs
@@ -5,7 +5,7 @@
 import Freckle.App.Prelude
 
 import Freckle.App.Stats (HasStatsClient)
-import qualified Freckle.App.Stats as Stats
+import Freckle.App.Stats qualified as Stats
 import Scientist
 import Scientist.Duration
 
diff --git a/library/Freckle/App/Stats.hs b/library/Freckle/App/Stats.hs
--- a/library/Freckle/App/Stats.hs
+++ b/library/Freckle/App/Stats.hs
@@ -44,10 +44,10 @@
 import Data.String
 import Data.Time (diffUTCTime)
 import Freckle.App.Ecs
-import qualified Freckle.App.Env as Env
-import qualified Network.StatsD.Datadog as Datadog
+import Freckle.App.Env qualified as Env
+import Network.StatsD.Datadog qualified as Datadog
 import System.IO (hPutStrLn, stderr)
-import qualified System.Metrics.Gauge as EKG
+import System.Metrics.Gauge qualified as EKG
 import UnliftIO.Exception (bracket_)
 import Yesod.Core.Lens
 import Yesod.Core.Types (HandlerData)
@@ -178,9 +178,9 @@
 withGauge getGauge f = do
   gauge' <- lookupGauge getGauge
   bracket_ (inc gauge') (dec gauge') f
-  where
-    inc = incGauge
-    dec = decGauge
+ where
+  inc = incGauge
+  dec = decGauge
 
 lookupGauge
   :: (MonadReader app m, HasStatsClient app)
diff --git a/library/Freckle/App/Stats/Rts.hs b/library/Freckle/App/Stats/Rts.hs
--- a/library/Freckle/App/Stats/Rts.hs
+++ b/library/Freckle/App/Stats/Rts.hs
@@ -5,12 +5,12 @@
 
 import Freckle.App.Prelude
 
-import qualified Control.Immortal as Immortal
-import qualified Data.HashMap.Strict as HashMap
+import Control.Immortal qualified as Immortal
+import Data.HashMap.Strict qualified as HashMap
 import Freckle.App.Stats (HasStatsClient)
-import qualified Freckle.App.Stats as Stats
-import qualified System.Metrics as Ekg
-import qualified System.Metrics.Distribution.Internal as Ekg
+import Freckle.App.Stats qualified as Stats
+import System.Metrics qualified as Ekg
+import System.Metrics.Distribution.Internal qualified as Ekg
 import UnliftIO.Concurrent (threadDelay)
 
 -- | Initialize a thread to poll RTS stats
diff --git a/library/Freckle/App/Test.hs b/library/Freckle/App/Test.hs
--- a/library/Freckle/App/Test.hs
+++ b/library/Freckle/App/Test.hs
@@ -37,8 +37,8 @@
 import Control.Lens (view)
 import Control.Monad.Base
 import Control.Monad.Catch (ExitCase (..), MonadCatch, MonadThrow, mask)
-import qualified Control.Monad.Catch
-import qualified Control.Monad.Fail as Fail
+import Control.Monad.Catch qualified
+import Control.Monad.Fail qualified as Fail
 import Control.Monad.Primitive
 import Control.Monad.Random (MonadRandom (..))
 import Control.Monad.Reader
@@ -50,13 +50,13 @@
   , MonadSqlTx (..)
   , runDB
   )
-import qualified Freckle.App.Dotenv as Dotenv
-import qualified Freckle.App.Exception.MonadThrow as MonadThrow
+import Freckle.App.Dotenv qualified as Dotenv
+import Freckle.App.Exception.MonadThrow qualified as MonadThrow
 import Freckle.App.OpenTelemetry
-import qualified Test.Hspec as Hspec hiding (expectationFailure)
+import Test.Hspec qualified as Hspec hiding (expectationFailure)
 import Test.Hspec.Core.Spec (Arg, Example, SpecWith, evaluateExample)
-import qualified Test.Hspec.Expectations.Lifted as Hspec (expectationFailure)
-import qualified UnliftIO.Exception as UnliftIO
+import Test.Hspec.Expectations.Lifted qualified as Hspec (expectationFailure)
+import UnliftIO.Exception qualified as UnliftIO
 
 -- | An Hspec example over some @App@ value
 --
diff --git a/library/Freckle/App/Test/DocTest.hs b/library/Freckle/App/Test/DocTest.hs
--- a/library/Freckle/App/Test/DocTest.hs
+++ b/library/Freckle/App/Test/DocTest.hs
@@ -13,10 +13,10 @@
 
 import Control.Monad (filterM)
 import Data.Aeson
-import qualified Data.Text as T
-import qualified Data.Text.IO as T
+import Data.Text qualified as T
+import Data.Text.IO qualified as T
 import Data.Yaml (decodeFileThrow)
-import qualified Test.DocTest as DocTest
+import Test.DocTest qualified as DocTest
 import "Glob" System.FilePath.Glob (globDir1)
 
 -- | Run doctest on files in the given directory
diff --git a/library/Freckle/App/Test/Hspec/Runner.hs b/library/Freckle/App/Test/Hspec/Runner.hs
--- a/library/Freckle/App/Test/Hspec/Runner.hs
+++ b/library/Freckle/App/Test/Hspec/Runner.hs
@@ -29,7 +29,7 @@
   , hspecWithResult
   , readConfig
   )
-import qualified Prelude as Unsafe (read)
+import Prelude qualified as Unsafe (read)
 
 run :: String -> Spec -> IO ()
 run = runWith defaultConfig
diff --git a/library/Freckle/App/Test/Http.hs b/library/Freckle/App/Test/Http.hs
--- a/library/Freckle/App/Test/Http.hs
+++ b/library/Freckle/App/Test/Http.hs
@@ -49,13 +49,13 @@
 import Control.Monad (filterM)
 import Control.Monad.Reader (runReaderT)
 import Data.Aeson (ToJSON, encode)
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Lazy qualified as BSL
 import Data.List (stripPrefix)
 import Data.String (IsString (..))
 import Freckle.App.Http (MonadHttp (..))
 import Freckle.App.Test.Http.MatchRequest
 import Network.HTTP.Client (Request, Response)
-import qualified Network.HTTP.Client.Internal as HTTP
+import Network.HTTP.Client.Internal qualified as HTTP
 import Network.HTTP.Types.Header (ResponseHeaders, hAccept, hContentType)
 import Network.HTTP.Types.Status (Status, status200)
 import System.Directory (doesFileExist)
@@ -86,11 +86,11 @@
   maybe (error errorMessage) (toResponse req) $ headMay matched
  where
   (unmatched, matched) =
-    partitionEithers $
-      map
+    partitionEithers
+      $ map
         ( \stub ->
-            bimap (stub,) (const stub.response) $
-              matchRequest req stub.match
+            bimap (stub,) (const stub.response)
+              $ matchRequest req stub.match
         )
         stubs
 
diff --git a/library/Freckle/App/Test/Http/MatchRequest.hs b/library/Freckle/App/Test/Http/MatchRequest.hs
--- a/library/Freckle/App/Test/Http/MatchRequest.hs
+++ b/library/Freckle/App/Test/Http/MatchRequest.hs
@@ -24,13 +24,13 @@
 import Freckle.App.Prelude
 
 import Data.ByteString (ByteString)
-import qualified Data.ByteString.Char8 as BS8
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString.Char8 qualified as BS8
+import Data.ByteString.Lazy qualified as BSL
 import Data.List (isPrefixOf)
 import Data.List.NonEmpty (NonEmpty (..))
-import qualified Data.List.NonEmpty as NE
+import Data.List.NonEmpty qualified as NE
 import Network.HTTP.Client (Request, RequestBody (..), parseRequest_)
-import qualified Network.HTTP.Client.Internal as HTTP
+import Network.HTTP.Client.Internal qualified as HTTP
 import Network.HTTP.Types.Header (Header, RequestHeaders)
 import Network.HTTP.Types.Method (Method)
 
@@ -80,12 +80,12 @@
 
 hasExplicitPath :: Bool -> ByteString -> Int -> String -> Bool
 hasExplicitPath secure host port url =
-  any ((`isPrefixOf` url) . toUrlPrefix) $
-    concat
-      [ [Just port]
-      , Nothing <$ guard (secure && port == 443)
-      , Nothing <$ guard (not secure && port == 80)
-      ]
+  any
+    (any ((`isPrefixOf` url) . toUrlPrefix))
+    [ [Just port]
+    , Nothing <$ guard (secure && port == 443)
+    , Nothing <$ guard (not secure && port == 80)
+    ]
  where
   toUrlPrefix mport =
     mconcat
diff --git a/library/Freckle/App/Test/Yesod.hs b/library/Freckle/App/Test/Yesod.hs
--- a/library/Freckle/App/Test/Yesod.hs
+++ b/library/Freckle/App/Test/Yesod.hs
@@ -81,14 +81,14 @@
 import Control.Monad.Validate (ValidateT)
 import Data.Aeson (FromJSON, eitherDecode)
 import Data.BCP47 (BCP47)
-import qualified Data.BCP47 as BCP47
+import Data.BCP47 qualified as BCP47
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.ByteString.Lazy as BSL
+import Data.ByteString qualified as BS
+import Data.ByteString.Lazy qualified as BSL
 import Data.CaseInsensitive (CI)
 import Data.Csv (FromNamedRecord, decodeByName)
-import qualified Data.Text as T
-import qualified Data.Vector as V
+import Data.Text qualified as T
+import Data.Vector qualified as V
 import Network.HTTP.Types.Header (hAccept, hAcceptLanguage, hContentType)
 import Network.Wai.Test (SResponse (..))
 import Test.Hspec.Expectations.Lifted (expectationFailure)
@@ -110,7 +110,7 @@
   , setUrl
   , withResponse
   )
-import qualified Yesod.Test
+import Yesod.Test qualified
 
 class (MonadIO m, Yesod site) => MonadYesodExample site m | m -> site where
   liftYesodExample :: YesodExample site a -> m a
diff --git a/library/Freckle/App/Yesod.hs b/library/Freckle/App/Yesod.hs
--- a/library/Freckle/App/Yesod.hs
+++ b/library/Freckle/App/Yesod.hs
@@ -16,9 +16,9 @@
   , withException
   )
 import Freckle.App.Stats (HasStatsClient)
-import qualified Freckle.App.Stats as Stats
+import Freckle.App.Stats qualified as Stats
 import Network.HTTP.Types (ResponseHeaders, status503)
-import qualified Network.Wai as W
+import Network.Wai qualified as W
 import Yesod.Core.Handler (HandlerFor, sendWaiResponse)
 import Yesod.Core.Types (HandlerContents)
 
diff --git a/library/Freckle/App/Yesod/Routes.hs b/library/Freckle/App/Yesod/Routes.hs
--- a/library/Freckle/App/Yesod/Routes.hs
+++ b/library/Freckle/App/Yesod/Routes.hs
@@ -4,7 +4,7 @@
 
 import Freckle.App.Prelude
 
-import qualified Language.Haskell.TH as TH
+import Language.Haskell.TH qualified as TH
 import Yesod.Routes.TH.Types
 
 -- | Lambdacase expression to print route names
diff --git a/library/Network/Wai/Middleware/Cors.hs b/library/Network/Wai/Middleware/Cors.hs
--- a/library/Network/Wai/Middleware/Cors.hs
+++ b/library/Network/Wai/Middleware/Cors.hs
@@ -6,8 +6,8 @@
 import Freckle.App.Prelude
 
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
-import qualified Data.CaseInsensitive as CI
+import Data.ByteString qualified as BS
+import Data.CaseInsensitive qualified as CI
 import Network.HTTP.Types (ResponseHeaders)
 import Network.HTTP.Types.Status (status200)
 import Network.Wai
diff --git a/library/Network/Wai/Middleware/Stats.hs b/library/Network/Wai/Middleware/Stats.hs
--- a/library/Network/Wai/Middleware/Stats.hs
+++ b/library/Network/Wai/Middleware/Stats.hs
@@ -9,9 +9,9 @@
 import Control.Lens ((^.))
 import Control.Monad.Reader (runReaderT)
 import Data.Aeson ((.=))
-import qualified Data.Aeson.Key as Key
+import Data.Aeson.Key qualified as Key
 import Freckle.App.Stats (HasStatsClient (..), tagsL)
-import qualified Freckle.App.Stats as Stats
+import Freckle.App.Stats qualified as Stats
 import Network.HTTP.Types.Status (Status (..))
 import Network.Wai (Middleware, Request, requestMethod, responseStatus)
 
diff --git a/package.yaml b/package.yaml
--- a/package.yaml
+++ b/package.yaml
@@ -1,5 +1,5 @@
 name: freckle-app
-version: 1.18.0.0
+version: 1.18.1.0
 maintainer: Freckle Education
 category: Utils
 github: freckle/freckle-app
@@ -13,6 +13,8 @@
 extra-source-files:
   - package.yaml
 
+language: GHC2021
+
 ghc-options:
   - -fignore-optim-changes
   - -fwrite-ide-info
@@ -20,6 +22,7 @@
   - -Wno-all-missed-specialisations
   - -Wno-missing-exported-signatures # re-enables missing-signatures
   - -Wno-missing-import-lists
+  - -Wno-missing-kind-signatures
   - -Wno-missing-local-signatures
   - -Wno-missing-safe-haskell-mode
   - -Wno-monomorphism-restriction
@@ -31,38 +34,22 @@
   - condition: "impl(ghc >= 9.8)"
     ghc-options:
       - -Wno-missing-role-annotations
-  - condition: "impl(ghc >= 9.2)"
-    ghc-options:
-      - -Wno-missing-kind-signatures
+      - -Wno-missing-poly-kind-signatures
 
 dependencies:
   - base < 5
 
 default-extensions:
-  - BangPatterns
   - DataKinds
   - DeriveAnyClass
-  - DeriveFoldable
-  - DeriveFunctor
-  - DeriveGeneric
-  - DeriveLift
-  - DeriveTraversable
   - DerivingVia
   - DerivingStrategies
-  - FlexibleContexts
-  - FlexibleInstances
   - GADTs
-  - GeneralizedNewtypeDeriving
   - LambdaCase
-  - MultiParamTypeClasses
   - NoImplicitPrelude
   - NoMonomorphismRestriction
   - OverloadedStrings
-  - RankNTypes
   - RecordWildCards
-  - ScopedTypeVariables
-  - StandaloneDeriving
-  - TypeApplications
   - TypeFamilies
 
 library:
@@ -158,6 +145,7 @@
       - Blammo
       - QuickCheck
       - aeson
+      - async
       - bugsnag
       - bytestring
       - cassava
diff --git a/tests/Freckle/App/AsyncSpec.hs b/tests/Freckle/App/AsyncSpec.hs
new file mode 100644
--- /dev/null
+++ b/tests/Freckle/App/AsyncSpec.hs
@@ -0,0 +1,29 @@
+module Freckle.App.AsyncSpec
+  ( spec
+  ) where
+
+import Freckle.App.Prelude
+
+import Blammo.Logging
+import Blammo.Logging qualified as Blammo
+import Control.Concurrent.Async (wait)
+import Data.Aeson.KeyMap qualified as KeyMap
+import Freckle.App.Async (async)
+import OpenTelemetry.Context qualified as OpenTelemetry
+import OpenTelemetry.Context.ThreadLocal qualified as OpenTelemetry
+import Test.Hspec
+
+spec :: Spec
+spec = do
+  describe "async" $ do
+    it @(IO ()) "preserves Blammo context" $ do
+      let kvs = ["a" .= ("xyz" :: Text)]
+      Blammo.withThreadContext kvs $
+        (wait =<< async Blammo.myThreadContext)
+          `shouldReturn` KeyMap.fromList kvs
+    it @(IO ()) "preserves OpenTelemetry context" $ do
+      k <- OpenTelemetry.newKey "a"
+      let v = "xyz" :: Text
+      OpenTelemetry.adjustContext $ OpenTelemetry.insert k v
+      (wait =<< async (OpenTelemetry.lookup k <$> OpenTelemetry.getContext))
+        `shouldReturn` Just v
diff --git a/tests/Freckle/App/Bugsnag/MetaDataSpec.hs b/tests/Freckle/App/Bugsnag/MetaDataSpec.hs
--- a/tests/Freckle/App/Bugsnag/MetaDataSpec.hs
+++ b/tests/Freckle/App/Bugsnag/MetaDataSpec.hs
@@ -10,7 +10,7 @@
 import Freckle.App.Bugsnag
 import Freckle.App.Bugsnag.MetaData
 import Freckle.App.Stats
-import qualified Freckle.App.Stats as Stats
+import Freckle.App.Stats qualified as Stats
 
 spec :: Spec
 spec = do
diff --git a/tests/Freckle/App/CsvSpec.hs b/tests/Freckle/App/CsvSpec.hs
--- a/tests/Freckle/App/CsvSpec.hs
+++ b/tests/Freckle/App/CsvSpec.hs
@@ -7,11 +7,11 @@
 import Conduit (ConduitT, MonadThrow, ResourceT, yieldMany)
 import Control.Monad.Validate (Validate, refute, runValidate)
 import Data.ByteString (ByteString)
-import qualified Data.ByteString as BS
+import Data.ByteString qualified as BS
 import Data.Csv hiding (header)
-import qualified Data.List.NonEmpty as NE
+import Data.List.NonEmpty qualified as NE
 import Data.Sequence.NonEmpty (NESeq)
-import qualified Data.Vector as V
+import Data.Vector qualified as V
 import Data.Void (Void)
 import Freckle.App.Csv
 import Test.Hspec
diff --git a/tests/Freckle/App/Http/CacheSpec.hs b/tests/Freckle/App/Http/CacheSpec.hs
--- a/tests/Freckle/App/Http/CacheSpec.hs
+++ b/tests/Freckle/App/Http/CacheSpec.hs
@@ -7,13 +7,13 @@
 
 import Freckle.App.Prelude
 
-import qualified Codec.Compression.GZip as GZip
+import Codec.Compression.GZip qualified as GZip
 import Control.Lens ((&), (.~), (<>~))
 import Control.Monad.State (StateT, execStateT)
 import Data.Aeson (FromJSON, eitherDecode)
 import Data.ByteString.Lazy (ByteString)
-import qualified Data.ByteString.Lazy as BSL
-import qualified Data.HashMap.Strict as HashMap
+import Data.ByteString.Lazy qualified as BSL
+import Data.HashMap.Strict qualified as HashMap
 import Data.Time (addUTCTime)
 import Freckle.App.Http
 import Freckle.App.Http.Cache
diff --git a/tests/Freckle/App/HttpSpec.hs b/tests/Freckle/App/HttpSpec.hs
--- a/tests/Freckle/App/HttpSpec.hs
+++ b/tests/Freckle/App/HttpSpec.hs
@@ -7,7 +7,7 @@
 import Control.Lens (to, (^?), _Left, _Right)
 import Data.Aeson
 import Data.Aeson.Lens
-import qualified Data.List.NonEmpty as NE
+import Data.List.NonEmpty qualified as NE
 import Freckle.App.Http
 import Freckle.App.Test.Http
 import Network.HTTP.Types.Status (status200)
diff --git a/tests/Freckle/App/Memcached/ServersSpec.hs b/tests/Freckle/App/Memcached/ServersSpec.hs
--- a/tests/Freckle/App/Memcached/ServersSpec.hs
+++ b/tests/Freckle/App/Memcached/ServersSpec.hs
@@ -6,7 +6,7 @@
 
 import Control.Error.Util (hush)
 import Data.Either (isLeft, isRight)
-import qualified Database.Memcache.Client as Memcache
+import Database.Memcache.Client qualified as Memcache
 import Freckle.App.Memcached.Servers
 import Freckle.App.Test
 
diff --git a/tests/Freckle/App/MemcachedSpec.hs b/tests/Freckle/App/MemcachedSpec.hs
--- a/tests/Freckle/App/MemcachedSpec.hs
+++ b/tests/Freckle/App/MemcachedSpec.hs
@@ -11,12 +11,12 @@
 import Control.Lens (lens, to, (^?))
 import Data.Aeson (Value (..))
 import Data.Aeson.Lens
-import qualified Data.List.NonEmpty as NE
-import qualified Data.Text as T
-import qualified Freckle.App.Env as Env
+import Data.List.NonEmpty qualified as NE
+import Data.Text qualified as T
+import Freckle.App.Env qualified as Env
 import Freckle.App.Memcached
 import Freckle.App.Memcached.Client (MemcachedClient, withMemcachedClient)
-import qualified Freckle.App.Memcached.Client as Memcached
+import Freckle.App.Memcached.Client qualified as Memcached
 import Freckle.App.Memcached.Servers
 import Freckle.App.OpenTelemetry
 
diff --git a/tests/Freckle/App/OpenTelemetry/ContextSpec.hs b/tests/Freckle/App/OpenTelemetry/ContextSpec.hs
--- a/tests/Freckle/App/OpenTelemetry/ContextSpec.hs
+++ b/tests/Freckle/App/OpenTelemetry/ContextSpec.hs
@@ -7,11 +7,11 @@
 import Blammo.Logging
 import Blammo.Logging.Logger (newTestLogger)
 import Control.Lens (lens)
-import qualified Data.Text as T
+import Data.Text qualified as T
 import Freckle.App.OpenTelemetry
 import Freckle.App.OpenTelemetry.Context
 import Network.HTTP.Types.Header (Header)
-import qualified OpenTelemetry.Trace.Core as Trace
+import OpenTelemetry.Trace.Core qualified as Trace
 
 data App = App
   { appLogger :: Logger
diff --git a/tests/Main.hs b/tests/Main.hs
--- a/tests/Main.hs
+++ b/tests/Main.hs
@@ -5,7 +5,7 @@
 import Prelude
 
 import Freckle.App.Test.Hspec.Runner (runParConfig)
-import qualified Spec
+import Spec qualified
 import Test.Hspec
 
 main :: IO ()
