diff --git a/include/hpp/manipulation/path-planner/states-path-finder.hh b/include/hpp/manipulation/path-planner/states-path-finder.hh
index 560c595b0500501a4818467a5ad2ceedd8c4ab55..269b5675e5003a3b73c07fddf4b7efa6d4e55737 100644
--- a/include/hpp/manipulation/path-planner/states-path-finder.hh
+++ b/include/hpp/manipulation/path-planner/states-path-finder.hh
@@ -150,6 +150,16 @@ namespace hpp {
           SolveStepStatus solveStep(std::size_t wp);
           Configuration_t configSolved (std::size_t wp) const;
 
+          /// use inner state planner to plan path between two configurations.
+          /// these configs lie on same leaf (same RHS wrt edge constraints)
+          /// eg consecutive configs in the solved config list
+          /// \param q1 start configuration
+          /// \param q2 destination configuration
+          /// \param edge the edge connecting the two configurations
+          /// \throw core::path_planning_failed if fail to plan path
+          void planInState(const ConfigurationPtr_t& q1,
+              const ConfigurationPtr_t& q2, const graph::EdgePtr_t& edge);
+
           /// deletes from memory the latest working states list, which is used to
           /// resume finding solutions from that list in case of failure at a
           /// later step.
@@ -157,7 +167,8 @@ namespace hpp {
 
           virtual void startSolve();
           virtual void oneStep();
-          /// Do nothing
+          /// when both initial and goal configurations are given,
+          /// and they are in the same state, try connecting them directly
           virtual void tryConnectInitAndGoals ();
 
         protected:
diff --git a/src/path-planner/states-path-finder.cc b/src/path-planner/states-path-finder.cc
index 172772eafe42a0e6620439a9e6c224437a1af83e..9caea0823c6c21fbfec9cad32d7371e53eb96884 100644
--- a/src/path-planner/states-path-finder.cc
+++ b/src/path-planner/states-path-finder.cc
@@ -1029,7 +1029,13 @@ namespace hpp {
             }
           }
         }
-
+        // at this point jcmap contains all the constraints that
+        // - depend on relative pose between 2 joints j1 and j2,
+        // - are present in the solver of any waypoint wp_i
+        // - j1 and j2 are constrained by all the transitions from q_init to wp_i
+        //
+        // in the following we test that q_init satisfies the above constraints
+        // otherwise the list of transitions is discarded
         if (jcmap.size() == 0) {
           return true;
         }
@@ -1437,6 +1443,38 @@ namespace hpp {
         reset();
       }
 
