diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,242 +1,1 @@
-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.7.2.0...main)
-
-## [v1.7.2.0](https://github.com/freckle/stackctl/compare/v1.7.1.0...v1.7.2.0)
-
-- Automatically cancel any ongoing update-stack operations on `^C`
-
-## [v1.7.1.0](https://github.com/freckle/stackctl/compare/v1.7.0.0...v1.7.1.0)
-
-- Add `withAssumedRole`, deprecate `assumeRole`
-
-## [v1.7.0.0](https://github.com/freckle/stackctl/compare/v1.6.1.2...v1.7.0.0)
-
-- Retain numeric parameter types, and add boolean parameter handling, in our own
-  yaml generation
-
-  This required changing the `Generate` to use `ParametersYaml` instead of
-  `[Parameter]`, and ultimately led us to removing it, hence the major version
-  bump.
-
-## [v1.6.1.2](https://github.com/freckle/stackctl/compare/v1.6.1.1...v1.6.1.2)
-
-- Require Blammo-1.2.2.3
-
-## [v1.6.1.1](https://github.com/freckle/stackctl/compare/v1.6.1.0...v1.6.1.1)
-
-- Fix: finding removed stacks now respects `STACKCTL_DIRECTORY`
-
-## [v1.6.1.0](https://github.com/freckle/stackctl/compare/v1.6.0.0...v1.6.1.0)
-
-- Add `Ord` instance on `RequiredVersion` and `RequiredVersionOp`
-
-## [v1.6.0.0](https://github.com/freckle/stackctl/compare/v1.5.0.1...v1.6.0.0)
-
-- Re-implement `Stackctl.AWS` with `amazonka-mtl`.
-
-_No CLI or behavior changes._
-
-## [v1.5.0.1](https://github.com/freckle/stackctl/compare/v1.5.0.0...v1.5.0.1)
-
-- Handle missing-or-empty specs directory more explicitly
-- Add warning for `Depends` pointing to non-existent spec
-- Fix formatting of required version in warning message
-
-## [v1.5.0.0](https://github.com/freckle/stackctl/compare/v1.4.4.0...v1.5.0.0)
-
-Breaking changes:
-
-- Don't require a name argument to the `awsSimple` function
-
-New features:
-
-- Add `Exec` and `Shell` features in `actions[].run`
-- Support lists in `actions[].run` (single items still work)
-- Add more granular status indicators in `stack-ls(1)` output, print a legend of
-  these indicators as a footer (disable with `--no-legend`)
-
-Fixes:
-
-- Fix for redundant change-set creation errors in logging output
-- Fix globbing bug in auto-expansion of `--filter` arguments
-
-## [v1.4.4.0](https://github.com/freckle/stackctl/compare/v1.4.3.0...v1.4.4.0)
-
-- Add `awsSilently`
-
-## [v1.4.3.0](https://github.com/freckle/stackctl/compare/v1.4.2.2...v1.4.3.0)
-
-- Add `awsWithAuth`
-- Add `forEachSpec_`
-
-## [v1.4.2.2](https://github.com/freckle/stackctl/compare/v1.4.2.1...v1.4.2.2)
-
-- Use `amazonka-2.0` :tada:
-- Finalize update to `UnliftIO.Exception.Lens`
-- Re-export upstreamed `Blammo.Logging.Colors`
-
-## [v1.4.2.1](https://github.com/freckle/stackctl/compare/v1.4.2.0...v1.4.2.1)
-
-No changes. Bumped to trigger release workflow.
-
-## [v1.4.2.0](https://github.com/freckle/stackctl/compare/v1.4.0.1...v1.4.2.0)
-
-- Add `stackctl-ls` for listing stacks and their deployed status
-- Add `--auto-sso` option for automating `aws sso login` when required
-
-## [v1.4.0.1](https://github.com/freckle/stackctl/compare/v1.4.0.0...v1.4.0.1)
-
-- Document and read a consistently-named `STACKCTL_FILTER` for `--filter`. For
-  now, the old and incorrect `STACKCTL_FILTERS` will still work.
-
-## [v1.4.0.0](https://github.com/freckle/stackctl/compare/v1.3.0.2...v1.4.0.0)
-
-- Add `awsAssumeRole` for running an action as an assumed role
-- Refactor `Generate` interface to better support generating stacks with
-  pre-existing templates
-
-## [v1.3.0.2](https://github.com/freckle/stackctl/compare/v1.3.0.1...v1.3.0.2)
-
-- Adjust timeout when invoking Lambdas to allow up to Lambda's own execution
-  timeout (15 minutes).
-
-## [v1.3.0.1](https://github.com/freckle/stackctl/compare/v1.3.0.0...v1.3.0.1)
-
-- Fix bug where `LOG_COLOR` was never respected
-- Also accept `"required_version: == <version>"`
-- Add `Eq`, `ToJSON` instance on `RequiredVersion`
-
-## [v1.3.0.0](https://github.com/freckle/stackctl/compare/v1.2.0.1...v1.3.0.0)
-
-- Fix it so commands like `version` don't need a valid AWS environment
-
-  This changes the `Subcommand` interface and so is a major version update for
-  the purposes of those using Stackctl as a library.
-
-## [v1.2.0.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.2.0.0)
-
-- Use more specific types in `Has{Directory,Filter,Color}Option`
-- Add environment variable configuration for `STACKCTL_{DIRECTORY,FILTERS}`
-
-## [v1.1.4.0](https://github.com/freckle/stackctl/compare/v1.1.3.1...v1.1.4.0)
-
-- Support matching Stacks by glob in `capture`
-- Add `--tag` to `changes` and `deploy`
-
-## [v1.1.3.1](https://github.com/freckle/stackctl/compare/v1.1.3.0...v1.1.3.1)
-
-- Fix JSON formatting bugs in generating specification
-
-## [v1.1.3.0](https://github.com/freckle/stackctl/compare/v1.1.2.2...v1.1.3.0)
-
-- Repository-local configuration
-
-  See https://github.com/freckle/stackctl/commit/564678203fe70b5c4c46c655dd3daeaafb6de9e0
-
-- Don't duplicate re-used templates in `stackctl-cat`
-- Improve `--filter`
-
-  - Match against stack name and template, in addition to spec path.
-  - Automatically prepend `**/` (unless there is already a leading wildcard) and
-    append `{/*,.yaml,.json}` (unless there is already a trailing wildcard or
-    extension).
-
-  In general, this aims to make `--filter` match more things more intuitively
-  for operators, but still match exactly in programmatic use-cases.
-
-- Various documentation improvements
-- Support more natural `{Key}: {Value}` syntax in `Parameters` and `Tags`
-- Fix bug where we may generate an `{}` element in `Parameters`
-
-## [v1.1.2.2](https://github.com/freckle/stackctl/compare/v1.1.2.1...v1.1.2.2)
-
-- Add support for Stack descriptions
-
-## [v1.1.2.1](https://github.com/freckle/stackctl/compare/v1.1.2.0...v1.1.2.1)
-
-- Build with LTS-20.4 / GHC 9.2
-
-## [v1.1.2.0](https://github.com/freckle/stackctl/compare/v1.1.1.1...v1.1.2.0)
-
-- Fix incorrect ordering of log-messages by setting `LOG_CONCURRENCY=1`
-- Fix potential coloring of changes being redirected to a file
-- Make `PATH` optional (again) in `stackctl changes`
-- Add `--no-flip` to `stackctl capture`
-
-## [v1.1.1.1](https://github.com/freckle/stackctl/compare/v1.1.1.0...v1.1.1.1)
-
-- Trigger release
-
-## [v1.1.1.0](https://github.com/freckle/stackctl/compare/v1.1.0.5...v1.1.1.0)
-
-- Add `--parameter` to `changes` and `deploy`
-- Sort changes by causing-before-caused
-
-## [v1.1.0.5](https://github.com/freckle/stackctl/compare/v1.1.0.4...v1.1.0.5)
-
-- Trigger release workflow
-
-## [v1.1.0.4](https://github.com/freckle/stackctl/compare/v1.1.0.3...v1.1.0.4)
-
-- Fix bug where only the last spec in a multi-spec case had its changes present
-  in the output file generated by `changes`.
-
-## [v1.1.0.3](https://github.com/freckle/stackctl/compare/v1.1.0.2...v1.1.0.3)
-
-- Require Blammo-1.1.1.0
-
-## [v1.1.0.2](https://github.com/freckle/stackctl/compare/v1.1.0.1...v1.1.0.2)
-
-- Log responses from `awsLambdaInvoke` when running actions
-- Clarify discovery logging
-- Add install script
-
-## [v1.1.0.1](https://github.com/freckle/stackctl/compare/v1.1.0.0...v1.1.0.1)
-
-- Update to `cfn-flip-0.1.0.3`
-
-## [v1.1.0.0](https://github.com/freckle/stackctl/compare/v1.0.2.0...v1.1.0.0)
-
-- Fix interleaved or out-of-order output bugs by streaming deployment events
-  through the Logger instead of directly to `stdout`
-- Logging goes to `stdout` by default (`LOG_DESTINATION` can still be used)
-- The `changes` subcommand now requires a `PATH` argument
-
-## [v1.0.2.0](https://github.com/freckle/stackctl/compare/v1.0.1.2...v1.0.2.0)
-
-- Add `Stackctl.Action`
-
-  Support for taking actions during Stack management, currently we support
-  invoking a lambda post-deployment. In the future, we can add more, such as
-  running local pre-deploy validation or preparation scripts.
-
-- Add `awsCloudFormationDescribeStackOutputs`
-
-## [v1.0.1.2](https://github.com/freckle/stackctl/compare/v1.0.1.1...v1.0.1.2)
-
-- Always flush log messages before our own output
-
-## [v1.0.1.1](https://github.com/freckle/stackctl/compare/v1.0.1.0...v1.0.1.1)
-
-- Respect `LOG_DESTINATION` (the default remains `stderr`)
-
-## [v1.0.1.0](https://github.com/freckle/stackctl/compare/v1.0.0.2...v1.0.1.0)
-
-- Support reading CloudGenesis specifications
-
-  - Accept account paths like `id.name` or `name.id`
-  - Read `Parameters` as `Parameter{Key,Value}` or `{Name,Value}`
-
-  This allows us to work with specifications directories originally implemented
-  for, and potentially still used with, the CloudGenesis tooling.
-
-## [v1.0.0.2](https://github.com/freckle/stackctl/compare/v1.0.0.1...v1.0.0.2)
-
-- Fix tailing all events to read most recent, causing Throttling errors
-
-## [v1.0.0.1](https://github.com/freckle/stackctl/compare/v1.0.0.0...v1.0.0.1)
-
-- Fix non-portable paths issue in OSX executable build
-
-## [v1.0.0.0](https://github.com/freckle/stackctl/tree/v1.0.0.0)
-
-First release
+See https://github.com/freckle/stackctl/releases
diff --git a/app/Main.hs b/app/Main.hs
--- a/app/Main.hs
+++ b/app/Main.hs
@@ -11,8 +11,8 @@
 main =
   runSubcommand
     $ subcommand Commands.cat
