diff --git a/CHANGES.md b/CHANGES.md
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -1,6 +1,17 @@
 Internal/api/developer-ish changes in the hledger-lib (and hledger) packages.
 For user-visible changes, see the hledger package changelog.
 
+# 1.17.1 2020-03-19
+
+- require newer Decimal, math-functions libs to ensure consistent
+  rounding behaviour, even when built with old GHCs/snapshots. 
+  hledger uses banker's rounding (rounds to nearest even number, eg
+  0.5 with with zero decimal places is "0").
+
+- added: debug helpers traceAt, traceAtWith
+
+- Journal is now a Semigroup, not a Monoid (since <> is right-biased). (Stephen Morgan)
+
 # 1.17.0.1 2020-03-01
 
 - fix org heading comments and doctest setup comment that were
diff --git a/Hledger/Data/Dates.hs b/Hledger/Data/Dates.hs
--- a/Hledger/Data/Dates.hs
+++ b/Hledger/Data/Dates.hs
@@ -84,6 +84,7 @@
 import Control.Monad (guard, unless)
 import "base-compat-batteries" Data.List.Compat
 import Data.Default
+import Data.Foldable (asum)
 import Data.Maybe
 import qualified Data.Set as Set
 import Data.Text (Text)
@@ -654,7 +655,7 @@
 
 -- -- | Parse a couple of date-time string formats to a time type.
 -- parsedatetimeM :: String -> Maybe LocalTime
--- parsedatetimeM s = firstJust [
+-- parsedatetimeM s = asum [
 --     parseTime defaultTimeLocale "%Y/%m/%d %H:%M:%S" s,
 --     parseTime defaultTimeLocale "%Y-%m-%d %H:%M:%S" s
 --     ]
@@ -672,9 +673,9 @@
 -- `YYYY-MM-DD`, `YYYY/MM/DD` or `YYYY.MM.DD`, with leading zeros required.
 -- For internal use, not quite the same as the journal's "simple dates".
 parsedateM :: String -> Maybe Day
-parsedateM s = firstJust [
-     parsetime defaultTimeLocale "%Y/%m/%d" s,
+parsedateM s = asum [
      parsetime defaultTimeLocale "%Y-%m-%d" s,
+     parsetime defaultTimeLocale "%Y/%m/%d" s,
      parsetime defaultTimeLocale "%Y.%m.%d" s
      ]
 
diff --git a/Hledger/Data/Journal.hs b/Hledger/Data/Journal.hs
--- a/Hledger/Data/Journal.hs
+++ b/Hledger/Data/Journal.hs
@@ -90,16 +90,17 @@
 import Control.Monad.Reader as R
 import Control.Monad.ST
 import Data.Array.ST
+import Data.Default (Default(..))
 import Data.Function ((&))
+import qualified Data.HashTable.Class as H (toList)
 import qualified Data.HashTable.ST.Cuckoo as H
 import Data.List
 import Data.List.Extra (groupSort, nubSort)
 import qualified Data.Map as M
 import Data.Maybe
 #if !(MIN_VERSION_base(4,11,0))
-import Data.Monoid
+import Data.Semigroup (Semigroup(..))
 #endif
-import qualified Data.Semigroup as Sem
 import qualified Data.Set as S
 import Data.Text (Text)
 import qualified Data.Text as T
@@ -157,7 +158,7 @@
 --                      ,show $ map fst $ jfiles j
 --                      ]
 
--- The monoid instance for Journal is useful for two situations.
+-- The semigroup instance for Journal is useful for two situations.
 --
 -- 1. concatenating finalised journals, eg with multiple -f options:
 -- FIRST <> SECOND. The second's list fields are appended to the
@@ -168,7 +169,9 @@
 -- CHILD <> PARENT. A parsed journal's data is in reverse order, so
 -- this gives what we want.
 --
-instance Sem.Semigroup Journal where
+-- Note that (<>) is right-biased, so nulljournal is only a left identity.
+-- In particular, this prevents Journal from being a monoid.
+instance Semigroup Journal where
   j1 <> j2 = Journal {
      jparsedefaultyear          = jparsedefaultyear          j2
     ,jparsedefaultcommodity     = jparsedefaultcommodity     j2
@@ -190,12 +193,8 @@
     ,jlastreadtime              = max (jlastreadtime j1) (jlastreadtime j2)
     }
 
-instance Monoid Journal where
-  mempty = nulljournal
-#if !(MIN_VERSION_base(4,11,0))
-  -- This is redundant starting with base-4.11 / GHC 8.4.
-  mappend = (Sem.<>)
-#endif
+instance Default Journal where
+  def = nulljournal
 
 nulljournal :: Journal
 nulljournal = Journal {
@@ -587,7 +586,29 @@
 journalCheckBalanceAssertions :: Journal -> Maybe String
 journalCheckBalanceAssertions = either Just (const Nothing) . journalBalanceTransactions True
 
--- "Transaction balancing" - inferring missing amounts and checking transaction balancedness and balance assertions
+-- "Transaction balancing", including: inferring missing amounts,
+-- applying balance assignments, checking transaction balancedness,
+-- checking balance assertions, respecting posting dates. These things
+-- are all interdependent.
+-- WARN tricky algorithm and code ahead. 
+--
+-- Code overview as of 20190219, this could/should be simplified/documented more:
+--  parseAndFinaliseJournal['] (Cli/Utils.hs), journalAddForecast (Common.hs), budgetJournal (BudgetReport.hs), tests (BalanceReport.hs)
+--   journalBalanceTransactions
+--    runST
+--     runExceptT
+--      balanceTransaction (Transaction.hs)
+--       balanceTransactionHelper
+--      runReaderT
+--       balanceTransactionAndCheckAssertionsB
+--        addAmountAndCheckAssertionB
+--        addOrAssignAmountAndCheckAssertionB
+--        balanceTransactionHelper (Transaction.hs)
+--  uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j} (ErrorScreen.hs)
+--   journalCheckBalanceAssertions
+--    journalBalanceTransactions
+--  transactionWizard, postingsBalanced (Add.hs), tests (Transaction.hs)
+--   balanceTransaction (Transaction.hs)  XXX hledger add won't allow balance assignments + missing amount ?
 
 -- | Monad used for statefully balancing/amount-inferring/assertion-checking
 -- a sequence of transactions.
@@ -603,37 +624,54 @@
   ,bsAssrt        :: Bool                                       -- ^ whether to check balance assertions
    -- mutable
   ,bsBalances     :: H.HashTable s AccountName MixedAmount      -- ^ running account balances, initially empty
-  ,bsTransactions :: STArray s Integer Transaction              -- ^ the transactions being balanced
+  ,bsTransactions :: STArray s Integer Transaction              -- ^ a mutable array of the transactions being balanced
+    -- (for efficiency ? journalBalanceTransactions says: not strictly necessary but avoids a sort at the end I think)
   }
 
 -- | Access the current balancing state, and possibly modify the mutable bits,
 -- lifting through the Except and Reader layers into the Balancing monad.
-withB :: (BalancingState s -> ST s a) -> Balancing s a
-withB f = ask >>= lift . lift . f
+withRunningBalance :: (BalancingState s -> ST s a) -> Balancing s a
+withRunningBalance f = ask >>= lift . lift . f
 
--- | Get an account's running balance so far.
-getAmountB :: AccountName -> Balancing s MixedAmount
-getAmountB acc = withB $ \BalancingState{bsBalances} -> do
+-- | Get this account's current exclusive running balance.
+getRunningBalanceB :: AccountName -> Balancing s MixedAmount
+getRunningBalanceB acc = withRunningBalance $ \BalancingState{bsBalances} -> do
   fromMaybe 0 <$> H.lookup bsBalances acc
 
--- | Add an amount to an account's running balance, and return the new running balance.
-addAmountB :: AccountName -> MixedAmount -> Balancing s MixedAmount
-addAmountB acc amt = withB $ \BalancingState{bsBalances} -> do
+-- | Add this amount to this account's exclusive running balance.
+-- Returns the new running balance.
+addToRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
+addToRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do
   old <- fromMaybe 0 <$> H.lookup bsBalances acc
   let new = old + amt
   H.insert bsBalances acc new
   return new
 
--- | Set an account's running balance to this amount, and return the difference from the old.
-setAmountB :: AccountName -> MixedAmount -> Balancing s MixedAmount
-setAmountB acc amt = withB $ \BalancingState{bsBalances} -> do
+-- | Set this account's exclusive running balance to this amount.
+-- Returns the change in exclusive running balance.
+setRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
+setRunningBalanceB acc amt = withRunningBalance $ \BalancingState{bsBalances} -> do
   old <- fromMaybe 0 <$> H.lookup bsBalances acc
   H.insert bsBalances acc amt
   return $ amt - old
 
--- | Update (overwrite) this transaction with a new one.
-storeTransactionB :: Transaction -> Balancing s ()
-storeTransactionB t = withB $ \BalancingState{bsTransactions}  ->
+-- | Set this account's exclusive running balance to whatever amount
+-- makes its *inclusive* running balance (the sum of exclusive running
+-- balances of this account and any subaccounts) be the given amount.
+-- Returns the change in exclusive running balance.
+setInclusiveRunningBalanceB :: AccountName -> MixedAmount -> Balancing s MixedAmount
+setInclusiveRunningBalanceB acc newibal = withRunningBalance $ \BalancingState{bsBalances} -> do
+  oldebal  <- fromMaybe 0 <$> H.lookup bsBalances acc
+  allebals <- H.toList bsBalances
+  let subsibal =  -- sum of any subaccounts' running balances
+        sum $ map snd $ filter ((acc `isAccountNamePrefixOf`).fst) allebals
+  let newebal = newibal - subsibal
+  H.insert bsBalances acc newebal
+  return $ newebal - oldebal
+
+-- | Update (overwrite) this transaction in the balancing state.
+updateTransactionB :: Transaction -> Balancing s ()
+updateTransactionB t = withRunningBalance $ \BalancingState{bsTransactions}  ->
   void $ writeArray bsTransactions (tindex t) t
 
 -- | Infer any missing amounts (to satisfy balance assignments and
@@ -641,30 +679,11 @@
 -- and (optional) all balance assertions pass. Or return an error message
 -- (just the first error encountered).
 --
--- Assumes journalInferCommodityStyles has been called, since those affect transaction balancing.
---
--- This does multiple things because amount inferring, balance assignments,
--- balance assertions and posting dates are interdependent.
+-- Assumes journalInferCommodityStyles has been called, since those
+-- affect transaction balancing.
 --
--- This can be simplified further. Overview as of 20190219:
--- @
--- ****** parseAndFinaliseJournal['] (Cli/Utils.hs), journalAddForecast (Common.hs), budgetJournal (BudgetReport.hs), tests (BalanceReport.hs)
--- ******* journalBalanceTransactions
--- ******** runST
--- ********* runExceptT
--- ********** balanceTransaction (Transaction.hs)
--- *********** balanceTransactionHelper
--- ********** runReaderT
--- *********** balanceTransactionAndCheckAssertionsB
--- ************ addAmountAndCheckAssertionB
--- ************ addOrAssignAmountAndCheckAssertionB
--- ************ balanceTransactionHelper (Transaction.hs)
--- ****** uiCheckBalanceAssertions d ui@UIState{aopts=UIOpts{cliopts_=copts}, ajournal=j} (ErrorScreen.hs)
--- ******* journalCheckBalanceAssertions
--- ******** journalBalanceTransactions
--- ****** transactionWizard, postingsBalanced (Add.hs), tests (Transaction.hs)
--- ******* balanceTransaction (Transaction.hs)  XXX hledger add won't allow balance assignments + missing amount ?
--- @
+-- This does multiple things at once because amount inferring, balance
+-- assignments, balance assertions and posting dates are interdependent.
 journalBalanceTransactions :: Bool -> Journal -> Either String Journal
 journalBalanceTransactions assrt j' =
   let
@@ -716,11 +735,9 @@
 -- Transaction prices are removed, which helps eg balance-assertions.test: 15. Mix different commodities and assignments.
 -- This stores the balanced transactions in case 2 but not in case 1.
 balanceTransactionAndCheckAssertionsB :: Either Posting Transaction -> Balancing s ()
-
 balanceTransactionAndCheckAssertionsB (Left p@Posting{}) =
   -- update the account's running balance and check the balance assertion if any
   void $ addAmountAndCheckAssertionB $ removePrices p
-
 balanceTransactionAndCheckAssertionsB (Right t@Transaction{tpostings=ps}) = do
   -- make sure we can handle the balance assignments
   mapM_ checkIllegalBalanceAssignmentB ps
@@ -733,9 +750,9 @@
     Left err -> throwError err
     Right (t', inferredacctsandamts) -> do
       -- for each amount just inferred, update the running balance
-      mapM_ (uncurry addAmountB) inferredacctsandamts
+      mapM_ (uncurry addToRunningBalanceB) inferredacctsandamts
       -- and save the balanced transaction.
-      storeTransactionB t'
+      updateTransactionB t'
 
 -- | If this posting has an explicit amount, add it to the account's running balance.
 -- If it has a missing amount and a balance assignment, infer the amount from, and
@@ -744,28 +761,33 @@
 -- Then test the balance assertion if any.
 addOrAssignAmountAndCheckAssertionB :: Posting -> Balancing s Posting
 addOrAssignAmountAndCheckAssertionB p@Posting{paccount=acc, pamount=amt, pbalanceassertion=mba}
+  -- an explicit posting amount
   | hasAmount p = do
-      newbal <- addAmountB acc amt
+      newbal <- addToRunningBalanceB acc amt
       whenM (R.reader bsAssrt) $ checkBalanceAssertionB p newbal
       return p
-  | Just BalanceAssertion{baamount,batotal} <- mba = do
+
+  -- no explicit posting amount, but there is a balance assignment
+  -- TODO this doesn't yet handle inclusive assignments right, #1207
+  | Just BalanceAssertion{baamount,batotal,bainclusive} <- mba = do
       (diff,newbal) <- case batotal of
+        -- a total balance assignment (==, all commodities)
         True  -> do
-          -- a total balance assignment
           let newbal = Mixed [baamount]
-          diff <- setAmountB acc newbal
+          diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal
           return (diff,newbal)
+        -- a partial balance assignment (=, one commodity)
         False -> do
-          -- a partial balance assignment
-          oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getAmountB acc
+          oldbalothercommodities <- filterMixedAmount ((acommodity baamount /=) . acommodity) <$> getRunningBalanceB acc
           let assignedbalthiscommodity = Mixed [baamount]
               newbal = oldbalothercommodities + assignedbalthiscommodity
-          diff <- setAmountB acc newbal
+          diff <- (if bainclusive then setInclusiveRunningBalanceB else setRunningBalanceB) acc newbal
           return (diff,newbal)
       let p' = p{pamount=diff, poriginal=Just $ originalPosting p}
       whenM (R.reader bsAssrt) $ checkBalanceAssertionB p' newbal
       return p'
-  -- no amount, no balance assertion (GHC 7 doesn't like Nothing <- mba here)
+
+  -- no explicit posting amount, no balance assignment
   | otherwise = return p
 
 -- | Add the posting's amount to its account's running balance, and
@@ -775,7 +797,7 @@
 -- need to see the balance as it stands after each individual posting.
 addAmountAndCheckAssertionB :: Posting -> Balancing s Posting
 addAmountAndCheckAssertionB p | hasAmount p = do
-  newbal <- addAmountB (paccount p) (pamount p)
+  newbal <- addToRunningBalanceB (paccount p) (pamount p)
   whenM (R.reader bsAssrt) $ checkBalanceAssertionB p newbal
   return p
 addAmountAndCheckAssertionB p = return p
@@ -808,7 +830,7 @@
     if isinclusive
     then
       -- sum the running balances of this account and any of its subaccounts seen so far
-      withB $ \BalancingState{bsBalances} ->
+      withRunningBalance $ \BalancingState{bsBalances} ->
         H.foldM
           (\ibal (acc, amt) -> return $ ibal +
             if assertedacct==acc || assertedacct `isAccountNamePrefixOf` acc then amt else 0)
diff --git a/Hledger/Read.hs b/Hledger/Read.hs
--- a/Hledger/Read.hs
+++ b/Hledger/Read.hs
@@ -48,21 +48,24 @@
 import qualified Control.Exception as C
 import Control.Monad (when)
 import "mtl" Control.Monad.Except (runExceptT)
-import Data.Default
-import Data.List
-import Data.Maybe
-import Data.Ord
+import Data.Default (def)
+import Data.Foldable (asum)
+import Data.List (group, sort, sortBy)
+import Data.List.NonEmpty (nonEmpty)
+import Data.Maybe (fromMaybe)
+import Data.Ord (comparing)
+import Data.Semigroup (sconcat)
 import Data.Text (Text)
 import qualified Data.Text as T
 import Data.Time (Day)
-import Safe
+import Safe (headDef)
 import System.Directory (doesFileExist, getHomeDirectory)
 import System.Environment (getEnv)
 import System.Exit (exitFailure)
-import System.FilePath
+import System.FilePath ((<.>), (</>), splitDirectories, splitFileName)
 import System.Info (os)
-import System.IO
-import Text.Printf
+import System.IO (stderr, writeFile)
+import Text.Printf (hPrintf, printf)
 
 import Hledger.Data.Dates (getCurrentDay, parsedate, showDate)
 import Hledger.Data.Types
@@ -149,11 +152,7 @@
 -- Also the final parse state saved in the Journal does span all files.
 readJournalFiles :: InputOpts -> [PrefixedFilePath] -> IO (Either String Journal)
 readJournalFiles iopts =
-  (right mconcat1 . sequence <$>) . mapM (readJournalFile iopts)
-  where
-    mconcat1 :: Monoid t => [t] -> t
-    mconcat1 [] = mempty
-    mconcat1 x  = foldr1 mappend x
+  fmap (right (maybe def sconcat . nonEmpty) . sequence) . mapM (readJournalFile iopts)
 
 -- | Read a Journal from this file, or from stdin if the file path is -,
 -- or return an error message. The file path can have a READER: prefix.
@@ -170,7 +169,7 @@
 readJournalFile iopts prefixedfile = do
   let
     (mfmt, f) = splitReaderPrefix prefixedfile
-    iopts' = iopts{mformat_=firstJust [mfmt, mformat_ iopts]}
+    iopts' = iopts{mformat_=asum [mfmt, mformat_ iopts]}
   requireJournalFileExists f
   t <- readFileOrStdinPortably f
     -- <- T.readFile f  -- or without line ending translation, for testing
diff --git a/Hledger/Read/Common.hs b/Hledger/Read/Common.hs
--- a/Hledger/Read/Common.hs
+++ b/Hledger/Read/Common.hs
@@ -222,7 +222,7 @@
 runJournalParser, rjp
   :: Monad m
   => JournalParser m a -> Text -> m (Either (ParseErrorBundle Text CustomErr) a)
-runJournalParser p t = runParserT (evalStateT p mempty) "" t
+runJournalParser p t = runParserT (evalStateT p nulljournal) "" t
 rjp = runJournalParser
 
 -- | Run an erroring journal parser in some monad. See also: parseWithState.
@@ -232,7 +232,7 @@
   -> Text
   -> m (Either FinalParseError (Either (ParseErrorBundle Text CustomErr) a))
 runErroringJournalParser p t =
-  runExceptT $ runParserT (evalStateT p mempty) "" t
+  runExceptT $ runParserT (evalStateT p nulljournal) "" t
 rejp = runErroringJournalParser
 
 genericSourcePos :: SourcePos -> GenericSourcePos
@@ -680,7 +680,7 @@
 -- | Parse an amount from a string, or get an error.
 amountp' :: String -> Amount
 amountp' s =
-  case runParser (evalStateT (amountp <* eof) mempty) "" (T.pack s) of
+  case runParser (evalStateT (amountp <* eof) nulljournal) "" (T.pack s) of
     Right amt -> amt
     Left err  -> error' $ show err -- XXX should throwError
 
@@ -766,12 +766,12 @@
 -- attributes.
 --
 -- Some international number formats are accepted, eg either period or comma
--- may be used for the decimal point, and the other of these may be used for
+-- may be used for the decimal mark, and the other of these may be used for
 -- separating digit groups in the integer part. See
 -- http://en.wikipedia.org/wiki/Decimal_separator for more examples.
 --
 -- This returns: the parsed numeric value, the precision (number of digits
--- seen following the decimal point), the decimal point character used if any,
+-- seen following the decimal mark), the decimal mark character used if any,
 -- and the digit group style if any.
 --
 numberp :: Maybe AmountStyle -> TextParser m (Quantity, Int, Maybe Char, Maybe DigitGroupStyle)
diff --git a/Hledger/Read/CsvReader.hs b/Hledger/Read/CsvReader.hs
--- a/Hledger/Read/CsvReader.hs
+++ b/Hledger/Read/CsvReader.hs
@@ -457,6 +457,8 @@
   lift (dbgparse 2 "trying journalfieldnamep")
   T.unpack <$> choiceInState (map (lift . string . T.pack) journalfieldnames)
 
+maxpostings = 9
+
 -- Transaction fields and pseudo fields for CSV conversion.
 -- Names must precede any other name they contain, for the parser
 -- (amount-in before amount; date2 before date). TODO: fix
@@ -468,7 +470,7 @@
           ,"balance" ++ i
           ,"comment" ++ i
           ,"currency" ++ i
-          ] | x <- [1..9], let i = show x]
+          ] | x <- [1..maxpostings], let i = show x]
   ++
   ["amount-in"
   ,"amount-out"
@@ -757,13 +759,6 @@
 csvRule :: CsvRules -> DirectiveName -> Maybe FieldTemplate
 csvRule rules = (`getDirective` rules)
 
--- | Look up the final value assigned to a csv rule by rule keyword, taking
--- into account the current record and conditional rules.
--- Generally rules with keywords ("directives") don't have interpolated
--- values, but for now it's possible.
-csvRuleValue :: CsvRules -> CsvRecord -> DirectiveName -> Maybe String
-csvRuleValue rules record = fmap (renderTemplate rules record) . csvRule rules
-
 -- | Look up the value template assigned to a hledger field by field
 -- list/field assignment rules, taking into account the current record and
 -- conditional rules.
@@ -775,8 +770,6 @@
 hledgerFieldValue :: CsvRules -> CsvRecord -> HledgerFieldName -> Maybe String
 hledgerFieldValue rules record = fmap (renderTemplate rules record) . hledgerField rules record
 
-s `withDefault` def = if null s then def else s
-
 transactionFromCsvRecord :: SourcePos -> CsvRules -> CsvRecord -> Transaction
 transactionFromCsvRecord sourcepos rules record = t
   where
@@ -828,64 +821,27 @@
     precomment  = maybe "" singleline $ fieldval "precomment"
 
     ----------------------------------------------------------------------
-    -- 3. Generate the postings
-
-    -- Make posting 1 if possible, with special support for old syntax to
-    -- support pre-1.16 rules.
-    posting1 = mkPosting rules record "1"
-               ("account1" `withAlias` "account")
-               ("amount1" `withAlias` "amount")
-               ("amount1-in" `withAlias` "amount-in")
-               ("amount1-out" `withAlias` "amount-out")
-               ("balance1" `withAlias` "balance")
-               "comment1" -- comment1 does not have legacy alias
-               t
-      where
-        withAlias fld alias =
-          case (field fld, field alias) of
-            (Just fld, Just alias) -> error' $ unlines
-              [ "error: both \"" ++ fld ++ "\" and \"" ++ alias ++ "\" have values."
-              , showRecord record
-              , showRules rules record
-              ]
-            (Nothing, Just _) -> alias
-            (_, Nothing)      -> fld
-
-    -- Make other postings where possible, and gather all that were generated.
-    postings = catMaybes $ posting1 : otherpostings
-      where
-        otherpostings = [mkPostingN i | x<-[2..9], let i = show x]
-          where
-            mkPostingN n = mkPosting rules record n
-                           ("account"++n) ("amount"++n) ("amount"++n++"-in")
-                           ("amount"++n++"-out") ("balance"++n) ("comment"++n) t
-  
-    -- Auto-generate a second posting or second posting amount,
-    -- for compatibility with pre-1.16 rules.
-    postings' =
-      case postings of
-        -- when rules generate just one posting, of a kind that needs to be
-        -- balanced, generate the second posting to balance it.
-        [p1@(p1',_)] ->
-          if ptype p1' == VirtualPosting then [p1] else [p1, p2]
-            where
-              p2 = (nullposting{paccount=unknownExpenseAccount
-                               ,pamount=costOfMixedAmount (-pamount p1')
-                               ,ptransaction=Just t}, False)
-        -- when rules generate exactly two postings, and only the second has
-        -- no amount, give it the balancing amount.
-        [p1@(p1',_), p2@(p2',final2)] ->
-          if hasAmount p1' && not (hasAmount p2')
-          then [p1, (p2'{pamount=costOfMixedAmount(-(pamount p1'))}, final2)]
-          else [p1, p2]
-        --
-        ps -> ps
+    -- 3. Generate the postings for which an account has been assigned
+    -- (possibly indirectly due to an amount or balance assignment)
 
