diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,28 @@
+0.0.1.0
+=======
+
+* Fix: a nightly snapshot missing from the database (e.g. a date for which
+  no nightly was published) was reported as `✓ up to date`.
+  Now its GHC version is inferred from the closest known nightly.
+
+* Snapshots that cannot be analyzed are now reported as `? unknown snapshot`
+  instead of `✓ up to date`.  This includes snapshots newer than the database
+  (previously a downgrade could be suggested), unknown LTS major versions,
+  and resolvers that are not Stackage snapshots.
+
+* `stacker update` is much faster (seconds instead of minutes): the GHC version
+  is now read from the end of each snapshot file instead of parsing the whole YAML.
+  If this fails for a file, a warning is printed and full YAML parsing is used.
+
+* New subcommands `stacker info repo` and `stacker info snapshots`
+  that print just the repository path and just the snapshot list, respectively.
+
+* Shipped snapshot database updated (latest: lts-24.63, nightly-2026-10-12).
+
+* Tested with GHC 9.4.8 - 9.14.1.
+
+_Andreas Abel, 2026-10-15_
+
 0.0.0.0
 =======
 
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@
 | `dry-run [FILES...]` | Shows what updates would be performed by the respective `bump` (default command) |
 | `config`             | Configures `stacker`                                                             |
 | `update`             | Updates its database of Stackage snapshots                                       |
-| `info`               | Prints table mapping GHC major versions to their latest snapshots                |
+| `info [repo\|snapshots]` | Prints repository path and/or table mapping GHC major versions to their latest snapshots |
 
 These can be modified with the following option(s):
 
@@ -189,7 +189,7 @@
 stack-9.10.yaml   nightly-2024-07-07   → bump to lts-24.36
 stack-9.12.yaml   nightly-2025-10-03   → bump to nightly-2026-04-01
 ```
-When coloring is on, the `stack*.yml` column is printed in bold, `✓ up to date` in green and any bump action in bright yellow.
+When coloring is on, the `stack*.yml` column is printed in bold, `✓ up to date` in green, any bump action in bright yellow, and `? unknown snapshot` in red.
 
 Internally, `bump` and `dry-run` share the analysis phase that reads the `stack*.yaml` and creates for each an action plan,
 yet `dry-run` just pretty-prints this plan as detailed above whereas `bump` executes it.
@@ -254,6 +254,14 @@
      9.10.3: lts-24.36
      9.12.4: nightly-2026-04-01
    ```
+
+These can also be printed separately:
+- `stacker info repo` prints just the bare path `$REPO`.
+- `stacker info snapshots` prints just the map, without indentation, e.g.
+  ```
+  9.8.4: lts-23.28
+  9.10.3: lts-24.36
+  ```
 
 ## Testing
 
diff --git a/data/ghc.csv b/data/ghc.csv
--- a/data/ghc.csv
+++ b/data/ghc.csv
@@ -53,7 +53,7 @@
 9.8.4,lts-23.28
 9.10.1,nightly-2025-05-05
 9.10.2,lts-24.11
-9.10.3,lts-24.36
+9.10.3,lts-24.63
 9.12.2,nightly-2025-12-30
 9.12.3,nightly-2026-03-28
-9.12.4,nightly-2026-04-07
+9.12.4,nightly-2026-10-12
diff --git a/data/lts.csv b/data/lts.csv
--- a/data/lts.csv
+++ b/data/lts.csv
@@ -608,3 +608,30 @@
 24.34,9.10.3
 24.35,9.10.3
 24.36,9.10.3
+24.37,9.10.3
+24.38,9.10.3
+24.39,9.10.3
+24.40,9.10.3
+24.41,9.10.3
+24.42,9.10.3
+24.43,9.10.3
+24.44,9.10.3
+24.45,9.10.3
+24.46,9.10.3
+24.47,9.10.3
+24.48,9.10.3
+24.49,9.10.3
+24.50,9.10.3
+24.51,9.10.3
+24.52,9.10.3
+24.53,9.10.3
+24.54,9.10.3
+24.55,9.10.3
+24.56,9.10.3
+24.57,9.10.3
+24.58,9.10.3
+24.59,9.10.3
+24.60,9.10.3
+24.61,9.10.3
+24.62,9.10.3
+24.63,9.10.3
diff --git a/data/nightly.csv b/data/nightly.csv
--- a/data/nightly.csv
+++ b/data/nightly.csv
@@ -3094,3 +3094,160 @@
 2026-04-05,9.12.4
 2026-04-06,9.12.4
 2026-04-07,9.12.4
