diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -348,20 +348,21 @@
 Stack aims to depend on well-known packages. The specific versions on which it
 depends at any time are specified by `package.yaml` and `stack.yaml`. It does
 not aim to be compatible with more than one version of the `Cabal` package at
-any time. At the time of writing (December 2024) the package versions are
-primarily ones in Stackage snapshot LTS Haskell 23.0 (for GHC 9.8.4).
+any time. At the time of writing (March 2025) the package versions are
+primarily ones in Stackage snapshot LTS Haskell 23.14 (for GHC 9.8.4) and
+`hpack-0.38.0`.
 
 A Stack executable makes use of Cabal (the library) through a small 'Setup'
 executable that it compiles from Haskell source code. The executable compiles
 that code with a dependency on the version of Cabal that ships with the
 specified GHC compiler. Each release of Stack will normally aim to support all
-versions of GHC and the Cabal package in Stackage snapshots published within
-seven years of the release. For example, snapshot LTS Haskell 10.0, published on
-19 December 2017, was the first LTS Haskell snapshot to provide GHC 8.2.2 which
-comes with `base-4.10.1.0` and `Cabal-2.0.1.1`. Normally, until, at least,
-19 December 2024, Stack releases would aim to support the immediate
-predecessor, GHC 8.0.2 and `base-4.9.1.0`, `Cabal-1.24.2.0` and Haddock 2.17.4.
-However, the next version of Stack will drop support for versions of Cabal
+versions of GHC and the Cabal package in Stackage LTS Haskell snapshots
+published within seven years of the release. For example, snapshot LTS Haskell
+12.0, published on 9 July 2018, was the first LTS Haskell snapshot to
+provide GHC 8.4.3 which comes with `base-4.11.1.0` and `Cabal-2.2.0.1`.
+Normally, until, at least, 9 July 2025, Stack releases would aim to support the
+immediate predecessor, GHC 8.2.2 and `base-4.10.1.0`, `Cabal-2.0.1.1` and
+Haddock 2.18.1. However, Stack 3.1.1 dropped support for versions of Cabal
 before 2.2. `Cabal-2.2.0.0` was released with GHC 8.4.1 on 8 March 2018.
 
 When a version of the Stack executable actually ceases to support a version of
diff --git a/ChangeLog.md b/ChangeLog.md
--- a/ChangeLog.md
+++ b/ChangeLog.md
@@ -1,5 +1,38 @@
 # Changelog
 
+## v3.5.1 - 2025-03-29
+
+**Changes since v3.3.1:**
+
+Behavior changes:
+
+* Stack will also warn (message S-8432) if there is any non-ISO/IEC 8859-1
+  (Latin-1) character in Stack's 'programs' path, as `hsc2hs` does not work if
+  there is such a character in the path to its default template
+  `template-hsc.h`.
+* Stack customizes setup using `Cabal`, so if a `setup-depends` field does not
+  mention it as a dependency, Stack warns and adds the GHC boot package as a
+  dependency. Previously, Stack would not do so but only warn that build errors
+  were likely.
+
+Other enhancements:
+
+* Bump to Hpack 0.38.0.
+* In YAML configuration files, the `install-msys` key is introduced, to enable
+  or disable the download and installation of Stack-supplied MSYS2 when
+  necessary (subject to `skip-msys: false`). The default is the same as the
+  `install-ghc` setting (including if that is set at the command line).
+  Consequently, the default behaviour of Stack is unaffected.
+* Add the `stack config set install-msys` command to configure the
+  `install-msys` option in YAML configuration files.
+* Option `allow-newer-deps` is no longer classified as experimental in
+  documentation.
+* `stack sdist` and `stack upload` report the version of Cabal (the library)
+  being used to check packages.
+* Add the `stack config build-files` command to generate (when applicable) a
+  Cabal file from a package description in the Hpack format and/or a lock file
+  for Stack's project-level configuration, without taking any other build steps.
+
 ## v3.3.1 - 2024-12-28
 
 **Changes since v3.1.1:**
@@ -41,9 +74,9 @@
   as '`stack`' if the executable file is named `stack` or, on Windows,
   `stack.exe`. Previously only how it was invoked was considered.
 * `stack test --no-run-tests --dry-run` no longer reports that Stack would test
-   project packages with test suites and
-   `stack bench --no-run-benchmarks --dry-run` no longer reports that Stack
-   would benchmark project packages with benchmarks.
+  project packages with test suites and
+  `stack bench --no-run-benchmarks --dry-run` no longer reports that Stack
+  would benchmark project packages with benchmarks.
 * `StackSetupShim` compiles with `Cabal >= 3.14.0.0`.
 
 ## v3.1.1 - 2024-07-28
@@ -405,8 +438,8 @@
 * Drop support for `Cabal` versions before 1.22 and, consequently, GHC versions
   before 7.10.
 * `stack ghci` and `stack repl` now take into account the values of
-  `default-language` keys in Cabal files, like they take into account the values
-  of `default-extensions` keys.
+  `default-language` fields in Cabal files, like they take into account the
+  values of `default-extensions` fields.
 * Removed `--ghc-paths`, `--global-stack-root` and `--local-bin-path` flags for
   `stack path`, deprecated in Stack 1.1.0 in favour of `--programs`,
   `--stack-root` and `local-bin` respectively.
@@ -512,7 +545,7 @@
   user-specific global YAML configuration file (`config.yaml`).
 * Experimental: Add option `allow-newer-deps`, which allows users to specify a
   subset of dependencies for which version bounds should be ignored
-  (`allow-newer-deps: ['foo', 'bar']`). This field has no effect unless
+  (`allow-newer-deps: ['foo', 'bar']`). This key has no effect unless
   `allow-newer` is enabled.
 
 Bug fixes:
@@ -739,8 +772,8 @@
 
 Other enhancements:
 
