diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## v0.3.1
+
+API changes:
+* Re-export `BoxSpec` and `BoxContent` from `Skeletest.Plugin`
+
+Runtime changes:
+* Tweak formatting of test failures/errors
+
 ## v0.3.0
 
 GHC support:
diff --git a/skeletest.cabal b/skeletest.cabal
--- a/skeletest.cabal
+++ b/skeletest.cabal
@@ -1,7 +1,7 @@
 cabal-version: 3.0
 
 name: skeletest
-version: 0.3.0
+version: 0.3.1
 synopsis: Batteries-included, opinionated test framework
 description: Batteries-included, opinionated test framework. See README.md for more details.
 homepage: https://github.com/brandonchinn178/skeletest#readme
@@ -48,10 +48,10 @@
     Skeletest.Internal.Preprocessor
     Skeletest.Internal.Snapshot
     Skeletest.Internal.Spec
+    Skeletest.Internal.Spec.Output
     Skeletest.Internal.TestInfo
     Skeletest.Internal.TestRunner
     Skeletest.Internal.TestTargets
-    Skeletest.Internal.Utils.BoxDrawing
     Skeletest.Internal.Utils.Color
     Skeletest.Internal.Utils.Diff
     Skeletest.Internal.Utils.HList
diff --git a/src/Skeletest/Internal/Capture.hs b/src/Skeletest/Internal/Capture.hs
--- a/src/Skeletest/Internal/Capture.hs
+++ b/src/Skeletest/Internal/Capture.hs
@@ -28,11 +28,11 @@
   noCleanup,
   withCleanup,
  )
+import Skeletest.Internal.Spec.Output (BoxSpecContent (..))
 import Skeletest.Internal.TestRunner (
   TestResult (..),
   TestResultMessage (..),
  )
-import Skeletest.Internal.Utils.BoxDrawing (BoxSpecContent (..))
 import System.Directory (removePathForcibly)
 import System.IO qualified as IO
 import UnliftIO.Exception (finally)