-    -- Finally, wherever default "unknown" accounts were used, refine them
-    -- based on the sign of the posting amount if it's now known.
-    postings'' = map maybeImprove postings'
-      where
-        maybeImprove (p,final) = if final then p else improveUnknownAccountName p
+    p1IsVirtual = (accountNamePostingType . T.pack <$> fieldval "account1") == Just VirtualPosting
+    ps = [p | n <- [1..maxpostings]
+         ,let comment  = T.pack $ fromMaybe "" $ fieldval ("comment"++show n)
+         ,let currency = fromMaybe "" (fieldval ("currency"++show n) <|> fieldval "currency")
+         ,let mamount  = getAmount rules record currency p1IsVirtual n
+         ,let mbalance = getBalance rules record currency n
+         ,Just (acct,isfinal) <- [getAccount rules record mamount mbalance n]  -- skips Nothings
+         ,let acct' | not isfinal && acct==unknownExpenseAccount &&
+                      fromMaybe False (mamount >>= isNegativeMixedAmount) = unknownIncomeAccount
+                    | otherwise = acct
+         ,let p = nullposting{paccount          = accountNameWithoutPostingType acct'
+                             ,pamount           = fromMaybe missingmixedamt mamount
+                             ,ptransaction      = Just t
+                             ,pbalanceassertion = mkBalanceAssertion rules record <$> mbalance
+                             ,pcomment          = comment
+                             ,ptype             = accountNamePostingType acct
+                             }
+         ]
 
     ----------------------------------------------------------------------
     -- 4. Build the transaction (and name it, so the postings can reference it).
@@ -899,99 +855,91 @@
           ,tdescription      = T.pack description
           ,tcomment          = T.pack comment
           ,tprecedingcomment = T.pack precomment
-          ,tpostings         = postings''
+          ,tpostings         = ps
           }  
 
