diff --git a/hix.cabal b/hix.cabal
--- a/hix.cabal
+++ b/hix.cabal
@@ -5,7 +5,7 @@
 -- see: https://github.com/sol/hpack
 
 name:           hix
-version:        0.7.1
+version:        0.7.2
 synopsis:       Haskell/Nix development build tools
 description:    See https://hackage.haskell.org/package/hix/docs/Hix.html
 category:       Build
@@ -65,6 +65,8 @@
       Hix.Log
       Hix.Managed.Build
       Hix.Managed.Build.Mutation
+      Hix.Managed.Build.NixOutput
+      Hix.Managed.Build.NixOutput.Analysis
       Hix.Managed.Build.Single
       Hix.Managed.Build.Solve
       Hix.Managed.BuildOutput
@@ -91,6 +93,7 @@
       Hix.Managed.Cabal.Resources
       Hix.Managed.Cabal.Solve
       Hix.Managed.Cabal.Sort
+      Hix.Managed.Cabal.Source
       Hix.Managed.Cabal.Targets
       Hix.Managed.Constraints
       Hix.Managed.Data.BuildConfig
@@ -231,7 +234,7 @@
   ghc-options: -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages
   build-depends:
       Cabal
-    , aeson >=2.0 && <2.2
+    , aeson >=2.0 && <2.3
     , base ==4.*
     , bytestring
     , cabal-install
@@ -331,6 +334,7 @@
       Hix.Test.Managed.BuildOutputTest
       Hix.Test.Managed.Bump.CandidatesTest
       Hix.Test.Managed.Bump.MutationTest
+      Hix.Test.Managed.BumpNativeTest
       Hix.Test.Managed.DiffTest
       Hix.Test.Managed.LowerAuto.MutationOptimizeTest
       Hix.Test.Managed.LowerAuto.MutationStabilizeTest
@@ -391,7 +395,7 @@
   ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages
   build-depends:
       Cabal
-    , aeson >=2.0 && <2.2
+    , aeson >=2.0 && <2.3
     , base ==4.*
     , exon >=1.4 && <1.7
     , extra ==1.7.*
diff --git a/lib/Hix/Class/Map.hs b/lib/Hix/Class/Map.hs
--- a/lib/Hix/Class/Map.hs
+++ b/lib/Hix/Class/Map.hs
@@ -713,3 +713,11 @@
   map
 nWithoutKeys keys =
   nVia (`Map.withoutKeys` keys)
+
+nMember ::
+  NMap map k v sort =>
+  k ->
+  map ->
+  Bool
+nMember k =
+  Map.member k . nGet
diff --git a/lib/Hix/Data/Json.hs b/lib/Hix/Data/Json.hs
--- a/lib/Hix/Data/Json.hs
+++ b/lib/Hix/Data/Json.hs
@@ -31,3 +31,14 @@
   Parser a
 foldMissing o k =
   fold <$> o .:? k
+
+newtype JsonEither a b =
+  JsonEither (Either a b)
+  deriving stock (Eq, Show)
+
+jsonEither :: JsonEither a b -> Either a b
+jsonEither = coerce
+
+instance (FromJSON a, FromJSON b) => FromJSON (JsonEither a b) where
+  parseJSON v =
+    JsonEither <$> ((Right <$> parseJSON v) <|> (Left <$> parseJSON v))
diff --git a/lib/Hix/Data/PackageId.hs b/lib/Hix/Data/PackageId.hs
--- a/lib/Hix/Data/PackageId.hs
+++ b/lib/Hix/Data/PackageId.hs
@@ -14,7 +14,7 @@
     name :: PackageName,
     version :: Version
   }
-  deriving stock (Eq, Show, Generic)
+  deriving stock (Eq, Show, Ord, Generic)
 
 renderPackage :: PackageId -> Text
 renderPackage PackageId {..} =