@@ -85,7 +85,7 @@
   updateResult output result =
     result
       { testResultMessage =
-          TestResultMessageSection . concat $
+          TestResultMessageBox . concat $
             [ toBoxContents (testResultMessage result)
             , renderOutput output
             ]
@@ -93,7 +93,7 @@
   toBoxContents = \case
     TestResultMessageNone -> []
     TestResultMessageInline msg -> [BoxText msg]
-    TestResultMessageSection box -> box
+    TestResultMessageBox box -> box
   renderOutput (stdout, stderr) =
     concat
       [ if Text.null stdout then [] else [BoxHeader "Captured stdout", BoxText stdout]
diff --git a/src/Skeletest/Internal/Spec.hs b/src/Skeletest/Internal/Spec.hs
--- a/src/Skeletest/Internal/Spec.hs
+++ b/src/Skeletest/Internal/Spec.hs
@@ -50,6 +50,13 @@
   SomeMarker (..),
   findMarker,
  )
+import Skeletest.Internal.Spec.Output (
+  reportGroup,
+  reportTestInProgress,
+  reportTestResultWithBoxMessage,
+  reportTestResultWithInlineMessage,
+  reportTestResultWithoutMessage,
+ )
 import Skeletest.Internal.TestInfo (TestInfo (TestInfo), withTestInfo)
 import Skeletest.Internal.TestInfo qualified as TestInfo
 import Skeletest.Internal.TestRunner (
@@ -60,11 +67,10 @@
  )
 import Skeletest.Internal.TestTargets (TestTarget, TestTargets, matchesTest)
 import Skeletest.Internal.TestTargets qualified as TestTargets
-import Skeletest.Internal.Utils.BoxDrawing (drawBox)
 import Skeletest.Internal.Utils.Color qualified as Color
 import Skeletest.Plugin (Hooks (..), defaultHooks)
 import Skeletest.Prop.Internal (Property)
-import System.IO qualified as IO
+import System.Console.Terminal.Size qualified as Term
 import UnliftIO.Exception (
   finally,
   fromException,
@@ -164,12 +170,11 @@
   runTree baseTestInfo = \case
     SpecGroup{..} -> do
       let lvl = getIndentLevel baseTestInfo
-      Text.putStrLn $ indent lvl groupLabel
+      reportGroup lvl groupLabel
       runTrees baseTestInfo{TestInfo.testContexts = TestInfo.testContexts baseTestInfo <> [groupLabel]} groupTrees
     SpecTest{..} -> do
       let lvl = getIndentLevel baseTestInfo
-      Text.putStr $ indent lvl (testName <> ": ")
-      IO.hFlush IO.stdout
+      reportTestInProgress lvl testName
 
       let testInfo =
             baseTestInfo
@@ -181,11 +186,14 @@
           tid <- myThreadId
           runTest testInfo testAction `finally` cleanupFixtures (PerTestFixtureKey tid)
 
-      Text.putStrLn testResultLabel
       case testResultMessage of
-        TestResultMessageNone -> pure ()
-        TestResultMessageInline msg -> Text.putStrLn $ indent (lvl + 1) msg
-        TestResultMessageSection box -> drawBox box >>= Text.putStrLn
+        TestResultMessageNone -> do
+          reportTestResultWithoutMessage testResultLabel
+        TestResultMessageInline msg -> do
+          reportTestResultWithInlineMessage lvl testResultLabel msg
+        TestResultMessageBox box -> do
+          termSize <- Term.size
+          reportTestResultWithBoxMessage termSize lvl testName testResultLabel box
       pure testResultSuccess
 
   runTest info action =
@@ -200,7 +208,6 @@
               Nothing -> testResultFromError e
 
   getIndentLevel testInfo = length (TestInfo.testContexts testInfo) + 1 -- +1 to include the module name
-  indent lvl = Text.intercalate "\n" . map (Text.replicate (lvl * 4) " " <>) . Text.splitOn "\n"
 
 {----- Entrypoint -----}
 
diff --git a/src/Skeletest/Internal/Spec/Output.hs b/src/Skeletest/Internal/Spec/Output.hs
new file mode 100644
--- /dev/null
+++ b/src/Skeletest/Internal/Spec/Output.hs
@@ -0,0 +1,144 @@
+{-# LANGUAGE LambdaCase #-}
+{-# LANGUAGE OverloadedStrings #-}
+
+module Skeletest.Internal.Spec.Output (
+  reportGroup,
+  reportTestInProgress,
+  reportTestResultWithoutMessage,
+  reportTestResultWithInlineMessage,
+  reportTestResultWithBoxMessage,
+  renderPrettyFailure,
+  BoxSpec,
+  BoxSpecContent (..),
+  IndentLevel,
+  indent,
+) where
+
+import Data.Maybe (listToMaybe)
+import Data.Text (Text)
+import Data.Text qualified as Text
+import Data.Text.IO qualified as Text
+import System.Console.Terminal.Size qualified as Term
+import System.IO qualified as IO
+import UnliftIO.Exception (SomeException, try)
+
+reportGroup :: IndentLevel -> Text -> IO ()
+reportGroup lvl name = do
+  Text.putStrLn $ indent lvl name
+
+reportTestInProgress :: IndentLevel -> Text -> IO ()
+reportTestInProgress lvl testName = do
+  Text.putStr $ indent lvl (testName <> ": ")
+  IO.hFlush IO.stdout
+
+reportTestResultWithoutMessage :: Text -> IO ()
+reportTestResultWithoutMessage testResultLabel = do
+  Text.putStrLn testResultLabel
+
+reportTestResultWithInlineMessage :: IndentLevel -> Text -> Text -> IO ()
+reportTestResultWithInlineMessage lvl testResultLabel testResultMessage = do
+  Text.putStrLn testResultLabel
+  Text.putStrLn $ indent (lvl + 1) testResultMessage
+
+reportTestResultWithBoxMessage :: Maybe (Term.Window Int) -> IndentLevel -> Text -> Text -> BoxSpec -> IO ()
+reportTestResultWithBoxMessage termSize lvl testName testResultLabel box = do
+  if null box
+    then do
+      Text.putStrLn testResultLabel
+    else do
+      Text.putStr "\r"
+      Text.putStrLn $ drawBoxHeader lvl testName <> ": " <> testResultLabel
+      Text.putStrLn $ drawBoxBody termSize box
+
+type IndentLevel = Int
+
+indentSize :: Int
+indentSize = 4
+
+indentWith :: Text -> IndentLevel -> Text -> Text
+indentWith fill lvl = Text.intercalate "\n" . map (Text.replicate (lvl * indentSize) fill <>) . Text.splitOn "\n"
+
+indent :: IndentLevel -> Text -> Text
+indent = indentWith " "
+
+-- | Render a test failure like:
+--
+-- @
+-- At test/Skeletest/Internal/TestTargetsSpec.hs:19:
+-- |
+-- |           parseTestTargets input `shouldBe` Right (Just expected)
+-- |                                   ^^^^^^^^
+--
+-- Right 1 ≠ Left 1
+-- @
+renderPrettyFailure ::
+  -- | Message
+  Text ->
+  -- | Failure context
+  [Text] ->
+  -- | Call stack (file, line, startCol, endCol)
+  [(FilePath, Int, Int, Int)] ->
+  IO Text
+renderPrettyFailure msg ctx callstack = do
+  prettyStackTrace <- mapM renderCallLine . reverse $ callstack
+  pure . Text.intercalate "\n\n" . concat $
+    [ prettyStackTrace
+    , if null ctx
+        then []
+        else [Text.intercalate "\n" $ reverse ctx]
+    , [msg]
+    ]
+ where
+  renderCallLine (path, lineNum, startCol, endCol) = do
+    mLine <-
+      try (Text.readFile path) >>= \case
+        Right srcFile -> pure $ getLineNum lineNum srcFile
+        Left (_ :: SomeException) -> pure Nothing
+    let (srcLine, pointerLine) =
+          case mLine of
+            Just line ->
+              ( line
+              , Text.replicate (startCol - 1) " " <> Text.replicate (endCol - startCol) "^"
+              )
+            Nothing ->
+              ( "<unknown line>"
+              , ""
+              )
+
+    pure . Text.intercalate "\n" $
+      [ Text.pack path <> ":" <> (Text.pack . show) lineNum <> ":"
+      , "│"
+      , "│ " <> srcLine
+      , "│ " <> pointerLine
+      ]
+
+  getLineNum n = listToMaybe . take 1 . drop (n - 1) . Text.lines
+
+type BoxSpec = [BoxSpecContent]
+
+data BoxSpecContent
+  = BoxText Text
+  | BoxHeader Text
+  deriving (Show, Eq)
+
+drawBoxHeader :: IndentLevel -> Text -> Text
+drawBoxHeader lvl testName = "╭" <> Text.drop 2 (indentWith "─" lvl "") <> " " <> testName
+
+drawBoxBody :: Maybe (Term.Window Int) -> BoxSpec -> Text
+drawBoxBody termSize boxContents = Text.intercalate "\n" $ concatMap draw boxContents <> [footer]
+ where
+  termWidth = maybe 80 Term.width termSize
+  width =
+    maximum . (termWidth :) . flip concatMap boxContents $ \case
+      BoxHeader s -> [Text.length s + indentSize + 2]
+      BoxText s -> [Text.length line + 2 | line <- Text.lines s]
+
+  footer = "╰" <> Text.replicate (width - 1) "─"
+
+  draw = \case
+    BoxHeader s ->
+      [ drawLine ""
+      , "╞═══ " <> s
+      ]
+    BoxText s -> map drawLine $ Text.lines s
+  drawLine s = "│ " <> s
diff --git a/src/Skeletest/Internal/TestRunner.hs b/src/Skeletest/Internal/TestRunner.hs
--- a/src/Skeletest/Internal/TestRunner.hs
+++ b/src/Skeletest/Internal/TestRunner.hs
@@ -20,17 +20,19 @@
 
 import Control.Monad (guard)
 import Control.Monad.IO.Class (MonadIO)
-import Data.Maybe (listToMaybe)
 import Data.Text (Text)
 import Data.Text qualified as Text
-import Data.Text.IO qualified as Text
 import Data.Typeable (typeOf)
 import GHC.IO.Exception qualified as GHC
 import GHC.Stack (CallStack)
 import GHC.Stack qualified as GHC
 import Skeletest.Internal.Error (SkeletestError)
+import Skeletest.Internal.Spec.Output (
+  BoxSpec,
+  BoxSpecContent (..),
+  renderPrettyFailure,
+ )
 import Skeletest.Internal.TestInfo (TestInfo)
-import Skeletest.Internal.Utils.BoxDrawing (BoxSpec, BoxSpecContent (..))
 import Skeletest.Internal.Utils.Color qualified as Color
 import Text.Read (readMaybe)
 import UnliftIO.Exception (
@@ -38,7 +40,6 @@
   SomeException (..),
   displayException,
   fromException,
-  try,
  )
 
 {----- Testable -----}
@@ -65,7 +66,7 @@
 data TestResultMessage
   = TestResultMessageNone
   | TestResultMessageInline Text
-  | TestResultMessageSection BoxSpec
+  | TestResultMessageBox BoxSpec
 
 testResultPass :: TestResult
 testResultPass =
@@ -82,7 +83,7 @@
     TestResult
       { testResultSuccess = False
       , testResultLabel = Color.red "FAIL"
-      , testResultMessage = TestResultMessageSection [BoxText msg]
+      , testResultMessage = TestResultMessageBox [BoxText msg]
       }
 
 testResultFromError :: SomeException -> IO TestResult
@@ -92,7 +93,7 @@
     TestResult
       { testResultSuccess = False
       , testResultLabel = Color.red "ERROR"
-      , testResultMessage = TestResultMessageSection [BoxText msg]
+      , testResultMessage = TestResultMessageBox [BoxText msg]
       }
  where
   renderMsg
@@ -184,55 +185,3 @@
     [ (srcLocFile, srcLocStartLine, srcLocStartCol, srcLocEndCol)
     | (_, GHC.SrcLoc{..}) <- GHC.getCallStack callStack
     ]
-
--- | Render a test failure like:
---
--- @
--- At test/Skeletest/Internal/TestTargetsSpec.hs:19:
--- |
--- |           parseTestTargets input `shouldBe` Right (Just expected)
--- |                                   ^^^^^^^^
---
--- Right 1 ≠ Left 1
--- @
-renderPrettyFailure ::
-  -- | Message
-  Text ->
-  FailContext ->
-  -- | Call stack (file, line, startCol, endCol)
-  [(FilePath, Int, Int, Int)] ->
-  IO Text
-renderPrettyFailure msg ctx callstack = do
-  prettyStackTrace <- mapM renderCallLine . reverse $ callstack
-  pure . Text.intercalate "\n\n" . concat $
-    [ prettyStackTrace
-    , if null ctx
-        then []
-        else [Text.intercalate "\n" $ reverse ctx]
-    , [msg]
-    ]
- where
-  renderCallLine (path, lineNum, startCol, endCol) = do
-    mLine <-
-      try (Text.readFile path) >>= \case
-        Right srcFile -> pure $ getLineNum lineNum srcFile
-        Left (_ :: SomeException) -> pure Nothing
-    let (srcLine, pointerLine) =
-          case mLine of
-            Just line ->
-              ( line
-              , Text.replicate (startCol - 1) " " <> Text.replicate (endCol - startCol) "^"
-              )
-            Nothing ->
-              ( "<unknown line>"
-              , ""
-              )
-
-    pure . Text.intercalate "\n" $
-      [ Text.pack path <> ":" <> (Text.pack . show) lineNum <> ":"
-      , "|"
-      , "| " <> srcLine
-      , "| " <> pointerLine
-      ]
-
-  getLineNum n = listToMaybe . take 1 . drop (n - 1) . Text.lines
diff --git a/src/Skeletest/Internal/Utils/BoxDrawing.hs b/src/Skeletest/Internal/Utils/BoxDrawing.hs
deleted file mode 100644
--- a/src/Skeletest/Internal/Utils/BoxDrawing.hs
+++ /dev/null
@@ -1,49 +0,0 @@
-{-# LANGUAGE LambdaCase #-}
-{-# LANGUAGE OverloadedStrings #-}
-
-module Skeletest.Internal.Utils.BoxDrawing (
-  BoxSpec,
-  BoxSpecContent (..),
-  drawBox,
-) where
-
-import Data.Text (Text)
-import Data.Text qualified as Text
-import System.Console.Terminal.Size qualified as Term
-
-type BoxSpec = [BoxSpecContent]
-
-data BoxSpecContent
-  = BoxText Text
-  | BoxHeader Text
-  deriving (Show, Eq)
-
-drawBox :: BoxSpec -> IO Text
-drawBox box = do
-  width <- maybe 80 (max 40 . Term.width) <$> Term.size
-  pure $ drawBox' width box
-
-drawBox' :: Int -> BoxSpec -> Text
-drawBox' width boxContents = Text.intercalate "\n" $ [header] <> concatMap draw boxContents <> [footer]
- where
-  header = "╔" <> Text.replicate (width - 2) "═" <> "╗"
-  footer = "╚" <> Text.replicate (width - 2) "═" <> "╝"
-
-  draw = \case
-    BoxHeader s ->
-      [ drawLine ""
-      , "╟─" <> cpad (width - 4) "─" ("⟨ " <> s <> " ⟩") <> "─╢"
-      ]
-    BoxText s ->
-      [ drawLine line
-      | rawLine <- Text.lines s
-      , line <- if Text.null rawLine then [""] else Text.chunksOf (width - 4) rawLine
-      ]
-  drawLine s = "║ " <> rpad (width - 4) " " s <> " ║"
-
-  rpad n fill s = s <> Text.replicate (n - Text.length s) fill
-  cpad n fill s =
-    let total = n - Text.length s
-        left = total `div` 2
-        right = total - left
-     in Text.replicate left fill <> s <> Text.replicate right fill
diff --git a/src/Skeletest/Plugin.hs b/src/Skeletest/Plugin.hs
--- a/src/Skeletest/Plugin.hs
+++ b/src/Skeletest/Plugin.hs
@@ -11,6 +11,8 @@
   -- ** TestResult
   TestResult (..),
   TestResultMessage (..),
+  BoxSpec,
+  BoxSpecContent (..),
 
   -- ** TestInfo
   TestInfo (..),
@@ -23,6 +25,7 @@
 import Skeletest.Internal.CLI (Flag)
 import Skeletest.Internal.Markers (findMarker, hasMarkerNamed)
 import Skeletest.Internal.Snapshot (SnapshotRenderer)
+import Skeletest.Internal.Spec.Output (BoxSpec, BoxSpecContent (..))
 import Skeletest.Internal.TestInfo (TestInfo (..))
 import Skeletest.Internal.TestRunner (TestResult (..), TestResultMessage (..))
 
diff --git a/test/Skeletest/AssertionsSpec.hs b/test/Skeletest/AssertionsSpec.hs
--- a/test/Skeletest/AssertionsSpec.hs
+++ b/test/Skeletest/AssertionsSpec.hs
@@ -26,8 +26,7 @@
         , "spec = it \"should fail\" $ 1 `shouldBe` (2 :: Int)"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -45,8 +44,7 @@
         , "spec = it \"should fail\" $ 1 `shouldNotBe` (1 :: Int)"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -65,8 +63,7 @@
         , "spec = it \"should fail\" $ (-1) `shouldSatisfy` P.gt (0 :: Int)"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -85,8 +82,7 @@
         , "spec = it \"should fail\" $ 1 `shouldNotSatisfy` P.gt (0 :: Int)"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -103,8 +99,7 @@
         , "    1 `shouldBe` (2 :: Int)"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -119,8 +114,7 @@
         , "spec = it \"should fail\" $ failTest \"error message\""
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -141,8 +135,7 @@
       , "expectGT x actual = actual `shouldSatisfy` P.gt x"
       ]
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stderr `shouldBe` ""
     stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -159,8 +152,7 @@
       , "  x `shouldBe` True"
       ]
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stderr `shouldBe` ""
     stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -177,8 +169,7 @@
       , "  pure ()"
       ]
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stderr `shouldBe` ""
     sanitizeTraceback stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -188,8 +179,12 @@
 #if __GLASGOW_HASKELL__ == 910
 sanitizeTraceback s =
   let (pre, post) = break (Text.pack "HasCallStack backtrace:" `Text.isInfixOf`) $ Text.lines $ Text.pack s
-      (_, post2) = break (Text.pack "╚" `Text.isPrefixOf`) $ drop 1 post
-   in Text.unpack . Text.unlines $ pre ++ post2
+      (_, post2) = break (Text.pack "╰" `Text.isPrefixOf`) $ drop 1 post
+      post2' =
+        case post2 of
+          [] -> []
+          l : ls -> Text.take 80 l : ls
+   in Text.unpack . Text.unlines $ pre ++ post2'
 #else
 sanitizeTraceback = id
 #endif
diff --git a/test/Skeletest/Internal/CLISpec.hs b/test/Skeletest/Internal/CLISpec.hs
--- a/test/Skeletest/Internal/CLISpec.hs
+++ b/test/Skeletest/Internal/CLISpec.hs
@@ -84,8 +84,7 @@
         , "  pure ()"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
diff --git a/test/Skeletest/Internal/CaptureSpec.hs b/test/Skeletest/Internal/CaptureSpec.hs
--- a/test/Skeletest/Internal/CaptureSpec.hs
+++ b/test/Skeletest/Internal/CaptureSpec.hs
@@ -64,10 +64,9 @@
         , "    " <> render_hPutStrLn handle "line2"
         , "    1 `shouldBe` 2"
         ]
-      (code, stdout, stderr) <- runTests runner []
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
-      code `shouldBe` ExitFailure 1
 
     integration . it "is rendered on test error" $ do
       runner <- getFixture
@@ -88,10 +87,9 @@
         , "    Just _ <- pure Nothing"
         , "    pure ()"
         ]
-      (code, stdout, stderr) <- runTests runner []
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
-      code `shouldBe` ExitFailure 1
 
     integration . it "is not captured with --capture-output=off" $ do
       runner <- getFixture
@@ -141,10 +139,8 @@
         , "    s <- output." <> func
         , "    s `shouldBe` " <> show "test1\ntest2\n"
         ]
