diff --git a/NEWS b/NEWS
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,15 @@
+Next
+----
+
+- Fixed hcat, vcat, stringPlane, stringPlane documentation to match
+  behaviour (i.e. they do not error on empty strong/list).
+- Now `subPlane` too does not throw an exception when called with
+  inconsistent coordinates, but returns a transparent 1×1 plane.
+- Introduced `MalformedGRec` exception for when `readRecord` fails.
+- Provided instructions for hot-reloading a game running in normal
+  (interactive) mode with various means (tmux, urxvt, etc.).
+  Check `example/MainHotReload.hs`.
+
 1.8.0.0
 -------
 
diff --git a/ansi-terminal-game.cabal b/ansi-terminal-game.cabal
--- a/ansi-terminal-game.cabal
+++ b/ansi-terminal-game.cabal
@@ -1,5 +1,5 @@
 name:                ansi-terminal-game
-version:             1.8.0.0
+version:             1.8.0.1
 synopsis:            sdl-like functions for terminal applications, based on
                      ansi-terminal
 description:         Library which aims to replicate standard 2d game
@@ -30,8 +30,8 @@
   default:           False
 
 source-repository head
-    type:     darcs
-    location: http://www.ariis.it/link/repos/ansi-terminal-game/
+    type:     git
+    location: https://gitlab.com/fffaaa/ansi-terminal-game
 
 library
   exposed-modules:     Terminal.Game
@@ -61,7 +61,7 @@
                        exceptions == 0.10.*,
                        linebreak == 1.1.*,
                        mintty == 0.1.*,
-                       mtl == 2.2.*,
+                       mtl >=2.2 && <2.4,
                        QuickCheck >= 2.13 && < 2.15,
                        random >= 1.2 && < 1.3,
                        split == 0.2.*,
@@ -98,6 +98,7 @@
                        Terminal.Game.Layer.Object.Primitive,
                        Terminal.Game.Layer.Object.Record,
                        Terminal.Game.Layer.Object.Test,
+                       Terminal.Game.Layer.Object.TestSpec,
                        Terminal.Game.Utils,
                        Terminal.Game.Plane,
                        Terminal.Game.PlaneSpec
@@ -113,7 +114,7 @@
                        exceptions == 0.10.*,
                        linebreak == 1.1.*,
                        mintty == 0.1.*,
-                       mtl == 2.2.*,
+                       mtl >=2.2 && <2.4,
                        QuickCheck >= 2.13 && < 2.15,
                        random >= 1.2 && < 1.3,
                        split == 0.2.*,
@@ -122,6 +123,7 @@
                        timers-tick > 0.5 && < 0.6
                        -- the above plus hspec
                        , hspec
+  build-tool-depends:  hspec-discover:hspec-discover
   type:                exitcode-stdio-1.0
   ghc-options:         -Wall
 
diff --git a/example/Alone.hs b/example/Alone.hs
--- a/example/Alone.hs
+++ b/example/Alone.hs
@@ -77,7 +77,7 @@
 drawFun :: MyState -> Plane
 drawFun (MyState (r, c) _ _) =
                            blankPlane mw     mh            &
-                (1, 1)   % box mw mh '-'                   &
+                (1, 1)   % box mw mh '.'                   &
                 (2, 2)   % box (mw-2) (mh-2) ' '           &
                 (15, 20) % textBox 10 4
                                    "Tap WASD to move, tap again to stop." &
diff --git a/example/MainHotReload.hs b/example/MainHotReload.hs
--- a/example/MainHotReload.hs
+++ b/example/MainHotReload.hs
@@ -5,20 +5,76 @@
 import Terminal.Game
 
 -- Hot reloading is a handy feature while writing a game. Here I will