+2026-04-09,9.12.4
+2026-04-10,9.12.4
+2026-04-11,9.12.4
+2026-04-12,9.12.4
+2026-04-14,9.12.4
+2026-04-15,9.12.4
+2026-04-16,9.12.4
+2026-04-17,9.12.4
+2026-04-18,9.12.4
+2026-04-19,9.12.4
+2026-04-20,9.12.4
+2026-04-21,9.12.4
+2026-04-22,9.12.4
+2026-04-23,9.12.4
+2026-04-24,9.12.4
+2026-04-25,9.12.4
+2026-04-26,9.12.4
+2026-04-28,9.12.4
+2026-04-29,9.12.4
+2026-04-30,9.12.4
+2026-05-01,9.12.4
+2026-05-02,9.12.4
+2026-05-03,9.12.4
+2026-05-04,9.12.4
+2026-05-05,9.12.4
+2026-05-06,9.12.4
+2026-05-10,9.12.4
+2026-05-11,9.12.4
+2026-05-12,9.12.4
+2026-05-14,9.12.4
+2026-05-15,9.12.4
+2026-05-16,9.12.4
+2026-05-17,9.12.4
+2026-05-18,9.12.4
+2026-05-19,9.12.4
+2026-05-21,9.12.4
+2026-05-22,9.12.4
+2026-05-23,9.12.4
+2026-05-24,9.12.4
+2026-05-25,9.12.4
+2026-05-26,9.12.4
+2026-05-27,9.12.4
+2026-05-29,9.12.4
+2026-06-01,9.12.4
+2026-06-02,9.12.4
+2026-06-03,9.12.4
+2026-06-04,9.12.4
+2026-06-05,9.12.4
+2026-06-06,9.12.4
+2026-06-07,9.12.4
+2026-06-08,9.12.4
+2026-06-09,9.12.4
+2026-06-10,9.12.4
+2026-06-11,9.12.4
+2026-06-12,9.12.4
+2026-06-13,9.12.4
+2026-06-14,9.12.4
+2026-06-15,9.12.4
+2026-06-16,9.12.4
+2026-06-18,9.12.4
+2026-06-19,9.12.4
+2026-06-20,9.12.4
+2026-06-21,9.12.4
+2026-06-22,9.12.4
+2026-06-25,9.12.4
+2026-06-26,9.12.4
+2026-06-27,9.12.4
+2026-06-28,9.12.4
+2026-06-29,9.12.4
+2026-06-30,9.12.4
+2026-07-01,9.12.4
+2026-07-02,9.12.4
+2026-07-03,9.12.4
+2026-07-06,9.12.4
+2026-07-07,9.12.4
+2026-07-08,9.12.4
+2026-07-09,9.12.4
+2026-07-10,9.12.4
+2026-07-11,9.12.4
+2026-07-12,9.12.4
+2026-07-13,9.12.4
+2026-07-15,9.12.4
+2026-07-16,9.12.4
+2026-07-17,9.12.4
+2026-07-18,9.12.4
+2026-07-19,9.12.4
+2026-07-20,9.12.4
+2026-07-21,9.12.4
+2026-07-22,9.12.4
+2026-07-23,9.12.4
+2026-07-24,9.12.4
+2026-07-26,9.12.4
+2026-07-27,9.12.4
+2026-07-28,9.12.4
+2026-07-29,9.12.4
+2026-07-30,9.12.4
+2026-07-31,9.12.4
+2026-08-01,9.12.4
+2026-08-02,9.12.4
+2026-08-03,9.12.4
+2026-08-04,9.12.4
+2026-08-05,9.12.4
+2026-08-06,9.12.4
+2026-08-07,9.12.4
+2026-08-08,9.12.4
+2026-08-09,9.12.4
+2026-08-10,9.12.4
+2026-08-11,9.12.4
+2026-08-12,9.12.4
+2026-08-13,9.12.4
+2026-08-14,9.12.4
+2026-08-15,9.12.4
+2026-08-16,9.12.4
+2026-08-19,9.12.4
+2026-08-20,9.12.4
+2026-08-23,9.12.4
+2026-08-24,9.12.4
+2026-08-25,9.12.4
+2026-08-26,9.12.4
+2026-08-27,9.12.4
+2026-08-30,9.12.4
+2026-08-31,9.12.4
+2026-09-01,9.12.4
+2026-09-06,9.12.4
+2026-09-07,9.12.4
+2026-09-08,9.12.4
+2026-09-09,9.12.4
+2026-09-10,9.12.4
+2026-09-11,9.12.4
+2026-09-12,9.12.4
+2026-09-13,9.12.4
+2026-09-14,9.12.4
+2026-09-15,9.12.4
+2026-09-16,9.12.4
+2026-09-18,9.12.4
+2026-09-19,9.12.4
+2026-09-20,9.12.4
+2026-09-21,9.12.4
+2026-09-22,9.12.4
+2026-09-23,9.12.4
+2026-09-24,9.12.4
+2026-09-25,9.12.4
+2026-09-26,9.12.4
+2026-09-27,9.12.4
+2026-09-29,9.12.4
+2026-09-30,9.12.4
+2026-10-01,9.12.4
+2026-10-02,9.12.4
+2026-10-03,9.12.4
+2026-10-04,9.12.4
+2026-10-05,9.12.4
+2026-10-06,9.12.4
+2026-10-08,9.12.4
+2026-10-09,9.12.4
+2026-10-10,9.12.4
+2026-10-11,9.12.4
+2026-10-12,9.12.4
diff --git a/src/Analysis.hs b/src/Analysis.hs
--- a/src/Analysis.hs
+++ b/src/Analysis.hs
@@ -4,22 +4,24 @@
   ( analyzeStackYaml
   , analyzeAllStackYamls
   , analyzeStackYamls
+  , determineNewSnapshot
   ) where
 
 import Prelude hiding (min, span)
 