--- | Given CSV rules and a CSV record, generate the corresponding transaction's
--- Nth posting, if sufficient fields have been assigned for it.
--- N is provided as a string.
--- The names of the required fields are provided, allowing more flexibility.
--- The transaction which will contain this posting is also provided,
--- so we can build the usual transaction<->posting cyclic reference.
-mkPosting ::
-  CsvRules -> CsvRecord -> String ->
-  HledgerFieldName -> HledgerFieldName -> HledgerFieldName ->
-  HledgerFieldName -> HledgerFieldName -> HledgerFieldName ->
-  Transaction ->
-  Maybe (Posting, Bool)
-mkPosting rules record number accountFld amountFld amountInFld amountOutFld balanceFld commentFld t =
-  -- if we have figured out an account N, make a posting N
-  case maccountAndIsFinal of
-    Nothing            -> Nothing
-    Just (acct, final) ->
-      Just (posting{paccount          = accountNameWithoutPostingType acct
-                   ,pamount           = fromMaybe missingmixedamt mamount
-                   ,ptransaction      = Just t
-                   ,pbalanceassertion = mkBalanceAssertion rules record <$> mbalance
-                   ,pcomment          = comment
-                   ,ptype             = accountNamePostingType acct}
-           ,final)
+-- | Figure out the amount specified for posting N, if any.
+-- Looks for a non-zero amount assigned to one of "amountN", "amountN-in", "amountN-out".
+-- Postings 1 or 2 also look at "amount", "amount-in", "amount-out".
+-- Throws an error if more than one of these has a non-zero amount assigned.
+-- A currency symbol to prepend to the amount, if any, is provided,
+-- and whether posting 1 requires balancing or not.
+getAmount :: CsvRules -> CsvRecord -> String -> Bool -> Int -> Maybe MixedAmount
+getAmount rules record currency p1IsVirtual n =
+  let
+    unnumberedfieldnames = ["amount","amount-in","amount-out"]
+    fieldnames = map (("amount"++show n)++) ["","-in","-out"]
+                 -- For posting 1, also recognise the old amount/amount-in/amount-out names.
+                 -- For posting 2, the same but only if posting 1 needs balancing.
+                 ++ if n==1 || n==2 && not p1IsVirtual then unnumberedfieldnames else []
+    nonzeroamounts = [(f,a') | f <- fieldnames
+                     , Just v@(_:_) <- [strip . renderTemplate rules record <$> hledgerField rules record f]
+                     , let a = parseAmount rules record currency v
+                     , not $ isZeroMixedAmount a
+                       -- With amount/amount-in/amount-out, in posting 2,
+                       -- flip the sign and convert to cost, as they did before 1.17
+                     , let a' = if f `elem` unnumberedfieldnames && n==2 then costOfMixedAmount (-a) else a
+                     ]
+  in case nonzeroamounts of
+      [] -> Nothing
+      [(f,a)] | "-out" `isSuffixOf` f -> Just (-a)  -- for -out fields, flip the sign
+      [(_,a)] -> Just a
+      fs      -> error' $
+           "more than one non-zero amount for this record, please ensure just one\n"
+        ++ unlines ["    " ++ padright 11 f ++ ": " ++ showMixedAmount a
+                    ++ " from rule: " ++ fromMaybe "" (hledgerField rules record f)
+                   | (f,a) <- fs]
+        ++ "    " ++ showRecord record ++ "\n"
   where
-    -- the account name to use for this posting, if any, and whether it is the
-    -- default unknown account, which may be improved later, or an explicitly
-    -- set account, which may not.
-    maccountAndIsFinal :: Maybe (AccountName, Bool) =
-      case maccount of
-        -- accountN is set to the empty string - no posting will be generated
-        Just "" -> Nothing
-        -- accountN is set (possibly to "expenses:unknown"! #1192) - mark it final
-        Just a  -> Just (a, True)
-        -- accountN is unset
-        Nothing ->
-          case (mamount, mbalance) of
-            -- amountN is set, or implied by balanceN - set accountN to
-            -- the default unknown account ("expenses:unknown") and
-            -- allow it to be improved later
-            (Just _, _) -> Just (unknownExpenseAccount, False)
-            (_, Just _) -> Just (unknownExpenseAccount, False)
-            -- amountN is also unset - no posting will be generated
-            (Nothing, Nothing) -> Nothing
+    -- | Given a non-empty amount string to parse, along with a possibly
+    -- non-empty currency symbol to prepend, parse as a hledger amount (as
+    -- in journal format), or raise an error.
+    -- The CSV rules and record are provided for the error message.
+    parseAmount :: CsvRules -> CsvRecord -> String -> String -> MixedAmount
+    parseAmount rules record currency amountstr =
+      either mkerror (Mixed . (:[])) $
+      runParser (evalStateT (amountp <* eof) nulljournal) "" $
+      T.pack $ (currency++) $ simplifySign amountstr
       where
-        maccount = T.pack <$> (fieldval accountFld
-                              -- XXX what's this needed for ? Test & document, or drop.
-                              -- Also, this the only place we interpolate in a keyword rule, I think.
-                               `withDefault` ruleval ("default-account" ++ number))
-    -- XXX what's this needed for ? Test & document, or drop.
-    mdefaultcurrency = rule "default-currency"
-    currency = fromMaybe (fromMaybe "" mdefaultcurrency) $
-               fieldval ("currency"++number) `withDefault` fieldval "currency"
-    mamount = chooseAmount rules record currency amountFld amountInFld amountOutFld
-    mbalance :: Maybe (Amount, GenericSourcePos) =
-      fieldval balanceFld >>= parsebalance currency number
+        mkerror e = error' $ unlines
+          ["error: could not parse \""++amountstr++"\" as an amount"
+          ,showRecord record
+          ,showRules rules record
+          -- ,"the default-currency is: "++fromMaybe "unspecified" (getDirective "default-currency" rules)
+          ,"the parse error is:      "++customErrorBundlePretty e
+          ,"you may need to "
+           ++"change your amount*, balance*, or currency* rules, "
+           ++"or add or change your skip rule"
+          ]
+
+-- | Figure out the expected balance (assertion or assignment) specified for posting N,
+-- if any (and its parse position).
+getBalance :: CsvRules -> CsvRecord -> String -> Int -> Maybe (Amount, GenericSourcePos)
+getBalance rules record currency n =
+  (fieldval ("balance"++show n)
+    -- for posting 1, also recognise the old field name
+    <|> if n==1 then fieldval "balance" else Nothing)
+  >>= parsebalance currency n . strip
+  where
+    parsebalance currency n s
+      | null s    = Nothing
+      | otherwise = Just
+          (either (mkerror n s) id $
+            runParser (evalStateT (amountp <* eof) nulljournal) "" $
+            T.pack $ (currency++) $ simplifySign s
+          ,nullsourcepos)  -- XXX parse position to show when assertion fails,
+                           -- the csv record's line number would be good
       where
-        parsebalance currency n str
-          | all isSpace str = Nothing
-          | otherwise = Just
-              (either (balanceerror n str) id $
-                runParser (evalStateT (amountp <* eof) mempty) "" $
-                T.pack $ (currency++) $ simplifySign str
-              ,nullsourcepos)  -- XXX parse position to show when assertion fails,
-                               -- the csv record's line number would be good
-          where
-            balanceerror n str err = error' $ unlines
-              ["error: could not parse \""++str++"\" as balance"++n++" amount"
-              ,showRecord record
-              ,showRules rules record
-              ,"the default-currency is: "++fromMaybe "unspecified" mdefaultcurrency
-              ,"the parse error is:      "++customErrorBundlePretty err
-              ]
-    comment = T.pack $ fromMaybe "" $ fieldval commentFld
-    rule     = csvRule           rules        :: DirectiveName    -> Maybe FieldTemplate
-    ruleval  = csvRuleValue      rules record :: DirectiveName    -> Maybe String
-    -- field    = hledgerField      rules record :: HledgerFieldName -> Maybe FieldTemplate
+        mkerror n s e = error' $ unlines
+          ["error: could not parse \""++s++"\" as balance"++show n++" amount"
+          ,showRecord record
+          ,showRules rules record
+          -- ,"the default-currency is: "++fromMaybe "unspecified" mdefaultcurrency
+          ,"the parse error is:      "++customErrorBundlePretty e
+          ]
+    -- mdefaultcurrency = rule "default-currency"
     fieldval = hledgerFieldValue rules record :: HledgerFieldName -> Maybe String
--- | Default account names to use when needed.
-unknownExpenseAccount = "expenses:unknown"
-unknownIncomeAccount  = "income:unknown"
 
--- | If this posting has the "expenses:unknown" account name,
--- replace that with "income:unknown" if the amount is negative.
--- The posting's amount should be explicit.
-improveUnknownAccountName p@Posting{..}
-  | paccount == unknownExpenseAccount
-    && fromMaybe False (isNegativeMixedAmount pamount) = p{paccount=unknownIncomeAccount}
-  | otherwise = p
-
 -- | Make a balance assertion for the given amount, with the given parse
 -- position (to be shown in assertion failures), with the assertion type
 -- possibly set by a balance-type rule.
@@ -1013,48 +961,33 @@
           , showRules rules record
           ]
 
-chooseAmount :: CsvRules -> CsvRecord -> String -> String -> String -> String -> Maybe MixedAmount
-chooseAmount rules record currency amountFld amountInFld amountOutFld =
- let
-   mamount    = getEffectiveAssignment rules record amountFld
-   mamountin  = getEffectiveAssignment rules record amountInFld
-   mamountout = getEffectiveAssignment rules record amountOutFld
-   parse  amt = notZero =<< (parseAmount currency <$> notEmpty =<< (strip . renderTemplate rules record) <$> amt)
- in
-  case (parse mamount, parse mamountin, parse mamountout) of
-    (Nothing, Nothing, Nothing) -> Nothing
-    (Just a,  Nothing, Nothing) -> Just a
-    (Nothing, Just i,  Nothing) -> Just i
-    (Nothing, Nothing, Just o)  -> Just $ negate o
-    (Nothing, Just i,  Just o)  -> error' $    "both "++amountInFld++" and "++amountOutFld++" have a value\n"
-                                            ++ "    "++amountInFld++": "  ++ show i ++ "\n"
-                                            ++ "    "++amountOutFld++": " ++ show o ++ "\n"
-                                            ++ "    record: "     ++ showRecord record
-    _                           -> error' $    "found values for "++amountFld++" and for "++amountInFld++"/"++amountOutFld++"\n"
-                                            ++ "please use either "++amountFld++" or "++amountInFld++"/"++amountOutFld++"\n"
-                                            ++ "    record: " ++ showRecord record
- where
-   notZero amt = if isZeroMixedAmount amt then Nothing else Just amt
-   notEmpty str = if str=="" then Nothing else Just str
-
-   parseAmount currency amountstr =
-     either (amounterror amountstr) (Mixed . (:[]))
-     <$> runParser (evalStateT (amountp <* eof) mempty) ""
-     <$> T.pack
-     <$> (currency++)
-     <$> simplifySign
-     <$> amountstr
+-- | Figure out the account name specified for posting N, if any.
+-- And whether it is the default unknown account (which may be
+-- improved later) or an explicitly set account (which may not).
+getAccount :: CsvRules -> CsvRecord -> Maybe MixedAmount -> Maybe (Amount, GenericSourcePos) -> Int -> Maybe (AccountName, Bool)
+getAccount rules record mamount mbalance n =
+  let
+    fieldval = hledgerFieldValue rules record :: HledgerFieldName -> Maybe String
+    maccount = T.pack <$> fieldval ("account"++show n)
+  in case maccount of
+    -- accountN is set to the empty string - no posting will be generated
+    Just "" -> Nothing
+    -- accountN is set (possibly to "expenses:unknown"! #1192) - mark it final
+    Just a  -> Just (a, True)
+    -- accountN is unset
+    Nothing ->
+      case (mamount, mbalance) of
+        -- amountN is set, or implied by balanceN - set accountN to
+        -- the default unknown account ("expenses:unknown") and
+        -- allow it to be improved later
+        (Just _, _) -> Just (unknownExpenseAccount, False)
+        (_, Just _) -> Just (unknownExpenseAccount, False)
+        -- amountN is also unset - no posting will be generated
+        (Nothing, Nothing) -> Nothing
 
-   amounterror amountstr err = error' $ unlines
-     ["error: could not parse \""++fromJust amountstr++"\" as an amount"
-     ,showRecord record
-     ,showRules rules record
-     ,"the default-currency is: "++fromMaybe "unspecified" (getDirective "default-currency" rules)
-     ,"the parse error is:      "++customErrorBundlePretty err
-     ,"you may need to "
-      ++"change your amount or currency rules, "
-      ++"or add or change your skip rule"
-     ]
+-- | Default account names to use when needed.
+unknownExpenseAccount = "expenses:unknown"
+unknownIncomeAccount  = "income:unknown"
 
 type CsvAmountString = String
 
@@ -1088,7 +1021,7 @@
 
 -- | Show a (approximate) recreation of the original CSV record.
 showRecord :: CsvRecord -> String
-showRecord r = "the CSV record is:       "++intercalate "," (map show r)
+showRecord r = "record values: "++intercalate "," (map show r)
 
 -- | Given the conversion rules, a CSV record and a hledger field name, find
 -- the value template ultimately assigned to this field, if any, by a field
@@ -1152,7 +1085,7 @@
 -- the "simple date" formats (YYYY/MM/DD, YYYY-MM-DD, YYYY.MM.DD, leading
 -- zeroes optional).
 parseDateWithCustomOrDefaultFormats :: Maybe DateFormat -> String -> Maybe Day
-parseDateWithCustomOrDefaultFormats mformat s = firstJust $ map parsewith formats
+parseDateWithCustomOrDefaultFormats mformat s = asum $ map parsewith formats
   where
     parsetime =
 #if MIN_VERSION_time(1,5,0)
diff --git a/Hledger/Read/JournalReader.hs b/Hledger/Read/JournalReader.hs
--- a/Hledger/Read/JournalReader.hs
+++ b/Hledger/Read/JournalReader.hs
@@ -82,9 +82,10 @@
 import Control.Monad.Except (ExceptT(..), runExceptT)
 import Control.Monad.State.Strict (get,modify',put)
 import Control.Monad.Trans.Class (lift)
+import Data.Either (isRight)
 import qualified Data.Map.Strict as M
 #if !(MIN_VERSION_base(4,11,0))
-import Data.Monoid ((<>))
+import Data.Semigroup ((<>))
 #endif
 import Data.Text (Text)
 import Data.String
@@ -297,7 +298,7 @@
       put $ updatedChildj <> parentj
 
     newJournalWithParseStateFrom :: FilePath -> Journal -> Journal
-    newJournalWithParseStateFrom filepath j = mempty{
+    newJournalWithParseStateFrom filepath j = nulljournal{
       jparsedefaultyear      = jparsedefaultyear j
       ,jparsedefaultcommodity = jparsedefaultcommodity j
       ,jparseparentaccounts   = jparseparentaccounts j
@@ -424,7 +425,15 @@
   else modify' (\j -> j{jcommodities=M.insert acommodity comm $ jcommodities j})
 
 pleaseincludedecimalpoint :: String
-pleaseincludedecimalpoint = "to avoid ambiguity, please include a decimal separator in commodity directives"
+pleaseincludedecimalpoint = chomp $ unlines [
+   "Please include a decimal point or decimal comma in commodity directives,"
+  ,"to help us parse correctly. It may be followed by zero or more decimal digits."
+  ,"Examples:"
+  ,"commodity $1000.            ; no thousands mark, decimal period, no decimals"
+  ,"commodity 1.234,00 ARS      ; period at thousands, decimal comma, 2 decimals"
+  ,"commodity EUR 1 000,000     ; space at thousands, decimal comma, 3 decimals"
+  ,"commodity INR1,23,45,678.0  ; comma at thousands/lakhs/crores, decimal period, 1 decimal"
+  ]
 
 -- | Parse a multi-line commodity directive, containing 0 or more format subdirectives.
 --
@@ -746,7 +755,7 @@
     ,test "yearless date with no default year" $ assertParseError datep "1/1" "current year is unknown"
     ,test "yearless date with default year" $ do
       let s = "1/1"
-      ep <- parseWithState mempty{jparsedefaultyear=Just 2018} datep s
+      ep <- parseWithState nulljournal{jparsedefaultyear=Just 2018} datep s
       either (assertFailure . ("parse error at "++) . customErrorBundlePretty) (const $ return ()) ep
     ,test "no leading zero" $ assertParse datep "2018/1/1"
     ]
@@ -968,7 +977,7 @@
 
   ,test "defaultcommoditydirectivep" $ do
       assertParse defaultcommoditydirectivep "D $1,000.0\n"
-      assertParseError defaultcommoditydirectivep "D $1000\n" "please include a decimal separator"
+      assertParseError defaultcommoditydirectivep "D $1000\n" "Please include a decimal point or decimal comma"
 
   ,tests "defaultyeardirectivep" [
       test "1000" $ assertParse defaultyeardirectivep "Y 1000" -- XXX no \n like the others
diff --git a/Hledger/Utils.hs b/Hledger/Utils.hs
--- a/Hledger/Utils.hs
+++ b/Hledger/Utils.hs
@@ -146,13 +146,6 @@
 
 instance Default Bool where def = False
 
-isLeft :: Either a b -> Bool
-isLeft (Left _) = True
-isLeft _        = False
-
-isRight :: Either a b -> Bool
-isRight = not . isLeft
-
 -- | Apply a function the specified number of times,
 -- which should be > 0 (otherwise does nothing).
 -- Possibly uses O(n) stack ?
@@ -177,10 +170,6 @@
     ('~':'\\':p) -> (</> p) <$> getHomeDirectory
     ('~':_)      -> ioError $ userError "~USERNAME in paths is not supported"
     p            -> return p
-
-firstJust ms = case dropWhile (==Nothing) ms of
-    [] -> Nothing
-    (md:_) -> md
 
 -- | Read text from a file,
 -- converting any \r\n line endings to \n,,
diff --git a/Hledger/Utils/Debug.hs b/Hledger/Utils/Debug.hs
--- a/Hledger/Utils/Debug.hs
+++ b/Hledger/Utils/Debug.hs
@@ -12,6 +12,8 @@
   ,pshow
   ,ptrace
   ,traceWith
+  ,traceAt
+  ,traceAtWith
   ,debugLevel
   ,ptraceAt
   ,ptraceAtWith
@@ -81,8 +83,9 @@
 ptrace :: Show a => a -> a
 ptrace = traceWith pshow
 
--- | Trace (print to stderr) a showable value using a custom show function.
-traceWith :: (a -> String) -> a -> a
+-- | Like traceShowId, but uses a custom show function to render the value.
+-- traceShowIdWith was too much of a mouthful.
+traceWith :: Show a => (a -> String) -> a -> a
 traceWith f a = trace (f a) a
 
 -- | Global debug level, which controls the verbosity of debug output
@@ -107,6 +110,18 @@
 
     where
       args = unsafePerformIO getArgs
+
+-- | Trace (print to stderr) a string if the global debug level is at
+-- or above the specified level. At level 0, always prints. Otherwise,
+-- uses unsafePerformIO.
+traceAt :: Int -> String -> a -> a
+traceAt level
+    | level > 0 && debugLevel < level = flip const
+    | otherwise = trace
+
+-- | Trace (print to stderr) a showable value using a custom show function.
+traceAtWith :: (a -> String) -> a -> a
+traceAtWith f a = trace (f a) a
 
 -- | Pretty-print a label and a showable value to the console
 -- if the global debug level is at or above the specified level.
diff --git a/Hledger/Utils/Test.hs b/Hledger/Utils/Test.hs
--- a/Hledger/Utils/Test.hs
+++ b/Hledger/Utils/Test.hs
@@ -25,8 +25,9 @@
 
 import Control.Monad.Except (ExceptT, runExceptT)
 import Control.Monad.State.Strict (StateT, evalStateT, execStateT)
+import Data.Default (Default(..))
 #if !(MIN_VERSION_base(4,11,0))
-import Data.Monoid ((<>))
+import Data.Semigroup ((<>))
 #endif
 -- import Data.CallStack
 import Data.List (isInfixOf)
@@ -73,35 +74,35 @@
 -- | Assert that this stateful parser runnable in IO successfully parses
 -- all of the given input text, showing the parse error if it fails.
 -- Suitable for hledger's JournalParser parsers.
-assertParse :: (HasCallStack, Eq a, Show a, Monoid st) =>
+assertParse :: (HasCallStack, Eq a, Show a, Default st) =>
   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> Assertion
 assertParse parser input = do
-  ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
+  ep <- runParserT (evalStateT (parser <* eof) def) "" input
   either (assertFailure.(++"\n").("\nparse error at "++).customErrorBundlePretty)
          (const $ return ())
          ep
 
 -- | Assert a parser produces an expected value.
-assertParseEq :: (HasCallStack, Eq a, Show a, Monoid st) =>
+assertParseEq :: (HasCallStack, Eq a, Show a, Default st) =>
   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> a -> Assertion
 assertParseEq parser input expected = assertParseEqOn parser input id expected
 
 -- | Like assertParseEq, but transform the parse result with the given function
 -- before comparing it.
-assertParseEqOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
+assertParseEqOn :: (HasCallStack, Eq b, Show b, Default st) =>
   StateT st (ParsecT CustomErr T.Text IO) a -> T.Text -> (a -> b) -> b -> Assertion
 assertParseEqOn parser input f expected = do
-  ep <- runParserT (evalStateT (parser <* eof) mempty) "" input
+  ep <- runParserT (evalStateT (parser <* eof) def) "" input
   either (assertFailure . (++"\n") . ("\nparse error at "++) . customErrorBundlePretty)
          (assertEqual "" expected . f)
          ep
 
 -- | Assert that this stateful parser runnable in IO fails to parse
 -- the given input text, with a parse error containing the given string.
-assertParseError :: (HasCallStack, Eq a, Show a, Monoid st) =>
+assertParseError :: (HasCallStack, Eq a, Show a, Default st) =>
   StateT st (ParsecT CustomErr T.Text IO) a -> String -> String -> Assertion
 assertParseError parser input errstr = do
-  ep <- runParserT (evalStateT parser mempty) "" (T.pack input)
+  ep <- runParserT (evalStateT parser def) "" (T.pack input)
   case ep of
     Right v -> assertFailure $ "\nparse succeeded unexpectedly, producing:\n" ++ pshow v ++ "\n"
     Left e  -> do
@@ -113,28 +114,28 @@
 -- | Run a stateful parser in IO like assertParse, then assert that the
 -- final state (the wrapped state, not megaparsec's internal state),
 -- transformed by the given function, matches the given expected value.
-assertParseStateOn :: (HasCallStack, Eq b, Show b, Monoid st) =>
+assertParseStateOn :: (HasCallStack, Eq b, Show b, Default st) =>
      StateT st (ParsecT CustomErr T.Text IO) a
   -> T.Text
   -> (st -> b)
   -> b
   -> Assertion
 assertParseStateOn parser input f expected = do
-  es <- runParserT (execStateT (parser <* eof) mempty) "" input
+  es <- runParserT (execStateT (parser <* eof) def) "" input
   case es of
     Left err -> assertFailure $ (++"\n") $ ("\nparse error at "++) $ customErrorBundlePretty err
     Right s  -> assertEqual "" expected $ f s
 
 -- | These "E" variants of the above are suitable for hledger's ErroringJournalParser parsers.
 assertParseE
-  :: (HasCallStack, Eq a, Show a, Monoid st)
+  :: (HasCallStack, Eq a, Show a, Default st)
   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
   -> T.Text
   -> Assertion
 assertParseE parser input = do
   let filepath = ""
   eep <- runExceptT $
-           runParserT (evalStateT (parser <* eof) mempty) filepath input
+           runParserT (evalStateT (parser <* eof) def) filepath input
   case eep of
     Left finalErr ->
       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
@@ -145,7 +146,7 @@
              ep
 
 assertParseEqE
-  :: (Monoid st, Eq a, Show a, HasCallStack)
+  :: (Default st, Eq a, Show a, HasCallStack)
   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
   -> T.Text
   -> a
@@ -153,7 +154,7 @@
 assertParseEqE parser input expected = assertParseEqOnE parser input id expected
 
 assertParseEqOnE
-  :: (HasCallStack, Eq b, Show b, Monoid st)
+  :: (HasCallStack, Eq b, Show b, Default st)
   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
   -> T.Text
   -> (a -> b)
@@ -161,7 +162,7 @@
   -> Assertion
 assertParseEqOnE parser input f expected = do
   let filepath = ""
-  eep <- runExceptT $ runParserT (evalStateT (parser <* eof) mempty) filepath input
+  eep <- runExceptT $ runParserT (evalStateT (parser <* eof) def) filepath input
   case eep of
     Left finalErr ->
       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
@@ -172,14 +173,14 @@
              ep
 
 assertParseErrorE
-  :: (Monoid st, Eq a, Show a, HasCallStack)
+  :: (Default st, Eq a, Show a, HasCallStack)
   => StateT st (ParsecT CustomErr T.Text (ExceptT FinalParseError IO)) a
   -> T.Text
   -> String
   -> Assertion
 assertParseErrorE parser input errstr = do
   let filepath = ""
-  eep <- runExceptT $ runParserT (evalStateT parser mempty) filepath input
+  eep <- runExceptT $ runParserT (evalStateT parser def) filepath input
   case eep of
     Left finalErr -> do
       let prettyErr = finalErrorBundlePretty $ attachSource filepath input finalErr
diff --git a/hledger-lib.cabal b/hledger-lib.cabal
--- a/hledger-lib.cabal
+++ b/hledger-lib.cabal
@@ -4,10 +4,10 @@
 --
 -- see: https://github.com/sol/hpack
 --
--- hash: 4f9a789b279703a75cecaf4e5cca835f20a5ddb9cfeb9e98e62db665a573d016
+-- hash: 2280d91d2a756a6b17f33e15453b491d8945496a3876f4df74683110a0cc5d08
 
 name:           hledger-lib
-version:        1.17.0.1
+version:        1.17.1
 synopsis:       Core data types, parsers and functionality for the hledger accounting tools
 description:    This is a reusable library containing hledger's core functionality.
                 .
@@ -106,7 +106,7 @@
       ./.
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
   build-depends:
-      Decimal
+      Decimal >=0.5.1
     , Glob >=0.9
     , aeson
     , ansi-terminal >=0.6.2.3
@@ -157,7 +157,7 @@
       test
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
   build-depends:
-      Decimal
+      Decimal >=0.5.1
     , Glob >=0.7
     , aeson
     , ansi-terminal >=0.6.2.3
@@ -199,8 +199,8 @@
     , transformers >=0.2
     , uglymemo
     , utf8-string >=0.3.5
-  buildable: False
-  if (impl(ghc < 8.2))
+  buildable: True
+  if (impl(ghc < 8.2) || impl(ghc >= 8.10))
     buildable: False
   default-language: Haskell2010
 
@@ -212,7 +212,7 @@
       test
   ghc-options: -Wall -fno-warn-unused-do-bind -fno-warn-name-shadowing -fno-warn-missing-signatures -fno-warn-type-defaults -fno-warn-orphans
   build-depends:
-      Decimal
+      Decimal >=0.5.1
     , Glob >=0.9
     , aeson
     , ansi-terminal >=0.6.2.3
@@ -254,5 +254,4 @@
     , transformers >=0.2
     , uglymemo
     , utf8-string >=0.3.5
-  buildable: True
   default-language: Haskell2010
diff --git a/hledger_csv.5 b/hledger_csv.5
--- a/hledger_csv.5
+++ b/hledger_csv.5
@@ -1,6 +1,6 @@
 .\"t
 
-.TH "hledger_csv" "5" "March 2020" "hledger 1.17" "hledger User Manuals"
+.TH "hledger_csv" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
 
 
 
@@ -487,34 +487,48 @@
 \f[C]description\f[R], \f[C]comment\f[R] can be used to form the
 transaction\[aq]s first line.
 .SS Posting field names
+.SS account
 .PP
-\f[C]accountN\f[R], where N is 1 to 9, generates a posting, with that
-account name.
+\f[C]accountN\f[R], where N is 1 to 9, causes a posting to be generated,
+with that account name.
+.PP
 Most often there are two postings, so you\[aq]ll want to set
 \f[C]account1\f[R] and \f[C]account2\f[R].
-If a posting\[aq]s account name is left unset but its amount is set, a
-default account name will be chosen (like expenses:unknown or
-income:unknown).
+Typically \f[C]account1\f[R] is associated with the CSV file, and is set
+once with a top-level assignment, while \f[C]account2\f[R] is set based
+on each transaction\[aq]s description, and in conditional blocks.
 .PP
+If a posting\[aq]s account name is left unset but its amount is set (see
+below), a default account name will be chosen (like
+\[dq]expenses:unknown\[dq] or \[dq]income:unknown\[dq]).
+.SS amount
+.PP
 \f[C]amountN\f[R] sets posting N\[aq]s amount.
-Or, \f[C]amount\f[R] with no N sets posting 1\[aq]s.
-If the CSV has debits and credits in separate fields, use
-\f[C]amountN-in\f[R] and \f[C]amountN-out\f[R] instead.
-Or \f[C]amount-in\f[R] and \f[C]amount-out\f[R] with no N for posting 1.
+If the CSV uses separate fields for debit and credit amounts, you can
+use \f[C]amountN-in\f[R] and \f[C]amountN-out\f[R] instead.
 .PP
-For convenience and backwards compatibility, if you set the amount of
-posting 1 only, a second posting with the negative amount will be
-generated automatically.
-(Unless the account name is parenthesised indicating an unbalanced
-posting.)
+Also, for compatibility with hledger <1.17: \f[C]amount\f[R] or
+\f[C]amount-in\f[R]/\f[C]amount-out\f[R] with no number sets the amount
+for postings 1 and 2.
+For posting 2 the amount is negated, and converted to cost if
+there\[aq]s a transaction price.
+.SS currency
 .PP
-If the CSV has the currency symbol in a separate field, you can use
-\f[C]currencyN\f[R] to prepend it to posting N\[aq]s amount.
-\f[C]currency\f[R] with no N affects ALL postings.
+If the CSV has the currency symbol in a separate field (ie, not part of
+the amount field), you can use \f[C]currencyN\f[R] to prepend it to
+posting N\[aq]s amount.
+Or, \f[C]currency\f[R] with no number affects all postings.
+.SS balance
 .PP
 \f[C]balanceN\f[R] sets a balance assertion amount (or if the posting
-amount is left empty, a balance assignment).
-You may need to adjust this with the \f[C]balance-type\f[R] rule.
+amount is left empty, a balance assignment) on posting N.
+.PP
+Also, for compatibility with hledger <1.17: \f[C]balance\f[R] with no
+number is equivalent to \f[C]balance1\f[R].
+.PP
+You can adjust the type of assertion/assignment with the
+\f[C]balance-type\f[R] rule (see below).
+.SS comment
 .PP
 Finally, \f[C]commentN\f[R] sets a comment on the Nth posting.
 Comments can also contain tags, as usual.
diff --git a/hledger_csv.info b/hledger_csv.info
--- a/hledger_csv.info
+++ b/hledger_csv.info
@@ -3,8 +3,8 @@
 
 File: hledger_csv.info,  Node: Top,  Next: EXAMPLES,  Up: (dir)
 
-hledger_csv(5) hledger 1.17
-***************************
+hledger_csv(5) hledger 1.17.1
+*****************************
 
 CSV - how hledger reads CSV data, and the CSV rules file format
 
@@ -451,31 +451,79 @@
 2.2.2 Posting field names
 -------------------------
 
-'accountN', where N is 1 to 9, generates a posting, with that account
-name.  Most often there are two postings, so you'll want to set
-'account1' and 'account2'.  If a posting's account name is left unset
-but its amount is set, a default account name will be chosen (like
-expenses:unknown or income:unknown).
+* Menu:
 
-   'amountN' sets posting N's amount.  Or, 'amount' with no N sets
-posting 1's.  If the CSV has debits and credits in separate fields, use
-'amountN-in' and 'amountN-out' instead.  Or 'amount-in' and 'amount-out'
-with no N for posting 1.
+* account::
+* amount::
+* currency::
+* balance::
+* comment::
 
-   For convenience and backwards compatibility, if you set the amount of
-posting 1 only, a second posting with the negative amount will be
-generated automatically.  (Unless the account name is parenthesised
-indicating an unbalanced posting.)
+
+File: hledger_csv.info,  Node: account,  Next: amount,  Up: Posting field names
 
-   If the CSV has the currency symbol in a separate field, you can use
-'currencyN' to prepend it to posting N's amount.  'currency' with no N
-affects ALL postings.
+2.2.2.1 account
+...............
 
-   'balanceN' sets a balance assertion amount (or if the posting amount
-is left empty, a balance assignment).  You may need to adjust this with
-the 'balance-type' rule.
+'accountN', where N is 1 to 9, causes a posting to be generated, with
+that account name.
 
-   Finally, 'commentN' sets a comment on the Nth posting.  Comments can
+   Most often there are two postings, so you'll want to set 'account1'
+and 'account2'.  Typically 'account1' is associated with the CSV file,
+and is set once with a top-level assignment, while 'account2' is set
+based on each transaction's description, and in conditional blocks.
+
+   If a posting's account name is left unset but its amount is set (see
+below), a default account name will be chosen (like "expenses:unknown"
+or "income:unknown").
+
+
+File: hledger_csv.info,  Node: amount,  Next: currency,  Prev: account,  Up: Posting field names
+
+2.2.2.2 amount
+..............
+
+'amountN' sets posting N's amount.  If the CSV uses separate fields for
+debit and credit amounts, you can use 'amountN-in' and 'amountN-out'
+instead.
+
+   Also, for compatibility with hledger <1.17: 'amount' or
+'amount-in'/'amount-out' with no number sets the amount for postings 1
+and 2.  For posting 2 the amount is negated, and converted to cost if
+there's a transaction price.
+
+
+File: hledger_csv.info,  Node: currency,  Next: balance,  Prev: amount,  Up: Posting field names
+
+2.2.2.3 currency
+................
+
+If the CSV has the currency symbol in a separate field (ie, not part of
+the amount field), you can use 'currencyN' to prepend it to posting N's
+amount.  Or, 'currency' with no number affects all postings.
+
+
+File: hledger_csv.info,  Node: balance,  Next: comment,  Prev: currency,  Up: Posting field names
+
+2.2.2.4 balance
+...............
+
+'balanceN' sets a balance assertion amount (or if the posting amount is
+left empty, a balance assignment) on posting N.
+
+   Also, for compatibility with hledger <1.17: 'balance' with no number
+is equivalent to 'balance1'.
+
+   You can adjust the type of assertion/assignment with the
+'balance-type' rule (see below).
+
+
+File: hledger_csv.info,  Node: comment,  Prev: balance,  Up: Posting field names
+
+2.2.2.5 comment
+...............
+
+Finally, 'commentN' sets a comment on the Nth posting.  Comments can
 also contain tags, as usual.
 
    See TIPS below for more about setting amounts and currency.
@@ -971,64 +1019,74 @@
 
 Tag Table:
 Node: Top72
-Node: EXAMPLES2093
-Ref: #examples2199
-Node: Basic2407
-Ref: #basic2507
-Node: Bank of Ireland3049
-Ref: #bank-of-ireland3184
-Node: Amazon4646
-Ref: #amazon4764
-Node: Paypal6483
-Ref: #paypal6577
-Node: CSV RULES14221
-Ref: #csv-rules14330
-Node: skip14606
-Ref: #skip14699
-Node: fields15074
-Ref: #fields15196
-Node: Transaction field names16361
-Ref: #transaction-field-names16521
-Node: Posting field names16632
-Ref: #posting-field-names16784
-Node: field assignment18075
-Ref: #field-assignment18218
-Node: separator19036
-Ref: #separator19165
-Node: if19576
-Ref: #if19678
-Node: end21597
-Ref: #end21703
-Node: date-format21927
-Ref: #date-format22059
-Node: newest-first22808
-Ref: #newest-first22946
-Node: include23629
-Ref: #include23758
-Node: balance-type24202
-Ref: #balance-type24322
-Node: TIPS25022
-Ref: #tips25104
-Node: Rapid feedback25360
-Ref: #rapid-feedback25477
-Node: Valid CSV25937
-Ref: #valid-csv26067
-Node: File Extension26259
-Ref: #file-extension26411
-Node: Reading multiple CSV files26821
-Ref: #reading-multiple-csv-files27006
-Node: Valid transactions27247
-Ref: #valid-transactions27425
-Node: Deduplicating importing28053
-Ref: #deduplicating-importing28232
-Node: Setting amounts29265
-Ref: #setting-amounts29434
-Node: Setting currency/commodity30420
-Ref: #setting-currencycommodity30612
-Node: Referencing other fields31415
-Ref: #referencing-other-fields31615
-Node: How CSV rules are evaluated32512
-Ref: #how-csv-rules-are-evaluated32685
+Node: EXAMPLES2097
+Ref: #examples2203
+Node: Basic2411
+Ref: #basic2511
+Node: Bank of Ireland3053
+Ref: #bank-of-ireland3188
+Node: Amazon4650
+Ref: #amazon4768
+Node: Paypal6487
+Ref: #paypal6581
+Node: CSV RULES14225
+Ref: #csv-rules14334
+Node: skip14610
+Ref: #skip14703
+Node: fields15078
+Ref: #fields15200
+Node: Transaction field names16365
+Ref: #transaction-field-names16525
+Node: Posting field names16636
+Ref: #posting-field-names16788
+Node: account16858
+Ref: #account16974
+Node: amount17510
+Ref: #amount17641
+Node: currency18022
+Ref: #currency18157
+Node: balance18363
+Ref: #balance18497
+Node: comment18814
+Ref: #comment18931
+Node: field assignment19094
+Ref: #field-assignment19237
+Node: separator20055
+Ref: #separator20184
+Node: if20595
+Ref: #if20697
+Node: end22616
+Ref: #end22722
+Node: date-format22946
+Ref: #date-format23078
+Node: newest-first23827
+Ref: #newest-first23965
+Node: include24648
+Ref: #include24777
+Node: balance-type25221
+Ref: #balance-type25341
+Node: TIPS26041
+Ref: #tips26123
+Node: Rapid feedback26379
+Ref: #rapid-feedback26496
+Node: Valid CSV26956
+Ref: #valid-csv27086
+Node: File Extension27278
+Ref: #file-extension27430
+Node: Reading multiple CSV files27840
+Ref: #reading-multiple-csv-files28025
+Node: Valid transactions28266
+Ref: #valid-transactions28444
+Node: Deduplicating importing29072
+Ref: #deduplicating-importing29251
+Node: Setting amounts30284
+Ref: #setting-amounts30453
+Node: Setting currency/commodity31439
+Ref: #setting-currencycommodity31631
+Node: Referencing other fields32434
+Ref: #referencing-other-fields32634
+Node: How CSV rules are evaluated33531
+Ref: #how-csv-rules-are-evaluated33704
 
 End Tag Table
 
diff --git a/hledger_csv.txt b/hledger_csv.txt
--- a/hledger_csv.txt
+++ b/hledger_csv.txt
@@ -376,30 +376,45 @@
        transaction's first line.
 
    Posting field names
-       accountN, where N is 1 to 9, generates a  posting,  with  that  account
-       name.   Most  often  there  are two postings, so you'll want to set ac-
-       count1 and account2.  If a posting's account name is left unset but its
-       amount is set, a default account name will be chosen (like expenses:un-
-       known or income:unknown).
+   account
+       accountN, where N is 1 to 9, causes a posting  to  be  generated,  with
+       that account name.
 
-       amountN sets posting N's amount.  Or, amount with  no  N  sets  posting
-       1's.   If  the  CSV  has  debits  and  credits  in separate fields, use
-       amountN-in and amountN-out instead.  Or amount-in and  amount-out  with
-       no N for posting 1.
+       Most  often  there are two postings, so you'll want to set account1 and
+       account2.  Typically account1 is associated with the CSV file,  and  is
+       set  once  with  a top-level assignment, while account2 is set based on
+       each transaction's description, and in conditional blocks.
 
-       For  convenience  and backwards compatibility, if you set the amount of
-       posting 1 only, a second posting with the negative amount will be  gen-
-       erated  automatically.  (Unless the account name is parenthesised indi-
-       cating an unbalanced posting.)
+       If a posting's account name is left unset but its amount  is  set  (see
+       below),  a default account name will be chosen (like "expenses:unknown"
+       or "income:unknown").
 
-       If the CSV has the currency symbol in a separate  field,  you  can  use
-       currencyN  to prepend it to posting N's amount.  currency with no N af-
-       fects ALL postings.
+   amount
+       amountN sets posting N's amount.  If the CSV uses separate  fields  for
+       debit  and  credit  amounts, you can use amountN-in and amountN-out in-
+       stead.
 
-       balanceN sets a balance assertion amount (or if the posting  amount  is
-       left  empty,  a  balance assignment).  You may need to adjust this with
-       the balance-type rule.
+       Also, for compatibility with hledger <1.17: amount or amount-in/amount-
+       out with no number sets the amount for postings 1 and 2.  For posting 2
+       the amount is negated, and converted to cost if there's  a  transaction
+       price.
 
+   currency
+       If the CSV has the currency symbol in a separate field (ie, not part of
+       the amount field), you can use currencyN to prepend it to  posting  N's
+       amount.  Or, currency with no number affects all postings.
+
+   balance
+       balanceN  sets  a balance assertion amount (or if the posting amount is
+       left empty, a balance assignment) on posting N.
+
+       Also, for compatibility with hledger <1.17: balance with no  number  is
+       equivalent to balance1.
+
+       You  can  adjust the type of assertion/assignment with the balance-type
+       rule (see below).
+
+   comment
        Finally, commentN sets a comment on the Nth posting.  Comments can also
        contain tags, as usual.
 
@@ -819,4 +834,4 @@
 
 
 
-hledger 1.17                      March 2020                    hledger_csv(5)
+hledger 1.17.1                    March 2020                    hledger_csv(5)
diff --git a/hledger_journal.5 b/hledger_journal.5
--- a/hledger_journal.5
+++ b/hledger_journal.5
@@ -1,6 +1,6 @@
 .\"t
 
-.TH "hledger_journal" "5" "March 2020" "hledger 1.17" "hledger User Manuals"
+.TH "hledger_journal" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
 
 
 
@@ -524,7 +524,7 @@
 \f[R]
 .fi
 .PP
-A decimal mark (decimal point) can be written with a period or a comma:
+A decimal mark can be written as a period or a comma:
 .IP
 .nf
 \f[C]
@@ -1027,6 +1027,16 @@
 T}@T{
 following inline/included entries until end of current file
 T}
+T{
+\f[C]=\f[R]
+T}@T{
+T}@T{
+T}@T{
+declare an auto posting rule, adding postings to other transactions
+T}@T{
+all entries in parent/current/child files (but not sibling files, see
+#1212)
+T}
 .TE
 .PP
 And some definitions:
@@ -1066,13 +1076,21 @@
 affect, and whether they are focussed on input (parsing) or output
 (reports).
 Some directives have multiple effects.
+.SS Directives and multiple files
 .PP
-If you have a journal made up of multiple files, or pass multiple -f
-options on the command line, note that directives which affect input
-typically last only until the end of their defining file.
-This provides more simplicity and predictability, eg reports are not
-changed by writing file options in a different order.
-It can be surprising at times though.
+If you use multiple \f[C]-f\f[R]/\f[C]--file\f[R] options, or the
+\f[C]include\f[R] directive, hledger will process multiple input files.
+But note that directives which affect input (see above) typically last
+only until the end of the file in which they occur.
+.PP
+This may seem inconvenient, but it\[aq]s intentional; it makes reports
+stable and deterministic, independent of the order of input.
+Otherwise you could see different numbers if you happened to write -f
+options in a different order, or if you moved includes around while
+cleaning up your files.
+.PP
+It can be surprising though; for example, it means that \f[C]alias\f[R]
+directives do not affect parent or sibling files (see below).
 .SS Comment blocks
 .PP
 A line containing just \f[C]comment\f[R] starts a commented region of
@@ -1543,6 +1561,46 @@
 .PP
 In case of trouble, adding \f[C]--debug=6\f[R] to the command line will
 show which aliases are being applied when.
+.SS Aliases and multiple files
+.PP
+As explained at Directives and multiple files, \f[C]alias\f[R]
+directives do not affect parent or sibling files.
+Eg in this command,
+.IP
+.nf
+\f[C]
+hledger -f a.aliases -f b.journal
+\f[R]
+.fi
+.PP
+account aliases defined in a.aliases will not affect b.journal.
+Including the aliases doesn\[aq]t work either:
+.IP
+.nf
+\f[C]
+include a.aliases
+
+2020-01-01  ; not affected by a.aliases
+  foo  1
+  bar
+\f[R]
+.fi
+.PP
+This means that account aliases should usually be declared at the start
+of your top-most file, like this:
+.IP
+.nf
+\f[C]
+alias foo=Foo
+alias bar=Bar
+
+2020-01-01  ; affected by aliases above
+  foo  1
+  bar
+
+include c.journal  ; also affected
+\f[R]
+.fi
 .SS \f[C]end aliases\f[R]
 .PP
 You can clear (forget) all currently defined aliases with the
@@ -1759,31 +1817,28 @@
 For more details, see: balance: Budget report and Budgeting and
 Forecasting.
 .PP
-.SS Auto postings / transaction modifiers
+.SS Auto postings
 .PP
-Transaction modifier rules, AKA auto posting rules, describe changes to
-be applied automatically to certain matched transactions.
-Currently just one kind of change is possible - adding extra postings,
-which we call \[dq]automated postings\[dq] or just \[dq]auto
-postings\[dq].
-These rules become active when you use the \f[C]--auto\f[R] flag.
+\[dq]Automated postings\[dq] or \[dq]auto postings\[dq] are extra
+postings which get added automatically to transactions which match
+certain queries, defined by \[dq]auto posting rules\[dq], when you use
+the \f[C]--auto\f[R] flag.
 .PP
-A transaction modifier rule looks much like a normal transaction except
-the first line is an equals sign followed by a query that matches
-certain postings (mnemonic: \f[C]=\f[R] suggests matching).
-And each \[dq]posting\[dq] is actually a posting-generating rule:
+An auto posting rule looks a bit like a transaction:
 .IP
 .nf
 \f[C]
 = QUERY
     ACCOUNT  AMOUNT
-    ACCOUNT  [AMOUNT]
     ...
+    ACCOUNT  [AMOUNT]
 \f[R]
 .fi
 .PP
-These posting-generating rules look like normal postings, except the
-amount can be:
+except the first line is an equals sign (mnemonic: \f[C]=\f[R] suggests
+matching), followed by a query (which matches existing postings), and
+each \[dq]posting\[dq] line describes a posting to be generated, and the
+posting amounts can be:
 .IP \[bu] 2
 a normal amount with a commodity symbol, eg \f[C]$2\f[R].
 This will be used as-is.
@@ -1801,7 +1856,7 @@
 The matched posting\[aq]s amount will be multiplied by N, and its
 commodity symbol will be replaced with S.
 .PP
-A query term containing spaces must be enclosed in single or double
+Any query term containing spaces must be enclosed in single or double
 quotes, as on the command line.
 Eg, note the quotes around the second query term below:
 .IP
@@ -1812,10 +1867,6 @@
 \f[R]
 .fi
 .PP
-These rules have global effect - a rule appearing anywhere in your data
-can potentially affect any transaction, including transactions recorded
-above it or in another file.
-.PP
 Some examples:
 .IP
 .nf
@@ -1854,6 +1905,12 @@
     assets:checking            $20
 \f[R]
 .fi
+.SS Auto postings and multiple files
+.PP
+An auto posting rule can affect any transaction in the current file, or
+in any parent file or child file.
+Note, currently it will not affect sibling files (when multiple
+\f[C]-f\f[R]/\f[C]--file\f[R] are used - see #1212).
 .SS Auto postings and dates
 .PP
 A posting date (or secondary date) in the matched posting, or (taking
@@ -1861,7 +1918,7 @@
 used in the generated posting.
 .SS Auto postings and transaction balancing / inferred amounts / balance assertions
 .PP
-Currently, transaction modifiers are applied / auto postings are added:
+Currently, auto postings are added:
 .IP \[bu] 2
 after missing amounts are inferred, and transactions are checked for
 balancedness,
@@ -1873,7 +1930,7 @@
 This changed in hledger 1.12+; see #893 for background.
 .SS Auto posting tags
 .PP
-Postings added by transaction modifiers will have some extra tags:
+Automated postings will have some extra tags:
 .IP \[bu] 2
 \f[C]generated-posting:= QUERY\f[R] - shows this was generated by an
 auto posting rule, and the query
@@ -1883,8 +1940,8 @@
 This can be used to match postings generated \[dq]just now\[dq], rather
 than generated in the past and saved to the journal.
 .PP
-Also, any transaction that has been changed by transaction modifier
-rules will have these tags added:
+Also, any transaction that has been changed by auto posting rules will
+have these tags added:
 .IP \[bu] 2
 \f[C]modified:\f[R] - this transaction was modified
 .IP \[bu] 2
diff --git a/hledger_journal.info b/hledger_journal.info
--- a/hledger_journal.info
+++ b/hledger_journal.info
@@ -4,8 +4,8 @@
 
 File: hledger_journal.info,  Node: Top,  Up: (dir)
 
-hledger_journal(5) hledger 1.17
-*******************************
+hledger_journal(5) hledger 1.17.1
+*********************************
 
 Journal - hledger's default file format, representing a General Journal
 
@@ -85,7 +85,7 @@
 * Balance Assignments::
 * Directives::
 * Periodic transactions::
-* Auto postings / transaction modifiers::
+* Auto postings::
 
 
 File: hledger_journal.info,  Node: Dates,  Next: Status,  Up: Transactions
@@ -470,8 +470,7 @@
 1E-6
 EUR 1E3
 
-   A decimal mark (decimal point) can be written with a period or a
-comma:
+   A decimal mark can be written as a period or a comma:
 
 1.23
 1,23456780000009
@@ -912,6 +911,11 @@
                            dates                         inline/included
                                                          entries until end
                                                          of current file
+'='                        declare an auto posting       all entries in
+                           rule, adding postings to      parent/current/child
+                           other transactions            files (but not
+                                                         sibling files,
+                                                         see #1212)
 
    And some definitions:
 
@@ -929,15 +933,9 @@
 they affect, and whether they are focussed on input (parsing) or output
 (reports).  Some directives have multiple effects.
 
-   If you have a journal made up of multiple files, or pass multiple -f
-options on the command line, note that directives which affect input
-typically last only until the end of their defining file.  This provides
-more simplicity and predictability, eg reports are not changed by
-writing file options in a different order.  It can be surprising at
-times though.
-
 * Menu:
 
+* Directives and multiple files::
 * Comment blocks::
 * Including other files::
 * Default year::
@@ -949,9 +947,29 @@
 * Default parent account::
 
 
-File: hledger_journal.info,  Node: Comment blocks,  Next: Including other files,  Up: Directives
+File: hledger_journal.info,  Node: Directives and multiple files,  Next: Comment blocks,  Up: Directives
 
-1.12.1 Comment blocks
+1.12.1 Directives and multiple files
+------------------------------------
+
+If you use multiple '-f'/'--file' options, or the 'include' directive,
+hledger will process multiple input files.  But note that directives
+which affect input (see above) typically last only until the end of the
+file in which they occur.
+
+   This may seem inconvenient, but it's intentional; it makes reports
+stable and deterministic, independent of the order of input.  Otherwise
+you could see different numbers if you happened to write -f options in a
+different order, or if you moved includes around while cleaning up your
+files.
+
+   It can be surprising though; for example, it means that 'alias'
+directives do not affect parent or sibling files (see below).
+
+
+File: hledger_journal.info,  Node: Comment blocks,  Next: Including other files,  Prev: Directives and multiple files,  Up: Directives
+
+1.12.2 Comment blocks
 ---------------------
 
 A line containing just 'comment' starts a commented region of the file,
@@ -961,7 +979,7 @@
 
 File: hledger_journal.info,  Node: Including other files,  Next: Default year,  Prev: Comment blocks,  Up: Directives
 
-1.12.2 Including other files
+1.12.3 Including other files
 ----------------------------
 
 You can pull in the content of additional files by writing an include
@@ -979,7 +997,7 @@
 
 File: hledger_journal.info,  Node: Default year,  Next: Declaring commodities,  Prev: Including other files,  Up: Directives
 
-1.12.3 Default year
+1.12.4 Default year
 -------------------
 
 You can set a default year to be used for subsequent dates which don't
@@ -1005,7 +1023,7 @@
 
 File: hledger_journal.info,  Node: Declaring commodities,  Next: Default commodity,  Prev: Default year,  Up: Directives
 
-1.12.4 Declaring commodities
+1.12.5 Declaring commodities
 ----------------------------
 
 The 'commodity' directive has several functions:
@@ -1055,7 +1073,7 @@
 
 File: hledger_journal.info,  Node: Default commodity,  Next: Market prices,  Prev: Declaring commodities,  Up: Directives
 
-1.12.5 Default commodity
+1.12.6 Default commodity
 ------------------------
 
 The 'D' directive sets a default commodity, to be used for amounts
@@ -1082,7 +1100,7 @@
 
 File: hledger_journal.info,  Node: Market prices,  Next: Declaring accounts,  Prev: Default commodity,  Up: Directives
 
-1.12.6 Market prices
+1.12.7 Market prices
 --------------------
 
 The 'P' directive declares a market price, which is an exchange rate
@@ -1112,7 +1130,7 @@
 
 File: hledger_journal.info,  Node: Declaring accounts,  Next: Rewriting accounts,  Prev: Market prices,  Up: Directives
 
-1.12.7 Declaring accounts
+1.12.8 Declaring accounts
 -------------------------
 
 'account' directives can be used to pre-declare accounts.  Though not
@@ -1145,7 +1163,7 @@
 
 File: hledger_journal.info,  Node: Account comments,  Next: Account subdirectives,  Up: Declaring accounts
 
-1.12.7.1 Account comments
+1.12.8.1 Account comments
 .........................
 
 Comments, beginning with a semicolon, can be added:
@@ -1165,7 +1183,7 @@
 
 File: hledger_journal.info,  Node: Account subdirectives,  Next: Account types,  Prev: Account comments,  Up: Declaring accounts
 
-1.12.7.2 Account subdirectives
+1.12.8.2 Account subdirectives
 ..............................
 
 We also allow (and ignore) Ledger-style indented subdirectives, just for
@@ -1183,7 +1201,7 @@
 
 File: hledger_journal.info,  Node: Account types,  Next: Account display order,  Prev: Account subdirectives,  Up: Declaring accounts
 
-1.12.7.3 Account types
+1.12.8.3 Account types
 ......................
 
 hledger recognises five types (or classes) of account: Asset, Liability,
@@ -1228,7 +1246,7 @@
 
 File: hledger_journal.info,  Node: Account display order,  Prev: Account types,  Up: Declaring accounts
 
-1.12.7.4 Account display order
+1.12.8.4 Account display order
 ..............................
 
 Account directives also set the order in which accounts are displayed,
@@ -1274,7 +1292,7 @@
 
 File: hledger_journal.info,  Node: Rewriting accounts,  Next: Default parent account,  Prev: Declaring accounts,  Up: Directives
 
-1.12.8 Rewriting accounts
+1.12.9 Rewriting accounts
 -------------------------
 
 You can define account alias rules which rewrite your account names, or
@@ -1298,12 +1316,13 @@
 * Basic aliases::
 * Regex aliases::
 * Combining aliases::
+* Aliases and multiple files::
 * end aliases::
 
 
 File: hledger_journal.info,  Node: Basic aliases,  Next: Regex aliases,  Up: Rewriting accounts
 
-1.12.8.1 Basic aliases
+1.12.9.1 Basic aliases
 ......................
 
 To set an account alias, use the 'alias' directive in your journal file.
@@ -1326,7 +1345,7 @@
 
 File: hledger_journal.info,  Node: Regex aliases,  Next: Combining aliases,  Prev: Basic aliases,  Up: Rewriting accounts
 
-1.12.8.2 Regex aliases
+1.12.9.2 Regex aliases
 ......................
 
 There is also a more powerful variant that uses a regular expression,
@@ -1349,9 +1368,9 @@
 whitespace.
 
 
-File: hledger_journal.info,  Node: Combining aliases,  Next: end aliases,  Prev: Regex aliases,  Up: Rewriting accounts
+File: hledger_journal.info,  Node: Combining aliases,  Next: Aliases and multiple files,  Prev: Regex aliases,  Up: Rewriting accounts
 
-1.12.8.3 Combining aliases
+1.12.9.3 Combining aliases
 ..........................
 
 You can define as many aliases as you like, using journal directives
@@ -1386,9 +1405,41 @@
 which aliases are being applied when.
 
 
-File: hledger_journal.info,  Node: end aliases,  Prev: Combining aliases,  Up: Rewriting accounts
+File: hledger_journal.info,  Node: Aliases and multiple files,  Next: end aliases,  Prev: Combining aliases,  Up: Rewriting accounts
 
-1.12.8.4 'end aliases'
+1.12.9.4 Aliases and multiple files
+...................................
+
+As explained at Directives and multiple files, 'alias' directives do not
+affect parent or sibling files.  Eg in this command,
+
+hledger -f a.aliases -f b.journal
+
+   account aliases defined in a.aliases will not affect b.journal.
+Including the aliases doesn't work either:
+
+include a.aliases
+
+2020-01-01  ; not affected by a.aliases
+  foo  1
+  bar
+
+   This means that account aliases should usually be declared at the
+start of your top-most file, like this:
+
+alias foo=Foo
+alias bar=Bar
+
+2020-01-01  ; affected by aliases above
+  foo  1
+  bar
+
+include c.journal  ; also affected
+
+
+File: hledger_journal.info,  Node: end aliases,  Prev: Aliases and multiple files,  Up: Rewriting accounts
+
+1.12.9.5 'end aliases'
 ......................
 
 You can clear (forget) all currently defined aliases with the 'end
@@ -1399,8 +1450,8 @@
 
 File: hledger_journal.info,  Node: Default parent account,  Prev: Rewriting accounts,  Up: Directives
 
-1.12.9 Default parent account
------------------------------
+1.12.10 Default parent account
+------------------------------
 
 You can specify a parent account which will be prepended to all accounts
 within a section of the journal.  Use the 'apply account' and 'end apply
@@ -1438,7 +1489,7 @@
 parent account.
 
 
-File: hledger_journal.info,  Node: Periodic transactions,  Next: Auto postings / transaction modifiers,  Prev: Directives,  Up: Transactions
+File: hledger_journal.info,  Node: Periodic transactions,  Next: Auto postings,  Prev: Directives,  Up: Transactions
 
 1.13 Periodic transactions
 ==========================
@@ -1600,29 +1651,26 @@
 Forecasting.
 
 
-File: hledger_journal.info,  Node: Auto postings / transaction modifiers,  Prev: Periodic transactions,  Up: Transactions
+File: hledger_journal.info,  Node: Auto postings,  Prev: Periodic transactions,  Up: Transactions
 
-1.14 Auto postings / transaction modifiers
-==========================================
+1.14 Auto postings
+==================
 
-Transaction modifier rules, AKA auto posting rules, describe changes to
-be applied automatically to certain matched transactions.  Currently
-just one kind of change is possible - adding extra postings, which we
-call "automated postings" or just "auto postings".  These rules become
-active when you use the '--auto' flag.
+"Automated postings" or "auto postings" are extra postings which get
+added automatically to transactions which match certain queries, defined
+by "auto posting rules", when you use the '--auto' flag.
 
-   A transaction modifier rule looks much like a normal transaction
-except the first line is an equals sign followed by a query that matches
-certain postings (mnemonic: '=' suggests matching).  And each "posting"
-is actually a posting-generating rule:
+   An auto posting rule looks a bit like a transaction:
 
 = QUERY
     ACCOUNT  AMOUNT
-    ACCOUNT  [AMOUNT]
     ...
+    ACCOUNT  [AMOUNT]
 
-   These posting-generating rules look like normal postings, except the
-amount can be:
+   except the first line is an equals sign (mnemonic: '=' suggests
+matching), followed by a query (which matches existing postings), and
+each "posting" line describes a posting to be generated, and the posting
+amounts can be:
 
    * a normal amount with a commodity symbol, eg '$2'.  This will be
      used as-is.
@@ -1635,17 +1683,13 @@
      and symbol S). The matched posting's amount will be multiplied by
      N, and its commodity symbol will be replaced with S.
 
-   A query term containing spaces must be enclosed in single or double
+   Any query term containing spaces must be enclosed in single or double
 quotes, as on the command line.  Eg, note the quotes around the second
 query term below:
 
 = expenses:groceries 'expenses:dining out'
     (budget:funds:dining out)                 *-1
 
-   These rules have global effect - a rule appearing anywhere in your
-data can potentially affect any transaction, including transactions
-recorded above it or in another file.
-
    Some examples:
 
 ; every time I buy food, schedule a dollar donation
@@ -1679,14 +1723,25 @@
 
 * Menu:
 
+* Auto postings and multiple files::
 * Auto postings and dates::
 * Auto postings and transaction balancing / inferred amounts / balance assertions::
 * Auto posting tags::
 
 
-File: hledger_journal.info,  Node: Auto postings and dates,  Next: Auto postings and transaction balancing / inferred amounts / balance assertions,  Up: Auto postings / transaction modifiers
+File: hledger_journal.info,  Node: Auto postings and multiple files,  Next: Auto postings and dates,  Up: Auto postings
 
-1.14.1 Auto postings and dates
+1.14.1 Auto postings and multiple files
+---------------------------------------
+
+An auto posting rule can affect any transaction in the current file, or
+in any parent file or child file.  Note, currently it will not affect
+sibling files (when multiple '-f'/'--file' are used - see #1212).
+
+
+File: hledger_journal.info,  Node: Auto postings and dates,  Next: Auto postings and transaction balancing / inferred amounts / balance assertions,  Prev: Auto postings and multiple files,  Up: Auto postings
+
+1.14.2 Auto postings and dates
 ------------------------------
 
 A posting date (or secondary date) in the matched posting, or (taking
@@ -1694,13 +1749,12 @@
 used in the generated posting.
 
 
-File: hledger_journal.info,  Node: Auto postings and transaction balancing / inferred amounts / balance assertions,  Next: Auto posting tags,  Prev: Auto postings and dates,  Up: Auto postings / transaction modifiers
+File: hledger_journal.info,  Node: Auto postings and transaction balancing / inferred amounts / balance assertions,  Next: Auto posting tags,  Prev: Auto postings and dates,  Up: Auto postings
 
-1.14.2 Auto postings and transaction balancing / inferred amounts /
+1.14.3 Auto postings and transaction balancing / inferred amounts /
 -------------------------------------------------------------------
 
-balance assertions Currently, transaction modifiers are applied / auto
-postings are added:
+balance assertions Currently, auto postings are added:
 
    * after missing amounts are inferred, and transactions are checked
      for balancedness,
@@ -1711,12 +1765,12 @@
 for background.
 
 
-File: hledger_journal.info,  Node: Auto posting tags,  Prev: Auto postings and transaction balancing / inferred amounts / balance assertions,  Up: Auto postings / transaction modifiers
+File: hledger_journal.info,  Node: Auto posting tags,  Prev: Auto postings and transaction balancing / inferred amounts / balance assertions,  Up: Auto postings
 
-1.14.3 Auto posting tags
+1.14.4 Auto posting tags
 ------------------------
 
-Postings added by transaction modifiers will have some extra tags:
+Automated postings will have some extra tags:
 
    * 'generated-posting:= QUERY' - shows this was generated by an auto
      posting rule, and the query
@@ -1725,8 +1779,8 @@
      "just now", rather than generated in the past and saved to the
      journal.
 
-   Also, any transaction that has been changed by transaction modifier
-rules will have these tags added:
+   Also, any transaction that has been changed by auto posting rules
+will have these tags added:
 
    * 'modified:' - this transaction was modified
    * '_modified:' - a hidden tag not appearing in the comment; this
@@ -1735,116 +1789,122 @@
 
 Tag Table:
 Node: Top76
-Node: Transactions1869
-Ref: #transactions1961
-Node: Dates3150
-Ref: #dates3249
-Node: Simple dates3314
-Ref: #simple-dates3440
-Node: Secondary dates3949
-Ref: #secondary-dates4103
-Node: Posting dates5439
-Ref: #posting-dates5568
-Node: Status6940
-Ref: #status7061
-Node: Description8769
-Ref: #description8903
-Node: Payee and note9223
-Ref: #payee-and-note9337
-Node: Comments9672
-Ref: #comments9798
-Node: Tags10992
-Ref: #tags11107
-Node: Postings12500
-Ref: #postings12628
-Node: Virtual Postings13654
-Ref: #virtual-postings13771
-Node: Account names15076
-Ref: #account-names15217
-Node: Amounts15704
-Ref: #amounts15843
-Node: Digit group marks16775
-Ref: #digit-group-marks16923
-Node: Amount display style17861
-Ref: #amount-display-style18015
-Node: Transaction prices19176
-Ref: #transaction-prices19342
-Node: Balance Assertions21608
-Ref: #balance-assertions21788
-Node: Assertions and ordering22821
-Ref: #assertions-and-ordering23009
-Node: Assertions and included files23709
-Ref: #assertions-and-included-files23952
-Node: Assertions and multiple -f options24285
-Ref: #assertions-and-multiple--f-options24541
-Node: Assertions and commodities24673
-Ref: #assertions-and-commodities24905
-Node: Assertions and prices26062
-Ref: #assertions-and-prices26276
-Node: Assertions and subaccounts26716
-Ref: #assertions-and-subaccounts26945
-Node: Assertions and virtual postings27269
-Ref: #assertions-and-virtual-postings27511
-Node: Assertions and precision27653
-Ref: #assertions-and-precision27846
-Node: Balance Assignments28113
-Ref: #balance-assignments28287
-Node: Balance assignments and prices29451
-Ref: #balance-assignments-and-prices29623
-Node: Directives29847
-Ref: #directives30006
-Node: Comment blocks35654
-Ref: #comment-blocks35799
-Node: Including other files35975
-Ref: #including-other-files36155
-Node: Default year36563
-Ref: #default-year36732
-Node: Declaring commodities37139
-Ref: #declaring-commodities37322
-Node: Default commodity38995
-Ref: #default-commodity39171
-Node: Market prices40060
-Ref: #market-prices40225
-Node: Declaring accounts41066
-Ref: #declaring-accounts41242
-Node: Account comments42167
-Ref: #account-comments42330
-Node: Account subdirectives42754
-Ref: #account-subdirectives42949
-Node: Account types43262
-Ref: #account-types43446
-Node: Account display order45085
-Ref: #account-display-order45255
-Node: Rewriting accounts46406
-Ref: #rewriting-accounts46591
-Node: Basic aliases47317
-Ref: #basic-aliases47463
-Node: Regex aliases48167
-Ref: #regex-aliases48339
-Node: Combining aliases49057
-Ref: #combining-aliases49235
-Node: end aliases50511
-Ref: #end-aliases50659
-Node: Default parent account50760
-Ref: #default-parent-account50926
-Node: Periodic transactions51810
-Ref: #periodic-transactions52009
-Node: Periodic rule syntax53881
-Ref: #periodic-rule-syntax54087
-Node: Two spaces between period expression and description!54791
-Ref: #two-spaces-between-period-expression-and-description55110
-Node: Forecasting with periodic transactions55794
-Ref: #forecasting-with-periodic-transactions56099
-Node: Budgeting with periodic transactions58125
-Ref: #budgeting-with-periodic-transactions58364
-Node: Auto postings / transaction modifiers58813
-Ref: #auto-postings-transaction-modifiers59025
-Node: Auto postings and dates61521
-Ref: #auto-postings-and-dates61778
-Node: Auto postings and transaction balancing / inferred amounts / balance assertions61953
-Ref: #auto-postings-and-transaction-balancing-inferred-amounts-balance-assertions62328
-Node: Auto posting tags62706
-Ref: #auto-posting-tags62945
+Node: Transactions1873
+Ref: #transactions1965
+Node: Dates3130
+Ref: #dates3229
+Node: Simple dates3294
+Ref: #simple-dates3420
+Node: Secondary dates3929
+Ref: #secondary-dates4083
+Node: Posting dates5419
+Ref: #posting-dates5548
+Node: Status6920
+Ref: #status7041
+Node: Description8749
+Ref: #description8883
+Node: Payee and note9203
+Ref: #payee-and-note9317
+Node: Comments9652
+Ref: #comments9778
+Node: Tags10972
+Ref: #tags11087
+Node: Postings12480
+Ref: #postings12608
+Node: Virtual Postings13634
+Ref: #virtual-postings13751
+Node: Account names15056
+Ref: #account-names15197
+Node: Amounts15684
+Ref: #amounts15823
+Node: Digit group marks16737
+Ref: #digit-group-marks16885
+Node: Amount display style17823
+Ref: #amount-display-style17977
+Node: Transaction prices19138
+Ref: #transaction-prices19304
+Node: Balance Assertions21570
+Ref: #balance-assertions21750
+Node: Assertions and ordering22783
+Ref: #assertions-and-ordering22971
+Node: Assertions and included files23671
+Ref: #assertions-and-included-files23914
+Node: Assertions and multiple -f options24247
+Ref: #assertions-and-multiple--f-options24503
+Node: Assertions and commodities24635
+Ref: #assertions-and-commodities24867
+Node: Assertions and prices26024
+Ref: #assertions-and-prices26238
+Node: Assertions and subaccounts26678
+Ref: #assertions-and-subaccounts26907
+Node: Assertions and virtual postings27231
+Ref: #assertions-and-virtual-postings27473
+Node: Assertions and precision27615
+Ref: #assertions-and-precision27808
+Node: Balance Assignments28075
+Ref: #balance-assignments28249
+Node: Balance assignments and prices29413
+Ref: #balance-assignments-and-prices29585
+Node: Directives29809
+Ref: #directives29968
+Node: Directives and multiple files35649
+Ref: #directives-and-multiple-files35832
+Node: Comment blocks36496
+Ref: #comment-blocks36679
+Node: Including other files36855
+Ref: #including-other-files37035
+Node: Default year37443
+Ref: #default-year37612
+Node: Declaring commodities38019
+Ref: #declaring-commodities38202
+Node: Default commodity39875
+Ref: #default-commodity40051
+Node: Market prices40940
+Ref: #market-prices41105
+Node: Declaring accounts41946
+Ref: #declaring-accounts42122
+Node: Account comments43047
+Ref: #account-comments43210
+Node: Account subdirectives43634
+Ref: #account-subdirectives43829
+Node: Account types44142
+Ref: #account-types44326
+Node: Account display order45965
+Ref: #account-display-order46135
+Node: Rewriting accounts47286
+Ref: #rewriting-accounts47471
+Node: Basic aliases48228
+Ref: #basic-aliases48374
+Node: Regex aliases49078
+Ref: #regex-aliases49250
+Node: Combining aliases49968
+Ref: #combining-aliases50161
+Node: Aliases and multiple files51437
+Ref: #aliases-and-multiple-files51646
+Node: end aliases52225
+Ref: #end-aliases52382
+Node: Default parent account52483
+Ref: #default-parent-account52651
+Node: Periodic transactions53535
+Ref: #periodic-transactions53710
+Node: Periodic rule syntax55582
+Ref: #periodic-rule-syntax55788
+Node: Two spaces between period expression and description!56492
+Ref: #two-spaces-between-period-expression-and-description56811
+Node: Forecasting with periodic transactions57495
+Ref: #forecasting-with-periodic-transactions57800
+Node: Budgeting with periodic transactions59826
+Ref: #budgeting-with-periodic-transactions60065
+Node: Auto postings60514
+Ref: #auto-postings60654
+Node: Auto postings and multiple files62833
+Ref: #auto-postings-and-multiple-files63037
+Node: Auto postings and dates63246
+Ref: #auto-postings-and-dates63520
+Node: Auto postings and transaction balancing / inferred amounts / balance assertions63695
+Ref: #auto-postings-and-transaction-balancing-inferred-amounts-balance-assertions64046
+Node: Auto posting tags64388
+Ref: #auto-posting-tags64603
 
 End Tag Table
 
diff --git a/hledger_journal.txt b/hledger_journal.txt
--- a/hledger_journal.txt
+++ b/hledger_journal.txt
@@ -370,7 +370,7 @@
               1E-6
               EUR 1E3
 
-       A decimal mark (decimal point) can be written with a period or a comma:
+       A decimal mark can be written as a period or a comma:
 
               1.23
               1,23456780000009
@@ -709,6 +709,11 @@
                                         dates                          line/included   en-
                                                                        tries until end  of
                                                                        current file
+       =                                declare   an   auto  posting   all entries in par-
+                                        rule,  adding  postings   to   ent/current/child
+                                        other transactions             files (but not sib-
+                                                                       ling   files,   see
+                                                                       #1212)
 
        And some definitions:
 
@@ -718,12 +723,10 @@
        number   how to interpret numbers when parsing journal entries (the iden-
        nota-    tity  of the decimal separator character).  (Currently each com-
        tion     modity can have its own notation, even in the same file.)
+
        dis-     how to display amounts of a commodity in  reports  (symbol  side
        play     and spacing, digit groups, decimal separator, decimal places)
        style
-
-
-
        direc-   which  entries  and  (when there are multiple files) which files
        tive     are affected by a directive
        scope
@@ -732,34 +735,42 @@
        affect, and whether they are focussed on input (parsing) or output (re-
        ports).  Some directives have multiple effects.
 
-       If you have a journal made up of multiple files, or  pass  multiple  -f
-       options  on  the  command line, note that directives which affect input
-       typically last only until the end of their defining  file.   This  pro-
-       vides more simplicity and predictability, eg reports are not changed by
-       writing file options in a different order.  It  can  be  surprising  at
-       times though.
+   Directives and multiple files
+       If you use  multiple  -f/--file  options,  or  the  include  directive,
+       hledger  will  process  multiple input files.  But note that directives
+       which affect input (see above) typically last only until the end of the
+       file in which they occur.
 
+       This may seem inconvenient, but it's intentional; it makes reports sta-
+       ble and deterministic, independent of the order  of  input.   Otherwise
+       you  could see different numbers if you happened to write -f options in
+       a different order, or if you moved includes around  while  cleaning  up
+       your files.
+
+       It  can  be  surprising though; for example, it means that alias direc-
+       tives do not affect parent or sibling files (see below).
+
    Comment blocks
-       A  line  containing just comment starts a commented region of the file,
+       A line containing just comment starts a commented region of  the  file,
        and a line containing just end comment (or the end of the current file)
        ends it.  See also comments.
 
    Including other files
-       You  can  pull in the content of additional files by writing an include
+       You can pull in the content of additional files by writing  an  include
        directive, like this:
 
               include path/to/file.journal
 
-       If the path does not begin with a slash, it is relative to the  current
-       file.   The  include  file  path may contain common glob patterns (e.g.
+       If  the path does not begin with a slash, it is relative to the current
+       file.  The include file path may contain  common  glob  patterns  (e.g.
        *).
 
-       The include directive can only be used in journal files.   It  can  in-
+       The  include  directive  can only be used in journal files.  It can in-
        clude journal, timeclock or timedot files, but not CSV files.
 
    Default year
-       You  can set a default year to be used for subsequent dates which don't
-       specify a year.  This is a line beginning with Y followed by the  year.
+       You can set a default year to be used for subsequent dates which  don't
+       specify  a year.  This is a line beginning with Y followed by the year.
        Eg:
 
               Y2009  ; set default year to 2009
@@ -781,19 +792,19 @@
    Declaring commodities
        The commodity directive has several functions:
 
-       1. It  declares  commodities which may be used in the journal.  This is
+       1. It declares commodities which may be used in the journal.   This  is
           currently not enforced, but can serve as documentation.
 
-       2. It declares what decimal mark character (period or comma) to  expect
-          when  parsing  input  -  useful to disambiguate international number
-          formats in your data.  (Without this, hledger will parse both  1,000
+       2. It  declares what decimal mark character (period or comma) to expect
+          when parsing input - useful  to  disambiguate  international  number
+          formats  in your data.  (Without this, hledger will parse both 1,000
           and 1.000 as 1).
 
-       3. It  declares the amount display style to use in output - decimal and
+       3. It declares the amount display style to use in output - decimal  and
           digit group marks, number of decimal places, symbol placement etc.
 
-       You are likely to run into one of the problems solved by commodity  di-
-       rectives,  sooner or later, so it's a good idea to just always use them
+       You  are likely to run into one of the problems solved by commodity di-
+       rectives, sooner or later, so it's a good idea to just always use  them
        to declare your commodities.
 
        A commodity directive is just the word commodity followed by an amount.
@@ -806,8 +817,8 @@
               ; separating thousands with comma.
               commodity 1,000.0000 AAAA
 
-       or  on  multiple lines, using the "format" subdirective.  (In this case
-       the commodity symbol appears twice and  should  be  the  same  in  both
+       or on multiple lines, using the "format" subdirective.  (In  this  case
+       the  commodity  symbol  appears  twice  and  should be the same in both
        places.):
 
               ; commodity SYMBOL
@@ -820,19 +831,19 @@
                 format INR 1,00,00,000.00
 
        The quantity of the amount does not matter; only the format is signifi-
-       cant.  The number must include a decimal mark: either  a  period  or  a
+       cant.   The  number  must  include a decimal mark: either a period or a
        comma, followed by 0 or more decimal digits.
 
    Default commodity
-       The  D directive sets a default commodity, to be used for amounts with-
+       The D directive sets a default commodity, to be used for amounts  with-
        out a commodity symbol (ie, plain numbers).  This commodity will be ap-
        plied to all subsequent commodity-less amounts, or until the next D di-
        rective.  (Note, this is different from Ledger's D.)
 
-       For compatibility/historical reasons, D also acts like a commodity  di-
+       For  compatibility/historical reasons, D also acts like a commodity di-
        rective, setting the commodity's display style (for output) and decimal
        mark (for parsing input).  As with commodity, the amount must always be
-       written  with a decimal mark (period or comma).  If both directives are
+       written with a decimal mark (period or comma).  If both directives  are
        used, commodity's style takes precedence.
 
        The syntax is D AMOUNT.  Eg:
@@ -846,9 +857,9 @@
                 b
 
    Market prices
-       The P directive declares a market price, which is an exchange rate  be-
-       tween  two  commodities on a certain date.  (In Ledger, they are called
-       "historical prices".) These are often obtained from a  stock  exchange,
+       The  P directive declares a market price, which is an exchange rate be-
+       tween two commodities on a certain date.  (In Ledger, they  are  called
+       "historical  prices".)  These are often obtained from a stock exchange,
        cryptocurrency exchange, or the foreign exchange market.
 
        Here is the format:
@@ -859,16 +870,16 @@
 
        o COMMODITYA is the symbol of the commodity being priced
 
-       o COMMODITYBAMOUNT  is an amount (symbol and quantity) in a second com-
+       o COMMODITYBAMOUNT is an amount (symbol and quantity) in a second  com-
          modity, giving the price in commodity B of one unit of commodity A.
 
-       These two market price directives say that one euro was worth  1.35  US
+       These  two  market price directives say that one euro was worth 1.35 US
        dollars during 2009, and $1.40 from 2010 onward:
 
               P 2009/1/1 EUR $1.35
               P 2010/1/1 EUR $1.40
 
-       The  -V/--value flag can be used to convert reported amounts to another
+       The -V/--value flag can be used to convert reported amounts to  another
        commodity using these prices.
 
    Declaring accounts
@@ -878,20 +889,20 @@
        o They can document your intended chart of accounts, providing a refer-
          ence.
 
-       o They can store extra information  about  accounts  (account  numbers,
+       o They  can  store  extra  information about accounts (account numbers,
          notes, etc.)
 
-       o They  can  help  hledger know your accounts' types (asset, liability,
-         equity, revenue, expense), useful for reports like  balancesheet  and
+       o They can help hledger know your accounts'  types  (asset,  liability,
+         equity,  revenue,  expense), useful for reports like balancesheet and
          incomestatement.
 
-       o They  control  account  display order in reports, allowing non-alpha-
+       o They control account display order in  reports,  allowing  non-alpha-
          betic sorting (eg Revenues to appear above Expenses).
 
-       o They help with account name completion in the add  command,  hledger-
+       o They  help  with account name completion in the add command, hledger-
          iadd, hledger-web, ledger-mode etc.
 
-       The  simplest form is just the word account followed by a hledger-style
+       The simplest form is just the word account followed by a  hledger-style
        account name, eg:
 
               account assets:bank:checking
@@ -899,7 +910,7 @@
    Account comments
        Comments, beginning with a semicolon, can be added:
 
-       o on the same line, after two or more spaces (because ; is  allowed  in
+       o on  the  same line, after two or more spaces (because ; is allowed in
          account names)
 
        o on the next lines, indented
@@ -913,7 +924,7 @@
        Same-line comments are not supported by Ledger, or hledger <1.13.
 
    Account subdirectives
-       We  also  allow  (and ignore) Ledger-style indented subdirectives, just
+       We also allow (and ignore) Ledger-style  indented  subdirectives,  just
        for compatibility.:
 
               account assets:bank:checking
@@ -926,18 +937,18 @@
                 [LEDGER-STYLE SUBDIRECTIVES, IGNORED]
 
    Account types
-       hledger recognises five types (or classes) of account:  Asset,  Liabil-
-       ity,  Equity, Revenue, Expense.  This is used by a few accounting-aware
+       hledger  recognises  five types (or classes) of account: Asset, Liabil-
+       ity, Equity, Revenue, Expense.  This is used by a few  accounting-aware
        reports such as balancesheet, incomestatement and cashflow.
 
    Auto-detected account types
        If you name your top-level accounts with some variation of assets, lia-
-       bilities/debts,  equity,  revenues/income, or expenses, their types are
+       bilities/debts, equity, revenues/income, or expenses, their  types  are
        detected automatically.
 
    Account types declared with tags
-       More generally, you can declare an account's type with an  account  di-
-       rective,  by  writing  a type: tag in a comment, followed by one of the
+       More  generally,  you can declare an account's type with an account di-
+       rective, by writing a type: tag in a comment, followed by  one  of  the
        words Asset, Liability, Equity, Revenue, Expense, or one of the letters
        ALERX (case insensitive):
 
@@ -948,8 +959,8 @@
               account expenses     ; type:Expense
 
    Account types declared with account type codes
-       Or,  you can write one of those letters separated from the account name
-       by two or more spaces, but this should probably  be  considered  depre-
+       Or, you can write one of those letters separated from the account  name
+       by  two  or  more spaces, but this should probably be considered depre-
        cated as of hledger 1.13:
 
               account assets       A
@@ -959,7 +970,7 @@
               account expenses     X
 
    Overriding auto-detected types
-       If  you  ever override the types of those auto-detected english account
+       If you ever override the types of those auto-detected  english  account
        names mentioned above, you might need to help the reports a bit.  Eg:
 
               ; make "liabilities" not have the liability type - who knows why
@@ -970,8 +981,8 @@
               account -            ; type:L
 
    Account display order
-       Account directives also set the order in which accounts are  displayed,
-       eg  in  reports,  the  hledger-ui  accounts screen, and the hledger-web
+       Account  directives also set the order in which accounts are displayed,
+       eg in reports, the hledger-ui  accounts  screen,  and  the  hledger-web
        sidebar.  By default accounts are listed in alphabetical order.  But if
        you have these account directives in the journal:
 
@@ -993,20 +1004,20 @@
 
        Undeclared accounts, if any, are displayed last, in alphabetical order.
 
-       Note that sorting is done at each level of  the  account  tree  (within
-       each  group of sibling accounts under the same parent).  And currently,
+       Note  that  sorting  is  done at each level of the account tree (within
+       each group of sibling accounts under the same parent).  And  currently,
        this directive:
 
               account other:zoo
 
-       would influence the position of zoo among other's subaccounts, but  not
+       would  influence the position of zoo among other's subaccounts, but not
        the position of other among the top-level accounts.  This means:
 
-       o you  will  sometimes declare parent accounts (eg account other above)
+       o you will sometimes declare parent accounts (eg account  other  above)
          that you don't intend to post to, just to customize their display or-
          der
 
-       o sibling  accounts  stay together (you couldn't display x:y in between
+       o sibling accounts stay together (you couldn't display x:y  in  between
          a:b and a:c).
 
    Rewriting accounts
@@ -1024,14 +1035,14 @@
        o customising reports
 
        Account aliases also rewrite account names in account directives.  They
-       do not affect account names being entered via hledger add  or  hledger-
+       do  not  affect account names being entered via hledger add or hledger-
        web.
 
        See also Rewrite account names.
 
    Basic aliases
-       To  set an account alias, use the alias directive in your journal file.
-       This affects all subsequent journal entries in the current file or  its
+       To set an account alias, use the alias directive in your journal  file.
+       This  affects all subsequent journal entries in the current file or its
        included files.  The spaces around the = are optional:
 
               alias OLD = NEW
@@ -1039,49 +1050,49 @@
        Or, you can use the --alias 'OLD=NEW' option on the command line.  This
        affects all entries.  It's useful for trying out aliases interactively.
 
-       OLD and NEW are case sensitive full account names.   hledger  will  re-
-       place  any occurrence of the old account name with the new one.  Subac-
+       OLD  and  NEW  are case sensitive full account names.  hledger will re-
+       place any occurrence of the old account name with the new one.   Subac-
        counts are also affected.  Eg:
 
               alias checking = assets:bank:wells fargo:checking
               ; rewrites "checking" to "assets:bank:wells fargo:checking", or "checking:a" to "assets:bank:wells fargo:checking:a"
 
    Regex aliases
-       There is also a more powerful variant that uses a  regular  expression,
+       There  is  also a more powerful variant that uses a regular expression,
        indicated by the forward slashes:
 
               alias /REGEX/ = REPLACEMENT
 
        or --alias '/REGEX/=REPLACEMENT'.
 
-       REGEX  is  a  case-insensitive regular expression.  Anywhere it matches
-       inside an account name, the matched part will be replaced  by  REPLACE-
-       MENT.   If REGEX contains parenthesised match groups, these can be ref-
+       REGEX is a case-insensitive regular expression.   Anywhere  it  matches
+       inside  an  account name, the matched part will be replaced by REPLACE-
+       MENT.  If REGEX contains parenthesised match groups, these can be  ref-
        erenced by the usual numeric backreferences in REPLACEMENT.  Eg:
 
               alias /^(.+):bank:([^:]+)(.*)/ = \1:\2 \3
               ; rewrites "assets:bank:wells fargo:checking" to  "assets:wells fargo checking"
 
-       Also note that REPLACEMENT continues to the end of line (or on  command
-       line,  to  end  of  option argument), so it can contain trailing white-
+       Also  note that REPLACEMENT continues to the end of line (or on command
+       line, to end of option argument), so it  can  contain  trailing  white-
        space.
 
    Combining aliases
-       You can define as many aliases as you like,  using  journal  directives
+       You  can  define  as many aliases as you like, using journal directives
        and/or command line options.
 
-       Recursive  aliases  -  where an account name is rewritten by one alias,
-       then by another alias, and so on - are allowed.  Each  alias  sees  the
+       Recursive aliases - where an account name is rewritten  by  one  alias,
+       then  by  another  alias, and so on - are allowed.  Each alias sees the
        effect of previously applied aliases.
 
-       In  such  cases it can be important to understand which aliases will be
-       applied and in which order.  For (each account name  in)  each  journal
+       In such cases it can be important to understand which aliases  will  be
+       applied  and  in  which order.  For (each account name in) each journal
        entry, we apply:
 
-       1. alias  directives  preceding the journal entry, most recently parsed
+       1. alias directives preceding the journal entry, most  recently  parsed
           first (ie, reading upward from the journal entry, bottom to top)
 
-       2. --alias options, in the order they  appeared  on  the  command  line
+       2. --alias  options,  in  the  order  they appeared on the command line
           (left to right).
 
        In other words, for (an account name in) a given journal entry:
@@ -1092,13 +1103,40 @@
 
        o aliases defined after/below the entry do not affect it.
 
-       This  gives nearby aliases precedence over distant ones, and helps pro-
-       vide semantic stability - aliases will keep working the same way  inde-
+       This gives nearby aliases precedence over distant ones, and helps  pro-
+       vide  semantic stability - aliases will keep working the same way inde-
        pendent of which files are being read and in which order.
 
-       In  case  of  trouble,  adding  --debug=6 to the command line will show
+       In case of trouble, adding --debug=6 to  the  command  line  will  show
        which aliases are being applied when.
 
+   Aliases and multiple files
+       As  explained at Directives and multiple files, alias directives do not
+       affect parent or sibling files.  Eg in this command,
+
+              hledger -f a.aliases -f b.journal
+
+       account aliases defined in a.aliases will not  affect  b.journal.   In-
+       cluding the aliases doesn't work either:
+
+              include a.aliases
+
+              2020-01-01  ; not affected by a.aliases
+                foo  1
+                bar
+
+       This means that account aliases should usually be declared at the start
+       of your top-most file, like this:
+
+              alias foo=Foo
+              alias bar=Bar
+
+              2020-01-01  ; affected by aliases above
+                foo  1
+                bar
+
+              include c.journal  ; also affected
+
    end aliases
        You can clear (forget) all  currently  defined  aliases  with  the  end
        aliases directive:
@@ -1278,51 +1316,44 @@
        For more details, see: balance: Budget report and Budgeting  and  Fore-
        casting.
 
-   Auto postings / transaction modifiers
-       Transaction modifier rules, AKA auto posting rules, describe changes to
-       be applied automatically to certain  matched  transactions.   Currently
-       just  one  kind of change is possible - adding extra postings, which we
-       call "automated postings" or just "auto postings".  These rules  become
-       active when you use the --auto flag.
+   Auto postings
+       "Automated  postings"  or  "auto postings" are extra postings which get
+       added automatically to transactions which match  certain  queries,  de-
+       fined by "auto posting rules", when you use the --auto flag.
 
-       A transaction modifier rule looks much like a normal transaction except
-       the first line is an equals sign followed by a query that matches  cer-
-       tain  postings  (mnemonic: = suggests matching).  And each "posting" is
-       actually a posting-generating rule:
+       An auto posting rule looks a bit like a transaction:
 
               = QUERY
                   ACCOUNT  AMOUNT
-                  ACCOUNT  [AMOUNT]
                   ...
+                  ACCOUNT  [AMOUNT]
 
-       These posting-generating rules look like normal  postings,  except  the
-       amount can be:
+       except  the  first  line is an equals sign (mnemonic: = suggests match-
+       ing), followed by a query (which matches existing postings),  and  each
+       "posting"  line  describes  a  posting to be generated, and the posting
+       amounts can be:
 
-       o a  normal  amount  with a commodity symbol, eg $2.  This will be used
+       o a normal amount with a commodity symbol, eg $2.  This  will  be  used
          as-is.
 
        o a number, eg 2.  The commodity symbol (if any) from the matched post-
          ing will be added to this.
 
-       o a  numeric  multiplier,  eg  *2 (a star followed by a number N).  The
+       o a numeric multiplier, eg *2 (a star followed by  a  number  N).   The
          matched posting's amount (and total price, if any) will be multiplied
          by N.
 
-       o a  multiplier  with a commodity symbol, eg *$2 (a star, number N, and
+       o a multiplier with a commodity symbol, eg *$2 (a star, number  N,  and
          symbol S).  The matched posting's amount will be multiplied by N, and
          its commodity symbol will be replaced with S.
 
-       A  query  term  containing  spaces must be enclosed in single or double
-       quotes, as on the command line.  Eg, note the quotes around the  second
+       Any query term containing spaces must be enclosed in single  or  double
+       quotes,  as on the command line.  Eg, note the quotes around the second
        query term below:
 
               = expenses:groceries 'expenses:dining out'
                   (budget:funds:dining out)                 *-1
 
-       These rules have global effect - a rule appearing anywhere in your data
-       can potentially affect any transaction, including transactions recorded
-       above it or in another file.
-
        Some examples:
 
               ; every time I buy food, schedule a dollar donation
@@ -1354,36 +1385,41 @@
                   assets:checking:gifts     -$20
                   assets:checking            $20
 
+   Auto postings and multiple files
+       An auto posting rule can affect any transaction in the current file, or
+       in  any  parent file or child file.  Note, currently it will not affect
+       sibling files (when multiple -f/--file are used - see #1212).
+
    Auto postings and dates
-       A  posting  date (or secondary date) in the matched posting, or (taking
-       precedence) a posting date in the auto posting rule itself,  will  also
+       A posting date (or secondary date) in the matched posting,  or  (taking
+       precedence)  a  posting date in the auto posting rule itself, will also
        be used in the generated posting.
 
    Auto postings and transaction balancing / inferred amounts / balance asser-
        tions
-       Currently, transaction modifiers are applied / auto postings are added:
+       Currently, auto postings are added:
 
-       o after missing amounts are inferred, and transactions are checked  for
+       o after  missing amounts are inferred, and transactions are checked for
          balancedness,
 
        o but before balance assertions are checked.
 
-       Note  this  means that journal entries must be balanced both before and
+       Note this means that journal entries must be balanced both  before  and
        after auto postings are added.  This changed in hledger 1.12+; see #893
        for background.
 
    Auto posting tags
-       Postings added by transaction modifiers will have some extra tags:
+       Automated postings will have some extra tags:
 
        o generated-posting:= QUERY - shows this was generated by an auto post-
          ing rule, and the query
 
-       o _generated-posting:= QUERY - a hidden tag, which does not  appear  in
+       o _generated-posting:=  QUERY  - a hidden tag, which does not appear in
          hledger's output.  This can be used to match postings generated "just
          now", rather than generated in the past and saved to the journal.
 
-       Also, any transaction that has been  changed  by  transaction  modifier
-       rules will have these tags added:
+       Also,  any transaction that has been changed by auto posting rules will
+       have these tags added:
 
        o modified: - this transaction was modified
 
@@ -1393,7 +1429,7 @@
 
 
 REPORTING BUGS
-       Report bugs at http://bugs.hledger.org (or on the #hledger IRC  channel
+       Report  bugs at http://bugs.hledger.org (or on the #hledger IRC channel
        or hledger mail list)
 
 
@@ -1407,7 +1443,7 @@
 
 
 SEE ALSO
-       hledger(1),      hledger-ui(1),     hledger-web(1),     hledger-api(1),
+       hledger(1),     hledger-ui(1),     hledger-web(1),      hledger-api(1),
        hledger_csv(5), hledger_journal(5), hledger_timeclock(5), hledger_time-
        dot(5), ledger(1)
 
@@ -1415,4 +1451,4 @@
 
 
 
-hledger 1.17                      March 2020                hledger_journal(5)
+hledger 1.17.1                    March 2020                hledger_journal(5)
diff --git a/hledger_timeclock.5 b/hledger_timeclock.5
--- a/hledger_timeclock.5
+++ b/hledger_timeclock.5
@@ -1,5 +1,5 @@
 
-.TH "hledger_timeclock" "5" "March 2020" "hledger 1.17" "hledger User Manuals"
+.TH "hledger_timeclock" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
 
 
 
diff --git a/hledger_timeclock.info b/hledger_timeclock.info
--- a/hledger_timeclock.info
+++ b/hledger_timeclock.info
@@ -4,8 +4,8 @@
 
 File: hledger_timeclock.info,  Node: Top,  Up: (dir)
 
-hledger_timeclock(5) hledger 1.17
-*********************************
+hledger_timeclock(5) hledger 1.17.1
+***********************************
 
 Timeclock - the time logging format of timeclock.el, as read by hledger
 
diff --git a/hledger_timeclock.txt b/hledger_timeclock.txt
--- a/hledger_timeclock.txt
+++ b/hledger_timeclock.txt
@@ -78,4 +78,4 @@
 
 
 
-hledger 1.17                      March 2020              hledger_timeclock(5)
+hledger 1.17.1                    March 2020              hledger_timeclock(5)
diff --git a/hledger_timedot.5 b/hledger_timedot.5
--- a/hledger_timedot.5
+++ b/hledger_timedot.5
@@ -1,5 +1,5 @@
 
-.TH "hledger_timedot" "5" "March 2020" "hledger 1.17" "hledger User Manuals"
+.TH "hledger_timedot" "5" "March 2020" "hledger 1.17.1" "hledger User Manuals"
 
 
 
diff --git a/hledger_timedot.info b/hledger_timedot.info
--- a/hledger_timedot.info
+++ b/hledger_timedot.info
@@ -4,8 +4,8 @@
 
 File: hledger_timedot.info,  Node: Top,  Up: (dir)
 
-hledger_timedot(5) hledger 1.17
-*******************************
+hledger_timedot(5) hledger 1.17.1
+*********************************
 
 Timedot - hledger's human-friendly time logging format
 
diff --git a/hledger_timedot.txt b/hledger_timedot.txt
--- a/hledger_timedot.txt
+++ b/hledger_timedot.txt
@@ -161,4 +161,4 @@
 
 
 
-hledger 1.17                      March 2020                hledger_timedot(5)
+hledger 1.17.1                    March 2020                hledger_timedot(5)
diff --git a/test/doctests.hs b/test/doctests.hs
--- a/test/doctests.hs
+++ b/test/doctests.hs
@@ -27,6 +27,7 @@
 import "Glob" System.FilePath.Glob
 import Test.DocTest
 
+main :: IO ()
 main = do
   args <- getArgs
   let
diff --git a/test/unittest.hs b/test/unittest.hs
--- a/test/unittest.hs
+++ b/test/unittest.hs
@@ -5,7 +5,11 @@
 -- package-qualified import to avoid cabal missing-home-modules warning (and double-building ?)
 {-# LANGUAGE PackageImports #-}
 import "hledger-lib" Hledger (tests_Hledger)
-
+import System.Environment (setEnv)
 import Test.Tasty (defaultMain)
 
-main = defaultMain tests_Hledger
+main :: IO ()
+main = do
+  setEnv "TASTY_HIDE_SUCCESSES" "true"
+  setEnv "TASTY_ANSI_TRICKS" "false"  -- helps the above
+  defaultMain tests_Hledger