-    <> subcommand Commands.capture
-    <> subcommand Commands.changes
-    <> subcommand Commands.deploy
-    <> subcommand Commands.list
-    <> subcommand Commands.version
+      <> subcommand Commands.capture
+      <> subcommand Commands.changes
+      <> subcommand Commands.deploy
+      <> subcommand Commands.list
+      <> subcommand Commands.version
diff --git a/src/Stackctl/AWS/CloudFormation.hs b/src/Stackctl/AWS/CloudFormation.hs
--- a/src/Stackctl/AWS/CloudFormation.hs
+++ b/src/Stackctl/AWS/CloudFormation.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE DuplicateRecordFields #-}
+
 module Stackctl.AWS.CloudFormation
   ( Stack (..)
   , stack_stackName
@@ -194,7 +196,7 @@
   handling_ _ValidationError (pure Nothing)
     $ awsSilently -- don't log said 400
     $ Just
-    <$> awsCloudFormationDescribeStack stackName
+      <$> awsCloudFormationDescribeStack stackName
 
 awsCloudFormationDescribeStackOutputs
   :: (MonadIO m, MonadAWS m)
@@ -218,10 +220,10 @@
 
   runConduit
     $ AWS.paginate req
-    .| mapC (fromMaybe [] . (^. describeStackEventsResponse_stackEvents))
-    .| concatC
-    .| takeWhileC (\e -> Just (e ^. stackEvent_eventId) /= mLastId)
-    .| sinkList
+      .| mapC (fromMaybe [] . (^. describeStackEventsResponse_stackEvents))
+      .| concatC
+      .| takeWhileC (\e -> Just (e ^. stackEvent_eventId) /= mLastId)
+      .| sinkList
 
 awsCloudFormationGetStackNamesMatching
   :: (MonadIO m, MonadAWS m)
@@ -232,12 +234,12 @@
 
   runConduit
     $ AWS.paginate req
-    .| concatMapC (^. listStacksResponse_stackSummaries)
-    .| concatC
-    .| mapC (^. stackSummary_stackName)
-    .| filterC ((p `match`) . unpack)
-    .| mapC StackName
-    .| sinkList
+      .| concatMapC (^. listStacksResponse_stackSummaries)
+      .| concatC
+      .| mapC (^. stackSummary_stackName)
+      .| filterC ((p `match`) . unpack)
+      .| mapC StackName
+      .| sinkList
 
 awsCloudFormationGetMostRecentStackEventId
   :: (MonadIO m, MonadAWS m)
@@ -258,9 +260,9 @@
 
   AWS.simple req
     $ pure
-    . getFirstEventId
-    . fromMaybe []
-    . (^. describeStackEventsResponse_stackEvents)
+      . getFirstEventId
+      . fromMaybe []
+      . (^. describeStackEventsResponse_stackEvents)
 
 awsCloudFormationDeleteStack
   :: (MonadIO m, MonadLogger m, MonadAWS m)
@@ -314,7 +316,7 @@
 awaitStack waiter stackName =
   AWS.await waiter
     $ newDescribeStacks
-    & describeStacks_stackName ?~ unStackName stackName
+      & describeStacks_stackName ?~ unStackName stackName
 
 makeParameter :: Text -> Maybe Text -> Parameter
 makeParameter k v =
@@ -424,7 +426,7 @@
 
       logInfo
         $ "Creating changeset..."
-        :# ["name" .= name, "type" .= changeSetType]
+          :# ["name" .= name, "type" .= changeSetType]
       csId <- AWS.simple req (^. createChangeSetResponse_id)
 
       logDebug "Awaiting CREATE_COMPLETE"
@@ -471,16 +473,16 @@
   logInfo "Deleting all changesets"
   runConduit
     $ AWS.paginate (newListChangeSets $ unStackName stackName)
-    .| concatMapC
-      ( \resp -> fromMaybe [] $ do
-          ss <- resp ^. listChangeSetsResponse_summaries
-          pure $ mapMaybe Summary.changeSetId ss
-      )
-    .| mapM_C
-      ( \csId -> do
-          logInfo $ "Enqueing delete" :# ["changeSetId" .= csId]
-          void $ AWS.send $ newDeleteChangeSet csId
-      )
+      .| concatMapC
+        ( \resp -> fromMaybe [] $ do
+            ss <- resp ^. listChangeSetsResponse_summaries
+            pure $ mapMaybe Summary.changeSetId ss
+        )
+      .| mapM_C
+        ( \csId -> do
+            logInfo $ "Enqueing delete" :# ["changeSetId" .= csId]
+            void $ AWS.send $ newDeleteChangeSet csId
+        )
 
 -- | Did we abandoned this Stack's first ever ChangeSet?
 --
diff --git a/src/Stackctl/AWS/Core.hs b/src/Stackctl/AWS/Core.hs
--- a/src/Stackctl/AWS/Core.hs
+++ b/src/Stackctl/AWS/Core.hs
@@ -77,8 +77,6 @@
      , MonadIO m
      , MonadAWS m
      , AWSRequest a
-     , Typeable a
-     , Typeable (AWSResponse a)
      )
   => a
   -> (AWSResponse a -> Maybe b)