+import Control.Applicative ((<|>))
 import Control.Monad (filterM)
 import Data.Functor ((<&>))
 import Data.List (maximumBy)
 import Data.Map.Strict qualified as Map
-import Data.Maybe (catMaybes)
+import Data.Maybe (catMaybes, fromMaybe)
 import Data.Ord (comparing)
 import Data.Text (Text)
 import Data.Text qualified as T
 import System.Directory (doesFileExist)
 import Text.Printf (printf)
 
-import Types (Action(..), SnapshotDB(..), LTSVersion(..), NightlyVersion(..), GHCVersion(..), Snapshot(..))
+import Types (Action(..), Bump(..), SnapshotDB(..), LTSVersion(..), NightlyVersion(..), GHCVersion(..), Snapshot(..))
 import StackYaml (parseStackYaml, findStackYamlFiles, findStackYamlFilesRecursive, getSymlinkMap)
 
 -- | Analyze a single stack.yaml file
@@ -40,8 +42,8 @@
 analyzeStackYamls db files recursive = do
   -- Get files to analyze: either auto-discover or use provided list
   -- Non-existent files are silently filtered out
-  filesToAnalyze <- 
-    if null files 
+  filesToAnalyze <-
+    if null files
       then if recursive
            then findStackYamlFilesRecursive
            else findStackYamlFiles
@@ -55,14 +57,23 @@
 analyzeAllStackYamls db = analyzeStackYamls db [] False
 
 -- | Determine the new snapshot for a given old snapshot
-determineNewSnapshot :: SnapshotDB -> Text -> Maybe Text
+determineNewSnapshot :: SnapshotDB -> Text -> Bump
 determineNewSnapshot db oldSnap
-  | T.isPrefixOf "lts-" oldSnap = determineLTSBump db oldSnap
-  | T.isPrefixOf "nightly-" oldSnap = determineNightlyBump db oldSnap
-  | otherwise = Nothing
+  | T.isPrefixOf "lts-" oldSnap = fromMaybe Unknown $ determineLTSBump db oldSnap
+  | T.isPrefixOf "nightly-" oldSnap = fromMaybe Unknown $ determineNightlyBump db oldSnap
+  | otherwise = Unknown
 
+-- | Compare the latest known snapshot with the old one.
+-- If the old snapshot is newer than anything we know of,
+-- our database is outdated and we cannot give advice.
+compareWithLatest :: Ord a => a -> a -> Snapshot -> Bump
+compareWithLatest old latest snap = case compare latest old of
+  GT -> BumpTo (formatSnapshot snap)
+  EQ -> UpToDate
+  LT -> Unknown
+
 -- | Determine bump for LTS snapshot
-determineLTSBump :: SnapshotDB -> Text -> Maybe Text
+determineLTSBump :: SnapshotDB -> Text -> Maybe Bump
 determineLTSBump db oldSnap = do
   oldVersion <- parseLTSSnapshot oldSnap
   -- Find the latest LTS with the same major version
@@ -71,16 +82,16 @@
     then Nothing
     else do
       let (latestVersion, _) = maximumBy (comparing fst) sameMajor
-      let newSnap = formatSnapshot (LTS latestVersion)
-      if newSnap == oldSnap
-        then Nothing  -- Already up to date
-        else Just newSnap
+      return $ compareWithLatest oldVersion latestVersion (LTS latestVersion)
 
 -- | Determine bump for nightly snapshot
-determineNightlyBump :: SnapshotDB -> Text -> Maybe Text
+determineNightlyBump :: SnapshotDB -> Text -> Maybe Bump
 determineNightlyBump db oldSnap = do
   oldVersion <- parseNightlySnapshot oldSnap
-  oldGHC <- Map.lookup oldVersion (dbNightly db)
+  -- The exact nightly may be missing from the database
+  -- (e.g. a date for which no nightly was published);
+  -- then take the GHC version of the closest known neighbour.
+  (_, oldGHC) <- Map.lookupLE oldVersion (dbNightly db) <|> Map.lookupGE oldVersion (dbNightly db)
   -- Get GHC major version (e.g., (9,6) from GHCVersion 9 6 1)
   let ghcMajor = getGHCMajor oldGHC
   -- Find if there's an LTS for this GHC major version
@@ -89,7 +100,7 @@
     then do
       -- Bump to latest LTS for this GHC major
       let (latestLTS, _) = maximumBy (comparing fst) ltsForGHC
