diff --git a/publish.cabal b/publish.cabal
--- a/publish.cabal
+++ b/publish.cabal
@@ -4,16 +4,21 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: a4cc52f72afe6fb6f773a9c3b2fc7579f2146eb3a1367a22926f2c3519f6f897
+-- hash: bdc6778c7664e65e93e74b73702eef01ef70d5f056eaeca667c9ff73a6e095bd
 
 name:           publish
-version:        2.2.2
+version:        2.2.3
 synopsis:       Publishing tools for papers, books, and presentations
-description:    Tools for rendering markdown-centric documents into PDFs.
+description:    Tools for rendering markdown-centric documents into PDFs. There are two
+                programs:
                 .
+                  [/render/]for generating PDFs from Markdown and LaTeX input; and
+                .
+                  [/format/]for word-wrapping and nicely formatting Markdown files.
+                .
                 A description of this package, a list of features, and some background
                 to its design is contained in the
-                <https://github.com/aesiniath/publish/blob/master/README.md README>
+                <https://github.com/aesiniath/publish/blob/main/README.md README>
                 on GitHub.
 category:       Text
 stability:      experimental
@@ -25,7 +30,7 @@
 license:        MIT
 license-file:   LICENSE
 tested-with:
-    GHC == 8.10
+    GHC == 8.10.7
 build-type:     Simple
 
 source-repository head
@@ -45,8 +50,8 @@
     , bytestring
     , chronologique
     , core-data
-    , core-program >=0.2.7
-    , core-text >=0.3.0
+    , core-program >=0.2.12
+    , core-text >=0.3.4
     , deepseq
     , directory
     , filepath
@@ -78,8 +83,8 @@
     , bytestring
     , chronologique
     , core-data
-    , core-program >=0.2.7
-    , core-text >=0.3.0
+    , core-program >=0.2.12
+    , core-text >=0.3.4
     , deepseq
     , directory
     , filepath
@@ -113,8 +118,8 @@
     , bytestring
     , chronologique
     , core-data
-    , core-program >=0.2.7
-    , core-text >=0.3.0
+    , core-program >=0.2.12
+    , core-text >=0.3.4
     , deepseq
     , directory
     , filepath
diff --git a/src/FormatDocument.hs b/src/FormatDocument.hs
--- a/src/FormatDocument.hs
+++ b/src/FormatDocument.hs
@@ -28,16 +28,16 @@
 
 program :: Program None ()
 program = do
-    event "Identify document fragment"
+    info "Identify document fragment"
     file <- getFragmentName
 
-    event "Load to Pandoc internal representation"
+    info "Load to Pandoc internal representation"
     parsed <- loadFragment file
 
-    event "Write to Markdown format"
+    info "Write to Markdown format"
     writeResult file parsed
 
-    event "Complete"
+    info "Complete"
 
 getFragmentName :: Program None FilePath
 getFragmentName = do
diff --git a/src/FormatMain.hs b/src/FormatMain.hs
--- a/src/FormatMain.hs
+++ b/src/FormatMain.hs
@@ -23,7 +23,7 @@
     configure
       version
       None