-      (code, stdout, stderr) <- runTests runner []
-      stderr `shouldBe` ""
-      context stdout $
-        code `shouldBe` ExitSuccess
+      _ <- expectSuccess $ runTests runner []
+      pure ()
 
 fixtureReadSpec :: (String, String) -> Spec
 fixtureReadSpec (handle, func) =
@@ -173,10 +169,8 @@
         , "    s <- output." <> func
         , "    s `shouldBe` " <> show "test2\n"
         ]
-      (code, stdout, stderr) <- runTests runner []
-      stderr `shouldBe` ""
-      context stdout $
-        code `shouldBe` ExitSuccess
+      _ <- expectSuccess $ runTests runner []
+      pure ()
 
 render_hPutStrLn :: String -> String -> String
 render_hPutStrLn handle s = "IO.hPutStrLn IO." <> handle <> " " <> show s
diff --git a/test/Skeletest/Internal/FixturesSpec.hs b/test/Skeletest/Internal/FixturesSpec.hs
--- a/test/Skeletest/Internal/FixturesSpec.hs
+++ b/test/Skeletest/Internal/FixturesSpec.hs
@@ -51,7 +51,6 @@
         , "  pure ()"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
diff --git a/test/Skeletest/Internal/SnapshotSpec.hs b/test/Skeletest/Internal/SnapshotSpec.hs
--- a/test/Skeletest/Internal/SnapshotSpec.hs
+++ b/test/Skeletest/Internal/SnapshotSpec.hs
@@ -43,8 +43,7 @@
       ]
     addTestFile runner "__snapshots__/ExampleSpec.snap.md" ["asdf"]
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stderr `shouldBe` ""
     stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -105,8 +104,7 @@
       , "```"
       ]
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stderr `shouldBe` ""
     stdout `shouldSatisfy` P.matchesSnapshot
 