+      void StatesPathFinder::planInState(const ConfigurationPtr_t& q1,
+          const ConfigurationPtr_t& q2, const graph::EdgePtr_t& edge) {
+        // Copy edge constraints
+        core::ConstraintSetPtr_t constraints(HPP_DYNAMIC_PTR_CAST(
+            core::ConstraintSet, edge->pathConstraint()->copy()));
+        // Initialize right hand side
+        constraints->configProjector()->rightHandSideFromConfig(*q1);
+        assert(constraints->isSatisfied(*q2));
+        inStateProblem_->constraints(constraints);
+        inStateProblem_->pathValidation(edge->pathValidation());
+        inStateProblem_->initConfig(q1);
+        inStateProblem_->resetGoalConfigs();
+        inStateProblem_->addGoalConfig(q2);
+
+        core::PathPlannerPtr_t inStatePlanner
+          (core::DiffusingPlanner::create(inStateProblem_));
+        core::PathOptimizerPtr_t inStateOptimizer
+          (core::pathOptimization::RandomShortcut::create(inStateProblem_));
+        inStatePlanner->maxIterations(problem_->getParameter
+            ("StatesPathFinder/innerPlannerMaxIterations").intValue());
+        inStatePlanner->timeOut(problem_->getParameter
+            ("StatesPathFinder/innerPlannerTimeOut").floatValue());
+
+        core::PathVectorPtr_t path = inStatePlanner->solve();
+        for (std::size_t r = 0; r < path->numberPaths()-1; r++)
+          assert(path->pathAtRank(r)->end() ==
+                  path->pathAtRank(r+1)->initial());
+        roadmap()->merge(inStatePlanner->roadmap());
+        // core::PathVectorPtr_t opt = inStateOptimizer->optimize(path);
+        // roadmap()->insertPathVector(opt, true);
+      }
+
       void StatesPathFinder::oneStep ()
       {
         if (idxConfigList_ == 0) {
@@ -1457,45 +1495,18 @@ namespace hpp {
           q2 = ConfigurationPtr_t(new Configuration_t(configSolved
                                                       (idxConfigList_+1)));
           const graph::EdgePtr_t& edge(transitions[idxConfigList_]);
-          // Copy edge constraints
-          core::ConstraintSetPtr_t constraints(HPP_DYNAMIC_PTR_CAST(
-            core::ConstraintSet, edge->pathConstraint()->copy()));
-          // Initialize right hand side
-          constraints->configProjector()->rightHandSideFromConfig(*q1);
-          assert(constraints->isSatisfied(*q2)); 
-          inStateProblem_->constraints(constraints);
-          inStateProblem_->pathValidation(edge->pathValidation());
-          inStateProblem_->initConfig(q1);
-          inStateProblem_->resetGoalConfigs();
-          inStateProblem_->addGoalConfig(q2);
-
-          core::PathPlannerPtr_t inStatePlanner
-            (core::DiffusingPlanner::create(inStateProblem_));
-          core::PathOptimizerPtr_t inStateOptimizer
-            (core::pathOptimization::RandomShortcut::create(inStateProblem_));
-          inStatePlanner->maxIterations(problem_->getParameter
-              ("StatesPathFinder/innerPlannerMaxIterations").intValue());
-          inStatePlanner->timeOut(problem_->getParameter
-              ("StatesPathFinder/innerPlannerTimeOut").floatValue());
           hppDout(info, "calling InStatePlanner_.solve for transition "
                   << idxConfigList_);
-
-          core::PathVectorPtr_t path = inStatePlanner->solve();
-          for (std::size_t r = 0; r < path->numberPaths()-1; r++)
-            assert(path->pathAtRank(r)->end() ==
-                   path->pathAtRank(r+1)->initial());
-          roadmap()->merge(inStatePlanner->roadmap());
-          // core::PathVectorPtr_t opt = inStateOptimizer->optimize(path);
-          // roadmap()->insertPathVector(opt, true);
+          planInState(q1, q2, edge);
           idxConfigList_++;
           if (idxConfigList_ == configList_.size()-1) {
             hppDout(warning, "Solution " << idxSol_ << ": Success"
                     << "\n-----------------------------------------------");
           }
 
-        } catch(const core::path_planning_failed&(e)) {
+        } catch(const core::path_planning_failed& error) {
           std::ostringstream oss;
-          oss << "Error " << e.what() << "\n";
+          oss << "Error " << error.what() << "\n";
           oss << "Solution " << idxSol_ << ": Failed to build path at edge " << idxConfigList_ << ": ";
           oss << lastBuiltTransitions_[idxConfigList_]->name();
           hppDout(warning, oss.str());
@@ -1512,9 +1523,45 @@ namespace hpp {
         }
       }
 
-       void StatesPathFinder::tryConnectInitAndGoals()
-       {
-       }
+      void StatesPathFinder::tryConnectInitAndGoals()
+      {
+        GraphSearchData& d = *graphData_;
+        if (goalDefinedByConstraints_ || d.s1 != d.s2[0]) return;
+
+        // get the loop edge connecting the initial state to itself
+        const graph::Edges_t& loopEdges(problem_->constraintGraph()
+            ->getEdges(d.s1, d.s1));
+        // check that there is 1 loop edge
+        assert (loopEdges.size() == 1);
+        const graph::EdgePtr_t& edge(loopEdges[0]);
+        core::ConstraintSetPtr_t constraints(HPP_DYNAMIC_PTR_CAST(
+          core::ConstraintSet, edge->pathConstraint()->copy()));
+        // Initialize right hand side
+        constraints->configProjector()->rightHandSideFromConfig(*q1_);
+        // check that initial and final config are in the same leaf
+        if (!constraints->isSatisfied(*q2_)) {
+          hppDout(info, "Initial config and goal config are in the same "
+            "state \"" << d.s1->name() << "\". "
+            "But q2 does NOT satisfy edge constraints RHS initialized from q1. "
+            "They are likely not on the same leaf. Will not try to connect.");
+          return;
+        }
+        hppDout(info, "Initial config and goal config are in the same "
+            "state \"" << d.s1->name() << "\". "
+            "q2 satisfies edge constraints with RHS initialized from q1. "
+            "Calling InStatePlanner_.solve to connect them directly");
+        try {
+          planInState(q1_, q2_, edge);
+          hppDout(warning, "Connect init and goal directly: Success"
+              << "\n-----------------------------------------------");
+
+        } catch(const core::path_planning_failed& error) {
+          std::ostringstream oss;
+          oss << "Error " << error.what() << "\n";
+          oss << "Failed to connect init and goal config directly";
+          hppDout(warning, oss.str());
+        }
+      }
 
       std::vector<std::string> StatesPathFinder::constraintNamesFromSolverAtWaypoint
         (std::size_t wp)