-      return $ formatSnapshot (LTS latestLTS)
+      return $ BumpTo $ formatSnapshot (LTS latestLTS)
     else do
       -- Bump to latest nightly for this GHC major
       let nightliesForGHC = filter (\(_, ghc) -> getGHCMajor ghc == ghcMajor) $ Map.toList (dbNightly db)
@@ -97,10 +108,7 @@
         then Nothing
         else do
           let (latestNightly, _) = maximumBy (comparing fst) nightliesForGHC
-          let newSnap = formatSnapshot (Nightly latestNightly)
-          if newSnap == oldSnap
-            then Nothing
-            else Just newSnap
+          return $ compareWithLatest oldVersion latestNightly (Nightly latestNightly)
 
 -- | Get GHC major version from full version (e.g., (9,6) from GHCVersion 9 6 1)
 getGHCMajor :: GHCVersion -> (Int, Int)
diff --git a/src/CSV.hs b/src/CSV.hs
--- a/src/CSV.hs
+++ b/src/CSV.hs
@@ -9,7 +9,8 @@
 import Prelude hiding (lines, min)
 
 import Control.Monad (unless)
-import Data.List (sortBy, sortOn)
+import Data.ByteString.Char8 qualified as BS
+import Data.List (find, sortBy, sortOn)
 import Data.Map.Strict (Map)
 import Data.Map.Strict qualified as Map
 import Data.Ord (comparing)
@@ -25,7 +26,7 @@
 import Text.Printf (printf)
 import Types (LTSVersion(..), NightlyVersion(..), GHCVersion(..), Snapshot(..), SnapshotDB(..))
 import XDG (getStateDir)
-import System.IO (stderr)
+import System.IO (IOMode(ReadMode), SeekMode(AbsoluteSeek), hFileSize, hSeek, stderr, withBinaryFile)
 import Paths_stacker (getDataDir)
 import System.Directory qualified as Dir
 
@@ -83,7 +84,7 @@
   stateDir <- getStateDir
   createDirectoryIfMissing True stateDir
 
-  TIO.hPutStrLn stderr $ "Processing snapshot database.  This might take a couple of minutes..."
+  TIO.hPutStrLn stderr $ "Processing snapshot database..."
 
   -- Process LTS snapshots
   TIO.hPutStrLn stderr $ "Processing LTSs..."
@@ -165,9 +166,46 @@
       | otherwise = error $ "Unexpected file in nightly directory: " ++ fname ++
                            " (expected .yaml extension)"
 
--- | Extract GHC version from a snapshot YAML file
+-- | Extract GHC version from a snapshot YAML file.
+-- Tries the fast method first, falling back to (slow) full YAML parsing with a warning.
 extractGHCVersion :: FilePath -> IO GHCVersion
 extractGHCVersion file = do
+  fast <- extractGHCVersionFast file
+  case fast of
+    Just ghc -> return ghc
+    Nothing -> do
+      TIO.hPutStrLn stderr $ "Warning: could not find compiler field at the end of " <> T.pack file
+        <> ", falling back to slow YAML parsing"
+      extractGHCVersionYaml file
+
+-- | Number of bytes at the end of a snapshot file to search for the compiler field
+tailSize :: Integer
+tailSize = 4096
+
+-- | Extract GHC version by searching the end of a snapshot YAML file for the compiler field.
+-- This avoids parsing the whole (large) file.  The compiler field is the last entry,
+-- either at top level (old format) or under @resolver:@ (new format):
+--
+-- > compiler: ghc-7.8.3
+--
+-- > resolver:
+-- >   compiler: ghc-9.12.4
+extractGHCVersionFast :: FilePath -> IO (Maybe GHCVersion)
+extractGHCVersionFast file = do
+  chunk <- withBinaryFile file ReadMode $ \h -> do
+    size <- hFileSize h
+    hSeek h AbsoluteSeek (max 0 (size - tailSize))
+    BS.hGetContents h
+  return $ do
+    line <- find isCompilerLine $ reverse $ BS.lines chunk
+    parseGHCVersionText $ T.pack $ BS.unpack $ BS.strip $ BS.drop (BS.length prefix) $ BS.dropWhile (== ' ') line
+  where
+    prefix = "compiler: ghc-"
+    isCompilerLine l = any (`BS.isPrefixOf` l) [prefix, "  " <> prefix]
+
+-- | Extract GHC version from a snapshot YAML file by full YAML parsing
+extractGHCVersionYaml :: FilePath -> IO GHCVersion
+extractGHCVersionYaml file = do
   result <- Yaml.decodeFileEither file
   case result of
     Left err -> error $ "Failed to parse " ++ file ++ ": " ++ show err
diff --git a/src/Commands.hs b/src/Commands.hs
--- a/src/Commands.hs
+++ b/src/Commands.hs
@@ -48,7 +48,7 @@
       putStrLn "  dry-run [FILES...]       Show what would be updated by 'bump' (default)"
       putStrLn "                           Use --recursive/-r to search subdirectories"
       putStrLn "  update                   Update stackage snapshots database"