diff --git a/test/Skeletest/Internal/SpecSpec.hs b/test/Skeletest/Internal/SpecSpec.hs
--- a/test/Skeletest/Internal/SpecSpec.hs
+++ b/test/Skeletest/Internal/SpecSpec.hs
@@ -52,8 +52,7 @@
         , "  it \"should fail too\" $ pure ()"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
diff --git a/test/Skeletest/Internal/__snapshots__/CLISpec.snap.md b/test/Skeletest/Internal/__snapshots__/CLISpec.snap.md
--- a/test/Skeletest/Internal/__snapshots__/CLISpec.snap.md
+++ b/test/Skeletest/Internal/__snapshots__/CLISpec.snap.md
@@ -4,9 +4,7 @@
 
 ```
 ./ExampleSpec.hs
-    should error: ERROR
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ CLI flag 'my-flag' was not registered. Did you add it to cliFlags in Main.hs ║
-║ ?                                                                            ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should error: ERROR
+│ CLI flag 'my-flag' was not registered. Did you add it to cliFlags in Main.hs?
+╰───────────────────────────────────────────────────────────────────────────────
 ```
diff --git a/test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md b/test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md
--- a/test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md
+++ b/test/Skeletest/Internal/__snapshots__/FixturesSpec.snap.md
@@ -4,9 +4,7 @@
 
 ```
 ./ExampleSpec.hs
-    should error: ERROR
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ Found circular dependency when resolving fixtures: FixtureA -> FixtureB -> F ║
-║ ixtureD -> FixtureA                                                          ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should error: ERROR
+│ Found circular dependency when resolving fixtures: FixtureA -> FixtureB -> FixtureD -> FixtureA
+╰────────────────────────────────────────────────────────────────────────────────────────────────
 ```