@@ -160,10 +158,10 @@
   handling _ServiceError $ \e -> do
     logError
       $ "Exiting due to AWS Service error"
-      :# [ "code" .= toText (e ^. serviceError_code)
-         , "message" .= fmap toText (e ^. serviceError_message)
-         , "requestId" .= fmap toText (e ^. serviceError_requestId)
-         ]
+        :# [ "code" .= toText (e ^. serviceError_code)
+           , "message" .= fmap toText (e ^. serviceError_message)
+           , "requestId" .= fmap toText (e ^. serviceError_requestId)
+           ]
     exitFailure
 
 formatServiceError :: ServiceError -> Text
diff --git a/src/Stackctl/AWS/Lambda.hs b/src/Stackctl/AWS/Lambda.hs
--- a/src/Stackctl/AWS/Lambda.hs
+++ b/src/Stackctl/AWS/Lambda.hs
@@ -93,11 +93,11 @@
 
   logDebug
     $ "Function result"
-    :# [ "name" .= name
-       , "status" .= status
-       , "error" .= mError
-       , "functionError" .= mFunctionError
-       ]
+      :# [ "name" .= name
+         , "status" .= status
+         , "error" .= mError
+         , "functionError" .= mFunctionError
+         ]
 
   pure
     $ if
diff --git a/src/Stackctl/AWS/Orphans.hs b/src/Stackctl/AWS/Orphans.hs
--- a/src/Stackctl/AWS/Orphans.hs
+++ b/src/Stackctl/AWS/Orphans.hs
@@ -14,6 +14,9 @@
 import Data.Aeson
 import GHC.Generics (Rep)
 
+-- TODO: upstream
+deriving newtype instance MonadUnliftIO m => MonadUnliftIO (WithLogger env m)
+
 -- Makes it syntactally easier to do a bunch of these
 newtype Generically a = Generically {unGenerically :: a}
 
diff --git a/src/Stackctl/AWS/Scope.hs b/src/Stackctl/AWS/Scope.hs
--- a/src/Stackctl/AWS/Scope.hs
+++ b/src/Stackctl/AWS/Scope.hs
@@ -26,20 +26,16 @@
 awsScopeSpecPatterns AwsScope {..} =
   [ compile
       $ "stacks"
-      </> unpack (unAccountId awsAccountId)
-      <> ".*"
-      </> unpack (fromRegion awsRegion)
-      <> "**"
-      </> "*"
-      <.> "yaml"
+        </> unpack (unAccountId awsAccountId) <> ".*"
+        </> unpack (fromRegion awsRegion)
+        </> "**"
+        </> "*" <.> "yaml"
   , compile
       $ "stacks"
-      </> "*."
-      <> unpack (unAccountId awsAccountId)
-      </> unpack (fromRegion awsRegion)
-      <> "**"
-      </> "*"
-      <.> "yaml"
+        </> "*." <> unpack (unAccountId awsAccountId)
+        </> unpack (fromRegion awsRegion)
+        </> "**"
+        </> "*" <.> "yaml"
   ]
 
 awsScopeSpecStackName :: AwsScope -> FilePath -> Maybe StackName