-      putStrLn "  info                     Print GHC version to snapshot mapping"
+      putStrLn "  info [repo|snapshots]    Print repository path and/or GHC version to snapshot mapping"
       putStrLn "  config                   Configure stacker"
       putStrLn ""
       putStrLn "Available options:"
@@ -70,6 +70,7 @@
       putStrLn ""
       putStrLn "For more information, see the README"
     Config configCmd -> runConfig configCmd  -- Handle config first!
+    Info InfoRepo -> getRepoPath >>= putStrLn  -- Does not need CSV files
     cmd -> runEssentialCommand useColor cmd
 
 -- | Run an essential command (requires CSV files)
@@ -86,10 +87,7 @@
       repoPath <- getRepoPath
       ensureRepo repoPath
       runUpdate repoPath
-    Info -> do
-      -- Info command shows the repo path
-      repoPath <- getRepoPath
-      runInfo repoPath
+    Info infoCmd -> runInfo infoCmd
     _ -> return ()
 
 -- | Print version information
@@ -111,13 +109,13 @@
   -- Validate: recursive cannot be used with explicit files
   when (recursive && not (null files)) $
     die "Error: --recursive cannot be used with explicit file arguments"
-  
+
   db <- loadSnapshotDB
   actions <- analyzeStackYamls db files recursive
 
   -- Sort actions by filename
   let sortedActions = sortBy (comparing actionFile) actions
-  
+
   -- Calculate the maximum filename length for proper alignment (add 2 for padding)
   let maxFileLen = case sortedActions of
         [] -> 20
@@ -131,7 +129,7 @@
 printActionWithWidth useColor fileWidth action = do
   let file = actionFile action
   let oldSnap = actionOldSnapshot action
-  let newSnap = actionNewSnapshot action
+  let bump = actionBump action
   let symlinkTarget = actionSymlinkTarget action
 
   -- Print with proper alignment (file padded to fileWidth chars, oldSnap to 25 chars)
@@ -147,11 +145,14 @@
         putStr "= symlink to "
         putStr target
     Nothing ->
-      case newSnap of
-        Nothing -> do
+      case bump of
+        UpToDate -> do
           withColor useColor [SetColor Foreground Vivid Green] $
             putStr "✓ up to date"
-        Just new -> do
+        Unknown -> do
+          withColor useColor [SetColor Foreground Vivid Red] $
+            putStr "? unknown snapshot"
+        BumpTo new -> do
           withColor useColor [SetColor Foreground Vivid Yellow] $ do
             putStr "→ bump to "
             putStr $ T.unpack new
@@ -168,7 +169,7 @@
   -- Validate: recursive cannot be used with explicit files
   when (recursive && not (null files)) $
     die "Error: --recursive cannot be used with explicit file arguments"
-  
+
   db <- loadSnapshotDB
   actions <- analyzeStackYamls db files recursive
   mapM_ (applyAction True) actions
@@ -181,15 +182,20 @@
   generateCSVs repoPath
 
 -- | Run info command
-runInfo :: FilePath -> IO ()
-runInfo repoPath = do
-  putStrLn $ "repo: " ++ repoPath
-  db <- loadSnapshotDB
-  putStrLn "snapshots:"
-
-  let ghcEntries = Map.toAscList (dbGHC db)
-  forM_ ghcEntries $ \(ghc, snapshot) -> do
-    putStrLn $ "  " ++ formatGHCVersionText ghc ++ ": " ++ T.unpack (formatSnapshotText snapshot)
+runInfo :: InfoCmd -> IO ()
+runInfo = \case
+  InfoAll -> do
+    repoPath <- getRepoPath
+    putStrLn $ "repo: " ++ repoPath
+    putStrLn "snapshots:"
+    printSnapshots "  "
+  InfoRepo -> getRepoPath >>= putStrLn
+  InfoSnapshots -> printSnapshots ""
+  where
+    printSnapshots indent = do
+      db <- loadSnapshotDB
+      forM_ (Map.toAscList (dbGHC db)) $ \(ghc, snapshot) ->
+        putStrLn $ indent ++ formatGHCVersionText ghc ++ ": " ++ T.unpack (formatSnapshotText snapshot)
 
 -- | Format GHC version as text
 formatGHCVersionText :: GHCVersion -> String
diff --git a/src/Options.hs b/src/Options.hs
--- a/src/Options.hs
+++ b/src/Options.hs
@@ -36,27 +36,27 @@
     -- Helper to combine a command parser with the color option, creating an Options value
     withColorOption :: Parser Command -> Parser Options
     withColorOption cmdParser = Options <$> cmdParser <*> colorOption
