diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -4,6 +4,10 @@
 
 ## Functional Versions
 
+### 0.9.0.6 -- 2026-10-21
+* Fixed a lone Escape never timing out in `readKeyPress` and the line editor. `waitKey` called `isEOF` before `hWaitForInput`; on a terminal, `isEOF` blocks until the next key, so Esc cancellation and the 50 ms sequence timeout never fired. `waitKey` now relies on `hWaitForInput` and converts only end-of-file exceptions into `KeyEnded`.
+* Verified the Linux terminal checklist in `docs/platforms.md` under a pseudo-terminal: immediate keys, arrow isolation, forms, editor navigation, Esc cancellation, horizontal scrolling, colour detection and resize all pass.
+
 ### 0.9.0.5 -- 2026-10-21
 * Pinned the macOS CI job to `macos-15`. The `macos-latest` image ships an SDK that the GHC 9.14.1 linker cannot parse, so the build failed before the tests ran.
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@
 
 > **Formerly known as Vis.** Ansiform is a terminal formatting library, unrelated to the JavaScript `vis-network` graph visualization library or to the Hackage package [`Vis`](https://hackage.haskell.org/package/Vis) (3D graphics). See [Naming](https://github.com/Tralen/ansiform/blob/master/docs/naming.md).
 
-Current version: **0.9.0.5**.
+Current version: **0.9.0.6**.
 
 Repository: <https://github.com/Tralen/ansiform>.
 
diff --git a/ansiform.cabal b/ansiform.cabal
--- a/ansiform.cabal
+++ b/ansiform.cabal
@@ -1,6 +1,6 @@
 cabal-version:       3.0
 name:                ansiform
-version:             0.9.0.5
+version:             0.9.0.6
 synopsis:            Display formatting for command line interfaces.
 description:         Basic format DSL for building an ANSI command line interface
                      with colors, alignment, tables, paragraphs and more.
diff --git a/docs/platforms.md b/docs/platforms.md
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -73,3 +73,5 @@
 4. Type a value wider than the terminal and confirm that the window scrolls with the cursor.
 5. Confirm that colours appear on a terminal and disappear when output is redirected.
 6. Resize the terminal and confirm that the layout uses the new width.
+
+On 2026-10-21 the checklist was executed on Linux under a pseudo-terminal and all six items passed. That run found the lone-Escape timeout fixed in 0.9.0.6. macOS and Windows still need the same run on real terminals.
diff --git a/docs/releasing.md b/docs/releasing.md
--- a/docs/releasing.md
+++ b/docs/releasing.md
@@ -5,7 +5,7 @@
 ## Current status
 
 - Package: `ansiform`. The name is already registered on Hackage.
-- Version: 0.7.10.4.
+- Version: 0.9.0.5.
 - License: MIT.
 - `cabal sdist` produces a complete source distribution with sources, docs, examples, tests, README, ChangeLog and LICENSE.
 - Haddock builds with 100% coverage for the public functions and types. The remaining warnings are only about module re-exports.
@@ -13,9 +13,9 @@
 - GitHub: public at <https://github.com/Tralen/ansiform>; the local remote is `git@github.com:Tralen/ansiform.git`.
 - `source-repository`, `homepage` and `bug-reports` are set to the GitHub URL.
 - The history has been rewritten and contains no personal paths.
-- Tag `0.7.10.4` and its GitHub release are published.
-- Hackage: 0.7.10.4 is published at <https://hackage.haskell.org/package/ansiform-0.7.10.4>, with documentation uploaded.
-- The 0.7.10.4 release removes the obsolete tutorial comment. The 0.7.10.3 release added the tutorial, the bookmarks example, the readable muted theme and the borderless `BordersNone` layout.
+- Tag `0.9.0.5` and its GitHub release are published at <https://github.com/Tralen/ansiform/releases/tag/0.9.0.5>.
+- Hackage: 0.9.0.5 is published at <https://hackage.haskell.org/package/ansiform-0.9.0.5>, with documentation uploaded.
+- The 0.9.0.5 release fixes the Windows render crash and the macOS CI toolchain, and marks the first Hackage publication since 0.7.10.4. The 0.8.0 to 0.9.0.4 changes are recorded in `ChangeLog.md`, including editable form fields, `Ansiform.Line`, `readKeyPress` and the platform documentation.
 
 ## Credentials
 
@@ -159,4 +159,4 @@
 - The public types no longer use the `Vis` prefix, and the internal builders use the `form` prefix.
 - `hie.yaml` is optional editor configuration and is not tracked. Commit it only if the team decides to share it.
 - Commits use the GitHub noreply address; maintainer contact uses `dev@tralen.cc`.
-- The CI workflow targets GHC 9.14.1 and Cabal 3.18.1.0. Extend the matrix once the supported GHC range is validated.
+- The CI workflow runs GHC 9.2.8 and GHC 9.14.1 with Cabal 3.18.1.0 on Ubuntu, Windows and `macos-15`. Keep the macOS pin until a newer GHC supports the `macos-latest` SDK.
diff --git a/examples/Demo.hs b/examples/Demo.hs
--- a/examples/Demo.hs
+++ b/examples/Demo.hs
@@ -16,7 +16,7 @@
 
 -- Version of this demo, following the package version.
 demoVersion :: String
-demoVersion = "0.9.0.5"
+demoVersion = "0.9.0.6"
 
 -- The philosophers shown in the Groups screen.
 data Philosopher
diff --git a/src/Ansiform/Internal/Input.hs b/src/Ansiform/Internal/Input.hs
--- a/src/Ansiform/Internal/Input.hs
+++ b/src/Ansiform/Internal/Input.hs
@@ -63,19 +63,21 @@
 
 -- | Waits up to the given number of milliseconds for the next key.
 --   Uses 'hWaitForInput', which is portable and avoids interrupting a blocked
---   read with an asynchronous exception. Only an end-of-file exception is
---   turned into data; other I/O errors still propagate.
+--   read with an asynchronous exception. Do not call 'isEOF' first: on a
+--   terminal it blocks until a key arrives and would defeat the timeout.
+--   Only an end-of-file exception is turned into data; other I/O errors still
+--   propagate.
 waitKey :: Int -> IO KeyWait
 waitKey ms = do
-    eof <- isEOF
-    if eof
-        then return KeyEnded
-        else do
-            ready <- tryJust eofOnly (hWaitForInput stdin ms)
-            case ready of
+    ready <- tryJust eofOnly (hWaitForInput stdin ms)
+    case ready of
+        Left _ -> return KeyEnded
+        Right False -> return KeyTimeout
+        Right True -> do
+            value <- tryJust eofOnly getChar
+            case value of
                 Left _ -> return KeyEnded
-                Right False -> return KeyTimeout
-                Right True -> KeyReady <$> getChar
+                Right c -> return (KeyReady c)
   where
     eofOnly e = if isEOFError e then Just e else Nothing
 