diff --git a/lib/Hix/Error.hs b/lib/Hix/Error.hs
--- a/lib/Hix/Error.hs
+++ b/lib/Hix/Error.hs
@@ -71,18 +71,18 @@
   [exon|#{reason} the source file '#{pathText source}'|]
 
 catchIO ::
-  (Text -> ExceptT Error IO a) ->
+  (Text -> ExceptT e IO a) ->
   IO a ->
-  ExceptT Error IO a
+  ExceptT e IO a
 catchIO handleError ma =
   liftIO (tryIOError ma) >>= \case
     Right a -> pure a
     Left err -> handleError (show err)
 
 tryIOWith ::
-  (Text -> Error) ->
+  (Text -> e) ->
   IO a ->
-  ExceptT Error IO a
+  ExceptT e IO a
 tryIOWith mkErr ma =
   liftIO (tryIOError ma) >>= \case
     Right a -> pure a
diff --git a/lib/Hix/Managed/Build.hs b/lib/Hix/Managed/Build.hs
--- a/lib/Hix/Managed/Build.hs
+++ b/lib/Hix/Managed/Build.hs
@@ -2,17 +2,22 @@
 
 import Control.Monad (foldM)
 import qualified Data.Map.Strict as Map
+import qualified Data.Text as Text
 import Distribution.Pretty (Pretty)
 import Exon (exon)
 import Text.PrettyPrint (vcat)
 
-import Hix.Data.EnvName (EnvName)
+import qualified Hix.Console
+import Hix.Console (color, colors)
+import Hix.Data.EnvName (EnvName (EnvName))
 import Hix.Data.Monad (M)
 import Hix.Data.Overrides (Overrides)
+import qualified Hix.Data.PackageId
 import Hix.Data.PackageId (PackageId)
 import Hix.Data.Version (Version, Versions)
 import Hix.Data.VersionBounds (VersionBounds)
 import qualified Hix.Log as Log
+import Hix.Managed.Build.NixOutput (PackageDerivation (..))
 import Hix.Managed.Build.Solve (solveMutation)
 import qualified Hix.Managed.Cabal.Changes
 import Hix.Managed.Cabal.Config (isNonReinstallableDep, isReinstallableId)
@@ -33,13 +38,12 @@
 import Hix.Managed.Data.StageContext (StageContext (StageContext))
 import qualified Hix.Managed.Data.StageState
 import Hix.Managed.Data.StageState (
-  BuildResult (Finished, TimedOut),
-  BuildStatus (Failure, Success),
-  StageState,
+  BuildFailure (PackageFailure, TimeoutFailure),
+  BuildResult (BuildFailure, BuildSuccess),
+  BuildStatus,
+  StageState (failed, iterations),
   buildStatus,
-  failed,
   initStageState,
-  iterations,
   justSuccess,
   )
 import qualified Hix.Managed.Handlers.Build
@@ -57,18 +61,28 @@
   [PackageId] ->
   M ()
 logBuildInputs env description overrides = do
-  Log.info [exon|Building targets in '##{env}' with #{description}...|]
+  Log.info [exon|Building targets in #{color colors.yellow (coerce env)} with #{description}...|]
   Log.debugP (vcat ["Overrides:", prettyL overrides])
 
 logBuildResult :: Text -> BuildResult -> M ()
 logBuildResult description result =
-  Log.info [exon|Build with ##{description} #{describeResult result}|]
+  Log.info [exon|Build with ##{description} #{describeResult result}#{describePackages result}|]
   where
     describeResult = \case
-      Finished Success -> "succeeded"
-      Finished Failure -> "failed"
-      TimedOut -> "timed out"
+      BuildSuccess -> "succeeded"
+      BuildFailure (TimeoutFailure _) -> "timed out"
+      BuildFailure _ -> "failed"
 
+    describePackages = \case
+      BuildFailure (TimeoutFailure pkgs) | not (null pkgs) -> packageFragment pkgs
+      BuildFailure (PackageFailure pkgs) -> packageFragment ((.package) <$> toList pkgs)
+      _ -> ""
+
+    packageFragment pkgs =
+      [exon| in #{names}|]
+      where
+        names = Text.intercalate ", " (color colors.blue . showP . (.name) <$> pkgs)
+
 updateMutationState ::
   (Version -> VersionBounds -> VersionBounds) ->
   Versions ->
@@ -84,20 +98,19 @@
   }
 
 -- | /Note/: This quietly discards non-reinstallable packages.
---
--- TODO Discard installed versions, for builds with initial versions.
 buildVersions ::
   EnvBuilder ->
   EnvContext ->
   Text ->
+  Bool ->
   Versions ->
   [PackageId] ->
-  M (Overrides, BuildStatus)
-buildVersions builder context description versions overrideVersions = do
+  M (Overrides, Set PackageId, BuildStatus)
+buildVersions builder context description allowRevisions versions overrideVersions = do
   logBuildInputs context.env description reinstallable
-  (overrides, status) <- builder.buildWithState versions reinstallable
-  logBuildResult description status
-  pure (overrides, buildStatus status)
+  (result, (overrides, revisions)) <- builder.buildWithState allowRevisions versions reinstallable
+  logBuildResult description result
+  pure (overrides, revisions, buildStatus result)
   where
     reinstallable = filter isReinstallableId overrideVersions
 
@@ -105,25 +118,30 @@
   EnvBuilder ->
   EnvContext ->
   Text ->
+  Bool ->
+  Set PackageId ->
   SolverState ->
-  M (Maybe (Versions, Overrides, BuildStatus))
-buildConstraints builder context description state =
-  solveMutation builder.cabal context.deps state >>= traverse \ changes -> do
-    (overrides, status) <- buildVersions builder context description changes.versions changes.overrides
-    pure (changes.versions, overrides, status)
+  M (Maybe (Versions, Overrides, Set PackageId, BuildStatus))
+buildConstraints builder context description allowRevisions prevRevisions state =
+  solveMutation builder.cabal context.deps prevRevisions state >>= traverse \ changes -> do
+    (overrides, revisions, status) <-
+      buildVersions builder context description allowRevisions changes.versions changes.overrides
+    pure (changes.versions, overrides, prevRevisions <> revisions, status)
 
 buildMutation ::
   EnvBuilder ->
   EnvContext ->
   MutationState ->
+  Set PackageId ->
   BuildMutation ->
-  M (Maybe MutationState)
-buildMutation builder context state BuildMutation {description, solverState, updateBound} =
-  result <$> buildConstraints builder context description solverState
+  M (Maybe (MutationState, Set PackageId))
+buildMutation builder context state prevRevisions BuildMutation {description, solverState, updateBound} =
+  result <$> buildConstraints builder context description True prevRevisions solverState
   where
     result = \case
-      Just (versions, overrides, status) ->
-        justSuccess (updateMutationState updateBound versions overrides state) status
+      Just (versions, overrides, revisions, status) -> do
+        new <- justSuccess (updateMutationState updateBound versions overrides state) status
+        pure (new, revisions)
       Nothing -> Nothing
 
 logMutationResult ::
@@ -131,14 +149,14 @@
   MutationResult s ->
   M ()
 logMutationResult package = \case
-  MutationSuccess candidate True _ _ ->
+  MutationSuccess {candidate, changed = True} ->
     Log.verbose [exon|Build succeeded for #{showP candidate}|]
-  MutationSuccess _ False _ _ ->
+  MutationSuccess {changed = False} ->
     Log.verbose [exon|Build is up to date for '##{package}'|]
   MutationKeep ->
     Log.verbose [exon|No better version found for '##{package}'|]
   MutationFailed ->
-    Log.verbose [exon|Could not find a buildable version of '##{package}'|]
+    Log.verbose [exon|Could not find a buildable version of #{color colors.blue (showP package)}|]
 
 validateMutation ::
   EnvBuilder ->
@@ -157,7 +175,7 @@
       = pure MutationKeep
       | otherwise
       = handlers.process stageState.ext mutation build
-    build = buildMutation envBuilder context stageState.state
+    build = buildMutation envBuilder context stageState.state stageState.revisions
 
 convergeMutations ::
   Pretty a =>
diff --git a/lib/Hix/Managed/Build/Mutation.hs b/lib/Hix/Managed/Build/Mutation.hs
--- a/lib/Hix/Managed/Build/Mutation.hs
+++ b/lib/Hix/Managed/Build/Mutation.hs
@@ -56,18 +56,18 @@
 -- TODO If we'd use the @retract@ field from @DepMutation@ and the target bound here, we could probably use a universal
 -- bounds updater without leaking implementation...investigate.
 buildCandidate ::
-  (BuildMutation -> M (Maybe MutationState)) ->
+  (BuildMutation -> M (Maybe (MutationState, Set PackageId))) ->
   (Version -> VersionBounds -> VersionBounds) ->
   (MutableId -> PackageId -> MutationConstraints -> MutationConstraints) ->
   SolverState ->
   MutableDep ->
   Version ->
-  M (Maybe (MutableId, SolverState, MutationState))
+  M (Maybe (MutableId, SolverState, MutationState, Set PackageId))
 buildCandidate build updateStateBound updateConstraintBound solverState package version = do
   Log.debug [exon|Mutation constraints for #{showP candidate}: #{showP mutationSolverState.constraints}|]
   fmap result <$> build (candidateMutation mutationSolverState candidate updateStateBound)
   where
-    result newState = (candidate, newSolverState newState, newState)
+    result (newState, revisions) = (candidate, newSolverState newState, newState, revisions)
 
     candidate = MutableId {name = package, version}
 
diff --git a/lib/Hix/Managed/Build/NixOutput.hs b/lib/Hix/Managed/Build/NixOutput.hs
new file mode 100644
--- /dev/null
+++ b/lib/Hix/Managed/Build/NixOutput.hs
@@ -0,0 +1,226 @@
+module Hix.Managed.Build.NixOutput where
+
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.State.Strict (StateT (runStateT), modify', state)
+import qualified Data.Aeson as Aeson
+import Data.Aeson (FromJSON, Value (String), withObject, (.:), (.:?))
+import qualified Data.ByteString.Char8 as ByteString
+import qualified Data.Map.Strict as Map
+import Data.Map.Strict ((!?))
+import qualified Data.Sequence as Seq
+import Data.Sequence ((|>))
+import Distribution.Compat.CharParsing (
+  CharParsing (char, notChar, string),
+  Parsing (notFollowedBy, try),
+  digit,
+  letter,
+  lower,
+  )
+import Distribution.Parsec (Parsec, eitherParsec, parsec)
+import Exon (exon)
+
+import Hix.Data.Json (jsonEither)
+import Hix.Data.Monad (M)
+import qualified Hix.Data.PackageId as PackageId
+import Hix.Data.PackageId (PackageId (..))
+import qualified Hix.Log as Log
+import Hix.Pretty (showP)
+
+data Derivation =
+  Derivation {
+    path :: Text,
+    log :: (Seq Text, Seq Text)
+  }
+  deriving stock (Eq, Show, Generic)
+
+data PackageDerivation =
+  PackageDerivation {
+    package :: PackageId,
+    success :: Bool,
+    log :: [Text]
+  }
+  deriving stock (Eq, Show, Generic)
+
+data BuildsState =
+  BuildsState {
+    id :: Integer,
+    done :: Int,
+    failed :: Int,
+    unassigned :: [Bool]
+  }
+  deriving stock (Eq, Show, Generic)
+
+data OutputState =
+  OutputState {
+    builds :: Maybe BuildsState,
+    running :: Map Integer Derivation,
+    finished :: [PackageDerivation]
+  }
+  deriving stock (Eq, Show, Generic)
+  deriving anyclass (Default)
+
+data OutputResult =
+  OutputResult {
+    failedPackages :: Maybe (NonEmpty PackageDerivation)
+  }
+  deriving stock (Eq, Show, Generic)
+
+outputResult :: OutputState -> OutputResult
+outputResult OutputState {finished} =
+  OutputResult {failedPackages = nonEmpty (filter (not . (.success)) finished)}
+
+runOutputState ::
+  Monad m =>
+  StateT OutputState m a ->
+  m (a, OutputResult)
+runOutputState ma =
+  second outputResult <$> runStateT ma def
+
+data NixAction =
+  NixResult { aid :: Integer, rtype :: Int, fields :: [Either Text Int] }
+  |
+  NixStartBuilds Integer
+  |
+  NixStart Integer Text
+  |
+  NixStop Integer
+  |
+  NixStartOther Integer
+  |
+  NixMessage
+  deriving stock (Eq, Show, Generic)
+
+instance FromJSON NixAction where
+  parseJSON =
+    withObject "NixOutput" \ o ->
+      o .: "action" >>= \case
+        "result" -> do
+          aid <- o .: "id"
+          rtype <- o .: "type"
+          fields <- fmap jsonEither <$> o .: "fields"
+          pure NixResult {..}
+        "start" -> do
+          i <- o .: "id"
+          o .:? "type" >>= \case
+            Just (105 :: Int) ->
+              o .: "fields" >>= \case
+                String path : _ -> pure (NixStart i path)
+                _ -> pure (NixStartOther i)
+            Just 104 ->
+              pure (NixStartBuilds i)
+            _ -> pure (NixStartOther i)
+        "stop" -> do
+          i <- o .: "id"
+          pure (NixStop i)
+        "msg" -> pure NixMessage
+        (act :: Text) -> fail [exon|Unknown action: #{toString act}|]
+
+parseError :: String -> StateT s M ()
+parseError err =
+  lift $ Log.debug [exon|Nix output message parse error: #{toText err}|]
+
+newtype StorePathName =
+  StorePathName String
+  deriving stock (Eq, Show, Generic)
+
+instance Parsec StorePathName where
+  parsec = do
+    string "/nix/store/"
+    some (lower <|> digit)
+    char '-'
+    pid0 <- some (notChar '.')
+    pid <- some (notChar '.' <|> (try (char '.' <* notFollowedBy letter)))
+    string ".drv"
+    pure (StorePathName (pid0 <> pid))
+
+addLogMessage :: Text -> Derivation -> Derivation
+addLogMessage message Derivation {log = (current, prev), ..} =
+  Derivation {log = updated, ..}
+  where
+    updated | Seq.length current > 100 = (pure message, current)
+            | otherwise = (current |> message, prev)
+
+finish :: Bool -> Derivation -> Either String PackageDerivation
+finish success Derivation {path, log = (current, prev)} = do
+  StorePathName name <- eitherParsec (toString path)
+  package <- PackageId.fromCabal <$> eitherParsec name
+  pure PackageDerivation {package, success, log = toList (prev <> current)}
+
+tryFinish ::
+  Maybe BuildsState ->
+  Maybe Derivation ->
+  (Maybe (Either Derivation PackageDerivation), [PackageDerivation], Maybe BuildsState)
+tryFinish builds = \case
+  Nothing -> (Nothing, [], builds)
+  Just drv ->
+    case finish success drv of
+      Right pkg -> (Just (Right pkg), [pkg], newBuilds)
+      Left _ -> (Just (Left drv), [], newBuilds)
+  where
+    (success, newBuilds) = case builds of
+      Just s@BuildsState {unassigned = h : t} ->
+        (h, Just s {unassigned = t})
+      _ ->
+        (False, builds)
+
+reportFinished :: Either Derivation PackageDerivation -> StateT OutputState M ()
+reportFinished result =
+  lift $ Log.debug [exon|Nix build of #{desc} #{status}|]
+  where
+    (desc, status) = case result of
+      Right PackageDerivation {package, success} -> (showP package, if success then "succeeded" else "failed")
+      Left Derivation {path} -> (path, "finished with unknown status")
+
+updateBuilds :: [Either Text Int] -> OutputState -> OutputState
+updateBuilds [Right updatedDone, _, _, Right updatedFailed] s@OutputState {builds = Just bs@BuildsState {done, failed, unassigned}}
+  | newDone > 0
+  , newFailed == 0
+  = s {builds = Just newBs {unassigned = unassigned ++ replicate newDone True}}
+  | newFailed > 0
+  , newDone == 0
+  = s {builds = Just newBs {unassigned = unassigned ++ replicate newFailed False}}
+  where
+    newBs = bs {done = updatedDone, failed = updatedFailed}
+    newDone = updatedDone - done
+    newFailed = updatedFailed - failed
+updateBuilds _ s = s
+
+processResult :: Integer -> Int -> [Either Text Int] -> OutputState -> OutputState
+processResult aid rtype fields s
+  | rtype == 105
+  , Just BuildsState {id = buildsId} <- s.builds
+  , aid == buildsId
+  = updateBuilds fields s
+  | rtype == 101
+  , Left message : _ <- fields
+  = s {running = Map.adjust (addLogMessage message) aid s.running}
+  | otherwise
+  = s
+
+processMessage ::
+  ByteString ->
+  NixAction ->
+  StateT OutputState M ()
+processMessage _ = \case
+  NixResult {aid, rtype, fields} -> do
+    -- lift (Log.debug (decodeUtf8 payload))
+    modify' (processResult aid rtype fields)
+  NixStartBuilds i -> modify' \ s -> s {builds = Just BuildsState {id = i, done = 0, failed = 0, unassigned = []}}
+  NixStart i path -> do
+    lift $ Log.debug [exon|Started build of #{path} (#{show i})|]
+    modify' \ OutputState {running, ..} ->
+      OutputState {running = Map.insert i (Derivation path mempty) running, ..}
+  NixStop i -> do
+    result <- state \ OutputState {running, ..} -> do
+      let (result, package, newBuilds) = tryFinish builds (running !? i)
+      (result, OutputState {running = Map.delete i running, finished = finished ++ package, builds = newBuilds, ..})
+    traverse_ reportFinished result
+  NixStartOther _ -> unit
+  NixMessage -> unit
+
+outputParse :: ByteString -> StateT OutputState M ()
+outputParse outputLine
+  | Just payload <- ByteString.stripPrefix "@nix " outputLine
+  = either parseError (processMessage payload) (Aeson.eitherDecodeStrict' payload)
+  | otherwise
+  = lift (Log.debug (decodeUtf8 outputLine))
diff --git a/lib/Hix/Managed/Build/NixOutput/Analysis.hs b/lib/Hix/Managed/Build/NixOutput/Analysis.hs
new file mode 100644
--- /dev/null
+++ b/lib/Hix/Managed/Build/NixOutput/Analysis.hs
@@ -0,0 +1,16 @@
+module Hix.Managed.Build.NixOutput.Analysis where
+
+import Distribution.Parsec (simpleParsec)
+import Distribution.Simple (Dependency)
+
+import qualified Hix.Data.Dep as Dep
+import Hix.Data.Dep (Dep)
+
+analyzeLog :: [Text] -> Maybe (NonEmpty Dep)
+analyzeLog log =
+  nonEmpty bounds
+  where
+    bounds = catMaybes (takeWhile isJust (parseDep <$> afterBoundsMarker))
+    afterBoundsMarker = drop 1 (dropWhile (boundsMarker /=) log)
+    boundsMarker = "Error: Setup: Encountered missing or private dependencies:"
+    parseDep = fmap Dep.fromCabal . simpleParsec @Dependency . toString
diff --git a/lib/Hix/Managed/Build/Single.hs b/lib/Hix/Managed/Build/Single.hs
--- a/lib/Hix/Managed/Build/Single.hs
+++ b/lib/Hix/Managed/Build/Single.hs
@@ -11,6 +11,8 @@
 import Hix.Managed.Data.StageState (BuildStatus (Failure))
 import Hix.Managed.Handlers.Build (EnvBuilder)
 
+-- | Passes 'False' to 'buildConstraints' to disable revisions, since this function is used to check whether the current
+-- configuration builds successful, and revisions modify the set of overrides on failure.
 buildVersions ::
   EnvBuilder ->
   EnvContext ->
@@ -18,8 +20,8 @@
   MutableVersions ->
   M BuildStatus
 buildVersions builder context description versions =
-  buildConstraints builder context description solver <&> \case
-    Just (_, _, status) -> status
+  buildConstraints builder context description False [] solver <&> \case
+    Just (_, _, _, status) -> status
     Nothing -> Failure
   where
     solver = solverState context.solverBounds context.deps (fromVersions exactVersion versions) def
diff --git a/lib/Hix/Managed/Build/Solve.hs b/lib/Hix/Managed/Build/Solve.hs
--- a/lib/Hix/Managed/Build/Solve.hs
+++ b/lib/Hix/Managed/Build/Solve.hs
@@ -1,9 +1,11 @@
 module Hix.Managed.Build.Solve where
 
+import Data.List (partition)
 import qualified Data.Set as Set
+import Distribution.PackageDescription (customFieldsPD)
 import Distribution.Pretty (pretty)
 import Exon (exon)
-import Text.PrettyPrint (hang)
+import Text.PrettyPrint (hang, ($$), (<+>))
 
 import Hix.Class.Map (nRestrictKeys)
 import Hix.Data.Monad (M)
@@ -14,38 +16,65 @@
 import qualified Hix.Log as Log
 import qualified Hix.Managed.Cabal.Changes
 import Hix.Managed.Cabal.Changes (SolverChanges (SolverChanges), SolverPlan (SolverPlan))
+import qualified Hix.Managed.Cabal.Data.SolverState
 import Hix.Managed.Cabal.Data.SolverState (SolverState)
 import qualified Hix.Managed.Data.EnvContext
 import Hix.Managed.Data.EnvContext (EnvDeps)
 import Hix.Managed.Data.Mutable (depName)
 import qualified Hix.Managed.Handlers.Cabal
 import Hix.Managed.Handlers.Cabal (CabalHandlers)
-import Hix.Pretty (showPL)
+import Hix.Pretty (prettyL, showPL)
 
 logNonReinstallable :: NonEmpty PackageId -> M ()
 logNonReinstallable ids =
   Log.verbose [exon|NOTE: Cabal solver suggested new versions for non-reinstallable packages: #{showPL ids}|]
 
--- TODO Do we need to filter locals out of @matching@ or can those not be in there?
+checkRevision ::
+  Bool ->
+  CabalHandlers ->
+  PackageId ->
+  Either PackageId PackageId
+checkRevision forceRevisions cabal package
+  | forceRevisions
+  , Just packageDesc <- cabal.sourcePackage package
+  , any isRevision packageDesc.customFieldsPD
+  = Right package
+  | otherwise
+  = Left package
+  where
+    isRevision = \case
+      ("x-revision", rev) | rev /= "0" -> True
+      _ -> False
+
 processSolverPlan ::
+  Bool ->
+  CabalHandlers ->
   EnvDeps ->
+  Set PackageId ->
   SolverPlan ->
   M SolverChanges
-processSolverPlan deps SolverPlan {..} = do
-  Log.debugP (hang "New project deps from solver:" 2 (pretty projectDeps))
+processSolverPlan forceRevisions cabal deps prevRevisions SolverPlan {..} = do
+  Log.debugP $
+    hang "New project deps from solver:" 2 (pretty projectDeps) $$
+    "Forced revisions:" <+> prettyL forcedRevisions $$
+    "Reused revisions:" <+> prettyL reusedRevisions
   traverse_ logNonReinstallable nonReinstallable
   pure SolverChanges {versions, overrides, projectDeps}
   where
     projectDeps = nRestrictKeys mutablePIds versions
-    versions = packageIdVersions (overrides ++ matching)
-    overrides = filter notLocal changes
+    versions = packageIdVersions (overrides ++ installed)
+    overrides = filter notLocal (changes ++ forcedRevisions ++ reusedRevisions)
+    (reusedRevisions, installed) = partition (flip Set.member prevRevisions) noForcedRevisions
+    (noForcedRevisions, forcedRevisions) = partitionEithers (checkRevision forceRevisions cabal <$> matching)
     notLocal PackageId {name} = not (isLocalPackage deps.local name)
     mutablePIds = Set.fromList (depName <$> Set.toList deps.mutable)
 
+-- TODO probably best to store the revisions in the SolverState
 solveMutation ::
   CabalHandlers ->
   EnvDeps ->
+  Set PackageId ->
   SolverState ->
   M (Maybe SolverChanges)
-solveMutation cabal deps state =
-  traverse (processSolverPlan deps) =<< cabal.solveForVersion state
+solveMutation cabal deps prevRevisions state =
+  traverse (processSolverPlan state.flags.forceRevisions cabal deps prevRevisions) =<< cabal.solveForVersion state
diff --git a/lib/Hix/Managed/Bump/App.hs b/lib/Hix/Managed/Bump/App.hs
--- a/lib/Hix/Managed/Bump/App.hs
+++ b/lib/Hix/Managed/Bump/App.hs
@@ -6,7 +6,6 @@
 import Hix.Data.Options (BumpOptions (BumpOptions))
 import Hix.Json (jsonConfigE)
 import Hix.Managed.Bump.Optimize (bumpOptimizeMain)
-import qualified Hix.Managed.Data.BuildConfig
 import qualified Hix.Managed.Data.ProjectContextProto
 import Hix.Managed.Handlers.Build.Test (chooseHandlers)
 import Hix.Managed.ProjectContext (withProjectContext)
@@ -15,6 +14,5 @@
 bumpCli BumpOptions {common = opts} = do
   context <- jsonConfigE Client opts.context
   handlers <- liftIO $
-    chooseHandlers opts.stateFile context.envs context.buildOutputsPrefix opts.project.build.timeout opts.cabal
-      opts.handlers
+    chooseHandlers opts.stateFile context.envs context.buildOutputsPrefix opts.project.build opts.cabal opts.handlers
   withProjectContext handlers opts.project context (bumpOptimizeMain handlers)
diff --git a/lib/Hix/Managed/Bump/Optimize.hs b/lib/Hix/Managed/Bump/Optimize.hs
--- a/lib/Hix/Managed/Bump/Optimize.hs
+++ b/lib/Hix/Managed/Bump/Optimize.hs
@@ -83,7 +83,7 @@
   pure (stageResult success failure result)
   where
     ext = solverState env.solverBounds env.deps (bumpSolverParams env.deps builder.cabal state) flags
-    flags = SolverFlags {allowNewer = True}
+    flags = SolverFlags {allowNewer = True, forceRevisions = False}
 
 bumpBuildStage ::
   BuildHandlers ->
diff --git a/lib/Hix/Managed/Cabal/Data/SolverState.hs b/lib/Hix/Managed/Cabal/Data/SolverState.hs
--- a/lib/Hix/Managed/Cabal/Data/SolverState.hs
+++ b/lib/Hix/Managed/Cabal/Data/SolverState.hs
@@ -22,12 +22,13 @@
 
 data SolverFlags =
   SolverFlags {
-    allowNewer :: Bool
+    allowNewer :: Bool,
+    forceRevisions :: Bool
   }
   deriving stock (Eq, Show, Generic)
 
 instance Default SolverFlags where
-  def = SolverFlags {allowNewer = False}
+  def = SolverFlags {allowNewer = False, forceRevisions = False}
 
 flagAllowNewer :: DepResolverParams -> DepResolverParams
 flagAllowNewer =
diff --git a/lib/Hix/Managed/Cabal/Data/SourcePackage.hs b/lib/Hix/Managed/Cabal/Data/SourcePackage.hs
--- a/lib/Hix/Managed/Cabal/Data/SourcePackage.hs
+++ b/lib/Hix/Managed/Cabal/Data/SourcePackage.hs
@@ -1,30 +1,41 @@
 module Hix.Managed.Cabal.Data.SourcePackage where
 
+import Distribution.PackageDescription (PackageDescription)
 import Distribution.Pretty (Pretty (pretty))
 import Distribution.Simple (Version)
 import GHC.Exts (IsList)
 
-import Hix.Class.Map (LookupMaybe, LookupMonoid, NMap, nPretty1, nPrettyWith)
+import Hix.Class.Map (LookupMaybe, LookupMonoid, NMap, nPretty, nPretty1, nPrettyWith)
 import Hix.Data.Dep (Dep)
 import Hix.Data.PackageName (PackageName)
 import Hix.Pretty (prettyL)
 
-newtype SourcePackageDeps =
-  SourcePackageDeps (Map Version [Dep])
+data SourcePackageId =
+  SourcePackageId {
+    deps :: [Dep],
+    description :: Maybe PackageDescription
+  }
   deriving stock (Eq, Show, Generic)
+
+instance Pretty SourcePackageId where
+  pretty SourcePackageId {deps} = prettyL deps
+
+newtype SourcePackage =
+  SourcePackage (Map Version SourcePackageId)
+  deriving stock (Eq, Show, Generic)
   deriving newtype (Semigroup, Monoid, IsList)
 
-instance NMap SourcePackageDeps Version [Dep] LookupMonoid where
+instance NMap SourcePackage Version SourcePackageId LookupMonoid where
 
-instance Pretty SourcePackageDeps where
-  pretty = nPrettyWith prettyL
+instance Pretty SourcePackage where
+  pretty = nPretty
 
 newtype SourcePackages =
-  SourcePackages (Map PackageName SourcePackageDeps)
+  SourcePackages (Map PackageName SourcePackage)
   deriving stock (Eq, Show, Generic)
   deriving newtype (Semigroup, Monoid, IsList)
 
-instance NMap SourcePackages PackageName SourcePackageDeps LookupMaybe where
+instance NMap SourcePackages PackageName SourcePackage LookupMaybe where
 
 instance Pretty SourcePackages where
   pretty = nPretty1
diff --git a/lib/Hix/Managed/Cabal/Mock/SourcePackage.hs b/lib/Hix/Managed/Cabal/Mock/SourcePackage.hs
--- a/lib/Hix/Managed/Cabal/Mock/SourcePackage.hs
+++ b/lib/Hix/Managed/Cabal/Mock/SourcePackage.hs
@@ -3,39 +3,42 @@
 import qualified Data.Map.Strict as Map
 import Distribution.Client.Types (PackageLocation (LocalTarballPackage), SourcePackageDb (..), UnresolvedSourcePackage)
 import Distribution.PackageDescription (
+  BuildInfo (targetBuildDepends),
   CondTree (..),
   Library (libBuildInfo),
   LibraryVisibility (LibraryVisibilityPublic),
   PackageDescription (..),
   emptyLibrary,
-  emptyPackageDescription, BuildInfo (targetBuildDepends),
+  emptyPackageDescription,
   )
 import Distribution.Simple (Version)
 import qualified Distribution.Solver.Types.PackageIndex as PackageIndex
 import Distribution.Solver.Types.PackageIndex (PackageIndex)
-import Distribution.Solver.Types.SourcePackage (SourcePackage (..))
+import qualified Distribution.Solver.Types.SourcePackage as Cabal
 import Distribution.Types.GenericPackageDescription (GenericPackageDescription (..), emptyGenericPackageDescription)
 import Distribution.Types.Library (libVisibility)
 import Exon (exon)
 
-import Hix.Class.Map (nGet, nKeys, nMap, nTo1, nTransform, nVia, (!!))
+import Hix.Class.Map (nGet, nKeys, nMap, nTo1, nTransform, (!!))
 import qualified Hix.Data.Dep as Dep
 import Hix.Data.Dep (Dep)
 import Hix.Data.Monad (M)
 import qualified Hix.Data.PackageId as PackageId
 import Hix.Data.PackageId (PackageId (PackageId))
 import Hix.Data.PackageName (LocalPackage (LocalPackage), PackageName, localPackageName)
-import Hix.Managed.Cabal.Data.SourcePackage (SourcePackageDeps, SourcePackageVersions, SourcePackages)
+import Hix.Managed.Cabal.Data.SourcePackage (SourcePackage, SourcePackageId (..), SourcePackageVersions, SourcePackages)
 import qualified Hix.Managed.Data.ManagedPackage as ManagedPackage
 import Hix.Managed.Data.ManagedPackage (ManagedPackage)
 import Hix.Managed.Data.Packages (Packages)
 import Hix.Monad (noteClient)
 
-allDeps :: [Dep] -> SourcePackageDeps -> SourcePackageDeps
-allDeps deps = nVia (fmap (deps ++))
+allDeps :: [Dep] -> SourcePackage -> SourcePackage
+allDeps deps =
+  nMap \ p -> p {deps = deps ++ p.deps}
+  where
 
-allDep :: Dep -> SourcePackageDeps -> SourcePackageDeps
-allDep dep = nVia (fmap (dep :))
+allDep :: Dep -> SourcePackage -> SourcePackage
+allDep = allDeps . pure
 
 sourcePackageVersions :: SourcePackages -> SourcePackageVersions
 sourcePackageVersions = nMap (sort . nKeys)
@@ -59,12 +62,12 @@
 -- | This _must_ have the dependencies in both 'targetBuildDepends' and 'condTreeConstraints', otherwise they will be
 -- ignored or deleted depending on what resolver params are used.
 -- This is also evidenced by looking at a parsed Cabal file, see @CabalTest@.
-mockSourcePackage :: PackageName -> Version -> [Dep] -> UnresolvedSourcePackage
-mockSourcePackage name version deps =
-  SourcePackage {
+mockSourcePackage :: PackageName -> Version -> SourcePackageId -> UnresolvedSourcePackage
+mockSourcePackage name version SourcePackageId {description, deps} =
+  Cabal.SourcePackage {
     srcpkgPackageId = cabalId,
     srcpkgDescription = emptyGenericPackageDescription {
-      packageDescription = emptyPackageDescription {
+      packageDescription = (fromMaybe emptyPackageDescription description) {
         package = cabalId
       },
       condLibrary = Just library
@@ -97,7 +100,9 @@
 
 managedSourcePackage :: ManagedPackage -> UnresolvedSourcePackage
 managedSourcePackage package =
-  mockSourcePackage (localPackageName package.package) package.version (ManagedPackage.deps package)
+  mockSourcePackage (localPackageName package.package) package.version pid
+  where
+    pid = SourcePackageId {deps = ManagedPackage.deps package, description = Nothing}
 
 managedPackageIndex :: Packages ManagedPackage -> PackageIndex UnresolvedSourcePackage
 managedPackageIndex packages =
diff --git a/lib/Hix/Managed/Cabal/Solve.hs b/lib/Hix/Managed/Cabal/Solve.hs
--- a/lib/Hix/Managed/Cabal/Solve.hs
+++ b/lib/Hix/Managed/Cabal/Solve.hs
@@ -68,16 +68,23 @@
     pkgSpecifiers = (.dep) <$> targets
     prefs = (.prefs) =<< targets
 
-solveWithCabal ::
+solveWithCabal' ::
   SolveResources ->
   SolverState ->
-  M (Maybe SolverPlan)
-solveWithCabal solveResources SolverState {constraints, flags} = do
+  M (Maybe SolverInstallPlan)
+solveWithCabal' solveResources SolverState {constraints, flags} = do
   solveForTargets solveResources (compileSolverFlags flags) targets >>= \case
     Right plan ->
-      pure (Just (solverPlan plan))
+      pure (Just plan)
     Left err -> do
       Log.debug [exon|Solver found no plan: ##{err}|]
       pure Nothing
   where
     targets = solveTargets constraints
+
+solveWithCabal ::
+  SolveResources ->
+  SolverState ->
+  M (Maybe SolverPlan)
+solveWithCabal solveResources solverState =
+  fmap solverPlan <$> solveWithCabal' solveResources solverState
diff --git a/lib/Hix/Managed/Cabal/Source.hs b/lib/Hix/Managed/Cabal/Source.hs
new file mode 100644
--- /dev/null
+++ b/lib/Hix/Managed/Cabal/Source.hs
@@ -0,0 +1,20 @@
+module Hix.Managed.Cabal.Source where
+
+import Distribution.Client.Types (SourcePackageDb (SourcePackageDb, packageIndex))
+import Distribution.PackageDescription (
+  GenericPackageDescription (GenericPackageDescription, packageDescription),
+  PackageDescription,
+  )
+import Distribution.Solver.Types.PackageIndex (lookupPackageId)
+import Distribution.Solver.Types.SourcePackage (SourcePackage (SourcePackage, srcpkgDescription))
+
+import qualified Hix.Data.PackageId as PackageId
+import Hix.Data.PackageId (PackageId)
+
+sourcePackage ::
+  SourcePackageDb ->
+  PackageId ->
+  Maybe PackageDescription
+sourcePackage SourcePackageDb {packageIndex} package =
+  lookupPackageId packageIndex (PackageId.toCabal package) <&> \case
+    SourcePackage {srcpkgDescription = GenericPackageDescription {packageDescription}} -> packageDescription
diff --git a/lib/Hix/Managed/Data/BuildConfig.hs b/lib/Hix/Managed/Data/BuildConfig.hs
--- a/lib/Hix/Managed/Data/BuildConfig.hs
+++ b/lib/Hix/Managed/Data/BuildConfig.hs
@@ -11,7 +11,8 @@
     validate :: Bool,
     buildOutput :: Bool,
     toposortMutations :: Bool,
-    timeout :: Maybe BuildTimeout
+    timeout :: Maybe BuildTimeout,
+    disableNixMonitor :: Bool
   }
   deriving stock (Eq, Show, Generic)
 
@@ -24,5 +25,6 @@
     validate = False,
     buildOutput = False,
     toposortMutations = True,
-    timeout = Nothing
+    timeout = Nothing,
+    disableNixMonitor = False
   }
diff --git a/lib/Hix/Managed/Data/Mutation.hs b/lib/Hix/Managed/Data/Mutation.hs
--- a/lib/Hix/Managed/Data/Mutation.hs
+++ b/lib/Hix/Managed/Data/Mutation.hs
@@ -4,6 +4,7 @@
 import qualified Text.PrettyPrint as PrettyPrint
 import Text.PrettyPrint (parens, (<+>))
 
+import Hix.Data.PackageId (PackageId)
 import Hix.Data.Version (Version)
 import Hix.Data.VersionBounds (VersionBounds)
 import Hix.Managed.Cabal.Data.SolverState (SolverState)
@@ -36,7 +37,13 @@
   deriving stock (Generic)
 
 data MutationResult s =
-  MutationSuccess { candidate :: MutableId, changed :: Bool, state :: MutationState, ext :: s }
+  MutationSuccess {
+    candidate :: MutableId,
+    changed :: Bool,
+    state :: MutationState,
+    revisions :: Set PackageId,
+    ext :: s
+  }
   |
   MutationKeep
   |
diff --git a/lib/Hix/Managed/Data/StageState.hs b/lib/Hix/Managed/Data/StageState.hs
--- a/lib/Hix/Managed/Data/StageState.hs
+++ b/lib/Hix/Managed/Data/StageState.hs
@@ -1,5 +1,7 @@
 module Hix.Managed.Data.StageState where
 
+import Hix.Data.PackageId (PackageId)
+import Hix.Managed.Build.NixOutput (PackageDerivation)
 import Hix.Managed.Data.Initial (Initial (Initial))
 import Hix.Managed.Data.Mutable (MutableDep)
 import qualified Hix.Managed.Data.MutableId
@@ -18,23 +20,35 @@
   Success -> Just a
   Failure -> Nothing
 
+data BuildFailure =
+  UnknownFailure
+  |
+  PackageFailure (NonEmpty PackageDerivation)
+  |
+  TimeoutFailure [PackageId]
+  deriving stock (Eq, Show, Generic)
+
 data BuildResult =
-  Finished BuildStatus
+  BuildSuccess
   |
-  TimedOut
+  BuildFailure BuildFailure
   deriving stock (Eq, Show, Generic)
 
 buildUnsuccessful :: BuildResult -> Bool
 buildUnsuccessful = \case
-  Finished Success -> False
-  Finished Failure -> True
-  TimedOut -> True
+  BuildSuccess -> False
+  BuildFailure _ -> True
 
 buildStatus :: BuildResult -> BuildStatus
 buildStatus = \case
-  Finished s -> s
-  TimedOut -> Failure
+  BuildSuccess -> Success
+  BuildFailure _ -> Failure
 
+resultFromStatus :: BuildStatus -> BuildResult
+resultFromStatus = \case
+  Success -> BuildSuccess
+  Failure -> BuildFailure UnknownFailure
+
 data BuildSuccess =
   CandidateBuilt MutableId
   |
@@ -57,6 +71,7 @@
     success :: Map MutableDep BuildSuccess,
     failed :: [DepMutation a],
     state :: MutationState,
+    revisions :: Set PackageId,
     iterations :: Natural,
     ext :: s
   }
@@ -64,4 +79,4 @@
 
 initStageState :: Initial MutationState -> s -> StageState a s
 initStageState (Initial state) ext =
-  StageState {success = [], failed = [], iterations = 0, ..}
+  StageState {success = [], failed = [], revisions = [], iterations = 0, ..}
diff --git a/lib/Hix/Managed/Handlers/Build.hs b/lib/Hix/Managed/Handlers/Build.hs
--- a/lib/Hix/Managed/Handlers/Build.hs
+++ b/lib/Hix/Managed/Handlers/Build.hs
@@ -16,7 +16,7 @@
 import Hix.Managed.Data.Initial (Initial)
 import Hix.Managed.Data.ManagedPackage (ManagedPackage)
 import Hix.Managed.Data.Packages (Packages)
-import Hix.Managed.Data.StageState (BuildResult (Finished), BuildStatus (Failure))
+import Hix.Managed.Data.StageState (BuildFailure (UnknownFailure), BuildResult (BuildFailure))
 import qualified Hix.Managed.Handlers.Cabal as Solve
 import qualified Hix.Managed.Handlers.Cabal as Cabal
 import Hix.Managed.Handlers.Cabal (CabalHandlers)
@@ -40,7 +40,7 @@
 data EnvBuilder =
   EnvBuilder {
     cabal :: CabalHandlers,
-    buildWithState :: Versions -> [PackageId] -> M (Overrides, BuildResult)
+    buildWithState :: Bool -> Versions -> [PackageId] -> M (BuildResult, (Overrides, Set PackageId))
   }
 
 data Builder =
@@ -61,16 +61,19 @@
     latestVersion :: PackageName -> M (Maybe Version)
   }
 
-testBuilder :: (Versions -> [PackageId] -> M (Overrides, BuildResult)) -> (Builder -> M a) -> M a
+testBuilder ::
+  (Bool -> Versions -> [PackageId] -> M (BuildResult, (Overrides, Set PackageId))) ->
+  (Builder -> M a) ->
+  M a
 testBuilder buildWithState use =
   use Builder {withEnvBuilder = \ cabal _ _ useE -> useE EnvBuilder {cabal, buildWithState}}
 
 versionsBuilder :: HackageHandlers -> (Versions -> M BuildResult) -> (Builder -> M a) -> M a
 versionsBuilder hackage build =
-  testBuilder \ versions overrideVersions -> do
+  testBuilder \ _ versions overrideVersions -> do
     overrides <- packageOverrides hackage overrideVersions
     status <- build versions
-    pure (overrides, status)
+    pure (status, (overrides, mempty))
 
 handlersNull :: BuildHandlers
 handlersNull =
@@ -78,7 +81,7 @@
     stateFile = StateFileHandlers.handlersNull,
     report = Report.handlersNull,
     cabal = \ _ _ -> pure Solve.handlersNull,
-    withBuilder = testBuilder \ _ _ -> pure (mempty, Finished Failure),
+    withBuilder = testBuilder \ _ _ _ -> pure (BuildFailure UnknownFailure, mempty),
     versions = \ _ -> pure [],
     latestVersion = \ _ -> pure Nothing
   }
diff --git a/lib/Hix/Managed/Handlers/Build/Prod.hs b/lib/Hix/Managed/Handlers/Build/Prod.hs
--- a/lib/Hix/Managed/Handlers/Build/Prod.hs
+++ b/lib/Hix/Managed/Handlers/Build/Prod.hs
@@ -1,16 +1,24 @@
 module Hix.Managed.Handlers.Build.Prod where
 
 import Control.Monad.Catch (catch)
-import Control.Monad.Trans.Reader (asks)
+import Control.Monad.Trans.Class (lift)
+import Control.Monad.Trans.Reader (ask, asks)
+import Control.Monad.Trans.State.Strict (StateT (runStateT), gets, modify')
+import qualified Data.ByteString.Char8 as ByteString
+import Data.List.Extra (firstJust)
+import qualified Data.Set as Set
 import Exon (exon)
 import Network.HTTP.Client (newManager)
 import Network.HTTP.Client.TLS (tlsManagerSettings)
 import Path (Abs, Dir, Path, toFilePath)
 import Path.IO (copyDirRecur')
-import System.IO.Error (IOError)
+import System.IO (BufferMode (LineBuffering), Handle, hSetBuffering)
+import System.IO.Error (IOError, tryIOError)
 import System.Process.Typed (
   ExitCode (ExitFailure, ExitSuccess),
   ProcessConfig,
+  createPipe,
+  getStderr,
   inherit,
   nullStream,
   proc,
@@ -22,36 +30,36 @@
   )
 import System.Timeout (timeout)
 
+import Hix.Class.Map (nInsert, nMember)
 import Hix.Data.EnvName (EnvName)
 import Hix.Data.Error (Error (Fatal))
 import qualified Hix.Data.Monad
 import Hix.Data.Monad (M (M))
 import Hix.Data.Overrides (Overrides)
+import qualified Hix.Data.PackageId
 import Hix.Data.PackageId (PackageId)
 import Hix.Data.PackageName (LocalPackage)
 import Hix.Data.Version (Versions)
 import Hix.Error (pathText)
 import Hix.Hackage (latestVersionHackage, versionsHackage)
 import qualified Hix.Log as Log
+import Hix.Managed.Build.NixOutput (OutputResult (OutputResult), PackageDerivation (..), outputParse, runOutputState)
+import Hix.Managed.Build.NixOutput.Analysis (analyzeLog)
 import Hix.Managed.Cabal.Data.Config (CabalConfig)
+import qualified Hix.Managed.Data.BuildConfig
+import Hix.Managed.Data.BuildConfig (BuildConfig)
 import Hix.Managed.Data.EnvConfig (EnvConfig)
 import qualified Hix.Managed.Data.EnvContext
-import Hix.Managed.Data.EnvContext (EnvContext (EnvContext))
+import Hix.Managed.Data.EnvContext (EnvContext)
 import Hix.Managed.Data.EnvState (EnvState)
 import Hix.Managed.Data.Envs (Envs)
 import Hix.Managed.Data.Initial (Initial)
-import Hix.Managed.Data.StageState (BuildResult (Finished, TimedOut), BuildStatus (Failure, Success), buildUnsuccessful)
+import Hix.Managed.Data.StageState (BuildFailure (..), BuildResult (..), buildUnsuccessful)
 import qualified Hix.Managed.Data.StateFileConfig
 import Hix.Managed.Data.StateFileConfig (StateFileConfig)
 import Hix.Managed.Data.Targets (firstMTargets)
 import qualified Hix.Managed.Handlers.Build
-import Hix.Managed.Handlers.Build (
-  BuildHandlers (..),
-  BuildOutputsPrefix,
-  BuildTimeout,
-  Builder (Builder),
-  EnvBuilder (EnvBuilder),
-  )
+import Hix.Managed.Handlers.Build (BuildHandlers (..), BuildOutputsPrefix, Builder (Builder), EnvBuilder (EnvBuilder))
 import Hix.Managed.Handlers.Cabal (CabalHandlers)
 import qualified Hix.Managed.Handlers.Cabal.Prod as CabalHandlers
 import Hix.Managed.Handlers.Hackage (HackageHandlers)
@@ -59,10 +67,11 @@
 import qualified Hix.Managed.Handlers.Report.Prod as ReportHandlers
 import Hix.Managed.Handlers.StateFile (StateFileHandlers)
 import qualified Hix.Managed.Handlers.StateFile.Prod as StateFileHandlers
-import Hix.Managed.Overrides (packageOverrides)
+import Hix.Managed.Overrides (packageOverride, packageOverrides)
 import Hix.Managed.Path (rootOrCwd)
 import Hix.Managed.StateFile (writeBuildStateFor, writeInitialEnvState)
-import Hix.Monad (throwM, tryIOM, withTempDir)
+import Hix.Monad (runMUsing, throwM, tryIOM, withTempDir)
+import Hix.Pretty (showP)
 
 data BuilderResources =
   BuilderResources {
@@ -71,7 +80,7 @@
     envsConf :: Envs EnvConfig,
     buildOutputsPrefix :: Maybe BuildOutputsPrefix,
     root :: Path Abs Dir,
-    buildTimeout :: Maybe BuildTimeout
+    buildConfig :: BuildConfig
   }
 
 data EnvBuilderResources =
@@ -93,55 +102,178 @@
         use tmpRoot
     \ (err :: IOError) -> throwM (Fatal (show err))
 
+data OutputConfig =
+  OutputDebug
+  |
+  OutputParse
+  |
+  OutputIgnore
+
+data NixProcResult =
+  NixProcSuccess [Text]
+  |
+  NixProcFailure Text
+  deriving stock (Eq, Show, Generic)
+
+outputLines ::
+  MonadIO m =>
+  (ByteString -> m ()) ->
+  Handle ->
+  m (Maybe Text)
+outputLines parse handle = do
+  liftIO (hSetBuffering handle LineBuffering)
+  spin mempty
+  where
+    spin buf = do
+      liftIO (tryIOError (ByteString.hGet handle 4096)) >>= \case
+        Left err -> pure (Just (show err))
+        Right new ->
+          join <$> for (completeLines buf new) \ (lns, newBuf) -> do
+            for_ lns parse
+            spin newBuf
+
+    completeLines = \cases
+      -- Empty output means the handle was closed, empty buffer means we can terminate
+      "" "" ->
+        Nothing
+      -- First iteration after handle was closed, need to emit the last line
+      buf "" ->
+        Just ([buf], "")
+      buf new ->
+        Just (breakNl [] (buf <> new))
+
+    breakNl acc s =
+      case ByteString.break (== '\n') s of
+        (rest, "") -> (reverse acc, rest)
+        (ln, rest) -> breakNl (ln : acc) (ByteString.drop 1 rest)
+
 nixProc ::
+  OutputConfig ->
   Path Abs Dir ->
   [Text] ->
   Text ->
   [Text] ->
-  M (ProcessConfig () () ())
-nixProc root cmd installable extra = do
+  M (ProcessConfig () () (Maybe Handle))
+nixProc output root cmd installable extra = do
   Log.debug [exon|Running nix at '#{pathText root}' with args #{show args}|]
-  conf <$> M (asks (.debug))
+  pure conf
   where
-    conf debug = err debug (setWorkingDir (toFilePath root) (proc "nix" args))
+    conf = err (setWorkingDir (toFilePath root) (proc "nix" args))
 
-    err = \case
-      True -> setStderr inherit
-      False -> setStderr nullStream
+    err = case output of
+      OutputParse -> setStderr (Just <$> createPipe)
+      OutputDebug -> setStdout inherit . setStderr (Nothing <$ inherit)
+      OutputIgnore -> setStderr (Nothing <$ nullStream)
 
-    args = toString <$> cmd ++ [exon|path:#{".#"}#{installable}|] : extra
+    args = toString <$> cmd ++ [exon|path:#{".#"}#{installable}|] : extra ++ logArgs
 
-buildPackage ::
-  Maybe BuildTimeout ->
+    logArgs = case output of
+      OutputParse -> ["--log-format", "internal-json"]
+      OutputDebug -> ["-L"]
+      OutputIgnore -> []
+
+runProc ::
+  BuildConfig ->
+  (Handle -> IO a) ->
+  ProcessConfig () () (Maybe Handle) ->
+  M (Maybe (Maybe a, ExitCode))
+runProc buildConf pipeHandler conf =
+  tryIOM (withProcessTerm conf interact)
+  where
+    interact prc =
+      limit do
+        output <- traverse pipeHandler (getStderr prc)
+        res <- waitExitCode prc
+        pure (output, res)
+
+    limit | Just t <- buildConf.timeout
+          , t > 0
+          = timeout (coerce t * 1_000_000)
+          | otherwise = fmap Just
+
+outputResult ::
+  Maybe (Either Error (Maybe Text, OutputResult)) ->
+  ExitCode ->
+  M BuildResult
+outputResult result = \case
+  ExitSuccess -> pure BuildSuccess
+  ExitFailure _ -> pure (BuildFailure (maybe UnknownFailure PackageFailure failedPackage))
+  where
+    failedPackage =
+      result >>= \case
+        Right (_, OutputResult pkg) -> pkg
+        Left _ -> Nothing
+
+buildTarget ::
+  BuildConfig ->
   Path Abs Dir ->
   EnvName ->
   LocalPackage ->
   M BuildResult
-buildPackage processTimeout root env target = do
+buildTarget buildConf root env target = do
+  appRes <- M ask
   debug <- M (asks (.debug))
-  conf <- nixProc root ["-L", "build"] [exon|env.##{env}.##{target}|] []
-  tryIOM (withProcessTerm (err debug conf) (limit . waitExitCode)) <&> \case
-    Just ExitSuccess -> Finished Success
-    Just (ExitFailure _) -> Finished Failure
-    Nothing -> TimedOut
+  conf <- nixProc (outputHandler debug) root ["build"] [exon|env.##{env}.##{target}|] []
+  runProc buildConf (runOutput appRes) conf >>= \case
+    Just (output, code) ->
+      outputResult output code
+    Nothing -> pure (BuildFailure (TimeoutFailure []))
   where
-    limit | Just t <- processTimeout = timeout (coerce t * 1_000_000)
-          | otherwise = fmap Just
+    runOutput appRes handle =
+      runMUsing appRes (runOutputState (outputLines outputParse handle))
 
-    err = \case
-      True -> setStdout inherit
-      False -> id
+    outputHandler debug
+      | buildConf.buildOutput || (buildConf.disableNixMonitor && debug)
+      = OutputDebug
+      | buildConf.disableNixMonitor
+      = OutputIgnore
+      | otherwise
+      = OutputParse
 
+buildAdaptive ::
+  EnvBuilderResources ->
+  Bool ->
+  LocalPackage ->
+  StateT (Overrides, Set PackageId) M BuildResult
+buildAdaptive EnvBuilderResources {global, context} allowRevisions target = do
+  build
+  where
+    build = do
+      overrides <- gets fst
+      result <- lift do
+        writeBuildStateFor "current build" global.stateFileHandlers global.root context overrides
+        buildTarget global.buildConfig global.root context.env target
+      checkResult overrides result
+
+    checkResult overrides result
+      | allowRevisions
+      , BuildFailure (PackageFailure pkgs) <- result
+      , Just package <- firstJust logFailure (toList pkgs)
+      , not (nMember package.name overrides)
+      = retry package
+      | otherwise
+      = pure result
+
+    logFailure PackageDerivation {package, log} =
+      package <$ analyzeLog log
+
+    retry broken = do
+      lift $ Log.verbose [exon|Installed package failed with bounds error, retrying with override: #{showP broken}|]
+      (package, newOverride) <- lift $ packageOverride global.hackage broken
+      modify' \ (overrides, revisions) -> (nInsert package newOverride overrides, Set.insert broken revisions)
+      build
+
 buildWithState ::
   EnvBuilderResources ->
+  Bool ->
   Versions ->
   [PackageId] ->
-  M (Overrides, BuildResult)
-buildWithState EnvBuilderResources {global, context = context@EnvContext {env, targets}} _ overrideVersions = do
-  overrides <- packageOverrides global.hackage overrideVersions
-  writeBuildStateFor "current build" global.stateFileHandlers global.root context overrides
-  status <- firstMTargets (Finished Success) buildUnsuccessful (buildPackage global.buildTimeout global.root env) targets
-  pure (overrides, status)
+  M (BuildResult, (Overrides, Set PackageId))
+buildWithState builder allowRevisions _ overrideVersions = do
+  overrides <- packageOverrides builder.global.hackage overrideVersions
+  let build = buildAdaptive builder allowRevisions
+      s0 = (overrides, Set.empty)
+  runStateT (firstMTargets BuildSuccess buildUnsuccessful build builder.context.targets) s0
 
 -- | This used to have the purpose of reading an updated GHC package db using the current managed state, but this has
 -- become obsolete.
@@ -173,10 +305,10 @@
   StateFileConfig ->
   Envs EnvConfig ->
   Maybe BuildOutputsPrefix ->
-  Maybe BuildTimeout ->
+  BuildConfig ->
   (Builder -> M a) ->
   M a
-withBuilder hackage stateFileHandlers stateFileConf envsConf buildOutputsPrefix buildTimeout use =
+withBuilder hackage stateFileHandlers stateFileConf envsConf buildOutputsPrefix buildConfig use =
   withTempProject stateFileConf.projectRoot \ root -> do
     let resources = BuilderResources {..}
     use Builder {withEnvBuilder = withEnvBuilder resources}
@@ -186,11 +318,11 @@
   StateFileConfig ->
   Envs EnvConfig ->
   Maybe BuildOutputsPrefix ->
-  Maybe BuildTimeout ->
+  BuildConfig ->
   CabalConfig ->
   Bool ->
   m BuildHandlers
-handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest = do
+handlersProd stateFileConf envsConf buildOutputsPrefix buildConfig cabalConf oldest = do
   manager <- liftIO (newManager tlsManagerSettings)
   hackage <- HackageHandlers.handlersProd
   let stateFile = StateFileHandlers.handlersProd stateFileConf
@@ -198,7 +330,7 @@
     stateFile,
     report = ReportHandlers.handlersProd,
     cabal = CabalHandlers.handlersProd cabalConf oldest,
-    withBuilder = withBuilder hackage stateFile stateFileConf envsConf buildOutputsPrefix buildTimeout,
+    withBuilder = withBuilder hackage stateFile stateFileConf envsConf buildOutputsPrefix buildConfig,
     versions = versionsHackage manager,
     latestVersion = latestVersionHackage manager
   }
diff --git a/lib/Hix/Managed/Handlers/Build/Test.hs b/lib/Hix/Managed/Handlers/Build/Test.hs
--- a/lib/Hix/Managed/Handlers/Build/Test.hs
+++ b/lib/Hix/Managed/Handlers/Build/Test.hs
@@ -13,6 +13,7 @@
 import qualified Hix.Managed.Cabal.Data.Packages
 import Hix.Managed.Cabal.Data.Packages (GhcPackages)
 import Hix.Managed.Cabal.Mock.SourcePackage (queryVersions, queryVersionsLatest, sourcePackageVersions)
+import Hix.Managed.Data.BuildConfig (BuildConfig)
 import Hix.Managed.Data.EnvConfig (EnvConfig)
 import Hix.Managed.Data.Envs (Envs)
 import Hix.Managed.Data.Mutation (FailedMutation)
@@ -21,7 +22,6 @@
 import Hix.Managed.Handlers.Build (
   BuildHandlers (..),
   BuildOutputsPrefix,
-  BuildTimeout,
   SpecialBuildHandlers (TestBumpHandlers),
   versionsBuilder,
   )
@@ -68,12 +68,12 @@
   StateFileConfig ->
   Envs EnvConfig ->
   Maybe BuildOutputsPrefix ->
-  Maybe BuildTimeout ->
+  BuildConfig ->
   CabalConfig ->
   Bool ->
   m BuildHandlers
-handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest = do
-  handlers <- handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest
+handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildConf cabalConf oldest = do
+  handlers <- handlersProd stateFileConf envsConf buildOutputsPrefix buildConf cabalConf oldest
   pure handlers {
     cabal = CabalHandlers.handlersTest cabalConf oldest,
     latestVersion = latestVersionNixTestBump
@@ -84,12 +84,12 @@
   StateFileConfig ->
   Envs EnvConfig ->
   Maybe BuildOutputsPrefix ->
-  Maybe BuildTimeout ->
+  BuildConfig ->
   CabalConfig ->
   Maybe SpecialBuildHandlers ->
   m BuildHandlers
-chooseHandlers stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf = \case
-  Just TestBumpHandlers -> handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest
-  Nothing -> handlersProd stateFileConf envsConf buildOutputsPrefix buildTimeout cabalConf oldest
+chooseHandlers stateFileConf envsConf buildOutputsPrefix buildConf cabalConf = \case
+  Just TestBumpHandlers -> handlersBumpTest stateFileConf envsConf buildOutputsPrefix buildConf cabalConf oldest
+  Nothing -> handlersProd stateFileConf envsConf buildOutputsPrefix buildConf cabalConf oldest
   where
     oldest = False
diff --git a/lib/Hix/Managed/Handlers/Cabal.hs b/lib/Hix/Managed/Handlers/Cabal.hs
--- a/lib/Hix/Managed/Handlers/Cabal.hs
+++ b/lib/Hix/Managed/Handlers/Cabal.hs
@@ -1,9 +1,11 @@
 module Hix.Managed.Handlers.Cabal where
 
 import Data.IORef (IORef, modifyIORef')
+import Distribution.PackageDescription (PackageDescription)
 
 import Hix.Class.Map (nFromKeys)
 import Hix.Data.Monad (M)
+import Hix.Data.PackageId (PackageId)
 import Hix.Data.PackageName (PackageName)
 import Hix.Data.Version (Version)
 import Hix.Managed.Cabal.Changes (SolverPlan)
@@ -16,6 +18,7 @@
   CabalHandlers {
     solveForVersion :: SolverState -> M (Maybe SolverPlan),
     installedVersion :: PackageName -> Maybe Version,
+    sourcePackage :: PackageId -> Maybe PackageDescription,
     sortMutations :: ∀ a . [DepMutation a] -> M [DepMutation a]
   }
 
@@ -24,6 +27,7 @@
   CabalHandlers {
     solveForVersion = \ _ -> pure Nothing,
     installedVersion = const Nothing,
+    sourcePackage = const Nothing,
     sortMutations = pure
   }
 
diff --git a/lib/Hix/Managed/Handlers/Cabal/Prod.hs b/lib/Hix/Managed/Handlers/Cabal/Prod.hs
--- a/lib/Hix/Managed/Handlers/Cabal/Prod.hs
+++ b/lib/Hix/Managed/Handlers/Cabal/Prod.hs
@@ -15,11 +15,12 @@
 import Hix.Data.PackageName (PackageName)
 import Hix.Managed.Cabal.Data.Config (CabalConfig, GhcDb)
 import qualified Hix.Managed.Cabal.Data.SolveResources as SolveResources
-import Hix.Managed.Cabal.Data.SolveResources (SolveResources (SolveResources), solverParams)
+import Hix.Managed.Cabal.Data.SolveResources (SolveResources (SolveResources, solverParams))
 import Hix.Managed.Cabal.Installed (installedVersion)
 import qualified Hix.Managed.Cabal.Resources as SolveResources
 import Hix.Managed.Cabal.Solve (solveWithCabal)
 import Hix.Managed.Cabal.Sort (sortMutations)
+import Hix.Managed.Cabal.Source (sourcePackage)
 import Hix.Managed.Data.ManagedPackage (ManagedPackage)
 import Hix.Managed.Data.Packages (Packages)
 import Hix.Managed.Handlers.Cabal (CabalHandlers (..))
@@ -37,6 +38,7 @@
   pure CabalHandlers {
     solveForVersion = solveWithCabal solveResources {solverParams},
     installedVersion = installedVersion solveResources.installedPkgIndex,
+    sourcePackage = sourcePackage solveResources.sourcePkgDb,
     sortMutations = sortMutations solveResources
   }
   where
@@ -59,7 +61,9 @@
     PackageId {name = "base", version = [4, 17, 2, 0]},
     PackageId {name = "extra", version = [1, 7, 14]},
     PackageId {name = "th-abstraction", version = [0, 5, 0, 0]},
-    PackageId {name = "path", version = [0, 9, 5]}
+    PackageId {name = "path", version = [0, 9, 5]},
+    PackageId {name = "multi-fail1", version = [0, 1, 0]},
+    PackageId {name = "multi-fail2", version = [0, 1, 0]}
   ]
 
 removeLaterVersions ::
diff --git a/lib/Hix/Managed/Handlers/Hackage/Prod.hs b/lib/Hix/Managed/Handlers/Hackage/Prod.hs
--- a/lib/Hix/Managed/Handlers/Hackage/Prod.hs
+++ b/lib/Hix/Managed/Handlers/Hackage/Prod.hs
@@ -2,15 +2,22 @@
 
 import Data.IORef (newIORef)
 
+import Hix.Data.Version (SourceHash)
 import Hix.Hackage (fetchHashHackageCached)
 import qualified Hix.Managed.Handlers.Hackage
 import Hix.Managed.Handlers.Hackage (HackageHandlers (HackageHandlers))
 
-handlersProd ::
+handlersProdWith ::
   MonadIO m =>
+  Map Text SourceHash ->
   m HackageHandlers
-handlersProd = do
-  hashCache <- liftIO (newIORef mempty)
+handlersProdWith initial = do
+  hashCache <- liftIO (newIORef initial)
   pure HackageHandlers {
     fetchHash = fetchHashHackageCached hashCache
   }
+
+handlersProd ::
+  MonadIO m =>
+  m HackageHandlers
+handlersProd = handlersProdWith mempty
diff --git a/lib/Hix/Managed/Handlers/Mutation.hs b/lib/Hix/Managed/Handlers/Mutation.hs
--- a/lib/Hix/Managed/Handlers/Mutation.hs
+++ b/lib/Hix/Managed/Handlers/Mutation.hs
@@ -1,6 +1,7 @@
 module Hix.Managed.Handlers.Mutation where
 
 import Hix.Data.Monad (M)
+import Hix.Data.PackageId (PackageId)
 import Hix.Managed.Data.Mutation (BuildMutation, DepMutation, MutationResult)
 import Hix.Managed.Data.MutationState (MutationState)
 
@@ -9,6 +10,6 @@
     process ::
       s ->
       DepMutation a ->
-      (BuildMutation -> M (Maybe MutationState)) ->
+      (BuildMutation -> M (Maybe (MutationState, Set PackageId))) ->
       M (MutationResult s)
   }
diff --git a/lib/Hix/Managed/Handlers/Mutation/Bump.hs b/lib/Hix/Managed/Handlers/Mutation/Bump.hs
--- a/lib/Hix/Managed/Handlers/Mutation/Bump.hs
+++ b/lib/Hix/Managed/Handlers/Mutation/Bump.hs
@@ -35,12 +35,12 @@
 processMutationBump ::
   SolverState ->
   DepMutation Bump ->
-  (BuildMutation -> M (Maybe MutationState)) ->
+  (BuildMutation -> M (Maybe (MutationState, Set PackageId))) ->
   M (MutationResult SolverState)
 processMutationBump solver DepMutation {package, mutation = Bump {version, changed}} build =
   builder version <&> \case
-    Just (candidate, ext, state) ->
-      MutationSuccess {candidate, changed, state, ext}
+    Just (candidate, ext, state, revisions) ->
+      MutationSuccess {candidate, changed, state, revisions, ext}
     Nothing ->
       MutationFailed
   where
diff --git a/lib/Hix/Managed/Handlers/Mutation/Lower.hs b/lib/Hix/Managed/Handlers/Mutation/Lower.hs
--- a/lib/Hix/Managed/Handlers/Mutation/Lower.hs
+++ b/lib/Hix/Managed/Handlers/Mutation/Lower.hs
@@ -38,12 +38,12 @@
   (Bool -> MutableId -> PackageId -> MutationConstraints -> MutationConstraints) ->
   SolverState ->
   DepMutation Lower ->
-  (BuildMutation -> M (Maybe MutationState)) ->
+  (BuildMutation -> M (Maybe (MutationState, Set PackageId))) ->
   M (MutationResult SolverState)
 processMutationLower conf mode update solver DepMutation {package, retract, mutation = Lower {majors}} build = do
   foldM buildMajor (Right 0, Nothing) majors <&> \case
-    (_, Just (candidate, ext, state)) ->
-      MutationSuccess {candidate, changed = True, state, ext}
+    (_, Just (candidate, ext, state, revisions)) ->
+      MutationSuccess {candidate, changed = True, state, revisions, ext}
     (_, Nothing) ->
       mode.noSuccess
   where
diff --git a/lib/Hix/Managed/Handlers/StateFile/Test.hs b/lib/Hix/Managed/Handlers/StateFile/Test.hs
--- a/lib/Hix/Managed/Handlers/StateFile/Test.hs
+++ b/lib/Hix/Managed/Handlers/StateFile/Test.hs
@@ -4,7 +4,7 @@
 
 import Hix.Data.Monad (M)
 import Hix.Data.NixExpr (Expr)
-import Hix.Managed.Handlers.StateFile (StateFileHandlers (StateFileHandlers), writeFile)
+import Hix.Managed.Handlers.StateFile (StateFileHandlers (StateFileHandlers, writeFile))
 
 writeFileIORef ::
   IORef [Expr] ->
diff --git a/lib/Hix/Managed/Lower/App.hs b/lib/Hix/Managed/Lower/App.hs
--- a/lib/Hix/Managed/Lower/App.hs
+++ b/lib/Hix/Managed/Lower/App.hs
@@ -5,7 +5,6 @@
 import qualified Hix.Data.Options
 import Hix.Data.Options (LowerOptions (LowerOptions))
 import Hix.Json (jsonConfigE)
-import qualified Hix.Managed.Data.BuildConfig
 import Hix.Managed.Data.LowerConfig (LowerConfig, lowerConfig)
 import Hix.Managed.Data.ProjectContext (ProjectContext)
 import qualified Hix.Managed.Data.ProjectContextProto
@@ -24,8 +23,8 @@
   M ()
 lowerCli main opts@LowerOptions {common} = do
   context <- jsonConfigE Client common.context
-  handlers <- chooseHandlers common.stateFile context.envs context.buildOutputsPrefix common.project.build.timeout
-    common.cabal common.handlers
+  handlers <- chooseHandlers common.stateFile context.envs context.buildOutputsPrefix common.project.build common.cabal
+    common.handlers
   withProjectContext handlers common.project context (main conf handlers)
   where
     conf = lowerConfig opts
diff --git a/lib/Hix/Managed/Lower/Auto.hs b/lib/Hix/Managed/Lower/Auto.hs
--- a/lib/Hix/Managed/Lower/Auto.hs
+++ b/lib/Hix/Managed/Lower/Auto.hs
@@ -15,7 +15,7 @@
 import Hix.Managed.Data.ProjectResult (ProjectResult)
 import Hix.Managed.Data.Query (Query (Query))
 import qualified Hix.Managed.Data.QueryDep
-import Hix.Managed.Data.StageContext (StageContext (StageContext), query)
+import Hix.Managed.Data.StageContext (StageContext (StageContext, query))
 import Hix.Managed.Data.StageState (BuildStatus (Failure, Success))
 import Hix.Managed.Diff (reifyVersionChanges)
 import Hix.Managed.Flow (Flow, evalStageState, runStage_, stageError)
diff --git a/lib/Hix/Managed/StageState.hs b/lib/Hix/Managed/StageState.hs
--- a/lib/Hix/Managed/StageState.hs
+++ b/lib/Hix/Managed/StageState.hs
@@ -1,6 +1,7 @@
 module Hix.Managed.StageState where
 
 import qualified Data.Map.Strict as Map
+import qualified Data.Set as Set
 
 import qualified Hix.Managed.Data.MutableId
 import qualified Hix.Managed.Data.Mutation
@@ -13,10 +14,11 @@
   MutationResult s ->
   StageState a s
 updateStageState old mutation = \case
-  MutationSuccess candidate changed state ext ->
+  MutationSuccess {..} ->
     old {
       success = Map.insert candidate.name buildSuccess old.success,
       state,
+      revisions = Set.union revisions old.revisions,
       ext
     }
     where
diff --git a/lib/Hix/Managed/StateFile.hs b/lib/Hix/Managed/StateFile.hs
--- a/lib/Hix/Managed/StateFile.hs
+++ b/lib/Hix/Managed/StateFile.hs
@@ -38,7 +38,7 @@
   ProjectState ->
   M ()
 writeStateFile purpose handlers tmpRoot state = do
-  Log.debug [exon|writing managed stated file for #{purpose}: #{renderRootExpr expr}|]
+  Log.debug [exon|writing managed state file for #{purpose}: #{renderRootExpr expr}|]
   handlers.writeFile tmpRoot expr
   where
     expr = encodeNix state
diff --git a/lib/Hix/Monad.hs b/lib/Hix/Monad.hs
--- a/lib/Hix/Monad.hs
+++ b/lib/Hix/Monad.hs
@@ -96,10 +96,14 @@
   log <- readIORef logRef
   pure (log, result)
 
+runMUsing :: AppResources -> M a -> IO (Either Error a)
+runMUsing res (M ma) =
+  runExceptT (runReaderT ma res)
+
 runMLoggerWith :: (LogLevel -> Text -> IO ()) -> GlobalOptions -> M a -> IO (Either Error a)
-runMLoggerWith logger GlobalOptions {..} (M ma) =
+runMLoggerWith logger GlobalOptions {..} ma =
   withSystemTempDir "hix-cli" \ tmp ->
-    runExceptT (runReaderT ma AppResources {logger = logWith logger, ..})
+    runMUsing AppResources {logger = logWith logger, ..} ma
 
 runMLogWith :: GlobalOptions -> M a -> IO ([Text], Either Error a)
 runMLogWith opts ma =
diff --git a/lib/Hix/Options.hs b/lib/Hix/Options.hs
--- a/lib/Hix/Options.hs
+++ b/lib/Hix/Options.hs
@@ -258,6 +258,7 @@
   validate <- switch (long "validate" <> help "Validate new versions, but don't update the project state")
   buildOutput <- switch (long "build-output" <> help "Show output from Nix builds")
   timeout <- fmap BuildTimeout <$> optional (option auto (long "build-timeout" <> help buildTimeoutHelp))
+  disableNixMonitor <- switch (long "disable-nix-monitor" <> help "Don't parse Nix build output")
   pure BuildConfig {..}
   where
     maxIterationsHelp = "Number of restarts when some dependencies fail"
diff --git a/test/Hix/Test/Hedgehog.hs b/test/Hix/Test/Hedgehog.hs
--- a/test/Hix/Test/Hedgehog.hs
+++ b/test/Hix/Test/Hedgehog.hs
@@ -1,6 +1,6 @@
 module Hix.Test.Hedgehog where
 
-import Data.List.Extra (zipWithLongest)
+import Data.List.Extra (takeEnd, zipWithLongest)
 import qualified Data.Text as Text
 import Hedgehog (TestT, evalEither, (===))
 
@@ -50,8 +50,20 @@
   [a] ->
   [a] ->
   TestT m ()
-listEqZip target log =
-  for_ (zip [0 :: Natural ..] (zipWithLongest (,) target log)) \case
+listEqZip target actual =
+  for_ (zip [0 :: Natural ..] (zipWithLongest (,) target actual)) \case
     (i, (Just t, Just l)) -> (i, t) === (i, l)
-    _ | [] <- log -> fail "Result list is empty."
-    _ -> target === log
+    _ | [] <- actual -> fail "Result list is empty."
+    _ -> target === actual
+
+listEqTail ::
+  ∀ m a .
+  Monad m =>
+  HasCallStack =>
+  Eq a =>
+  Show a =>
+  [a] ->
+  [a] ->
+  TestT m ()
+listEqTail target actual =
+  target === takeEnd (length target) actual
diff --git a/test/Hix/Test/Managed/Bump/MutationTest.hs b/test/Hix/Test/Managed/Bump/MutationTest.hs
--- a/test/Hix/Test/Managed/Bump/MutationTest.hs
+++ b/test/Hix/Test/Managed/Bump/MutationTest.hs
@@ -1,8 +1,8 @@
 module Hix.Test.Managed.Bump.MutationTest where
 
 import qualified Data.Text as Text
+import Distribution.Types.PackageDescription (customFieldsPD, emptyPackageDescription)
 import Exon (exon)
-import Hedgehog ((===))
 import Test.Tasty (TestTree, testGroup)
 
 import Hix.Data.Error (Error (Fatal))
@@ -10,7 +10,7 @@
 import Hix.Managed.Bump.Optimize (bumpOptimizeMain)
 import qualified Hix.Managed.Cabal.Data.Packages
 import Hix.Managed.Cabal.Data.Packages (GhcPackages (GhcPackages), InstalledPackages)
-import Hix.Managed.Cabal.Data.SourcePackage (SourcePackages)
+import Hix.Managed.Cabal.Data.SourcePackage (SourcePackageId (description), SourcePackages)
 import Hix.Managed.Data.ManagedPackageProto (ManagedPackageProto, managedPackages)
 import Hix.Managed.Data.Packages (Packages)
 import qualified Hix.Managed.Data.ProjectStateProto
@@ -19,7 +19,7 @@
 import Hix.Monad (M, throwM)
 import Hix.NixExpr (renderRootExpr)
 import Hix.Pretty (showP)
-import Hix.Test.Hedgehog (eqLines)
+import Hix.Test.Hedgehog (eqLines, listEqTail)
 import qualified Hix.Test.Managed.Run
 import Hix.Test.Managed.Run (Result (Result), TestParams (..), bumpTest, testParams)
 import Hix.Test.Utils (UnitTest, unitTest)
@@ -32,7 +32,9 @@
     "direct2",
     "direct3",
     "direct4",
-    "direct5"
+    "direct5",
+    "direct6",
+    "direct7"
   ])]
 
 installed :: InstalledPackages
@@ -43,55 +45,80 @@
     ("direct2-1.0.1", []),
     ("direct3-1.0.1", []),
     ("direct4-1.0.1", []),
-    ("direct5-1.0.1", [])
+    ("direct5-1.0.1", []),
+    ("direct6-1.0.1", []),
+    ("direct7-1.0.1", ["transitive1-1.0.1"]),
+    ("transitive1-1.0.1", ["direct6-1.0.1"])
   ]
 
-packageDb :: SourcePackages
-packageDb =
-  [
-    ("base", [
-      ([4, 11, 0, 0], []),
-      ([4, 12, 0, 0], []),
-      ([4, 13, 0, 0], [])
-    ]),
-    ("direct1", [
-      ([1, 0, 1], []),
-      ([1, 1, 1], []),
-      ([1, 2, 1], ["direct2 ==1.2.1"])
-    ]),
-    ("direct2", [
-      ([1, 0, 1], []),
-      ([1, 1, 1], []),
-      ([1, 2, 1], [])
-    ]),
-    ("direct3", [
-      ([1, 0, 1], []),
-      ([1, 1, 1], []),
-      ([1, 2, 1], [])
-    ]),
-    ("direct4", [
-      ([1, 0, 1], []),
-      ([1, 1, 1], []),
-      ([1, 2, 1], [])
-    ]),
-    ("direct5", [
-      ([1, 0, 1], []),
-      ([1, 1, 1], []),
-      ([1, 2, 1], ["direct4 <1.2"])
-    ])
-  ]
+available :: SourcePackages
+available =
+  pkgs
+  where
+    pkgs =
+      [
+        ("base", [
+          ([4, 11, 0, 0], []),
+          ([4, 12, 0, 0], []),
+          ([4, 13, 0, 0], [])
+        ]),
+        ("direct1", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], []),
+          ([1, 2, 1], ["direct2 ==1.2.1"])
+        ]),
+        ("direct2", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], []),
+          ([1, 2, 1], [])
+        ]),
+        ("direct3", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], []),
+          ([1, 2, 1], [])
+        ]),
+        ("direct4", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], []),
+          ([1, 2, 1], [])
+        ]),
+        ("direct5", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], []),
+          ([1, 2, 1], ["direct4 <1.2"])
+        ]),
+        ("direct6", [
+          ([1, 0, 1], []),
+          ([1, 1, 1], [])
+        ]),
+        ("direct7", [
+          ([1, 0, 1], ["transitive1 <1.1"])
+        ]),
+        ("transitive1", [
+          ([1, 0, 1], ["direct6 <1.2"] {
+            description = Just (emptyPackageDescription {customFieldsPD = [("x-revision", "2")]})
+          })
+        ])
+      ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed, available = packageDb}
+ghcPackages = GhcPackages {installed, available}
 
 build :: Versions -> M BuildStatus
 build = \case
-  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 0, 1]), ("direct5", [1, 1, 1])] -> pure Success
-  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 2, 1]), ("direct4", [1, 0, 1]), ("direct5", [1, 1, 1])] -> pure Failure
-  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 1, 1])] -> pure Success
-  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 2, 1])] -> pure Success
-  -- second iteration
-  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 2, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 2, 1])] -> pure Failure
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 0, 1]), ("direct5", [1, 1, 1]), ("direct6", [1, 0, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Success
+
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 2, 1]), ("direct4", [1, 0, 1]), ("direct5", [1, 1, 1]), ("direct6", [1, 0, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Failure
+
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 1, 1]), ("direct6", [1, 0, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Success
+
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 2, 1]), ("direct6", [1, 0, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Success
+
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 0, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 2, 1]), ("direct6", [1, 1, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Success
+
+--   -- second iteration
+  [("base", [4, 12, 0, 0]), ("direct1", [1, 2, 1]), ("direct2", [1, 2, 1]), ("direct3", [1, 2, 1]), ("direct4", [1, 2, 1]), ("direct5", [1, 2, 1]), ("direct6", [1, 1, 1]), ("direct7", [1, 0, 1]), ("transitive1", [1, 0, 1])] -> pure Failure
+
   versions -> throwM (Fatal [exon|Unexpected build plan: #{showP versions}|])
 
 state :: ProjectStateProto
@@ -101,7 +128,8 @@
       ("local1", [
         ("direct1", ">=0.1"),
         ("direct2", ">=0.1"),
-        ("direct4", "^>=1.0")
+        ("direct4", "^>=1.0"),
+        ("direct6", "<1.1")
       ])
     ],
     versions = [("fancy", [("direct4", "1.0.1")])],
@@ -141,6 +169,14 @@
         lower = null;
         upper = "1.3";
       };
+      direct6 = {
+        lower = null;
+        upper = "1.2";
+      };
+      direct7 = {
+        lower = null;
+        upper = "1.1";
+      };
     };
   };
   versions = {
@@ -151,6 +187,8 @@
       direct3 = "1.0.1";
       direct4 = "1.2.1";
       direct5 = "1.2.1";
+      direct6 = "1.1.1";
+      direct7 = "1.0.1";
     };
   };
   initial = {
@@ -174,6 +212,18 @@
         version = "1.2.1";
         hash = "direct5-1.2.1";
       };