@@ -50,13 +46,13 @@
   -- pretty fast and loose with the "parsing" step
   pure
     $ path -- stacks/account/region/x/y.yaml
-    & splitPath -- [stacks/, account/, region/, x/, y.yaml]
-    & drop 3 -- [x, y.yaml]
-    & joinPath -- x/y.yaml
-    & dropExtension -- x/y
-    & pack
-    & T.replace "/" "-" -- x-y
-    & StackName
+      & splitPath -- [stacks/, account/, region/, x/, y.yaml]
+      & drop 3 -- [x, y.yaml]
+      & joinPath -- x/y.yaml
+      & dropExtension -- x/y
+      & pack
+      & T.replace "/" "-" -- x-y
+      & StackName
 
 class HasAwsScope env where
   awsScopeL :: Lens' env AwsScope
diff --git a/src/Stackctl/Action.hs b/src/Stackctl/Action.hs
--- a/src/Stackctl/Action.hs
+++ b/src/Stackctl/Action.hs
@@ -128,10 +128,10 @@
         Nothing -> do
           logError
             $ "Output not found"
-            :# [ "stackName" .= stackName
-               , "desiredOutput" .= outputName
-               , "availableOutputs" .= map (^. output_outputKey) outputs
-               ]
+              :# [ "stackName" .= stackName
+                 , "desiredOutput" .= outputName
+                 , "availableOutputs" .= map (^. output_outputKey) outputs
+                 ]
           throwIO NoSuchOutput
         Just name -> invoke name
     InvokeLambdaByName name -> invoke name
diff --git a/src/Stackctl/CLI.hs b/src/Stackctl/CLI.hs
--- a/src/Stackctl/CLI.hs
+++ b/src/Stackctl/CLI.hs
@@ -7,6 +7,7 @@
 
 import Stackctl.Prelude
 
+import Blammo.Logging.LogSettings
 import qualified Blammo.Logging.LogSettings.Env as LoggingEnv
 import Control.Monad.AWS as AWS
 import Control.Monad.AWS.ViaReader as AWS
@@ -60,7 +61,7 @@
   autoSSOOptionL = optionsL . autoSSOOptionL
 
 newtype AppT app m a = AppT