--- show you how to do that with ansi-terminal-game.
---
--- 1. install `entr` from your repositories;
--- 2. run `find example/*.hs | entr -cr cabal run -f examples hot-reload`;
--- 3. now modify example/Alone.hs and see your changes live!
---
--- Caveat: entr and similar applications do *not* work with interactive
--- programs, so you need — as shown below — to load a record and play
--- it as a demo.
--- This is still useful to iteratively build NPCs’ behaviour, GUIs, etc.
+-- show you various ways to do that with ansi-terminal-game.
 --
--- Remember that you can use `recordGame` to record a session. If you
--- need something fancier for your game (e.g. hot-reload with input),
--- `venzone` [1] (module Watcher) has a builtin /watch mode/ you can
+-- Hot reloading makes use of `entr` (install it from your repos) and
+-- some additional scaffolding, provided by `tmux` or your plain terminal.
+-- Read below to see two ideas in action.
+
+
+{- === HOT RELOAD WITH ENTR AND TMUX ===
+
+	1. Install `entr` and `tmux`.
+
+    2. open a tmux window, in the bottom-right plane launch the game
+       in an infinite loop, e.g.
+
+         while true; do cabal run -f examples alone; done
+
+       Remember, the pane has to be the bottom-right one, like this:
+
+            +----------------------------------------------+
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            |                        |---------------------|
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            |                        |       G A M E       |
+            |                        |                     |
+            |                        |                     |
+            |                        |                     |
+            +----------------------------------------------+
+
+     3. in another pane launch `entr` in this fashion:
+
+          find src example/ -name *.hs | entr tmux send-keys -t {bottom-right} q
+
+     4. Now whenever you modify a source file in example/ , the game
+        will be reloaded!  -}
+
+
+{- === HOT RELOAD WITH ENTR AND PLAIN TERMINAL ===
+
+    1. If your terminal has a `command` option, it is even easier.
+       We will use `urxvt` for this example
+
+          find src example/ -name *.hs | entr -r urxvt -e cabal run -f examples alone
+
+    2. Every time you save a source file, a terminal will be spawn with
+       your game running in it.  -}
+
+
+{-  === HOT RELOAD REPLAYS WITH ENTR ===
+
+    `entr` by itself *cannot* autoreload a game in the same window, as it
+    cannot handle interactive programs. But if you are just displaying a
+    replay, this can come handy
+
+        find example/*.hs | entr -cr cabal run -f examples hot reload
+
+    This is very useful to incrementally build NPCs’ behaviour,
+    iron out mechanics bugs etc.
+
+    Remember that you can use `recordGame` to record a session.  -}
+
+
+-- If you you need something fancier for your game (e.g. hot reloading user
+-- maps), `venzone` [1] (module Watcher) has a builtin /watch mode/ you can
 -- take inspiration from.
 --
 -- [1] https://hackage.haskell.org/package/venzone
diff --git a/platform-dep/windows/Terminal/Game/Utils.hs b/platform-dep/windows/Terminal/Game/Utils.hs
--- a/platform-dep/windows/Terminal/Game/Utils.hs
+++ b/platform-dep/windows/Terminal/Game/Utils.hs
@@ -24,6 +24,7 @@
 foreign import ccall safe "conio.h getch"
   c_getch :: IO FT.CInt
 
+-- TODO elimina isWin32Console [win]
 -- not perfect, but it is what it is (on win, non minTTY)
 isWin32Console :: IO Bool
 isWin32Console = not <$> M.isMinTTY
diff --git a/src/Terminal/Game.hs b/src/Terminal/Game.hs
--- a/src/Terminal/Game.hs
+++ b/src/Terminal/Game.hs
@@ -17,6 +17,12 @@
 -- Basic col-on-black ASCII terminal, operations.
 -- Only module to be imported.
 
+-- todo you can eliminate unidecode!
+-- todo you probably can eliminate part of platform dep too (ffi)
+--      (anche check what is linked -- see lib.so.8 error)
+-- todo you should check again on win the «press exit to continue» test
+-- todo write that ascii/latin-1/greek letters should be ok on win
+
 module Terminal.Game ( -- * Running
                        TPS,
                        FPS,
@@ -175,7 +181,8 @@
 --   that go well with either (blue, red, etc.);
 --
 -- * some terminals/multiplexers (i.e. tmux) do not make a distinction
---   between vivid/dull; do not base your game mechanics on that
+--   between vivid/dull, others do not display bold; do not base your game
+--   mechanics on that
 --   difference.
 
 -- | /Usable/ terminal display size (on Win32 console the last line is
diff --git a/src/Terminal/Game/Draw.hs b/src/Terminal/Game/Draw.hs
--- a/src/Terminal/Game/Draw.hs
+++ b/src/Terminal/Game/Draw.hs
@@ -97,14 +97,14 @@
                 (r, c) % b
 
 
--- | Place a list of 'Plane's side-by-side, horizontally. @error@s on
--- empty list.
+-- | Place a list of 'Plane's side-by-side, horizontally. Returns a 1×1
+-- transparent plane on empty list.
 hcat :: [Plane] -> Plane
 hcat [] = blankPlane 1 1 # makeTransparent ' '
 hcat ps = L.foldl1' (|||) ps
 
--- | Place a list of 'Plane's side-by-side, vertically. @error@s on
--- empty list.
+-- | Place a list of 'Plane's side-by-side, vertically. Returns a 1×1
+-- transparent plane on empty list.
 vcat :: [Plane] -> Plane
 vcat [] = blankPlane 1 1 # makeTransparent ' '
 vcat ps = L.foldl1' (===) ps
diff --git a/src/Terminal/Game/Layer/Imperative.hs b/src/Terminal/Game/Layer/Imperative.hs
--- a/src/Terminal/Game/Layer/Imperative.hs
+++ b/src/Terminal/Game/Layer/Imperative.hs
@@ -76,7 +76,14 @@
 -- | Tests a game in a /pure/ environment. Aims to accurately emulate 'GEnv'
 -- changes (screen size, FPS) too.
 testGame :: Game s -> GRec -> s
-testGame g ts = fst $ runTest (runGameGeneral g) ts
+testGame g ts =
+        case runTest (runGameGeneral g) ts of
+            (Nothing, l) -> error $ "testGame, exception called: " ++
+                                    show l
+                -- it is fine to use error here since in the end
+                -- hspec can deal with it gracefully and we give
+                -- more infos on a failed test
+            (Just s, _) -> s
 
 -- | As 'testGame', but returns 'Game' instead of a bare state.
 -- Useful to fast-forward (e.g.: skip menus) before invoking 'playGame'.
diff --git a/src/Terminal/Game/Layer/Object/IO.hs b/src/Terminal/Game/Layer/Object/IO.hs
--- a/src/Terminal/Game/Layer/Object/IO.hs
+++ b/src/Terminal/Game/Layer/Object/IO.hs
@@ -145,6 +145,9 @@
             -- inefficient as it gets (attempts to scroll past
             -- bottom right)
         isWin32Console >>= \bw ->
+            -- cmd.exe is present on Win10 `C:\Windows\system32\cmd.exe`
+            -- — and default — too. So this is needed for the foreseeable
+            -- future.
 
         return (fmap (f bw) ts)
     where
diff --git a/src/Terminal/Game/Layer/Object/Interface.hs b/src/Terminal/Game/Layer/Object/Interface.hs
--- a/src/Terminal/Game/Layer/Object/Interface.hs
+++ b/src/Terminal/Game/Layer/Object/Interface.hs
@@ -13,7 +13,6 @@
 import Terminal.Game.Layer.Object.Primitive
 
 import qualified Control.Concurrent as CC
-import qualified Control.Monad.Catch as MC
 
 -------------------------------------------------------------------------------
 -- mtl interface for game
@@ -56,34 +55,3 @@
 displaySizeErr = displaySize >>= \case
                    Nothing -> throwExc CannotGetDisplaySize
                    Just d -> return d
-
--------------------------------------------------------------------------------
--- Error handling
-
--- | @ATGException@s are thrown synchronously for easier catching.
-data ATGException = CannotGetDisplaySize
-                  | DisplayTooSmall Dimensions Dimensions
-                        -- ^ Required and actual dimensions.
-
-instance Show ATGException where
-    show CannotGetDisplaySize = "CannotGetDisplaySize"
-    show (DisplayTooSmall (sw, sh) tds) =
-      let colS ww = ww < sw
-          rowS wh = wh < sh
-
-          smallMsg :: Dimensions -> String
-          smallMsg (ww, wh) =
-                let cm = show ww ++ " columns"
-                    rm = show wh ++ " rows"
-                    em | colS ww && rowS wh = cm ++ " and " ++ rm
-                       | colS ww = cm
-                       | rowS wh = rm
-                       | otherwise = "smallMsg: passed correct term size!"
-                in
-                  "This games requires a display of " ++ show sw ++
-                  " columns and " ++ show sh ++ " rows.\n" ++
-                  "Yours only has " ++ em ++ "!\n\n" ++
-                  "Please resize your terminal and restart the game.\n"
-      in "DisplayTooSmall.\n" ++ smallMsg tds
-
-instance MC.Exception ATGException where
diff --git a/src/Terminal/Game/Layer/Object/Primitive.hs b/src/Terminal/Game/Layer/Object/Primitive.hs
--- a/src/Terminal/Game/Layer/Object/Primitive.hs
+++ b/src/Terminal/Game/Layer/Object/Primitive.hs
@@ -5,6 +5,7 @@
 
 import Terminal.Game.Plane
 
+import qualified Control.Monad.Catch as MC
 import qualified GHC.Generics as G
 import qualified Data.ByteString as BS
 import qualified Data.Serialize as Z
@@ -29,6 +30,9 @@
 -- | An @Event@ is a 'Tick' (time passes) or a 'KeyPress'.
 data Event = Tick
            | KeyPress Char
+              -- ↑↓→← do not work on Windows (are handled by the app,
+              -- not passed to the program) both on cmd.exe and
+              -- PowerShell.
            deriving (Show, Eq, G.Generic)
 instance Z.Serialize Event where
 
@@ -78,11 +82,11 @@
 isOver (GRec S.Empty _) = True
 isOver _ = False
 
--- | Reads a file containing a recorded session.
+-- | Reads a file containing a recorded session. Throws
+-- 'MalformedGRec' on failure.
 readRecord :: FilePath -> IO GRec
 readRecord fp = Z.decode <$> BS.readFile fp >>= \case
-                  Left e  -> error $ "readRecord could not decode: " ++
-                                     show e
+                  Left e  -> MC.throwM (MalformedGRec e)
                   Right r -> return r
 
 -- | Convenience function to create a 'GRec' from screen size (constant) plus a list of events. Useful with 'setupGame'.
@@ -91,3 +95,36 @@
                    GRec (S.fromList [es])
                         (S.fromList . replicate l $ Just ds)
 
+-------------------------------------------------------------------------------
+-- Exceptions
+
+-- | @ATGException@s are thrown synchronously for easier catching.
+data ATGException = CannotGetDisplaySize
+                  | DisplayTooSmall Dimensions Dimensions
+                        -- ^ Required and actual dimensions.
+                  | MalformedGRec String
+        deriving (Eq)
+
+instance Show ATGException where
+    show CannotGetDisplaySize = "CannotGetDisplaySize"
+    show (DisplayTooSmall (sw, sh) tds) =
+      let colS ww = ww < sw
+          rowS wh = wh < sh
+
+          smallMsg :: Dimensions -> String
+          smallMsg (ww, wh) =
+                let cm = show ww ++ " columns"
+                    rm = show wh ++ " rows"
+                    em | colS ww && rowS wh = cm ++ " and " ++ rm
+                       | colS ww = cm
+                       | rowS wh = rm
+                       | otherwise = "smallMsg: passed correct term size!"
+                in
+                  "This games requires a display of " ++ show sw ++
+                  " columns and " ++ show sh ++ " rows.\n" ++
+                  "Yours only has " ++ em ++ "!\n\n" ++
+                  "Please resize your terminal and restart the game.\n"
+      in "DisplayTooSmall.\n" ++ smallMsg tds
+    show (MalformedGRec e) = "MalformedGRec: " ++ e
+
+instance MC.Exception ATGException where
diff --git a/src/Terminal/Game/Layer/Object/Test.hs b/src/Terminal/Game/Layer/Object/Test.hs
--- a/src/Terminal/Game/Layer/Object/Test.hs
+++ b/src/Terminal/Game/Layer/Object/Test.hs
@@ -13,7 +13,9 @@
 import Terminal.Game.Layer.Object.Interface
 import Terminal.Game.Layer.Object.Primitive
 
+import qualified Control.Monad.Except as E
 import qualified Control.Monad.RWS as S
+import qualified Data.Bifunctor as B
 
 
 -----------
@@ -21,6 +23,7 @@
 -----------
 
 data TestEvent = TCleanUpError
+               | TException ATGException
                | TQuitGame
                | TSetupDisplay
                | TShutdownDisplay
@@ -29,24 +32,22 @@
                | TStopEvents
         deriving (Eq, Show)
 
+-- e: ()
 -- r: ()
 -- w: [TestEvents]
 -- s: [GTest]
-newtype Test a = Test (S.RWS () [TestEvent] GRec a)
+newtype Test a = Test (E.ExceptT () (S.RWS () [TestEvent] GRec) a)
                deriving (Functor, Applicative, Monad,
-                         S.MonadState GRec,
+                         E.MonadError (), S.MonadState GRec,
                          S.MonadWriter [TestEvent])
 
-runTest :: Test a -> GRec -> (a, [TestEvent])
-runTest (Test m) es = S.evalRWS m () es
-
-
------------
--- CLASS --
------------
+runTest :: Test a -> GRec -> (Maybe a, [TestEvent])
+runTest (Test em) es = let m = E.runExceptT em
+                           t = S.evalRWS m () es in
+                       B.first (either (const Nothing) Just) t
 
-tconst :: a -> Test a
-tconst a = Test $ return a
+-------------------------------------------------------------------------------
+-- Class
 
 mockHandle :: InputHandle
 mockHandle = InputHandle (error "mock handle keyMvar")
@@ -64,7 +65,8 @@
 
 instance MonadException Test where
     cleanUpErr a _ = S.tell [TCleanUpError] >> a
-    throwExc e = error . show $ e
+    throwExc e = S.tell [TException e] >>
+                 E.throwError ()
 
 instance MonadLogic Test where
     checkQuit _ _ = S.gets isOver
diff --git a/src/Terminal/Game/Plane.hs b/src/Terminal/Game/Plane.hs
--- a/src/Terminal/Game/Plane.hs
+++ b/src/Terminal/Game/Plane.hs
@@ -100,12 +100,12 @@
 reverseCell Transparent        = Transparent
 
 -- | Creates 'Plane' from 'String', good way to import ASCII
--- art/diagrams. @error@s on empty string.
+-- art/diagrams. Returns a 1×1 transparent plane on empty string.
 stringPlane :: String -> Plane
 stringPlane t = stringPlaneGeneric Nothing t
 
 -- | Same as 'stringPlane', but with transparent 'Char'.
--- @error@s on empty string.
+-- Returns a 1×1 transparent plane on empty string.
 stringPlaneTrans :: Char -> String -> Plane
 stringPlaneTrans c t = stringPlaneGeneric (Just c) t
 
@@ -158,9 +158,10 @@
           (w2, h2)  = planeSize p2
 
 -- | Cut out a plane by top-left and bottom-right coordinates.
+-- Returns a 1×1 transparent plane when @r1>r2@ or @c1>c2@.
 subPlane :: Plane -> Coords -> Coords -> Plane
 subPlane p (r1, c1) (r2, c2)
-        | r1 > r2 || c1 > c2 = err (r1, c1) (r2, c2)
+        | r1 > r2 || c1 > c2 = makeTransparent ' ' (blankPlane 1 1)
         | otherwise          =
             let cs       = assocsPlane p
                 fs       = filter f cs
@@ -170,9 +171,6 @@
     where
           f ((rw, cw), _) = rw >= r1 && rw <= r2 &&
                             cw >= c1 && cw <= c2
-
-          err p1 p2 = error ("subPlane: top-left point " ++ show p1 ++
-                             " > bottom-right point " ++ show p2 ++ ".")
 
 -------------
 -- INQUIRE --
diff --git a/test/Terminal/Game/Layer/ImperativeSpec.hs b/test/Terminal/Game/Layer/ImperativeSpec.hs
--- a/test/Terminal/Game/Layer/ImperativeSpec.hs
+++ b/test/Terminal/Game/Layer/ImperativeSpec.hs
@@ -9,7 +9,9 @@
 import Test.Hspec
 import Test.Hspec.QuickCheck
 
+import qualified Control.Exception as E
 import qualified Test.QuickCheck as Q
+import qualified GHC.Exts as X
 
 spec :: Spec
 spec = do
@@ -52,3 +54,10 @@
              \es -> let w = createGRec (80, 24) es
                         a = testGame aloneInARoom w
                     in a == a
+    it "fails with an informative message" $ do
+        r <- readRecord "test/records/alone-record-test.gr"
+        let r' = r { aTermSize = X.fromList (replicate 1000 Nothing) }
+            t = testGame aloneInARoom r'
+            e = "testGame, exception called: [TSetupDisplay,TStartEvents,\
+                \TException CannotGetDisplaySize]"
+        E.evaluate t `shouldThrow` errorCall e
diff --git a/test/Terminal/Game/Layer/Object/TestSpec.hs b/test/Terminal/Game/Layer/Object/TestSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Terminal/Game/Layer/Object/TestSpec.hs
@@ -0,0 +1,19 @@
+module Terminal.Game.Layer.Object.TestSpec where
+
+import Terminal.Game.Layer.Imperative
+import Terminal.Game.Layer.Object
+import Alone
+
+import Test.Hspec
+
+import qualified GHC.Exts as E
+
+spec :: Spec
+spec = do
+
+  describe "runTest" $ do
+    it "logs exceptions without failing" $ do
+        r <- readRecord "test/records/alone-record-test.gr"
+        let r' = r { aTermSize = E.fromList (replicate 1000 Nothing) }
+            t = runTest (runGameGeneral aloneInARoom) r'
+        last (snd t) `shouldBe` TException CannotGetDisplaySize
diff --git a/test/Terminal/Game/PlaneSpec.hs b/test/Terminal/Game/PlaneSpec.hs
--- a/test/Terminal/Game/PlaneSpec.hs
+++ b/test/Terminal/Game/PlaneSpec.hs
@@ -4,9 +4,7 @@
 import Terminal.Game.Plane
 import Terminal.Game.Draw
 
-import qualified Control.Exception as E
 
-
 spec :: Spec
 spec = do
 
@@ -44,9 +42,9 @@
       planePaper (subPlane pa (1, 1) (2, 1)) `shouldBe` "p\nf\n"
     it "does not crash on OOB" $
       planeSize (subPlane pa (1, 1) (10, 10)) `shouldBe` (5, 2)
-    it "errs on emptycell" $
-      E.evaluate (subPlane pa (2, 3) (1, 1)) `shouldThrow`
-        errorCall "subPlane: top-left point (2,3) > bottom-right point (1,1)."
+    it "does not err on inconsistent coords" $
+      subPlane pa (2, 3) (1, 1) `shouldBe`
+        (blankPlane 1 1 # makeTransparent ' ')
     it "but not on a single cell" $
       subPlane pa (2, 3) (2, 3) `shouldBe` cell 'l'
 