+      direct6 = {
+        version = "1.1.1";
+        hash = "direct6-1.1.1";
+      };
+      direct7 = {
+        version = "1.0.1";
+        hash = "direct7-1.0.1";
+      };
+      transitive1 = {
+        version = "1.0.1";
+        hash = "transitive1-1.0.1";
+      };
     };
   };
   resolving = false;
@@ -192,6 +242,8 @@
     📦 [34mdirect2[0m   1.2.1      ↕ >=0.1 -> [0.1, [32m1.3[0m]
     📦 [34mdirect3[0m   1.0.1      ↕ [no bounds] -> <[32m1.1[0m
     📦 [34mdirect5[0m   1.2.1      ↕ [no bounds] -> <[32m1.3[0m
+    📦 [34mdirect6[0m   1.1.1      ↕ <[31m1.1[0m -> <[32m1.2[0m
+    📦 [34mdirect7[0m   1.0.1      ↕ [no bounds] -> <[32m1.1[0m
 [35m[1m>>>[0m Updated versions:
     📦 [34mdirect4[0m   [31m1.0.1[0m -> [32m1.2.1[0m   ↕ [1.0, [31m1.1[0m] -> [1.0, [32m1.3[0m]
 |]
@@ -231,7 +283,7 @@
 test_bumpMutationBasic = do
   Result {stateFile, log} <- bumpTest params bumpOptimizeMain
   eqLines stateFileTarget (renderRootExpr stateFile)
-  logTarget === drop 12 (reverse log)
+  listEqTail logTarget (reverse log)
   where
     params = (testParams False packages) {
       log = True,
diff --git a/test/Hix/Test/Managed/BumpNativeTest.hs b/test/Hix/Test/Managed/BumpNativeTest.hs
new file mode 100644
--- /dev/null
+++ b/test/Hix/Test/Managed/BumpNativeTest.hs
@@ -0,0 +1,135 @@
+module Hix.Test.Managed.BumpNativeTest where
+
+import Control.Monad.Trans.Reader (ask)
+import qualified Data.Text.IO as Text
+import Exon (exon)
+import Hedgehog (evalEither)
+import Path (Abs, Dir, File, Path, Rel, parent, reldir, relfile, toFilePath, (</>))
+import Path.IO (createDirIfMissing, getCurrentDir)
+
+import qualified Hix.Data.Monad
+import Hix.Data.Monad (AppResources (AppResources), M (M))
+import Hix.Data.Options (ProjectOptions (query), projectOptions)
+import Hix.Error (pathText)
+import Hix.Managed.Bump.Optimize (bumpOptimizeMain)
+import Hix.Managed.Cabal.Data.Config (GhcDb (GhcDbSystem))
+import qualified Hix.Managed.Data.EnvConfig
+import Hix.Managed.Data.EnvConfig (EnvConfig (EnvConfig))
+import Hix.Managed.Data.ManagedPackageProto (ManagedPackageProto, managedPackages)
+import Hix.Managed.Data.Packages (Packages)
+import qualified Hix.Managed.Data.ProjectContext
+import qualified Hix.Managed.Data.ProjectContextProto
+import Hix.Managed.Data.ProjectContextProto (ProjectContextProto (ProjectContextProto))
+import qualified Hix.Managed.Data.ProjectResult
+import Hix.Managed.Data.ProjectStateProto (ProjectStateProto (ProjectStateProto))
+import qualified Hix.Managed.Data.StateFileConfig
+import Hix.Managed.Data.StateFileConfig (StateFileConfig (StateFileConfig))
+import qualified Hix.Managed.Handlers.Build.Prod as Build
+import Hix.Managed.ProjectContext (updateProject)
+import qualified Hix.Managed.ProjectContextProto as ProjectContextProto
+import Hix.Test.Hedgehog (eqLines)
+import Hix.Test.Utils (UnitTest, runMTest)
+
+packages :: Packages ManagedPackageProto
+packages =
+  managedPackages [
+    (("root", "1.0"), ["tasty <1.5", "criterion <1.7"])
+  ]
+
+flake :: Path Abs Dir -> Text
+flake hixRoot =
+  [exon|{
+  description = "hix test project";
+  inputs.hix.url = "path:#{pathText hixRoot}";
+  outputs = {self, hix, ...}: hix.lib.flake ({config, lib, ...}: {
+    managed = {
+      enable = true;
+      latest.compiler = "ghc98";
+    };
+    compat.enable = false;
+    ghcVersions = [];
+    packages = {
+      root = {
+        src = ./.;
+        library = {
+          enable = true;
+          dependencies = ["tasty" "criterion"];
+        };
+      };
+    };
+    envs.latest.localPackage = api: api.minimal;
+  });
+}|]
+
+libMod :: Text
+libMod =
+  [exon|module Root where
+-- import Test.Tasty (blarkh)
+string :: String
+string = "hello"
+|]
+
+addFile :: Path Abs Dir -> Path Rel File -> Text -> M ()
+addFile root path content = do
+  createDirIfMissing True (parent file)
+  liftIO (Text.writeFile (toFilePath file) content)
+  where
+    file = root </> path
+
+setupProject :: M (Path Abs Dir)
+setupProject = do
+  AppResources {tmp} <- M ask
+  cwd <- getCurrentDir
+  let projectRoot = tmp </> [reldir|project|]
+  let hixRoot = parent (parent cwd)
+  createDirIfMissing True projectRoot
+  addFile projectRoot [relfile|flake.nix|] (flake hixRoot)
+  addFile projectRoot [relfile|lib/Root.hs|] libMod
+  pure projectRoot
+
+targetStateFile :: Text
+targetStateFile =
+  [exon|{
+  resolving = false;
+}
+|]
+
+bumpNativeTest :: M Text
+bumpNativeTest = do
+  root <- setupProject
+  let
+    stateFileConf = StateFileConfig {
+      file = [relfile|ops/managed.nix|],
+      projectRoot = Just root
+    }
+    envsConfig = [("latest", EnvConfig {targets = ["root"], ghc = GhcDbSystem Nothing})]
+    buildConfig = def
+  handlers <- Build.handlersProd stateFileConf envsConfig Nothing buildConfig def False
+  let
+    opts = (projectOptions ["latest"]) {query = ["tasty"]}
+
+    proto0 =
+      ProjectContextProto {
+        packages,
+        state = ProjectStateProto [] [] mempty mempty False,
+        envs = envsConfig,
+        buildOutputsPrefix = Nothing
+      }
+
+    stateFile = root </> [relfile|ops/managed.nix|]
+
+    run context process = do
+      result <- process handlers context
+      updateProject handlers context.build result
+      stateFileContent <- liftIO (Text.readFile (toFilePath stateFile))
+      pure (result.state, stateFileContent)
+
+  context0 <- ProjectContextProto.validate opts proto0
+  (_, stateFileContent) <- run context0 bumpOptimizeMain
+  pure stateFileContent
+
+test_bumpNative :: UnitTest
+test_bumpNative = do
+  stateFileContent <- evalEither =<< liftIO do
+    runMTest True bumpNativeTest
+  eqLines targetStateFile stateFileContent
diff --git a/test/Hix/Test/Managed/LowerAuto/MutationOptimizeTest.hs b/test/Hix/Test/Managed/LowerAuto/MutationOptimizeTest.hs
--- a/test/Hix/Test/Managed/LowerAuto/MutationOptimizeTest.hs
+++ b/test/Hix/Test/Managed/LowerAuto/MutationOptimizeTest.hs
@@ -11,7 +11,7 @@
 import qualified Hix.Managed.Cabal.Data.Packages
 import Hix.Managed.Cabal.Data.Packages (GhcPackages (GhcPackages), InstalledPackages)
 import Hix.Managed.Cabal.Data.SourcePackage (SourcePackages)
-import Hix.Managed.Data.LowerConfig (reset)
+import Hix.Managed.Data.LowerConfig (LowerConfig (reset))
 import Hix.Managed.Data.ManagedPackageProto (ManagedPackageProto, managedPackages)
 import Hix.Managed.Data.Packages (Packages)
 import qualified Hix.Managed.Data.ProjectStateProto
@@ -76,8 +76,8 @@
     ("direct3-1.2", [])
   ]
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("direct1", versions),
     ("direct2", versions),
@@ -94,7 +94,7 @@
       ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed, available = packageDb}
+ghcPackages = GhcPackages {installed, available}
 
 buildVersionsBasic :: Versions -> M BuildStatus
 buildVersionsBasic = \case
diff --git a/test/Hix/Test/Managed/LowerAuto/MutationStabilizeTest.hs b/test/Hix/Test/Managed/LowerAuto/MutationStabilizeTest.hs
--- a/test/Hix/Test/Managed/LowerAuto/MutationStabilizeTest.hs
+++ b/test/Hix/Test/Managed/LowerAuto/MutationStabilizeTest.hs
@@ -10,7 +10,7 @@
 import qualified Hix.Managed.Cabal.Data.Packages
 import Hix.Managed.Cabal.Data.Packages (GhcPackages (GhcPackages), InstalledPackages)
 import Hix.Managed.Cabal.Data.SourcePackage (SourcePackages)
-import Hix.Managed.Data.LowerConfig (stabilize)
+import Hix.Managed.Data.LowerConfig (LowerConfig (stabilize))
 import Hix.Managed.Data.ManagedPackageProto (ManagedPackageProto, managedPackages)
 import Hix.Managed.Data.Packages (Packages)
 import qualified Hix.Managed.Data.ProjectStateProto
@@ -42,8 +42,8 @@
     ("direct3-1.2", [])
   ]
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("direct1", versions),
     ("direct2", versions),
@@ -60,7 +60,7 @@
       ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed, available = packageDb}
+ghcPackages = GhcPackages {installed, available}
 
 state :: ProjectStateProto
 state =
diff --git a/test/Hix/Test/Managed/LowerInit/MutationTest.hs b/test/Hix/Test/Managed/LowerInit/MutationTest.hs
--- a/test/Hix/Test/Managed/LowerInit/MutationTest.hs
+++ b/test/Hix/Test/Managed/LowerInit/MutationTest.hs
@@ -6,7 +6,7 @@
 
 import Hix.Class.Map ((!!))
 import Hix.Data.Error (Error (Fatal))
-import Hix.Data.Options (envs, readUpperBounds)
+import Hix.Data.Options (ProjectOptions (envs, readUpperBounds))
 import qualified Hix.Data.Overrides
 import Hix.Data.Overrides (Override (Override))
 import Hix.Data.Version (SourceHash (SourceHash), Versions)
@@ -27,7 +27,7 @@
 import Hix.NixExpr (renderRootExpr)
 import Hix.Pretty (showP)
 import Hix.Test.Hedgehog (eqLines, listEqZip)
-import Hix.Test.Managed.Run (Result (..), TestParams (..), lowerTest, testParams, nosortOptions)
+import Hix.Test.Managed.Run (Result (..), TestParams (..), lowerTest, nosortOptions, testParams)
 import Hix.Test.Utils (UnitTest)
 
 packages :: Packages ManagedPackageProto
@@ -48,8 +48,8 @@
     (("local8", "1.0"), ["direct1", "direct6"])
   ]
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("direct1", [
       ([1, 0, 3], ["transitive1 >=1"]),
@@ -86,7 +86,7 @@
   ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed = [], available = packageDb}
+ghcPackages = GhcPackages {installed = [], available}
 
 state :: ProjectStateProto
 state =
diff --git a/test/Hix/Test/Managed/LowerNativeTest.hs b/test/Hix/Test/Managed/LowerNativeTest.hs
--- a/test/Hix/Test/Managed/LowerNativeTest.hs
+++ b/test/Hix/Test/Managed/LowerNativeTest.hs
@@ -233,7 +233,8 @@
       projectRoot = Just root
     }
     envsConfig = [("lower", EnvConfig {targets = ["root"], ghc = GhcDbSystem Nothing})]
-  handlers <- Build.handlersProd stateFileConf envsConfig Nothing Nothing def False
+    buildConfig = def
+  handlers <- Build.handlersProd stateFileConf envsConfig Nothing buildConfig def False
   let
     opts = projectOptions ["lower"]
 
@@ -256,7 +257,7 @@
   context0 <- ProjectContextProto.validate opts proto0
   (state1, stateFileContentInit) <- run context0 (lowerInitMain def)
   -- TODO the packages here aren't updated with the result from the first run
-  let context1 = projectContext def state1 context0.packages context0.envs
+  let context1 = projectContext buildConfig state1 context0.packages context0.envs
   (_, stateFileContentOptimize) <- run context1 lowerOptimizeMain
   pure (stateFileContentInit, stateFileContentOptimize)
 
diff --git a/test/Hix/Test/Managed/LowerOptimize/CandidatesTest.hs b/test/Hix/Test/Managed/LowerOptimize/CandidatesTest.hs
--- a/test/Hix/Test/Managed/LowerOptimize/CandidatesTest.hs
+++ b/test/Hix/Test/Managed/LowerOptimize/CandidatesTest.hs
@@ -4,12 +4,13 @@
 import Distribution.Version (Version)
 import Hedgehog (evalEither, (===))
 
+import Hix.Data.PackageId (PackageId)
 import Hix.Data.PackageName (PackageName)
 import qualified Hix.Data.VersionBounds
 import Hix.Data.VersionBounds (fromLower, fromUpper)
 import qualified Hix.Managed.Cabal.Data.SolverState
 import Hix.Managed.Cabal.Data.SolverState (SolverState (SolverState), solverState, updateSolverState)
-import Hix.Managed.Data.Constraints (MutationConstraints (MutationConstraints), mutation)
+import Hix.Managed.Data.Constraints (MutationConstraints (MutationConstraints, mutation))
 import Hix.Managed.Data.Mutable (MutableDep)
 import qualified Hix.Managed.Data.MutableId
 import Hix.Managed.Data.MutableId (MutableId (MutableId))
@@ -45,10 +46,12 @@
 candidateVersion :: Version
 candidateVersion = [1, 9, 2]
 
-build :: IORef [Maybe Version] -> BuildMutation -> M (Maybe MutationState)
+build :: IORef [Maybe Version] -> BuildMutation -> M (Maybe (MutationState, Set PackageId))
 build buildRef BuildMutation {solverState = SolverState {constraints = [("dep", MutationConstraints {mutation})]}} = do
   liftIO (modifyIORef' buildRef (mutation.lower :))
-  pure (result =<< mutation.lower)
+  pure do
+    s <- result =<< mutation.lower
+    pure (s, [])
   where
     result version
       | candidateVersion == version
@@ -71,7 +74,7 @@
     for majors \ mut ->
       processMutationLower def lowerOptimizeMode lowerOptimizeUpdate initialState mut (build buildRef)
   mutationResults <- evalEither result
-  Just (MutationSuccess candidate True mstate (updateSolverState (const newConstraints) initialState)) === mutationResults
+  Just (MutationSuccess candidate True mstate [] (updateSolverState (const newConstraints) initialState)) === mutationResults
   triedVersions <- liftIO (readIORef buildRef)
   (Just <$> targets) === triedVersions
   where
diff --git a/test/Hix/Test/Managed/LowerOptimize/MutationTest.hs b/test/Hix/Test/Managed/LowerOptimize/MutationTest.hs
--- a/test/Hix/Test/Managed/LowerOptimize/MutationTest.hs
+++ b/test/Hix/Test/Managed/LowerOptimize/MutationTest.hs
@@ -25,8 +25,8 @@
 packages =
   managedPackages [(("local1", "1.0"), ["direct1", "direct2"])]
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("direct1", allDep "transitive1 >=1.0" [
       ([1, 8, 1], []),
@@ -46,7 +46,7 @@
   ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed = [], available = packageDb}
+ghcPackages = GhcPackages {installed = [], available}
 
 state :: ProjectStateProto
 state =
diff --git a/test/Hix/Test/Managed/LowerOptimize/OrderTest.hs b/test/Hix/Test/Managed/LowerOptimize/OrderTest.hs
--- a/test/Hix/Test/Managed/LowerOptimize/OrderTest.hs
+++ b/test/Hix/Test/Managed/LowerOptimize/OrderTest.hs
@@ -21,8 +21,8 @@
 packages =
   managedPackages [(("local1", "1.0"), ["dep1", "dep2", "dep3"])]
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("dep1", [
       ([1, 8], []),
@@ -43,7 +43,7 @@
   ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed = [], available = packageDb}
+ghcPackages = GhcPackages {installed = [], available}
 
 state :: ProjectStateProto
 state =
diff --git a/test/Hix/Test/Managed/LowerStabilize/MutationTest.hs b/test/Hix/Test/Managed/LowerStabilize/MutationTest.hs
--- a/test/Hix/Test/Managed/LowerStabilize/MutationTest.hs
+++ b/test/Hix/Test/Managed/LowerStabilize/MutationTest.hs
@@ -22,8 +22,8 @@
 import Hix.Test.Managed.Run (TestParams (..), Result (..), testParams, lowerTest)
 import Hix.Test.Utils (UnitTest)
 
-packageDb :: SourcePackages
-packageDb =
+available :: SourcePackages
+available =
   [
     ("direct1", [
       ([1, 8, 1], []),
@@ -38,7 +38,7 @@
   ]
 
 ghcPackages :: GhcPackages
-ghcPackages = GhcPackages {installed = [], available = packageDb}
+ghcPackages = GhcPackages {installed = [], available}
 
 packages :: Packages ManagedPackageProto
 packages =
diff --git a/test/Hix/Test/Managed/Run.hs b/test/Hix/Test/Managed/Run.hs
--- a/test/Hix/Test/Managed/Run.hs
+++ b/test/Hix/Test/Managed/Run.hs
@@ -26,7 +26,7 @@
 import Hix.Managed.Data.ProjectContextProto (ProjectContextProto (..))
 import Hix.Managed.Data.ProjectResult (ProjectResult)
 import Hix.Managed.Data.ProjectStateProto (ProjectStateProto)
-import Hix.Managed.Data.StageState (BuildResult (Finished), BuildStatus (Failure))
+import Hix.Managed.Data.StageState (BuildStatus (Failure), resultFromStatus)
 import qualified Hix.Managed.Handlers.Build as BuildHandlers
 import Hix.Managed.Handlers.Build (BuildHandlers (..))
 import qualified Hix.Managed.Handlers.Build.Test as BuildHandlers
@@ -98,7 +98,8 @@
   TestT IO (Result a)
 managedTest' defaultEnvName params main =
   withFrozenCallStack do
-    (handlers0, stateFileRef, _) <- BuildHandlers.handlersUnitTest params.ghcPackages (fmap Finished . params.build)
+    (handlers0, stateFileRef, _) <-
+      BuildHandlers.handlersUnitTest params.ghcPackages (fmap resultFromStatus . params.build)
     (cabalRef, handlers1) <-
       if params.cabalLog
       then first Just <$> BuildHandlers.logCabal handlers0
diff --git a/test/Hix/Test/Managed/SolveTest.hs b/test/Hix/Test/Managed/SolveTest.hs
--- a/test/Hix/Test/Managed/SolveTest.hs
+++ b/test/Hix/Test/Managed/SolveTest.hs
@@ -5,10 +5,11 @@
 
 import Hix.Class.Map (nKeys)
 import qualified Hix.Managed.Cabal.Changes
+import Hix.Managed.Cabal.Changes (solverPlan)
 import Hix.Managed.Cabal.Data.Config (GhcDb (GhcDbSystem))
 import Hix.Managed.Cabal.Data.SolverState (solverState)
 import qualified Hix.Managed.Cabal.Resources as SolveResources
-import Hix.Managed.Cabal.Solve (solveWithCabal)
+import Hix.Managed.Cabal.Solve (solveWithCabal')
 import Hix.Managed.Cabal.Sort (sortDeps)
 import Hix.Managed.Data.EnvContext (EnvDeps (EnvDeps))
 import Hix.Managed.Data.Mutable (unsafeMutableDep)
@@ -21,16 +22,21 @@
   evalEither =<< liftIO do
     runMTest True do
       res <- SolveResources.acquire mempty def (GhcDbSystem Nothing)
-      result <- solveWithCabal res state
+      mb_sip <- solveWithCabal' res state
       sorted <- sortDeps res id (nKeys constraints)
       liftIO do
         putStrLn ""
         putStrLn "Constraints:"
         putStrLn (showP constraints)
-        putStrLn ""
-        putStrLn "Changes:"
-        putStrLn (maybe "no plan" (show . prettyL . (.changes)) result)
-        putStrLn ""
+        for_ mb_sip \ sip -> do
+          let plan = solverPlan sip
+          putStrLn ""
+          putStrLn "Changes:"
+          putStrLn (show (prettyL plan.changes))
+          putStrLn ""
+          putStrLn "Matching DB:"
+          putStrLn (show (prettyL plan.matching))
+          putStrLn ""
         putStrLn "Sorted:"
         putStrLn (showPL sorted)
   where
diff --git a/test/Hix/Test/Managed/UnsafeIsString.hs b/test/Hix/Test/Managed/UnsafeIsString.hs
--- a/test/Hix/Test/Managed/UnsafeIsString.hs
+++ b/test/Hix/Test/Managed/UnsafeIsString.hs
@@ -18,6 +18,7 @@
 import Hix.Data.Version (SourceHash (SourceHash), Version)
 import qualified Hix.Data.VersionBounds
 import Hix.Data.VersionBounds (VersionBounds (VersionBounds), fromLower, unsafeVersionBoundsFromRange, versionBounds)
+import Hix.Managed.Cabal.Data.SourcePackage (SourcePackageId (..))
 import Hix.Managed.Data.Constraints (MutationConstraints (mutation))
 import Hix.Managed.Data.Mutable (MutableDep, unsafeMutableDep)
 import qualified Hix.Managed.Data.MutableId
@@ -71,3 +72,10 @@
     (package, mempty {mutation = unsafeVersionBoundsFromRange version})
     where
       Dep {package, version} = fromString s
+
+instance IsList SourcePackageId where
+  type Item SourcePackageId = Dep
+
+  fromList s = SourcePackageId {deps = fromList s, description = Nothing}
+
+  toList = (.deps)