-    
+
     -- Subcommand parser that includes color options for each command
     subcommandWithOptions = subparser
       ( command "bump" (info (withColorOption bumpParser) (progDesc "Update stack*.yaml files (optionally specify files, or use --recursive/-r to search subdirectories)"))
      <> command "dry-run" (info (withColorOption dryRunParser) (progDesc "Show what would be updated (default, optionally specify files, or use --recursive/-r to search subdirectories)"))
      <> command "update" (info (withColorOption (pure Update)) (progDesc "Update stackage snapshots database"))
-     <> command "info" (info (withColorOption (pure Info)) (progDesc "Print GHC version to snapshot mapping"))
+     <> command "info" (info (withColorOption infoCmdParser) (progDesc "Print repository path and GHC version to snapshot mapping"))
      <> command "config" (info (withColorOption configParser) (progDesc "Configure stacker"))
      <> command "version" (info (withColorOption (pure Version)) (progDesc "Print version information (also: -V, --version)"))
      <> command "numeric-version" (info (withColorOption (pure NumericVersion)) (progDesc "Print version number (also: --numeric-version)"))
      <> command "license" (info (withColorOption (pure PrintLicense)) (progDesc "Print license text (also: --license)"))
      <> command "help" (info (withColorOption (pure Help)) (progDesc "Print this help (also: -h, --help)"))
       )
-    
+
     -- Top-level parser for version/help flags and default command when no subcommand is specified
     topLevelOptions = Options
       <$> commandParserTopLevel
       <*> colorOption
-    
+
     -- Top-level command parser (for flags)
-    commandParserTopLevel = 
+    commandParserTopLevel =
       flag' Version (long "version" <> short 'V' <> help "Print version information")
       <|> flag' NumericVersion (long "numeric-version" <> help "Print version number")
       <|> flag' PrintLicense (long "license" <> help "Print license text")
@@ -69,13 +69,20 @@
   ( command "repo" (info repoParser (progDesc "Set repository path"))
   )
 
+-- | Info command parser
+infoCmdParser :: Parser Command
+infoCmdParser = Info <$> (subparser
+  ( command "repo" (info (pure InfoRepo) (progDesc "Print repository path"))
+ <> command "snapshots" (info (pure InfoSnapshots) (progDesc "Print GHC version to snapshot mapping"))
+  ) <|> pure InfoAll)
+
 -- | Repo config parser
 repoParser :: Parser ConfigCmd
 repoParser = SetRepo <$> argument str (metavar "PATH")
 
 -- | Helper to parse file arguments with optional recursive flag
 filesParser :: ([FilePath] -> Bool -> Command) -> Parser Command
-filesParser cmd = cmd 
+filesParser cmd = cmd
   <$> many (argument str (metavar "FILES..." <> action "file"))
   <*> recursiveOption
 
diff --git a/src/PathUtil.hs b/src/PathUtil.hs
--- a/src/PathUtil.hs
+++ b/src/PathUtil.hs
@@ -1,6 +1,6 @@
 -- | Path utilities for cross-platform compatibility
--- 
--- This module provides path utilities that use forward slash (/) as the 
+--
+-- This module provides path utilities that use forward slash (/) as the
 -- path separator on all platforms, including Windows. While Windows primarily
 -- uses backslash (\), it also accepts forward slash for relative paths,
 -- which is what this tool deals with.
diff --git a/src/StackYaml.hs b/src/StackYaml.hs
--- a/src/StackYaml.hs
+++ b/src/StackYaml.hs
@@ -18,7 +18,7 @@
 import Data.Text.IO qualified as TIO
 import System.Directory (listDirectory, doesFileExist, pathIsSymbolicLink, getSymbolicLinkTarget, doesDirectoryExist)
 import PathUtil ((</>), normalizeFilePath, collapseDots, takeDirectory, takeFileName, toForwardSlashes)
-import Types (Action(..))
+import Types (Action(..), Bump(..))
 
 -- | Check if a filename is a stack*.yaml file
 isStackYaml :: FilePath -> Bool
@@ -43,17 +43,17 @@
   entries <- listDirectory dir
   -- Use custom </> to ensure consistent path output with "/" separator on all platforms
   let fullPaths = map (dir </>) entries
-  
+
   -- Process files and directories separately
   files <- filterM doesFileExist fullPaths
   dirs <- filterM doesDirectoryExist fullPaths
-  
+
   -- Find stack*.yaml files in current directory, normalize to remove "./" prefix
   let stackYamls = map normalizeFilePath $ filter isStackYaml files
-  
+
   -- Recursively search subdirectories
   subResults <- mapM findStackYamlFilesInDir dirs
-  
+
   -- Combine and sort all results (all paths are already normalized)
   return $ sort (stackYamls ++ concat subResults)
 
@@ -122,9 +122,10 @@
   case actionSymlinkTarget action of
     Just _ -> return ()  -- Skip symlinks
     Nothing ->
-      case actionNewSnapshot action of
-        Nothing -> return ()  -- No update needed
-        Just newSnap -> do
+      case actionBump action of
+        UpToDate -> return ()
+        Unknown -> return ()
+        BumpTo newSnap -> do
           when verbose $ putStrLn $ "Updating " ++ actionFile action
           content <- TIO.readFile (actionFile action)
           let (before, after) = splitAtSpan (actionSpan action) content