-  { unAppT :: ReaderT app (LoggingT (ResourceT m)) a
+  { unAppT :: ReaderT app (ResourceT m) a
   }
   deriving newtype
     ( Functor
@@ -70,12 +71,13 @@
     , MonadUnliftIO
     , MonadResource
     , MonadReader app
-    , MonadLogger
     , MonadThrow
     , MonadCatch
     , MonadMask
     )
   deriving (MonadAWS) via (ReaderAWS (AppT app m))
+  deriving (MonadLogger) via (WithLogger app (ResourceT m))
+  deriving (MonadLoggerIO) via (WithLogger app (ResourceT m))
 
 runAppT
   :: ( MonadMask m
@@ -94,36 +96,33 @@
       . setLogSettingsConcurrency (Just 1)
       $ defaultLogSettings
 
-  logger <-
-    newLogger
-      $ adjustLogSettings
-        (options ^. colorOptionL)
-        (options ^. verboseOptionL)
-        envLogSettings
+  let logSettings =
+        adjustLogSettings
+          (options ^. colorOptionL)
+          (options ^. verboseOptionL)
+          envLogSettings
 
-  app <- runResourceT $ runLoggerLoggingT logger $ do
-    aws <- runReaderT (handleAutoSSO options AWS.discover) logger
+  withLogger logSettings $ \appLogger -> do
+    appAwsEnv <- runWithLogger appLogger $ handleAutoSSO options AWS.discover
+    appConfig <- runWithLogger appLogger loadConfigOrExit
+    appAwsScope <- AWS.runEnvT fetchAwsScope appAwsEnv
 
-    App logger
-      <$> loadConfigOrExit
-      <*> pure options
-      <*> AWS.runEnvT fetchAwsScope aws
-      <*> pure aws
+    let
+      AwsScope {..} = appAwsScope
 
-  let
-    AwsScope {..} = appAwsScope app
+      context =
+        [ "region" .= awsRegion
+        , "accountId" .= awsAccountId
+        , "accountName" .= awsAccountName
+        ]
 
-    context =
-      [ "region" .= awsRegion
-      , "accountId" .= awsAccountId
-      , "accountName" .= awsAccountName
-      ]
+      appOptions = options
+      app = App {..}
 
-  runResourceT
-    $ runLoggerLoggingT app
-    $ flip runReaderT app
-    $ withThreadContext context
-    $ unAppT f
+    runResourceT
+      $ flip runReaderT app
+      $ withThreadContext context
+      $ unAppT f
 
 adjustLogSettings
   :: Maybe ColorOption -> Verbosity -> LogSettings -> LogSettings
diff --git a/src/Stackctl/Config.hs b/src/Stackctl/Config.hs
--- a/src/Stackctl/Config.hs
+++ b/src/Stackctl/Config.hs
@@ -75,9 +75,10 @@
 
 loadConfig :: MonadIO m => m (Either ConfigError Config)
 loadConfig =
-  runExceptT $ getConfigFile >>= \case
-    Nothing -> pure emptyConfig
-    Just cf -> loadConfigFrom cf
+  runExceptT
+    $ getConfigFile >>= \case
+      Nothing -> pure emptyConfig
+      Just cf -> loadConfigFrom cf
 
 loadConfigFrom :: (MonadIO m, MonadError ConfigError m) => FilePath -> m Config
 loadConfigFrom path = loadConfigFromBytes =<< liftIO (readFileBinary path)
diff --git a/src/Stackctl/Config/RequiredVersion.hs b/src/Stackctl/Config/RequiredVersion.hs
--- a/src/Stackctl/Config/RequiredVersion.hs
+++ b/src/Stackctl/Config/RequiredVersion.hs
@@ -54,7 +54,7 @@
     ws ->
       Left
         $ show (unpack $ T.unwords ws)
-        <> " did not parse as optional operator and version string"
+          <> " did not parse as optional operator and version string"
 
   parseRequiredVersion :: Text -> Text -> Either String RequiredVersion
   parseRequiredVersion op w = RequiredVersion <$> parseOp op <*> parseVersion w
@@ -71,8 +71,8 @@
     op ->
       Left
         $ "Invalid comparison operator ("
-        <> unpack op
-        <> "), may only be =, <, <=, >, >=, or =~"
+          <> unpack op
+          <> "), may only be =, <, <=, >, >=, or =~"
 
   parseVersion :: Text -> Either String Version
   parseVersion t =
diff --git a/src/Stackctl/FilterOption.hs b/src/Stackctl/FilterOption.hs
--- a/src/Stackctl/FilterOption.hs
+++ b/src/Stackctl/FilterOption.hs
@@ -44,8 +44,8 @@
     Env.var (first Env.UnreadError . readFilterOption) name
       $ Env.help
       $ "Filter "
-      <> items
-      <> " by patterns"
+        <> items
+        <> " by patterns"
 
 filterOption :: String -> Parser FilterOption
 filterOption items =
diff --git a/src/Stackctl/Options.hs b/src/Stackctl/Options.hs
--- a/src/Stackctl/Options.hs
+++ b/src/Stackctl/Options.hs
@@ -55,11 +55,11 @@
 envParser =
   Env.prefixed "STACKCTL_"
     $ Options
-    <$> optional envDirectoryOption
-    <*> optional (envFilterOption "specifications")
-    <*> pure mempty -- use LOG_COLOR
-    <*> pure mempty -- use LOG_LEVEL
-    <*> optional envAutoSSOOption
+      <$> optional envDirectoryOption
+      <*> optional (envFilterOption "specifications")
+      <*> pure mempty -- use LOG_COLOR
+      <*> pure mempty -- use LOG_LEVEL
+      <*> optional envAutoSSOOption
 
 -- brittany-disable-next-binding
 
diff --git a/src/Stackctl/Prelude.hs b/src/Stackctl/Prelude.hs
--- a/src/Stackctl/Prelude.hs
+++ b/src/Stackctl/Prelude.hs
@@ -20,6 +20,8 @@
   )
 
 import Blammo.Logging as X
+import Blammo.Logging.Setup as X
+import Blammo.Logging.ThreadContext as X
 import Control.Error.Util as X (hush, note)
 import Data.Aeson as X (ToJSON (..), object)
 import Data.Text as X (pack, unpack)
diff --git a/src/Stackctl/Spec/Capture.hs b/src/Stackctl/Spec/Capture.hs
--- a/src/Stackctl/Spec/Capture.hs
+++ b/src/Stackctl/Spec/Capture.hs
@@ -132,9 +132,8 @@
   case results of
     [] -> do
       logError
-        $ "No Active Stacks match "
-        <> pack (decompile scoStackName)
-        :# []
+        $ "No Active Stacks match " <> pack (decompile scoStackName)
+          :# []
       exitFailure
     [stackName] -> do
       stack <- awsCloudFormationDescribeStack stackName
diff --git a/src/Stackctl/Spec/Cat.hs b/src/Stackctl/Spec/Cat.hs
--- a/src/Stackctl/Spec/Cat.hs
+++ b/src/Stackctl/Spec/Cat.hs
@@ -138,11 +138,11 @@
     kvs <- f <$> mA
     pure
       $ [cyan label <> ":"]
-      <> map
-        ( \(k, mV) ->
-            "  " <> cyan k <> ":" <> maybe "" (\v -> " " <> green v) mV
-        )
-        kvs
+        <> map
+          ( \(k, mV) ->
+              "  " <> cyan k <> ":" <> maybe "" (\v -> " " <> green v) mV
+          )
+          kvs
 
   ppList :: Text -> (a -> [Text]) -> Maybe a -> [Text]
   ppList label f = maybe [] (((cyan label <> ":") :) . f)
@@ -186,8 +186,8 @@
   displayObjectProperty =
     displayPropertyWith @(HashMap Text Value)
       $ map (("  - " <>) . green)
-      . sort
-      . HashMap.keys
+        . sort
+        . HashMap.keys
 
   displayPropertyWith
     :: (FromJSON a, ToJSON a) => (a -> [Text]) -> Text -> [Text]
diff --git a/src/Stackctl/Spec/Changes/Format.hs b/src/Stackctl/Spec/Changes/Format.hs
--- a/src/Stackctl/Spec/Changes/Format.hs
+++ b/src/Stackctl/Spec/Changes/Format.hs
@@ -76,10 +76,11 @@
   (Nothing, _) -> "No changes for " <> name
   (_, Nothing) -> "Metadata only changes (e.g. Tags or Outputs)"
   (_, Just rcs) ->
-    ("\n" <>) $ (<> "\n") $ mconcat $ ("Changes for " <> cyan name <> ":")
-      : map
-        (("\n  " <>) . formatResourceChange)
-        (NE.toList rcs)
+    ("\n" <>)
+      $ (<> "\n")
+      $ mconcat
+      $ ("Changes for " <> cyan name <> ":")
+        : map (("\n  " <>) . formatResourceChange) (NE.toList rcs)
  where
   rChanges = do
     cs <- mChangeSet
@@ -149,20 +150,20 @@
       , "\n| Action | Logical Id | Physical Id | Type | Replacement | Scope | Details |"
       , "\n| ---    | ---        | ---         | ---  | ---         | ---   | ---     |"
       ]
-    <> map commentTableRow (NE.toList rcs)
-    <> case omitFull of
-      OmitFull -> []
-      IncludeFull ->
-        [ "\n"
-        , "\n<details>"
-        , "\n<summary>Full changes</summary>"
-        , "\n"
-        , "\n```json"
-        , "\n" <> changeSetJSON cs
-        , "\n```"
-        , "\n"
-        , "\n</details>"
-        ]
+      <> map commentTableRow (NE.toList rcs)
+      <> case omitFull of
+        OmitFull -> []
+        IncludeFull ->
+          [ "\n"
+          , "\n<details>"
+          , "\n<summary>Full changes</summary>"
+          , "\n"
+          , "\n```json"
+          , "\n" <> changeSetJSON cs
+          , "\n```"
+          , "\n"
+          , "\n</details>"
+          ]
 
 commentTableRow :: ResourceChange -> Text
 commentTableRow ResourceChange' {..} =
@@ -194,10 +195,10 @@
 
   pure
     $ toText c