-* `stack ls dependencies json` now includes fields `sha256` and `size` for
-  dependencies of `type` `archive` in `location`.
+* The output of `stack ls dependencies json` now includes keys `sha256` and
+  `size` for dependencies of `type` `archive` in `location`.
   [#5280](https://github.com/commercialhaskell/stack/issues/5280)
 * Build failures now show a hint to scroll up to the corresponding section
   [#5279](https://github.com/commercialhaskell/stack/issues/5279)
@@ -1013,9 +1046,9 @@
     * Drop support for multiple package indices and legacy
       `00-index.tar` style indices. See
       [#4137](https://github.com/commercialhaskell/stack/issues/4137).
-    * Support for archives and repos in the `packages` section has
-      been removed. Instead, you must use `extra-deps` for such
-      dependencies. `packages` now only supports local filepaths.
+    * Support for archives and repos in values of the `packages` key has been
+      removed. Instead, you must use the `extra-deps` key for such dependencies.
+      `packages` now only supports local filepaths.
     * Add support for Git repositories containing (recursive) submodules.
     * Addition of new configuration options for specifying a "pantry
       tree" key, which provides more reproducibility around builds,
@@ -1078,7 +1111,7 @@
 * When using `stack script`, custom snapshot files will be resolved
   relative to the directory containing the script.
 * Remove the deprecated `--upgrade-cabal` flag to `stack setup`.
-* Support the `drop-packages` field in `stack.yaml`
+* Support the `drop-packages` key in `stack.yaml`
 * Remove the GPG signing code during uploads. The GPG signatures have
   never been used yet, and there are no plans to implement signature
   verification.
@@ -1190,13 +1223,11 @@
   variables. See [#620](https://github.com/commercialhaskell/stack/issues/620).
 * Less verbose output from `stack setup` on Windows. See
   [#1212](https://github.com/commercialhaskell/stack/issues/1212).
-* Add an optional `ignore-expiry` flag to the `hackage-security`
-  section of the `~/.stack/config.yaml`. It allows to disable timestamp
-  expiration verification just like `cabal --ignore-expiry` does.
-  The flag is not enabled by default so that the default functionality
-  is not changed.
-* Include default values for most command line flags in the `--help`
-  output. See
+* Add an optional `ignore-expiry` key to the `hackage-security` key of
+  `~/.stack/config.yaml`. It allows disabling of timestamp expiration
+  verification just like `cabal --ignore-expiry` does. The flag is not enabled
+  by default so that the default functionality is not changed.
+* Include default values for most command line flags in the `--help` output. See
   [#893](https://github.com/commercialhaskell/stack/issues/893).
 * Set the `GHC_ENVIRONMENT` environment variable to specify dependency
   packages explicitly when running test. This is done to prevent
@@ -1664,7 +1695,7 @@
 Major changes:
 
 * Complete overhaul of how snapshots are defined, the `packages` and
-  `extra-deps` fields, and a number of related items. For full
+  `extra-deps` keys, and a number of related items. For full
   details, please see the
   [writeup](https://www.fpcomplete.com/blog/2017/07/stacks-new-extensible-snapshots)
   on these changes.
@@ -1750,7 +1781,7 @@
   Sometimes GHC's heuristics would work fine even before this change,
   for example in `stack ghci`, but this override's GHC's heuristics
   when they're broken by our collecting and processing GHC's output.
-* Extended the `ghc-options` field to support `$locals`, `$targets`,
+* Extended the `ghc-options` key to support `$locals`, `$targets`,
   and `$everything`. See:
   [#3329](https://github.com/commercialhaskell/stack/issues/3329)
 * Better error message for case that `stack ghci` file targets are
@@ -2043,7 +2074,7 @@
   ([#2384](https://github.com/commercialhaskell/stack/issues/2384))
 * `stack haddock` now shows index.html paths when documentation is already up to
   date. Resolved [#781](https://github.com/commercialhaskell/stack/issues/781)
-* Respects the `custom-setup` field introduced in Cabal 1.24. This supercedes
+* Respects the `custom-setup` stanza introduced in Cabal 1.24. This supercedes
   any `explicit-setup-deps` settings in your `stack.yaml` and trusts the
   package's Cabal file to explicitly state all its dependencies.
 * If system package installation fails, `get-stack.sh` will fail as well. Also
@@ -2174,8 +2205,8 @@
 
 * `stack haddock` now supports `--haddock-internal`. See
   [#2229](https://github.com/commercialhaskell/stack/issues/2229)
-* Add support for `system-ghc` and `install-ghc` fields to `stack config set`
-  command.
+* Add support for `system-ghc` and `install-ghc` subcommands to
+  `stack config set` command.
 * Add `ghc-build` option to override autodetected GHC build to use (e.g. gmp4,
   tinfo6, nopie) on Linux.
 * `stack setup` detects systems where gcc enables PIE by default (such as Ubuntu
@@ -2193,7 +2224,7 @@
 * Add the `--open` option to "stack hpc report" command, causing the report to
   be opened in the browser.
 * The `stack config set` command now accepts a `--global` flag for suitable
-  fields which causes it to modify the global user configuration
+  subcommands which causes it to modify the global user configuration
   (`~/.stack/config.yaml`) instead of the project configuration.
   [#2675](https://github.com/commercialhaskell/stack/pull/2675)
 * Information on the latest available snapshots is now downloaded from S3
@@ -2303,8 +2334,7 @@
 
 * Add `stack hoogle` command.
   [#55](https://github.com/commercialhaskell/stack/issues/55)
-* Support for absolute file path in `url` field of `setup-info` or
-  `--ghc-bindist`
+* Support for absolute file path in `url` key of `setup-info` or `--ghc-bindist`
 * Add support for rendering GHCi scripts targeting different GHCi like
   applications
   [#2457](https://github.com/commercialhaskell/stack/pull/2457)
@@ -2539,11 +2569,11 @@
   package version - it is treated as an extra-dep. `stack build local-pkg-1.2.3`
   is an error even if the version number matches the local package
   [#2028](https://github.com/commercialhaskell/stack/issues/2028).
-* Having a `nix:` section no longer implies enabling nix build. This allows the
-  user to globally configure whether nix is used (unless the project overrides
-  the default explicitly). See
+* A `nix` key in a Stack YAML configuration file no longer implies enabling a
+  Nix build. This allows the user to globally configure whether Nix is used
+  (unless the project overrides the default explicitly). See
   [#1924](https://github.com/commercialhaskell/stack/issues/1924).
-* Remove deprecated valid-wanted field.
+* Remove deprecated `valid-wanted` key.
 * Docker: mount home directory in container
   [#1949](https://github.com/commercialhaskell/stack/issues/1949).
 * Deprecate `stack path --local-bin-path`; instead use `--local-bin`.
@@ -2931,7 +2961,7 @@
 
 Major changes:
 
-* GHCJS can now be used with stackage snapshots via the new `compiler` field.
+* GHCJS can now be used with stackage snapshots via the new `compiler` key.
 * Windows installers are now available:
   [download them here](http://docs.haskellstack.org/en/stable/install_and_upgrade/#windows)
   [#613](https://github.com/commercialhaskell/stack/issues/613)
@@ -3176,9 +3206,9 @@
   [#824](https://github.com/commercialhaskell/stack/issues/824)
 * By default, `stack upgrade` automatically installs GHC as necessary
   [#797](https://github.com/commercialhaskell/stack/issues/797)
-* Added the `ghc-options` field to `stack.yaml`
+* Added the `ghc-options` key to `stack.yaml`
   [#796](https://github.com/commercialhaskell/stack/issues/796)
-* Added the `extra-path` field to `stack.yaml`
+* Added the `extra-path` key to `stack.yaml`
 * Code page changes on Windows only apply to the build command (and its
   synonyms), and can be controlled via a command line flag (still defaults to
   on) [#757](https://github.com/commercialhaskell/stack/issues/757)
@@ -3286,8 +3316,8 @@
 
 Bug fixes:
 
-* Extensions from the `other-extensions` field no longer enabled by default
-  [#449](https://github.com/commercialhaskell/stack/issues/449)
+* Extensions from the Cabal `other-extensions` field no longer enabled by
+  default [#449](https://github.com/commercialhaskell/stack/issues/449)
 * Fix: haddock forces rebuild of empty packages
   [#452](https://github.com/commercialhaskell/stack/issues/452)
 * Don't copy over executables excluded by component selection
diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,24 +1,26 @@
-Copyright (c) 2015-2024, Stack contributors
-All rights reserved.
+Copyright (c) 2015-2025, Stack contributors. All rights reserved.
 
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-    * Redistributions of source code must retain the above copyright
-      notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright
-      notice, this list of conditions and the following disclaimer in the
-      documentation and/or other materials provided with the distribution.
-    * Neither the name of Stack nor the
-      names of its contributors may be used to endorse or promote products
-      derived from this software without specific prior written permission.
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
 
+1.  Redistributions of source code must retain the above copyright notice, this
+    list of conditions and the following disclaimer.
+
+2.  Redistributions in binary form must reproduce the above copyright notice,
+    this list of conditions and the following disclaimer in the documentation
+    and/or other materials provided with the distribution.
+
+3.  Neither the name of Stack nor the names of its contributors may be used to
+    endorse or promote products derived from this software without specific
+    prior written permission.
+
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL STACK CONTRIBUTORS BE LIABLE FOR ANY
-DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+DISCLAIMED. IN NO EVENT SHALL STACK CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/cabal.config b/cabal.config
--- a/cabal.config
+++ b/cabal.config
@@ -20,7 +20,7 @@
   , async ==2.2.5
   , attoparsec ==0.14.4
   , attoparsec-aeson ==2.2.2.0
-  , auto-update ==0.2.4
+  , auto-update ==0.2.6
   , base ==4.19.2.0
   , base-orphans ==0.9.3
   , base16-bytestring ==1.0.2.0
@@ -45,9 +45,9 @@
   , colour ==2.3.6
   , comonad ==5.0.9
   , companion ==0.1.0
-  , conduit ==1.3.6
+  , conduit ==1.3.6.1
   , conduit-combinators ==1.3.0
-  , conduit-extra ==1.3.6
+  , conduit-extra ==1.3.7
   , containers ==0.6.8
   , contravariant ==1.5.5
   , cookie ==0.5.0
@@ -56,9 +56,9 @@
   , crypton-conduit ==0.2.3
   , crypton-connection ==0.4.3
   , crypton-x509 ==1.7.7
-  , crypton-x509-store ==1.6.9
+  , crypton-x509-store ==1.6.10
   , crypton-x509-system ==1.6.7
-  , crypton-x509-validation ==1.6.13
+  , crypton-x509-validation ==1.6.14
   , data-default ==0.7.1.3
   , data-default-class ==0.1.2.2
   , data-default-instances-containers ==0.1.0.3
@@ -92,10 +92,10 @@
   , hashable ==1.4.7.0
   , hi-file-parser ==0.1.7.0
   , hourglass ==0.2.12
-  , hpack ==0.37.0
+  , hpack ==0.38.0
   , hpc ==0.7.0.0
   , http-api-data ==0.6.1
-  , http-client ==0.7.17
+  , http-client ==0.7.18
   , http-client-tls ==0.3.6.4
   , http-conduit ==2.3.9.1
   , http-download ==0.2.1.0
@@ -105,7 +105,7 @@
   , infer-license ==0.2.0
   , integer-conversion ==0.1.1
   , integer-gmp ==1.1
-  , integer-logarithms ==1.0.3.1
+  , integer-logarithms ==1.0.4
   , iproute ==1.7.15
   , libyaml ==0.1.4
   , libyaml-clib ==0.2.5
@@ -116,13 +116,13 @@
   , memory ==0.18.0
   , microlens ==0.4.13.1
   , microlens-mtl ==0.2.0.3
-  , microlens-th ==0.4.3.15
+  , microlens-th ==0.4.3.17
   , mime-types ==0.1.2.0
   , mintty ==0.1.4
   , monad-control ==1.0.3.1
-  , monad-logger ==0.3.40
+  , monad-logger ==0.3.41
   , monad-loops ==0.4.3
-  , mono-traversable ==1.0.20.0
+  , mono-traversable ==1.0.21.0
   , mtl ==2.3.1
   , mtl-compat ==0.2.2
   , mustache ==2.4.2
@@ -151,7 +151,7 @@
   , primitive ==0.9.0.0
   , process ==1.6.25.0
   , project-template ==0.2.1.0
-  , random ==1.2.1.2
+  , random ==1.2.1.3
   , resource-pool ==0.4.0.0
   , resourcet ==1.3.0
   , retry ==0.9.3.1
@@ -168,17 +168,17 @@
   , silently ==1.2.5.4
   , socks ==0.6.1
   , split ==0.2.5
-  , splitmix ==0.1.0.5
-  , stack ==3.3.1
+  , splitmix ==0.1.1
+  , stack ==3.5.1
   , static-bytes ==0.1.1
   , stm ==2.5.3.1
   , stm-chans ==3.0.0.9
-  , streaming-commons ==0.2.2.6
+  , streaming-commons ==0.2.3.0
   , strict ==0.5.1
   , tagged ==0.8.8
   , tar ==0.6.3.0
   , tar-conduit ==0.4.1
-  , tasty ==1.5.2
+  , tasty ==1.5.3
   , template-haskell ==2.21.0.0
   , temporary ==1.3
   , text ==2.1.1
@@ -206,7 +206,7 @@
   , uuid-types ==1.0.6
   , vault ==0.3.1.5
   , vector ==0.13.2.0
-  , vector-algorithms ==0.9.0.3
+  , vector-algorithms ==0.9.1.0
   , vector-stream ==0.1.0.1
   , witherable ==0.5
   , yaml ==0.11.11.2
diff --git a/cabal.project b/cabal.project
--- a/cabal.project
+++ b/cabal.project
@@ -33,7 +33,7 @@
 -- specified by the snapshot specifed in Stack's project-level YAML
 -- configuration file (`stack.yaml`). The relevant version of GHC can be
 -- confirmed by reviewing the snapshot on Stackage. For example, at:
--- https://www.stackage.org/lts-23.0/cabal.config.
+-- https://www.stackage.org/lts-23.14/cabal.config.
 --
 with-compiler: ghc-9.8.4
 import: cabal.config
diff --git a/doc/CONTRIBUTING.md b/doc/CONTRIBUTING.md
--- a/doc/CONTRIBUTING.md
+++ b/doc/CONTRIBUTING.md
@@ -348,20 +348,21 @@
 Stack aims to depend on well-known packages. The specific versions on which it
 depends at any time are specified by `package.yaml` and `stack.yaml`. It does
 not aim to be compatible with more than one version of the `Cabal` package at
-any time. At the time of writing (December 2024) the package versions are
-primarily ones in Stackage snapshot LTS Haskell 23.0 (for GHC 9.8.4).
+any time. At the time of writing (March 2025) the package versions are
+primarily ones in Stackage snapshot LTS Haskell 23.14 (for GHC 9.8.4) and
+`hpack-0.38.0`.
 
 A Stack executable makes use of Cabal (the library) through a small 'Setup'
 executable that it compiles from Haskell source code. The executable compiles
 that code with a dependency on the version of Cabal that ships with the
 specified GHC compiler. Each release of Stack will normally aim to support all
-versions of GHC and the Cabal package in Stackage snapshots published within
-seven years of the release. For example, snapshot LTS Haskell 10.0, published on
-19 December 2017, was the first LTS Haskell snapshot to provide GHC 8.2.2 which
-comes with `base-4.10.1.0` and `Cabal-2.0.1.1`. Normally, until, at least,
-19 December 2024, Stack releases would aim to support the immediate
-predecessor, GHC 8.0.2 and `base-4.9.1.0`, `Cabal-1.24.2.0` and Haddock 2.17.4.
-However, the next version of Stack will drop support for versions of Cabal
+versions of GHC and the Cabal package in Stackage LTS Haskell snapshots
+published within seven years of the release. For example, snapshot LTS Haskell
+12.0, published on 9 July 2018, was the first LTS Haskell snapshot to
+provide GHC 8.4.3 which comes with `base-4.11.1.0` and `Cabal-2.2.0.1`.
+Normally, until, at least, 9 July 2025, Stack releases would aim to support the
+immediate predecessor, GHC 8.2.2 and `base-4.10.1.0`, `Cabal-2.0.1.1` and
+Haddock 2.18.1. However, Stack 3.1.1 dropped support for versions of Cabal
 before 2.2. `Cabal-2.2.0.0` was released with GHC 8.4.1 on 8 March 2018.
 
 When a version of the Stack executable actually ceases to support a version of
diff --git a/doc/ChangeLog.md b/doc/ChangeLog.md
--- a/doc/ChangeLog.md
+++ b/doc/ChangeLog.md
@@ -1,5 +1,38 @@
 # Changelog
 
+## v3.5.1 - 2025-03-29
+
+**Changes since v3.3.1:**
+
+Behavior changes:
+
+* Stack will also warn (message S-8432) if there is any non-ISO/IEC 8859-1
+  (Latin-1) character in Stack's 'programs' path, as `hsc2hs` does not work if
+  there is such a character in the path to its default template
+  `template-hsc.h`.
+* Stack customizes setup using `Cabal`, so if a `setup-depends` field does not
+  mention it as a dependency, Stack warns and adds the GHC boot package as a
+  dependency. Previously, Stack would not do so but only warn that build errors
+  were likely.
+
+Other enhancements:
+
+* Bump to Hpack 0.38.0.
+* In YAML configuration files, the `install-msys` key is introduced, to enable
+  or disable the download and installation of Stack-supplied MSYS2 when
+  necessary (subject to `skip-msys: false`). The default is the same as the
+  `install-ghc` setting (including if that is set at the command line).
+  Consequently, the default behaviour of Stack is unaffected.
+* Add the `stack config set install-msys` command to configure the
+  `install-msys` option in YAML configuration files.
+* Option `allow-newer-deps` is no longer classified as experimental in
+  documentation.
+* `stack sdist` and `stack upload` report the version of Cabal (the library)
+  being used to check packages.
+* Add the `stack config build-files` command to generate (when applicable) a
+  Cabal file from a package description in the Hpack format and/or a lock file
+  for Stack's project-level configuration, without taking any other build steps.
+
 ## v3.3.1 - 2024-12-28
 
 **Changes since v3.1.1:**
@@ -41,9 +74,9 @@
   as '`stack`' if the executable file is named `stack` or, on Windows,
   `stack.exe`. Previously only how it was invoked was considered.
 * `stack test --no-run-tests --dry-run` no longer reports that Stack would test
-   project packages with test suites and
-   `stack bench --no-run-benchmarks --dry-run` no longer reports that Stack
-   would benchmark project packages with benchmarks.
+  project packages with test suites and
+  `stack bench --no-run-benchmarks --dry-run` no longer reports that Stack
+  would benchmark project packages with benchmarks.
 * `StackSetupShim` compiles with `Cabal >= 3.14.0.0`.
 
 ## v3.1.1 - 2024-07-28
@@ -405,8 +438,8 @@
 * Drop support for `Cabal` versions before 1.22 and, consequently, GHC versions
   before 7.10.
 * `stack ghci` and `stack repl` now take into account the values of
-  `default-language` keys in Cabal files, like they take into account the values
-  of `default-extensions` keys.
+  `default-language` fields in Cabal files, like they take into account the
+  values of `default-extensions` fields.
 * Removed `--ghc-paths`, `--global-stack-root` and `--local-bin-path` flags for
   `stack path`, deprecated in Stack 1.1.0 in favour of `--programs`,
   `--stack-root` and `local-bin` respectively.
@@ -512,7 +545,7 @@
   user-specific global YAML configuration file (`config.yaml`).
 * Experimental: Add option `allow-newer-deps`, which allows users to specify a
   subset of dependencies for which version bounds should be ignored
-  (`allow-newer-deps: ['foo', 'bar']`). This field has no effect unless
+  (`allow-newer-deps: ['foo', 'bar']`). This key has no effect unless
   `allow-newer` is enabled.
 
 Bug fixes:
@@ -739,8 +772,8 @@
 
 Other enhancements:
 
-* `stack ls dependencies json` now includes fields `sha256` and `size` for
-  dependencies of `type` `archive` in `location`.
+* The output of `stack ls dependencies json` now includes keys `sha256` and
+  `size` for dependencies of `type` `archive` in `location`.
   [#5280](https://github.com/commercialhaskell/stack/issues/5280)
 * Build failures now show a hint to scroll up to the corresponding section
   [#5279](https://github.com/commercialhaskell/stack/issues/5279)
@@ -1013,9 +1046,9 @@
     * Drop support for multiple package indices and legacy
       `00-index.tar` style indices. See
       [#4137](https://github.com/commercialhaskell/stack/issues/4137).
-    * Support for archives and repos in the `packages` section has
-      been removed. Instead, you must use `extra-deps` for such
-      dependencies. `packages` now only supports local filepaths.
+    * Support for archives and repos in values of the `packages` key has been
+      removed. Instead, you must use the `extra-deps` key for such dependencies.
+      `packages` now only supports local filepaths.
     * Add support for Git repositories containing (recursive) submodules.
     * Addition of new configuration options for specifying a "pantry
       tree" key, which provides more reproducibility around builds,
@@ -1078,7 +1111,7 @@
 * When using `stack script`, custom snapshot files will be resolved
   relative to the directory containing the script.
 * Remove the deprecated `--upgrade-cabal` flag to `stack setup`.
-* Support the `drop-packages` field in `stack.yaml`
+* Support the `drop-packages` key in `stack.yaml`
 * Remove the GPG signing code during uploads. The GPG signatures have
   never been used yet, and there are no plans to implement signature
   verification.
@@ -1190,13 +1223,11 @@
   variables. See [#620](https://github.com/commercialhaskell/stack/issues/620).
 * Less verbose output from `stack setup` on Windows. See
   [#1212](https://github.com/commercialhaskell/stack/issues/1212).
-* Add an optional `ignore-expiry` flag to the `hackage-security`
-  section of the `~/.stack/config.yaml`. It allows to disable timestamp
-  expiration verification just like `cabal --ignore-expiry` does.
-  The flag is not enabled by default so that the default functionality
-  is not changed.
-* Include default values for most command line flags in the `--help`
-  output. See
+* Add an optional `ignore-expiry` key to the `hackage-security` key of
+  `~/.stack/config.yaml`. It allows disabling of timestamp expiration
+  verification just like `cabal --ignore-expiry` does. The flag is not enabled
+  by default so that the default functionality is not changed.
+* Include default values for most command line flags in the `--help` output. See
   [#893](https://github.com/commercialhaskell/stack/issues/893).
 * Set the `GHC_ENVIRONMENT` environment variable to specify dependency
   packages explicitly when running test. This is done to prevent
@@ -1664,7 +1695,7 @@
 Major changes:
 
 * Complete overhaul of how snapshots are defined, the `packages` and
-  `extra-deps` fields, and a number of related items. For full
+  `extra-deps` keys, and a number of related items. For full
   details, please see the
   [writeup](https://www.fpcomplete.com/blog/2017/07/stacks-new-extensible-snapshots)
   on these changes.
@@ -1750,7 +1781,7 @@
   Sometimes GHC's heuristics would work fine even before this change,
   for example in `stack ghci`, but this override's GHC's heuristics
   when they're broken by our collecting and processing GHC's output.
-* Extended the `ghc-options` field to support `$locals`, `$targets`,
+* Extended the `ghc-options` key to support `$locals`, `$targets`,
   and `$everything`. See:
   [#3329](https://github.com/commercialhaskell/stack/issues/3329)
 * Better error message for case that `stack ghci` file targets are
@@ -2043,7 +2074,7 @@
   ([#2384](https://github.com/commercialhaskell/stack/issues/2384))
 * `stack haddock` now shows index.html paths when documentation is already up to
   date. Resolved [#781](https://github.com/commercialhaskell/stack/issues/781)
-* Respects the `custom-setup` field introduced in Cabal 1.24. This supercedes
+* Respects the `custom-setup` stanza introduced in Cabal 1.24. This supercedes
   any `explicit-setup-deps` settings in your `stack.yaml` and trusts the
   package's Cabal file to explicitly state all its dependencies.
 * If system package installation fails, `get-stack.sh` will fail as well. Also
@@ -2174,8 +2205,8 @@
 
 * `stack haddock` now supports `--haddock-internal`. See
   [#2229](https://github.com/commercialhaskell/stack/issues/2229)
-* Add support for `system-ghc` and `install-ghc` fields to `stack config set`
-  command.
+* Add support for `system-ghc` and `install-ghc` subcommands to
+  `stack config set` command.
 * Add `ghc-build` option to override autodetected GHC build to use (e.g. gmp4,
   tinfo6, nopie) on Linux.
 * `stack setup` detects systems where gcc enables PIE by default (such as Ubuntu
@@ -2193,7 +2224,7 @@
 * Add the `--open` option to "stack hpc report" command, causing the report to
   be opened in the browser.
 * The `stack config set` command now accepts a `--global` flag for suitable
-  fields which causes it to modify the global user configuration
+  subcommands which causes it to modify the global user configuration
   (`~/.stack/config.yaml`) instead of the project configuration.
   [#2675](https://github.com/commercialhaskell/stack/pull/2675)
 * Information on the latest available snapshots is now downloaded from S3
@@ -2303,8 +2334,7 @@
 
 * Add `stack hoogle` command.
   [#55](https://github.com/commercialhaskell/stack/issues/55)
-* Support for absolute file path in `url` field of `setup-info` or
-  `--ghc-bindist`
+* Support for absolute file path in `url` key of `setup-info` or `--ghc-bindist`
 * Add support for rendering GHCi scripts targeting different GHCi like
   applications
   [#2457](https://github.com/commercialhaskell/stack/pull/2457)
@@ -2539,11 +2569,11 @@
   package version - it is treated as an extra-dep. `stack build local-pkg-1.2.3`
   is an error even if the version number matches the local package
   [#2028](https://github.com/commercialhaskell/stack/issues/2028).
-* Having a `nix:` section no longer implies enabling nix build. This allows the
-  user to globally configure whether nix is used (unless the project overrides
-  the default explicitly). See
+* A `nix` key in a Stack YAML configuration file no longer implies enabling a
+  Nix build. This allows the user to globally configure whether Nix is used
+  (unless the project overrides the default explicitly). See
   [#1924](https://github.com/commercialhaskell/stack/issues/1924).
-* Remove deprecated valid-wanted field.
+* Remove deprecated `valid-wanted` key.
 * Docker: mount home directory in container
   [#1949](https://github.com/commercialhaskell/stack/issues/1949).
 * Deprecate `stack path --local-bin-path`; instead use `--local-bin`.
@@ -2931,7 +2961,7 @@
 
 Major changes:
 
-* GHCJS can now be used with stackage snapshots via the new `compiler` field.
+* GHCJS can now be used with stackage snapshots via the new `compiler` key.
 * Windows installers are now available:
   [download them here](http://docs.haskellstack.org/en/stable/install_and_upgrade/#windows)
   [#613](https://github.com/commercialhaskell/stack/issues/613)
@@ -3176,9 +3206,9 @@
   [#824](https://github.com/commercialhaskell/stack/issues/824)
 * By default, `stack upgrade` automatically installs GHC as necessary
   [#797](https://github.com/commercialhaskell/stack/issues/797)
-* Added the `ghc-options` field to `stack.yaml`
+* Added the `ghc-options` key to `stack.yaml`
   [#796](https://github.com/commercialhaskell/stack/issues/796)
-* Added the `extra-path` field to `stack.yaml`
+* Added the `extra-path` key to `stack.yaml`
 * Code page changes on Windows only apply to the build command (and its
   synonyms), and can be controlled via a command line flag (still defaults to
   on) [#757](https://github.com/commercialhaskell/stack/issues/757)
@@ -3286,8 +3316,8 @@
 
 Bug fixes:
 
-* Extensions from the `other-extensions` field no longer enabled by default
-  [#449](https://github.com/commercialhaskell/stack/issues/449)
+* Extensions from the Cabal `other-extensions` field no longer enabled by
+  default [#449](https://github.com/commercialhaskell/stack/issues/449)
 * Fix: haddock forces rebuild of empty packages
   [#452](https://github.com/commercialhaskell/stack/issues/452)
 * Don't copy over executables excluded by component selection
diff --git a/doc/build_overview.md b/doc/build_overview.md
--- a/doc/build_overview.md
+++ b/doc/build_overview.md
@@ -59,12 +59,12 @@
 
 This file is parsed to provide the following config values:
 
-* `snapshot` (or, alternatively, `resolver`) (required field)
-* `compiler` (optional field)
-* `packages` (optional field, defaults to `["."]`)
-* `extra-deps` (optional field, defaults to `[]`)
-* `flags` (optional field, defaults to `{}`)
-* `ghc-options` (optional field, defaults to `{}`)
+* `snapshot` (or, alternatively, `resolver`) (required key)
+* `compiler` (optional key)
+* `packages` (optional key, value defaults to `["."]`)
+* `extra-deps` (optional key, value defaults to `[]`)
+* `flags` (optional key, value defaults to `{}`)
+* `ghc-options` (optional key, value defaults to `{}`)
 
 `flags` and `ghc-options` break down into both _by name_ (applied to a
 specific package) and _general_ (general option `*` for flags is only available
diff --git a/doc/commands/build_command.md b/doc/commands/build_command.md
--- a/doc/commands/build_command.md
+++ b/doc/commands/build_command.md
@@ -205,11 +205,40 @@
 
 ## Controlling what gets built
 
+Stack will rebuild a targeted project package if it considers one or more of
+its files to be dirty.
+
+Stack will consider a package to be dirty if a file is added to the
+`extra-source-files` field of its Cabal file or the contents of an existing file
+listed in the `extra-source-files` field is changed.
+
+??? note "GHC's recompilation checker and Template Haskell"
+
+    GHC's recompilation checker (which is on by default) stops compilation early
+    if GHC can determine that a module does not need to be recompiled.
+
+    For modules that use Template Haskell, when the module is compiled, GHC can
+    determine dependencies, or be told about dependent files, of the code
+    inserted by the splice. (Instances of the `Quasi` class promise to provide
+    `qAddDependentFile`; see package `template-haskell`.)
+
+    However, GHC cannot be told of as yet *unknown* dependent files when a
+    module using Template Haskell is compiled. For example, this can affect the
+    `embedDir` function provided by package `file-embed`, when files are added
+    to the directory in question after the module is compiled. The resolution is
+    either to specify GHC's `-fforce-recomp` option (to turn off the
+    recompilation checker for the package) or to do a clean build.
+
 Stack will automatically build the necessary dependencies. See the introductory
 part of Stack's
 [user's guide](../tutorial/building_your_project.md#adding-dependencies) for
 information about how these dependencies get specified.
 
+If a package description specifies a custom build type, it must also specify a
+custom setup. That should list the dependencies needed to compile `Setup.hs`.
+Stack further customises the setup, using the `Cabal` package. If that package
+is not listed, Stack will warn and add the GHC boot package as a dependency. 
+
 In addition to specifying targets, you can also control what gets built, or
 retained, with the flags and options listed below. You can also affect what gets
 built by specifying Cabal (the library) options for the configure step
@@ -595,10 +624,19 @@
 include 'escaped' characters, escaped with an initial `\` character.
 
 Account may need to be taken of the shell's approach to the processing of
-command line arguments. For example, to pass `'a single quoted string'`:
+command line arguments:
 
 === "Unix-like (Bash or Zsh)"
 
+    For example, to pass `word` and `words with spaces` in Bash, or Zsh:
+
+    `stack test --benchmark-arguments 'word "words with spaces"'`
+
+    The content of single quotes is taken literally, but cannot contain a single
+    quote.
+
+    For example, to pass `'a single quoted string'`:
+
     In Bash, or Zsh (if `RC_QUOTES` option not set):
 
     `stack bench --benchmark-arguments \"\''a single quoted string'\'\"`
@@ -616,8 +654,14 @@
 
 === "Windows"
 
-    In PowerShell:
+    For example, to pass `word` and `words with spaces` in PowerShell:
 
+    `stack test --benchmark-arguments 'word "words with spaces"'`
+
+    The content of single quotes is taken literally.
+
+    For example, to pass `'a single quoted string'` in PowerShell:
+
     `stack bench --benchmark-arguments '"''a single quoted string''"'`
 
     The content of single quotes is taken literally. Within single quotes, `''`
@@ -638,10 +682,19 @@
 include 'escaped' characters, escaped with an initial `\` character.
 
 Account may need to be taken of the shell's approach to the processing of
-command line arguments. For example, to pass `'a single quoted string'`:
+command line arguments:
 
 === "Unix-like (Bash or Zsh)"
 
+    For example, to pass `word` and `words with spaces` in Bash, or Zsh:
+
+    `stack build --exec '<command> word "words with spaces"'`
+
+    The content of single quotes is taken literally, but cannot contain a single
+    quote.
+
+    For example, to pass `'a single quoted string'`:
+
     In Bash, or Zsh (if `RC_QUOTES` option not set):
 
     `stack build --exec '<command> '\"\''a single quoted string'\'\"`
@@ -659,8 +712,14 @@
 
 === "Windows"
 
-    In PowerShell:
+    For example, to pass `word` and `words with spaces` in PowerShell:
 
+    `stack build --exec '<command> word "words with spaces"'`
+
+    The content of single quotes is taken literally.
+
+    For example, to pass `'a single quoted string'` in PowerShell:
+
     `stack build --exec '<command> "''a single quoted string''"'`
 
     The content of single quotes is taken literally. Within single quotes, `''`
@@ -696,10 +755,19 @@
 include 'escaped' characters, escaped with an initial `\` character.
 
 Account may need to be taken of the shell's approach to the processing of
-command line arguments. For example, to pass `'a single quoted string'`:
+command line arguments:
 
 === "Unix-like (Bash or Zsh)"
 
+    For example, to pass `word` and `words with spaces` in Bash, or Zsh:
+
+    `stack test --test-arguments 'word "words with spaces"'`
+
+    The content of single quotes is taken literally, but cannot contain a single
+    quote.
+
+    For example, to pass `'a single quoted string'`:
+
     In Bash, or Zsh (if `RC_QUOTES` option not set):
 
     `stack test --test-arguments \"\''a single quoted string'\'\"`
@@ -717,7 +785,13 @@
 
 === "Windows"
 
-    In PowerShell:
+    For example, to pass `word` and `words with spaces` in PowerShell:
+
+    `stack test --test-arguments 'word "words with spaces"'`
+
+    The content of single quotes is taken literally.
+
+    For example, to pass `'a single quoted string'` in PowerShell:
 
     `stack test --test-arguments '"''a single quoted string''"'`
 
diff --git a/doc/commands/config_command.md b/doc/commands/config_command.md
--- a/doc/commands/config_command.md
+++ b/doc/commands/config_command.md
@@ -6,13 +6,32 @@
 stack config COMMAND
 
 Available commands:
-  env                      Print environment variables for use in a shell
-  set                      Sets a key in configuration file to value
+  build-files              Generate (when applicable) a Cabal file from a
+                           package description in the Hpack format and/or a lock
+                           file for Stack's project-level configuration.
+  env                      Print environment variables for use in a shell.
+  set                      Set a key in a configuration file to value.
 ~~~
 
 The `stack config` commands provide assistance with accessing or modifying
 Stack's configuration. See `stack config` for the available commands.
 
+## The `stack config build-files` command
+
+~~~text
+stack config build-files
+~~~
+
+`stack config build-files` generates (when applicable):
+
+* a Cabal file from a package description in the Hpack format (`package.yaml`);
+  and/or
+
+* a [lock file](../topics/lock_files.md) for Stack's project-level
+  configuration (by default, `stack.yaml`);
+
+without taking any other build steps.
+
 ## The `stack config env` command
 
 ~~~text
@@ -48,6 +67,8 @@
 Available commands:
   install-ghc              Configure whether or not Stack should automatically
                            install GHC when necessary.
+  install-msys             Configure whether or not Stack should automatically
+                           install MSYS2 when necessary.
   package-index            Configure Stack's package index
   recommend-stack-upgrade  Configure whether or not Stack should notify the user
                            if it identifes a new version of Stack is available.
@@ -74,6 +95,21 @@
 
 `stack config set install-ghc true` or `false` sets the
 [`install-ghc`](../configure/yaml/non-project.md#install-ghc) non-project
+specific configuration option in a configuration file, accordingly. By default,
+the project-level configuration file (`stack.yaml`, by default) is altered. The
+`--global` flag specifies the user-specific global configuration file
+(`config.yaml`).
+
+## The `stack config set install-msys` command
+
+[:octicons-tag-24: 3.5.1](https://github.com/commercialhaskell/stack/releases/tag/v3.5.1)
+
+~~~text
+stack config set install-msys [--global] true|false
+~~~
+
+`stack config set install-msys true` or `false` sets the
+[`install-msys`](../configure/yaml/non-project.md#install-msys) non-project
 specific configuration option in a configuration file, accordingly. By default,
 the project-level configuration file (`stack.yaml`, by default) is altered. The
 `--global` flag specifies the user-specific global configuration file
diff --git a/doc/commands/list_command.md b/doc/commands/list_command.md
--- a/doc/commands/list_command.md
+++ b/doc/commands/list_command.md
@@ -30,7 +30,7 @@
 
 ~~~text
 stack list base unix Win32 acme-missiles pantry
-base-4.20.0.1
+base-4.21.0.0
 unix-2.8.6.0
 Win32-2.14.1.0
 acme-missiles-0.3
@@ -42,20 +42,20 @@
 Package index cache populated
 Error: [S-4926]
        * Could not find package paltry on Hackage. Perhaps you meant one of:
-         pantry, pretty, pasty, xattr, alloy, para, pappy, alure, polar and
-         factory.
+         tasty, retry, path, pretty, pasty, xattr, alloy, para, pappy and
+         alure.
 
-stack --snapshot lts-23.0 list base unix Win32 acme-missiles pantry
+stack --snapshot lts-23.14 list base unix Win32 acme-missiles pantry
 Error: [S-4926]
        * Package does not appear in snapshot (directly or indirectly): acme-missiles.
 
-stack --snapshot lts-23.0 list base unix Win32 pantry
+stack --snapshot lts-23.14 list base unix Win32 pantry
 base-4.19.2.0
 unix-2.8.6.0
 Win32-2.13.4.0
 pantry-0.10.0
 
-stack --snapshot lts-23.0 list
+stack --snapshot lts-23.14 list
 AC-Angle-1.0
 ALUT-2.4.0.3
 ...
diff --git a/doc/commands/script_command.md b/doc/commands/script_command.md
--- a/doc/commands/script_command.md
+++ b/doc/commands/script_command.md
@@ -49,7 +49,7 @@
 For example:
 
 ~~~text
-stack script --snapshot lts-23.0 MyScript.hs
+stack script --snapshot lts-23.14 MyScript.hs
 ~~~
 
 A package version can be added to the snapshot on the command line with the
@@ -187,7 +187,7 @@
 can be compiled and run, with arguments, with:
 
 ~~~text
-stack --snapshot lts-23.0 script --package acme-missiles --compile MyScript.hs -- "Don't panic!" "Duck and cover!"
+stack --snapshot lts-23.14 script --package acme-missiles --compile MyScript.hs -- "Don't panic!" "Duck and cover!"
 ~~~
 
 `acme-missiles-0.3` (the most recent version in the package index) will be used.
@@ -207,7 +207,7 @@
 
 ~~~haskell
 {- stack script
-   -- snapshot lts-23.0
+   -- snapshot lts-23.14
    -- extra-dep acme-missiles-0.2
    -- package acme-missiles
 -}
@@ -309,7 +309,7 @@
 
 ~~~haskell
 {- stack script
-   --snapshot lts-23.0
+   --snapshot lts-23.14
 -}
 
 {-# LANGUAGE OverloadedStrings #-}
diff --git a/doc/commands/sdist_command.md b/doc/commands/sdist_command.md
--- a/doc/commands/sdist_command.md
+++ b/doc/commands/sdist_command.md
@@ -39,3 +39,9 @@
     For futher information, see the
     [`pvp-bounds`](../configure/yaml/non-project.md#pvp-bounds) non-project
     specific configuration option documentation.
+
+!!! note
+
+    Stack checks a package for common mistakes using checks provided by Cabal
+    (the library). The version of Cabal used by Stack to check may be
+    older than the version used by Stack to build.
diff --git a/doc/commands/setup_command.md b/doc/commands/setup_command.md
--- a/doc/commands/setup_command.md
+++ b/doc/commands/setup_command.md
@@ -7,7 +7,7 @@
             [--ghcjs-boot-options GHCJS_BOOT] [--[no-]ghcjs-boot-clean]
 ~~~
 
-`stack setup` attempts to install a version of GHC.
+`stack setup` attempts to install a version of GHC and, on Windows, MSYS2.
 
 By default:
 
@@ -28,8 +28,9 @@
 specific configuration option documentation.
 
 If Stack is configured not to install GHC (`install-ghc: false` or passing the
-`--no-install-ghc` flag) then `stack setup` will warn that the flag and the
-command are inconsistent and take no action.
+`--no-install-ghc` flag) or not to install MSYS2 (`install-msys: false` or
+passing the `--no-install-msys` flag) then `stack setup` will warn that the flag
+and the command are inconsistent and take no action.
 
 === "Linux"
 
diff --git a/doc/commands/upload_command.md b/doc/commands/upload_command.md
--- a/doc/commands/upload_command.md
+++ b/doc/commands/upload_command.md
@@ -43,8 +43,9 @@
 
 By default:
 
-* the command will check each package for common mistakes. Pass the flag
-  `--ignore-check` to disable such checks;
+* the command will check each package for common mistakes. For further
+  information, see the [`stack sdist` command](sdist_command.md) documentation.
+  Pass the flag `--ignore-check` to disable such checks; and
 
 * Stack will not test the resulting package archive. Pass the flag
   `--test-tarball` to cause Stack to test each resulting package archive, by
diff --git a/doc/configure/environment_variables.md b/doc/configure/environment_variables.md
--- a/doc/configure/environment_variables.md
+++ b/doc/configure/environment_variables.md
@@ -5,6 +5,12 @@
 The environment variables listed in alphabetal order below can affect how Stack
 behaves.
 
+If using Stack's
+[Nix integration](../topics/nix_integration.md#pure-and-impure-nix-shells) and a
+*pure* Nix build environment (or *shell*), by default, these environment
+variables will not be forwarded from your user session to the Nix shell.
+However, the Nix shell can be configured to keep specific environment variables.
+
 ## `GH_TOKEN` or `GITHUB_TOKEN`
 
 [:octicons-tag-24: 2.11.1](https://github.com/commercialhaskell/stack/releases/tag/v2.11.1)
diff --git a/doc/configure/global_flags.md b/doc/configure/global_flags.md
--- a/doc/configure/global_flags.md
+++ b/doc/configure/global_flags.md
@@ -144,10 +144,29 @@
 
 Default: Enabled
 
-Enables/disables the download and instalation of GHC if necessary. For further
-information, see the documentation for the corresponding non-project specific
-configuration [option](yaml/non-project.md#install-ghc).
+Enables/disables the download and installation of GHC when necessary. On
+Windows, `--no-install-ghc` also disables the download and installation of the
+Stack-supplied MSYS2 when necessary. For further information, see the
+documentation for the corresponding non-project specific configuration
+[option](yaml/non-project.md#install-ghc).
 
+## `--[no-]install-msys` flag
+
+[:octicons-tag-24: 3.5.1](https://github.com/commercialhaskell/stack/releases/tag/v3.5.1)
+
+Restrictions: Windows systems only
+
+Default: Same as the [`install-ghc`](yaml/non-project.md#install-ghc) setting
+(including if that is set on the command line)
+
+If Stack is checking for the Stack-supplied MSYS2 when Stack is setting up the
+environment, enables/disables the download and installation of MSYS2 when
+necessary. For further information, see the documentation for the corresponding
+non-project specific configuration [option](yaml/non-project.md#install-msys).
+
+To skip entirely checking for the Stack-supplied MSYS2, see the documentation
+for the [`skip-msys`](yaml/non-project.md#skip-msys) configuration option.
+
 ## `--jobs` or `-j` option
 
 Pass the option `--jobs <number_of_jobs>` to specify the number of concurrent
@@ -267,6 +286,9 @@
 corresponding non-project specific configuration
 [option](yaml/non-project.md#skip-msys).
 
+To prevent installation of MSYS2, if it is not installed, see the documentation
+for the [`install-msys`](yaml/non-project.md#install-msys) configuration option.
+
 ## `--snapshot` option
 
 [:octicons-tag-24: 2.15.1](https://github.com/commercialhaskell/stack/releases/tag/v2.15.1)
@@ -400,7 +422,9 @@
 Default: `https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master`
 
 The `--snapshot-location-base <url>` command option specifies the base location
-of snapshots.
+of snapshots. For further information, see the documentation for the
+corresponding non-project specific configuration
+[option](yaml/non-project.md#snapshot-location-base).
 
 ## `--help` or `-h` command flags
 
diff --git a/doc/configure/yaml/index.md b/doc/configure/yaml/index.md
--- a/doc/configure/yaml/index.md
+++ b/doc/configure/yaml/index.md
@@ -16,7 +16,7 @@
 
 -   :material-account:{ .lg .middle } __Project-specific__
 
-    Configured at the project level
+    Configured only at the project level
 
     ---
 
@@ -32,6 +32,8 @@
 
 </div>
 
+Most of Stack's configuration options are non-project specific.
+
 ## Project-level and global configuration files
 
 Stack's configuration files are each of one of two types:
@@ -122,5 +124,5 @@
     If Stack is following the XDG Base Directory Specification, the location of
     `config.yaml` (for user-specific options) is `<XDG_CONFIG_HOME>/stack`. If
     the `XDG_CONFIG_HOME` environment variable does not exist, the default is
-    `~/.config/stack` on Unix-like operating systems and `%APPDIR%\stack` on
+    `~/.config/stack` on Unix-like operating systems and `%APPDATA%\stack` on
     Windows.
diff --git a/doc/configure/yaml/non-project.md b/doc/configure/yaml/non-project.md
--- a/doc/configure/yaml/non-project.md
+++ b/doc/configure/yaml/non-project.md
@@ -2,12 +2,19 @@
 
 # Non-project-specific configuration
 
-Non-project configuration options can be included in a project-level
-configuration file (`stack.yaml`, by default) or in global configuration files
-(`config.yaml`). However, non-project-specific options in the project-level
-configuration file in the `global-project` directory are ignored by Stack. The
-options below are listed in alphabetic order.
+Non-project configuration options can be included in:
 
+* a [project-level configuration file](../yaml/index.md#project-level-and-global-configuration-files)
+  (`stack.yaml`, by default), like [project-specific](project.md) ones; or
+
+* [global configuration files](../yaml/index.md#project-level-and-global-configuration-files)
+  (`config.yaml`).
+
+However, non-project-specific options in the project-level configuration file in
+the `global-project` directory are ignored by Stack.
+
+The options below are listed in alphabetic order.
+
 ## allow-different-user
 
 [:octicons-tag-24: 1.0.1.0](https://github.com/commercialhaskell/stack/releases/tag/v1.0.1.0)
@@ -54,8 +61,6 @@
 
 ## allow-newer-deps
 
-:octicons-beaker-24: Experimental
-
 [:octicons-tag-24: 2.9.3](https://github.com/commercialhaskell/stack/releases/tag/v2.9.3)
 
 Default: `none`
@@ -294,7 +299,7 @@
 Stackage snapshot, like this:
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 compiler: ghc-9.8.3
 compiler-check: match-exact
 ~~~
@@ -872,10 +877,31 @@
 Default: `true`
 ([:octicons-tag-24: 1.5.0](https://github.com/commercialhaskell/stack/releases/tag/v1.5.0))
 
-Command line equivalent (takes precedence): `--[no-]install-ghc` flag
+Command line equivalent (takes precedence):
+[`--[no-]install-ghc`](../global_flags.md#-no-install-ghc-flag) flag
 
-Whether or not to automatically install GHC when necessary.
+Whether or not to download and install GHC when necessary. On Windows,
+`install-ghc: false` also disables the download and installation of the
+Stack-supplied MSYS2 when necessary.
 
+## install-msys
+
+[:octicons-tag-24: 3.5.1](https://github.com/commercialhaskell/stack/releases/tag/v3.5.1)
+
+Restrictions: Windows systems only
+
+Default: same as the [`install-ghc`](#install-ghc) setting (including if that is
+set on the command line)
+
+Command line equivalent (takes precedence):
+[`--[no-]install-msys`](../global_flags.md#-no-install-msys-flag) flag
+
+If Stack is checking for the Stack-supplied MSYS2 when Stack is setting up the
+environment, whether or not to download and install MSYS2 when necessary.
+
+To skip entirely checking for the Stack-supplied MSYS2, see the documentation
+for the [`skip-msys`](#skip-msys) configuration option.
+
 ## jobs
 
 Default: the number of CPUs (cores) that the machine has.
@@ -1479,6 +1505,9 @@
 Skips checking for the Stack-supplied MSYS2 (and installing that MSYS2, if it is
 not installed) when Stack is setting up the environment.
 
+To prevent installation of MSYS2, if it is not installed, see the documentation
+for the [`install-msys`](#install-msys) configuration option.
+
 ~~~yaml
 skip-msys: true
 ~~~
@@ -1495,7 +1524,9 @@
 Default: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/
 (as set in the `pantry` library)
 
-Command line equivalent (takes precedence): `--snapshot-location-base` option
+Command line equivalent (takes precedence):
+[`--snapshot-location-base`](../global_flags.md#--snapshot-location-base-command-option)
+option
 
 Sets the base location of the LTS Haskell or Stackage Nightly snapshots.
 
@@ -1610,7 +1641,7 @@
   set per project by passing `-p "category:value"` to the `stack new` command.
 * _copyright_ - sets the `copyright` property in Cabal. It is typically the
   name of the holder of the copyright on the package and the year(s) from which
-  copyright is claimed. For example: `Copyright (c) 2023-2024 Joe Bloggs`
+  copyright is claimed. For example: `Copyright (c) 2023-2025 Joe Bloggs`
 * _year_ - if `copyright` is not specified, `year` and `author-name` are used
   to generate the copyright property in Cabal. If `year` is not specified, it
   defaults to the current year.
@@ -1633,7 +1664,7 @@
     author-name: Your Name
     author-email: youremail@example.com
     category: Your Projects Category
-    copyright: 'Copyright (c) 2024 Your Name'
+    copyright: 'Copyright (c) 2025 Your Name'
     github-username: yourusername
 ~~~
 
diff --git a/doc/configure/yaml/project.md b/doc/configure/yaml/project.md
--- a/doc/configure/yaml/project.md
+++ b/doc/configure/yaml/project.md
@@ -3,7 +3,8 @@
 # Project-specific configuration
 
 Project-specific configuration options are valid only in a project-level
-configuration file (`stack.yaml`, by default).
+configuration file (`stack.yaml`, by default). Most of Stack's configuration
+options are [non-project specific](non-project.md).
 
 Each of the Haskell packages to which a Stack project relates is either a
 **project package** that is part of the project and located locally or a package
@@ -39,8 +40,8 @@
 resolver since a snapshot states how dependencies are resolved. There are
 currently four snapshot types:
 
-* LTS Haskell snapshots, e.g. `snapshot: lts-23.0`
-* Stackage Nightly snapshots, e.g. `snapshot: nightly-2024-12-13`
+* LTS Haskell snapshots, e.g. `snapshot: lts-23.14`
+* Stackage Nightly snapshots, e.g. `snapshot: nightly-2025-02-15`
 * No snapshot, just use packages shipped with the compiler. For GHC this looks
   like `snapshot: ghc-9.8.4`
 * Custom snapshot, via a URL or relative file path. For further information, see
diff --git a/doc/glossary.md b/doc/glossary.md
--- a/doc/glossary.md
+++ b/doc/glossary.md
@@ -7,7 +7,7 @@
 |Term                |Meaning                                                  |
 |--------------------|---------------------------------------------------------|
 |Cabal               |The Haskell Common Architecture for Building Applications and Libraries, provided by the [`Cabal` package](https://hackage.haskell.org/package/Cabal). Also referred to as Cabal (the library) to distinguish it from Cabal (the tool).|
-|Cabal file          |A file containing a [package description](https://cabal.readthedocs.io/en/stable/cabal-package-description-file.html) used by Cabal, named `<package_name>.cabal`.|
+|Cabal file          |A file containing a [package description](https://cabal.readthedocs.io/en/stable/cabal-package-description-file.html) used by Cabal, named `<package_name>.cabal`. A Cabal file specifies properties in the form of field/value pairs.|
 |Cabal (the tool)    |The Haskell tool used for building provided by the [`cabal-install` package](https://hackage.haskell.org/package/cabal-install).|
 |CI                  |Continuous integration.                                  |
 |CMake               |A [system](https://cmake.org/) for managing build processes.|
@@ -60,4 +60,4 @@
 |Windows             |A group of operating systems developed by Microsoft.     |
 |WSL                 |[Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/). Provides a Linux environment on Windows.|
 |XDG Base Directory Specification|A [specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html) of directories relative to which files should be located.|
-|YAML                |A human-friendly [data serialization language](https://yaml.org/).|
+|YAML                |A human-friendly [data serialization language](https://yaml.org/). The YAML syntax allows for key/value pairs.|
diff --git a/doc/install_and_upgrade.md b/doc/install_and_upgrade.md
--- a/doc/install_and_upgrade.md
+++ b/doc/install_and_upgrade.md
@@ -463,8 +463,10 @@
 
             |GHC version|LLVM versions|
             |-----------|-------------|
+            |9.12.2     |11 to 15     |
+            |9.10.1     |11 to 15     |
             |9.8.4      |11 to 15     |
-            |9.6.6      |11 to 15     |
+            |9.6.7      |11 to 15     |
             |9.4.8      |10 to 14     |
             |9.2.8      |9 to 12      |
             |9.0.2      |9, 10 or 12  |
diff --git a/doc/maintainers/releases.md b/doc/maintainers/releases.md
--- a/doc/maintainers/releases.md
+++ b/doc/maintainers/releases.md
@@ -2,63 +2,27 @@
 
 # Releases
 
-!!! todo "To do - Simplify the branch or version structure"
-
-    Just release from the `master` branch (but keep the `stable` branch
-    tracking the latest stable release plus updates to documentation).
-
-## Version scheme
-
-A Stack package or executable may have a version with three or four components:
-`X.Y.Z` or `X.Y.Z.A`.
-
-### Development or stable versions
-
-* Versions with an _even_ `Y` component are development versions (the `master`
-  branch)
-* Versions with an _odd_ `Y` component are stable versions (the `stable` branch,
-  or in a `rc/vX.Y` release candidate branch for not-yet-released versions)
-
-### Unreleased or released versions
-
-* Versions with an _even_ `Z` component are unreleased versions (including
-  release candidates)
-* Versions with an _odd_ `Z` component are released versions
-* Except for the `release` branch (which matches exactly the most recent
-  release), all branches must have an even `Z` component
-* Branches other than `stable`, `release`, and a `rc/vX.Y` release candidate
-  will always have a `0` `Z` component
+## Branches
 
-### Use of a fourth component
+* The `release` branch is intended to preserve the most recent release
+  (including, if applicable, Hackage-only dependency compatibility patch
+  releases).
 
-* Release candidate binaries will be released with an odd `A` component
-* Hackage-only dependency compatibility patch releases add a `A` component
-  (e.g. `v2.5.5.1`, in the `release` branch)
-* Pre-release unstable binaries will be released with the date as the `A`
-  component (e.g. `2.14.0.20240126`)
+* The `stable` branch is intended to be a copy of the `release` branch together
+  with any subsequent commits that (only) revise the documentation for the most
+  recent release. That documentation is presented at <https://haskellstack.org>.
 
-Examples:
+* The `master` branch is the current development branch. It is intended that a
+  working version of Stack can be built from the branch. The release process
+  begins with a copy of the branch.
 
-* `2.15.0.0`: `v2.15.x` series pre-release branch (`rc/v2.15` branch)
-* `2.15.0.1`: first release candidate for first release of `v2.15.x` series
-  (`rc/v2.15` branch)
-* `2.15.0.2`: continuing development on pre-release branch
-* `2.15.0.3`: second release candidate for first release of `v2.15.x` series
-  (`rc/v2.15` branch)
-* `2.15.1`: first release of the `2.15.x` series (`release` branch)
-* `2.15.2`: development for second release of `2.15.x` series
-  (`stable` branch)
-* `2.15.2.1`: first release candidate for second release of `2.15.x` series
-  (`rc/v2.15` branch)
-* `2.15.3`: second release of `2.15.x` series (`release` branch)
-* `2.15.3.1`: first Hackage-only patch of `2.15.3` (`release` branch)
-* `2.15.3.2`: second Hackage-only patch of `2.15.3` (`release` branch)
-* `2.14.0`: unstable development code (`master` branch)
-* `2.14.0.20240126`: pre-release snapshot of unstable version (`master` branch)
+* A `rc/vX.Y` branch (named after a release in the Stack X.Y.* series) is
+  intended to be for release candidates and final releases. It begins as a copy
+  of the `master` branch.
 
 ## Pre-release checks
 
-1.  Check for any P0 and P1 issues that should be dealt with before release.
+1.  Check for any important issues that should be dealt with before release.
 
 2.  Check for un-merged pull requests that should be merged before release.
 
@@ -99,37 +63,52 @@
 
 ### A: In the `master` branch
 
-* `package.yaml`: bump to the next release candidate version (bump the second
-  component to the next odd number, ensure the third component is `0`, and add
-  patchlevel `0`; e.g. from `2.14.0` to `2.15.0.0`).
+1.  `package.yaml`: bump to the next release candidate version (bump the second
+    component to the next odd number, ensure the third component is `0`, and add
+    patchlevel `0`; e.g. from `3.4.0` to `3.5.0.0`).
 
     !!! attention
 
         Be sure to update also `stack.cabal` (for example by using
         `stack build --dry-run`).
 
-* `ChangeLog.md`: Check for any entries that snuck into the previous version's
-  changes due to merges (`git diff origin/stable HEAD ChangeLog.md`)
+2.  `cabal.config`: Ensure the `stack` constraint is set to the same version as
+    in the `package.yaml`.
 
+3.  `ChangeLog.md`: Check for any entries that snuck into the previous version's
+    changes due to merges (`git diff origin/stable HEAD ChangeLog.md`)
+
+4.  Commit the changes to the `master` branch.
+
 ### B: Create a new release candidate branch
 
-Cut a new release candidate (RC) branch named `rc/vX.Y` from the `master`
-branch.
+From the `master` branch, checkout a new release candidate (RC) branch named
+`rc/vX.Y` (replacing `X.Y` with the first and second components of the release
+version).
 
+~~~text
+git checkout -b rc/vX.Y
+~~~
+
 ### C: Return to the `master` branch
 
 1.  `package.yaml`: bump version to the next unstable version (bump the second
     component to the next even number, ensure the third component is `0`; e.g.
-    from `2.15.0` to `2.16.0`).
+    from `3.5.0` to `3.6.0`).
 
     !!! attention
 
         Be sure to update also `stack.cabal` (for example by using
         `stack build --dry-run`).
 
-2.  `Changelog.md`:
+2.  `cabal.config`: Ensure the `stack` constraint is set to the same version as
+    in the `package.yaml`.
+
+3.  `Changelog.md`:
+
     *   Change the title of the existing **Unreleased changes** section to what
-        will be the next final (non-RC) release (e.g. `v2.15.1`).
+        will be the next final (non-RC) release (e.g. `v3.5.1`).
+
     *   Add new "Unreleased changes" section:
 
         ~~~markdown
@@ -148,18 +127,19 @@
         Bug fixes:
         ~~~
 
-3.  `cabal.config`: Ensure the `stack` constraint is set to the same version as
-    in the `package.yaml`.
+4.  Commit the changes to the `master` branch.
 
-### D: In the release candidate branch
+### D: For each release candidate, in the release candidate branch
 
 1.  Review documentation for any changes that need to be made:
 
     *   Ensure all the documentation pages are listed in the `mkdocs.yaml` file.
         Use `git diff --stat origin/stable..HEAD doc/` to look for new or
         deleted files.
+
     *   Any new documentation pages should have the "may not be correct for the
         released version of Stack" warning at the top.
+
     *   Search for old Stack version, unstable Stack version, and the next
         "obvious" possible versions in sequence, and `UNRELEASED` and replace
         with next release version (`X.Y.1`, where Y is odd).
@@ -172,16 +152,21 @@
 
     *   Search for old snapshots, set to latest snapshot (e.g. in documentation
         where it references the "currently the latest LTS")
+
     *   Look for any links to "latest" (`latest/`) documentation, replace with
         version tag
 
 2.  Check for any platform entries that need to be added to (or removed from):
 
     * [releases.yaml](https://github.com/fpco/stackage-content/blob/master/stack/releases.yaml),
+
     * [install_and_upgrade.md](https://github.com/commercialhaskell/stack/blob/master/doc/install_and_upgrade.md),
+
     * [get-stack.sh](https://github.com/commercialhaskell/stack/blob/master/etc/scripts/get-stack.sh),
+
     * [doc/README.md](https://github.com/commercialhaskell/stack/blob/master/doc/README.md),
       and
+
     * `get.haskellstack.org` redirects.
 
 3.  Re-do the pre-release checks (see the section above).
@@ -225,8 +210,10 @@
 5.  Ensure the `stack ==` constraint in `cabal.config` is set to be equal to the
     same version as `package.yaml`.
 
-6.  Follow the steps in the *Release process* section below that apply.
+6.  Commit the changes to the release candidate branch.
 
+7.  Follow the steps in the *Release process* section below that apply.
+
 ## Release process
 
 The release process differs between a first, second etc release candidate and a
@@ -266,6 +253,7 @@
 
     * Add `(release candidate)` to the name field and ensure that
       *This is a pre-release* is checked.
+
     * Add the ChangeLog to the description.
 
     Publish the GitHub release.
@@ -281,8 +269,11 @@
     prerequisites are:
 
     * a computer with that platform (operating system, machine architecture);
+
     * a sufficiently-recent existing version of Stack for that platform;
+
     * a tool to print SHA checksums, such as `shasum` on Linux and macOS; and
+
     * the GNU Privacy Guard tool (`gpg`), which has had imported the private key
       used to sign Stack executables (see further below).
 
@@ -335,13 +326,16 @@
     In the `rc/vX.Y` branch:
 
     * `package.yaml`: bump the version number. Bump the fourth component to an
-       even number (e.g. from `2.15.0.1` to `2.15.0.2`).
+       even number (e.g. from `3.5.0.1` to `3.5.0.2`).
 
         !!! attention
 
             Be sure to update also `stack.cabal` (for example by using
             `stack build --dry-run`).
 
+    * `cabal.config`: Ensure the `stack` constraint is set to the same version
+      as in the `package.yaml`.
+
     * `ChangeLog.md`: Add an “Unreleased changes” section (update the “changes
       since” version):
 
@@ -372,32 +366,35 @@
 
     ### G: Announce the release candidate
 
-    Announce the release candidate on the
-    [Haskell Community](https://discourse.haskell.org/c/announcements/10/l/latest)
-    forum.
+    Announce the release candidate:
 
-    Announce the release candidate in the Haskell Foundation's
-    [general](https://matrix.to/#/#haskell-foundation-general:matrix.org) room
-    (address `#haskell-foundation-general:matrix.org`) on
-    [Matrix](https://matrix.org/).
+    * on the
+      [Haskell Community](https://discourse.haskell.org/c/announcements/10/l/latest)
+      forum;
 
-    Announce the release candidate in the Haskell
-    [Stack and Stackage](https://matrix.to/#/#haskell-stack:matrix.org) room
-    (address `#haskell-stack:matrix.org`) on [Matrix](https://matrix.org/).
+    * in the Haskell Foundation's
+      [general](https://matrix.to/#/#haskell-foundation-general:matrix.org) room
+      (address `#haskell-foundation-general:matrix.org`) on
+      [Matrix](https://matrix.org/);
 
-    Announce the release candidate in Reddit's
-    [Haskell](https://www.reddit.com/r/haskell/) community.
+    * in the Haskell
+      [Stack and Stackage](https://matrix.to/#/#haskell-stack:matrix.org) room
+      (address `#haskell-stack:matrix.org`) on [Matrix](https://matrix.org/);
+      and
 
+    * in Reddit's [Haskell](https://www.reddit.com/r/haskell/) community.
+
     In each case, use the subject (change 'first' to 'second' etc for subsequent
     release candidates):
 
-    * `ANN: first release candidate for stack-X.Y.Z`
+    * `[ANN] First release candidate for Stack X.Y.Z`
 
     In the message, include:
 
     * a link to the release on GitHub
       (`https://github.com/commercialhaskell/stack/releases/tag/rc/vX.Y.Z.A`) to
       download it
+
     * the release description from Github.
 
 === "Final Release"
@@ -463,8 +460,11 @@
     prerequisites are:
 
     * a computer with that platform (operating system, machine architecture);
+
     * a sufficiently-recent existing version of Stack for that platform;
+
     * a tool to print SHA checksums, such as `shasum` on Linux and macOS; and
+
     * the GNU Privacy Guard tool (`gpg`), which has had imported the private key
       used to sign Stack executables (see further below).
 
@@ -593,31 +593,15 @@
     In the `stable` branch:
 
     * `package.yaml`: bump the version number. Bump the third component to an
-      even number (e.g. from `2.15.1` to `2.15.2`).
+      even number (e.g. from `3.5.1` to `3.5.2`).
 
         !!! attention
 
             Be sure to update also `stack.cabal` (for example by using
             `stack build --dry-run`).
 
-    * `ChangeLog.md`: Add an “Unreleased changes” section (update the “changes
-      since” version):
-
-        ~~~markdown
-        ## Unreleased changes
-
-        Release notes:
-
-        **Changes since vX.Y.Z:**
-
-        Major changes:
-
-        Behavior changes:
-
-        Other enhancements:
-
-        Bug fixes:
-        ~~~
+    * `cabal.config`: Ensure the `stack` constraint is set to the same version
+      as in the `package.yaml`.
 
     ### I: Update the repository's issue and pull request templates
 
@@ -636,24 +620,26 @@
 
     ### K: Announce the release
 
-    Announce the release on the
-    [Haskell Community](https://discourse.haskell.org/c/announcements/10/l/latest)
-    forum.
+    Announce the release:
 
-    Announce the release candidate in the Haskell Foundation's
-    [general](https://matrix.to/#/#haskell-foundation-general:matrix.org)
-    room (address `#haskell-foundation-general:matrix.org`) on
-    [Matrix](https://matrix.org/).
+    * on the
+      [Haskell Community](https://discourse.haskell.org/c/announcements/10/l/latest)
+      forum.
 
-    Announce the release in the Haskell
-    [Stack and Stackage](https://matrix.to/#/#haskell-stack:matrix.org) room
-    (address `#haskell-stack:matrix.org`) on [Matrix](https://matrix.org/).
+    * in the Haskell Foundation's
+      [general](https://matrix.to/#/#haskell-foundation-general:matrix.org)
+      room (address `#haskell-foundation-general:matrix.org`) on
+      [Matrix](https://matrix.org/).
 
-    Announce the release in Reddit's
-    [Haskell](https://www.reddit.com/r/haskell/) community.
+    * in the Haskell
+      [Stack and Stackage](https://matrix.to/#/#haskell-stack:matrix.org) room
+      (address `#haskell-stack:matrix.org`) on [Matrix](https://matrix.org/).
 
+    * in Reddit's [Haskell](https://www.reddit.com/r/haskell/) community.
+
     In each case, use the subject:
-    * `ANN: stack-X.Y.Z`
+
+    * `[ANN] Stack X.Y.Z`
 
     In the message, include:
 
diff --git a/doc/maintainers/self-hosted_runners.md b/doc/maintainers/self-hosted_runners.md
--- a/doc/maintainers/self-hosted_runners.md
+++ b/doc/maintainers/self-hosted_runners.md
@@ -3,10 +3,17 @@
 # Self-hosted runners
 
 [GitHub Actions](https://docs.github.com/en/actions) is used to do CI on Stack.
-The `linux-arm64` job of the `integration-tests.yml` workflow runs on a
-self-hosted runner for Linux and ARM64.
 
-The current basic setup is:
+In the case of the Linux/AArch64 platform, since 8 February 2025, that is done
+with a GitHub-hosted runner (currently in public preview). Previously, that was
+done in a separate `linux-arm64` job of the `integration-tests.yml` workflow
+that ran on a self-hosted runner for Linux and ARM64. Given that the
+GitHub-hosted runner is only in preview, the documentation of the self-hosted
+runner is being preserved for the time being.
+
+## Introduction
+
+The current basic setup was:
 
 * FP Complete has an Oracle Cloud account that provides a free tier that
   includes a really powerful ARM64 machine;
diff --git a/doc/maintainers/version_scheme.md b/doc/maintainers/version_scheme.md
new file mode 100644
--- /dev/null
+++ b/doc/maintainers/version_scheme.md
@@ -0,0 +1,63 @@
+<div class="hidden-warning"><a href="https://docs.haskellstack.org/"><img src="https://cdn.jsdelivr.net/gh/commercialhaskell/stack/doc/img/hidden-warning.svg"></a></div>
+
+# Version scheme
+
+A Stack package or executable may have a version with three or four components:
+`X.Y.Z` or `X.Y.Z.A`.
+
+## Development or stable versions
+
+* Versions with an _even_ `Y` component are development versions (the `master`
+  branch)
+
+* Versions with an _odd_ `Y` component are stable versions (the `stable` branch,
+  or in a `rc/vX.Y` release candidate branch for not-yet-released versions)
+
+## Unreleased or released versions
+
+* Versions with an _even_ `Z` component are unreleased versions (including
+  release candidates)
+
+* Versions with an _odd_ `Z` component are released versions
+
+* Except for the `release` branch, all branches must have an even `Z` component
+
+* Except for the `release`, `stable` and `rc/vX.Y` release candidate branches,
+  all branches will have a `0` `Z` component
+
+## Use of a fourth component
+
+* Release candidate binaries will be released with an odd `A` component
+
+* Hackage-only dependency compatibility patch releases add a `A` component
+  (e.g. `v2.5.1.1`, in the `release` branch)
+
+* Pre-release unstable binaries will be released with the date as the `A`
+  component (e.g. `3.6.0.20241228`)
+
+## Examples
+
+* `3.5.0.0`: `v3.5.*` series pre-release branch (`rc/v3.5` branch)
+
+* `3.5.0.1`: first release candidate for first release of `v3.5.*` series
+  (`rc/v3.5` branch)
+
+* `3.5.0.2`: continuing development on pre-release branch
+
+* `3.5.0.3`: second release candidate for first release of `v3.5.*` series
+  (`rc/v3.5` branch)
+
+* `3.5.1`: first release of the `3.5.*` series (`release` branch)
+
+* `3.5.2.1`: first release candidate for second release of `3.5.*` series
+  (`rc/v3.5` branch)
+
+* `3.5.3`: second release of `3.5.*` series (`release` branch)
+
+* `3.5.3.1`: first Hackage-only patch of `3.5.3` (`release` branch)
+
+* `3.5.3.2`: second Hackage-only patch of `3.5.3` (`release` branch)
+
+* `3.6.0`: unstable development code (`master` branch)
+
+* `3.6.0.20241228`: pre-release snapshot of unstable version (`master` branch)
diff --git a/doc/topics/Stack_and_VS_Code.md b/doc/topics/Stack_and_VS_Code.md
--- a/doc/topics/Stack_and_VS_Code.md
+++ b/doc/topics/Stack_and_VS_Code.md
@@ -150,7 +150,7 @@
 
         Each time that a snapshot is used that references a different version of
         GHC, then GHCup must be used to install it (if GHCup has not already
-        installed that version). For example, to use `snapshot: lts-23.0`
+        installed that version). For example, to use `snapshot: lts-23.14`
         (GHC 9.8.4), the command `ghcup install ghc 9.8.4` must have been used
         to install GHC 9.8.4. That may be a minor inconvenience for some people,
         as one the primary benefits of Stack over other tools for building
diff --git a/doc/topics/custom_snapshot.md b/doc/topics/custom_snapshot.md
--- a/doc/topics/custom_snapshot.md
+++ b/doc/topics/custom_snapshot.md
@@ -18,7 +18,7 @@
     for snapshot specification.
 
 ~~~yaml
-snapshot: lts-23.0 # Inherits GHC version and package set
+snapshot: lts-23.14 # Inherits GHC version and package set
 compiler: ghc-9.8.3 # Overwrites GHC version in the snapshot, optional
 
 # Additional packages, follows extra-deps syntax
@@ -62,35 +62,35 @@
 
 ### Overriding the compiler
 
-The following snapshot specification will be identical to `lts-23.0`, but
+The following snapshot specification will be identical to `lts-23.14`, but
 instead use `ghc-9.8.3` instead of `ghc-9.8.4`:
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 compiler: ghc-9.8.3
 ~~~
 
 ### Dropping packages
 
-The following snapshot specification will be identical to `lts-23.0`, but
+The following snapshot specification will be identical to `lts-23.14`, but
 without the `text` package in our snapshot. Removing this package will cause all
 the packages that depend on `text` to be unbuildable, but they will still be
 present in the snapshot.
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 drop-packages:
 - text
 ~~~
 
 ### Hiding packages
 
-The following snapshot specification will be identical to `lts-23.0`, but the
+The following snapshot specification will be identical to `lts-23.14`, but the
 `text` package will be hidden when registering. This will affect, for example,
 the import parser in the script command.
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 hidden:
 - text
 ~~~
@@ -101,18 +101,18 @@
 [ghc-options](../configure/yaml/non-project.md#ghc-options) key for build
 configuration.
 
-The following snapshot specification will be identical to `lts-23.0`, but
+The following snapshot specification will be identical to `lts-23.14`, but
 provides `-O1` as a ghc-option for `text`:
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 packages:
 - text-2.0.2
 ghc-options:
   text: -O1
 ~~~
 
-This works somewhat differently than the stack.yaml `ghc-options` field, in that
+This works somewhat differently than the stack.yaml `ghc-options` key, in that
 options can only be specified for packages that are mentioned in the custom
 snapshot's `packages` list. It sets the ghc-options, rather than extending those
 specified in the snapshot being extended.
@@ -124,11 +124,11 @@
 
 In order to specify Cabal flags for a package, you use the same syntax as the
 [flags](../configure/yaml/project.md#flags) key for build configuration. The
-following snapshot specification will be identical to `lts-23.0`, but
+following snapshot specification will be identical to `lts-23.14`, but
 it enables the `developer` Cabal flag:
 
 ~~~yaml
-snapshot: lts-23.0
+snapshot: lts-23.14
 packages:
 - text-2.0.2
 flags:
diff --git a/doc/topics/haskell_and_c_code.md b/doc/topics/haskell_and_c_code.md
--- a/doc/topics/haskell_and_c_code.md
+++ b/doc/topics/haskell_and_c_code.md
@@ -83,7 +83,7 @@
 
 The project's `stack.yaml` file only needs to identify a snapshot:
 ~~~yaml
-snapshot: lts-23.0 # GHC 9.8.4
+snapshot: lts-23.14 # GHC 9.8.4
 ~~~
 
 This example project can be built with Stack in the normal way (`stack build`),
@@ -220,7 +220,7 @@
 
 The project's `stack.yaml` file only needs to identify a snapshot:
 ~~~yaml
-snapshot: lts-23.0 # GHC 9.8.4
+snapshot: lts-23.14 # GHC 9.8.4
 ~~~
 
 This example project can be built with Stack in the normal way (`stack build`),
diff --git a/doc/topics/lock_files.md b/doc/topics/lock_files.md
--- a/doc/topics/lock_files.md
+++ b/doc/topics/lock_files.md
@@ -10,6 +10,7 @@
 * Entering all of the information to fully provide reproducibility is tedious.
   This would include things like Hackage revisions, hashes of remote tarballs,
   etc. Users don't want to enter this information.
+
 * Many operations in Stack rely upon a "snapshot hash," which transitively
   includes the completed information for all of these dependencies. If any of
   that information is missing when parsing the `stack.yaml` file or snapshot
@@ -184,3 +185,11 @@
 [`--lock-file`](../configure/global_flags.md#-lock-file-option) option, that new
 lock file is compared against the one on disk and, if there are any differences,
 written out to the disk.
+
+## `stack config build-files` command
+
+The
+[`stack config build-files`](../commands/config_command.md#the-stack-config-build-files-command)
+loads a project-level configuration file (see above) without taking any other
+build steps (other than generating, when applicable, a Cabal file from a package
+description in the Hpack format).
diff --git a/doc/topics/nix_integration.md b/doc/topics/nix_integration.md
--- a/doc/topics/nix_integration.md
+++ b/doc/topics/nix_integration.md
@@ -13,6 +13,7 @@
   conflict with any existing versions of these libraries on your system. That
   they are managed locally to the project means that you don't need to alter
   your system in any way to build any odd project pulled from the Internet; and
+
 * implicit sharing of system packages between projects. This means you don't
   have more copies on-disk than you need.
 
@@ -317,12 +318,13 @@
 ## Pure and impure Nix shells
 
 By default, Stack will run the build in a *pure* Nix build environment (or
-*shell*), which means two important things:
+*shell*). Building in a pure Nix shell means:
 
-1. basically **no environment variable will be forwarded** from your user
-   session to the nix-shell (variables like `HTTP_PROXY` or `PATH` notably will
-   not be available); and
-2. the build should fail if you haven't specified all the dependencies in the
+1. with limited exceptions, **no environment variable will be forwarded** from
+   your user session to the Nix shell (variables like `HTTP_PROXY`, `PATH`,
+   `STACK_XDG` and `STACK_ROOT` notably will not be available); and
+
+2. the build should fail if you have not specified all the dependencies in the
    `packages:` section of the Stack configuration file, even if these
    dependencies are installed elsewhere on your system. This behaviour enforces
    a complete description of the build environment to facilitate
@@ -338,6 +340,24 @@
 ~~~
 
 The equivalent command line flag (which will prevail) is `--[no-]-nix-pure`.
+
+To run the build in a *pure* Nix shell but preserve specific environment
+variables use Nix's `nix-shell` command's `--keep` option. For example, to
+preserve the [`STACK_XDG`](../configure/environment_variables.md#stack_xdg)
+environment variable, add the following to your Stack YAML configuration file:
+
+~~~yaml
+nix:
+  nix-shell-options:
+  - --keep
+  - STACK_XDG
+~~~
+
+The equivalent command line option is:
+
+~~~text
+--nix-shell-options "--keep STACK_XDG"
+~~~
 
 **Note:** On macOS, shells are non-pure by default currently. This is due soon
 to be resolved locale issues. So on macOS you'll need to be a bit more careful
diff --git a/doc/topics/scripts.md b/doc/topics/scripts.md
--- a/doc/topics/scripts.md
+++ b/doc/topics/scripts.md
@@ -19,7 +19,7 @@
 
 ~~~haskell
 #!/usr/bin/env stack
--- stack script --snapshot lts-23.0 --package turtle
+-- stack script --snapshot lts-23.14 --package turtle
 {-# LANGUAGE OverloadedStrings #-}
 import Turtle (echo)
 main = echo "Hello World!"
@@ -78,11 +78,11 @@
 
 The second line of the source code is the Stack interpreter options comment. In
 this example, it specifies the [`stack script`](../commands/script_command.md)
-command with the options of a LTS Haskell 23.0 snapshot
-(`--snapshot lts-23.0`) and ensuring the
+command with the options of a LTS Haskell 23.14 snapshot
+(`--snapshot lts-23.14`) and ensuring the
 [`turtle`](https://hackage.haskell.org/package/turtle) package is available
 (`--package turtle`). The version of the package will be that in the specified
-snapshot (`lts-23.0` provides `turtle-1.6.2`).
+snapshot (`lts-23.14` provides `turtle-1.6.2`).
 
 ## Arguments and interpreter options and arguments
 
@@ -117,7 +117,7 @@
 ~~~haskell
 #!/usr/bin/env stack
 {- stack script
-   --snapshot lts-23.0
+   --snapshot lts-23.14
    --
    +RTS -s -RTS
 -}
@@ -133,7 +133,7 @@
 is equivalent to the following command at the command line:
 
 ~~~text
-stack script --snapshot lts-23.0 -- MyScript.hs arg1 arg2 +RTS -s -RTS
+stack script --snapshot lts-23.14 -- MyScript.hs arg1 arg2 +RTS -s -RTS
 ~~~
 
 where `+RTS -s -RTS` are some of GHC's
@@ -162,7 +162,7 @@
 ~~~haskell
 #!/usr/bin/env stack
 {- stack script
-   --snapshot lts-23.0
+   --snapshot lts-23.14
    --package turtle
    --package "stm async"
    --package http-client,http-conduit
@@ -207,7 +207,7 @@
 
 ~~~haskell
 {- stack script
-   --snapshot lts-23.0
+   --snapshot lts-23.14
    --package acme-missiles
 -}
 import Acme.Missiles (launchMissiles)
@@ -218,7 +218,7 @@
 
 The command `stack --script-no-run-compile Script.hs` then behaves as if the
 command
-`stack script --snapshot lts-23.0 --package acme-missiles --no-run --compile -- Script.hs`
+`stack script --snapshot lts-23.14 --package acme-missiles --no-run --compile -- Script.hs`
 had been given. `Script.hs` is compiled (without optimisation) and the resulting
 executable is not run: no missiles are launched in the process!
 
@@ -260,7 +260,7 @@
 {- stack
   runghc
   --install-ghc
-  --snapshot lts-23.0
+  --snapshot lts-23.14
   --package base
   --package turtle
   --
@@ -284,7 +284,7 @@
 {- stack
    exec ghci
    --install-ghc
-   --snapshot lts-23.0
+   --snapshot lts-23.14
    --package turtle
 -}
 ~~~
diff --git a/doc/topics/snapshot_location.md b/doc/topics/snapshot_location.md
--- a/doc/topics/snapshot_location.md
+++ b/doc/topics/snapshot_location.md
@@ -39,25 +39,37 @@
 4.  Via a _convenience synonym_, which provides a short form for some common
     URLs. These are:
 
-    * GitHub: `github:user/repo:path` is treated as:
+    *   GitHub: `github:user/repo:path` is treated as:
 
         ~~~text
         https://raw.githubusercontent.com/user/repo/master/path
         ~~~
 
-    * LTS Haskell: `lts-X.Y` is treated (by default) as:
+    *   LTS Haskell: `lts-X.Y` is treated (by default) as:
 
         ~~~text
         github:commercialhaskell/stackage-snapshots:lts/X/Y.yaml
         ~~~
 
-    * Stackage Nightly: `nightly-YYYY-MM-DD` is treated (by default) as:
+        and, consequently, expands to:
 
         ~~~text
+        https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/X/Y.yaml
+        ~~~
+
+    *   Stackage Nightly: `nightly-YYYY-MM-DD` is treated (by default) as:
+
+        ~~~text
         github:commercialhaskell/stackage-snapshots:nightly/YYYY/M/D.yaml
         ~~~
 
-!!! info
+        and, consequently, expands to:
+
+        ~~~text
+        https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/nightly/YYYY/M/D.yaml
+        ~~~
+
+!!! info "Overriding the default snapshot location base"
 
     By default, LTS Haskell and Stackage Nightly snapshot configurations are
     retrieved from the `stackage-snapshots` GitHub repository of user
diff --git a/doc/topics/stack_root.md b/doc/topics/stack_root.md
--- a/doc/topics/stack_root.md
+++ b/doc/topics/stack_root.md
@@ -33,7 +33,7 @@
 
 === "Windows"
 
-    The default Stack root is `$Env:APPDIR\stack`.
+    The default Stack root is `$Env:APPDATA\stack`.
 
     If the `LOCALAPPDATA` environment variable exists, then the default location
     of tools is `$Env:LOCALAPPDATA\Programs\stack`. Otherwise, it is the
@@ -61,7 +61,7 @@
 
 === "Windows (Command Prompt)"
 
-    The default Stack root is `%APPDIR%\stack`.
+    The default Stack root is `%APPDATA%\stack`.
 
     If the `LOCALAPPDATA` environment variable exists, then the default location
     of tools is `%LOCALAPPDATA%\Programs\stack`. Otherwise, it is the `programs`
@@ -91,11 +91,11 @@
 
     The Stack root is `<XDG_DATA_HOME>/stack`. If the `XDG_DATA_HOME`
     environment variable does not exist, the default is `~/.local/share/stack`
-    on Unix-like operating systems and `%APPDIR%\stack` on Windows.
+    on Unix-like operating systems and `%APPDATA%\stack` on Windows.
 
     The location of `config.yaml` is `<XDG_CONFIG_HOME>/stack`. If the
     `XDG_CONFIG_HOME` environment variable does not exist, the default is
-    `~/.config/stack` on Unix-like operating systems and `%APPDIR%\stack` on
+    `~/.config/stack` on Unix-like operating systems and `%APPDATA%\stack` on
     Windows.
 
     This approach treats:
diff --git a/doc/topics/stack_yaml_vs_cabal_package_file.md b/doc/topics/stack_yaml_vs_cabal_package_file.md
--- a/doc/topics/stack_yaml_vs_cabal_package_file.md
+++ b/doc/topics/stack_yaml_vs_cabal_package_file.md
@@ -64,9 +64,9 @@
 
 The most common means by which this set of packages is defined is via a
 snapshot provided by Stackage. For example, if you go to the page
-<https://www.stackage.org/lts-23.0>, you will see a list of 3,189 packages at
-specific version numbers. When you then specify `snapshot: lts-23.0` or,
-alternatively, `resolver: lts-23.0`, you're telling Stack to use those package
+<https://www.stackage.org/lts-23.14>, you will see a list of 3,198 packages at
+specific version numbers. When you then specify `snapshot: lts-23.14` or,
+alternatively, `resolver: lts-23.14`, you're telling Stack to use those package
 versions in resolving dependencies down to specific versions of packages.
 
 Sometimes a snapshot doesn't have all of the packages that you want. Or you want
diff --git a/doc/tutorial/building_existing_projects.md b/doc/tutorial/building_existing_projects.md
--- a/doc/tutorial/building_existing_projects.md
+++ b/doc/tutorial/building_existing_projects.md
@@ -79,7 +79,7 @@
 ~~~
 
 Looking at `stack.yaml`, you will see that the excluded packages have been
-commented out under the `packages` field. In case wrong packages are excluded
+commented out under the `packages` key. In case wrong packages are excluded
 you can uncomment the right one and comment the other one.
 
 Packages may get excluded due to conflicting requirements among user packages or
@@ -123,7 +123,7 @@
 reading a Cabal file. You may want to pay attention to the warnings as sometimes
 they may result in incomprehensible errors later on during dependency solving.
 
-_Package naming_: If the `Name` field defined in a Cabal file does not match
+_Package naming_: If the `name` field defined in a Cabal file does not match
 with the Cabal file name then `stack init` will refuse to continue.
 
 _User warnings_: When packages are excluded or external dependencies added Stack
diff --git a/doc/tutorial/building_your_project.md b/doc/tutorial/building_your_project.md
--- a/doc/tutorial/building_your_project.md
+++ b/doc/tutorial/building_your_project.md
@@ -249,7 +249,7 @@
 mentioned the LTS snapshots, and you can get information about it at
 [https://www.stackage.org/lts](https://www.stackage.org/lts), including:
 
-* The appropriate value (`lts-23.0`, as is currently the latest LTS)
+* The appropriate value (`lts-23.14`, as is currently the latest LTS)
 * The GHC version used
 * A full list of all packages versions available in this snapshot
 * The ability to perform a Hoogle search on the packages in this snapshot
diff --git a/doc/tutorial/cabal_flags_and_ghc_options.md b/doc/tutorial/cabal_flags_and_ghc_options.md
--- a/doc/tutorial/cabal_flags_and_ghc_options.md
+++ b/doc/tutorial/cabal_flags_and_ghc_options.md
@@ -2,100 +2,135 @@
 
 # 10. Cabal flags and GHC options
 
-There are two common ways to alter how a package will install: with Cabal flags
-and with GHC options.
+There are two common ways to affect how a package will be built: with Cabal
+flags and with GHC options.
 
-## Cabal flag management
+## Cabal flags
 
-To change a Cabal flag setting, we can use the command line `--flag` option. The
-`yackage` package has an `upload` flag that is enabled by default. We can
-command:
+Cabal flags can be set or unset at the command line or as a project-specific
+Stack option.
 
+To set or unset a Cabal flag at the command line, we can use the `--flag`
+option. The `yackage` package has an `upload` flag that is enabled by default.
+We can command:
+
 ~~~text
 stack build --flag yackage:-upload
 ~~~
 
-This means: when compiling the `yackage` package, turn off the `upload` flag
-(thus the `-` in `-upload`). Unlike other tools, Stack is explicit about which
-package's flag you want to change. It does this for two reasons:
+This means: when compiling the `yackage` package, turn off the `upload` Cabal
+flag (thus the `-` in `-upload`). Unlike other tools, Stack is explicit about
+which package's flag you want to change. It does this for two reasons:
 
 1. There's no global meaning for Cabal flags, and therefore two packages can
    use the same flag name for completely different things.
+
 2. By following this approach, we can avoid unnecessarily recompiling snapshot
-   packages that happen to use a flag that we're using.
+   packages that happen to use a Cabal flag that we're using.
 
-You can also change flag values on the command line for extra-dep and snapshot
-packages. If you do this, that package will automatically be promoted to an
-extra-dep, since the build plan is different than what the plan snapshot
+You can also change Cabal flag values on the command line for extra-dep and
+snapshot packages. If you do this, that package will automatically be promoted
+to an extra-dep, since the build plan is different than what the plan snapshot
 definition would entail.
 
+If you have Cabal flags that you will be setting regularly when building your
+packages, you can add them to your Stack project-level configuration file
+(`stack.yaml`). For more information, see the
+[flags](../configure/yaml/project.md#flags) project-specific configuration
+option documentation.
+
 ## GHC options
 
-GHC options follow a similar logic as in managing Cabal flags, with a few
-nuances to adjust for common use cases. Let's consider the command:
+GHC options can be specified at the command line or as an non-project specific
+Stack option.
 
+At the command line, consider the command:
+
 ~~~text
 stack build --ghc-options="-Wall -Werror"
 ~~~
 
-This will set the `-Wall -Werror` options for all *local targets*. Note that
-this will not affect extra-dep and snapshot packages at all. This design
+or, equivalently:
+
+~~~text
+stack build --ghc-options=--pedantic
+~~~
+
+By default, this will set GHC's `-Wall` and `-Werror` options for all *project
+packages*. This will not, however, affect other packages at all. This design
 provides us with reproducible and fast builds.
 
-(By the way: the above GHC options have a special convenience flag:
-`--pedantic`.)
+??? question "Can GHC options for other packages be specified at the command line?"
 
-There's one extra nuance about command line GHC options: Since they only apply
-to local targets, if you change your local targets, they will no longer apply
-to other packages. Let's play around with an example from the `wai` repository,
-which includes the `wai` and `warp` packages, the latter depending on the
-former. If we command again:
+    Yes, GHC options can be specified at the command line for all packages or
+    only project packages that are targets. For further information, see the
+    documentation for the
+    [apply-ghc-options](../configure/yaml/non-project.md#apply-ghc-options)
+    non-project specific configuration option.
 
-~~~text
-stack build --ghc-options=-O0 wai
-~~~
+??? question "What if GHC options specified at the command line apply only to targets?"
 
-It will build all of the dependencies of `wai`, and then build `wai` with all
-optimizations disabled. Now let's add in `warp` as well. Command:
+    By changing the default using the
+    [apply-ghc-options](../configure/yaml/non-project.md#apply-ghc-options)
+    configuration option, it is possble to specify that GHC options at the
+    command line apply only to project packages that are *targets*. If this is
+    done and you change your targets, the options will no longer apply to other
+    project packages.
 
-~~~text
-stack build --ghc-options=-O0 wai warp
-~~~
+    Let us consider an example from the `wai` repository, which includes the
+    `wai` and `warp` packages, the latter depending on the former. If we
+    command:
 
-This builds the additional dependencies for `warp`, and then builds `warp` with
-optimizations disabled. Importantly: it does not rebuild `wai`, since `wai`'s
-configuration has not been altered. Now the surprising case. Command:
+    ~~~text
+    stack build --ghc-options=-O0 wai
+    ~~~
 
-~~~text
-stack build --ghc-options=-O0 warp
-wai-3.0.3.0-5a49351d03cba6cbaf906972d788e65d: unregistering (flags changed from ["--ghc-options","-O0"] to [])
-warp-3.1.3-a91c7c3108f63376877cb3cd5dbe8a7a: unregistering (missing dependencies: wai)
-wai-3.0.3.0: configure
-~~~
+    Stack will build all of the dependencies of `wai` (inclduding `warp`) and then
+    build `wai` with all GHC optimizations disabled.
 
-You may expect this to be a no-op: neither `wai` nor `warp` has changed.
-However, Stack will instead recompile `wai` with optimizations enabled again,
-and then rebuild `warp` (with optimizations disabled) against this newly built
-`wai`. The reason: reproducible builds. If we'd never built `wai` or `warp`
-before, trying to build `warp` would necessitate building all of its
-dependencies, and it would do so with default GHC options (optimizations
-enabled). This dependency would include `wai`. So when we command:
+    Now let's add `warp` as a target. If we command:
 
-~~~text
-stack build --ghc-options=-O0 warp
-~~~
+    ~~~text
+    stack build --ghc-options=-O0 wai warp
+    ~~~
 
-We want its behavior to be unaffected by any previous build steps we took.
-While this specific corner case does catch people by surprise, the overall goal
-of reproducible builds is - in the Stack maintainers' views - worth the
-confusion.
+    this builds the additional dependencies for `warp`, and then builds `warp`
+    with GHC optimizations disabled. Importantly, Stack does not rebuild `wai`,
+    since `wai`'s configuration has not been altered.
 
-Final point: if you have GHC options that you'll be regularly passing to your
-packages, you can add them to your `stack.yaml` file. For more information, see
-the [ghc-options](../configure/yaml/non-project.md#ghc-options) non-project
-specific configuration option documentation.
+    Now the surprising case. If we command:
 
-!!! note
+    ~~~text
+    stack build --ghc-options=-O0 warp
+    ~~~
 
-    That's it, the heavy content of this guide is done! Everything from here on
-    out is simple explanations of commands. Congratulations!
+    you may expect this to do nothing, as neither `wai` nor `warp` has changed.
+    However, Stack will rebuild `wai` with GHC optimizations enabled again, and
+    then rebuild `warp` (with optimizations disabled) against this newly-built
+    `wai`. The reason is reproducible builds. If we had never built `wai` or
+    `warp` before, trying to build `warp` would require building all of its
+    dependencies, and it would do so with default GHC options (that is, GHC
+    optimizations enabled). These dependencies would include `wai`. So when we
+    command:
+
+    ~~~text
+    stack build --ghc-options=-O0 warp
+    ~~~
+
+    we want Stack's behavior to be unaffected by any previous build steps we
+    took.
+
+If you have GHC options that you will be applying regularly when building your
+packages, you can add them to your Stack project-level configuration file
+(`stack.yaml`) or (if applicable) to a
+[global Stack configuration file](../configure/yaml/index.md#project-level-and-global-configuration-files).
+For more information, see the
+[ghc-options](../configure/yaml/non-project.md#ghc-options) non-project specific
+configuration option documentation.
+
+??? question "Can Stack be configured to specify GHC options for specific packages?"
+
+    Yes, Stack can be configured to specify GHC options for specific packages,
+    either globally or at the project level. For more information, see the
+    [ghc-options](../configure/yaml/non-project.md#ghc-options) non-project
+    specific configuration option documentation.
diff --git a/doc/tutorial/executing_commands.md b/doc/tutorial/executing_commands.md
--- a/doc/tutorial/executing_commands.md
+++ b/doc/tutorial/executing_commands.md
@@ -33,7 +33,7 @@
 ~~~text
 Writing the configuration file for the implicit global project to:
 .../global-project/stack.yaml. Note: You can change the snapshot via the
-snapshot field there.
+snapshot key there.
 Using the latest snapshot lts-22.31.
 I installed the stm package via --package stm
 ~~~
diff --git a/doc/tutorial/package_description.md b/doc/tutorial/package_description.md
--- a/doc/tutorial/package_description.md
+++ b/doc/tutorial/package_description.md
@@ -136,8 +136,9 @@
 
     A `package.yaml` file can be more concise and less repetitive than the Cabal
     file that is generated from it. That is because the Hpack format uses
-    defaults and top-level fields common to sections. The YAML syntax, which may
-    already be familiar for some users, can also avoid repetition.
+    defaults and top-level keys common to other parts of the format. The YAML
+    syntax, which may already be familiar for some users, can also avoid
+    repetition.
 
     In particular, the format's defaults can infer the names of exposed and
     other modules.
@@ -176,7 +177,7 @@
     # The email address to contact the maintainer of the package (optional):
     maintainer:          "example@example.com"
     # The copyright for the package's files (optional):
-    copyright:           "2024 Author name here"
+    copyright:           "2025 Author name here"
 
     # Extra files (if any) to be distributed with the source files of the
     # package:
@@ -315,7 +316,7 @@
     -- The email address to contact the maintainer of the package:
     maintainer:     example@example.com
     -- The copyright for the package's files:
-    copyright:      2024 Author name here
+    copyright:      2025 Author name here
     -- The licence for the use of the package's files:
     license:        BSD-3-Clause
     -- The file documenting the terms of the licence:
diff --git a/doc/tutorial/project_configuration.md b/doc/tutorial/project_configuration.md
--- a/doc/tutorial/project_configuration.md
+++ b/doc/tutorial/project_configuration.md
@@ -25,15 +25,17 @@
 ## `stack.yaml`
 
 Stack requires a Stack project-level configuration file for every project.
-`stack.yaml` is that file. The contents of the file define project-specific
-options and non-project-specific options that apply to the project.
+`stack.yaml` is that file. The contents of the file set project-specific and
+non-project-specific options that apply to the project. (Non-project
+specific options that affect the project may also be set in a
+[global Stack configuration file](../configure/yaml/index.md#project-level-and-global-configuration-files).)
 
-The contents of the file include comments beginning `#`. Ignoring those
-comments, the contents will look something like this:
+The contents of the `stack.yaml` file include comments beginning `#`. Ignoring
+those comments, the contents will look something like this:
 
 ~~~yaml
 snapshot:
-  url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/23/0.yaml
+  url: https://raw.githubusercontent.com/commercialhaskell/stackage-snapshots/master/lts/23/8.yaml
 packages:
 - .
 ~~~
@@ -42,7 +44,7 @@
 project-specific configuration option. Its value tells Stack *how* to build your
 package: which GHC version to use, which versions of package dependencies to
 use, and so on. Our value here says to use
-[LTS Haskell 23.0](https://www.stackage.org/lts-23.0), which implies GHC 9.8.4
+[LTS Haskell 23.14](https://www.stackage.org/lts-23.14), which implies GHC 9.8.4
 (which is why `stack build` installs that version of GHC if it is not already
 available to Stack). There are a number of values you can use for `snapshot`,
 which we'll cover later.
diff --git a/src/GHC/Utils/GhcPkg/Main/Compat.hs b/src/GHC/Utils/GhcPkg/Main/Compat.hs
--- a/src/GHC/Utils/GhcPkg/Main/Compat.hs
+++ b/src/GHC/Utils/GhcPkg/Main/Compat.hs
@@ -554,13 +554,14 @@
  where
   -- Update a list of 'packages by package database' for a package. Assumes that
   -- a package to be unregistered is in no more than one database.
-  getPkgsByPkgDBs :: [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
-                  -- ^ List of considered 'packages by package database'
-                  -> [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
-                  -- ^ List of to be considered 'packages by package database'
-                  -> PackageArg
-                  -- Package to update
-                  -> RIO env [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
+  getPkgsByPkgDBs ::
+       [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
+       -- ^ List of considered 'packages by package database'
+    -> [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
+       -- ^ List of to be considered 'packages by package database'
+    -> PackageArg
+       -- Package to update
+    -> RIO env [(PackageDB GhcPkg.DbReadWrite, [UnitId])]
   -- No more 'packages by package database' to consider? We need to try to get
   -- another package database.
   getPkgsByPkgDBs pkgsByPkgDBs [] pkgarg =
diff --git a/src/Network/HTTP/StackClient.hs b/src/Network/HTTP/StackClient.hs
--- a/src/Network/HTTP/StackClient.hs
+++ b/src/Network/HTTP/StackClient.hs
@@ -153,19 +153,24 @@
 -- appropriate destination.
 --
 -- Throws an exception if things go wrong
-download :: HasTerm env
-         => Request
-         -> Path Abs File -- ^ destination
-         -> RIO env Bool -- ^ Was a downloaded performed (True) or did the file already exist (False)?
+download ::
+     HasTerm env
+  => Request
+  -> Path Abs File
+     -- ^ destination
+  -> RIO env Bool
+     -- ^ Was a downloaded performed (True) or did the file already exist
+     -- (False)?
 download req = Download.download (setUserAgent req)
 
 -- | Same as 'download', but will download a file a second time if it is already present.
 --
 -- Returns 'True' if the file was downloaded, 'False' otherwise
-redownload :: HasTerm env
-           => Request
-           -> Path Abs File -- ^ destination
-           -> RIO env Bool
+redownload ::
+     HasTerm env
+  => Request
+  -> Path Abs File -- ^ destination
+  -> RIO env Bool
 redownload req = Download.redownload (setUserAgent req)
 
 -- | Copied and extended version of Network.HTTP.Download.download.
diff --git a/src/Path/Find.hs b/src/Path/Find.hs
--- a/src/Path/Find.hs
+++ b/src/Path/Find.hs
@@ -19,33 +19,36 @@
                    ( getSymbolicLinkStatus, isSymbolicLink )
 
 -- | Find the location of a file matching the given predicate.
-findFileUp :: (MonadIO m, MonadThrow m)
-           => Path Abs Dir              -- ^ Start here.
-           -> (Path Abs File -> Bool)   -- ^ Predicate to match the file.
-           -> Maybe (Path Abs Dir)      -- ^ Do not ascend above this directory.
-           -> m (Maybe (Path Abs File)) -- ^ Absolute file path.
+findFileUp ::
+     (MonadIO m, MonadThrow m)
+  => Path Abs Dir              -- ^ Start here.
+  -> (Path Abs File -> Bool)   -- ^ Predicate to match the file.
+  -> Maybe (Path Abs Dir)      -- ^ Do not ascend above this directory.
+  -> m (Maybe (Path Abs File)) -- ^ Absolute file path.
 findFileUp = findPathUp snd
 
 -- | Find the location of a directory matching the given predicate.
-findDirUp :: (MonadIO m,MonadThrow m)
-          => Path Abs Dir               -- ^ Start here.
-          -> (Path Abs Dir -> Bool)     -- ^ Predicate to match the directory.
-          -> Maybe (Path Abs Dir)       -- ^ Do not ascend above this directory.
-          -> m (Maybe (Path Abs Dir))   -- ^ Absolute directory path.
+findDirUp ::
+     (MonadIO m,MonadThrow m)
+  => Path Abs Dir               -- ^ Start here.
+  -> (Path Abs Dir -> Bool)     -- ^ Predicate to match the directory.
+  -> Maybe (Path Abs Dir)       -- ^ Do not ascend above this directory.
+  -> m (Maybe (Path Abs Dir))   -- ^ Absolute directory path.
 findDirUp = findPathUp fst
 
 -- | Find the location of a path matching the given predicate.
-findPathUp :: (MonadIO m,MonadThrow m)
-           => (([Path Abs Dir],[Path Abs File]) -> [Path Abs t])
-              -- ^ Choose path type from pair.
-           -> Path Abs Dir
-              -- ^ Start here.
-           -> (Path Abs t -> Bool)
-              -- ^ Predicate to match the path.
-           -> Maybe (Path Abs Dir)
-              -- ^ Do not ascend above this directory.
-           -> m (Maybe (Path Abs t))
-              -- ^ Absolute path.
+findPathUp ::
+     (MonadIO m,MonadThrow m)
+  => (([Path Abs Dir],[Path Abs File]) -> [Path Abs t])
+     -- ^ Choose path type from pair.
+  -> Path Abs Dir
+     -- ^ Start here.
+  -> (Path Abs t -> Bool)
+     -- ^ Predicate to match the path.
+  -> Maybe (Path Abs Dir)
+     -- ^ Do not ascend above this directory.
+  -> m (Maybe (Path Abs t))
+     -- ^ Absolute path.
 findPathUp pathType dir p upperBound = do
   entries <- listDir dir
   case L.find p (pathType entries) of
@@ -61,14 +64,15 @@
 --
 -- TODO: write one of these that traverses symbolic links but
 -- efficiently ignores loops.
-findFiles :: Path Abs Dir
-             -- ^ Root directory to begin with.
-          -> (Path Abs File -> Bool)
-             -- ^ Predicate to match files.
-          -> (Path Abs Dir -> Bool)
-             -- ^ Predicate for which directories to traverse.
-          -> IO [Path Abs File]
-             -- ^ List of matching files.
+findFiles ::
+     Path Abs Dir
+     -- ^ Root directory to begin with.
+  -> (Path Abs File -> Bool)
+     -- ^ Predicate to match files.
+  -> (Path Abs Dir -> Bool)
+     -- ^ Predicate for which directories to traverse.
+  -> IO [Path Abs File]
+     -- ^ List of matching files.
 findFiles dir p traversep = do
   (dirs,files) <- catchJust (\ e -> if isPermissionError e
                                       then Just ()
diff --git a/src/Stack/Build/Cache.hs b/src/Stack/Build/Cache.hs
--- a/src/Stack/Build/Cache.hs
+++ b/src/Stack/Build/Cache.hs
@@ -82,14 +82,18 @@
                    ( getFileStatus, modificationTime, setFileTimes )
 
 -- | Directory containing files to mark an executable as installed
-exeInstalledDir :: (HasEnvConfig env)
-                => InstallLocation -> RIO env (Path Abs Dir)
+exeInstalledDir ::
+     (HasEnvConfig env)
+  => InstallLocation
+  -> RIO env (Path Abs Dir)
 exeInstalledDir Snap = (</> relDirInstalledPackages) <$> installationRootDeps
 exeInstalledDir Local = (</> relDirInstalledPackages) <$> installationRootLocal
 
 -- | Get all of the installed executables
-getInstalledExes :: (HasEnvConfig env)
-                 => InstallLocation -> RIO env [PackageIdentifier]
+getInstalledExes ::
+     (HasEnvConfig env)
+  => InstallLocation
+  -> RIO env [PackageIdentifier]
 getInstalledExes loc = do
   dir <- exeInstalledDir loc
   (_, files) <- liftIO $ handleIO (const $ pure ([], [])) $ listDir dir
@@ -104,8 +108,11 @@
     mapMaybe (parsePackageIdentifier . toFilePath . filename) files
 
 -- | Mark the given executable as installed
-markExeInstalled :: (HasEnvConfig env)
-                 => InstallLocation -> PackageIdentifier -> RIO env ()
+markExeInstalled ::
+     (HasEnvConfig env)
+  => InstallLocation
+  -> PackageIdentifier
+  -> RIO env ()
 markExeInstalled loc ident = do
   dir <- exeInstalledDir loc
   ensureDir dir
@@ -121,17 +128,21 @@
   writeBinaryFileAtomic fp "Installed"
 
 -- | Mark the given executable as not installed
-markExeNotInstalled :: (HasEnvConfig env)
-                    => InstallLocation -> PackageIdentifier -> RIO env ()
+markExeNotInstalled ::
+     (HasEnvConfig env)
+  => InstallLocation
+  -> PackageIdentifier
+  -> RIO env ()
 markExeNotInstalled loc ident = do
   dir <- exeInstalledDir loc
   ident' <- parseRelFile $ packageIdentifierString ident
   liftIO $ ignoringAbsence (removeFile $ dir </> ident')
 
-buildCacheFile :: (HasEnvConfig env, MonadReader env m, MonadThrow m)
-               => Path Abs Dir
-               -> NamedComponent
-               -> m (Path Abs File)
+buildCacheFile ::
+     (HasEnvConfig env, MonadReader env m, MonadThrow m)
+  => Path Abs Dir
+  -> NamedComponent
+  -> m (Path Abs File)
 buildCacheFile dir component = do
   cachesDir <- buildCachesDir dir
   smh <- view $ envConfigL . to (.sourceMapHash)
@@ -140,10 +151,11 @@
   pure $ cachesDir </> smDirName </> cacheFileName
 
 -- | Try to read the dirtiness cache for the given package directory.
-tryGetBuildCache :: HasEnvConfig env
-                 => Path Abs Dir
-                 -> NamedComponent
-                 -> RIO env (Maybe (Map FilePath FileCacheInfo))
+tryGetBuildCache ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> NamedComponent
+  -> RIO env (Maybe (Map FilePath FileCacheInfo))
 tryGetBuildCache dir component = do
   fp <- buildCacheFile dir component
   ensureDir $ parent fp
@@ -197,27 +209,30 @@
     tryIO (readFileBinary fp)
 
 -- | Write the dirtiness cache for this package's files.
-writeBuildCache :: HasEnvConfig env
-                => Path Abs Dir
-                -> NamedComponent
-                -> Map FilePath FileCacheInfo -> RIO env ()
+writeBuildCache ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> NamedComponent
+  -> Map FilePath FileCacheInfo -> RIO env ()
 writeBuildCache dir component times = do
   fp <- toFilePath <$> buildCacheFile dir component
   liftIO $ Yaml.encodeFile fp BuildCache { times = times }
 
 -- | Write the dirtiness cache for this package's configuration.
-writeConfigCache :: HasEnvConfig env
-                => Path Abs Dir
-                -> ConfigCache
-                -> RIO env ()
+writeConfigCache ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> ConfigCache
+  -> RIO env ()
 writeConfigCache dir =
   saveConfigCache (configCacheKey dir ConfigCacheTypeConfig)
 
 -- | See 'tryGetCabalMod'
-writeCabalMod :: HasEnvConfig env
-              => Path Abs Dir
-              -> CTime
-              -> RIO env ()
+writeCabalMod ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> CTime
+  -> RIO env ()
 writeCabalMod dir x = do
   fp <- configCabalMod dir
   writeBinaryFileAtomic fp "Just used for its modification time"
@@ -267,17 +282,19 @@
       configCacheKey installationRoot (ConfigCacheTypeFlagExecutable ident)
 
 -- | Loads the flag cache for the given installed extra-deps
-tryGetFlagCache :: HasEnvConfig env
-                => Installed
-                -> RIO env (Maybe ConfigCache)
+tryGetFlagCache ::
+     HasEnvConfig env
+  => Installed
+  -> RIO env (Maybe ConfigCache)
 tryGetFlagCache gid = do
   key <- flagCacheKey gid
   loadConfigCache key
 
-writeFlagCache :: HasEnvConfig env
-               => Installed
-               -> ConfigCache
-               -> RIO env ()
+writeFlagCache ::
+     HasEnvConfig env
+  => Installed
+  -> ConfigCache
+  -> RIO env ()
 writeFlagCache gid cache = do
   key <- flagCacheKey gid
   saveConfigCache key cache
@@ -294,10 +311,11 @@
   | TSUnknown
 
 -- | Mark test suite status
-setTestStatus :: HasEnvConfig env
-              => Path Abs Dir
-              -> TestStatus
-              -> RIO env ()
+setTestStatus ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> TestStatus
+  -> RIO env ()
 setTestStatus dir status = do
   fp <- testSuccessFile dir
   writeBinaryFileAtomic fp $
@@ -307,9 +325,10 @@
       TSUnknown -> unknownBS
 
 -- | Check if the test suite already passed
-getTestStatus :: HasEnvConfig env
-              => Path Abs Dir
-              -> RIO env TestStatus
+getTestStatus ::
+     HasEnvConfig env
+  => Path Abs Dir
+  -> RIO env TestStatus
 getTestStatus dir = do
   fp <- testSuccessFile dir
   -- we could ensure the file is the right size first, but we're not expected an
diff --git a/src/Stack/Build/ConstructPlan.hs b/src/Stack/Build/ConstructPlan.hs
--- a/src/Stack/Build/ConstructPlan.hs
+++ b/src/Stack/Build/ConstructPlan.hs
@@ -621,10 +621,11 @@
 
 -- | Given a 'PackageSource' and perhaps an 'Installed' value, adds build
 -- 'Task's for the package and its dependencies.
-installPackage :: PackageName
-               -> PackageSource
-               -> Maybe Installed
-               -> M (Either ConstructPlanException AddDepRes)
+installPackage ::
+     PackageName
+  -> PackageSource
+  -> Maybe Installed
+  -> M (Either ConstructPlanException AddDepRes)
 installPackage name ps minstalled = do
   ctx <- ask
   case ps of
@@ -1234,9 +1235,10 @@
 -- value; and (2) a pair of an 'InstallLocation' value and an 'Installed' value.
 -- Checks that the version of the 'PackageSource' value and the version of the
 -- `Installed` value are the same.
-combineSourceInstalled :: PackageSource
-                       -> (InstallLocation, Installed)
-                       -> PackageInfo
+combineSourceInstalled ::
+     PackageSource
+  -> (InstallLocation, Installed)
+  -> PackageInfo
 combineSourceInstalled ps (location, installed) =
   assert (psVersion ps == installedVersion installed) $
     case location of
diff --git a/src/Stack/Build/Execute.hs b/src/Stack/Build/Execute.hs
--- a/src/Stack/Build/Execute.hs
+++ b/src/Stack/Build/Execute.hs
@@ -226,17 +226,21 @@
   missing = task.configOpts.missing
 
 -- | Perform the actual plan
-executePlan :: HasEnvConfig env
-            => BuildOptsCLI
-            -> BaseConfigOpts
-            -> [LocalPackage]
-            -> [DumpPackage] -- ^ global packages
-            -> [DumpPackage] -- ^ snapshot packages
-            -> [DumpPackage] -- ^ project packages and local extra-deps
-            -> InstalledMap
-            -> Map PackageName Target
-            -> Plan
-            -> RIO env ()
+executePlan ::
+     HasEnvConfig env
+  => BuildOptsCLI
+  -> BaseConfigOpts
+  -> [LocalPackage]
+  -> [DumpPackage]
+     -- ^ global packages
+  -> [DumpPackage]
+     -- ^ snapshot packages
+  -> [DumpPackage]
+     -- ^ project packages and local extra-deps
+  -> InstalledMap
+  -> Map PackageName Target
+  -> Plan
+  -> RIO env ()
 executePlan
     boptsCli
     baseConfigOpts
@@ -281,9 +285,9 @@
     Map.keysSet plan.tasks <> Map.keysSet plan.finals
 
 copyExecutables ::
-       HasEnvConfig env
-    => Map StackUnqualCompName InstallLocation
-    -> RIO env ()
+     HasEnvConfig env
+  => Map StackUnqualCompName InstallLocation
+  -> RIO env ()
 copyExecutables exes | Map.null exes = pure ()
 copyExecutables exes = do
   snapBin <- (</> bindirSuffix) <$> installationRootDeps
@@ -360,12 +364,13 @@
   old = dest ++ ".old"
 
 -- | Perform the actual plan (internal)
-executePlan' :: HasEnvConfig env
-             => InstalledMap
-             -> Map PackageName Target
-             -> Plan
-             -> ExecuteEnv
-             -> RIO env ()
+executePlan' ::
+     HasEnvConfig env
+  => InstalledMap
+  -> Map PackageName Target
+  -> Plan
+  -> ExecuteEnv
+  -> RIO env ()
 executePlan' installedMap0 targets plan ee = do
   config <- view configL
   let !buildOpts = ee.buildOpts
@@ -556,13 +561,14 @@
 
     _ -> for_ ids . unregisterSinglePkg $ \ident _gid -> Left ident
 
-toActions :: HasEnvConfig env
-          => InstalledMap
-          -> Maybe (MVar ())
-          -> (RIO env () -> IO ())
-          -> ExecuteEnv
-          -> (Maybe Task, Maybe Task) -- build and final
-          -> [Action]
+toActions ::
+     HasEnvConfig env
+  => InstalledMap
+  -> Maybe (MVar ())
+  -> (RIO env () -> IO ())
+  -> ExecuteEnv
+  -> (Maybe Task, Maybe Task) -- build and final
+  -> [Action]
 toActions installedMap mtestLock runInBase ee (mbuild, mfinal) =
   abuild ++ afinal
  where
diff --git a/src/Stack/Build/ExecuteEnv.hs b/src/Stack/Build/ExecuteEnv.hs
--- a/src/Stack/Build/ExecuteEnv.hs
+++ b/src/Stack/Build/ExecuteEnv.hs
@@ -183,11 +183,15 @@
   <> simpleSetupCode
 
 -- | Get a compiled Setup exe
-getSetupExe :: HasEnvConfig env
-            => Path Abs File -- ^ Setup.hs input file
-            -> Path Abs File -- ^ SetupShim.hs input file
-            -> Path Abs Dir -- ^ temporary directory
-            -> RIO env (Maybe (Path Abs File))
+getSetupExe ::
+     HasEnvConfig env
+  => Path Abs File
+     -- ^ Setup.hs input file
+  -> Path Abs File
+     -- ^ SetupShim.hs input file
+  -> Path Abs Dir
+     -- ^ temporary directory
+  -> RIO env (Maybe (Path Abs File))
 getSetupExe setupHs setupShimHs tmpdir = do
   wc <- view $ actualCompilerVersionL . whichCompilerL
   platformDir <- platformGhcRelDir
@@ -253,10 +257,14 @@
   -> BuildOptsCLI
   -> BaseConfigOpts
   -> [LocalPackage]
-  -> [DumpPackage] -- ^ global packages
-  -> [DumpPackage] -- ^ snapshot packages
-  -> [DumpPackage] -- ^ project packages and local extra-deps
-  -> Maybe Int -- ^ largest package name, for nicer interleaved output
+  -> [DumpPackage]
+     -- ^ global packages
+  -> [DumpPackage]
+     -- ^ snapshot packages
+  -> [DumpPackage]
+     -- ^ project packages and local extra-deps
+  -> Maybe Int
+     -- ^ largest package name, for nicer interleaved output
   -> (ExecuteEnv -> RIO env a)
   -> RIO env a
 withExecuteEnv
@@ -488,8 +496,10 @@
 -- <https://github.com/commercialhaskell/stack/issues/2730>
 withLockedDistDir ::
      forall env a. HasEnvConfig env
-  => (StyleDoc -> RIO env ()) -- ^ A pretty announce function
-  -> Path Abs Dir -- ^ root directory for package
+  => (StyleDoc -> RIO env ())
+     -- ^ A pretty announce function
+  -> Path Abs Dir
+     -- ^ root directory for package
   -> RIO env a
   -> RIO env a
 withLockedDistDir announce root inner = do
@@ -729,13 +739,21 @@
               -- providing an explicit list of dependencies, and we should
               -- simply use all of them.
               Just customSetupDeps -> do
-                unless (Map.member (mkPackageName "Cabal") customSetupDeps) $
-                  prettyWarnL
-                    [ fromPackageName package.name
-                    , flow "has a setup-depends field, but it does not mention \
-                           \a Cabal dependency. This is likely to cause build \
-                           \errors."
-                    ]
+                cabalPackageArg' <-
+                  if Map.member (mkPackageName "Cabal") customSetupDeps
+                    then pure []
+                    else do
+                      prettyWarnL
+                        [ style Current (fromPackageName package.name)
+                        , flow "has a"
+                        , style Shell "setup-depends"
+                        , flow "field, but it does not mention a"
+                        , style Current "Cabal"
+                        , flow "dependency. Stack customizes setup using \
+                               \Cabal, so it has added the GHC boot package as \
+                               \a dependency."
+                        ]
+                      pure cabalPackageArg
                 matchedDeps <-
                   forM (Map.toList customSetupDeps) $ \(name, depValue) -> do
                     let matches (PackageIdentifier name' version) =
@@ -772,7 +790,7 @@
                           )
                       )
                   )
-                pure (packageDBArgs ++ depsArgs ++ cppArgs)
+                pure (packageDBArgs ++ depsArgs ++ cabalPackageArg' ++ cppArgs)
 
               -- This branch is usually taken for builds, and is always taken
               -- for `stack sdist`.
@@ -930,10 +948,14 @@
 -- | Strip Template Haskell "Loading package" lines and making paths absolute.
 mungeBuildOutput ::
      forall m. (MonadIO m, MonadUnliftIO m)
-  => ExcludeTHLoading       -- ^ exclude TH loading?
-  -> ConvertPathsToAbsolute -- ^ convert paths to absolute?
-  -> Path Abs Dir           -- ^ package's root directory
-  -> ActualCompiler         -- ^ compiler we're building with
+  => ExcludeTHLoading
+     -- ^ exclude TH loading?
+  -> ConvertPathsToAbsolute
+     -- ^ convert paths to absolute?
+  -> Path Abs Dir
+     -- ^ package's root directory
+  -> ActualCompiler
+     -- ^ compiler we're building with
   -> ConduitM Text Text m ()
 mungeBuildOutput excludeTHLoading makeAbsolute pkgDir compilerVer = void $
   CT.lines
@@ -1032,8 +1054,9 @@
 
 -- | Find the Setup.hs or Setup.lhs in the given directory. If none exists,
 -- throw an exception.
-getSetupHs :: Path Abs Dir -- ^ project directory
-           -> IO (Path Abs File)
+getSetupHs ::
+     Path Abs Dir -- ^ project directory
+  -> IO (Path Abs File)
 getSetupHs dir = do
   exists1 <- doesFileExist fp1
   if exists1
diff --git a/src/Stack/Build/ExecutePackage.hs b/src/Stack/Build/ExecutePackage.hs
--- a/src/Stack/Build/ExecutePackage.hs
+++ b/src/Stack/Build/ExecutePackage.hs
@@ -214,15 +214,21 @@
   pure (allDepsMap, cache)
 
 -- | Ensure that the configuration for the package matches what is given
-ensureConfig :: HasEnvConfig env
-             => ConfigCache -- ^ newConfigCache
-             -> Path Abs Dir -- ^ package directory
-             -> BuildOpts
-             -> RIO env () -- ^ announce
-             -> (ExcludeTHLoading -> [String] -> RIO env ()) -- ^ cabal
-             -> Path Abs File -- ^ Cabal file
-             -> Task
-             -> RIO env Bool
+ensureConfig ::
+     HasEnvConfig env
+  => ConfigCache
+     -- ^ newConfigCache
+  -> Path Abs Dir
+     -- ^ package directory
+  -> BuildOpts
+  -> RIO env ()
+     -- ^ announce
+  -> (ExcludeTHLoading -> [String] -> RIO env ())
+     -- ^ cabal
+  -> Path Abs File
+     -- ^ Cabal file
+  -> Task
+  -> RIO env Bool
 ensureConfig newConfigCache pkgDir buildOpts announce cabal cabalFP task = do
   newCabalMod <-
     liftIO $ modificationTime <$> getFileStatus (toFilePath cabalFP)
@@ -355,13 +361,15 @@
 --   local install directory. Note that this is literally invoking Cabal
 --   with @copy@, and not the copying done by @stack install@ - that is
 --   handled by 'copyExecutables'.
-singleBuild :: forall env. (HasEnvConfig env, HasRunner env)
-            => ActionContext
-            -> ExecuteEnv
-            -> Task
-            -> InstalledMap
-            -> Bool             -- ^ Is this a final build?
-            -> RIO env ()
+singleBuild ::
+     forall env. (HasEnvConfig env, HasRunner env)
+  => ActionContext
+  -> ExecuteEnv
+  -> Task
+  -> InstalledMap
+  -> Bool
+     -- ^ Is this a final build?
+  -> RIO env ()
 singleBuild
     ac
     ee
@@ -958,14 +966,15 @@
 
 -- | Implements running a package's tests. Also handles producing
 -- coverage reports if coverage is enabled.
-singleTest :: HasEnvConfig env
-           => TestOpts
-           -> [StackUnqualCompName]
-           -> ActionContext
-           -> ExecuteEnv
-           -> Task
-           -> InstalledMap
-           -> RIO env ()
+singleTest ::
+     HasEnvConfig env
+  => TestOpts
+  -> [StackUnqualCompName]
+  -> ActionContext
+  -> ExecuteEnv
+  -> Task
+  -> InstalledMap
+  -> RIO env ()
 singleTest topts testsToRun ac ee task installedMap = do
   -- FIXME: Since this doesn't use cabal, we should be able to avoid using a
   -- full blown 'withSingleContext'.
@@ -1242,14 +1251,15 @@
         setTestStatus pkgDir $ if succeeded then TSSuccess else TSFailure
 
 -- | Implements running a package's benchmarks.
-singleBench :: HasEnvConfig env
-            => BenchmarkOpts
-            -> [StackUnqualCompName]
-            -> ActionContext
-            -> ExecuteEnv
-            -> Task
-            -> InstalledMap
-            -> RIO env ()
+singleBench ::
+     HasEnvConfig env
+  => BenchmarkOpts
+  -> [StackUnqualCompName]
+  -> ActionContext
+  -> ExecuteEnv
+  -> Task
+  -> InstalledMap
+  -> RIO env ()
 singleBench beopts benchesToRun ac ee task installedMap = do
   (allDepsMap, _cache) <- getConfigCache ee task installedMap False True
   withSingleContext ac ee task.taskType allDepsMap (Just "bench") $
diff --git a/src/Stack/Build/Haddock.hs b/src/Stack/Build/Haddock.hs
--- a/src/Stack/Build/Haddock.hs
+++ b/src/Stack/Build/Haddock.hs
@@ -371,9 +371,10 @@
     destHtmlAbsDir = parent opts.destInterfaceFile
 
 -- | Find first DumpPackage matching the GhcPkgId
-lookupDumpPackage :: GhcPkgId
-                  -> [Map GhcPkgId DumpPackage]
-                  -> Maybe DumpPackage
+lookupDumpPackage ::
+     GhcPkgId
+  -> [Map GhcPkgId DumpPackage]
+  -> Maybe DumpPackage
 lookupDumpPackage ghcPkgId dumpPkgs =
   listToMaybe $ mapMaybe (Map.lookup ghcPkgId) dumpPkgs
 
diff --git a/src/Stack/Build/Installed.hs b/src/Stack/Build/Installed.hs
--- a/src/Stack/Build/Installed.hs
+++ b/src/Stack/Build/Installed.hs
@@ -53,14 +53,15 @@
   pure $ projectInstalls <> depInstalls
 
 -- | Returns the new InstalledMap and all of the locally registered packages.
-getInstalled :: HasEnvConfig env
-             => InstallMap -- ^ does not contain any installed information
-             -> RIO env
-                  ( InstalledMap
-                  , [DumpPackage] -- globally installed
-                  , [DumpPackage] -- snapshot installed
-                  , [DumpPackage] -- locally installed
-                  )
+getInstalled ::
+     HasEnvConfig env
+  => InstallMap -- ^ does not contain any installed information
+  -> RIO env
+       ( InstalledMap
+       , [DumpPackage] -- globally installed
+       , [DumpPackage] -- snapshot installed
+       , [DumpPackage] -- locally installed
+       )
 getInstalled {-opts-} installMap = do
   logDebug "Finding out which packages are already installed"
   snapDBPath <- packageDatabaseDeps
diff --git a/src/Stack/Build/Source.hs b/src/Stack/Build/Source.hs
--- a/src/Stack/Build/Source.hs
+++ b/src/Stack/Build/Source.hs
@@ -491,10 +491,11 @@
       fileDigests
       oldCache
  where
-  go :: FilePath
-     -> Maybe SHA256
-     -> Maybe FileCacheInfo
-     -> RIO env (Set FilePath, Map FilePath FileCacheInfo)
+  go ::
+       FilePath
+    -> Maybe SHA256
+    -> Maybe FileCacheInfo
+    -> RIO env (Set FilePath, Map FilePath FileCacheInfo)
   -- Filter out the cabal_macros file to avoid spurious recompilations
   go fp _ _ | takeFileName fp == "cabal_macros.h" = pure (Set.empty, Map.empty)
   -- Common case where it's in the cache and on the filesystem.
diff --git a/src/Stack/Build/Target.hs b/src/Stack/Build/Target.hs
--- a/src/Stack/Build/Target.hs
+++ b/src/Stack/Build/Target.hs
@@ -148,11 +148,12 @@
   deriving (Eq, Show)
 
 -- | Same as @parseRawTarget@, but also takes directories into account.
-parseRawTargetDirs :: MonadIO m
-                   => Path Abs Dir -- ^ current directory
-                   -> Map PackageName ProjectPackage
-                   -> RawInput -- ^ raw target information from the commandline
-                   -> m (Either StyleDoc [(RawInput, RawTarget)])
+parseRawTargetDirs ::
+     MonadIO m
+  => Path Abs Dir -- ^ current directory
+  -> Map PackageName ProjectPackage
+  -> RawInput -- ^ raw target information from the commandline
+  -> m (Either StyleDoc [(RawInput, RawTarget)])
 parseRawTargetDirs root locals ri =
   case parseRawTarget t of
     Just rt -> pure $ Right [(ri, rt)]
diff --git a/src/Stack/BuildPlan.hs b/src/Stack/BuildPlan.hs
--- a/src/Stack/BuildPlan.hs
+++ b/src/Stack/BuildPlan.hs
@@ -91,7 +91,7 @@
           , case mapMaybe goRecommend $ Map.toList unknown of
               [] -> []
               rec ->
-                  ("Recommended action: modify the extra-deps field of " ++
+                  ("Recommended action: modify the value of the extra-deps key of " ++
                   toFilePath stackYaml ++
                   " to include the following:")
                   : (rec
@@ -125,7 +125,7 @@
       | otherwise = concat
           [ ["The following packages are shadowed by project packages:"]
           , map go (Map.toList shadowed)
-          , ["Recommended action: modify the extra-deps field of " ++
+          , ["Recommended action: modify the value of the extra-deps key of " ++
              toFilePath stackYaml ++
              " to include the following:"]
           , extraDeps
diff --git a/src/Stack/CLI.hs b/src/Stack/CLI.hs
--- a/src/Stack/CLI.hs
+++ b/src/Stack/CLI.hs
@@ -26,7 +26,11 @@
 import           Stack.Build ( buildCmd )
 import           Stack.BuildInfo ( hpackVersion, versionString' )
 import           Stack.Clean ( CleanCommand (..), cleanCmd )
-import           Stack.ConfigCmd as ConfigCmd
+import           Stack.ConfigCmd
+                   ( cfgCmdBuildFiles, cfgCmdBuildFilesName, cfgCmdEnv
+                   , cfgCmdEnvName, configCmdEnvParser, cfgCmdName, cfgCmdSet
+                   , cfgCmdSetName, configCmdSetParser
+                   )
 import           Stack.Constants
                    ( globalFooter, osIsWindows, relFileStack, relFileStackDotExe
                    , stackProgName
@@ -72,7 +76,9 @@
 import           Stack.Prelude
 import           Stack.Query ( queryCmd )
 import           Stack.Runners
-                   ( ShouldReexec (..), withConfig, withDefaultEnvConfig )
+                   ( ShouldReexec (..), withBuildConfig, withConfig
+                   , withDefaultEnvConfig
+                   )
 import           Stack.SDist ( sdistCmd )
 import           Stack.Script ( ScriptOpts (..), scriptCmd )
 import           Stack.SetupCmd ( setupCmd )
@@ -224,19 +230,28 @@
     (cleanOptsParser Clean)
 
   config = addSubCommands'
-      ConfigCmd.cfgCmdName
+      cfgCmdName
         "Subcommands for accessing and modifying configuration values."
         ( do
             addCommand'
-              ConfigCmd.cfgCmdSetName
-              "Sets a key in configuration file to value."
+              cfgCmdSetName
+              "Set a key in a configuration file to value."
               (withConfig NoReexec . cfgCmdSet)
               configCmdSetParser
             addCommandWithLocalInstallRootFooter
-              ConfigCmd.cfgCmdEnvName
+              cfgCmdEnvName
               "Print environment variables for use in a shell."
               (withConfig YesReexec . withDefaultEnvConfig . cfgCmdEnv)
               configCmdEnvParser
+            addCommand'
+              cfgCmdBuildFilesName
+              "Generate (when applicable) a Cabal file from a package \
+              \ description in the Hpack format and/or a lock file for Stack's \
+              \project-level configuration."
+              -- It is withBuildConfig that yields the desired actions;
+              -- cfgCmdBuildFiles itself yields nothing of interest.
+              (withConfig YesReexec . withBuildConfig . cfgCmdBuildFiles)
+              (pure ())
         )
 
   docker = addSubCommands'
diff --git a/src/Stack/ComponentFile.hs b/src/Stack/ComponentFile.hs
--- a/src/Stack/ComponentFile.hs
+++ b/src/Stack/ComponentFile.hs
@@ -390,9 +390,10 @@
     fmap
       (nubOrd . concat)
       (mapM (makeDirCandidates haskellPreprocessorExts) dirs)
-  makeDirCandidates :: [Text]
-                    -> Path Abs Dir
-                    -> IO [Path Abs File]
+  makeDirCandidates ::
+       [Text]
+    -> Path Abs Dir
+    -> IO [Path Abs File]
   makeDirCandidates haskellPreprocessorExts dir =
     case name of
       DotCabalMain fp -> resolveCandidate dir fp
@@ -431,7 +432,10 @@
     , line <> bulletedList (map pretty possibilities)
     , flow "If you are using a custom preprocessor for this module"
     , flow "with its own file extension, consider adding the extension"
-    , flow "to the 'custom-preprocessor-extensions' field in stack.yaml."
+    , flow "to the value of the"
+    , style Shell "custom-preprocessor-extensions"
+    , flow "key in Stack's project-level configuration file"
+    , "(" <> style File "stack.yaml" <> ")."
     ]
  where
   makePossibilities name =
@@ -525,8 +529,9 @@
 
 -- | Resolve the directory, if it can't be resolved, warn for the user
 -- (purely to be helpful).
-resolveDirOrWarn :: FilePath.FilePath
-                 -> RIO GetPackageFileContext (Maybe (Path Abs Dir))
+resolveDirOrWarn ::
+     FilePath.FilePath
+  -> RIO GetPackageFileContext (Maybe (Path Abs Dir))
 resolveDirOrWarn = resolveOrWarn "Directory" f
  where
   f p x = forgivingResolveDir p x >>= rejectMissingDir
diff --git a/src/Stack/Config.hs b/src/Stack/Config.hs
--- a/src/Stack/Config.hs
+++ b/src/Stack/Config.hs
@@ -41,6 +41,7 @@
 import           Data.Array.IArray ( (!), (//) )
 import qualified Data.ByteString as S
 import           Data.ByteString.Builder ( byteString )
+import           Data.Char ( isLatin1 )
 import           Data.Coerce ( coerce )
 import qualified Data.Either.Extra as EE
 import qualified Data.IntMap as IntMap
@@ -285,6 +286,7 @@
           configMonoid.compilerRepository
         ghcBuild = getFirst configMonoid.ghcBuild
         installGHC = fromFirstTrue configMonoid.installGHC
+        installMsys = fromFirst installGHC configMonoid.installMsys
         skipGHCCheck = fromFirstFalse configMonoid.skipGHCCheck
         skipMsys = fromFirstFalse configMonoid.skipMsys
         defMsysEnvironment = case platform of
@@ -341,33 +343,57 @@
       Nothing -> getDefaultLocalProgramsBase stackRoot platform origEnv
       Just path -> pure path
     let localProgramsFilePath = toFilePath localProgramsBase
-    when (osIsWindows && ' ' `elem` localProgramsFilePath) $ do
-      ensureDir localProgramsBase
-      -- getShortPathName returns the long path name when a short name does not
-      -- exist.
-      shortLocalProgramsFilePath <-
-        liftIO $ getShortPathName localProgramsFilePath
+        spaceInLocalProgramsPath = ' ' `elem` localProgramsFilePath
+        nonLatin1InLocalProgramsPath = not $ all isLatin1 localProgramsFilePath
+        problematicLocalProgramsPath =
+             nonLatin1InLocalProgramsPath
+          || (osIsWindows && spaceInLocalProgramsPath)
+    when problematicLocalProgramsPath $ do
+      let msgSpace =
+            [ flow "It contains a space character. This will prevent building \
+                   \with GHC 9.4.1 or later."
+            | osIsWindows && spaceInLocalProgramsPath
+            ]
+      msgNoShort <- if osIsWindows && spaceInLocalProgramsPath
+        then do
+          ensureDir localProgramsBase
+          -- getShortPathName returns the long path name when a short name does not
+          -- exist.
+          shortLocalProgramsFilePath <-
+            liftIO $ getShortPathName localProgramsFilePath
+          pure [ flow "It also has no alternative short ('8 dot 3') name. This \
+                      \will cause problems with packages that use the GNU \
+                      \project's 'configure' shell script."
+               | ' ' `elem` shortLocalProgramsFilePath
+               ]
+        else pure []
+      let msgNonLatin1 = if nonLatin1InLocalProgramsPath
+            then
+              [ flow "It contains at least one non-ISO/IEC 8859-1 (Latin-1) \
+                     \character (Unicode code point > 255). This will cause \
+                     \problems with packages that build using the"
+              , style Shell "hsc2hs"
+              , flow "tool with its default template"
+              , style Shell "template-hsc.h" <> "."
+              ]
+            else []
       prettyWarn $
           "[S-8432]"
           <> line
           <> fillSep
                (  [ flow "Stack's 'programs' path is"
                   , style File (fromString localProgramsFilePath) <> "."
-                  , flow "It contains a space character. This will prevent \
-                         \building with GHC 9.4.1 or later."
                   ]
-               <> [ flow "It also has no alternative short ('8 dot 3') name. \
-                         \This will cause problems with packages that use the \
-                         \GNU project's 'configure' shell script."
-                  | ' ' `elem` shortLocalProgramsFilePath
-                  ]
+               <> msgSpace
+               <> msgNoShort
+               <> msgNonLatin1
                )
           <> blankLine
           <> fillSep
                [ flow "To avoid sucn problems, use the"
                , style Shell "local-programs-path"
                , flow "non-project specific configuration option to specify an \
-                      \alternative space-free path."
+                      \alternative path without those characteristics."
                ]
           <> line
     platformOnlyDir <-
@@ -562,6 +588,7 @@
                 , latestSnapshot
                 , systemGHC
                 , installGHC
+                , installMsys
                 , skipGHCCheck
                 , skipMsys
                 , msysEnvironment
@@ -623,12 +650,13 @@
 withLocalLogFunc logFunc = local (set logFuncL logFunc)
 
 -- | Runs the provided action with a new 'LogFunc', given a 'StylesUpdate'.
-withNewLogFunc :: MonadUnliftIO m
-               => GlobalOpts
-               -> Bool  -- ^ Use color
-               -> StylesUpdate
-               -> (LogFunc -> m a)
-               -> m a
+withNewLogFunc ::
+     MonadUnliftIO m
+  => GlobalOpts
+  -> Bool  -- ^ Use color
+  -> StylesUpdate
+  -> (LogFunc -> m a)
+  -> m a
 withNewLogFunc go useColor (StylesUpdate update) inner = do
   logOptions0 <- logOptionsHandle stderr False
   let logOptions
@@ -651,11 +679,12 @@
   highlightColor = fromString $ setSGRCode $ snd $ styles ! Highlight
 
 -- | Get the default location of the local programs directory.
-getDefaultLocalProgramsBase :: MonadThrow m
-                            => Path Abs Dir
-                            -> Platform
-                            -> ProcessContext
-                            -> m (Path Abs Dir)
+getDefaultLocalProgramsBase ::
+     MonadThrow m
+  => Path Abs Dir
+  -> Platform
+  -> ProcessContext
+  -> m (Path Abs Dir)
 getDefaultLocalProgramsBase configStackRoot configPlatform override =
   case configPlatform of
     -- For historical reasons, on Windows a subdirectory of LOCALAPPDATA is
@@ -795,7 +824,7 @@
             , pretty dest <> "."
             , flow "Note: You can change the snapshot via the"
             , style Shell "snapshot"
-            , flow "field there."
+            , flow "key there."
             ]
           p <- getEmptyProject mSnapshot []
           liftIO $ do
@@ -1140,9 +1169,10 @@
 -- | Determine the extra config file locations which exist.
 --
 -- Returns most local first
-getExtraConfigs :: HasTerm env
-                => Path Abs File -- ^ use config path
-                -> RIO env [Path Abs File]
+getExtraConfigs ::
+     HasTerm env
+  => Path Abs File -- ^ use config path
+  -> RIO env [Path Abs File]
 getExtraConfigs userConfigPath = liftIO $ do
   env <- getEnvironment
   mstackConfig <-
@@ -1185,10 +1215,11 @@
           pure (Right res)
 
 -- | Get the location of the project config file, if it exists.
-getProjectConfig :: HasTerm env
-                 => StackYamlLoc
-                 -- ^ Override stack.yaml
-                 -> RIO env (ProjectConfig (Path Abs File))
+getProjectConfig ::
+     HasTerm env
+  => StackYamlLoc
+     -- ^ Override stack.yaml
+  -> RIO env (ProjectConfig (Path Abs File))
 getProjectConfig (SYLOverride stackYaml) = pure $ PCProject stackYaml
 getProjectConfig SYLGlobalProject = pure PCGlobalProject
 getProjectConfig SYLDefault = do
diff --git a/src/Stack/ConfigCmd.hs b/src/Stack/ConfigCmd.hs
--- a/src/Stack/ConfigCmd.hs
+++ b/src/Stack/ConfigCmd.hs
@@ -13,6 +13,8 @@
   , configCmdEnvParser
   , cfgCmdEnv
   , cfgCmdEnvName
+  , cfgCmdBuildFiles
+  , cfgCmdBuildFilesName
   , cfgCmdName
   ) where
 
@@ -40,9 +42,11 @@
                    )
 import           Stack.Constants ( stackDotYaml )
 import           Stack.Prelude
+import           Stack.Types.BuildConfig ( BuildConfig )
 import           Stack.Types.Config ( Config (..), HasConfig (..) )
 import           Stack.Types.ConfigMonoid
                    ( configMonoidInstallGHCName
+                   , configMonoidInstallMsysName
                    , configMonoidRecommendStackUpgradeName
                    , configMonoidSystemGHCName
                    )
@@ -71,6 +75,7 @@
   | ConfigCmdSetResolver !(Unresolved AbstractSnapshot)
   | ConfigCmdSetSystemGhc !CommandScope !Bool
   | ConfigCmdSetInstallGhc !CommandScope !Bool
+  | ConfigCmdSetInstallMsys !CommandScope !Bool
   | ConfigCmdSetRecommendStackUpgrade !CommandScope !Bool
   | ConfigCmdSetDownloadPrefix !CommandScope !Text
 
@@ -86,6 +91,7 @@
 configCmdSetScope (ConfigCmdSetResolver _) = CommandScopeProject
 configCmdSetScope (ConfigCmdSetSystemGhc scope _) = scope
 configCmdSetScope (ConfigCmdSetInstallGhc scope _) = scope
+configCmdSetScope (ConfigCmdSetInstallMsys scope _) = scope
 configCmdSetScope (ConfigCmdSetRecommendStackUpgrade scope _) = scope
 configCmdSetScope (ConfigCmdSetDownloadPrefix scope _) = scope
 
@@ -267,6 +273,7 @@
   snapshotValue root newSnapshot
 cfgCmdSetValue _ (ConfigCmdSetSystemGhc _ bool') = pure $ Yaml.Bool bool'
 cfgCmdSetValue _ (ConfigCmdSetInstallGhc _ bool') = pure $ Yaml.Bool bool'
+cfgCmdSetValue _ (ConfigCmdSetInstallMsys _ bool') = pure $ Yaml.Bool bool'
 cfgCmdSetValue _ (ConfigCmdSetRecommendStackUpgrade _ bool') =
   pure $ Yaml.Bool bool'
 cfgCmdSetValue _ (ConfigCmdSetDownloadPrefix _ url) = pure $ Yaml.String url
@@ -288,6 +295,7 @@
 cfgCmdSetKeys (ConfigCmdSetResolver _) = [["resolver"], ["snapshot"]]
 cfgCmdSetKeys (ConfigCmdSetSystemGhc _ _) = [[configMonoidSystemGHCName]]
 cfgCmdSetKeys (ConfigCmdSetInstallGhc _ _) = [[configMonoidInstallGHCName]]
+cfgCmdSetKeys (ConfigCmdSetInstallMsys _ _) = [[configMonoidInstallMsysName]]
 cfgCmdSetKeys (ConfigCmdSetRecommendStackUpgrade _ _) =
   [[configMonoidRecommendStackUpgradeName]]
 cfgCmdSetKeys (ConfigCmdSetDownloadPrefix _ _) =
@@ -302,6 +310,9 @@
 cfgCmdEnvName :: String
 cfgCmdEnvName = "env"
 
+cfgCmdBuildFilesName :: String
+cfgCmdBuildFilesName = "build-files"
+
 configCmdSetParser :: OA.Parser ConfigCmdSet
 configCmdSetParser =
   OA.hsubparser $
@@ -341,6 +352,14 @@
               ( OA.progDesc
                   "Configure whether or not Stack should automatically install \
                   \GHC when necessary." ))
+      , OA.command (T.unpack configMonoidInstallMsysName)
+          ( OA.info
+              (   ConfigCmdSetInstallMsys
+              <$> globalScopeFlag
+              <*> boolArgument )
+              ( OA.progDesc
+                  "Configure whether or not Stack should automatically install \
+                  \MSYS2 when necessary." ))
       , OA.command (T.unpack configMonoidRecommendStackUpgradeName)
           ( OA.info
               (   ConfigCmdSetRecommendStackUpgrade
@@ -448,3 +467,8 @@
       escape '\'' = "'\"'\"'"
       escape c = T.singleton c
   putBuilder $ Map.foldMapWithKey toLine actions
+
+-- | This function takes no settings and yields no action of interest. It is
+-- 'withBuildConfig' that yields the desired actions.
+cfgCmdBuildFiles :: () -> RIO BuildConfig ()
+cfgCmdBuildFiles () = pure ()
diff --git a/src/Stack/Constants.hs b/src/Stack/Constants.hs
--- a/src/Stack/Constants.hs
+++ b/src/Stack/Constants.hs
@@ -299,8 +299,9 @@
 defaultGlobalConfigPath = parseAbsFile "/etc/stack/config.yaml"
 
 -- | Path where build plans are stored.
-buildPlanDir :: Path Abs Dir -- ^ Stack root
-             -> Path Abs Dir
+buildPlanDir ::
+     Path Abs Dir -- ^ Stack root
+  -> Path Abs Dir
 buildPlanDir = (</> $(mkRelDir "build-plan"))
 
 -- | Path where binary caches of the build plans are stored.
diff --git a/src/Stack/Docker.hs b/src/Stack/Docker.hs
--- a/src/Stack/Docker.hs
+++ b/src/Stack/Docker.hs
@@ -354,9 +354,10 @@
     sshRelDir = relDirDotSsh
 
 -- | Inspect Docker image or container.
-inspect :: (HasProcessContext env, HasLogFunc env)
-        => String
-        -> RIO env (Maybe Inspect)
+inspect ::
+     (HasProcessContext env, HasLogFunc env)
+  => String
+  -> RIO env (Maybe Inspect)
 inspect image = do
   results <- inspects [image]
   case Map.toList results of
@@ -365,9 +366,10 @@
     _ -> throwIO (InvalidInspectOutputException "expect a single result")
 
 -- | Inspect multiple Docker images and/or containers.
-inspects :: (HasProcessContext env, HasLogFunc env)
-         => [String]
-         -> RIO env (Map Text Inspect)
+inspects ::
+     (HasProcessContext env, HasLogFunc env)
+  => [String]
+  -> RIO env (Map Text Inspect)
 inspects [] = pure Map.empty
 inspects images = do
   maybeInspectOut <-
@@ -397,10 +399,11 @@
   either throwIO (pullImage docker) docker.image
 
 -- | Pull Docker image from registry.
-pullImage :: (HasProcessContext env, HasTerm env)
-          => DockerOpts
-          -> String
-          -> RIO env ()
+pullImage ::
+     (HasProcessContext env, HasTerm env)
+  => DockerOpts
+  -> String
+  -> RIO env ()
 pullImage docker image = do
   prettyInfoL
     [ flow "Pulling image from registry:"
diff --git a/src/Stack/Ghci.hs b/src/Stack/Ghci.hs
--- a/src/Stack/Ghci.hs
+++ b/src/Stack/Ghci.hs
@@ -1171,13 +1171,16 @@
                 , pretty projectConfigFile <> ","
                 , flow "then you can use"
                 , style Shell "stack init"
-                , flow "to create a new stack.yaml for the packages in the \
-                       \current directory."
+                , flow "to create a new"
+                , style File "stack.yaml"
+                , flow "for the packages in the current directory."
                 , line
                 ]
             , flow "If you want to use the project configuration at"
             , pretty projectConfigFile <> ","
-            , flow "then you can add to its 'packages' field."
+            , flow "then you can add to the value of its"
+            , style Shell "packages"
+            , "key."
             ]
         , ""
         ]
diff --git a/src/Stack/Init.hs b/src/Stack/Init.hs
--- a/src/Stack/Init.hs
+++ b/src/Stack/Init.hs
@@ -376,9 +376,8 @@
    where
     convert v = B.byteString (Yaml.encode $ Yaml.object [(name, v)])
 
-    -- Some fields in stack.yaml are optional and may not be
-    -- generated. For these, we provided commented out dummy
-    -- values to go along with the comments.
+    -- Some keys in stack.yaml are optional and may not be generated. For these,
+    -- we provided commented out dummy values to go along with the comments.
     nonPresentValue "extra-deps" = Just "# extra-deps: []\n"
     nonPresentValue "flags" = Just "# flags: {}\n"
     nonPresentValue "extra-package-dbs" = Just "# extra-package-dbs: []\n"
@@ -431,8 +430,8 @@
     , "A snapshot resolver dictates the compiler version and the set of packages"
     , "to be used for project dependencies. For example:"
     , ""
-    , "snapshot: lts-23.0"
-    , "snapshot: nightly-2024-12-13"
+    , "snapshot: lts-23.14"
+    , "snapshot: nightly-2025-02-15"
     , "snapshot: ghc-9.8.4"
     , ""
     , "The location of a snapshot can be provided as a file or url. Stack assumes"
diff --git a/src/Stack/New.hs b/src/Stack/New.hs
--- a/src/Stack/New.hs
+++ b/src/Stack/New.hs
@@ -333,9 +333,10 @@
       downloadFromUrl settings templateDir
 
  where
-  loadLocalFile :: Path b File
-                -> (ByteString -> Either String Text)
-                -> RIO env Text
+  loadLocalFile ::
+       Path b File
+    -> (ByteString -> Either String Text)
+    -> RIO env Text
   loadLocalFile path extract = do
     logDebug $
          "Opening local template: \""
@@ -386,10 +387,11 @@
       (useCachedVersionOrThrow url path)
     loadLocalFile path extract
 
-  useCachedVersionOrThrow :: String
-                          -> Path Abs File
-                          -> VerifiedDownloadException
-                          -> RIO env ()
+  useCachedVersionOrThrow ::
+       String
+    -> Path Abs File
+    -> VerifiedDownloadException
+    -> RIO env ()
   useCachedVersionOrThrow url path exception = do
     exists <- doesFileExist path
 
diff --git a/src/Stack/Options/ConfigParser.hs b/src/Stack/Options/ConfigParser.hs
--- a/src/Stack/Options/ConfigParser.hs
+++ b/src/Stack/Options/ConfigParser.hs
@@ -33,7 +33,7 @@
 -- | Command-line arguments parser for configuration.
 configOptsParser :: FilePath -> GlobalOptsContext -> Parser ConfigMonoid
 configOptsParser currentDir hide0 =
-  ( \stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC arch
+  ( \stackRoot workDir buildOpts dockerOpts nixOpts systemGHC installGHC installMsys arch
      ghcVariant ghcBuild jobs extraIncludeDirs extraLibDirs
      customPreprocessorExts overrideGccPath overrideHpack hpackForce skipGHCCheck skipMsys
      localBinPath setupInfoLocations modifyCodePage allowDifferentUser dumpLogs
@@ -45,6 +45,7 @@
        , nixOpts
        , systemGHC
        , installGHC
+       , installMsys
        , skipGHCCheck
        , arch
        , ghcVariant
@@ -99,6 +100,11 @@
         "install-ghc"
         "downloading and installing GHC if necessary. (Can be done manually \
         \with 'stack setup'.)"
+        hide
+  <*> firstBoolFlagsNoDefault
+        "install-msys"
+        "downloading and installing MSYS2 if necessary (Windows only). (Can be \
+        \done manually with 'stack setup'.) (default: same as 'install-ghc')"
         hide
   <*> optionalFirst (strOption
         (  long "arch"
diff --git a/src/Stack/Package.hs b/src/Stack/Package.hs
--- a/src/Stack/Package.hs
+++ b/src/Stack/Package.hs
@@ -826,7 +826,8 @@
 -- iterating by increasing amount of required dependencies.
 -- 'PackageType' with value 'PTProject' here means the component is a direct target
 -- and 'PTDependency' means it's a dependency of a direct target.
-topProcessPackageComponent :: forall b.
+topProcessPackageComponent ::
+     forall b.
      Package
   -> Target
   -> (    forall component. (HasComponentInfo component)
diff --git a/src/Stack/PackageDump.hs b/src/Stack/PackageDump.hs
--- a/src/Stack/PackageDump.hs
+++ b/src/Stack/PackageDump.hs
@@ -189,9 +189,10 @@
 
 -- | Find the package IDs matching the given constraints with all dependencies installed.
 -- Packages not mentioned in the provided @Map@ are allowed to be present too.
-sinkMatching :: Monad m
-             => Map PackageName Version -- ^ allowed versions
-             -> ConduitM DumpPackage o m (Map PackageName DumpPackage)
+sinkMatching ::
+     Monad m
+  => Map PackageName Version -- ^ allowed versions
+  -> ConduitM DumpPackage o m (Map PackageName DumpPackage)
 sinkMatching allowed =
     Map.fromList
   . map (pkgName . (.packageIdent) &&& id)
@@ -307,9 +308,10 @@
 type Line = Text
 
 -- | Apply the given Sink to each section of output, broken by a single line containing ---
-eachSection :: Monad m
-            => ConduitM Line Void m a
-            -> ConduitM Text a m ()
+eachSection ::
+     Monad m
+  => ConduitM Line Void m a
+  -> ConduitM Text a m ()
 eachSection inner = CL.map (T.filter (/= '\r')) .| CT.lines .| start
  where
   peekText = await >>= maybe (pure Nothing) (\bs ->
@@ -326,9 +328,10 @@
     start
 
 -- | Grab each key/value pair
-eachPair :: Monad m
-         => (Text -> ConduitM Line Void m a)
-         -> ConduitM Line a m ()
+eachPair ::
+     Monad m
+  => (Text -> ConduitM Line Void m a)
+  -> ConduitM Line a m ()
 eachPair inner = start
  where
   start = await >>= maybe (pure ()) start'
diff --git a/src/Stack/PackageFile.hs b/src/Stack/PackageFile.hs
--- a/src/Stack/PackageFile.hs
+++ b/src/Stack/PackageFile.hs
@@ -40,8 +40,9 @@
 
 -- | Resolve the file, if it can't be resolved, warn for the user
 -- (purely to be helpful).
-resolveFileOrWarn :: FilePath.FilePath
-                  -> RIO GetPackageFileContext (Maybe (Path Abs File))
+resolveFileOrWarn ::
+     FilePath.FilePath
+  -> RIO GetPackageFileContext (Maybe (Path Abs File))
 resolveFileOrWarn = resolveOrWarn "File" f
  where
   f p x = forgivingResolveFile p x >>= rejectMissingFile
diff --git a/src/Stack/Prelude.hs b/src/Stack/Prelude.hs
--- a/src/Stack/Prelude.hs
+++ b/src/Stack/Prelude.hs
@@ -146,10 +146,11 @@
   Path.IO.withSystemTempDir str $ run . inner
 
 -- | Like `withSystemTempDir`, but the temporary directory is not deleted.
-withKeepSystemTempDir :: MonadUnliftIO m
-                      => String
-                      -> (Path Abs Dir -> m a)
-                      -> m a
+withKeepSystemTempDir ::
+     MonadUnliftIO m
+  => String
+  -> (Path Abs Dir -> m a)
+  -> m a
 withKeepSystemTempDir str inner = withRunInIO $ \run -> do
   path <- Path.IO.getTempDir
   dir <- Path.IO.createTempDir path str
@@ -210,20 +211,22 @@
 -- | Read from the process, ignoring any output.
 --
 -- Throws a 'ReadProcessException' exception if the process fails.
-readProcessNull :: (HasProcessContext env, HasLogFunc env, HasCallStack)
-                => String -- ^ Command
-                -> [String] -- ^ Command line arguments
-                -> RIO env ()
+readProcessNull ::
+     (HasProcessContext env, HasLogFunc env, HasCallStack)
+  => String -- ^ Command
+  -> [String] -- ^ Command line arguments
+  -> RIO env ()
 readProcessNull name args =
   -- We want the output to appear in any exceptions, so we capture and drop it
   void $ proc name args readProcess_
 
 -- | Use the new 'ProcessContext', but retain the working directory
 -- from the parent environment.
-withProcessContext :: HasProcessContext env
-                   => ProcessContext
-                   -> RIO env a
-                   -> RIO env a
+withProcessContext ::
+     HasProcessContext env
+  => ProcessContext
+  -> RIO env a
+  -> RIO env a
 withProcessContext pcNew inner = do
   pcOld <- view processContextL
   let pcNew' = set workingDirL (view workingDirL pcOld) pcNew
diff --git a/src/Stack/SDist.hs b/src/Stack/SDist.hs
--- a/src/Stack/SDist.hs
+++ b/src/Stack/SDist.hs
@@ -42,6 +42,7 @@
 import qualified Distribution.PackageDescription.Parsec as Cabal
 import           Distribution.PackageDescription.PrettyPrint
                    ( showGenericPackageDescription )
+import           Distribution.Simple.Utils ( cabalVersion )
 import           Distribution.Version
                    ( earlierVersion, hasLowerBound, hasUpperBound, isAnyVersion
                    , orLaterVersion, simplifyVersionRange
@@ -574,7 +575,8 @@
   prettyInfoL
     [ flow "Checking package"
     , style Current (fromPackageName name)
-    , flow "for common mistakes."
+    , flow "for common mistakes using Cabal version"
+    , fromString $ versionString cabalVersion <> "."
     ]
   let pkgChecks =
         -- MSS 2017-12-12: Try out a few different variants of pkgDesc to try
diff --git a/src/Stack/Setup.hs b/src/Stack/Setup.hs
--- a/src/Stack/Setup.hs
+++ b/src/Stack/Setup.hs
@@ -620,7 +620,8 @@
   "https://raw.githubusercontent.com/commercialhaskell/stackage-content/master/stack/stack-setup-2.yaml"
 
 data SetupOpts = SetupOpts
-  { installIfMissing :: !Bool
+  { installGhcIfMissing :: !Bool
+  , installMsysIfMissing :: !Bool
   , useSystem :: !Bool
     -- ^ Should we use a system compiler installation, if available?
   , wantedCompiler :: !WantedCompiler
@@ -661,7 +662,8 @@
   actual <- either throwIO pure $ wantedToActual wcVersion
   let wc = actual^.whichCompilerL
       sopts = SetupOpts
-        { installIfMissing = config.installGHC
+        { installGhcIfMissing = config.installGHC
+        , installMsysIfMissing = config.installMsys
         , useSystem = config.systemGHC
         , wantedCompiler = wcVersion
         , compilerCheck = config.compilerCheck
@@ -1120,7 +1122,7 @@
       case getInstalledTool installed (mkPackageName "msys2") (const True) of
         Just tool -> pure (Just tool)
         Nothing
-          | sopts.installIfMissing -> do
+          | sopts.installMsysIfMissing -> do
               si <- runMemoized getSetupInfo'
               let msysDir = fillSep
                     [ style Dir "msys2-yyyymmdd"
@@ -1185,7 +1187,7 @@
   case existingCompilers of
     (tool, build_):_ -> pure (tool, build_)
     []
-      | sopts.installIfMissing -> do
+      | sopts.installGhcIfMissing -> do
           si <- runMemoized getSetupInfo'
           downloadAndInstallPossibleCompilers
             (map snd possibleCompilers)
@@ -1694,6 +1696,10 @@
     Just ghcBuild -> pure [ghcBuild]
     Nothing -> determineGhcBuild
  where
+  -- The GHCup project is also interested in the algorithm below, as it copies
+  -- it at GHCup.Platform.getStackGhcBuilds. If you change this algorithm, it
+  -- would be a courtesy to bring that to the attention of the GHCup project
+  -- maintainers.
   determineGhcBuild = do
     -- TODO: a more reliable, flexible, and data driven approach would be to
     -- actually download small "test" executables (from setup-info) that link to
diff --git a/src/Stack/Setup/Installed.hs b/src/Stack/Setup/Installed.hs
--- a/src/Stack/Setup/Installed.hs
+++ b/src/Stack/Setup/Installed.hs
@@ -78,25 +78,28 @@
 parseToolText (parsePackageIdentifier . T.unpack -> Just pkgId) = Just (Tool pkgId)
 parseToolText _ = Nothing
 
-markInstalled :: (MonadIO m, MonadThrow m)
-              => Path Abs Dir
-              -> Tool
-              -> m ()
+markInstalled ::
+     (MonadIO m, MonadThrow m)
+  => Path Abs Dir
+  -> Tool
+  -> m ()
 markInstalled programsPath tool = do
   fpRel <- parseRelFile $ toolString tool ++ ".installed"
   writeBinaryFileAtomic (programsPath </> fpRel) "installed"
 
-unmarkInstalled :: MonadIO m
-                => Path Abs Dir
-                -> Tool
-                -> m ()
+unmarkInstalled ::
+     MonadIO m
+  => Path Abs Dir
+  -> Tool
+  -> m ()
 unmarkInstalled programsPath tool = liftIO $ do
   fpRel <- parseRelFile $ toolString tool ++ ".installed"
   ignoringAbsence (removeFile $ programsPath </> fpRel)
 
-listInstalled :: (MonadIO m, MonadThrow m)
-              => Path Abs Dir
-              -> m [Tool]
+listInstalled ::
+     (MonadIO m, MonadThrow m)
+  => Path Abs Dir
+  -> m [Tool]
 listInstalled programsPath =
   doesDirExist programsPath >>= \case
     False -> pure []
@@ -107,10 +110,11 @@
     x <- T.stripSuffix ".installed" $ T.pack $ toFilePath $ filename fp
     parseToolText x
 
-filterTools :: PackageName       -- ^ package to find
-            -> (Version -> Bool) -- ^ which versions are acceptable
-            -> [Tool]            -- ^ tools to filter
-            -> [PackageIdentifier]
+filterTools ::
+     PackageName       -- ^ package to find
+  -> (Version -> Bool) -- ^ which versions are acceptable
+  -> [Tool]            -- ^ tools to filter
+  -> [PackageIdentifier]
 filterTools name goodVersion installed =
   [ pkgId | Tool pkgId <- installed
           , pkgName pkgId == name
@@ -178,10 +182,11 @@
  where
   isGHC n = "ghc" == n || "ghc-" `L.isPrefixOf` n
 
-installDir :: (MonadReader env m, MonadThrow m)
-           => Path Abs Dir
-           -> Tool
-           -> m (Path Abs Dir)
+installDir ::
+     (MonadReader env m, MonadThrow m)
+  => Path Abs Dir
+  -> Tool
+  -> m (Path Abs Dir)
 installDir programsDir tool = do
   relativeDir <- parseRelDir $ toolString tool
   pure $ programsDir </> relativeDir
diff --git a/src/Stack/SetupCmd.hs b/src/Stack/SetupCmd.hs
--- a/src/Stack/SetupCmd.hs
+++ b/src/Stack/SetupCmd.hs
@@ -38,33 +38,50 @@
 setupCmd :: SetupCmdOpts -> RIO Runner ()
 setupCmd sco = withConfig YesReexec $ do
   installGHC <- view $ configL . to (.installGHC)
-  if installGHC
-    then
-       withBuildConfig $ do
-       (wantedCompiler, compilerCheck, mConfigFile) <-
-         case sco.compilerVersion of
-           Just v -> pure (v, MatchMinor, Nothing)
-           Nothing -> do
-            wantedCompilerVersion <- view wantedCompilerVersionL
-            compilerCheck <- view (configL . to (.compilerCheck))
-            configFile <- view configFileL
-            -- We are indifferent as to whether the configuration file is a
-            -- user-specific global or a project-level one.
-            let eitherConfigFile = EE.fromEither configFile
-            pure
-              ( wantedCompilerVersion
-              , compilerCheck
-              , Just eitherConfigFile
-              )
-       setup sco wantedCompiler compilerCheck mConfigFile
-    else
-      prettyWarnL
-        [ "The"
-        , style Shell "--no-install-ghc"
-        , flow "flag is inconsistent with"
-        , style Shell (flow "stack setup") <> "."
-        , flow "No action taken."
-        ]
+  installMsys <- view $ configL . to (.installMsys)
+  case (installGHC, installMsys) of
+    (True, True) -> withBuildConfig $ do
+      (wantedCompiler, compilerCheck, mConfigFile) <-
+        case sco.compilerVersion of
+          Just v -> pure (v, MatchMinor, Nothing)
+          Nothing -> do
+           wantedCompilerVersion <- view wantedCompilerVersionL
+           compilerCheck <- view (configL . to (.compilerCheck))
+           configFile <- view configFileL
+           -- We are indifferent as to whether the configuration file is a
+           -- user-specific global or a project-level one.
+           let eitherConfigFile = EE.fromEither configFile
+           pure
+             ( wantedCompilerVersion
+             , compilerCheck
+             , Just eitherConfigFile
+             )
+      setup sco wantedCompiler compilerCheck mConfigFile
+    (False, True) -> warn
+      [ styledNoInstallGHC
+      , singleFlag
+      ]
+    (True, False) -> warn
+      [ styledNoInstallMsys
+      , singleFlag
+      ]
+    (False, False) -> warn
+      [ styledNoInstallGHC
+      , "and"
+      , styledNoInstallMsys
+      , flow "flags are"
+      ]
+ where
+  styledNoInstallGHC = style Shell "--no-install-ghc"
+  styledNoInstallMsys = style Shell "--no-install-msys"
+  singleFlag = flow "flag is"
+  warn docs = prettyWarnL $
+       ["The"]
+    <> docs
+    <> [ flow "inconsistent with"
+       , style Shell (flow "stack setup") <> "."
+       , flow "No action taken."
+       ]
 
 setup ::
      (HasBuildConfig env, HasGHCVariant env)
@@ -78,7 +95,8 @@
 setup sco wantedCompiler compilerCheck configFile = do
   config <- view configL
   sandboxedGhc <- (.sandboxed) . fst <$> ensureCompilerAndMsys SetupOpts
-    { installIfMissing = True
+    { installGhcIfMissing = True
+    , installMsysIfMissing = True
     , useSystem = config.systemGHC && not sco.forceReinstall
     , wantedCompiler
     , compilerCheck
diff --git a/src/Stack/Types/Config.hs b/src/Stack/Types/Config.hs
--- a/src/Stack/Types/Config.hs
+++ b/src/Stack/Types/Config.hs
@@ -97,6 +97,9 @@
   , installGHC              :: !Bool
     -- ^ Should we automatically install GHC if missing or the wrong
     -- version is available? Can be overridden by command line options.
+  , installMsys              :: !Bool
+    -- ^ On Windows, should we automatically install MSYS2 if missing? Can be
+    -- overridden by command line options.
   , skipGHCCheck            :: !Bool
     -- ^ Don't bother checking the GHC version or architecture.
   , skipMsys                :: !Bool
diff --git a/src/Stack/Types/ConfigMonoid.hs b/src/Stack/Types/ConfigMonoid.hs
--- a/src/Stack/Types/ConfigMonoid.hs
+++ b/src/Stack/Types/ConfigMonoid.hs
@@ -11,6 +11,7 @@
   , configMonoidAllowDifferentUserName
   , configMonoidGHCVariantName
   , configMonoidInstallGHCName
+  , configMonoidInstallMsysName
   , configMonoidRecommendStackUpgradeName
   , configMonoidSystemGHCName
   ) where
@@ -83,6 +84,8 @@
     -- ^ See: 'configSystemGHC'
   , installGHC              :: !FirstTrue
     -- ^ See: 'configInstallGHC'
+  , installMsys             :: !(First Bool)
+    -- ^ See: 'configInstallMsys'
   , skipGHCCheck            :: !FirstFalse
     -- ^ See: 'configSkipGHCCheck'
   , skipMsys                :: !FirstFalse
@@ -248,6 +251,7 @@
     First <$> jsonSubWarningsT (obj ..:?  configMonoidPackageIndexName)
   systemGHC <- First <$> obj ..:? configMonoidSystemGHCName
   installGHC <- FirstTrue <$> obj ..:? configMonoidInstallGHCName
+  installMsys <- First <$> obj ..:? configMonoidInstallMsysName
   skipGHCCheck <- FirstFalse <$> obj ..:? configMonoidSkipGHCCheckName
   skipMsys <- FirstFalse <$> obj ..:? configMonoidSkipMsysName
   msysEnvironment <- First <$> obj ..:? configMonoidMsysEnvironmentName
@@ -366,6 +370,7 @@
     , packageIndex
     , systemGHC
     , installGHC
+    , installMsys
     , skipGHCCheck
     , skipMsys
     , msysEnvironment
@@ -460,6 +465,9 @@
 
 configMonoidInstallGHCName :: Text
 configMonoidInstallGHCName = "install-ghc"
+
+configMonoidInstallMsysName :: Text
+configMonoidInstallMsysName = "install-msys"
 
 configMonoidSkipGHCCheckName :: Text
 configMonoidSkipGHCCheckName = "skip-ghc-check"
diff --git a/src/Stack/Types/Package.hs b/src/Stack/Types/Package.hs
--- a/src/Stack/Types/Package.hs
+++ b/src/Stack/Types/Package.hs
@@ -347,10 +347,11 @@
 lpFiles :: HasEnvConfig env => LocalPackage -> RIO env (Set.Set (Path Abs File))
 lpFiles = runMemoizedWith . fmap (Set.unions . M.elems) . (.componentFiles)
 
-lpFilesForComponents :: HasEnvConfig env
-                     => Set NamedComponent
-                     -> LocalPackage
-                     -> RIO env (Set.Set (Path Abs File))
+lpFilesForComponents ::
+     HasEnvConfig env
+  => Set NamedComponent
+  -> LocalPackage
+  -> RIO env (Set.Set (Path Abs File))
 lpFilesForComponents components lp = runMemoizedWith $ do
   componentFiles <- lp.componentFiles
   pure $ mconcat (M.elems (M.restrictKeys componentFiles components))
diff --git a/src/Stack/Types/PackageName.hs b/src/Stack/Types/PackageName.hs
--- a/src/Stack/Types/PackageName.hs
+++ b/src/Stack/Types/PackageName.hs
@@ -11,8 +11,9 @@
 
 -- | An argument which accepts a template name of the format
 -- @foo.hsfiles@.
-packageNameArgument :: O.Mod O.ArgumentFields PackageName
-                    -> O.Parser PackageName
+packageNameArgument ::
+     O.Mod O.ArgumentFields PackageName
+  -> O.Parser PackageName
 packageNameArgument =
   O.argument
     (do s <- O.str
diff --git a/src/Stack/Types/Snapshot.hs b/src/Stack/Types/Snapshot.hs
--- a/src/Stack/Types/Snapshot.hs
+++ b/src/Stack/Types/Snapshot.hs
@@ -43,8 +43,8 @@
     ]
   displayException (FilepathInDownloadedSnapshot url) = unlines
     [ "Error: [S-4865]"
-    , "Downloaded snapshot specified a 'snapshot: { location: filepath }' "
-    , "field, but filepaths are not allowed in downloaded snapshots.\n"
+    , "Downloaded snapshot specified 'snapshot: { location: filepath }', "
+    , "but filepaths are not allowed in downloaded snapshots.\n"
     , "Filepath specified: " ++ T.unpack url
     ]
 
diff --git a/src/Stack/Types/TemplateName.hs b/src/Stack/Types/TemplateName.hs
--- a/src/Stack/Types/TemplateName.hs
+++ b/src/Stack/Types/TemplateName.hs
@@ -73,16 +73,18 @@
 
 -- | An argument which accepts a template name of the format @foo.hsfiles@ or
 -- @foo@, ultimately normalized to @foo@.
-templateNameArgument :: O.Mod O.ArgumentFields TemplateName
-                     -> O.Parser TemplateName
+templateNameArgument ::
+     O.Mod O.ArgumentFields TemplateName
+  -> O.Parser TemplateName
 templateNameArgument =
   O.argument
     (do s <- O.str
         either O.readerError pure (parseTemplateNameFromString s))
 
 -- | An argument which accepts a @key:value@ pair for specifying parameters.
-templateParamArgument :: O.Mod O.OptionFields (Text,Text)
-                      -> O.Parser (Text,Text)
+templateParamArgument ::
+     O.Mod O.OptionFields (Text,Text)
+  -> O.Parser (Text,Text)
 templateParamArgument =
   O.option
     (do s <- O.str
diff --git a/stack.cabal b/stack.cabal
--- a/stack.cabal
+++ b/stack.cabal
@@ -1,7 +1,7 @@
-cabal-version:      2.0
+cabal-version:      2.2
 name:               stack
-version:            3.3.1
-license:            BSD3
+version:            3.5.1
+license:            BSD-3-Clause
 license-file:       LICENSE
 maintainer:         manny@fpcomplete.com
 author:             Commercial Haskell SIG
@@ -93,6 +93,7 @@
     doc/maintainers/self-hosted_runners.md
     doc/maintainers/stack_errors.md
     doc/maintainers/team_process.md
+    doc/maintainers/version_scheme.md
     doc/other_resources.md
     doc/README.md
     doc/SIGNING_KEY.md
@@ -420,8 +421,8 @@
         bytestring >=0.12.1.0,
         casa-client >=0.0.2,
         companion >=0.1.0,
-        conduit >=1.3.6,
-        conduit-extra >=1.3.6,
+        conduit >=1.3.6.1,
+        conduit-extra >=1.3.7,
         containers >=0.6.8,
         crypton >=1.0.1,
         directory >=1.3.8.5,
@@ -438,7 +439,7 @@
         hi-file-parser >=0.1.7.0,
         hpack >=0.36.0,
         hpc >=0.7.0.0,
-        http-client >=0.7.17,
+        http-client >=0.7.18,
         http-client-tls >=0.3.6.2,
         http-conduit >=2.3.9.1,
         http-download >=0.2.1.0,
@@ -458,7 +459,7 @@
         pretty >=1.1.3.6,
         process >=1.6.13.2,
         project-template >=0.2.1.0,
-        random >=1.2.1.2,
+        random >=1.2.1.3,
         rio >=0.1.22.0,
         rio-prettyprint >=0.1.8.0,
         split >=0.2.5,
@@ -548,8 +549,8 @@
         bytestring >=0.12.1.0,
         casa-client >=0.0.2,
         companion >=0.1.0,
-        conduit >=1.3.6,
-        conduit-extra >=1.3.6,
+        conduit >=1.3.6.1,
+        conduit-extra >=1.3.7,
         containers >=0.6.8,
         crypton >=1.0.1,
         directory >=1.3.8.5,
@@ -566,7 +567,7 @@
         hi-file-parser >=0.1.7.0,
         hpack >=0.36.0,
         hpc >=0.7.0.0,
-        http-client >=0.7.17,
+        http-client >=0.7.18,
         http-client-tls >=0.3.6.2,
         http-conduit >=2.3.9.1,
         http-download >=0.2.1.0,
@@ -586,7 +587,7 @@
         pretty >=1.1.3.6,
         process >=1.6.13.2,
         project-template >=0.2.1.0,
-        random >=1.2.1.2,
+        random >=1.2.1.3,
         rio >=0.1.22.0,
         rio-prettyprint >=0.1.8.0,
         split >=0.2.5,
@@ -656,8 +657,8 @@
         bytestring >=0.12.1.0,
         casa-client >=0.0.2,
         companion >=0.1.0,
-        conduit >=1.3.6,
-        conduit-extra >=1.3.6,
+        conduit >=1.3.6.1,
+        conduit-extra >=1.3.7,
         containers >=0.6.8,
         crypton >=1.0.1,
         directory >=1.3.8.5,
@@ -674,8 +675,8 @@
         hi-file-parser >=0.1.7.0,
         hpack >=0.36.0,
         hpc >=0.7.0.0,
-        hspec >=2.11.10,
-        http-client >=0.7.17,
+        hspec >=2.11.11,
+        http-client >=0.7.18,
         http-client-tls >=0.3.6.2,
         http-conduit >=2.3.9.1,
         http-download >=0.2.1.0,
@@ -696,7 +697,7 @@
         pretty >=1.1.3.6,
         process >=1.6.13.2,
         project-template >=0.2.1.0,
-        random >=1.2.1.2,
+        random >=1.2.1.3,
         rio >=0.1.22.0,
         rio-prettyprint >=0.1.8.0,
         split >=0.2.5,
@@ -782,8 +783,8 @@
         bytestring >=0.12.1.0,
         casa-client >=0.0.2,
         companion >=0.1.0,
-        conduit >=1.3.6,
-        conduit-extra >=1.3.6,
+        conduit >=1.3.6.1,
+        conduit-extra >=1.3.7,
         containers >=0.6.8,
         crypton >=1.0.1,
         directory >=1.3.8.5,
@@ -800,8 +801,8 @@
         hi-file-parser >=0.1.7.0,
         hpack >=0.36.0,
         hpc >=0.7.0.0,
-        hspec >=2.11.10,
-        http-client >=0.7.17,
+        hspec >=2.11.11,
+        http-client >=0.7.18,
         http-client-tls >=0.3.6.2,
         http-conduit >=2.3.9.1,
         http-download >=0.2.1.0,
@@ -821,7 +822,7 @@
         pretty >=1.1.3.6,
         process >=1.6.13.2,
         project-template >=0.2.1.0,
-        random >=1.2.1.2,
+        random >=1.2.1.3,
         raw-strings-qq >=1.1,
         rio >=0.1.22.0,
         rio-prettyprint >=0.1.8.0,
diff --git a/stack.yaml b/stack.yaml
--- a/stack.yaml
+++ b/stack.yaml
@@ -1,4 +1,8 @@
-snapshot: lts-23.0 # GHC 9.8.4
+snapshot: lts-23.14 # GHC 9.8.4
+
+extra-deps:
+# lts-23.14 provides hpack-0.37.0
+- hpack-0.38.0
 
 docker:
   enable: false
diff --git a/tests/integration/lib/StackTest.hs b/tests/integration/lib/StackTest.hs
--- a/tests/integration/lib/StackTest.hs
+++ b/tests/integration/lib/StackTest.hs
@@ -362,7 +362,7 @@
 -- the main @stack.yaml@.
 --
 defaultSnapshotArg :: String
-defaultSnapshotArg = "--snapshot=lts-23.0"
+defaultSnapshotArg = "--snapshot=lts-23.14"
 
 -- | Remove a file and ignore any warnings about missing files.
 removeFileIgnore :: HasCallStack => FilePath -> IO ()
diff --git a/tests/unit/Stack/ConfigSpec.hs b/tests/unit/Stack/ConfigSpec.hs
--- a/tests/unit/Stack/ConfigSpec.hs
+++ b/tests/unit/Stack/ConfigSpec.hs
@@ -53,12 +53,12 @@
 
 sampleConfig :: String
 sampleConfig =
-  "snapshot: lts-23.0\n" ++
+  "snapshot: lts-23.14\n" ++
   "packages: ['.']\n"
 
 buildOptsConfig :: String
 buildOptsConfig =
-  "snapshot: lts-23.0\n" ++
+  "snapshot: lts-23.14\n" ++
   "packages: ['.']\n" ++
   "build:\n" ++
   "  library-profiling: true\n" ++
@@ -104,7 +104,7 @@
 
 buildOptsHaddockForHackageConfig :: String
 buildOptsHaddockForHackageConfig =
-  "snapshot: lts-23.0\n" ++
+  "snapshot: lts-23.14\n" ++
   "packages: ['.']\n" ++
   "build:\n" ++
   "  haddock: true\n" ++
@@ -120,24 +120,24 @@
 
 hpackConfig :: String
 hpackConfig =
-  "snapshot: lts-23.0\n" ++
+  "snapshot: lts-23.14\n" ++
   "with-hpack: /usr/local/bin/hpack\n" ++
   "packages: ['.']\n"
 
 resolverConfig :: String
 resolverConfig =
-  "resolver: lts-23.0\n" ++
+  "resolver: lts-23.14\n" ++
   "packages: ['.']\n"
 
 snapshotConfig :: String
 snapshotConfig =
-  "snapshot: lts-23.0\n" ++
+  "snapshot: lts-23.14\n" ++
   "packages: ['.']\n"
 
 resolverSnapshotConfig :: String
 resolverSnapshotConfig =
-  "resolver: lts-23.0\n" ++
-  "snapshot: lts-23.0\n" ++
+  "resolver: lts-23.14\n" ++
+  "snapshot: lts-23.14\n" ++
   "packages: ['.']\n"
 
 stackDotYaml :: Path Rel File
@@ -187,11 +187,11 @@
 
     it "parses snapshot using 'resolver'" $ inTempDir $ do
       loadProject resolverConfig $ \project ->
-        project.snapshot `shouldBe` RSLSynonym (LTS 23 0)
+        project.snapshot `shouldBe` RSLSynonym (LTS 23 14)
 
     it "parses snapshot using 'snapshot'" $ inTempDir $ do
       loadProject snapshotConfig $ \project ->
-        project.snapshot `shouldBe` RSLSynonym (LTS 23 0)
+        project.snapshot `shouldBe` RSLSynonym (LTS 23 14)
 
     it "throws if both 'resolver' and 'snapshot' are present" $ inTempDir $ do
       loadProject resolverSnapshotConfig (const (pure ()))