diff --git a/src/Types.hs b/src/Types.hs
--- a/src/Types.hs
+++ b/src/Types.hs
@@ -34,11 +34,18 @@
   | Nightly NightlyVersion
   deriving (Eq, Ord, Show, Generic)
 
+-- | Outcome of analyzing a snapshot
+data Bump
+  = UpToDate      -- ^ Already the latest snapshot for its GHC major version
+  | BumpTo Text   -- ^ Should be bumped to this snapshot
+  | Unknown       -- ^ Snapshot not recognized (e.g. not in the database)
+  deriving (Eq, Show, Generic)
+
 -- | Action to take for a stack*.yaml file
 data Action = Action
   { actionFile :: FilePath
   , actionOldSnapshot :: Text
-  , actionNewSnapshot :: Maybe Text
+  , actionBump :: Bump
   , actionIsResolver :: Bool  -- True if 'resolver' field, False if 'snapshot'
   , actionSpan :: (Int, Int)  -- Character span to replace
   , actionSymlinkTarget :: Maybe FilePath  -- Just target if this is a symlink to another stack*.yaml in the list
@@ -62,11 +69,18 @@
   | DryRun [FilePath] Bool  -- Optional list of files to check, recursive flag
   | Config ConfigCmd
   | Update
-  | Info
+  | Info InfoCmd
   | Version
   | NumericVersion
   | PrintLicense
   | Help
+  deriving (Eq, Show)
+
+-- | Info command
+data InfoCmd
+  = InfoAll        -- ^ Print repo path and snapshots (in YAML format)
+  | InfoRepo       -- ^ Print just the repo path
+  | InfoSnapshots  -- ^ Print just the GHC version to snapshot mapping
   deriving (Eq, Show)
 
 -- | Config command
diff --git a/stacker.cabal b/stacker.cabal
--- a/stacker.cabal
+++ b/stacker.cabal
@@ -1,6 +1,6 @@
 cabal-version:      3.0
 name:               stacker
-version:            0.0.0.0
+version:            0.0.1.0
 synopsis:           Bump snapshots in stack.yaml files
 description:
   A tool to bump snapshots (resolvers) in stack*.yaml files.
@@ -19,6 +19,7 @@
   GHC == 9.8.4
   GHC == 9.10.3
   GHC == 9.12.4
+  GHC == 9.14.1
 
 extra-doc-files:
   CHANGELOG.md
@@ -84,6 +85,7 @@
     Paths_stacker
   build-depends:
     , base                  >= 4.17 && < 5
+    , bytestring            >= 0.11
     , directory             >= 1.3.7.0
         -- XdgState requires directory-1.3.7.0 or higher
     , filepath              >= 1.4
@@ -110,6 +112,7 @@
   hs-source-dirs:   test
   main-is:          Main.hs
   other-modules:
+    TestAnalysis
     TestDryRun
     TestBump
   build-depends:
@@ -117,8 +120,10 @@
     , stacker
     , tasty         >= 1.4
     , tasty-golden  >= 2.3
+    , tasty-hunit   >= 0.10
     , directory     >= 1.3.7.0
     , text          >= 2.0
+    , containers    >= 0.6
     , bytestring    >= 0.11
     , temporary     >= 1.3
     , process
diff --git a/test/Main.hs b/test/Main.hs
--- a/test/Main.hs
+++ b/test/Main.hs
@@ -1,6 +1,7 @@
 module Main where
 
 import Test.Tasty ( defaultMain, testGroup, TestTree )
+import TestAnalysis ( analysisTests )
 import TestDryRun ( dryRunTests )
 import TestBump ( bumpTestsIO )
 
@@ -11,6 +12,7 @@
 
 tests :: [TestTree] -> TestTree
 tests bumpTests = testGroup "stack-snapshots tests"
-  [ testGroup "dry-run tests" dryRunTests
+  [ testGroup "analysis tests" analysisTests
+  , testGroup "dry-run tests" dryRunTests
   , testGroup "bump tests" bumpTests
   ]
diff --git a/test/TestAnalysis.hs b/test/TestAnalysis.hs
new file mode 100644
--- /dev/null
+++ b/test/TestAnalysis.hs
@@ -0,0 +1,51 @@
+{-# LANGUAGE OverloadedStrings #-}
+
+module TestAnalysis (analysisTests) where
+
+import Data.Map.Strict qualified as Map
+import Test.Tasty (TestTree)
+import Test.Tasty.HUnit (testCase, (@?=))
+
+import Analysis (determineNewSnapshot)
+import Types
+
+-- | A small, fixed snapshot database, independent of the user's state directory.
+testDB :: SnapshotDB
+testDB = SnapshotDB
+  { dbLTS = Map.fromList
+      [ (LTSVersion 24 1, GHCVersion 9 10 2)
+      , (LTSVersion 24 5, GHCVersion 9 10 3)
+      ]
+  , dbNightly = Map.fromList
+      [ (NightlyVersion 2025 5 9, GHCVersion 9 10 2)
+      , (NightlyVersion 2026 8 27, GHCVersion 9 12 4)
+      -- No nightly for 2026-08-28 and 2026-08-29.
+      , (NightlyVersion 2026 8 30, GHCVersion 9 12 4)
+      , (NightlyVersion 2026 10 12, GHCVersion 9 12 4)
+      ]
+  , dbGHC = Map.empty
+  }
+
+analysisTests :: [TestTree]
+analysisTests =
+  [ testCase "nightly with LTS for its GHC bumps to LTS" $
+      determineNewSnapshot testDB "nightly-2025-05-09" @?= BumpTo "lts-24.5"
+  , testCase "known nightly bumps to latest nightly" $
+      determineNewSnapshot testDB "nightly-2026-08-27" @?= BumpTo "nightly-2026-10-12"
+  , testCase "nightly missing from database bumps to latest nightly" $
+      determineNewSnapshot testDB "nightly-2026-08-28" @?= BumpTo "nightly-2026-10-12"
+  , testCase "latest nightly is up to date" $
+      determineNewSnapshot testDB "nightly-2026-10-12" @?= UpToDate
+  , testCase "nightly newer than database is unknown" $
+      determineNewSnapshot testDB "nightly-2026-10-20" @?= Unknown
+  , testCase "LTS bumps to latest minor" $
+      determineNewSnapshot testDB "lts-24.1" @?= BumpTo "lts-24.5"
+  , testCase "latest LTS is up to date" $
+      determineNewSnapshot testDB "lts-24.5" @?= UpToDate
+  , testCase "LTS newer than database is unknown" $
+      determineNewSnapshot testDB "lts-24.9" @?= Unknown
+  , testCase "LTS with unknown major is unknown" $
+      determineNewSnapshot testDB "lts-99.1" @?= Unknown
+  , testCase "non-Stackage resolver is unknown" $
+      determineNewSnapshot testDB "ghc-9.12.4" @?= Unknown
+  ]
diff --git a/test/golden/dry-run-recursive.golden b/test/golden/dry-run-recursive.golden
--- a/test/golden/dry-run-recursive.golden
+++ b/test/golden/dry-run-recursive.golden
@@ -1,6 +1,6 @@
-other/stack.yaml                 lts-24.20                → bump to lts-24.36
+other/stack.yaml                 lts-24.20                → bump to lts-24.63
 stack-9.6.yaml                   lts-22.40                → bump to lts-22.44
 stack.yaml                       lts-22.40                = symlink to stack-9.6.yaml
 subdir1/stack-9.8.yaml           lts-23.20                → bump to lts-23.28
 subdir1/stack-other.yaml         lts-24.20                = symlink to other/stack.yaml
-subdir1/subdir2/stack-9.10.yaml  nightly-2025-01-01       → bump to lts-24.36
+subdir1/subdir2/stack-9.10.yaml  nightly-2025-01-01       → bump to lts-24.63
diff --git a/test/golden/dry-run.golden b/test/golden/dry-run.golden
--- a/test/golden/dry-run.golden
+++ b/test/golden/dry-run.golden
@@ -1,6 +1,6 @@
 stack-9.0.yaml      nightly-2021-12-04       → bump to lts-19.33
-stack-9.10.yaml     nightly-2025-05-14       → bump to lts-24.36
-stack-9.12.yaml     nightly-2025-10-05       → bump to nightly-2026-04-07
+stack-9.10.yaml     nightly-2025-05-14       → bump to lts-24.63
+stack-9.12.yaml     nightly-2025-10-05       → bump to nightly-2026-10-12
 stack-9.2.yaml      lts-20.1                 → bump to lts-20.26
 stack-9.4.yaml      lts-21.20                → bump to lts-21.25
 stack-9.6.yaml      nightly-2023-08-04       → bump to lts-22.44
diff --git a/test/golden/tests/stack-9.10.yaml b/test/golden/tests/stack-9.10.yaml
--- a/test/golden/tests/stack-9.10.yaml
+++ b/test/golden/tests/stack-9.10.yaml
@@ -1,4 +1,4 @@
-resolver: lts-24.36
+resolver: lts-24.63
 
 packages:
   - lib/
diff --git a/test/golden/tests/stack-9.12.yaml b/test/golden/tests/stack-9.12.yaml
--- a/test/golden/tests/stack-9.12.yaml
+++ b/test/golden/tests/stack-9.12.yaml
@@ -1,1 +1,1 @@
-resolver: nightly-2026-04-07
+resolver: nightly-2026-10-12
diff --git a/test/golden/tests/stack.yaml b/test/golden/tests/stack.yaml
--- a/test/golden/tests/stack.yaml
+++ b/test/golden/tests/stack.yaml
@@ -1,4 +1,4 @@
-resolver: lts-24.36
+resolver: lts-24.63
 
 packages:
   - lib/
