diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -24,6 +24,31 @@
 
 [KaC]: <https://keepachangelog.com/en/1.0.0/>
 
+## 0.8.0.0 (2025-01-03)
+
+### Breaking
+
+* Support whitespace-separated tags and items instead of CSV
+* Remove support for GHC 8.6, constraining lower bounds
+* Remove support for GHC 8.4, constraining lower bounds
+* Remove support for GHC 8.2, constraining lower bounds
+* Change minimal Cabal from 1.24 to 3.0
+
+### Non-Breaking
+
+* Bump `aeson` dependency version upper bound
+* Bump `ansi-wl-pprint` dependency version upper bound
+* Bump `base` dependency version upper bound
+* Bump `bytestring` dependency version upper bound
+* Bump `filepath` dependency version upper bound
+* Add support for `optparse-applicative` `0.18`
+* Bump `tasty` dependency version upper bound
+* Bump `text` dependency version upper bound
+* Bump `transformers` dependency version upper bound
+* Bump `ttc` dependency version upper bound
+* Bump `vector` dependency version upper bound
+* Adjust dependency constraints to match tested versions
+
 ## 0.7.0.2 (2022-03-02)
 
 ### Non-Breaking
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 The MIT License
 
-Copyright (c) 2020-2022 Travis Cardwell
+Copyright (c) 2020-2025 Travis Cardwell
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -40,10 +40,13 @@
 Queue Sheet has only been tested on Linux.  It *might* work on Windows and
 macOS.
 