diff --git a/test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md b/test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md
--- a/test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md
+++ b/test/Skeletest/Internal/__snapshots__/SnapshotSpec.snap.md
@@ -4,10 +4,9 @@
 
 ```
 ./ExampleSpec.hs
-    should error: ERROR
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ Snapshot file was corrupted: ./__snapshots__/ExampleSpec.snap.md             ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should error: ERROR
+│ Snapshot file was corrupted: ./__snapshots__/ExampleSpec.snap.md
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## renders JSON values
@@ -25,23 +24,21 @@
 
 ```
 ./ExampleSpec.hs
-    fails: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:7:                                                          ║
-║ |                                                                            ║
-║ |   unlines ["new1", "same1", "same2", "new2"] `shouldSatisfy` P.matchesSnap ║
-║ shot                                                                         ║
-║ |                                              ^^^^^^^^^^^^^^^               ║
-║                                                                              ║
-║ Result differed from snapshot. Update snapshot with --update.                ║
-║ --- expected                                                                 ║
-║ +++ actual                                                                   ║
-║ @@ -1,4 +1,4 @@                                                              ║
-║ +new1                                                                        ║
-║  same1                                                                       ║
-║ -old1                                                                        ║
-║  same2                                                                       ║
-║ -old2                                                                        ║
-║ +new2                                                                        ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── fails: FAIL
+│ ./ExampleSpec.hs:7:
+│ │
+│ │   unlines ["new1", "same1", "same2", "new2"] `shouldSatisfy` P.matchesSnapshot
+│ │                                              ^^^^^^^^^^^^^^^
+│ 
+│ Result differed from snapshot. Update snapshot with --update.
+│ --- expected
+│ +++ actual
+│ @@ -1,4 +1,4 @@
+│ +new1
+│  same1
+│ -old1
+│  same2
+│ -old2
+│ +new2
+╰─────────────────────────────────────────────────────────────────────────────────
 ```
diff --git a/test/Skeletest/MainSpec.hs b/test/Skeletest/MainSpec.hs
--- a/test/Skeletest/MainSpec.hs
+++ b/test/Skeletest/MainSpec.hs
@@ -14,8 +14,7 @@
     setMainFile runner []
     addTestFile runner "ExampleSpec.hs" (minimalTest "ExampleSpec")
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stdout `shouldBe` ""
     normalizePluginError stderr `shouldSatisfy` P.matchesSnapshot
 
@@ -44,8 +43,7 @@
       ]
     addTestFile runner "ExampleSpec.hs" (minimalTest "ExampleSpec")
 
-    (code, stdout, stderr) <- runTests runner []
-    code `shouldBe` ExitFailure 1
+    (stdout, stderr) <- expectFailure $ runTests runner []
     stdout `shouldBe` ""
     normalizeGhc29916 stderr `shouldSatisfy` P.matchesSnapshot
 
diff --git a/test/Skeletest/PredicateSpec.hs b/test/Skeletest/PredicateSpec.hs
--- a/test/Skeletest/PredicateSpec.hs
+++ b/test/Skeletest/PredicateSpec.hs
@@ -143,8 +143,7 @@
           , "  User \"alice\" `shouldSatisfy` P.con User{name = P.eq \"\"}"
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stderr `shouldBe` ""
         stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -162,8 +161,7 @@
           , "  User \"alice\" `shouldSatisfy` P.con User{foo = P.eq \"\"}"
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stdout `shouldBe` ""
         stderr `shouldSatisfy` P.matchesSnapshot
 
@@ -181,8 +179,7 @@
           , "  User \"alice\" (Just 1) `shouldSatisfy` P.con (User (P.eq \"\"))"
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stdout `shouldBe` ""
         (normalizeConFailure . normalizeVars) stderr `shouldSatisfy` P.matchesSnapshot
 
@@ -198,8 +195,7 @@
           , "  \"\" `shouldSatisfy` P.con \"\""
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stdout `shouldBe` ""
         stderr `shouldSatisfy` P.matchesSnapshot
 
@@ -215,8 +211,7 @@
           , "  \"\" `shouldSatisfy` P.con"
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stdout `shouldBe` ""
         stderr `shouldSatisfy` P.matchesSnapshot
 
@@ -232,8 +227,7 @@
           , "  \"\" `shouldSatisfy` P.con 1 2"
           ]
 
-        (code, stdout, stderr) <- runTests runner []
-        code `shouldBe` ExitFailure 1
+        (stdout, stderr) <- expectFailure $ runTests runner []
         stdout `shouldBe` ""
         stderr `shouldSatisfy` P.matchesSnapshot
 
diff --git a/test/Skeletest/PropSpec.hs b/test/Skeletest/PropSpec.hs
--- a/test/Skeletest/PropSpec.hs
+++ b/test/Skeletest/PropSpec.hs
@@ -22,8 +22,7 @@
         , "  discard"
         ]
 
-      (code, stdout, stderr) <- runTests runner []
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner []
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
 
@@ -49,7 +48,6 @@
         , "    (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)"
         ]
 
-      (code, stdout, stderr) <- runTests runner ["--seed=0:0"]
-      code `shouldBe` ExitFailure 1
+      (stdout, stderr) <- expectFailure $ runTests runner ["--seed=0:0"]
       stderr `shouldBe` ""
       stdout `shouldSatisfy` P.matchesSnapshot
diff --git a/test/Skeletest/TestUtils/Integration.hs b/test/Skeletest/TestUtils/Integration.hs
--- a/test/Skeletest/TestUtils/Integration.hs
+++ b/test/Skeletest/TestUtils/Integration.hs
@@ -13,7 +13,9 @@
 
   -- * runTests
   runTests,
+  expectCode,
   expectSuccess,
+  expectFailure,
 
   -- * Re-exports
   ExitCode (..),
@@ -107,15 +109,25 @@
       ]
   setCWD dir p = p{cwd = Just dir}
 
-  sanitize = Text.unpack . stripControlChars . Text.strip . Text.pack
+  sanitize = Text.unpack . stripOverwrites . stripControlChars . Text.strip . Text.pack
+  stripOverwrites s =
+    case Text.breakOn "\r" s of
+      (_, "") -> s
+      (pre, post) -> Text.dropWhileEnd (/= '\n') pre <> stripOverwrites (Text.drop 1 post)
   stripControlChars s =
     case Text.breakOn "\x1b" s of
       (_, "") -> s
       (pre, post) -> pre <> stripControlChars (Text.drop 1 . Text.dropWhile (/= 'm') $ post)
 
-expectSuccess :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)
-expectSuccess m = do
+expectCode :: (HasCallStack) => ExitCode -> IO (ExitCode, String, String) -> IO (String, String)
+expectCode expected m = do
   (code, stdout, stderr) <- m
   context (unlines ["===== stdout =====", stdout, "===== stderr =====", stderr]) $
-    code `shouldBe` ExitSuccess
+    code `shouldBe` expected
   pure (stdout, stderr)
+
+expectSuccess :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)
+expectSuccess = expectCode ExitSuccess
+
+expectFailure :: (HasCallStack) => IO (ExitCode, String, String) -> IO (String, String)
+expectFailure = expectCode $ ExitFailure 1
diff --git a/test/Skeletest/__snapshots__/AssertionsSpec.snap.md b/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
--- a/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
+++ b/test/Skeletest/__snapshots__/AssertionsSpec.snap.md
@@ -4,154 +4,145 @@
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:7:                                                          ║
-║ |                                                                            ║
-║ |     1 `shouldBe` (2 :: Int)                                                ║
-║ |       ^^^^^^^^^^                                                           ║
-║                                                                              ║
-║ hello                                                                        ║
-║ world                                                                        ║
-║                                                                              ║
-║ 1 ≠ 2                                                                        ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:7:
+│ │
+│ │     1 `shouldBe` (2 :: Int)
+│ │       ^^^^^^^^^^
+│ 
+│ hello
+│ world
+│ 
+│ 1 ≠ 2
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## failTest / should show failure
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:5:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ failTest "error message"                         ║
-║ |                           ^^^^^^^^                                         ║
-║                                                                              ║
-║ error message                                                                ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:5:
+│ │
+│ │ spec = it "should fail" $ failTest "error message"
+│ │                           ^^^^^^^^
+│ 
+│ error message
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shouldBe / should show helpful failure
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:5:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ 1 `shouldBe` (2 :: Int)                          ║
-║ |                             ^^^^^^^^^^                                     ║
-║                                                                              ║
-║ 1 ≠ 2                                                                        ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:5:
+│ │
+│ │ spec = it "should fail" $ 1 `shouldBe` (2 :: Int)
+│ │                             ^^^^^^^^^^
+│ 
+│ 1 ≠ 2
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shouldNotBe / should show helpful failure
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:5:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ 1 `shouldNotBe` (1 :: Int)                       ║
-║ |                             ^^^^^^^^^^^^^                                  ║
-║                                                                              ║
-║ 1 = 1                                                                        ║
-║                                                                              ║
-║ Expected:                                                                    ║
-║   ≠ 1                                                                        ║
-║                                                                              ║
-║ Got:                                                                         ║
-║   1                                                                          ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:5:
+│ │
+│ │ spec = it "should fail" $ 1 `shouldNotBe` (1 :: Int)
+│ │                             ^^^^^^^^^^^^^
+│ 
+│ 1 = 1
+│ 
+│ Expected:
+│   ≠ 1
+│ 
+│ Got:
+│   1
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shouldNotSatisfy / should show helpful failure
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:6:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ 1 `shouldNotSatisfy` P.gt (0 :: Int)             ║
-║ |                             ^^^^^^^^^^^^^^^^^^                             ║
-║                                                                              ║
-║ 1 > 0                                                                        ║
-║                                                                              ║
-║ Expected:                                                                    ║
-║   ≯ 0                                                                        ║
-║                                                                              ║
-║ Got:                                                                         ║
-║   1                                                                          ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:6:
+│ │
+│ │ spec = it "should fail" $ 1 `shouldNotSatisfy` P.gt (0 :: Int)
+│ │                             ^^^^^^^^^^^^^^^^^^
+│ 
+│ 1 > 0
+│ 
+│ Expected:
+│   ≯ 0
+│ 
+│ Got:
+│   1
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shouldSatisfy / should show helpful failure
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:6:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ (-1) `shouldSatisfy` P.gt (0 :: Int)             ║
-║ |                                ^^^^^^^^^^^^^^^                             ║
-║                                                                              ║
-║ -1 ≯ 0                                                                       ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:6:
+│ │
+│ │ spec = it "should fail" $ (-1) `shouldSatisfy` P.gt (0 :: Int)
+│ │                                ^^^^^^^^^^^^^^^
+│ 
+│ -1 ≯ 0
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shows backtrace of failed assertions
 
 ```
 ./ExampleSpec.hs