-      ( simple
+      ( simpleConfig
           [ Option
               "inplace"
               (Just 'i')
diff --git a/src/ParseBookfile.hs b/src/ParseBookfile.hs
--- a/src/ParseBookfile.hs
+++ b/src/ParseBookfile.hs
@@ -15,48 +15,50 @@
 
 parseMagicLine :: Parser Int
 parseMagicLine = do
-  void (char '%') <?> "first line to begin with % character"
-  void spaceChar <?> "a space character"
-  void (string "publish")
-  void spaceChar <?> "a space character"
-  void (char 'v') <?> "the character v and then a number"
-  v <- L.decimal <?> "the bookfile schema version number"
-  unless (v == __VERSION__) (fail ("currently recognized bookfile schema version is v" ++ show __VERSION__))
-  void newline
-  return v
+    void (char '%') <?> "first line to begin with % character"
+    void spaceChar <?> "a space character"
+    void (string "publish")
+    void spaceChar <?> "a space character"
+    void (char 'v') <?> "the character v and then a number"
+    v <- L.decimal <?> "the bookfile schema version number"
+    unless (v == __VERSION__) (fail ("currently recognized bookfile schema version is v" ++ show __VERSION__))
+    void newline
+    return v
 
 parseBeginLine :: Parser ()
-parseBeginLine = try $ label "begin marker" $ do
-  void (string "% begin")
-  void newline
+parseBeginLine = try $
+    label "begin marker" $ do
+        void (string "% begin")
+        void newline
 
 parseFileLine :: Parser FilePath
 parseFileLine = do
-  notFollowedBy (char '%')
-  file <- takeWhile1P (Just "line containing a filename") (/= '\n')
-  return file
+    notFollowedBy (char '%')
+    file <- takeWhile1P (Just "line containing a filename") (/= '\n')
+    return file
 
 parseEndLine :: Parser ()
-parseEndLine = try $ label "end marker" $ do
-  void (string "% end")
-  void newline
+parseEndLine = try $
+    label "end marker" $ do
+        void (string "% end")
+        void newline
 
 parseBlank :: Parser ()
 parseBlank = do
-  void (hidden (many newline))
+    void (hidden (many newline))
 
 parseBookfile :: Parser Bookfile
 parseBookfile = do
-  version <- parseMagicLine
-  preambles <- many (parseBlank *> parseFileLine <* parseBlank)
-  parseBeginLine
-  fragments <- many (parseBlank *> parseFileLine <* parseBlank)
-  parseEndLine
-  trailers <- many (parseBlank *> parseFileLine <* parseBlank)
-  return
-    Bookfile
-      { versionFrom = version,
-        preamblesFrom = preambles,
-        fragmentsFrom = fragments,
-        trailersFrom = trailers
-      }
+    version <- parseMagicLine
+    preambles <- many (parseBlank *> parseFileLine <* parseBlank)
+    parseBeginLine
+    fragments <- many (parseBlank *> parseFileLine <* parseBlank)
+    parseEndLine
+    trailers <- many (parseBlank *> parseFileLine <* parseBlank)
+    return
+        Bookfile
+            { versionFrom = version
+            , preamblesFrom = preambles
+            , fragmentsFrom = fragments
+            , trailersFrom = trailers
+            }
diff --git a/src/RenderDocument.hs b/src/RenderDocument.hs
--- a/src/RenderDocument.hs
+++ b/src/RenderDocument.hs
@@ -61,7 +61,7 @@
     params <- getCommandLine
     (mode, copy) <- extractMode params
 
-    event "Identify .book file"
+    info "Identify .book file"
     bookfile <- extractBookFile params
 
     case mode of
@@ -74,10 +74,10 @@
 
 renderDocument :: (Mode, Copy) -> FilePath -> Program Env [FilePath]
 renderDocument (mode, copy) file = do
-    event "Read .book file"
+    info "Read .book file"
     book <- processBookFile file
 
-    event "Setup temporary directory"
+    info "Setup temporary directory"
     setupTargetFile file
     setupPreambleFile
     validatePreamble book
@@ -86,21 +86,21 @@
     let fragments = fragmentsFrom book
     let trailers = trailersFrom book
 
-    event "Convert preamble fragments and begin marker to LaTeX"
+    info "Convert preamble fragments and begin marker to LaTeX"
     mapM_ processFragment preambles
     setupBeginningFile
 
-    event "Convert document fragments to LaTeX"
+    info "Convert document fragments to LaTeX"
     mapM_ processFragment fragments
 
-    event "Convert end marker and trailing fragments to LaTeX"
+    info "Convert end marker and trailing fragments to LaTeX"
     setupEndingFile
     mapM_ processFragment trailers
 
-    event "Write intermediate LaTeX file"
+    info "Write intermediate LaTeX file"
     produceResult
 
-    event "Render document to PDF"
+    info "Render document to PDF"
     catch
         ( do
             renderPDF
@@ -313,6 +313,7 @@
         case probe of
             True -> return True
             False -> do
+                warn "Fragment not found"
                 write ("warning: Fragment \"" <> intoRope fragment <> "\" not found, skipping")
                 return False
 
@@ -432,7 +433,7 @@
 
         case exit of
             ExitFailure _ -> do
-                event "Image processing failed"
+                info "Image processing failed"
                 debug "stderr" (intoRope err)
                 debug "stdout" (intoRope out)
                 write ("error: Unable to convert " <> intoRope file <> " from SVG to PDF")
@@ -512,7 +513,7 @@
     (exit, out, err) <- execProcess latexmk
     case exit of
         ExitFailure _ -> do
-            event "Render failed"
+            info "Render failed"
             debug "stderr" (intoRope err)
             debug "stdout" (intoRope out)
             write (parseOutputForError tmpdir out)
@@ -528,11 +529,11 @@
     changed <- isNewer result final
     case changed of
         True -> do
-            event "Copy resultant PDF to starting directory"
+            info "Copy resultant PDF to starting directory"
             debugS "result" result
             debugS "final" final
             liftIO $ do
                 copyFileWithMetadata result final
-            event "Complete"
+            info "Complete"
         False -> do
-            event "Result unchanged"
+            info "Result unchanged"
diff --git a/src/RenderMain.hs b/src/RenderMain.hs
--- a/src/RenderMain.hs
+++ b/src/RenderMain.hs
@@ -25,7 +25,7 @@
         configure
             version
             env
-            ( simple
+            ( simpleConfig
                 [ Option
                     "builtin-preamble"
                     (Just 'p')
diff --git a/src/Utilities.hs b/src/Utilities.hs
--- a/src/Utilities.hs
+++ b/src/Utilities.hs
@@ -2,13 +2,12 @@
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
-module Utilities
-  ( ensureDirectory,
+module Utilities (
+    ensureDirectory,
     execProcess,
     ifNewer,
     isNewer,
-  )
-where
+) where
 
 import Chrono.Compat (convertToUTC)
 import Control.Monad (when)
@@ -16,12 +15,12 @@
 import Core.System
 import Core.Text
 import qualified Data.List as List (intercalate)
-import System.Directory
-  ( createDirectoryIfMissing,
+import System.Directory (
+    createDirectoryIfMissing,
     doesDirectoryExist,
     doesFileExist,
     getModificationTime,
-  )
+ )
 import System.Exit (ExitCode (..))
 import System.FilePath.Posix (takeDirectory)
 import System.Process.Typed (closed, proc, readProcess, setStdin)
@@ -32,11 +31,11 @@
 -}
 ensureDirectory :: FilePath -> Program t ()
 ensureDirectory target =
-  let subdir = takeDirectory target
-   in liftIO $ do
-        probe <- doesDirectoryExist subdir
-        when (not probe) $ do
-          createDirectoryIfMissing True subdir
+    let subdir = takeDirectory target
+     in liftIO $ do
+            probe <- doesDirectoryExist subdir
+            when (not probe) $ do
+                createDirectoryIfMissing True subdir
 
 {-
 Thin wrapper around **typed-process**'s `readProcess` so that the command
@@ -49,43 +48,45 @@
 execProcess :: [String] -> Program t (ExitCode, Rope, Rope)
 execProcess [] = error "No command provided"
 execProcess (cmd : args) =
-  let task = proc cmd args
-      task' = setStdin closed task
-      command = List.intercalate " " (cmd : args)
-   in do
-        debugS "command" command
+    let task = proc cmd args
+        task' = setStdin closed task
+        command = List.intercalate " " (cmd : args)
+     in do
+            debugS "command" command
 
-        (exit, out, err) <- liftIO $ do
-          readProcess task'
+            (exit, out, err) <- liftIO $ do
+                readProcess task'
 
-        return (exit, intoRope out, intoRope err)
+            return (exit, intoRope out, intoRope err)
 
--- |
--- If the source file is newer than the target file, then run an action. For
--- example, if you want to install a file but only do so if the file has been
--- rebuilt, then you could do this:
---
--- @
--- copyFileIfNewer :: 'FilePath' -> 'FilePath' -> 'Program' τ ()
--- copyFileIfNewer source target = do
---     'ifNewer' source target $ do
---         'liftIO' ('copyFileWithMetadata' source target)
--- @
---
--- This is basically a build system in a box, although the usual caveats
--- about the brittleness of timestamps apply.
---
--- TODO this could potentially move to the **unbeliever** library
+{- |
+ If the source file is newer than the target file, then run an action. For
+ example, if you want to install a file but only do so if the file has been
+ rebuilt, then you could do this:
+
+ @
+ copyFileIfNewer :: 'FilePath' -> 'FilePath' -> 'Program' τ ()
+ copyFileIfNewer source target = do
+     'ifNewer' source target $ do
+         'liftIO' ('copyFileWithMetadata' source target)
+ @
+
+ This is basically a build system in a box, although the usual caveats
+ about the brittleness of timestamps apply.
+
+ TODO this could potentially move to the **unbeliever** library
+-}
 ifNewer :: FilePath -> FilePath -> Program t () -> Program t ()
 ifNewer source target program = do
-  changed <- isNewer source target
-  when changed $ do
-    program
+    changed <- isNewer source target
+    when changed $ do
+        program
 
 isNewer :: FilePath -> FilePath -> Program t Bool
 isNewer source target = liftIO $ do
-  time1 <- getModificationTime source
-  time2 <- doesFileExist target >>= \case
-    True -> getModificationTime target
-    False -> return (convertToUTC 0) -- the epoch
-  return (time1 > time2)
+    time1 <- getModificationTime source
+    time2 <-
+        doesFileExist target >>= \case
+            True -> getModificationTime target
+            False -> return (convertToUTC 0) -- the epoch
+    return (time1 > time2)
