diff --git a/Core/include/Acts/Fitter/KalmanFitter.hpp b/Core/include/Acts/Fitter/KalmanFitter.hpp
index ddb52be9e2a13d3466dfb3bdbb808d7bfe7de0c5..56c8d1da901fe93070552ce8e6886808c06c376f 100644
--- a/Core/include/Acts/Fitter/KalmanFitter.hpp
+++ b/Core/include/Acts/Fitter/KalmanFitter.hpp
@@ -20,8 +20,8 @@
 #include "Acts/MagneticField/MagneticFieldContext.hpp"
 #include "Acts/Propagator/AbortList.hpp"
 #include "Acts/Propagator/ActionList.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Utilities/CalibrationContext.hpp"
 #include "Acts/Utilities/Definitions.hpp"
@@ -396,7 +396,7 @@ class KalmanFitter {
       stepper.update(state.stepping, smoothedPars);
       // Reverse the propagation direction
       state.stepping.stepSize =
-          detail::ConstrainedStep(-1. * state.options.maxStepSize);
+          ConstrainedStep(-1. * state.options.maxStepSize);
       state.options.direction = backward;
 
       return Result<void>::success();
diff --git a/Core/include/Acts/Propagator/AtlasStepper.hpp b/Core/include/Acts/Propagator/AtlasStepper.hpp
index c1f923dbac2aa879a64ac4cbf4ea642ae2441472..36c5b9b3886c0074ddefd53e99d1d799e684c2a6 100644
--- a/Core/include/Acts/Propagator/AtlasStepper.hpp
+++ b/Core/include/Acts/Propagator/AtlasStepper.hpp
@@ -12,7 +12,8 @@
 #include <functional>
 #include "Acts/EventData/TrackParameters.hpp"
 #include "Acts/Geometry/GeometryContext.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
+#include "Acts/Propagator/detail/SteppingHelper.hpp"
 #include "Acts/Surfaces/Surface.hpp"
 #include "Acts/Utilities/Definitions.hpp"
 #include "Acts/Utilities/Intersection.hpp"
@@ -28,8 +29,6 @@ using namespace Acts::UnitLiterals;
 template <typename bfield_t>
 class AtlasStepper {
  public:
-  using cstep = detail::ConstrainedStep;
-
   using Jacobian = BoundMatrix;
   using Covariance = BoundSymMatrix;
   using BoundState = std::tuple<BoundParameters, Jacobian, double>;
@@ -51,11 +50,13 @@ class AtlasStepper {
     /// @param[in] pars Input parameters
     /// @param[in] ndir The navigation direction w.r.t. parameters
     /// @param[in] ssize the steps size limitation
+    /// @param [in] stolerance is the stepping tolerance
     template <typename Parameters>
     State(std::reference_wrapper<const GeometryContext> gctx,
           std::reference_wrapper<const MagneticFieldContext> mctx,
           const Parameters& pars, NavigationDirection ndir = forward,
-          double ssize = std::numeric_limits<double>::max())
+          double ssize = std::numeric_limits<double>::max(),
+          double stolerance = s_onSurfaceTolerance)
         : navDir(ndir),
           useJacobian(false),
           step(0.),
@@ -67,6 +68,7 @@ class AtlasStepper {
           covariance(nullptr),
           t0(pars.time()),
           stepSize(ndir * std::abs(ssize)),
+          tolerance(stolerance),
           fieldCache(mctx),
           geoContext(gctx) {
       // The rest of this constructor is copy&paste of AtlasStepper::update() -
@@ -271,8 +273,14 @@ class AtlasStepper {
     // Starting time
     const double t0;
 
-    // adaptive step size of the runge-kutta integration
-    cstep stepSize = std::numeric_limits<double>::max();
+    // Adaptive step size of the runge-kutta integration
+    ConstrainedStep stepSize = std::numeric_limits<double>::max();
+
+    // Previous step size for overstep estimation
+    double previousStepSize = 0.;
+
+    /// The tolerance for the stepping
+    double tolerance = s_onSurfaceTolerance;
 
     /// It caches the current magnetic field cell and stays (and interpolates)
     ///  within as long as this is valid. See step() code for details.
@@ -330,15 +338,59 @@ class AtlasStepper {
   /// Time access
   double time(const State& state) const { return state.t0 + state.pVector[3]; }
 
-  /// Tests if the state reached a surface
+  /// Update surface status
+  ///
+  /// This method intersect the provided surface and update the navigation
+  /// step estimation accordingly (hence it changes the state). It also
+  /// returns the status of the intersection to trigger onSurface in case
+  /// the surface is reached.
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param surface [in] The surface provided
+  /// @param bcheck [in] The boundary check for this status update
+  Intersection::Status updateSurfaceStatus(State& state, const Surface& surface,
+                                           const BoundaryCheck& bcheck) const {
+    return detail::updateSingleSurfaceStatus<AtlasStepper>(*this, state,
+                                                           surface, bcheck);
+  }
+
+  /// Update step size
+  ///
+  /// It checks the status to the reference surface & updates
+  /// the step size accordingly
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param oIntersection [in] The ObjectIntersection to layer, boundary, etc
+  /// @param release [in] boolean to trigger step size release
+  template <typename object_intersection_t>
+  void updateStepSize(State& state, const object_intersection_t& oIntersection,
+                      bool release = true) const {
+    detail::updateSingleStepSize<AtlasStepper>(state, oIntersection, release);
+  }
+
+  /// Set Step size - explicitely with a double
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param stepSize [in] The step size value
+  /// @param stype [in] The step size type to be set
+  void setStepSize(State& state, double stepSize,
+                   ConstrainedStep::Type stype = ConstrainedStep::actor) const {
+    state.previousStepSize = state.stepSize;
+    state.stepSize.update(stepSize, stype, true);
+  }
+
+  /// Release the Step size
   ///
-  /// @param [in] state State that is tests
-  /// @param [in] surface Surface that is tested
+  /// @param state [in,out] The stepping state (thread-local cache)
+  void releaseStepSize(State& state) const {
+    state.stepSize.release(ConstrainedStep::actor);
+  }
+
+  /// Output the Step Size - single component
   ///
-  /// @return Boolean statement if surface is reached by state
-  bool surfaceReached(const State& state, const Surface* surface) const {
-    return surface->isOnSurface(state.geoContext, position(state),
-                                direction(state), true);
+  /// @param state [in,out] The stepping state (thread-local cache)
+  std::string outputStepSize(const State& state) const {
+    return state.stepSize.toString();
   }
 
   /// Create and return the bound state at the current position
diff --git a/Core/include/Acts/Propagator/detail/ConstrainedStep.hpp b/Core/include/Acts/Propagator/ConstrainedStep.hpp
similarity index 89%
rename from Core/include/Acts/Propagator/detail/ConstrainedStep.hpp
rename to Core/include/Acts/Propagator/ConstrainedStep.hpp
index 5826341bbd7d317bd0e2423b6208ce3e8e6538a5..e92fbc8c8b2fd0cf7f34b6611408d26666763a8a 100644
--- a/Core/include/Acts/Propagator/detail/ConstrainedStep.hpp
+++ b/Core/include/Acts/Propagator/ConstrainedStep.hpp
@@ -17,7 +17,6 @@
 #include "Acts/Utilities/Definitions.hpp"
 
 namespace Acts {
-namespace detail {
 
 /// A constrained step class for the steppers
 struct ConstrainedStep {
@@ -129,12 +128,28 @@ struct ConstrainedStep {
 
 inline std::string ConstrainedStep::toString() const {
   std::stringstream dstream;
-  dstream << "(" << std::setw(5) << values[accuracy];
-  dstream << ", " << std::setw(5) << values[actor];
-  dstream << ", " << std::setw(5) << values[aborter];
-  dstream << ", " << std::setw(5) << values[user] << " )";
+
+  // Helper method to avoid unreadable screen output
+  auto streamValue = [&](ConstrainedStep cstep) -> void {
+    double val = values[cstep];
+    dstream << std::setw(5);
+    if (std::abs(val) == std::numeric_limits<double>::max()) {
+      dstream << (val > 0 ? "+∞" : "-∞");
+    } else {
+      dstream << val;
+    }
+  };
+
+  dstream << "(";
+  streamValue(accuracy);
+  dstream << ", ";
+  streamValue(actor);
+  dstream << ", ";
+  streamValue(aborter);
+  dstream << ", ";
+  streamValue(user);
+  dstream << " )";
   return dstream.str();
 }
 
-}  // namespace detail
 }  // namespace Acts
diff --git a/Core/include/Acts/Propagator/DirectNavigator.hpp b/Core/include/Acts/Propagator/DirectNavigator.hpp
index a71a1a41002f89dab8ba2a40782d6904507d0425..bcf26f9a82660ce748c97f05d8c22efe808320d7 100644
--- a/Core/include/Acts/Propagator/DirectNavigator.hpp
+++ b/Core/include/Acts/Propagator/DirectNavigator.hpp
@@ -17,14 +17,12 @@
 #include "Acts/Geometry/Layer.hpp"
 #include "Acts/Geometry/TrackingGeometry.hpp"
 #include "Acts/Geometry/TrackingVolume.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
 #include "Acts/Surfaces/Surface.hpp"
 
 namespace Acts {
 
-using Cstep = detail::ConstrainedStep;
-
 /// DirectNavigator class
 ///
 /// This is a fully guided navigator that progresses through
@@ -36,7 +34,7 @@ class DirectNavigator {
  public:
   /// The sequentially crossed surfaces
   using SurfaceSequence = std::vector<const Surface*>;
-  using SurfaceIter = std::vector<const Surface*>::const_iterator;
+  using SurfaceIter = std::vector<const Surface*>::iterator;
 
   /// Defaulted Constructed
   DirectNavigator() = default;
@@ -76,8 +74,6 @@ class DirectNavigator {
         state.navigation.surfaceSequence = surfaceSequence;
         state.navigation.nextSurfaceIter =
             state.navigation.surfaceSequence.begin();
-        state.navigation.endSurfaceIter =
-            state.navigation.surfaceSequence.end();
         r.initialized = true;
       }
     }
@@ -100,8 +96,6 @@ class DirectNavigator {
 
     /// Iterator the the next surface
     SurfaceIter nextSurfaceIter = surfaceSequence.begin();
-    /// Iterator to the end for the end sequence trigger
-    SurfaceIter endSurfaceIter = surfaceSequence.end();
 
     /// Navigation state - external interface: the start surface
     const Surface* startSurface = nullptr;
@@ -130,20 +124,50 @@ class DirectNavigator {
 
     // Navigator status always resets the current surface
     state.navigation.currentSurface = nullptr;
+    // Output the position in the sequence
+    debugLog(state, [&] {
+      std::stringstream dstream;
+      dstream << std::distance(state.navigation.nextSurfaceIter,
+                               state.navigation.surfaceSequence.end());
+      dstream << " out of " << state.navigation.surfaceSequence.size();
+      dstream << " surfaces remain to try.";
+      return dstream.str();
+    });
     // Check if we are on surface
-    if (state.navigation.nextSurfaceIter != state.navigation.endSurfaceIter &&
-        stepper.surfaceReached(state.stepping,
-                               *state.navigation.nextSurfaceIter)) {
-      // Set the current surface
-      state.navigation.currentSurface = *state.navigation.nextSurfaceIter;
-      debugLog(state, [&] {
-        std::stringstream dstream;
-        dstream << "Current surface set to  "
-                << state.navigation.currentSurface->geoID();
-        return dstream.str();
-      });
-      // Move the sequence to the next surface
-      ++state.navigation.nextSurfaceIter;
+    if (state.navigation.nextSurfaceIter !=
+        state.navigation.surfaceSequence.end()) {
+      // Establish the surface status
+      auto surfaceStatus = stepper.updateSurfaceStatus(
+          state.stepping, **state.navigation.nextSurfaceIter, false);
+      if (surfaceStatus == Intersection::Status::onSurface) {
+        // Set the current surface
+        state.navigation.currentSurface = *state.navigation.nextSurfaceIter;
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Current surface set to  "
+                  << state.navigation.currentSurface->geoID();
+          return dstream.str();
+        });
+        // Move the sequence to the next surface
+        ++state.navigation.nextSurfaceIter;
+        if (state.navigation.nextSurfaceIter !=
+            state.navigation.surfaceSequence.end()) {
+          debugLog(state, [&] {
+            std::stringstream dstream;
+            dstream << "Next surface candidate is  "
+                    << (*state.navigation.nextSurfaceIter)->geoID();
+            return dstream.str();
+          });
+          stepper.releaseStepSize(state.stepping);
+        }
+      } else if (surfaceStatus == Intersection::Status::reachable) {
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Next surface reachable at distance  "
+                  << stepper.outputStepSize(state.stepping);
+          return dstream.str();
+        });
+      }
     }
   }
 
@@ -161,29 +185,37 @@ class DirectNavigator {
 
     // Navigator target always resets the current surface
     state.navigation.currentSurface = nullptr;
-
-    if (state.navigation.nextSurfaceIter != state.navigation.endSurfaceIter) {
-      // take the target intersection
-      auto nextIntersection =
-          (*state.navigation.nextSurfaceIter)
-              ->intersect(state.geoContext, stepper.position(state.stepping),
-                          stepper.direction(state.stepping), false);
-
-      // Intersect the next surface and go
-      double navStep = nextIntersection.intersection.pathLength;
-      double overstepLimit = stepper.overstepLimit(state.stepping);
-      if (navStep < overstepLimit and nextIntersection.alternative) {
-        navStep = nextIntersection.alternative.pathLength;
+    // Output the position in the sequence
+    debugLog(state, [&] {
+      std::stringstream dstream;
+      dstream << std::distance(state.navigation.nextSurfaceIter,
+                               state.navigation.surfaceSequence.end());
+      dstream << " out of " << state.navigation.surfaceSequence.size();
+      dstream << " surfaces remain to try.";
+      return dstream.str();
+    });
+    if (state.navigation.nextSurfaceIter !=
+        state.navigation.surfaceSequence.end()) {
+      // Establish & update the surface status
+      auto surfaceStatus = stepper.updateSurfaceStatus(
+          state.stepping, **state.navigation.nextSurfaceIter, false);
+      if (surfaceStatus == Intersection::Status::unreachable) {
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Surface not reachable anymore, switching to next one in "
+                     "sequence";
+          return dstream.str();
+        });
+        // Move the sequence to the next surface
+        ++state.navigation.nextSurfaceIter;
+      } else {
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Navigation stepSize set to ";
+          dstream << stepper.outputStepSize(state.stepping);
+          return dstream.str();
+        });
       }
-
-      // Set the step size - this has to be outsourced to the Stepper
-      state.stepping.stepSize.update(navStep, Cstep::actor, true);
-      debugLog(state, [&] {
-        std::stringstream dstream;
-        dstream << "Navigation stepSize released and updated to ";
-        dstream << state.stepping.stepSize.toString();
-        return dstream.str();
-      });
     } else {
       // Set the navigation break
       state.navigation.navigationBreak = true;
diff --git a/Core/include/Acts/Propagator/EigenStepper.hpp b/Core/include/Acts/Propagator/EigenStepper.hpp
index 5be22e9f7f2d367e3683610e755f0d63f23f1ed3..2281518c1d7ca3c8f8ffda7bd4a43d8091108441 100644
--- a/Core/include/Acts/Propagator/EigenStepper.hpp
+++ b/Core/include/Acts/Propagator/EigenStepper.hpp
@@ -18,6 +18,7 @@
 #include "Acts/Propagator/EigenStepperError.hpp"
 #include "Acts/Propagator/StepperExtensionList.hpp"
 #include "Acts/Propagator/detail/Auctioneer.hpp"
+#include "Acts/Propagator/detail/SteppingHelper.hpp"
 #include "Acts/Utilities/Intersection.hpp"
 #include "Acts/Utilities/Result.hpp"
 #include "Acts/Utilities/Units.hpp"
@@ -43,8 +44,6 @@ template <typename bfield_t,
           typename auctioneer_t = detail::VoidAuctioneer>
 class EigenStepper {
  public:
-  using cstep = detail::ConstrainedStep;
-
   /// Jacobian, Covariance and State defintions
   using Jacobian = BoundMatrix;
   using Covariance = BoundSymMatrix;
@@ -67,13 +66,15 @@ class EigenStepper {
     /// @param [in] par The track parameters at start
     /// @param [in] ndir The navigation direciton w.r.t momentum
     /// @param [in] ssize is the maximum step size
+    /// @param [in] stolerance is the stepping tolerance
     ///
     /// @note the covariance matrix is copied when needed
     template <typename parameters_t>
     explicit State(std::reference_wrapper<const GeometryContext> gctx,
                    std::reference_wrapper<const MagneticFieldContext> mctx,
                    const parameters_t& par, NavigationDirection ndir = forward,
-                   double ssize = std::numeric_limits<double>::max())
+                   double ssize = std::numeric_limits<double>::max(),
+                   double stolerance = s_onSurfaceTolerance)
         : pos(par.position()),
           dir(par.momentum().normalized()),
           p(par.momentum().norm()),
@@ -81,11 +82,13 @@ class EigenStepper {
           t0(par.time()),
           navDir(ndir),
           stepSize(ndir * std::abs(ssize)),
+          tolerance(stolerance),
           fieldCache(mctx),
           geoContext(gctx) {
       // remember the start parameters
       startPos = pos;
       startDir = dir;
+
       // Init the jacobian matrix if needed
       if (par.covariance()) {
         // Get the reference surface for navigation
@@ -142,11 +145,17 @@ class EigenStepper {
     bool covTransport = false;
     Covariance cov = Covariance::Zero();
 
-    /// accummulated path length state
+    /// Accummulated path length state
     double pathAccumulated = 0.;
 
-    /// adaptive step size of the runge-kutta integration
-    cstep stepSize{std::numeric_limits<double>::max()};
+    /// Adaptive step size of the runge-kutta integration
+    ConstrainedStep stepSize{std::numeric_limits<double>::max()};
+
+    /// Last performed step (for overstep limit calculation)
+    double previousStepSize = 0.;
+
+    /// The tolerance for the stepping
+    double tolerance = s_onSurfaceTolerance;
 
     /// This caches the current magnetic field cell and stays
     /// (and interpolates) within it as long as this is valid.
@@ -210,20 +219,67 @@ class EigenStepper {
   /// @param state [in] The stepping state (thread-local cache)
   double time(const State& state) const { return state.t0 + state.dt; }
 
-  /// Overstep limit
+  /// Update surface status
   ///
-  /// @param state [in] The stepping state (thread-local cache)
-  double overstepLimit(const State& /*state*/) const { return m_overstepLimit; }
+  /// It checks the status to the reference surface & updates
+  /// the step size accordingly
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param surface [in] The surface provided
+  /// @param bcheck [in] The boundary check for this status update
+  Intersection::Status updateSurfaceStatus(State& state, const Surface& surface,
+                                           const BoundaryCheck& bcheck) const {
+    return detail::updateSingleSurfaceStatus<EigenStepper>(*this, state,
+                                                           surface, bcheck);
+  }
+
+  /// Update step size
+  ///
+  /// This method intersect the provided surface and update the navigation
+  /// step estimation accordingly (hence it changes the state). It also
+  /// returns the status of the intersection to trigger onSurface in case
+  /// the surface is reached.
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param oIntersection [in] The ObjectIntersection to layer, boundary, etc
+  /// @param release [in] boolean to trigger step size release
+  template <typename object_intersection_t>
+  void updateStepSize(State& state, const object_intersection_t& oIntersection,
+                      bool release = true) const {
+    detail::updateSingleStepSize<EigenStepper>(state, oIntersection, release);
+  }
+
+  /// Set Step size - explicitely with a double
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param stepSize [in] The step size value
+  /// @param stype [in] The step size type to be set
+  void setStepSize(State& state, double stepSize,
+                   ConstrainedStep::Type stype = ConstrainedStep::actor) const {
+    state.previousStepSize = state.stepSize;
+    state.stepSize.update(stepSize, stype, true);
+  }
 
-  /// Tests if the state reached a surface
+  /// Release the Step size
   ///
-  /// @param [in] state State that is tests
-  /// @param [in] surface Surface that is tested
+  /// @param state [in,out] The stepping state (thread-local cache)
+  void releaseStepSize(State& state) const {
+    state.stepSize.release(ConstrainedStep::actor);
+  }
+
+  /// Output the Step Size - single component
   ///
-  /// @return Boolean statement if surface is reached by state
-  bool surfaceReached(const State& state, const Surface* surface) const {
-    return surface->isOnSurface(state.geoContext, position(state),
-                                direction(state), true);
+  /// @param state [in,out] The stepping state (thread-local cache)
+  std::string outputStepSize(const State& state) const {
+    return state.stepSize.toString();
+  }
+
+  /// Overstep limit
+  ///
+  /// @param state [in] The stepping state (thread-local cache)
+  double overstepLimit(const State& /*state*/) const {
+    // A dynamic overstep limit could sit here
+    return -m_overstepLimit;
   }
 
   /// Create and return the bound state at the current position
@@ -319,7 +375,7 @@ class EigenStepper {
   BField m_bField;
 
   /// Overstep limit: could/should be dynamic
-  double m_overstepLimit = -50_um;
+  double m_overstepLimit = 100_um;
 };
 }  // namespace Acts
 
diff --git a/Core/include/Acts/Propagator/EigenStepper.ipp b/Core/include/Acts/Propagator/EigenStepper.ipp
index e30f2f8e25a768a8427ab6e2429c0612082730f6..109160aa92a0c7e654d7f4893fd3f7ccc64f5575 100644
--- a/Core/include/Acts/Propagator/EigenStepper.ipp
+++ b/Core/include/Acts/Propagator/EigenStepper.ipp
@@ -260,9 +260,7 @@ Acts::Result<double> Acts::EigenStepper<B, E, A>::step(
     return (error_estimate <= state.options.tolerance);
   };
 
-  double stepSizeScaling;
-
-  unsigned int stepAttempts = 0;
+  double stepSizeScaling = 1.;
   // Select and adjust the appropriate Runge-Kutta step size as given
   // ATL-SOFT-PUB-2009-001
   while (!tryRungeKuttaStep(state.stepping.stepSize)) {
@@ -276,14 +274,6 @@ Acts::Result<double> Acts::EigenStepper<B, E, A>::step(
     }
     state.stepping.stepSize = state.stepping.stepSize * stepSizeScaling;
 
-    // @HOTFIX to break the RK step trying, @TODO will be replaced
-    // by proper overstepping mechanism
-    if (++stepAttempts == 100) {
-      // step in mm steps, costly but should do
-      state.stepping.stepSize = state.stepping.navDir * 1_mm;
-      break;
-    }
-
     // If step size becomes too small the particle remains at the initial
     // place
     if (state.stepping.stepSize * state.stepping.stepSize <
diff --git a/Core/include/Acts/Propagator/Navigator.hpp b/Core/include/Acts/Propagator/Navigator.hpp
index 5e9352c5d9f3b632a4c30a5eda4179f3656c46cd..de498b9e43084eeab69d38db54c7fa485d586f99 100644
--- a/Core/include/Acts/Propagator/Navigator.hpp
+++ b/Core/include/Acts/Propagator/Navigator.hpp
@@ -17,14 +17,13 @@
 #include "Acts/Geometry/Layer.hpp"
 #include "Acts/Geometry/TrackingGeometry.hpp"
 #include "Acts/Geometry/TrackingVolume.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
 #include "Acts/Surfaces/Surface.hpp"
 #include "Acts/Utilities/Units.hpp"
 
 namespace Acts {
 
-using Cstep = detail::ConstrainedStep;
 using namespace Acts::UnitLiterals;
 
 /// @brief struct for the Navigation options that are forwarded to
@@ -97,8 +96,8 @@ struct NavigationOptions {
 /// This navigation actor thus always needs to run first!
 /// It does two things: it figures out the order of volumes, layers and
 /// surfaces. For each propagation step, the operator() runs, which checks if
-/// the current surface (or layer/volume boundary) is reached via
-/// surfaceReached.
+/// the current surface (or layer/volume boundary) is reached.
+///
 /// The current target surface is the surface pointed to by of the iterators
 /// for the surfaces, layers or volume boundaries.
 /// If a surface is found, the state.navigation.currentSurface
@@ -106,7 +105,7 @@ struct NavigationOptions {
 /// actor uses the ordered  iterators  to figure out which surface, layer or
 /// volume boundary is _supposed_ to be hit next. It then sets the maximum
 /// step size to the path length found out by straight line intersection. If
-/// the surfaceReached call fails, it also  re-computes the step size, to make
+/// the state is not on surface, it also  re-computes the step size, to make
 /// sure we end up at the desired surface.
 ///
 class Navigator {
@@ -325,7 +324,7 @@ class Navigator {
           });
           // Navigation break & release navigation stepping
           state.navigation.navigationBreak = true;
-          state.stepping.stepSize.release(Cstep::actor);
+          stepper.releaseStepSize(state.stepping);
           return;
         } else {
           debugLog(state, [&] { return std::string("Volume updated."); });
@@ -345,7 +344,7 @@ class Navigator {
       });
       // Set navigation break and release the navigation step size
       state.navigation.navigationBreak = true;
-      state.stepping.stepSize.release(Cstep::actor);
+      stepper.releaseStepSize(state.stepping);
     } else {
       debugLog(state, [&] {
         return std::string("Status could not be determined - good luck.");
@@ -399,7 +398,7 @@ class Navigator {
       });
       // Set navigation break and release the navigation step size
       state.navigation.navigationBreak = true;
-      state.stepping.stepSize.release(Cstep::actor);
+      stepper.releaseStepSize(state.stepping);
     }
 
     // Navigator target always resets the current surface
@@ -518,7 +517,9 @@ class Navigator {
     // Check if we are at a surface
     // If we are on the surface pointed at by the iterator, we can make
     // it the current one to pass it to the other actors
-    if (stepper.surfaceReached(state.stepping, surface)) {
+    auto surfaceStatus =
+        stepper.updateSurfaceStatus(state.stepping, *surface, true);
+    if (surfaceStatus == Intersection::Status::onSurface) {
       debugLog(state, [&] {
         return std::string("Status Surface successfully hit, storing it.");
       });
@@ -572,7 +573,7 @@ class Navigator {
         });
         // set the navigation break
         state.navigation.navigationBreak = true;
-        state.stepping.stepSize.release(Cstep::actor);
+        stepper.releaseStepSize(state.stepping);
       }
       return startResolved;
     }
@@ -586,13 +587,7 @@ class Navigator {
       });
       return false;
     }
-
-    // Create the navigaton options
-    NavigationOptions<Surface> navOpts(state.stepping.navDir, true);
-    navOpts.pathLimit = state.stepping.stepSize.value(Cstep::aborter);
-    navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
-
-    // Loop over the navigation surfaces
+    // Loop over the remaining navigation surfaces
     while (state.navigation.navSurfaceIter !=
            state.navigation.navSurfaces.end()) {
       // Screen output how much is left to try
@@ -613,26 +608,20 @@ class Navigator {
         dstream << surface->geoID();
         return dstream.str();
       });
-      // Now intersect (should exclude punch-through)
-      auto surfaceIntersect = surface->intersect(
-          state.geoContext, stepper.position(state.stepping),
-          stepper.direction(state.stepping), navOpts.boundaryCheck);
-      double surfaceDistance = surfaceIntersect.intersection.pathLength;
-      if (!surfaceIntersect) {
+      // Estimate the surface status
+      auto surfaceStatus =
+          stepper.updateSurfaceStatus(state.stepping, *surface, true);
+      if (surfaceStatus == Intersection::Status::reachable) {
         debugLog(state, [&] {
           std::stringstream dstream;
-          dstream << "Surface intersection at path length ";
-          dstream << surfaceDistance;
-          dstream << " is not valid.";
+          dstream << "Surface reachable, step size updated to ";
+          dstream << stepper.outputStepSize(state.stepping);
           return dstream.str();
         });
-        ++state.navigation.navSurfaceIter;
-        continue;
+        return true;
       }
-      // Update the step for the stepper
-      updateStep(state, surfaceDistance, true);
-      // Return to the propagator
-      return true;
+      ++state.navigation.navSurfaceIter;
+      continue;
     }
 
     // Reached the end of the surface iteration
@@ -730,7 +719,7 @@ class Navigator {
         if (!protoNavSurfaces.empty()) {
           // did we find any surfaces?
 
-          // are we on the first surface?
+          // Check: are we on the first surface?
           if (state.navigation.currentSurface == nullptr ||
               state.navigation.currentSurface !=
                   protoNavSurfaces.front().object) {
@@ -741,9 +730,15 @@ class Navigator {
                 state.navigation.navSurfaces.begin();
             state.navigation.navLayers = {};
             state.navigation.navLayerIter = state.navigation.navLayers.end();
-            updateStep(state,
-                       state.navigation.navSurfaceIter->intersection.pathLength,
-                       true);
+            // The stepper updates the step size ( single / multi component)
+            stepper.updateStepSize(state.stepping,
+                                   *state.navigation.navSurfaceIter, true);
+            debugLog(state, [&] {
+              std::stringstream dstream;
+              dstream << "Navigation stepSize updated to ";
+              dstream << stepper.outputStepSize(state.stepping);
+              return dstream.str();
+            });
             return true;
           }
         }
@@ -772,24 +767,24 @@ class Navigator {
           continue;
         }
       }
-      // Otherwise try to step towards it
-      NavigationOptions<Surface> navOpts(state.stepping.navDir, true);
-      navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
-      auto layerIntersect = layerSurface->intersect(
-          state.geoContext, stepper.position(state.stepping),
-          stepper.direction(state.stepping), navOpts.boundaryCheck);
-      // check if the intersect is invalid
-      if (!layerIntersect) {
+      // Try to step towards it
+      auto layerStatus =
+          stepper.updateSurfaceStatus(state.stepping, *layerSurface, true);
+      if (layerStatus == Intersection::Status::reachable) {
         debugLog(state, [&] {
-          return std::string("Layer intersection not valid, skipping it.");
+          std::stringstream dstream;
+          dstream << "Layer reachable, step size updated to ";
+          dstream << stepper.outputStepSize(state.stepping);
+          return dstream.str();
         });
-        ++state.navigation.navLayerIter;
-      } else {
-        // update the navigation step size, release the former first
-        updateStep(state, layerIntersect.intersection.pathLength, true);
         return true;
       }
+      debugLog(state, [&] {
+        return std::string("Layer intersection not valid, skipping it.");
+      });
+      ++state.navigation.navLayerIter;
     }
+
     // Re-initialize target at last layer, only in case it is the target volume
     // This avoids a wrong target volume estimation
     if (state.navigation.currentVolume == state.navigation.targetVolume) {
@@ -851,7 +846,7 @@ class Navigator {
             "No sufficient information to resolve boundary, "
             "stopping navigation.");
       });
-      state.stepping.stepSize.release(Cstep::actor);
+      stepper.releaseStepSize(state.stepping);
       return false;
     } else if (state.navigation.currentVolume ==
                state.navigation.targetVolume) {
@@ -860,7 +855,7 @@ class Navigator {
             "In target volume: no need to resolve boundary, "
             "stopping navigation.");
         state.navigation.navigationBreak = true;
-        state.stepping.stepSize.release(Cstep::actor);
+        stepper.releaseStepSize(state.stepping);
       });
       return true;
     }
@@ -870,7 +865,7 @@ class Navigator {
 
     // The navigation options
     NavigationOptions<Surface> navOpts(state.stepping.navDir, true);
-    navOpts.pathLimit = state.stepping.stepSize.value(Cstep::aborter);
+    navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
     navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
 
     // If you came until here, and you might not have boundaries
@@ -902,6 +897,18 @@ class Navigator {
       });
       // Set the begin iterator
       state.navigation.navBoundaryIter = state.navigation.navBoundaries.begin();
+      if (not state.navigation.navBoundaries.empty()) {
+        // Set to the first and return to the stepper
+        stepper.updateStepSize(state.stepping,
+                               *state.navigation.navBoundaryIter, true);
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Navigation stepSize updated to ";
+          dstream << stepper.outputStepSize(state.stepping);
+          return dstream.str();
+        });
+        return true;
+      }
     }
 
     // Loop over the boundary surface
@@ -909,38 +916,20 @@ class Navigator {
            state.navigation.navBoundaries.end()) {
       // That is the current boundary surface
       auto boundarySurface = state.navigation.navBoundaryIter->representation;
-      // Step towards the boundary surface
-      auto boundaryIntersect = boundarySurface->intersect(
-          state.geoContext, stepper.position(state.stepping),
-          stepper.direction(state.stepping), navOpts.boundaryCheck);
-      // Distance
-      auto distance = boundaryIntersect.intersection.pathLength;
-      // Check the boundary is properly intersected: we are in target mode
-      if (!boundaryIntersect or
-          distance * distance < s_onSurfaceTolerance * s_onSurfaceTolerance) {
+      // Step towards the boundary surfrace
+      auto boundaryStatus = stepper.updateSurfaceStatus(
+          state.stepping, *boundarySurface, navOpts.boundaryCheck);
+      if (boundaryStatus == Intersection::Status::reachable) {
         debugLog(state, [&] {
-          std::stringstream ss;
-          ss << "Boundary intersection with:\n";
-          boundaryIntersect.object->toStream(state.geoContext, ss);
-          ss << "\n";
-          ss << "Boundary intersection not valid, skipping it.\n";
-          ss << "valid: " << bool(boundaryIntersect) << "\n";
-          ss << "pathLength: " << distance << "\n";
-          if (distance < 0 && std::abs(distance) < 0.01) {
-            ss << "Very likely overstepped over boundary surface! \n";
-          }
-          return ss.str();
+          std::stringstream dstream;
+          dstream << "Boundary reachable, step size updated to ";
+          dstream << stepper.outputStepSize(state.stepping);
+          return dstream.str();
         });
-        // Increase the iterator to the next one
-        ++state.navigation.navBoundaryIter;
-
-      } else {
-        debugLog(state,
-                 [&] { return std::string("Boundary intersection valid."); });
-        // This is a new navigation stream, release the former first
-        updateStep(state, distance, true);
         return true;
       }
+      // Increase the iterator to the next one
+      ++state.navigation.navBoundaryIter;
     }
     // Could not do anything
     return false;
@@ -969,7 +958,8 @@ class Navigator {
   template <typename propagator_state_t, typename stepper_t>
   void initializeTarget(propagator_state_t& state,
                         const stepper_t& stepper) const {
-    if (state.navigation.targetVolume && state.stepping.pathAccumulated == 0.) {
+    if (state.navigation.targetVolume and
+        state.stepping.pathAccumulated == 0.) {
       debugLog(state, [&] {
         return std::string("Re-initialzing cancelled as it is the first step.");
       });
@@ -1002,39 +992,35 @@ class Navigator {
       }
       // Slow navigation initialization for target:
       // target volume and layer search through global search
-      NavigationOptions<Surface> navOpts(state.stepping.navDir, false,
-                                         resolveSensitive, resolveMaterial,
-                                         resolvePassive);
-      navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
-
-      // take the target intersection
       auto targetIntersection = state.navigation.targetSurface->intersect(
           state.geoContext, stepper.position(state.stepping),
-          stepper.direction(state.stepping), navOpts.boundaryCheck);
-      debugLog(state, [&] {
-        std::stringstream dstream;
-        dstream << "Target estimate position (";
-        dstream << targetIntersection.intersection.position.x() << ", ";
-        dstream << targetIntersection.intersection.position.y() << ", ";
-        dstream << targetIntersection.intersection.position.z() << ")";
-        return dstream.str();
-      });
-      /// get the target volume from the intersection
-      auto tPosition = targetIntersection.intersection.position;
-      state.navigation.targetVolume =
-          trackingGeometry->lowestTrackingVolume(state.geoContext, tPosition);
-      state.navigation.targetLayer =
-          state.navigation.targetVolume
-              ? state.navigation.targetVolume->associatedLayer(state.geoContext,
-                                                               tPosition)
-              : nullptr;
-      if (state.navigation.targetVolume) {
+          state.stepping.navDir * stepper.direction(state.stepping), false);
+      if (targetIntersection) {
         debugLog(state, [&] {
           std::stringstream dstream;
-          dstream << "Target volume estimated : ";
-          dstream << state.navigation.targetVolume->volumeName();
+          dstream << "Target estimate position (";
+          dstream << targetIntersection.intersection.position.x() << ", ";
+          dstream << targetIntersection.intersection.position.y() << ", ";
+          dstream << targetIntersection.intersection.position.z() << ")";
           return dstream.str();
         });
+        /// get the target volume from the intersection
+        auto tPosition = targetIntersection.intersection.position;
+        state.navigation.targetVolume =
+            trackingGeometry->lowestTrackingVolume(state.geoContext, tPosition);
+        state.navigation.targetLayer =
+            state.navigation.targetVolume
+                ? state.navigation.targetVolume->associatedLayer(
+                      state.geoContext, tPosition)
+                : nullptr;
+        if (state.navigation.targetVolume) {
+          debugLog(state, [&] {
+            std::stringstream dstream;
+            dstream << "Target volume estimated : ";
+            dstream << state.navigation.targetVolume->volumeName();
+            return dstream.str();
+          });
+        }
       }
     }
   }
@@ -1064,7 +1050,7 @@ class Navigator {
         state.stepping.navDir, true, resolveSensitive, resolveMaterial,
         resolvePassive, startSurface, state.navigation.targetSurface);
     // Check the limit
-    navOpts.pathLimit = state.stepping.stepSize.value(Cstep::aborter);
+    navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
     // No overstepping on start layer, otherwise ask the stepper
     navOpts.overstepLimit = (cLayer != nullptr)
                                 ? s_onSurfaceTolerance
@@ -1087,11 +1073,15 @@ class Navigator {
       });
       // set the iterator
       state.navigation.navSurfaceIter = state.navigation.navSurfaces.begin();
-      // Update the navigation step size before you return to the stepper
-      // This is a new navigation stream, release the former step size first
-      updateStep(state,
-                 state.navigation.navSurfaceIter->intersection.pathLength,
-                 true);
+      // The stepper updates the step size ( single / multi component)
+      stepper.updateStepSize(state.stepping, *state.navigation.navSurfaceIter,
+                             true);
+      debugLog(state, [&] {
+        std::stringstream dstream;
+        dstream << "Navigation stepSize updated to ";
+        dstream << stepper.outputStepSize(state.stepping);
+        return dstream.str();
+      });
       return true;
     }
     state.navigation.navSurfaceIter = state.navigation.navSurfaces.end();
@@ -1133,7 +1123,7 @@ class Navigator {
                                      resolvePassive, startLayer, nullptr);
     // Set also the target surface
     navOpts.targetSurface = state.navigation.targetSurface;
-    navOpts.pathLimit = state.stepping.stepSize.value(Cstep::aborter);
+    navOpts.pathLimit = state.stepping.stepSize.value(ConstrainedStep::aborter);
     navOpts.overstepLimit = stepper.overstepLimit(state.stepping);
     // Request the compatible layers
     state.navigation.navLayers =
@@ -1160,11 +1150,15 @@ class Navigator {
           state.navigation.navLayerIter->object !=
               state.navigation.startLayer) {
         debugLog(state, [&] { return std::string("Target at layer."); });
-        // update the navigation step size before you return
-        updateStep(state,
-                   state.navigation.navLayerIter->intersection.pathLength,
-                   true);
-        // Trigger the return to the propagator
+        // The stepper updates the step size ( single / multi component)
+        stepper.updateStepSize(state.stepping, *state.navigation.navLayerIter,
+                               true);
+        debugLog(state, [&] {
+          std::stringstream dstream;
+          dstream << "Navigation stepSize updated to ";
+          dstream << stepper.outputStepSize(state.stepping);
+          return dstream.str();
+        });
         return true;
       }
     }
@@ -1176,33 +1170,11 @@ class Navigator {
     debugLog(state, [&] {
       return std::string("No compatible layer candidates found.");
     });
-    // Update the navigation step to the target step to trigger
-    // step modification when requested
-    updateStep(state, state.stepping.stepSize.value(Cstep::aborter));
-
+    // Release the step size
+    stepper.releaseStepSize(state.stepping);
     return false;
   }
 
-  /// This method updates the constrained step size
-  ///
-  /// @tparam propagator_state_t is the state type
-  ///
-  /// @param[in,out] state The state object for the step length
-  /// @param[in] step the step size
-  /// @param[in] release flag steers if the step is released first
-  template <typename propagator_state_t>
-  void updateStep(propagator_state_t& state, double navigationStep,
-                  bool release = false) const {  //  update the step
-    state.stepping.stepSize.update(navigationStep, Cstep::actor, release);
-    debugLog(state, [&] {
-      std::stringstream dstream;
-      std::string releaseFlag = release ? "released and " : "";
-      dstream << "Navigation stepSize " << releaseFlag << "updated to ";
-      dstream << state.stepping.stepSize.toString();
-      return dstream.str();
-    });
-  }
-
   /// --------------------------------------------------------------------
   /// Inactive
   ///
@@ -1238,12 +1210,12 @@ class Navigator {
       if (state.navigation.targetReached || !state.navigation.targetSurface) {
         return true;
       }
+      auto targetStatus = stepper.updateSurfaceStatus(
+          state.stepping, *state.navigation.targetSurface, true);
       // the only advance could have been to the target
-      if (stepper.surfaceReached(state.stepping,
-                                 state.navigation.targetSurface)) {
+      if (targetStatus == Intersection::Status::onSurface) {
         // set the target surface
         state.navigation.currentSurface = state.navigation.targetSurface;
-
         debugLog(state, [&] {
           std::stringstream dstream;
           dstream << "Current surface set to target surface ";
diff --git a/Core/include/Acts/Propagator/Propagator.hpp b/Core/include/Acts/Propagator/Propagator.hpp
index 57822b9a9101f0b94b5af5530d0f057b418a42fd..eb55c63bf94b3170321791c5c66f01c1edf863da 100644
--- a/Core/include/Acts/Propagator/Propagator.hpp
+++ b/Core/include/Acts/Propagator/Propagator.hpp
@@ -94,9 +94,9 @@ struct PropagatorOptions {
     eoptions.absPdgCode = absPdgCode;
     eoptions.mass = mass;
     eoptions.maxSteps = maxSteps;
-    eoptions.maxStepSize = maxStepSize;
+    eoptions.maxStepSize = direction * std::abs(maxStepSize);
     eoptions.targetTolerance = targetTolerance;
-    eoptions.pathLimit = pathLimit;
+    eoptions.pathLimit = direction * std::abs(pathLimit);
     eoptions.loopProtection = loopProtection;
     eoptions.loopFraction = loopFraction;
     // Output option
@@ -241,7 +241,7 @@ class Propagator final {
     State(const parameters_t& start, const propagator_options_t& topts)
         : options(topts),
           stepping(topts.geoContext, topts.magFieldContext, start,
-                   topts.direction, topts.maxStepSize),
+                   topts.direction, topts.maxStepSize, topts.tolerance),
           geoContext(topts.geoContext) {
       // Setting the start surface
       navigation.startSurface = &start.referenceSurface();
diff --git a/Core/include/Acts/Propagator/Propagator.ipp b/Core/include/Acts/Propagator/Propagator.ipp
index b4c684c4d686ef9ca58763108a3471a7c0d31c32..f5f188081e57b4f83fbc3ced3fa1808adf3144f0 100644
--- a/Core/include/Acts/Propagator/Propagator.ipp
+++ b/Core/include/Acts/Propagator/Propagator.ipp
@@ -107,6 +107,8 @@ auto Acts::Propagator<S, N>::propagate(
 
   // Expand the abort list with a path aborter
   path_aborter_t pathAborter;
+  pathAborter.internalLimit = options.pathLimit;
+
   auto abortList = options.abortList.append(pathAborter);
 
   // The expanded options (including path limit)
@@ -166,6 +168,7 @@ auto Acts::Propagator<S, N>::propagate(
   // Type of provided options
   target_aborter_t targetAborter;
   path_aborter_t pathAborter;
+  pathAborter.internalLimit = options.pathLimit;
   auto abortList = options.abortList.append(targetAborter, pathAborter);
 
   // Create the extended options and declare their type
diff --git a/Core/include/Acts/Propagator/StepperConcept.hpp b/Core/include/Acts/Propagator/StepperConcept.hpp
index 585d33281f4c857d0ed531a95743f4a6a9cd8ae9..7dc850663c11be5dd8e809fd18cf2a58984303d7 100644
--- a/Core/include/Acts/Propagator/StepperConcept.hpp
+++ b/Core/include/Acts/Propagator/StepperConcept.hpp
@@ -9,8 +9,11 @@
 #pragma once
 
 #include "Acts/EventData/TrackParameters.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
+#include "Acts/Surfaces/BoundaryCheck.hpp"
+#include "Acts/Surfaces/Surface.hpp"
 #include "Acts/Utilities/Definitions.hpp"
+#include "Acts/Utilities/Intersection.hpp"
 #include "Acts/Utilities/TypeTraits.hpp"
 
 namespace Acts {
@@ -40,12 +43,15 @@ namespace concept {
   METHOD_TRAIT(charge_t, charge);
   METHOD_TRAIT(time_t, time);
   METHOD_TRAIT(overstep_t, overstepLimit);
-  METHOD_TRAIT(surface_reached_t, surfaceReached);
   METHOD_TRAIT(bound_state_method_t, boundState);
   METHOD_TRAIT(curvilinear_state_method_t, curvilinearState);
   METHOD_TRAIT(update_t, update);
   METHOD_TRAIT(covariance_transport_t, covarianceTransport);
   METHOD_TRAIT(step_t, step);
+  METHOD_TRAIT(update_surface_status_t, updateSurfaceStatus);
+  METHOD_TRAIT(set_step_size_t, setStepSize);
+  METHOD_TRAIT(release_step_size_t, releaseStepSize);
+  METHOD_TRAIT(output_step_size_t, outputStepSize);
 
   template <typename T>
   using cov_transport_t = decltype(std::declval<T>().covTransport);
@@ -65,7 +71,7 @@ namespace concept {
                 has_member<S, cov_t, BoundSymMatrix>,
                 has_member<S, nav_dir_t, NavigationDirection>,
                 has_member<S, path_accumulated_t, double>,
-                has_member<S, step_size_t, detail::ConstrainedStep>
+                has_member<S, step_size_t, ConstrainedStep>
                >;
   // clang-format on
 
@@ -98,8 +104,6 @@ namespace concept {
         static_assert(time_exists, "time method not found");
         constexpr static bool overstep_exists = has_method<const S, double, overstep_t, const state&>;
         static_assert(overstep_exists, "overstepLimit method not found");
-        constexpr static bool surface_reached_exists = has_method<const S, bool, surface_reached_t, const state&, const Surface*>;
-        static_assert(surface_reached_exists, "surfaceReached method not found");
         constexpr static bool bound_state_method_exists= has_method<const S, typename S::BoundState, bound_state_method_t, state&, const Surface&, bool>;
         static_assert(bound_state_method_exists, "boundState method not found");
         constexpr static bool curvilinear_state_method_exists = has_method<const S, typename S::CurvilinearState, curvilinear_state_method_t, state&, bool>;
@@ -110,6 +114,14 @@ namespace concept {
         constexpr static bool covariance_transport_exists = require<has_method<const S, void, covariance_transport_t, state&, bool>,
                                                                     has_method<const S, void, covariance_transport_t, state&, const Surface&, bool>>;
         static_assert(covariance_transport_exists, "covarianceTransport method not found");
+        constexpr static bool update_surface_exists = has_method<const S, Intersection::Status, update_surface_status_t, state&, const Surface&, const BoundaryCheck&>;
+        static_assert(update_surface_exists, "updateSurfaceStatus method not found");
+        constexpr static bool set_step_size_exists = has_method<const S, void, set_step_size_t, state&, double, ConstrainedStep::Type>;
+        static_assert(set_step_size_exists, "setStepSize method not found");
+        constexpr static bool release_step_size_exists = has_method<const S, void, release_step_size_t, state&>;
+        static_assert(release_step_size_exists, "releaseStepSize method not found");
+        constexpr static bool output_step_size_exists = has_method<const S, std::string, output_step_size_t, const state&>;
+        static_assert(output_step_size_exists, "outputStepSize method not found");
 
         constexpr static bool value = require<state_exists,
                                               jacobian_exists,
@@ -123,11 +135,14 @@ namespace concept {
                                               momentum_exists,
                                               charge_exists,
                                               time_exists,
-                                              surface_reached_exists,
                                               bound_state_method_exists,
                                               curvilinear_state_method_exists,
                                               update_method_exists,
-                                              covariance_transport_exists>;
+                                              covariance_transport_exists,
+                                              update_surface_exists,
+                                              set_step_size_exists,
+                                              release_step_size_exists,
+                                              output_step_size_exists>;
       };
   // clang-format on
   }  // namespace Stepper
diff --git a/Core/include/Acts/Propagator/StraightLineStepper.hpp b/Core/include/Acts/Propagator/StraightLineStepper.hpp
index df6cfa061d6a247b8a17749e5223f2bbf29c8062..df07a82071cee025051778f589f78da867c21f0b 100644
--- a/Core/include/Acts/Propagator/StraightLineStepper.hpp
+++ b/Core/include/Acts/Propagator/StraightLineStepper.hpp
@@ -15,7 +15,8 @@
 #include "Acts/Geometry/GeometryContext.hpp"
 #include "Acts/MagneticField/MagneticFieldContext.hpp"
 #include "Acts/MagneticField/NullBField.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
+#include "Acts/Propagator/detail/SteppingHelper.hpp"
 #include "Acts/Surfaces/Surface.hpp"
 #include "Acts/Utilities/Definitions.hpp"
 #include "Acts/Utilities/Intersection.hpp"
@@ -30,8 +31,6 @@ namespace Acts {
 /// used for simple material mapping, navigation validation
 class StraightLineStepper {
  public:
-  using cstep = detail::ConstrainedStep;
-
   using Jacobian = BoundMatrix;
   using Covariance = BoundSymMatrix;
   using BoundState = std::tuple<BoundParameters, Jacobian, double>;
@@ -53,11 +52,13 @@ class StraightLineStepper {
     /// @param [in] par The track parameters at start
     /// @param [in] ndir is the navigation direction
     /// @param [in] ssize is the (absolute) maximum step size
+    /// @param [in] stolerance is the stepping tolerance
     template <typename parameters_t>
     explicit State(std::reference_wrapper<const GeometryContext> gctx,
                    std::reference_wrapper<const MagneticFieldContext> /*mctx*/,
                    const parameters_t& par, NavigationDirection ndir = forward,
-                   double ssize = std::numeric_limits<double>::max())
+                   double ssize = std::numeric_limits<double>::max(),
+                   double stolerance = s_onSurfaceTolerance)
         : pos(par.position()),
           dir(par.momentum().normalized()),
           p(par.momentum().norm()),
@@ -65,6 +66,7 @@ class StraightLineStepper {
           t0(par.time()),
           navDir(ndir),
           stepSize(ndir * std::abs(ssize)),
+          tolerance(stolerance),
           geoContext(gctx) {
       if (par.covariance()) {
         // Get the reference surface for navigation
@@ -119,7 +121,13 @@ class StraightLineStepper {
     double pathAccumulated = 0.;
 
     /// adaptive step size of the runge-kutta integration
-    cstep stepSize = std::numeric_limits<double>::max();
+    ConstrainedStep stepSize = std::numeric_limits<double>::max();
+
+    // Previous step size for overstep estimation (ignored for SL stepper)
+    double previousStepSize = 0.;
+
+    /// The tolerance for the stepping
+    double tolerance = s_onSurfaceTolerance;
 
     // Cache the geometry context of this propagation
     std::reference_wrapper<const GeometryContext> geoContext;
@@ -174,15 +182,60 @@ class StraightLineStepper {
     return s_onSurfaceTolerance;
   }
 
-  /// Tests if the state reached a surface
+  /// Update surface status
+  ///
+  /// This method intersect the provided surface and update the navigation
+  /// step estimation accordingly (hence it changes the state). It also
+  /// returns the status of the intersection to trigger onSurface in case
+  /// the surface is reached.
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param surface [in] The surface provided
+  /// @param bcheck [in] The boundary check for this status update
+  Intersection::Status updateSurfaceStatus(State& state, const Surface& surface,
+                                           const BoundaryCheck& bcheck) const {
+    return detail::updateSingleSurfaceStatus<StraightLineStepper>(
+        *this, state, surface, bcheck);
+  }
+
+  /// Update step size
+  ///
+  /// It checks the status to the reference surface & updates
+  /// the step size accordingly
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param oIntersection [in] The ObjectIntersection to layer, boundary, etc
+  /// @param release [in] boolean to trigger step size release
+  template <typename object_intersection_t>
+  void updateStepSize(State& state, const object_intersection_t& oIntersection,
+                      bool release = true) const {
+    detail::updateSingleStepSize<StraightLineStepper>(state, oIntersection,
+                                                      release);
+  }
+
+  /// Set Step size - explicitely with a double
+  ///
+  /// @param state [in,out] The stepping state (thread-local cache)
+  /// @param stepSize [in] The step size value
+  /// @param stype [in] The step size type to be set
+  void setStepSize(State& state, double stepSize,
+                   ConstrainedStep::Type stype = ConstrainedStep::actor) const {
+    state.previousStepSize = state.stepSize;
+    state.stepSize.update(stepSize, stype, true);
+  }
+
+  /// Release the Step size
   ///
-  /// @param [in] state State that is tests
-  /// @param [in] surface Surface that is tested
+  /// @param state [in,out] The stepping state (thread-local cache)
+  void releaseStepSize(State& state) const {
+    state.stepSize.release(ConstrainedStep::actor);
+  }
+
+  /// Output the Step Size - single component
   ///
-  /// @return Boolean statement if surface is reached by state
-  bool surfaceReached(const State& state, const Surface* surface) const {
-    return surface->isOnSurface(state.geoContext, position(state),
-                                direction(state), true);
+  /// @param state [in,out] The stepping state (thread-local cache)
+  std::string outputStepSize(const State& state) const {
+    return state.stepSize.toString();
   }
 
   /// Create and return the bound state at the current position
diff --git a/Core/include/Acts/Propagator/detail/LoopProtection.hpp b/Core/include/Acts/Propagator/detail/LoopProtection.hpp
index 58d92d1483c56589a11cc731f73377b36388a049..53bade8e34f62836a8902b0891d1e041549c95bd 100644
--- a/Core/include/Acts/Propagator/detail/LoopProtection.hpp
+++ b/Core/include/Acts/Propagator/detail/LoopProtection.hpp
@@ -9,6 +9,7 @@
 #pragma once
 
 #include "Acts/Utilities/Definitions.hpp"
+#include "Acts/Utilities/Helpers.hpp"
 
 namespace Acts {
 namespace detail {
@@ -22,20 +23,65 @@ struct LoopProtection {
   ///
   /// @param [in,out] state State object provided for the call
   /// @param [in] stepper Stepper used
-  template <typename state_t, typename stepper_t>
-  void operator()(state_t& state, const stepper_t& stepper) const {
+  template <typename propagator_state_t, typename stepper_t>
+  void operator()(propagator_state_t& state, const stepper_t& stepper) const {
     // Estimate the loop protection limit
     if (state.options.loopProtection) {
       // Get the field at the start position
       Vector3D field =
           stepper.getField(state.stepping, stepper.position(state.stepping));
-      const double p = stepper.momentum(state.stepping);
       const double B = field.norm();
-      const double helixPath = 2 * M_PI * p / B;
-      // now set the new loop limit
-      auto& pathAborter =
-          state.options.abortList.template get<path_arborter_t>();
-      pathAborter.internalLimit = state.options.loopFraction * helixPath;
+      if (B != 0.) {
+        // Transverse component at start is taken for the loop protection
+        const double p = stepper.momentum(state.stepping);
+        // Calculate the full helix path
+        const double helixPath = state.stepping.navDir * 2 * M_PI * p / B;
+        // And set it as the loop limit if it overwrites the internal limit
+        auto& pathAborter =
+            state.options.abortList.template get<path_arborter_t>();
+        double loopLimit = state.options.loopFraction * helixPath;
+        double pathLimit = pathAborter.internalLimit;
+        if (loopLimit * loopLimit < pathLimit * pathLimit) {
+          pathAborter.internalLimit = loopLimit;
+          debugLog(state, [&] {
+            std::stringstream dstream;
+            dstream << "Path aborter limit set to ";
+            dstream << loopLimit << " (full helix =  " << helixPath << ")";
+            return dstream.str();
+          });
+        }
+      }
+    }
+  }
+
+  /// The private loop protection debug logging
+  ///
+  /// It needs to be fed by a lambda function that returns a string,
+  /// that guarantees that the lambda is only called in the
+  /// state.options.debug == true
+  /// case in order not to spend time when not needed.
+  ///
+  /// @tparam propagator_state_t Type of the propagator state
+  ///
+  /// @param[in,out] state the propagator state for the debug flag,
+  ///      prefix and length
+  /// @param logAction is a callable function that returns a streamable object
+  template <typename propagator_state_t>
+  void debugLog(propagator_state_t& state,
+                const std::function<std::string()>& logAction) const {
+    if (state.options.debug) {
+      std::vector<std::string> lines;
+      std::string input = logAction();
+      boost::split(lines, input, boost::is_any_of("\n"));
+      for (const auto& line : lines) {
+        std::stringstream dstream;
+        dstream << '\n';
+        dstream << " ∞ " << std::setw(state.options.debugPfxWidth)
+                << " loop protection "
+                << " | ";
+        dstream << std::setw(state.options.debugMsgWidth) << line << '\n';
+        state.options.debugString += dstream.str();
+      }
     }
   }
 };
diff --git a/Core/include/Acts/Propagator/detail/StandardAborters.hpp b/Core/include/Acts/Propagator/detail/StandardAborters.hpp
index 93ec9c9d85378b373abe8bc18ca441612d3ba460..0bec804b675cb925d60063caeddb21ff3ec1d704 100644
--- a/Core/include/Acts/Propagator/detail/StandardAborters.hpp
+++ b/Core/include/Acts/Propagator/detail/StandardAborters.hpp
@@ -11,7 +11,7 @@
 #include <limits>
 #include <sstream>
 #include <string>
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Surfaces/BoundaryCheck.hpp"
 #include "Acts/Surfaces/Surface.hpp"
 #include "Acts/Utilities/Definitions.hpp"
@@ -80,10 +80,8 @@ struct PathLimitReached {
     if (state.navigation.targetReached) {
       return true;
     }
-
     // Check if the maximum allowed step size has to be updated
-    double limit = std::min(internalLimit, state.options.pathLimit);
-    double distance = limit - state.stepping.pathAccumulated;
+    double distance = internalLimit - state.stepping.pathAccumulated;
     double tolerance = state.options.targetTolerance;
     state.stepping.stepSize.update(distance, ConstrainedStep::aborter);
     bool limitReached = (distance * distance < tolerance * tolerance);
diff --git a/Core/include/Acts/Propagator/detail/SteppingHelper.hpp b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0356245298b3eeba07ecf32c9ed1e882f015241d
--- /dev/null
+++ b/Core/include/Acts/Propagator/detail/SteppingHelper.hpp
@@ -0,0 +1,83 @@
+// This file is part of the Acts project.
+//
+// Copyright (C) 2019 CERN for the benefit of the Acts project
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#pragma once
+
+#include "Acts/Propagator/ConstrainedStep.hpp"
+#include "Acts/Surfaces/BoundaryCheck.hpp"
+#include "Acts/Surfaces/Surface.hpp"
+#include "Acts/Utilities/Definitions.hpp"
+#include "Acts/Utilities/Intersection.hpp"
+
+namespace Acts {
+
+namespace detail {
+
+/// Update surface status - Single component
+///
+/// This method intersect the provided surface and update the navigation
+/// step estimation accordingly (hence it changes the state). It also
+/// returns the status of the intersection to trigger onSurface in case
+/// the surface is reached.
+///
+/// @param state [in,out] The stepping state (thread-local cache)
+/// @param surface [in] The surface provided
+/// @param bcheck [in] The boundary check for this status update
+template <typename stepper_t>
+Acts::Intersection::Status updateSingleSurfaceStatus(
+    const stepper_t& stepper, typename stepper_t::State& state,
+    const Surface& surface, const BoundaryCheck& bcheck) {
+  auto sIntersection =
+      surface.intersect(state.geoContext, stepper.position(state),
+                        state.navDir * stepper.direction(state), bcheck);
+
+  // The intersection is on surface already
+  if (sIntersection.intersection.status == Intersection::Status::onSurface) {
+    // Release navigation step size
+    state.stepSize.release(ConstrainedStep::actor);
+    return Intersection::Status::onSurface;
+  } else if (sIntersection.intersection or sIntersection.alternative) {
+    // Path and overstep limit checking
+    double pLimit = state.stepSize.value(ConstrainedStep::aborter);
+    double oLimit = stepper.overstepLimit(state);
+    auto checkIntersection = [&](const Intersection& intersection) -> bool {
+      double cLimit = intersection.pathLength;
+      bool accept = (cLimit > oLimit and cLimit * cLimit < pLimit * pLimit);
+      if (accept) {
+        stepper.setStepSize(state, state.navDir * cLimit);
+      }
+      return accept;
+    };
+    // If either of the two intersections are viable return reachable
+    if (checkIntersection(sIntersection.intersection) or
+        (sIntersection.alternative and
+         checkIntersection(sIntersection.alternative))) {
+      return Intersection::Status::reachable;
+    }
+  }
+  return Intersection::Status::unreachable;
+}
+
+/// Update the Step size - single component
+///
+/// It takes a (valid) object intersection from the compatibleX(...)
+/// calls in the geometry and updates the step size
+///
+/// @param state [in,out] The stepping state (thread-local cache)
+/// @param oIntersection [in] The object that yielded this step size
+/// @param release [in] A release flag
+template <typename stepper_t, typename object_intersection_t>
+void updateSingleStepSize(typename stepper_t::State& state,
+                          const object_intersection_t& oIntersection,
+                          bool release = true) {
+  double stepSize = oIntersection.intersection.pathLength;
+  state.stepSize.update(stepSize, ConstrainedStep::actor, release);
+}
+
+}  // namespace detail
+}  // namespace Acts
\ No newline at end of file
diff --git a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp
index ccaa3bd42754447da8f7d253fd992e1da4363753..f27c5bb6d7b73838045b31edc2f6e844fb285dca 100644
--- a/Core/include/Acts/Propagator/detail/SteppingLogger.hpp
+++ b/Core/include/Acts/Propagator/detail/SteppingLogger.hpp
@@ -9,7 +9,7 @@
 #pragma once
 
 #include "Acts/Geometry/GeometryID.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Utilities/Definitions.hpp"
 
 namespace Acts {
diff --git a/Plugins/DD4hep/CMakeLists.txt b/Plugins/DD4hep/CMakeLists.txt
index 6069faab1de999432fff17ae45730ad5c97f41a8..813b57e319dba5d9fcae6f559274de8e22ae837b 100644
--- a/Plugins/DD4hep/CMakeLists.txt
+++ b/Plugins/DD4hep/CMakeLists.txt
@@ -4,7 +4,8 @@ add_library(
   src/ConvertDD4hepDetector.cpp
   src/ConvertDD4hepMaterial.cpp
   src/DD4hepDetectorElement.cpp
-  src/DD4hepLayerBuilder.cpp)
+  src/DD4hepLayerBuilder.cpp
+  src/DD4hepVolumeBuilder.cpp)
 target_include_directories(
   ActsDD4hepPlugin
   PUBLIC
diff --git a/Tests/Core/Geometry/TrackingVolumeTests.cpp b/Tests/Core/Geometry/TrackingVolumeTests.cpp
index 42e37dabc3f1af1692ba67089e3c6094672e2b9e..b24a244d6f96442322b3d752ca84aa713f5ab9dd 100644
--- a/Tests/Core/Geometry/TrackingVolumeTests.cpp
+++ b/Tests/Core/Geometry/TrackingVolumeTests.cpp
@@ -26,7 +26,7 @@
 #include "Acts/Propagator/ActionList.hpp"
 #include "Acts/Propagator/StraightLineStepper.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/detail/DebugOutputActor.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Propagator/detail/SteppingLogger.hpp"
diff --git a/Tests/Core/Propagator/AbortListTests.cpp b/Tests/Core/Propagator/AbortListTests.cpp
index 1337255e9495e093c620c5d5e0471dced698fca1..4c613c4ebf7d0b041f93c0c242023630bf599950 100644
--- a/Tests/Core/Propagator/AbortListTests.cpp
+++ b/Tests/Core/Propagator/AbortListTests.cpp
@@ -20,7 +20,7 @@
 // leave blank line
 
 #include "Acts/Propagator/AbortList.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Utilities/Definitions.hpp"
 #include "Acts/Utilities/Units.hpp"
@@ -43,7 +43,6 @@ using PathLimit = detail::PathLimitReached;
 using EndOfWorld = detail::EndOfWorldReached;
 
 // the constrained step class
-using cstep = detail::ConstrainedStep;
 
 /// This is a simple cache struct to mimic the
 /// Navigator state
@@ -67,11 +66,12 @@ struct PropagatorState {
   // This is a simple cache struct to mimic the
   // Stepper cache in the propagation
   struct StepperState {
-    // accummulated path length cache
+    // Accummulated path length cache
     double pathAccumulated = 0.;
-
+    // Navigation direction
+    NavigationDirection navDir = forward;
     // adaptive sep size of the runge-kutta integration
-    cstep stepSize = std::numeric_limits<double>::max();
+    ConstrainedStep stepSize = std::numeric_limits<double>::max();
   };
 
   /// emulate the options template
@@ -84,7 +84,7 @@ struct PropagatorState {
 
     /// Debug output
     /// the string where debug messages are stored (optionally)
-    bool debug = false;
+    bool debug = true;
     std::string debugString = "";
     /// buffer & formatting for consistent output
     size_t debugPfxWidth = 30;
@@ -117,6 +117,7 @@ BOOST_AUTO_TEST_CASE(AbortListTest_PathLimit) {
   Result result;
 
   AbortList<PathLimit> abortList;
+  abortList.get<PathLimit>().internalLimit = state.options.pathLimit;
 
   // It should not abort yet
   BOOST_CHECK(!abortList(result, state, stepper));
diff --git a/Tests/Core/Propagator/ActionListTests.cpp b/Tests/Core/Propagator/ActionListTests.cpp
index c32e2c0aa0cc840e8ce838dbaf8e913fc5f22cd5..e05d8a4cd8a71c585bb5f743e37e342c0c326f58 100644
--- a/Tests/Core/Propagator/ActionListTests.cpp
+++ b/Tests/Core/Propagator/ActionListTests.cpp
@@ -38,7 +38,6 @@ namespace Acts {
 namespace Test {
 
 // the constrained step class
-using cstep = detail::ConstrainedStep;
 
 /// This is a simple cache struct to mimic the
 /// Propagator cache
@@ -50,7 +49,7 @@ struct PropagatorState {
     double pathAccumulated = 0.;
 
     // adaptive sep size of the runge-kutta integration
-    cstep stepSize = std::numeric_limits<double>::max();
+    ConstrainedStep stepSize = std::numeric_limits<double>::max();
   };
 
   /// emulate the options template
diff --git a/Tests/Core/Propagator/ConstrainedStepTests.cpp b/Tests/Core/Propagator/ConstrainedStepTests.cpp
index 0158333b547a391d4d64dba48be6f2e79be42e13..e977c7a1debac59621fbd359ea3d6061242424a4 100644
--- a/Tests/Core/Propagator/ConstrainedStepTests.cpp
+++ b/Tests/Core/Propagator/ConstrainedStepTests.cpp
@@ -19,7 +19,7 @@
 #include <boost/test/output_test_stream.hpp>
 // leave blank line
 
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 
 namespace bdata = boost::unit_test::data;
 namespace tt = boost::test_tools;
@@ -31,86 +31,84 @@ namespace Test {
 // This tests the implementation of the AbortList
 // and the standard aborters
 BOOST_AUTO_TEST_CASE(ConstrainedStepTest) {
-  using cstep = detail::ConstrainedStep;
-
   // forward stepping test
-  cstep stepSize_p = 0.25;
+  ConstrainedStep stepSize_p = 0.25;
 
   // All of the types should be 0.25 now
-  BOOST_CHECK_EQUAL(stepSize_p.values[cstep::accuracy],
+  BOOST_CHECK_EQUAL(stepSize_p.values[ConstrainedStep::accuracy],
                     std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_p.values[cstep::actor],
+  BOOST_CHECK_EQUAL(stepSize_p.values[ConstrainedStep::actor],
                     std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_p.values[cstep::aborter],
+  BOOST_CHECK_EQUAL(stepSize_p.values[ConstrainedStep::aborter],
                     std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_p.values[cstep::user], 0.25);
+  BOOST_CHECK_EQUAL(stepSize_p.values[ConstrainedStep::user], 0.25);
 
   // Check the cast operation to double
   BOOST_CHECK_EQUAL(stepSize_p, 0.25);
 
   // now we update the accuracy
-  stepSize_p.update(0.1, cstep::accuracy);
+  stepSize_p.update(0.1, ConstrainedStep::accuracy);
   BOOST_CHECK_EQUAL(stepSize_p, 0.1);
 
   // now we update the actor to smaller
-  stepSize_p.update(0.05, cstep::actor);
+  stepSize_p.update(0.05, ConstrainedStep::actor);
   BOOST_CHECK_EQUAL(stepSize_p, 0.05);
   // we increase the actor, but do not release the step size
-  stepSize_p.update(0.15, cstep::actor, false);
+  stepSize_p.update(0.15, ConstrainedStep::actor, false);
   BOOST_CHECK_EQUAL(stepSize_p, 0.05);
   // we increase the actor, but now DO release the step size
   // it falls back to the accuracy
-  stepSize_p.update(0.15, cstep::actor, true);
+  stepSize_p.update(0.15, ConstrainedStep::actor, true);
   BOOST_CHECK_EQUAL(stepSize_p, 0.1);
 
   // now set two and update them
-  stepSize_p.update(0.05, cstep::user);
-  stepSize_p.update(0.03, cstep::accuracy);
+  stepSize_p.update(0.05, ConstrainedStep::user);
+  stepSize_p.update(0.03, ConstrainedStep::accuracy);
   BOOST_CHECK_EQUAL(stepSize_p, 0.03);
 
   // now we release the accuracy - to the highest available value
-  stepSize_p.release(cstep::accuracy);
-  BOOST_CHECK_EQUAL(stepSize_p.values[cstep::accuracy],
+  stepSize_p.release(ConstrainedStep::accuracy);
+  BOOST_CHECK_EQUAL(stepSize_p.values[ConstrainedStep::accuracy],
                     std::numeric_limits<double>::max());
   BOOST_CHECK_EQUAL(stepSize_p, 0.05);
 
   // backward stepping test
-  cstep stepSize_n = -0.25;
+  ConstrainedStep stepSize_n = -0.25;
 
   // All of the types should be 0.25 now
-  BOOST_CHECK_EQUAL(stepSize_n.values[cstep::accuracy],
+  BOOST_CHECK_EQUAL(stepSize_n.values[ConstrainedStep::accuracy],
                     -std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_n.values[cstep::actor],
+  BOOST_CHECK_EQUAL(stepSize_n.values[ConstrainedStep::actor],
                     -std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_n.values[cstep::aborter],
+  BOOST_CHECK_EQUAL(stepSize_n.values[ConstrainedStep::aborter],
                     -std::numeric_limits<double>::max());
-  BOOST_CHECK_EQUAL(stepSize_n.values[cstep::user], -0.25);
+  BOOST_CHECK_EQUAL(stepSize_n.values[ConstrainedStep::user], -0.25);
 
   // Check the cast operation to double
   BOOST_CHECK_EQUAL(stepSize_n, -0.25);
 
   // now we update the accuracy
-  stepSize_n.update(-0.1, cstep::accuracy);
+  stepSize_n.update(-0.1, ConstrainedStep::accuracy);
   BOOST_CHECK_EQUAL(stepSize_n, -0.1);
 
   // now we update the actor to smaller
-  stepSize_n.update(-0.05, cstep::actor);
+  stepSize_n.update(-0.05, ConstrainedStep::actor);
   BOOST_CHECK_EQUAL(stepSize_n, -0.05);
   // we increase the actor and accuracy is smaller again, without reset
-  stepSize_n.update(-0.15, cstep::actor, false);
+  stepSize_n.update(-0.15, ConstrainedStep::actor, false);
   BOOST_CHECK_EQUAL(stepSize_n, -0.05);
   // we increase the actor and accuracy is smaller again, with reset
-  stepSize_n.update(-0.15, cstep::actor, true);
+  stepSize_n.update(-0.15, ConstrainedStep::actor, true);
   BOOST_CHECK_EQUAL(stepSize_n, -0.1);
 
   // now set two and update them
-  stepSize_n.update(-0.05, cstep::user);
-  stepSize_n.update(-0.03, cstep::accuracy);
+  stepSize_n.update(-0.05, ConstrainedStep::user);
+  stepSize_n.update(-0.03, ConstrainedStep::accuracy);
   BOOST_CHECK_EQUAL(stepSize_n, -0.03);
 
   // now we release the accuracy - to the highest available value
-  stepSize_n.release(cstep::accuracy);
-  BOOST_CHECK_EQUAL(stepSize_n.values[cstep::accuracy],
+  stepSize_n.release(ConstrainedStep::accuracy);
+  BOOST_CHECK_EQUAL(stepSize_n.values[ConstrainedStep::accuracy],
                     -std::numeric_limits<double>::max());
   BOOST_CHECK_EQUAL(stepSize_n, -0.05);
 }
diff --git a/Tests/Core/Propagator/DirectNavigatorTests.cpp b/Tests/Core/Propagator/DirectNavigatorTests.cpp
index fb2fbfd43aa8077ee602fd2871db11483e5ef2d2..71e76b2321e7635c218ee4eed66cf8d1680d68f9 100644
--- a/Tests/Core/Propagator/DirectNavigatorTests.cpp
+++ b/Tests/Core/Propagator/DirectNavigatorTests.cpp
@@ -62,10 +62,12 @@ Stepper dstepper(bField);
 ReferencePropagator rpropagator(std::move(estepper), std::move(navigator));
 DirectPropagator dpropagator(std::move(dstepper), std::move(dnavigator));
 
-const int ntests = 500;
+const int ntests = 1000;
 const int skip = 0;
 bool debugMode = false;
 bool referenceTiming = false;
+bool oversteppingTest = false;
+double oversteppingMaxStepSize = 1_mm;
 
 /// The actual test nethod that runs the test
 /// can be used with several propagator types
@@ -113,6 +115,9 @@ void runTest(const rpropagator_t& rprop, const dpropagator_t& dprop, double pT,
   using Options = PropagatorOptions<RefereceActionList, ReferenceAbortList>;
   Options pOptions(tgContext, mfContext);
   pOptions.debug = debugMode;
+  if (oversteppingTest) {
+    pOptions.maxStepSize = oversteppingMaxStepSize;
+  }
 
   // Surface collector configuration
   auto& sCollector = pOptions.actionList.template get<SurfaceCollector<>>();
@@ -200,7 +205,7 @@ BOOST_DATA_TEST_CASE(
     test_direct_navigator,
     bdata::random((bdata::seed = 20,
                    bdata::distribution =
-                       std::uniform_real_distribution<>(0.5_GeV, 10_GeV))) ^
+                       std::uniform_real_distribution<>(0.15_GeV, 10_GeV))) ^
         bdata::random((bdata::seed = 21,
                        bdata::distribution =
                            std::uniform_real_distribution<>(-M_PI, M_PI))) ^
diff --git a/Tests/Core/Propagator/LoopProtectionTests.cpp b/Tests/Core/Propagator/LoopProtectionTests.cpp
index 2d2cf9b787887bf388f6da79f9c70becd2f0285f..3cc6a23168b04a592f2e634aed7073e0f82d21e7 100644
--- a/Tests/Core/Propagator/LoopProtectionTests.cpp
+++ b/Tests/Core/Propagator/LoopProtectionTests.cpp
@@ -20,6 +20,7 @@
 #include "Acts/Propagator/Propagator.hpp"
 #include "Acts/Propagator/detail/LoopProtection.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
+#include "Acts/Propagator/detail/DebugOutputActor.hpp"
 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
 #include "Acts/Geometry/GeometryContext.hpp"
 #include "Acts/MagneticField/MagneticFieldContext.hpp"
@@ -46,6 +47,8 @@ struct SteppingState {
   Vector3D pos = Vector3D(0., 0., 0.);
   Vector3D dir = Vector3D(0., 0., 1);
   double p = 100_MeV;
+
+  NavigationDirection navDir;
 };
 
 /// @brief mockup of stepping state
@@ -87,6 +90,11 @@ struct Options {
   bool loopProtection = true;
   double loopFraction = 0.5;
 
+  bool debug = false;
+  std::string debugString;
+  int debugMsgWidth = 60;
+  int debugPfxWidth = 30;
+
   /// Contains: target aborters
   AbortList<PathLimitReached> abortList;
 };
@@ -183,10 +191,20 @@ BOOST_DATA_TEST_CASE(
   Vector3D mom(px, py, pz);
   CurvilinearParameters start(std::nullopt, pos, mom, q, 42.);
 
-  PropagatorOptions<> options(tgContext, mfContext);
+  using DebugOutput = Acts::detail::DebugOutputActor;
+  using ProopagatorOptions =
+      PropagatorOptions<ActionList<DebugOutput>, AbortList<>>;
+  ProopagatorOptions options(tgContext, mfContext);
+  options.debug = false;
   options.maxSteps = 1e6;
   const auto& result = epropagator.propagate(start, options).value();
 
+  if (options.debug) {
+    const auto debugString =
+        result.template get<DebugOutput::result_type>().debugString;
+    std::cout << debugString << std::endl;
+  }
+
   // this test assumes state.options.loopFraction = 0.5
   CHECK_CLOSE_REL(px, -result.endParameters->momentum().x(), 1e-2);
   CHECK_CLOSE_REL(py, -result.endParameters->momentum().y(), 1e-2);
diff --git a/Tests/Core/Propagator/NavigatorTests.cpp b/Tests/Core/Propagator/NavigatorTests.cpp
index b8ae317f7384144dafa92315a4675d3762479b9d..51d428ecf6c0d466d6ed33440d632c7a2220a75a 100644
--- a/Tests/Core/Propagator/NavigatorTests.cpp
+++ b/Tests/Core/Propagator/NavigatorTests.cpp
@@ -20,7 +20,8 @@
 #include "Acts/Propagator/Navigator.hpp"
 #include "Acts/Propagator/StraightLineStepper.hpp"
 #include "Acts/Propagator/StepperConcept.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
+#include "Acts/Propagator/detail/SteppingHelper.hpp"
 #include "Acts/Surfaces/CylinderSurface.hpp"
 #include "Acts/Tests/CommonHelpers/CylindricalTrackingGeometry.hpp"
 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
@@ -81,7 +82,15 @@ struct PropagatorState {
       double pathAccumulated = 0.;
 
       // adaptive sep size of the runge-kutta integration
-      Cstep stepSize = Cstep(100_cm);
+      ConstrainedStep stepSize = ConstrainedStep(100_cm);
+
+      // Previous step size for overstep estimation (ignored here)
+      double previousStepSize = 0.;
+
+      /// The tolerance for the stepping
+      double tolerance = s_onSurfaceTolerance;
+
+      GeometryContext geoContext = GeometryContext();
     };
 
     /// Global particle position accessor
@@ -104,9 +113,33 @@ struct PropagatorState {
       return s_onSurfaceTolerance;
     }
 
-    bool surfaceReached(const State& state, const Surface* surface) const {
-      return surface->isOnSurface(tgContext, position(state), direction(state),
-                                  true);
+    Intersection::Status updateSurfaceStatus(
+        State& state, const Surface& surface,
+        const BoundaryCheck& bcheck) const {
+      return detail::updateSingleSurfaceStatus<Stepper>(*this, state, surface,
+                                                        bcheck);
+    }
+
+    template <typename object_intersection_t>
+    void updateStepSize(State& state,
+                        const object_intersection_t& oIntersection,
+                        bool release = true) const {
+      detail::updateSingleStepSize<Stepper>(state, oIntersection, release);
+    }
+
+    void setStepSize(
+        State& state, double stepSize,
+        ConstrainedStep::Type stype = ConstrainedStep::actor) const {
+      state.previousStepSize = state.stepSize;
+      state.stepSize.update(stepSize, stype, true);
+    }
+
+    void releaseStepSize(State& state) const {
+      state.stepSize.release(ConstrainedStep::actor);
+    }
+
+    std::string outputStepSize(const State& state) const {
+      return state.stepSize.toString();
     }
 
     BoundState boundState(State& state, const Surface& surface,
diff --git a/Tests/Core/Propagator/PropagatorTests.cpp b/Tests/Core/Propagator/PropagatorTests.cpp
index 5c5f18945f4316a3f33959184bfc04bc78f56195..1a408bca856bf3173644b0b0a11d9d220332f403 100644
--- a/Tests/Core/Propagator/PropagatorTests.cpp
+++ b/Tests/Core/Propagator/PropagatorTests.cpp
@@ -19,7 +19,7 @@
 #include "Acts/Propagator/ActionList.hpp"
 #include "Acts/Propagator/EigenStepper.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Surfaces/CylinderSurface.hpp"
 #include "Acts/Tests/CommonHelpers/FloatComparisons.hpp"
@@ -40,7 +40,6 @@ namespace Test {
 GeometryContext tgContext = GeometryContext();
 MagneticFieldContext mfContext = MagneticFieldContext();
 
-using cstep = detail::ConstrainedStep;
 using Covariance = BoundSymMatrix;
 
 /// An observer that measures the perpendicular distance
@@ -95,13 +94,13 @@ struct SurfaceObserver {
                                      stepper.direction(state.stepping), true)
               .pathLength;
       // Adjust the step size so that we cannot cross the target surface
-      state.stepping.stepSize.update(distance, cstep::actor);
+      state.stepping.stepSize.update(distance, ConstrainedStep::actor);
       // return true if you fall below tolerance
       if (std::abs(distance) <= tolerance) {
         ++result.surfaces_passed;
         result.surface_passed_r = perp(stepper.position(state.stepping));
         // release the step size, will be re-adjusted
-        state.stepping.stepSize.release(cstep::actor);
+        state.stepping.stepSize.release(ConstrainedStep::actor);
       }
     }
   }
diff --git a/Tests/Core/Propagator/StepperTests.cpp b/Tests/Core/Propagator/StepperTests.cpp
index 86a9e77cfab663da15114eedaf999d0952049641..1331eb1a06216edf0247167a033edf8578b81276 100644
--- a/Tests/Core/Propagator/StepperTests.cpp
+++ b/Tests/Core/Propagator/StepperTests.cpp
@@ -42,7 +42,6 @@ using namespace Acts::UnitLiterals;
 namespace Acts {
 namespace Test {
 
-using cstep = detail::ConstrainedStep;
 using Covariance = BoundSymMatrix;
 
 // Create a test context
diff --git a/Tests/Integration/BVHNavigationTest.cpp b/Tests/Integration/BVHNavigationTest.cpp
index 1e2f82b61a9c314c321b057b1a00e971de1400db..65097395725f15c9ac74ffc04dd3f2ffc29678bd 100644
--- a/Tests/Integration/BVHNavigationTest.cpp
+++ b/Tests/Integration/BVHNavigationTest.cpp
@@ -35,7 +35,7 @@
 #include "Acts/Propagator/ActionList.hpp"
 #include "Acts/Propagator/StraightLineStepper.hpp"
 #include "Acts/Propagator/Propagator.hpp"
-#include "Acts/Propagator/detail/ConstrainedStep.hpp"
+#include "Acts/Propagator/ConstrainedStep.hpp"
 #include "Acts/Propagator/detail/DebugOutputActor.hpp"
 #include "Acts/Propagator/detail/StandardAborters.hpp"
 #include "Acts/Propagator/detail/SteppingLogger.hpp"
diff --git a/Tests/Integration/PropagationTestBase.hpp b/Tests/Integration/PropagationTestBase.hpp
index 56034286855291cec81a5d3ee35ccdc011383284..98158db9b041950e4949dda2f496142b13617ae6 100644
--- a/Tests/Integration/PropagationTestBase.hpp
+++ b/Tests/Integration/PropagationTestBase.hpp
@@ -22,6 +22,8 @@
 
 using namespace Acts::UnitLiterals;
 
+unsigned int itest = 0;
+
 // datasets for Boost::Test data-driven test cases
 namespace ds {
 // track parameters
@@ -50,6 +52,9 @@ auto threeRandom = (rand1 ^ rand2 ^ rand2);
 BOOST_DATA_TEST_CASE(forward_backward_propagation_,
                      ds::trackParameters* ds::propagationLimit, pT, phi, theta,
                      charge, plimit) {
+  std::cout << "Running (first patch) tests : " << itest << std::endl;
+  ++itest;
+
   // foward backward check atlas stepper
   foward_backward(apropagator, pT, phi, theta, charge, plimit, 1_um, 1_eV,
                   debug);
diff --git a/Tests/Integration/PropagationTestHelper.hpp b/Tests/Integration/PropagationTestHelper.hpp
index c764c27d29d0878c88aafaabce1caa5fb2eae7d5..29d5c08f1fc982000ec2a4b72be3cd4c954ae2b6 100644
--- a/Tests/Integration/PropagationTestHelper.hpp
+++ b/Tests/Integration/PropagationTestHelper.hpp
@@ -224,7 +224,7 @@ void foward_backward(const Propagator_type& propagator, double pT, double phi,
     std::cout << " - resulted at position : "
               << fwdResult.endParameters->position() << std::endl;
 
-    auto bwdOutput = fwdResult.template get<DebugOutput::result_type>();
+    auto bwdOutput = bwdResult.template get<DebugOutput::result_type>();
     std::cout << ">>>>> Output for backward propagation " << std::endl;
     std::cout << bwdOutput.debugString << std::endl;
     std::cout << " - resulted at position : "