-    should fail: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:6:                                                          ║
-║ |                                                                            ║
-║ | spec = it "should fail" $ expectPositive (-1)                              ║
-║ |                           ^^^^^^^^^^^^^^                                   ║
-║                                                                              ║
-║ ./ExampleSpec.hs:9:                                                          ║
-║ |                                                                            ║
-║ | expectPositive = expectGT 0                                                ║
-║ |                  ^^^^^^^^                                                  ║
-║                                                                              ║
-║ ./ExampleSpec.hs:12:                                                         ║
-║ |                                                                            ║
-║ | expectGT x actual = actual `shouldSatisfy` P.gt x                          ║
-║ |                            ^^^^^^^^^^^^^^^                                 ║
-║                                                                              ║
-║ -1 ≯ 0                                                                       ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: FAIL
+│ ./ExampleSpec.hs:6:
+│ │
+│ │ spec = it "should fail" $ expectPositive (-1)
+│ │                           ^^^^^^^^^^^^^^
+│ 
+│ ./ExampleSpec.hs:9:
+│ │
+│ │ expectPositive = expectGT 0
+│ │                  ^^^^^^^^
+│ 
+│ ./ExampleSpec.hs:12:
+│ │
+│ │ expectGT x actual = actual `shouldSatisfy` P.gt x
+│ │                            ^^^^^^^^^^^^^^^
+│ 
+│ -1 ≯ 0
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shows helpful error on pattern match fail
 
 ```
 ./ExampleSpec.hs
-    should fail: ERROR
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ExampleSpec.hs:7:                                                            ║
-║ |                                                                            ║
-║ |   Just x <- pure Nothing                                                   ║
-║ |   ^^^^^^                                                                   ║
-║                                                                              ║
-║ Pattern match failure in 'do' block                                          ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: ERROR
+│ ExampleSpec.hs:7:
+│ │
+│ │   Just x <- pure Nothing
+│ │   ^^^^^^
+│ 
+│ Pattern match failure in 'do' block
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## shows unrecognized exceptions
 
 ```
 ./ExampleSpec.hs