-    <> maybe "" ((" in " <>) . toText) attr
-    <> maybe "" (\x -> " (" <> magenta (toText x) <> ")") n
-    <> maybe "" ((", recreation " <>) . formatRR) rr
-    <> maybe "" ((", caused by " <>) . toText) causingEntity
+      <> maybe "" ((" in " <>) . toText) attr
+      <> maybe "" (\x -> " (" <> magenta (toText x) <> ")") n
+      <> maybe "" ((", recreation " <>) . formatRR) rr
+      <> maybe "" ((", caused by " <>) . toText) causingEntity
  where
   formatRR = \case
     x@RequiresRecreation_Always -> red (toText x)
diff --git a/src/Stackctl/Spec/Deploy.hs b/src/Stackctl/Spec/Deploy.hs
--- a/src/Stackctl/Spec/Deploy.hs
+++ b/src/Stackctl/Spec/Deploy.hs
@@ -266,8 +266,7 @@
   timestamp <-
     liftIO
       $ formatTime defaultTimeLocale "%F %T %Z"
-      <$> utcToLocalZonedTime
-        (e ^. stackEvent_timestamp)
+        <$> utcToLocalZonedTime (e ^. stackEvent_timestamp)
 
   pure
     $ mconcat
@@ -276,8 +275,7 @@
       , maybe "" colorStatus $ e ^. stackEvent_resourceStatus
       , maybe "" (magenta . (" " <>)) $ e ^. stackEvent_logicalResourceId
       , maybe "" ((\x -> " (" <> x <> ")") . T.strip)
-          $ e
-          ^. stackEvent_resourceStatusReason
+          $ e ^. stackEvent_resourceStatusReason
       ]
  where
   colorStatus = \case
diff --git a/src/Stackctl/Spec/Discover.hs b/src/Stackctl/Spec/Discover.hs
--- a/src/Stackctl/Spec/Discover.hs
+++ b/src/Stackctl/Spec/Discover.hs
@@ -105,9 +105,9 @@
 
       logError
         $ "Multiple specifications produced the same Stack name"
-        :# [ "name" .= stackSpecPathStackName (NE.head specPaths)
-           , "paths" .= collidingPaths
-           ]
+          :# [ "name" .= stackSpecPathStackName (NE.head specPaths)
+             , "paths" .= collidingPaths
+             ]
 
     exitFailure
 
@@ -130,18 +130,17 @@
     for_ depends $ \depend -> do
       let (nearest, _distance) =
             NE.minimumBy1 (comparing snd)
-              $ (id &&& getDistance depend)
-              <$> known
+              $ (id &&& getDistance depend) <$> known
 
       logWarn
         $ "Stack lists dependency that does not exist"
-        :# [ "dependency"
-              .= ( unStackName (stackSpecStackName spec)
-                    <> " -> "
-                    <> unStackName depend
-                 )
-           , "hint" .= ("Did you mean " <> unStackName nearest <> "?")
-           ]
+          :# [ "dependency"
+                 .= ( unStackName (stackSpecStackName spec)
+                        <> " -> "
+                        <> unStackName depend
+                    )
+             , "hint" .= ("Did you mean " <> unStackName nearest <> "?")
+             ]
 
   getDistance = levenshtein `on` unStackName
 
diff --git a/src/Stackctl/Spec/List.hs b/src/Stackctl/Spec/List.hs
--- a/src/Stackctl/Spec/List.hs
+++ b/src/Stackctl/Spec/List.hs
@@ -79,7 +79,7 @@
   when loLegend
     $ pushLoggerLn
     $ "\nLegend:\n  "
-    <> T.intercalate ", " (map legendItem [minBound .. maxBound])
+      <> T.intercalate ", " (map legendItem [minBound .. maxBound])
 
 data Indicator
   = Deployed
diff --git a/src/Stackctl/StackSpec.hs b/src/Stackctl/StackSpec.hs
--- a/src/Stackctl/StackSpec.hs
+++ b/src/Stackctl/StackSpec.hs
@@ -81,8 +81,7 @@
 stackSpecTemplate spec =
   StackTemplate
     $ FilePath.normalise
-    $ ssSpecRoot spec
-    </> stackSpecTemplateFile spec
+    $ ssSpecRoot spec </> stackSpecTemplateFile spec
 
 stackSpecParameters :: StackSpec -> [Parameter]
 stackSpecParameters =
@@ -197,8 +196,7 @@
     (stackSpecStackName spec)
     (stackSpecStackDescription spec)
     (stackSpecTemplate spec)
-    ( nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec
-    )
+    (nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec)
     (stackSpecCapabilities spec)
     (nubOrdOn (^. tag_key) $ tags <> stackSpecTags spec)
 
diff --git a/src/Stackctl/StackSpecPath.hs b/src/Stackctl/StackSpecPath.hs
--- a/src/Stackctl/StackSpecPath.hs
+++ b/src/Stackctl/StackSpecPath.hs
@@ -83,16 +83,16 @@
       unless (pathAccountId == awsAccountId)
         $ Left
         $ "Unexpected account: "
-        <> unpack (unAccountId pathAccountId)
-        <> " != "
-        <> unpack (unAccountId awsAccountId)
+          <> unpack (unAccountId pathAccountId)
+          <> " != "
+          <> unpack (unAccountId awsAccountId)
 
       unless (unpack (fromRegion awsRegion) == pathRegion)
         $ Left
         $ "Unexpected region: "
-        <> pathRegion
-        <> " != "
-        <> unpack (fromRegion awsRegion)
+          <> pathRegion
+          <> " != "
+          <> unpack (fromRegion awsRegion)
 
       stackName <-
         maybe (Left "Must end in .yaml") (Right . StackName)
@@ -117,6 +117,6 @@
   _ ->
     Left
       $ "Path matches neither {account-id}.{account-name}, nor {account-name}.{account-id}: "
-      <> path
+        <> path
  where
   isAccountId x = T.length x == 12 && T.all isDigit x
diff --git a/src/Stackctl/StackSpecYaml.hs b/src/Stackctl/StackSpecYaml.hs
--- a/src/Stackctl/StackSpecYaml.hs
+++ b/src/Stackctl/StackSpecYaml.hs
@@ -85,8 +85,7 @@
       $ KeyMap.toList
       $ KeyMap.fromListWith (<>)
       $ map (pyKey &&& pyValue)
-      $ bs -- flipped to make sure Last-wins
-      <> as
+      $ bs <> as -- flipped to make sure Last-wins
 
 instance FromJSON ParametersYaml where
   parseJSON = \case