-Queue Sheet uses [XeTeX](https://tug.org/xetex/) to build PDFs.  It is usually
-installed as part of [TeX Live](https://www.tug.org/texlive/).  The LaTeX
-packages used depend entirely on the contents of the template.
+Queue Sheet uses [XeTeX][] to build PDFs.  It is usually installed as part of
+[TeX Live][].  The LaTeX packages used depend entirely on the contents of the
+template.
 
+[XeTeX]: <https://tug.org/xetex/>
+[TeX Live]: <https://www.tug.org/texlive/>
+
 ### Installation
 
 #### `.deb` Package Installation
@@ -80,29 +83,49 @@
 
 ### Usage
 
-See the [`queue-sheet` man page](doc/queue-sheet.1.md) for usage information.
+See the [`queue-sheet` man page][] for usage information.
 
-See the [examples](examples) directory for example queue files, templates, and
-built output.
+See the [examples][] directory for example queue files, templates, and built
+output.
 
-## Project
+[`queue-sheet` man page]: <doc/queue-sheet.1.md>
+[examples]: <examples>
 
-Queue Sheet was written quickly to solve a particular pain point.  There are
-no plans to put the package on Hackage.
+## Project
 
 ### Links
 
 * Hackage: <https://hackage.haskell.org/package/queue-sheet>
 * Stackage: <https://www.stackage.org/package/queue-sheet>
+* Flora: <https://flora.pm/packages/@hackage/queue-sheet>
 * GitHub: <https://github.com/ExtremaIS/queue-sheet-haskell>
 * GitHub Actions CI: <https://github.com/ExtremaIS/queue-sheet-haskell/actions>
 
+### Branches
+
+The `main` branch is reserved for releases.  It may be considered stable, and
+`HEAD` is always the latest release.
+
+The `develop` branch is the primary development branch.  It contains changes
+that have not yet been released, and it is not necessarily stable.
+
+[Hackage revisions][] are made for metadata changes, such as relaxation of
+constraints when new versions of dependencies are released.  The
+`queue-sheet.cabal` metadata in the `main` branch may therefore not match that
+of Hackage.  The `queue-sheet.cabal` metadata in the `develop` branch may
+match, *unless* work is being done on a new release that contains other
+changes.
+
+[Hackage revisions]: <https://github.com/haskell-infra/hackage-trustees/blob/master/revisions-information.md#hackage-metadata-revisions--what-they-are-how-they-work>
+
 ### Tags
 
 All releases are tagged in the `main` branch.  Release tags are signed using
 the
-[`security@extrema.is` GPG key](http://keys.gnupg.net/pks/lookup?op=vindex&fingerprint=on&search=0x1D484E4B4705FADF).
+[`security@extrema.is` GPG key][].
 
+[`security@extrema.is` GPG key]: <https://keyserver.ubuntu.com/pks/lookup?search=0x1D484E4B4705FADF&fingerprint=on&op=index>
+
 ### Contribution
 
 Issues and feature requests are tracked on GitHub:
@@ -112,6 +135,8 @@
 
 ### License
 
-This project is released under the
-[MIT License](https://opensource.org/licenses/MIT) as specified in the
-[`LICENSE`](LICENSE) file.
+This project is released under the [MIT License][] as specified in the
+[`LICENSE`][] file.
+
+[MIT License]: <https://opensource.org/licenses/MIT>
+[`LICENSE`]: <LICENSE>
diff --git a/app/LibOA.hs b/app/LibOA.hs
--- a/app/LibOA.hs
+++ b/app/LibOA.hs
@@ -2,22 +2,28 @@
 -- |
 -- Module      : LibOA
 -- Description : supplementary functions for optparse-applicative
--- Copyright   : Copyright (c) 2019-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2019-2025 Travis Cardwell
 -- License     : MIT
 --
 -- This is a collection of functions that I often use with
 -- @optparse-applicative@.  I do not feel that it is worth maintaining yet
 -- another helper package on Hackage, so I just copy the code to different
 -- projects as required.  If the library grows to a substantial size or others
--- with to use it, I will reconsider.
+-- want to use it, I will reconsider.
 --
--- Revision: 2022-01-02
+-- Revision: 2023-05-26
 ------------------------------------------------------------------------------
 
 {-# LANGUAGE CPP #-}
 {-# LANGUAGE LambdaCase #-}
 {-# LANGUAGE TupleSections #-}
 
+#if defined(MIN_VERSION_ansi_wl_pprint)
+#if MIN_VERSION_ansi_wl_pprint (1,0,2)
+{-# OPTIONS_GHC -Wno-warnings-deprecations #-}
+#endif
+#endif
+
 module LibOA
   ( -- * Options
     -- $Options
@@ -28,14 +34,23 @@
     -- * Help
   , (<||>)
   , section
+  , section'
   , table
   , table_
   , vspace
+    -- ** Compatibility
+  , Doc
+  , (<$$>)
+  , empty
+  , string
+  , vcat
   ) where
 
 -- https://hackage.haskell.org/package/ansi-wl-pprint
+#if !MIN_VERSION_optparse_applicative (0,18,0)
 import qualified Text.PrettyPrint.ANSI.Leijen as Doc
 import Text.PrettyPrint.ANSI.Leijen (Doc)
+#endif
 
 -- https://hackage.haskell.org/package/base
 import Data.List (intersperse, transpose)
@@ -50,8 +65,16 @@
 import qualified Options.Applicative.Builder.Internal as OABI
 #endif
 import qualified Options.Applicative.Common as OAC
+#if MIN_VERSION_optparse_applicative (0,18,0)
+import Options.Applicative.Help.Pretty (Doc)
+#endif
 import qualified Options.Applicative.Types as OAT
 
+-- https://hackage.haskell.org/package/prettyprinter
+#if MIN_VERSION_optparse_applicative (0,18,0)
+import qualified Prettyprinter as Doc
+#endif
+
 ------------------------------------------------------------------------------
 -- $Options
 --
@@ -103,8 +126,12 @@
 commands :: OA.Parser a -> [String]
 commands =
     let go _ opt = case OAT.optMain opt of
+#if MIN_VERSION_optparse_applicative (0,18,0)
+           OAT.CmdReader _ cmdPs -> reverse $ fst <$> cmdPs
+#else
            OAT.CmdReader _ cmds _ -> reverse cmds
-           _otherReader           -> []
+#endif
+           _otherReader -> []
     in  concat . OAC.mapParser go
 
 ------------------------------------------------------------------------------
@@ -115,22 +142,26 @@
 d1 <||> d2 = d1 <> Doc.line <> Doc.line <> d2
 infixr 5 <||>
 
--- | Create a section with a title and indented body
+-- | Create a section with a title and body indented by 2 spaces
 section :: String -> Doc -> Doc
-section title = (Doc.text title Doc.<$$>) . Doc.indent 2
+section = section' 2
 
+-- | Create a section with a title and body indented by specified spaces
+section' :: Int -> String -> Doc -> Doc
+section' numSpaces title = (string title <$$>) . Doc.indent numSpaces
+
 -- | Create a table, with formatting
 table :: Int -> [[(String, Doc -> Doc)]] -> Doc
 table sep rows = Doc.vcat $
-    map (fromMaybe Doc.empty . foldr go Nothing . zip lengths) rows
+    map (fromMaybe empty . foldr go Nothing . zip lengths) rows
   where
     lengths :: [Int]
     lengths = map ((+) sep . maximum . map (length . fst)) $ transpose rows
 
     go :: (Int, (String, Doc -> Doc)) -> Maybe Doc -> Maybe Doc
     go (len, (s, f)) = Just . \case
-      Just doc -> Doc.fill len (f $ Doc.string s) <> doc
-      Nothing  -> f $ Doc.string s
+      Just doc -> Doc.fill len (f $ string s) <> doc
+      Nothing  -> f $ string s
 
 -- | Create a table, without formatting
 table_ :: Int -> [[String]] -> Doc
@@ -139,3 +170,30 @@
 -- | Vertically space documents with blank lines between them
 vspace :: [Doc] -> Doc
 vspace = mconcat . intersperse (Doc.line <> Doc.line)
+
+------------------------------------------------------------------------------
+-- $Compatibility
+
+(<$$>) :: Doc -> Doc -> Doc
+#if MIN_VERSION_optparse_applicative (0,18,0)
+l <$$> r = l <> Doc.line <> r
+#else
+(<$$>) = (Doc.<$$>)
+#endif
+
+empty :: Doc
+#if MIN_VERSION_optparse_applicative (0,18,0)
+empty = Doc.emptyDoc
+#else
+empty = Doc.empty
+#endif
+
+string :: String -> Doc
+#if MIN_VERSION_optparse_applicative (0,18,0)
+string = Doc.pretty
+#else
+string = Doc.string
+#endif
+
+vcat :: [Doc] -> Doc
+vcat = Doc.vcat
diff --git a/app/queue-sheet.hs b/app/queue-sheet.hs
--- a/app/queue-sheet.hs
+++ b/app/queue-sheet.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : Main
 -- Description : queue-sheet program
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
diff --git a/queue-sheet.cabal b/queue-sheet.cabal
--- a/queue-sheet.cabal
+++ b/queue-sheet.cabal
@@ -1,42 +1,44 @@
-name:           queue-sheet
-version:        0.7.0.2
-category:       Utils
-synopsis:       queue sheet utility
+cabal-version:      3.0
+name:               queue-sheet
+version:            0.8.0.0
+synopsis:           queue sheet utility
 description:
   This package provides a utility for creating queue sheets.  Please see the
   README on GitHub at
   <https://github.com/ExtremaIS/queue-sheet-haskell#readme>.
+homepage:           https://github.com/ExtremaIS/queue-sheet-haskell#readme
+bug-reports:        https://github.com/ExtremaIS/queue-sheet-haskell/issues
+license:            MIT
+license-file:       LICENSE
+author:             Travis Cardwell <travis.cardwell@extrema.is>
+maintainer:         Travis Cardwell <travis.cardwell@extrema.is>
+copyright:          Copyright (c) 2020-2025 Travis Cardwell
+category:           Utils
+build-type:         Simple
 
-homepage:       https://github.com/ExtremaIS/queue-sheet-haskell#readme
-bug-reports:    https://github.com/ExtremaIS/queue-sheet-haskell/issues
-author:         Travis Cardwell <travis.cardwell@extrema.is>
-maintainer:     Travis Cardwell <travis.cardwell@extrema.is>
-copyright:      Copyright (c) 2020-2022 Travis Cardwell
-license:        MIT
-license-file:   LICENSE
+extra-doc-files:
+  CHANGELOG.md
+  README.md
 
-cabal-version:  1.24
-build-type:     Simple
 tested-with:
-  GHC ==8.2.2
-   || ==8.4.4
-   || ==8.6.5
-   || ==8.8.4
+  GHC ==8.8.4
    || ==8.10.7
    || ==9.0.2
-   || ==9.2.1
-
-extra-source-files:
-  CHANGELOG.md
-  README.md
+   || ==9.2.8
+   || ==9.4.8
+   || ==9.6.6
+   || ==9.8.4
+   || ==9.10.1
 
 source-repository head
   type: git
   location: https://github.com/ExtremaIS/queue-sheet-haskell.git
 
-flag write-hie
-  description: write .hie files
+-- This flag is referenced in the Stack build-constraints.yaml configuration.
+flag optparse-applicative_ge_0_18
+  description: Use optparse-applicative 0.18 or newer
   default: False
+  manual: False
 
 library
   hs-source-dirs: src
@@ -48,25 +50,24 @@
     , QueueSheet.Types
   other-modules:
       Paths_queue_sheet
+  autogen-modules:
+      Paths_queue_sheet
   build-depends:
-      aeson >= 1.2 && <2.1
-    , base >=4.7 && <5
-    , bytestring >=0.10.8 && <0.12
-    , directory >=1.3 && <1.4
-    , filepath >=1.4 && <1.5
-    , ginger >=0.7.3 && <0.11
-    , process >=1.6 && <1.7
-    , scientific >=0.3 && <0.4
-    , text >=1.2.3 && <2.1
-    , transformers >=0.5.2 && <0.6
-    , ttc >=1.1 && <1.2
-    , vector >=0.12 && <0.13
-    , yaml >=0.8 && <0.12
+      aeson >= 1.4.7.1 && <2.3
+    , base >=4.13 && <4.21
+    , bytestring >=0.10.10.1 && <0.13
+    , directory >=1.3.6 && <1.4
+    , filepath >=1.4.2.1 && <1.6
+    , ginger >=0.10.1 && <0.11
+    , process >=1.6.9 && <1.7
+    , scientific >=0.3.6.2 && <0.4
+    , text >=1.2.4 && <2.2
+    , transformers >=0.5.6.2 && <0.7
+    , ttc >=1.1 && <1.6
+    , vector >=0.12.1.2 && <0.14
+    , yaml >=0.11.5 && <0.12
   default-language: Haskell2010
-  if flag(write-hie)
-    ghc-options: -Wall -fwrite-ide-info -hiedir=.hie
-  else
-    ghc-options: -Wall
+  ghc-options: -Wall
 
 executable queue-sheet
   hs-source-dirs: app
@@ -74,10 +75,16 @@
   other-modules:
       LibOA
   build-depends:
-      ansi-wl-pprint >=0.6 && <0.7
-    , base
-    , optparse-applicative >=0.14 && <0.18
+      base
     , queue-sheet
+  if flag(optparse-applicative_ge_0_18)
+    build-depends:
+        optparse-applicative >=0.18 && <0.19
+      , prettyprinter >=1.7.1 && <1.8
+  else
+    build-depends:
+        ansi-wl-pprint >=0.6.9 && <1.1
+      , optparse-applicative >=0.15.1 && <0.18
   default-language: Haskell2010
   ghc-options: -Wall
 
@@ -91,7 +98,7 @@
       base
     , bytestring
     , queue-sheet
-    , tasty >=1.0 && <1.5
-    , tasty-hunit >=0.10 && <0.11
+    , tasty >=1.2.3 && <1.6
+    , tasty-hunit >=0.10.0.3 && <0.11
   default-language: Haskell2010
   ghc-options: -Wall
diff --git a/src/QueueSheet.hs b/src/QueueSheet.hs
--- a/src/QueueSheet.hs
+++ b/src/QueueSheet.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet
 -- Description : queue sheet metadata
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
diff --git a/src/QueueSheet/Build.hs b/src/QueueSheet/Build.hs
--- a/src/QueueSheet/Build.hs
+++ b/src/QueueSheet/Build.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet.Build
 -- Description : queue sheet build functions
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
diff --git a/src/QueueSheet/File.hs b/src/QueueSheet/File.hs
--- a/src/QueueSheet/File.hs
+++ b/src/QueueSheet/File.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet.File
 -- Description : queue sheet file loading
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
diff --git a/src/QueueSheet/Template.hs b/src/QueueSheet/Template.hs
--- a/src/QueueSheet/Template.hs
+++ b/src/QueueSheet/Template.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet.Template
 -- Description : queue sheet template functions
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
@@ -23,6 +23,7 @@
 
 -- https://hackage.haskell.org/package/base
 import Data.Bifunctor (first)
+import Data.Either (fromRight)
 #if !MIN_VERSION_base (4,11,0)
 import Data.Monoid ((<>))
 #endif
@@ -80,7 +81,7 @@
     , date      = queueDate
     , tags      = queueTags
     , prevItem  = either Just (const Nothing) =<< queueItems
-    , nextItems = maybe [] (either (const []) id) queueItems
+    , nextItems = maybe [] (fromRight []) queueItems
     }
 
 ------------------------------------------------------------------------------
diff --git a/src/QueueSheet/Types.hs b/src/QueueSheet/Types.hs
--- a/src/QueueSheet/Types.hs
+++ b/src/QueueSheet/Types.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet.Types
 -- Description : queue sheet types
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
@@ -159,7 +159,7 @@
 
 instance FromJSON Tag where
   parseJSON = A.withText "Tag" $ \t -> do
-      when (T.null t) $ fail "empty tag"
+      when (T.null t) $ fail "empty string"
       unless (T.all isValidChar t) $ fail ("invalid tag: " ++ T.unpack t)
       return $ Tag t
     where
@@ -175,6 +175,8 @@
 
 -- | Queue item
 --
+-- Whitespace-separated tags are supported /instead of/ CSV from @0.8.0.0@.
+--
 -- @since 0.5.0.0
 data Item
   = Item
@@ -189,7 +191,7 @@
     (A.Object o) -> do
       itemName <- o .:  "name"
       itemUrl  <- o .:? "url"
-      itemTags <- maybe (pure []) parseCSV =<< (o .:? "tags")
+      itemTags <- maybe (pure []) parseSSV =<< (o .:? "tags")
       return Item{..}
     value -> do
       itemName <- Name <$> parseToString value
@@ -208,6 +210,9 @@
 
 -- | Queue information
 --
+-- Whitespace-separated items and tags are supported /instead of/ CSV from
+-- @0.8.0.0@.
+--
 -- @since 0.5.0.0
 data Queue
   = Queue
@@ -226,11 +231,11 @@
     queueUrl     <- o .:? "url"
     queueDate    <- o .:? "date"
     queueSection <- o .:? "section" .!= defaultSection
-    queueTags    <- maybe (pure []) parseCSV =<< (o .:? "tags")
+    queueTags    <- maybe (pure []) parseSSV =<< (o .:? "tags")
     mPrevItem    <- o .:? "prev"
     mNextValue   <- o .:? "next"
     queueItems   <- case (mPrevItem, mNextValue) of
-      (_,         Just nextValue) -> Just . Right <$> parseCSV nextValue
+      (_,         Just nextValue) -> Just . Right <$> parseSSV nextValue
       (Just item, Nothing)        -> pure . Just $ Left item
       (Nothing,   Nothing)        -> pure Nothing
     return Queue{..}
@@ -331,16 +336,17 @@
       '~'  -> acc <> "\\textasciitilde{}"
       c    -> acc `T.snoc` c
 
--- | Parse an array or string in simplified CSV format
+-- | Parse an array or string in space-separated-value format
 --
--- Strings are split on commas, and leading/trailing whitespace is removed
--- from each item.
-parseCSV :: A.FromJSON a => A.Value -> AT.Parser [a]
-parseCSV = \case
-    (A.String t) -> mapM (parseJSON . A.String . T.strip) $ T.splitOn "," t
-    (A.Array v)  -> mapM parseJSON $ V.toList v
-    A.Object{}   -> fail "unexpected object"
-    value        -> (: []) <$> parseJSON value
+-- Strings are split on whitespace.
+parseSSV :: A.FromJSON a => A.Value -> AT.Parser [a]
+parseSSV = \case
+    (A.String t)
+      | T.null t  -> fail "empty string"
+      | otherwise -> mapM (parseJSON . A.String) $ T.words t
+    (A.Array v)   -> mapM parseJSON $ V.toList v
+    A.Object{}    -> fail "unexpected object"
+    value         -> (: []) <$> parseJSON value
 
 -- | Parse any scalar value as a string
 --
diff --git a/test/QueueSheet/File/Test.hs b/test/QueueSheet/File/Test.hs
--- a/test/QueueSheet/File/Test.hs
+++ b/test/QueueSheet/File/Test.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : QueueSheet.File.Test
 -- Description : queues file tests
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
@@ -326,7 +326,7 @@
         , testCase "empty" $ do
             let message = intercalate "\n"
                   [ "error loading /tmp/test.yaml: Aeson exception:"
-                  , "Error in $[0]: empty tag"
+                  , "Error in $[0]: empty string"
                   ]
             Left message @=? loadYaml
               [ validFile "/tmp/test.yaml"
@@ -366,7 +366,7 @@
                   ]
             unless (Left messageOld == eeq) $ Left message @=? eeq
         ]
-    , testGroup "csv"
+    , testGroup "ssv"
         [ testCase "single" $ do
             let expected = defaultQueueSheet
                   { qsQueues =
@@ -394,13 +394,30 @@
             Right expected @=? loadYaml
               [ validFile "/tmp/test.yaml"
                   [ "- name: test"
-                  , "  tags: complete, partial"
+                  , "  tags: complete partial"
                   ]
               ]
+        , testCase "folded" $ do
+            let expected = defaultQueueSheet
+                  { qsQueues =
+                      [ defaultQueue
+                          { queueName = Name "test"
+                          , queueTags = [Tag "complete", Tag "partial"]
+                          }
+                      ]
+                  }
+            Right expected @=? loadYaml
+              [ validFile "/tmp/test.yaml"
+                  [ "- name: test"
+                  , "  tags: >"
+                  , "    complete"
+                  , "    partial"
+                  ]
+              ]
         , testCase "empty" $ do
             let message = intercalate "\n"
                   [ "error loading /tmp/test.yaml: Aeson exception:"
-                  , "Error in $[0]: empty tag"
+                  , "Error in $[0]: empty string"
                   ]
             Left message @=? loadYaml
               [ validFile "/tmp/test.yaml"
@@ -566,7 +583,7 @@
                   , "      - two"
                   ]
               ]
-        , testCase "csv" $ do
+        , testCase "ssv" $ do
             let expected = defaultQueueSheet
                   { qsQueues =
                       [ defaultQueue
@@ -583,7 +600,7 @@
                   [ "- name: test"
                   , "  prev:"
                   , "    name: 42"
-                  , "    tags: one, two"
+                  , "    tags: one two"
                   ]
               ]
         ]
@@ -712,7 +729,7 @@
                       , "        - two"
                       ]
                   ]
-            , testCase "csv" $ do
+            , testCase "ssv" $ do
                 let expected = defaultQueueSheet
                       { qsQueues =
                           [ defaultQueue
@@ -731,7 +748,7 @@
                       [ "- name: test"
                       , "  next:"
                       , "    - name: one"
-                      , "      tags: one, two"
+                      , "      tags: one two"
                       ]
                   ]
             ]
@@ -811,7 +828,7 @@
                   ]
               ]
         ]
-    , testGroup "csv"
+    , testGroup "ssv"
         [ testCase "single" $ do
             let expected = defaultQueueSheet
                   { qsQueues =
@@ -869,7 +886,31 @@
             Right expected @=? loadYaml
               [ validFile "/tmp/test.yaml"
                   [ "- name: test"
-                  , "  next: 11, 42"
+                  , "  next: 11 42"
+                  ]
+              ]
+        , testCase "folded" $ do
+            let expected = defaultQueueSheet
+                  { qsQueues =
+                      [ defaultQueue
+                          { queueName  = Name "test"
+                          , queueItems = Just $ Right
+                              [ defaultItem
+                                  { itemName = Name "11"
+                                  }
+                              , defaultItem
+                                  { itemName = Name "42"
+                                  }
+                              ]
+                          }
+                      ]
+                  }
+            Right expected @=? loadYaml
+              [ validFile "/tmp/test.yaml"
+                  [ "- name: test"
+                  , "  next: >"
+                  , "    11"
+                  , "    42"
                   ]
               ]
         , testCase "empty" $ do
diff --git a/test/Spec.hs b/test/Spec.hs
--- a/test/Spec.hs
+++ b/test/Spec.hs
@@ -2,7 +2,7 @@
 -- |
 -- Module      : Main
 -- Description : queue sheet test runner
--- Copyright   : Copyright (c) 2020-2022 Travis Cardwell
+-- Copyright   : Copyright (c) 2020-2025 Travis Cardwell
 -- License     : MIT
 ------------------------------------------------------------------------------
 