-    should fail: ERROR
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ Got exception of type `IOException`:                                         ║
-║ unknown-file.txt: openFile: does not exist (No such file or directory)       ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should fail: ERROR
+│ Got exception of type `IOException`:
+│ unknown-file.txt: openFile: does not exist (No such file or directory)
+╰───────────────────────────────────────────────────────────────────────────────
 ```
diff --git a/test/Skeletest/__snapshots__/PredicateSpec.snap.md b/test/Skeletest/__snapshots__/PredicateSpec.snap.md
--- a/test/Skeletest/__snapshots__/PredicateSpec.snap.md
+++ b/test/Skeletest/__snapshots__/PredicateSpec.snap.md
@@ -252,21 +252,20 @@
 
 ```
 ./ExampleSpec.hs
-    should error: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:9:                                                          ║
-║ |                                                                            ║
-║ |   User "alice" `shouldSatisfy` P.con User{name = P.eq ""}                  ║
-║ |                ^^^^^^^^^^^^^^^                                             ║
-║                                                                              ║
-║ "alice" ≠ []                                                                 ║
-║                                                                              ║
-║ Expected:                                                                    ║
-║   matches User{name = (= [])}                                                ║
-║                                                                              ║
-║ Got:                                                                         ║
-║   User "alice"                                                               ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── should error: FAIL
+│ ./ExampleSpec.hs:9:
+│ │
+│ │   User "alice" `shouldSatisfy` P.con User{name = P.eq ""}
+│ │                ^^^^^^^^^^^^^^^
+│ 
+│ "alice" ≠ []
+│ 
+│ Expected:
+│   matches User{name = (= [])}
+│ 
+│ Got:
+│   User "alice"
+╰───────────────────────────────────────────────────────────────────────────────
 ```
 
 ## Data types / list / shows helpful failure messages
diff --git a/test/Skeletest/__snapshots__/PropSpec.snap.md b/test/Skeletest/__snapshots__/PropSpec.snap.md
--- a/test/Skeletest/__snapshots__/PropSpec.snap.md
+++ b/test/Skeletest/__snapshots__/PropSpec.snap.md
@@ -4,51 +4,46 @@
 
 ```
 ./ExampleSpec.hs