@@ -108,7 +107,7 @@
   toJSON = object . parametersYamlPairs
   toEncoding = pairs . mconcat . parametersYamlPairs
 
-parametersYamlPairs :: KeyValue kv => ParametersYaml -> [kv]
+parametersYamlPairs :: KeyValue e kv => ParametersYaml -> [kv]
 parametersYamlPairs = map parameterYamlPair . unParametersYaml
 
 parametersYaml :: [ParameterYaml] -> ParametersYaml
@@ -125,7 +124,7 @@
     (mkParameterYaml <$> o .: "Name" <*> o .:? "Value")
       <|> (mkParameterYaml <$> o .: "ParameterKey" <*> o .:? "ParameterValue")
 
-parameterYamlPair :: KeyValue kv => ParameterYaml -> kv
+parameterYamlPair :: KeyValue e kv => ParameterYaml -> kv
 parameterYamlPair ParameterYaml {..} = pyKey .= pyValue
 
 parameterYaml :: Parameter -> Maybe ParameterYaml
@@ -215,8 +214,7 @@
       $ HashMap.toList
       $ HashMap.fromList
       $ map (toPair . unTagYaml)
-      $ as
-      <> bs
+      $ as <> bs
    where
     toPair :: Tag -> (Text, Text)
     toPair = (^. tag_key) &&& (^. tag_value)
@@ -237,7 +235,7 @@
   toJSON = object . tagsYamlPairs
   toEncoding = pairs . mconcat . tagsYamlPairs
 
-tagsYamlPairs :: KeyValue kv => TagsYaml -> [kv]
+tagsYamlPairs :: KeyValue e kv => TagsYaml -> [kv]
 tagsYamlPairs = map tagYamlPair . unTagsYaml
 
 tagsYaml :: [TagYaml] -> TagsYaml
@@ -253,5 +251,5 @@
     t <- newTag <$> o .: "Key" <*> o .: "Value"
     pure $ TagYaml t
 
-tagYamlPair :: KeyValue kv => TagYaml -> kv
+tagYamlPair :: KeyValue e kv => TagYaml -> kv
 tagYamlPair (TagYaml t) = Key.fromText (t ^. tag_key) .= (t ^. tag_value)
diff --git a/src/Stackctl/VerboseOption.hs b/src/Stackctl/VerboseOption.hs
--- a/src/Stackctl/VerboseOption.hs
+++ b/src/Stackctl/VerboseOption.hs
@@ -7,6 +7,7 @@
 
 import Stackctl.Prelude
 
+import Blammo.Logging.LogSettings
 import Blammo.Logging.LogSettings.LogLevels
 import Options.Applicative
 
diff --git a/src/Stackctl/Version.hs b/src/Stackctl/Version.hs
--- a/src/Stackctl/Version.hs
+++ b/src/Stackctl/Version.hs
@@ -3,10 +3,10 @@
   ) where
 
 import Stackctl.Prelude
+import Prelude (putStrLn)
 
 import Data.Version
 import qualified Paths_stackctl as Pkg
-import Prelude (putStrLn)
 
 logVersion :: MonadIO m => m ()
 logVersion = liftIO $ putStrLn $ ("Stackctl v" <>) $ showVersion Pkg.version
diff --git a/stackctl.cabal b/stackctl.cabal
--- a/stackctl.cabal
+++ b/stackctl.cabal
@@ -1,6 +1,6 @@
 cabal-version:   1.18
 name:            stackctl
-version:         1.7.3.0
+version:         1.7.3.2
 license:         MIT
 license-file:    LICENSE
 copyright:       2022 Renaissance Learning Inc
@@ -79,17 +79,17 @@
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missed-specialisations
         -Wno-missing-import-lists -Wno-missing-kind-signatures
-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
-        -Wno-prepositive-qualified-module -Wno-unsafe
-        -optP-Wno-nonportable-include-path
+        -Wno-missing-local-signatures -Wno-missing-role-annotations
+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module
+        -Wno-unsafe -optP-Wno-nonportable-include-path
 
     build-depends:
         Blammo >=1.1.2.3,
         Glob >=0.10.2,
-        QuickCheck >=2.14.2,
-        aeson >=2.0.3.0,
+        QuickCheck >=2.14.3,
+        aeson >=2.2.3.0,
         aeson-casing >=0.2.0.0,
-        aeson-pretty >=0.8.9,
+        aeson-pretty >=0.8.10,
         amazonka >=2.0,
         amazonka-cloudformation >=2.0,
         amazonka-core >=2.0,
@@ -99,33 +99,33 @@
         amazonka-sso >=2.0,
         amazonka-sts >=2.0,
         base >=4 && <5,
-        bytestring >=0.11.3.1,
+        bytestring >=0.12.1.0,
         cfn-flip >=0.1.0.3,
-        conduit >=1.3.4.3,
-        containers >=0.6.5.1,
-        envparse >=0.5.0,
+        conduit >=1.3.6,
+        containers >=0.6.8,
+        envparse >=0.6.0,
         errors >=2.3.0,
-        exceptions >=0.10.4,
-        extra >=1.7.12,
-        filepath >=1.4.2.2,
-        lens >=5.1.1,
-        lens-aeson >=1.2.2,
-        monad-logger >=0.3.37,
-        mtl >=2.2.2,
-        optparse-applicative >=0.17.0.0,
-        resourcet >=1.2.6,
+        exceptions >=0.10.7,
+        extra >=1.7.16,
+        filepath >=1.4.301.0,
+        lens >=5.3.3,
+        lens-aeson >=1.2.3,
+        monad-logger >=0.3.41,
+        mtl >=2.3.1,
+        optparse-applicative >=0.18.1.0,
+        resourcet >=1.3.0,
         rio >=0.1.22.0,
         semigroups >=0.20,
-        text >=1.2.5.0,
-        text-metrics >=0.3.2,
-        time >=1.11.1.1,
-        transformers >=0.5.6.2,
-        typed-process >=0.2.10.1,
-        unix >=2.7.2.2,
+        text >=2.1.1,
+        text-metrics >=0.3.3,
+        time >=1.12.2,
+        transformers >=0.6.1.0,
+        typed-process >=0.2.12.0,
+        unix >=2.8.6.0,
         unliftio >=0.2.25.0,
-        unordered-containers >=0.2.19.1,
-        uuid >=1.3.15,
-        yaml >=0.11.8.0
+        unordered-containers >=0.2.20,
+        uuid >=1.3.16,
+        yaml >=0.11.11.2
 
 executable stackctl
     main-is:            Main.hs
@@ -145,9 +145,9 @@
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missed-specialisations
         -Wno-missing-import-lists -Wno-missing-kind-signatures
-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
-        -Wno-prepositive-qualified-module -Wno-unsafe
-        -optP-Wno-nonportable-include-path -threaded -rtsopts
+        -Wno-missing-local-signatures -Wno-missing-role-annotations
+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module
+        -Wno-unsafe -optP-Wno-nonportable-include-path -threaded -rtsopts
         -with-rtsopts=-N
 
     build-depends:
@@ -190,31 +190,31 @@
         -fignore-optim-changes -fwrite-ide-info -Weverything
         -Wno-all-missed-specialisations -Wno-missed-specialisations
         -Wno-missing-import-lists -Wno-missing-kind-signatures
-        -Wno-missing-local-signatures -Wno-missing-safe-haskell-mode
-        -Wno-prepositive-qualified-module -Wno-unsafe
-        -optP-Wno-nonportable-include-path
+        -Wno-missing-local-signatures -Wno-missing-role-annotations
+        -Wno-missing-safe-haskell-mode -Wno-prepositive-qualified-module
+        -Wno-unsafe -optP-Wno-nonportable-include-path
 
     build-depends:
-        Blammo >=1.1.2.3,
+        Blammo >=2.1.1.0,
         Glob >=0.10.2,
-        QuickCheck >=2.14.2,
-        aeson >=2.0.3.0,
+        QuickCheck >=2.14.3,
+        aeson >=2.2.3.0,
         amazonka >=2.0,
         amazonka-cloudformation >=2.0,
         amazonka-ec2 >=2.0,
         amazonka-lambda >=2.0,
         amazonka-mtl >=0.1.1.0,
         base >=4 && <5,
-        bytestring >=0.11.3.1,
-        filepath >=1.4.2.2,
-        hspec >=2.9.7,
+        bytestring >=0.12.1.0,
+        filepath >=1.4.301.0,
+        hspec >=2.11.10,
         hspec-expectations-lifted >=0.10.0,
         hspec-golden >=0.2.1.0,
-        http-types >=0.12.3,
-        lens >=5.1.1,
-        mtl >=2.2.2,
+        http-types >=0.12.4,
+        lens >=5.3.3,
+        mtl >=2.3.1,
         stackctl,
-        text >=1.2.5.0,
-        time >=1.11.1.1,
+        text >=2.1.1,
+        time >=1.12.2,
         unliftio >=0.2.25.0,
-        yaml >=0.11.8.0
+        yaml >=0.11.11.2
diff --git a/test/Stackctl/AWS/EC2Spec.hs b/test/Stackctl/AWS/EC2Spec.hs
--- a/test/Stackctl/AWS/EC2Spec.hs
+++ b/test/Stackctl/AWS/EC2Spec.hs
@@ -23,7 +23,7 @@
             $ Right
             $ newDescribeAvailabilityZonesResponse 200
             & describeAvailabilityZonesResponse_availabilityZones
-            ?~ zones
+              ?~ zones
 
       withMatcher matcher awsEc2DescribeFirstAvailabilityZoneRegionName
         `shouldReturn` "us-east-1"
diff --git a/test/Stackctl/AWS/LambdaSpec.hs b/test/Stackctl/AWS/LambdaSpec.hs
--- a/test/Stackctl/AWS/LambdaSpec.hs
+++ b/test/Stackctl/AWS/LambdaSpec.hs
@@ -33,18 +33,16 @@
           [ SendMatcher (isInvocation "lambda-1")
               $ Right
               $ newInvokeResponse 200
-              & invokeResponse_payload
-              ?~ "<response>"
+              & invokeResponse_payload ?~ "<response>"
           , SendMatcher (isInvocation "lambda-2")
               $ Right
               $ newInvokeResponse 200
-              & invokeResponse_payload
-              ?~ BSL.toStrict (encode lambdaError)
+              & invokeResponse_payload ?~ BSL.toStrict (encode lambdaError)
           , SendMatcher (isInvocation "lambda-3")
               $ Right
               $ newInvokeResponse 500
               & (invokeResponse_payload ?~ "<response>")
-              . (invokeResponse_functionError ?~ "<error>")
+                . (invokeResponse_functionError ?~ "<error>")
           ]
 
       withMatchers matchers $ do
diff --git a/test/Stackctl/ConfigSpec.hs b/test/Stackctl/ConfigSpec.hs
--- a/test/Stackctl/ConfigSpec.hs
+++ b/test/Stackctl/ConfigSpec.hs
@@ -32,8 +32,7 @@
       case result of
         Left err -> do
           expectationFailure
-            $ "Expected to load a Config, got error: "
-            <> show err
+            $ "Expected to load a Config, got error: " <> show err
         Right config -> do
           configParameters config
             `shouldBe` Just (toParametersYaml [("Some", Just "Parameter")])
@@ -56,15 +55,15 @@
         Right config =
           loadConfigFromBytes
             $ "defaults:"
-            <> "\n  tags:"
-            <> "\n    From: Defaults"
-            <> "\n    Keep: \"You?\""
+              <> "\n  tags:"
+              <> "\n    From: Defaults"
+              <> "\n    Keep: \"You?\""
 
         Just tags = ssyTags (applyConfig config specYaml)
 
       tags
         `shouldBe` toTagsYaml
-          [("From", "Defaults"), ("Hi", "There"), ("Keep", "Me")]
+          [("Hi", "There"), ("From", "Defaults"), ("Keep", "Me")]
 
 loadConfigFromLines :: MonadError ConfigError m => [ByteString] -> m Config
 loadConfigFromLines = loadConfigFromBytes . mconcat . map (<> "\n")
diff --git a/test/Stackctl/Test/App.hs b/test/Stackctl/Test/App.hs
--- a/test/Stackctl/Test/App.hs
+++ b/test/Stackctl/Test/App.hs
@@ -15,6 +15,7 @@
 
 import Stackctl.Prelude
 
+import Blammo.Logging.LogSettings (defaultLogSettings)
 import Blammo.Logging.Logger (newTestLogger)
 import Control.Lens ((?~))
 import Control.Monad.AWS
@@ -92,5 +93,4 @@
   "stacks"
     </> "0123456789.test"
     </> "us-east-1"
-    </> unpack base
-    <.> "yaml"
+    </> unpack base <.> "yaml"