-    is isomorphic: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:10:                                                         ║
-║ |                                                                            ║
-║ |     (read . show) P.=== (+ 1) `shouldSatisfy` P.isoWith (Gen.int $ Range.l ║
-║ inear 0 10)                                                                  ║
-║ |                               ^^^^^^^^^^^^^^^                              ║
-║                                                                              ║
-║ Failed after 1 tests.                                                        ║
-║ Rerun with --seed=0:0 to reproduce.                                          ║
-║                                                                              ║
-║ ./ExampleSpec.hs:10:47 ==> 0                                                 ║
-║                                                                              ║
-║ 0 ≠ 1                                                                        ║
-║ where                                                                        ║
-║   0 = (read . show) 0                                                        ║
-║   1 = (+ 1) 0                                                                ║
-╚══════════════════════════════════════════════════════════════════════════════╝
-    is not isomorphic: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ ./ExampleSpec.hs:12:                                                         ║
-║ |                                                                            ║
-║ |     (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.l ║
-║ inear 0 10)                                                                  ║
-║ |                            ^^^^^^^^^^^^^^^^^^                              ║
-║                                                                              ║
-║ Failed after 1 tests.                                                        ║
-║ Rerun with --seed=0:0 to reproduce.                                          ║
-║                                                                              ║
-║ ./ExampleSpec.hs:12:47 ==> 0                                                 ║
-║                                                                              ║
-║ 0 = 0                                                                        ║
-║ where                                                                        ║
-║   0 = (read . show) 0                                                        ║
-║   0 = id 0                                                                   ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── is isomorphic: FAIL
+│ ./ExampleSpec.hs:10:
+│ │
+│ │     (read . show) P.=== (+ 1) `shouldSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)
+│ │                               ^^^^^^^^^^^^^^^
+│ 
+│ Failed after 1 tests.
+│ Rerun with --seed=0:0 to reproduce.
+│ 
+│ ./ExampleSpec.hs:10:47 ==> 0
+│ 
+│ 0 ≠ 1
+│ where
+│   0 = (read . show) 0
+│   1 = (+ 1) 0
+╰────────────────────────────────────────────────────────────────────────────────────────
+╭── is not isomorphic: FAIL
+│ ./ExampleSpec.hs:12:
+│ │
+│ │     (read . show) P.=== id `shouldNotSatisfy` P.isoWith (Gen.int $ Range.linear 0 10)
+│ │                            ^^^^^^^^^^^^^^^^^^
+│ 
+│ Failed after 1 tests.
+│ Rerun with --seed=0:0 to reproduce.
+│ 
+│ ./ExampleSpec.hs:12:47 ==> 0
+│ 
+│ 0 = 0
+│ where
+│   0 = (read . show) 0
+│   0 = id 0
+╰────────────────────────────────────────────────────────────────────────────────────────
 ```
 
 ## setDiscardLimit / sets discard limit
 
 ```
 ./ExampleSpec.hs
-    discards: FAIL
-╔══════════════════════════════════════════════════════════════════════════════╗
-║ Gave up after 10 discards.                                                   ║
-║ Passed 0 tests.                                                              ║
-╚══════════════════════════════════════════════════════════════════════════════╝
+╭── discards: FAIL
+│ Gave up after 10 discards.
+│ Passed 0 tests.
+╰───────────────────────────────────────────────────────────────────────────────
 ```
