diff --git a/Trigger/TrigSteer/DecisionHandling/CMakeLists.txt b/Trigger/TrigSteer/DecisionHandling/CMakeLists.txt
index 6defffecb79bf2253253723dddf2c804c6ef6477..dc1ea78ccad2882d4def4149f3f34689e96fe6eb 100644
--- a/Trigger/TrigSteer/DecisionHandling/CMakeLists.txt
+++ b/Trigger/TrigSteer/DecisionHandling/CMakeLists.txt
@@ -9,7 +9,7 @@ atlas_add_library( DecisionHandlingLib
                    src/DumpDecisions.cxx
                    src/HypoBase.cxx
                    src/InputMakerBase.cxx
-		   src/ITestHypoTool.cxx		   
+		           src/ITestHypoTool.cxx
                    PUBLIC_HEADERS DecisionHandling
                    LINK_LIBRARIES AthenaBaseComps AthenaMonitoringKernelLib GaudiKernel StoreGateLib TrigCompositeUtilsLib TrigCostMonitorMTLib TrigSteeringEvent TrigTimeAlgsLib
                    PRIVATE_LINK_LIBRARIES AthContainers AthViews xAODTrigger )
@@ -22,10 +22,11 @@ atlas_add_component( DecisionHandling
                      src/TriggerSummaryAlg.cxx
                      src/RoRSeqFilter.cxx
                      src/ViewCreator*.cxx
-		     src/TestRecoAlg.cxx
-		     src/TestInputMaker.cxx
-		     src/TestHypoTool.cxx
-		     src/TestHypoAlg.cxx
+                     src/TestRecoAlg.cxx
+                     src/TestInputMaker.cxx
+                     src/TestHypoTool.cxx
+                     src/TestHypoAlg.cxx
+                     src/PassFilter.cxx
                      LINK_LIBRARIES DecisionHandlingLib xAODTrigCalo AthViews xAODTracking xAODJet )
 
 atlas_install_python_modules( python/*.py POST_BUILD_CMD ${ATLAS_FLAKE8} )
diff --git a/Trigger/TrigSteer/DecisionHandling/python/DecisionHandlingConfig.py b/Trigger/TrigSteer/DecisionHandling/python/DecisionHandlingConfig.py
index 8fa029189db31349f76bb2bf19064ee65fd8d70e..fe55b17b79d69986da8bb5e64ecbd09e41418d63 100644
--- a/Trigger/TrigSteer/DecisionHandling/python/DecisionHandlingConfig.py
+++ b/Trigger/TrigSteer/DecisionHandling/python/DecisionHandlingConfig.py
@@ -3,6 +3,8 @@
 # 
 
 def setupFilterMonitoring( filterAlg ):    
+    if not hasattr(filterAlg, "Input"):
+        return
     from AthenaMonitoringKernel.GenericMonitoringTool import GenericMonitoringTool
     monTool = GenericMonitoringTool('MonTool')
     
diff --git a/Trigger/TrigSteer/DecisionHandling/src/PassFilter.cxx b/Trigger/TrigSteer/DecisionHandling/src/PassFilter.cxx
new file mode 100644
index 0000000000000000000000000000000000000000..6b89d26bb40a1f1098e4e45b655753197a4576a2
--- /dev/null
+++ b/Trigger/TrigSteer/DecisionHandling/src/PassFilter.cxx
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+
+#include "PassFilter.h"
+
+PassFilter::PassFilter(const std::string& name, ISvcLocator* pSvcLocator) :
+  AthReentrantAlgorithm(name, pSvcLocator)
+{
+}
+
+PassFilter::~PassFilter()
+{
+}
+
+StatusCode PassFilter::initialize()
+{
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PassFilter::finalize()
+{
+  return StatusCode::SUCCESS;
+}
+
+StatusCode PassFilter::execute(const EventContext&) const
+{
+  return StatusCode::SUCCESS;
+}
+
diff --git a/Trigger/TrigSteer/DecisionHandling/src/PassFilter.h b/Trigger/TrigSteer/DecisionHandling/src/PassFilter.h
new file mode 100644
index 0000000000000000000000000000000000000000..f6ea5c2f29a2877bdda08079203b8c41b12aae03
--- /dev/null
+++ b/Trigger/TrigSteer/DecisionHandling/src/PassFilter.h
@@ -0,0 +1,30 @@
+/*
+  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
+*/
+#ifndef DECISIONHANDLING_PASSFILTER_H
+#define DECISIONHANDLING_PASSFILTER_H
+
+// Framework includes
+#include "AthenaBaseComps/AthReentrantAlgorithm.h"
+
+// STL includes
+#include <string>
+
+/**
+ * @class PassFilter
+ * @brief Filter algorithm that always pass, needed to fill gaps in HLT CF
+ **/
+class PassFilter : public AthReentrantAlgorithm {
+public:
+  PassFilter(const std::string& name, ISvcLocator* pSvcLocator);
+  virtual ~PassFilter() override;
+
+  virtual StatusCode initialize() override;
+  virtual StatusCode execute(const EventContext& context) const override;
+  virtual StatusCode finalize() override;
+
+private:
+
+};
+
+#endif // DECISIONHANDLING_PASSFILTER_H
diff --git a/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.cxx b/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.cxx
index 359c1146d84abecdd6e94cbf0dcc38799e19c6d9..2876e30e74e221bb24cf7f241c15dcabcfb64f6a 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.cxx
@@ -27,25 +27,58 @@ StatusCode RoRSeqFilter::initialize()
   CHECK( not m_inputKeys.empty() );
   CHECK( not m_outputKeys.empty() );
 
-  if ( m_mergeInputs ) {
-    CHECK( m_inputKeys.size() > 1 );
-    CHECK( m_outputKeys.size() == 1 );    
-  } else {
-    CHECK( m_inputKeys.size() == m_outputKeys.size() );
-  }
 
   CHECK( m_inputKeys.initialize() );
   CHECK( m_outputKeys.initialize() );
 
   renounceArray(m_inputKeys);
 
+  // default IO mappnig nth input -> nth output
+  if ( m_ioMappingProperty.empty()) {
+    CHECK( m_inputKeys.size() == m_outputKeys.size() );
+    for ( unsigned i = 0; i < m_inputKeys.size(); ++i ) {
+      m_ioMapping.push_back( {i} );
+    }
+    ATH_MSG_DEBUG( "Default I/O collections mapping nth input -> nth output" );
+  } else {
+    m_ioMapping = m_ioMappingProperty;
+    ATH_MSG_DEBUG( "Custom I/O mapping used by the filter" );
+  }
+
+  for ( size_t i = 0; i < m_ioMapping.size(); ++i ) {
+    for ( unsigned inIndex: m_ioMapping[i] ) {
+      ATH_MSG_INFO("Input collection: " << m_inputKeys[inIndex] );
+    }
+    ATH_MSG_INFO("   Routed to output collection " << m_outputKeys[i] );
+  }
+
+  // crosscheck mapping (also the default one)
+  CHECK( m_ioMapping.size() == m_outputKeys.size() ); // all outputs will be filled
+  std::set<int> inIndices;
+  for ( auto i: m_ioMapping ) {
+    CHECK( not i.empty() ); // all outputs are connected to at least one input
+    inIndices.insert( i.begin(), i.end() );
+  }
+  CHECK( inIndices.size() == m_inputKeys.size() ); // all inputs are routed
+  for ( size_t i = 0; i < m_ioMapping.size(); ++i ) {
+    auto iset = std::set( m_ioMapping[i].begin(), m_ioMapping[i].end() );
+    for ( size_t j = i+1; j < m_ioMapping.size(); ++j ) {
+      for ( auto el: m_ioMapping[j] ) {
+        if ( iset.count( el ) != 0 ) {
+          ATH_MSG_ERROR( "The input index " << el << " directed to two outputs " << m_ioMapping[i] << " " << m_ioMapping[j] );
+          return StatusCode::FAILURE;
+        }
+      }
+    }
+  }
+
   if (msgLvl(MSG::DEBUG)){
     ATH_MSG_DEBUG("Will consume implicit ReadDH:" );
     for (auto& input: m_inputKeys){  
       ATH_MSG_DEBUG(" - "<<input.key());
     }
     ATH_MSG_DEBUG("Will produce WriteDH: ");
-    for (auto& output: m_outputKeys){  
+    for (auto& output: m_outputKeys){
       ATH_MSG_DEBUG(" - "<<output.key());
     }
   }
@@ -55,12 +88,16 @@ StatusCode RoRSeqFilter::initialize()
   for ( const std::string& el: m_chainsProperty ) 
     m_chains.insert( HLT::Identifier( el ).numeric() );
 
+  m_chainsPerInput.resize( m_chainsPerInputProperty.size() );
+  for ( size_t i = 0; i < m_chainsPerInputProperty.size(); ++i ) {
+    for ( const std::string& el: m_chainsPerInputProperty[i] )
+        m_chainsPerInput[i].insert( HLT::Identifier( el ).numeric() );
+  }
+
   if (msgLvl(MSG::DEBUG)){
     ATH_MSG_DEBUG( "Configured to require these chains: ");
     for ( const HLT::Identifier& id: m_chains )
       ATH_MSG_DEBUG( " - " << id );
-    
-    ATH_MSG_DEBUG( "mergeInputs is " << m_mergeInputs);
   }
   
   if ( not m_monTool.name().empty() ) {
@@ -71,28 +108,28 @@ StatusCode RoRSeqFilter::initialize()
 }
 
 
-StatusCode RoRSeqFilter::execute(const EventContext& ctx) const {
+StatusCode RoRSeqFilter::execute( const EventContext& ctx ) const {
   ATH_MSG_DEBUG ( "Executing " << name() << "..." );
-  auto inputHandles  = m_inputKeys.makeHandles(ctx);
-  auto outputHandles = m_outputKeys.makeHandles(ctx);
+  auto inputHandles  = m_inputKeys.makeHandles( ctx );
+  auto outputHandles = m_outputKeys.makeHandles( ctx );
 
-  std::vector<std::string> inputNames({"exec", "anyvalid"});
+  std::vector<std::string> inputNames( {"exec", "anyvalid"} );
   std::vector<bool> inputStats({true, false}); // position 0 for number of execs, always true, bool at position 1 is set later
   inputNames.reserve(inputHandles.size() + 2);
   inputStats.reserve(inputHandles.size() + 2);
   bool validInputs = false;
   for ( auto& inputHandle: inputHandles ) {
-    inputNames.push_back(inputHandle.name());
+    inputNames.push_back( inputHandle.name() );
     if( inputHandle.isValid() ) {// this is because input is implicit
       validInputs = true;
-      inputStats.push_back(true);
+      inputStats.push_back( true );
     } else {
-      inputStats.push_back(false);
+      inputStats.push_back( false );
     }
   }
   inputStats[1] = validInputs; // position 1 for number of execes with any collection valid
-  auto inputName = Monitored::Collection<std::vector<std::string>>("name", inputNames );
-  auto inputStat = Monitored::Collection<std::vector<bool>>("stat", inputStats );
+  auto inputName = Monitored::Collection<std::vector<std::string>>( "name", inputNames );
+  auto inputStat = Monitored::Collection<std::vector<bool>>( "stat", inputStats );
   Monitored::Group( m_monTool, inputStat, inputName );
   
   if (!validInputs) {
@@ -101,46 +138,43 @@ StatusCode RoRSeqFilter::execute(const EventContext& ctx) const {
     return StatusCode::SUCCESS;
   }
 
-  ATH_MSG_DEBUG( "Running on "<< inputHandles.size() <<" input keys");
+  ATH_MSG_DEBUG ( "Running on "<< inputHandles.size() <<" input keys" );
   
   size_t passCounter = 0;
-  size_t outputIndex = 0;
-  if ( m_mergeInputs ) {
-
-    ATH_MSG_DEBUG( "Recording " <<  m_outputKeys[ 0 ].key() ); 
-    createAndStore(outputHandles[0]);
-    DecisionContainer* output = outputHandles[0].ptr();
-    for ( auto& inputHandle: inputHandles) {
-      if( inputHandle.isValid() ) {
-        passCounter += copyPassing( *inputHandle,  *output );
-      }
-    }
-    outputIndex++;
-
-  } else { // Not merging inputs
-
-    for ( auto& inputHandle: inputHandles ) {
 
-      if( not inputHandle.isValid() ) {
-        ATH_MSG_DEBUG( "InputHandle "<< inputHandle.key() <<" not present" );
-      } else if ( inputHandle->empty() ) {
-        ATH_MSG_DEBUG( "InputHandle "<< inputHandle.key() <<" contains all rejected decisions, skipping" );
+  for ( size_t outputIndex = 0; outputIndex < m_ioMapping.size(); ++outputIndex ) {
+    bool anyPresent =  false;
+    for ( unsigned inputIndex : m_ioMapping[outputIndex] ) {
+      if( not inputHandles[inputIndex].isValid() ) {
+        ATH_MSG_DEBUG( "InputHandle "<< inputHandles[inputIndex].key() <<" not present" );
+      } else if ( inputHandles[inputIndex]->empty() ) {
+        ATH_MSG_DEBUG( "InputHandle "<< inputHandles[inputIndex].key() <<" contains all rejected decisions, skipping" );
       } else {
-        ATH_MSG_DEBUG( "Checking inputHandle: "<< inputHandle.key() <<" has " << inputHandle->size() <<" elements");
-        createAndStore(outputHandles[outputIndex]);
-        DecisionContainer* output = outputHandles[outputIndex].ptr();
-        passCounter += copyPassing( *inputHandle, *output );  
-        ATH_MSG_DEBUG( "Recorded output key " <<  m_outputKeys[ outputIndex ].key() <<" of size "<<output->size()  <<" at index "<< outputIndex);
+        anyPresent = true;
+      }
+    }
+    if ( anyPresent ) {
+      createAndStore(outputHandles[outputIndex]);
+      DecisionContainer* output = outputHandles[outputIndex].ptr();
+      for ( auto inputIndex : m_ioMapping[outputIndex] ) {
+        if ( inputHandles[inputIndex].isValid() and not inputHandles[inputIndex]->empty() ) {
+          ATH_MSG_DEBUG( "Checking inputHandle: "<< inputHandles[inputIndex].key() <<" has " << inputHandles[inputIndex]->size() <<" elements");
+          if ( not m_chainsPerInput.empty() ) {
+            passCounter += copyPassing( *inputHandles[inputIndex], *output, m_chainsPerInput[inputIndex] );
+          } else {
+            passCounter += copyPassing( *inputHandles[inputIndex], *output, m_chains );
+          }
+          ATH_MSG_DEBUG( "Recorded output key " <<  m_outputKeys[ outputIndex ].key() <<" of size "<<output->size()  <<" at index "<< outputIndex);
+        }
       }
-      outputIndex++; // Keep the mapping of inputKey<->outputKey correct
     }
   }
 
   setFilterPassed( passCounter != 0, ctx );
-  ATH_MSG_DEBUG( "Filter " << ( filterPassed(ctx) ? "passed" : "rejected") <<"; creating "<< outputIndex<<" valid outDecisions DH:");
-  if (msgLvl(MSG::DEBUG)){
-    for (auto& output: outputHandles){
-      if( output.isValid() ) ATH_MSG_DEBUG(" "<<output.key());
+  ATH_MSG_DEBUG( "Filter " << ( filterPassed(ctx) ? "passed" : "rejected" ) );
+  if ( msgLvl( MSG::DEBUG ) ){
+    for ( auto& output: outputHandles ) {
+      if( output.isValid() ) ATH_MSG_DEBUG( " "<<output.key() );
     }
   }
 
@@ -148,8 +182,8 @@ StatusCode RoRSeqFilter::execute(const EventContext& ctx) const {
   return StatusCode::SUCCESS;
 }
   
-size_t RoRSeqFilter::copyPassing( const DecisionContainer& input, 
-                                  DecisionContainer& output ) const {
+size_t RoRSeqFilter::copyPassing( const DecisionContainer& input,
+                                  DecisionContainer& output, const std::set<HLT::Identifier>& topass ) const {
   size_t passCounter = 0;
   for (size_t i = 0; i < input.size(); ++i) {
     const Decision* inputDecision = input.at(i);
@@ -160,7 +194,7 @@ size_t RoRSeqFilter::copyPassing( const DecisionContainer& input,
     ATH_MSG_DEBUG("Number of positive decisions for this input is " << objDecisions.size() <<". Now Filtering...." );
 
     DecisionIDContainer intersection;
-    std::set_intersection( m_chains.begin(), m_chains.end(),
+    std::set_intersection( topass.begin(), topass.end(),
          objDecisions.begin(), objDecisions.end(),
          std::inserter(intersection, intersection.end()) );
 
@@ -175,9 +209,9 @@ size_t RoRSeqFilter::copyPassing( const DecisionContainer& input,
       passCounter ++;
       ATH_MSG_DEBUG("Input satisfied at least one filtering chain. Chain(s) passing:");
       if (msgLvl(MSG::DEBUG)){
-	for ( DecisionID id : intersection ) {
-	  ATH_MSG_DEBUG( " -- " << HLT::Identifier( id ) );
-	}
+      	for ( DecisionID id : intersection ) {
+	       ATH_MSG_DEBUG( " -- " << HLT::Identifier( id ) );
+	      }
       }
       
     } else {
diff --git a/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.h b/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.h
index ac24edb149317fb1dd4986f82cfd09575b60de35..6fc374e0b55e2de76cea35403811d2bca7f87b73 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.h
+++ b/Trigger/TrigSteer/DecisionHandling/src/RoRSeqFilter.h
@@ -7,6 +7,7 @@
 #include <string>
 #include <set>
 
+#include "Gaudi/Parsers/Factory.h"
 #include "AthenaBaseComps/AthReentrantAlgorithm.h"
 #include "AthContainers/ConstDataVector.h"
 #include "TrigCompositeUtils/TrigCompositeUtils.h"
@@ -66,9 +67,18 @@ class RoRSeqFilter
   SG::WriteHandleKeyArray<TrigCompositeUtils::DecisionContainer> m_outputKeys{ this, "Output", {}, "Output" };
 
   Gaudi::Property<std::vector<std::string> > m_chainsProperty{ this, "Chains", {}, "Chains of which this filter is concerned" };
-  Gaudi::Property<bool> m_mergeInputs{ this, "MergeInputs", false, "Produce one output" };
   std::set<HLT::Identifier> m_chains;
 
+  Gaudi::Property<std::vector <std::vector<std::string>> > m_chainsPerInputProperty{ this, "ChainsPerInput", {}, "Chains of which this filter is concerned" };
+  std::vector<std::set<HLT::Identifier>> m_chainsPerInput;
+
+  /**
+   * It can be used to define a custom routing from input to output collections
+   * Example: [[0,1,3], [2]] means that inputs 0, 1, and 3 are directed to output 0, and input under the index 2  to aoutput 1
+   * When not set the default is used. I.e. nth input -> nth output
+   **/
+  Gaudi::Property< std::vector<std::vector<unsigned>> > m_ioMappingProperty { this, "IOMapping", {}, "Maps which inputs should be routed to which output" };
+  std::vector<std::vector<unsigned>> m_ioMapping;
 
 /**
  * @brief Applies generic filter to input container, keeping only the decision objects with at least 
@@ -82,7 +92,7 @@ class RoRSeqFilter
  * parent and stored in the output collection. It will form the starting point for the next Step. 
  **/
   size_t copyPassing( const TrigCompositeUtils::DecisionContainer& input, 
-                      TrigCompositeUtils::DecisionContainer& output) const;
+                      TrigCompositeUtils::DecisionContainer& output, const std::set<HLT::Identifier>& topass) const;
 
   ToolHandle<GenericMonitoringTool> m_monTool{ this, "MonTool", "", "Filter I/O monitoring" };
 }; 
diff --git a/Trigger/TrigSteer/DecisionHandling/src/components/DecisionHandling_entries.cxx b/Trigger/TrigSteer/DecisionHandling/src/components/DecisionHandling_entries.cxx
index 7f12647651da6dbe4a0ace81a4295b0195cc39a6..33eb0f68a38b197fe1a1a8610cbbf1ffeacbb0d4 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/components/DecisionHandling_entries.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/src/components/DecisionHandling_entries.cxx
@@ -1,5 +1,6 @@
 #include "../DumpDecisions.h"
 #include "../RoRSeqFilter.h"
+#include "../PassFilter.h"
 #include "../TriggerSummaryAlg.h"
 #include "DecisionHandling/ComboHypo.h"
 #include "../InputMakerForRoI.h"
@@ -28,6 +29,7 @@ DECLARE_COMPONENT( HLTTest::TestRecoAlg )
 
 DECLARE_COMPONENT( DumpDecisions )
 DECLARE_COMPONENT( RoRSeqFilter )
+DECLARE_COMPONENT( PassFilter )
 DECLARE_COMPONENT( TriggerSummaryAlg )
 DECLARE_COMPONENT( ComboHypo )
 DECLARE_COMPONENT( InputMakerForRoI )
diff --git a/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py b/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
index a5e8ece08353a46ee245556130dd796216dd2d16..f0e7b9ccf2c3bdf21c3cf8dce1b3d5788b5d678b 100644
--- a/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
+++ b/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
@@ -50,7 +50,7 @@ def collectHypos( steps ):
 def __decisionsFromHypo( hypo ):
     """ return all chains served by this hypo and the key of produced decision object """
     from TrigCompositeUtils.TrigCompositeUtils import isLegId
-    __log.debug("Hypo type is combo {}".format( __isCombo( hypo ) ) )
+    __log.debug("Hypo type {} is combo {}".format( hypo.getName(), __isCombo( hypo ) ) )
     if __isCombo( hypo ):
         return [key for key in list(hypo.MultiplicitiesMap.keys()) if not isLegId(key)], hypo.HypoOutputDecisions[0]
     else: # regular hypos
@@ -130,9 +130,9 @@ def collectFilterDecisionObjects(filters, inputs = True, outputs = True):
     decisionObjects = set()
     for step, stepFilters in six.iteritems (filters):
         for filt in stepFilters:
-            if inputs:
+            if inputs and hasattr( filt, "Input" ):
                 decisionObjects.update( filt.Input )
-            if outputs:
+            if outputs and hasattr( filt, "Output" ):
                 decisionObjects.update( filt.Output )
     __log.info("Collecting %i decision objects from filters", len(decisionObjects))
     return decisionObjects
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Electron/generateElectron.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Electron/generateElectron.py
index c73e14378ddbd87127fbb7befd0b201fbd3e37cb..553fbbb9de38512dbbdf12b145a75a9c8620c9e3 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Electron/generateElectron.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Electron/generateElectron.py
@@ -2,7 +2,7 @@
 
 from TriggerMenuMT.HLTMenuConfig.Electron.ElectronRecoSequences import l2CaloRecoCfg, l2CaloHypoCfg
 from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import CAMenuSequence, \
-    ChainStep, Chain, createStepView
+    ChainStep, Chain, createStepView, EmptyMenuSequence
 
 from TrigEgammaHypo.TrigEgammaFastCaloHypoTool import TrigEgammaFastCaloHypoToolFromDict
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
@@ -87,7 +87,7 @@ def generateChains( flags,  chainDict ):
     # # # EF ID
     
     # # # offline egamma
-
-    chain = Chain( chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[fastCaloStep, fastInDetStep] )
+    emptyStep = ChainStep(name="EmptyElStep", Sequences=[EmptyMenuSequence("EmptyElStep")], chainDicts=[chainDict])
+    chain = Chain( chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[fastCaloStep, fastInDetStep, emptyStep] )
     
     return chain
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py
index 5683afb49b17b36d8451e2f8a26dc083556eecfd..87f6f0587975394a6b92c814524aa5347dfdb15e 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/generateJet.py
@@ -83,7 +83,7 @@ def generateChains( flags, chainDict ):
     hypo = CompFactory.TrigJetHypoAlgMT("TrigJetHypoAlgMT_a4tcem_subjesIS")
     jetsfullname = HLT_AntiKt4EMTopo_subjesIS.fullname()
     hypo.Jets = jetsfullname
-    acc.addEventAlgo(hypo)
+    acc.addEventAlgo(hypo, sequenceName=stepView.getName() )
 
     jetSequence = CAMenuSequence( Sequence    = inEventReco.sequence(),
                                 Maker       = inEventReco.inputMaker(),
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig_newJO.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig_newJO.py
index f7e351239c6c37f8f7207e1213f81bf306ca5789..7fe9fd952164c11cbc348883fea90a38e48d2ce9 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig_newJO.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTCFConfig_newJO.py
@@ -1,17 +1,21 @@
 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 from functools import lru_cache
-from AthenaCommon.CFElements import findAllAlgorithms, parOR, seqAND
+from AthenaCommon.CFElements import findAllAlgorithms, parOR, seqAND, isSequence
 from AthenaCommon.Logging import logging
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 from AthenaConfiguration.ComponentFactory import CompFactory
 from TriggerMenuMT.HLTMenuConfig.Menu.ChainDictTools import splitChainInDict
 from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import (isComboHypoAlg,
-                                                             isHypoBase,
-                                                             isInputMakerBase)
+                                                             isFilterAlg,
+                                                             isHypoAlg,
+                                                             isInputMakerBase,
+                                                             EmptyMenuSequence)
 from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponentsNaming import CFNaming
 from TriggerMenuMT.HLTMenuConfig.Menu.TriggerConfigHLT import TriggerConfigHLT
 
-log = logging.getLogger( __name__ )
+log = logging.getLogger( __name__.split(".")[-1] )
+
+
 
 def printStepsMatrix(matrix):
     print('----- Steps matrix ------') # noqa: ATL901
@@ -22,6 +26,89 @@ def printStepsMatrix(matrix):
             print('---- {}: {}'.format(chainName, namesInCell))  # noqa: ATL901
     print('-------------------------')  # noqa: ATL901
 
+def resetDF(acc):
+    """
+    Find all algorithms involved in HLT decision DF and reset properites to empty (lists), unset
+    """
+    for alg in acc.getEventAlgos():
+        if isHypoAlg(alg):
+            log.verbose("Resetting IO for %s Hypo", alg.name )
+            alg.HypoInputDecisions  = ""
+            alg.HypoOutputDecisions = ""
+        if isInputMakerBase(alg):
+            log.verbose("Resetting IO for %s Input Maker", alg.name )
+            alg.InputMakerInputDecisions = []
+            alg.InputMakerOutputDecisions = ""
+        if isFilterAlg(alg):
+            log.verbose("Resetting IO for %s Filter", alg.name )
+            alg.Input = []
+            alg.Output = []
+        if isComboHypoAlg(alg):
+            log.verbose("Resetting IO for %s Combo Hypo", alg.name )
+            alg.MultiplicitiesMap = {}
+            alg.HypoInputDecisions = []
+            alg.HypoOutputDecisions = []
+
+def setHypoOutputs(acc):
+    for alg in acc.getEventAlgos():
+        if isHypoAlg(alg):
+            alg.HypoOutputDecisions = CFNaming.hypoAlgOutName(alg.name)
+
+def setIMOutputs(acc):
+    for alg in acc.getEventAlgos():
+        if isInputMakerBase(alg):
+            alg.InputMakerOutputDecisions = CFNaming.inputMakerOutName(alg.name)
+
+def traverse(acc, startCollectionName, functor):
+    """
+    Function to traverse DF collections path starting from an arbitrary one
+    For each checked algorithm visited the functor is called with the collection name through which traversing arrived to it, and the algorithm itself.
+    Examples are available in the test at the end of the file.
+
+    Note that this traversal has nothing to do with sequences. It only follows DF dependencies.
+    """
+    outputs = {}
+    # assemble all HLT decision DF algorithms in dictionary keyed by the output that they provide
+    for alg in acc.getEventAlgos(): # TODO improve CA implementation to obtain all algorithms when no specific sequence is required
+        if isHypoAlg(alg):
+                outputs[alg.HypoOutputDecisions] = alg
+        if isComboHypoAlg(alg):
+            for d in alg.HypoOutputDecisions:
+                outputs[d] = alg
+        if isInputMakerBase(alg):
+            outputs[alg.InputMakerOutputDecisions] = alg
+        if isFilterAlg(alg):
+            for d in alg.Output:
+                outputs[d] = alg
+
+    # for a given collection and algorithm returns list of inputs that lead to it
+    # all implementations except for ComboHypo and Filter are trivial
+    def __jumpToInputs(outputName, alg):
+        if isHypoAlg(alg):
+            return [alg.HypoInputDecisions]
+        if isInputMakerBase(alg):
+            return alg.InputMakerInputDecisions
+        if isFilterAlg(alg):
+            idx = alg.Output.index(outputName)
+            if len(alg.IOMapping) == 0: # trivial filter configuration, n-th input -> n-th output
+                return [alg.Input[idx]]
+            else:
+                return [alg.Input[i] for i in alg.IOMapping[idx]]
+        if isComboHypoAlg(alg):
+            idx = alg.HypoOutputDecisions.index(outputName)
+            return [alg.HypoInputDecisions[idx]]
+
+    # recursive traversal implementation
+    def __dive(outColl, alg):
+        functor(outColl, alg)
+        inputs = __jumpToInputs(outColl, alg)
+        for inputColl in inputs:
+            if inputColl in outputs:
+                __dive(inputColl, outputs[inputColl])
+
+    if startCollectionName in outputs:
+        __dive(startCollectionName, outputs[startCollectionName])
+
 
 def generateDecisionTree(chains):
     acc = ComponentAccumulator()
@@ -33,119 +120,104 @@ def generateDecisionTree(chains):
         """
         Returns sequence containing all filters for a step
         """
-        name = 'Step{}_{}'.format(stepNumber, CFNaming.FILTER_POSTFIX)
+        name = 'Step{}{}'.format(stepNumber, CFNaming.FILTER_POSTFIX)
         if stepNumber > 1:
             getRecosStepSeq( stepNumber -1 ) # make sure steps sequencers are correctly made: Step1_filter, Step1_recos, Step2_filters, Step2_recos ...
         seq = parOR( name )
         acc.addSequence( seq, parentName = mainSequenceName )
-        return seq
+        return acc.getSequence(seq.name)
 
     @lru_cache(None)
     def getRecosStepSeq( stepNumber ):
         """
+        Returns sequence containing all reconstruction algorithms
         """
-        getFiltersStepSeq( stepNumber ) # make sure there is filters step before recos
+        getFiltersStepSeq( stepNumber ) # make sure there is always filters step before reco
         name = 'Step{}{}'.format(stepNumber, CFNaming.RECO_POSTFIX)
         seq = parOR( name )
         acc.addSequence( seq, parentName = mainSequenceName )
-        return seq
+        return acc.getSequence(seq.name)
 
     @lru_cache(None)
     def getSingleMenuSeq( stepNumber, stepName ):
         """
         """
-        name = "Menu{}{}".format(stepNumber, stepName)
+        name = "Menu{}_{}".format(stepNumber, stepName)
         seq = seqAND( name )
 
         allRecoSeqName = getRecosStepSeq( stepNumber ).name
         acc.addSequence(seq, parentName = allRecoSeqName )
-        return seq
+        return acc.getSequence(seq.name)
 
     @lru_cache(None)
     def getComboSequences( stepNumber, stepName ):
         """
+        Produces sequnece encompassing reconstruction sequences needed in combined chains
         """
         singleMenuSeqName = getSingleMenuSeq( stepNumber, stepName ).name
 
-        stepComboName = "Combo{}{}".format(stepNumber,stepName)
+        stepComboName = "Combo{}_{}".format(stepNumber,stepName)
         acc.addSequence( seqAND(stepComboName), parentName=singleMenuSeqName )
 
-        stepComboRecoName ="ComboReco{}{}".format(stepNumber, stepName)
+        stepComboRecoName ="ComboReco{}_{}".format(stepNumber, stepName)
         acc.addSequence( parOR(stepComboRecoName), parentName=stepComboName )
         return acc.getSequence(stepComboName), acc.getSequence(stepComboRecoName)
 
+    def isEmpty( step ):
+        return all([isinstance(seq, EmptyMenuSequence) for seq in step.sequences])
+
     @lru_cache(None)
-    def getFilterAlg( stepNumber, stepName ):
+    def getFilterAlg( stepNumber, stepName, isEmpty ):
         """
         Returns, if need be created, filter for a given step
         """
+        filtersStep = getFiltersStepSeq(stepNumber)
+        singleMenuSeq = getSingleMenuSeq(stepNumber, step.name)
+        if isEmpty:
+            filterAlg = CompFactory.PassFilter(CFNaming.filterName("Pass"))
+        else:
+            filterAlg = CompFactory.RoRSeqFilter(CFNaming.filterName(stepName))
+        acc.addEventAlgo(filterAlg, sequenceName=filtersStep.name)
+        acc.addEventAlgo(filterAlg, sequenceName=singleMenuSeq.name)
 
-        filtersStep = getFiltersStepSeq( stepNumber )
-        singleMenuSeq = getSingleMenuSeq( stepNumber, stepName )
-
-        filterName = CFNaming.filterName( stepName )
-        filterAlg = CompFactory.RoRSeqFilter( filterName )
-
-        acc.addEventAlgo( filterAlg, sequenceName=filtersStep.name )
-        acc.addEventAlgo( filterAlg, sequenceName=singleMenuSeq.name )
+        log.debug('Creted filter {}'.format(filterAlg.name))
+        return acc.getEventAlgo(filterAlg.name)
 
-        log.debug('Creted filter {}'.format(filterName))
-        return filterAlg
 
-    @lru_cache(None)
-    def findInputMaker( stepCounter, stepName ):
-        seq = getSingleMenuSeq( stepCounter, stepName )
-        algs = findAllAlgorithms( seq )
-        for alg in algs:
-            if isInputMakerBase(alg):
-                return alg
-        raise Exception("No input maker in seq "+seq.name)
+    def __findAllOf( stepCounter, stepName, predicate):
+        """
+        Find all algorithms fulfilling predicate, for sequnces that do not look like menu None is placed in the result instead
+        """
+        _, comboRecoSeq = getComboSequences( stepCounter, stepName )
+        result = []
+        for recoSeq in comboRecoSeq.Members:
+            if isSequence( recoSeq ):
+                algs  = findAllAlgorithms( recoSeq )
+                for alg in algs:
+                    if predicate(alg):
+                        result.append(alg)
+                        break
+            else:
+                result.append(None)
+        return result
 
     @lru_cache(None)
     def findAllInputMakers( stepCounter, stepName ):
-        seq = getSingleMenuSeq( stepCounter, stepName )
-        algs = findAllAlgorithms( seq )
-        result = []
-        for alg in algs:
-            if isInputMakerBase(alg):
-                result.append(alg)
+        return __findAllOf( stepCounter, stepName, isInputMakerBase )
 
-        if result:
-            return result
-        else:
-            raise Exception("No input maker in seq "+seq.name)
     @lru_cache(None)
     def findComboHypoAlg( stepCounter, stepName ):
         seq = getSingleMenuSeq( stepCounter, stepName )
         algs = findAllAlgorithms( seq )
         for alg in algs:
-            if isComboHypoAlg(alg):
+            if isComboHypoAlg( alg ):
                 return alg
-        raise Exception("No combo hypo alg in seq "+seq.name)
-
-    @lru_cache(None)
-    def findHypoAlg( stepCounter, stepName ):
-        seq = getSingleMenuSeq( stepCounter, stepName )
-        algs = findAllAlgorithms( seq )
-        for alg in algs:
-            if isHypoBase(alg):
-                return alg
-        raise Exception("No hypo alg in seq "+seq.name)
-
+        return None
 
     @lru_cache(None)
     def findAllHypoAlgs( stepCounter, stepName ):
-        seq = getSingleMenuSeq( stepCounter, stepName )
-        algs = findAllAlgorithms( seq )
-        result = []
-        for alg in algs:
-            if isHypoBase(alg):
-                result.append(alg)
-
-        if result:
-            return result
-        else:
-            raise Exception("No hypo alg in seq "+seq.name)
+        r = __findAllOf( stepCounter, stepName, isHypoAlg )
+        return r
 
 
     def addAndAssureUniqness( prop, toadd, context="" ):
@@ -161,12 +233,15 @@ def generateDecisionTree(chains):
         """
         Central function setting strnig like proeprties (collection keys). Assures that valid names are not overwritten.
         """
-        if prop == "" or prop == toadd:
+        if prop == "" or prop == [] or prop == toadd:
             return toadd
         if prop != toadd:
             raise Exception("{}, when setting property found conflicting values, existing {} and new {}".format(context, prop, toadd))
 
     def clearUnderscores(s):
+        """
+        Humanizes collection name by removing multiple underscroers and stripping trailing ones
+        """
         p = s
         while True:
             n = p.replace("__", "_")
@@ -174,96 +249,138 @@ def generateDecisionTree(chains):
                 return p.rstrip("_")
             p = n
 
-    @lru_cache(None)
-    def prevStepOutput( chain, stepCounter ):
+    def prevStepComboHypoOutput(stepCounter, chain):
         """
-        Returns list of decision collections that are outputs of previous step as well as the hypo alg name that outpus it
+        Returns list of decision collections that are outputs of previous step ComboHypo
+        For the outputs of HypoAlgs use stepHypoOutput
         """
-        prevHypoAlgName = ""
         if stepCounter == 1: # L1 seed
-            out = chain.L1decisions
+            return chain.L1decisions, ""
         else:
             prevCounter = stepCounter-1
             prevName = chain.steps[prevCounter-1].name # counting steps from 1, for indexing need one less
-            prevHypoAlg = findComboHypoAlg( prevCounter, prevName )
-            out = prevHypoAlg.HypoOutputDecisions
-            prevHypoAlgName = prevHypoAlg.name
+            prevCombo = findComboHypoAlg(prevCounter, prevName)
+            if not prevCombo:
+                return prevStepComboHypoOutput(stepCounter - 1, chain)
+            return  prevCombo.HypoOutputDecisions, prevCombo.name
+
+    @lru_cache(None)
+    def stepHypoOutput(  stepCounter, chain ):
+        """
+        Return hypo outputs, if missing traverse sequences back to find one
+        """
+        hypos = findAllHypoAlgs( stepCounter, chain.steps[stepCounter-1].name )
+        if any( [h is None for h in hypos] ):
+            prevHypos = stepHypoOutput( stepCounter-1, chain )
+            return [ _1 if _1 else _2 for (_1, _2) in zip(hypos, prevHypos) ] # select leter hypo if set
+        return hypos
 
-        return [out] if isinstance( out, str) else out, prevHypoAlgName # normalise to list
 
-    # CF construction logic
-    # create all sequences and filter algs, merge CAs from signatures (decision CF)
+    # CF construction
+    # creates all sequences and filter algs, merge CAs from signatures
     for chain in chains:
         for stepCounter, step in enumerate( chain.steps, 1 ):
-            getFilterAlg( stepCounter, step.name )
-            getSingleMenuSeq( stepCounter, step.name ).name
+            getSingleMenuSeq( stepCounter, step.name )
+            getFilterAlg(stepCounter, step.name, isEmpty(step))
             # add sequences that allows reconstructions to be run in parallel, followed (in sequence) by the combo hypo
             comboSeq, comboRecoSeq = getComboSequences( stepCounter, step.name )
-            for sequence in step.sequences:
-                acc.merge( sequence.ca, sequenceName=comboRecoSeq.name)
-            comboHypo = CompFactory.ComboHypo( "CH"+step.name, CheckMultiplicityMap = len(step.sequences) != 1 )
-            acc.addEventAlgo( comboHypo, sequenceName=comboSeq.name )
-            pass
-
-    # cleanup settings made by Chain & related objects (can be removed in the future)
-    for chain in chains:
-        for stepCounter, step in enumerate( chain.steps, 1 ):
-            filterAlg = getFilterAlg( stepCounter, step.name )
-            filterAlg.Input = []
-            filterAlg.Output = []
-
-            imAlgs = findAllInputMakers( stepCounter, step.name )
-            for imAlg in imAlgs:
-                imAlg.InputMakerInputDecisions = []
-                imAlg.InputMakerOutputDecisions = ""
 
-            hypoAlgs = findAllHypoAlgs( stepCounter, step.name )
-            for hypoAlg in hypoAlgs:
-                hypoAlg.HypoInputDecisions  = ""
-                hypoAlg.HypoOutputDecisions = ""
-
-            comboHypoAlg = findComboHypoAlg( stepCounter, step.name )
-            comboHypoAlg.MultiplicitiesMap = {}
-            comboHypoAlg.HypoInputDecisions = []
-            comboHypoAlg.HypoOutputDecisions = []
+            needCombo = False
+            for sequence in step.sequences:
+                if not isinstance(sequence,EmptyMenuSequence): # empty sequence
+                    acc.merge( sequence.ca, sequenceName=comboRecoSeq.name)
+                    needCombo = True
+                else: # empty sequence
+                    acc.addEventAlgo( CompFactory.PassFilter("Pass"),  sequenceName=comboRecoSeq.name)
+            if needCombo:
+                comboHypo = CompFactory.ComboHypo( "CH"+step.name, CheckMultiplicityMap = len(step.sequences) != 1 )
+                acc.addEventAlgo( comboHypo, sequenceName=comboSeq.name )
 
+            pass
+    acc.printConfig()
+    resetDF(acc)
+    setHypoOutputs(acc)
+    setIMOutputs(acc)
+    log.debug("Configured Hypo Alg and IM Outputs")
 
-    # connect all outputs (decision DF) and add chains to filter on
+    # continue DF setting using chains info
     for chain in chains:
         for stepCounter, step in enumerate( chain.steps, 1 ):
-            # Filters linking
-            filterAlg = getFilterAlg( stepCounter, step.name )
+            if isEmpty(step):
+                continue
+
+            def __commonWihtOtherOuptut( newInputColl, filterAlg, comboHypo ):
+                """
+                For a new input collection that is meant to be added to the filter algorithm check
+                if any hypo leading to it has the same input as any other input to that filter
+                """
+                if len(filterAlg.Output) == 0: # there can not be any commonality if there is no output
+                    return None
+                colls = []
+                def __collectFilterInputs( outColl, alg ):
+                    if isFilterAlg(alg):
+                        idx = alg.Output.index(outColl)
+                        colls.append(alg.Input[idx])
+                colls = []
+                traverse( acc, newInputColl, __collectFilterInputs)
+                newInputHistory = set(colls)
+                for existingOutput in filterAlg.Output:
+                    colls = []
+                    traverse( acc, existingOutput, __collectFilterInputs)
+                    if len(set(colls).intersection(newInputHistory)) != 0:
+                        return existingOutput
+                return None
+
+
+            prevComboOut, prevComboName = prevStepComboHypoOutput(stepCounter, chain)
 
             def __setup(sequenceCounter, chainDict):
-                '''
-                Local function to setup filter/input makers/hypo algs  IO
-                '''
-                # set chain to filter on
-                filterAlg.Chains = addAndAssureUniqness( filterAlg.Chains, chainDict["chainName"], "{} filter alg chains".format( filterAlg.name ) )
+                """
+                Local function to setup filter/input makers/hypo algs IO
+                It is meant to be used for simple inclusive chains (im this case the sequenceCounter == 0)
+                For comined chains when the there is more than one sequence per step this function is called as many times as many sequences there is
+                """
+                filterAlg = getFilterAlg(stepCounter, step.name, isEmpty(step))
+
+                comboOut = prevComboOut[sequenceCounter]
+                if comboOut not in filterAlg.Input: # input is not yet connected to filter
+                    commonWihtOutput = __commonWihtOtherOuptut( comboOut, filterAlg, comboHypo )
+                    if commonWihtOutput: # this input has commonalities with other input, appropriate output is returned
+                        filterOut = commonWihtOutput
+                        filterAlg.IOMapping[ filterAlg.Output.index(commonWihtOutput) ].append( len(filterAlg.Input) )
+                    else: # entirely new input
+                        filterAlg.IOMapping.append( [len(filterAlg.Input)] )
+                        filterOut = clearUnderscores(CFNaming.filterOutName( filterAlg.name, comboOut ).replace(prevComboName, ""))
+                        filterAlg.Output = addAndAssureUniqness(filterAlg.Output, filterOut, "{} output".format(filterAlg.name))
+                    filterAlg.Input.append( comboOut )
+                    filterAlg.ChainsPerInput.append([])
+                else: # that input is connected to the filter, so it was already wired correctly to the output, we need to find out to which output it was connected
+                    inputIndex = filterAlg.Input.index(comboOut)
+                    for outIndex, inputIndices in enumerate(filterAlg.IOMapping):
+                        if inputIndex in inputIndices:
+                            filterOut = filterAlg.Output[outIndex]
 
-                filterIn, prevHypoName = prevStepOutput( chain, stepCounter)
-                filterAlg.Input = addAndAssureUniqness( filterAlg.Input, filterIn, "{} input".format( filterAlg.name ) )
-                filterOut = [ clearUnderscores(CFNaming.filterOutName( filterAlg.name,  s ).replace(prevHypoName, "")) for s in filterIn ]
-                filterAlg.Output = addAndAssureUniqness( filterAlg.Output, filterOut, "{} output".format( filterAlg.name ) )
+                # set chain to filter on
+                filterAlg.ChainsPerInput[filterAlg.Input.index(comboOut)] = addAndAssureUniqness( filterAlg.ChainsPerInput[filterAlg.Input.index(comboOut)], chainDict["chainName"],
+                                                                                                    "{} filtered cahins".format(filterAlg.name))
+                filterAlg.Chains = addAndAssureUniqness( filterAlg.Chains, chainDict["chainName"],
+                                                        "{} filtered cahins".format(filterAlg.name))
 
-                im = findAllInputMakers( stepCounter, step.name )[sequenceCounter]
-                im.InputMakerInputDecisions = addAndAssureUniqness( im.InputMakerInputDecisions,  filterOut[sequenceCounter], "{} input".format( im.name ) )
-                imOut = CFNaming.inputMakerOutName( im.name )
-                im.InputMakerOutputDecisions = assureUnsetOrTheSame( im.InputMakerOutputDecisions, imOut, "{} IM output".format( im.name ) )
+                imAlg = findAllInputMakers( stepCounter, step.name )[sequenceCounter]
+                if imAlg:
+                    imAlg.InputMakerInputDecisions = addAndAssureUniqness( imAlg.InputMakerInputDecisions, filterOut, "{} input".format( imAlg.name ) )
 
-                # Hypo linking
+                # IM -> Hypo linking
                 hypoAlg = findAllHypoAlgs( stepCounter, step.name )[sequenceCounter]
-                hypoAlg.HypoInputDecisions = assureUnsetOrTheSame( hypoAlg.HypoInputDecisions, im.InputMakerOutputDecisions,
-                    "{} hypo input".format( hypoAlg.name ) )
-                hypoOut = CFNaming.hypoAlgOutName( hypoAlg.name )
-                hypoAlg.HypoOutputDecisions = assureUnsetOrTheSame( hypoAlg.HypoOutputDecisions, hypoOut,
-                    "{} hypo output".format( hypoAlg.name )  )
-
-                hypoAlg.HypoTools.append( step.sequences[sequenceCounter]._hypoToolConf.confAndCreate( chainDict ) )
+                if hypoAlg:
+                    hypoAlg.HypoInputDecisions = assureUnsetOrTheSame( hypoAlg.HypoInputDecisions, imAlg.InputMakerOutputDecisions,
+                        "{} hypo input".format( hypoAlg.name ) )
+                    # chain selection is setup here
+                    hypoAlg.HypoTools.append( step.sequences[sequenceCounter]._hypoToolConf.confAndCreate( chainDict ) )
                 pass
 
             chainDictLegs = splitChainInDict( chain.name )
-            # possible cases: A) number of seqeunces == number of chain parts, e5_mu10 or just e3 type of chain 
+            # possible cases: A) number of seqeunces == number of chain parts, e5_mu10 or just e3 type of chain
             # ([0, 1], [0, 1]) is the set of indices
             indices = zip( range( len( step.sequences ) ), range( len( chainDictLegs ) ) )# case A
             # B) number of sequences == 1 && number of chain parts > 1 for single signature assymetric combined chains e5_e3 type chain
@@ -273,46 +390,140 @@ def generateDecisionTree(chains):
             for seqCounter, chainDictCounter  in indices:
                 chainLegDict = chainDictLegs[chainDictCounter]
                 __setup( seqCounter, chainLegDict )
-                comboHypoAlg = findComboHypoAlg( stepCounter, step.name )
+
+            comboHypoAlg = findComboHypoAlg( stepCounter, step.name )
+            if comboHypoAlg:
+
                 comboHypoAlg.MultiplicitiesMap[chain.name] = step.multiplicity
-                elementaryHypos = findAllHypoAlgs( stepCounter, step.name )
-                for hypo in elementaryHypos:
-                    if hypo == comboHypoAlg:
-                        continue
-                    comboHypoAlg.HypoInputDecisions = addAndAssureUniqness( comboHypoAlg.HypoInputDecisions, hypo.HypoOutputDecisions,
-                        "{} comboHypo input".format( comboHypoAlg.name ) )
-                    comboOut = CFNaming.comboHypoOutputName( comboHypoAlg.name, hypo.name )
-                    comboHypoAlg.HypoOutputDecisions = addAndAssureUniqness( comboHypoAlg.HypoOutputDecisions, comboOut,
-                        "{} comboHypo output".format( comboHypoAlg.name ) )
-                # Combo Hypo Tools
-                for comboToolConf in step.comboToolConfs:
-                    comboHypoAlg.ComboHypoTools.append( comboToolConf.confAndCreate( TriggerConfigHLT.getChainDictFromChainName( chain.name ) ) )
+                elementaryHyposOutputs = stepHypoOutput( stepCounter, chain )
+                for hypo in elementaryHyposOutputs:
+                    if hypo:
+                        comboHypoAlg.HypoInputDecisions = addAndAssureUniqness( comboHypoAlg.HypoInputDecisions, hypo.HypoOutputDecisions,
+                            "{} comboHypo input".format( comboHypoAlg.name ) )
+                        comboOut = CFNaming.comboHypoOutputName( comboHypoAlg.name, hypo.name )
+                        comboHypoAlg.HypoOutputDecisions = addAndAssureUniqness( comboHypoAlg.HypoOutputDecisions, comboOut,
+                            "{} comboHypo output".format( comboHypoAlg.name ) )
+                    # if chan requires special combo tools (TODO understand why not only one tool?)
+                    for comboToolConf in step.comboToolConfs:
+                        comboHypoAlg.ComboHypoTools.append( comboToolConf.confAndCreate( TriggerConfigHLT.getChainDictFromChainName( chain.name ) ) )
 
     for chain in chains:
-        log.info( "CF algorithms for chain {}".format( chain.name ) )
+        log.info( "CF algorithms for chain {}".format(chain.name))
         for stepCounter, step in enumerate( chain.steps, 1 ):
-            filterAlg = getFilterAlg( stepCounter, step.name )
-            log.info(" FilterAlg {} Inputs {} Outputs {} Chains {}".format( filterAlg.name, filterAlg.Input, filterAlg.Output, filterAlg.Chains ) )
-
-            imAlg = findInputMaker( stepCounter, step.name )
-            log.info("  InputMaker {} Inputs {} Outputs {}".format( imAlg.name, imAlg.InputMakerInputDecisions, imAlg.InputMakerOutputDecisions ) )
-            if step.isCombo:
-                hypoAlgs = findAllHypoAlgs( stepCounter, step.name )
-                for hypoAlg in hypoAlgs:
-                    if isComboHypoAlg(hypoAlg):
-                        continue
-                    log.info("   HypoAlg {} Inputs {} Outputs {} Tools {}".format( hypoAlg.name, hypoAlg.HypoInputDecisions, hypoAlg.HypoOutputDecisions, [t.name for t in hypoAlg.HypoTools] ) )
-                combo = findComboHypoAlg(  stepCounter, step.name )
-                log.info("  ComboHypoAlg {} Inputs {} Outputs {} Multiplicities {}".format( combo.name, combo.HypoInputDecisions, combo.HypoOutputDecisions, combo.MultiplicitiesMap ) )
+            filterAlg = getFilterAlg( stepCounter, step.name, isEmpty(step))
+            if filterAlg and hasattr(filterAlg, "Input"):
+                log.info("{} FilterAlg: {} input: {} output: {} IO mapping: {}".format(stepCounter, filterAlg.name, ", ".join(filterAlg.Input), ", ".join(filterAlg.Output), filterAlg.IOMapping))
+                for inIndex, input in enumerate(filterAlg.Input):
+                    log.info("{} filered chains from input: {} : {}".format( stepCounter, input,  ", ".join(filterAlg.ChainsPerInput[inIndex]) ))
+                assert len(filterAlg.IOMapping) == len(filterAlg.Output), "Not all output will be filled in filter"
 
-            else:
-                hypoAlg = findHypoAlg( stepCounter, step.name )
-                log.info("  HypoAlg {} Inputs {} Outputs {} Tools {}".format( hypoAlg.name, hypoAlg.HypoInputDecisions, hypoAlg.HypoOutputDecisions, [t.name for t in hypoAlg.HypoTools] ) )
+            imAlgs = findAllInputMakers( stepCounter, step.name )
+            for imAlg in imAlgs:
+                if imAlg:
+                    log.info("{}  InputMaker: {} input: {} output: {}".format(stepCounter, imAlg.name, ", ".join(imAlg.InputMakerInputDecisions), imAlg.InputMakerOutputDecisions))
 
+            hypoAlgs = findAllHypoAlgs(stepCounter, step.name)
+            for hypoAlg in hypoAlgs:
+                if hypoAlg:
+                    log.info("{}  HypoAlg: {} input: {} output: {}".format(stepCounter, hypoAlg.name, hypoAlg.HypoInputDecisions, hypoAlg.HypoOutputDecisions))
+                    log.info("{}  hypo tools: {}".format(stepCounter, ",".join([t.name for t in hypoAlg.HypoTools])))
+            combo = findComboHypoAlg(stepCounter, step.name)
+            if combo:
+                log.info("{}  ComboHypoAlg: {} input: {} output: {}".format(stepCounter, combo.name, ". ".join(combo.HypoInputDecisions), ", ".join(combo.HypoOutputDecisions)))
+                log.info("{}  multiplicities: {}".format(stepCounter, combo.MultiplicitiesMap))
+                assert len(combo.HypoInputDecisions) == len(combo.HypoInputDecisions), "Missconfiguraiton of {} ComboHypo input/output counts differ".format(combo.name)
+        log.info("-"*50)
+    log.info("")
     return acc
 
 
-
-def createControlFlowNewJO(HLTNode, CFseq_list):
-    """ Creates Control Flow Tree starting from the CFSequences in newJO"""
-    return
+if __name__ == "__main__":
+    # Test of DF collections traversal
+    # build hypothetical structure of DF to test back traversing
+    # make it ressemble the combined chain with one arm that is hollow/empty
+    # naming convention: H* - hypo, IM* - InputMaker, CH* - combo hypo, F* - filter
+    #                    last digit is step
+    #                    small letter a/b branch of the selection
+    acc = ComponentAccumulator()
+    # step 1
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fmerged1", Input=["Fa1_I","Fb1_I"], Output=["Fa1merged_O", "Fb1merged_O"]))
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fa1", Input=["Fa1_I"], Output=["Fa1_O"]))
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fb1", Input=["Fb1_I"], Output=["FOb1"]))
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMa1", InputMakerInputDecisions=["Fa1_O", "Fa1merged_O"], InputMakerOutputDecisions="IMa1_O"))
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMb1", InputMakerInputDecisions=["FOb1", "Fb1merged_O"], InputMakerOutputDecisions="IMb1_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Ha1", HypoInputDecisions="IMa1_O", HypoOutputDecisions="Ha1_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Hb1", HypoInputDecisions="IMb1_O", HypoOutputDecisions="Hb1_O"))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHmerged1", HypoInputDecisions=["Ha1_O","Hb1_O"], HypoOutputDecisions=["CHa1merged_O", "CHb1merged_O"]))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHa1", HypoInputDecisions=["Ha1_O"], HypoOutputDecisions=["CHa1_O"]))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHb1", HypoInputDecisions=["Hb1_O"], HypoOutputDecisions=["CHb1_O"]))
+
+    # step 2 with alternative merged sequences, when one is empty
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fmerged2", Input=["CHa1merged_O","CHb1merged_O"], Output=["Fa2merged_O", "Fb2merged_O"])) #FOb2maerged dangling/unused
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Faltmerged2", Input=["CHa1merged_O"], Output=["Fa2altmerged_O"])) #
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fa2", Input=["CHa1_O"], Output=["Fa2_O"]))
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fb2", Input=["CHb1_O"], Output=["Fb2_O"]))
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMa2", InputMakerInputDecisions=["Fa2_O", "Fa2merged_O", "Fa2altmerged_O"], InputMakerOutputDecisions="IMa2_O"))
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMb2", InputMakerInputDecisions=["Fb2_O", "Fb2merged_O"], InputMakerOutputDecisions="IMb2_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Ha2", HypoInputDecisions="IMa2_O", HypoOutputDecisions="Ha2_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Hb2", HypoInputDecisions="IMb2_O", HypoOutputDecisions="Hb2_O"))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHmerged2", HypoInputDecisions=["Ha2_O","Hb2_O"], HypoOutputDecisions=["CHa2merged_O", "CHb2merged_O"])) # assymetry here, see Hb1_O
+    acc.addEventAlgo(CompFactory.ComboHypo("CHaltmerged2", HypoInputDecisions=["Ha2_O","Hb1_O"], HypoOutputDecisions=["CHa2altmerged_O", "CHb2altmerged_O"])) # assymetry here, see Hb1_O
+    acc.addEventAlgo(CompFactory.ComboHypo("CHa2", HypoInputDecisions=["Ha2_O"], HypoOutputDecisions=["CHa2_O"]))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHb2", HypoInputDecisions=["Hb2_O"], HypoOutputDecisions=["CHb2_O"]))
+
+    # step 3 (wiht missing branch b)
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fmerged3", Input=["CHa2merged_O","CHb2merged_O", "CHa2altmerged_O", "CHb2altmerged_O"], Output=["Fa3merged_O", "Fb3merged_O"], IOMapping=[[0,2],[1,3]]))
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fa3", Input=["CHa2_O"], Output=["Fa3_O"]))
+    acc.addEventAlgo(CompFactory.RoRSeqFilter("Fb3", Input=["CHb2_O", "CHb1_O"], Output=["Fb3_O"], IOMapping=[[0,1]])) # reach to step 1, one output
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMa3", InputMakerInputDecisions=["Fa3_O", "Fa3merged_O"], InputMakerOutputDecisions="IMa3_O"))
+    acc.addEventAlgo(CompFactory.EventViewCreatorAlgorithm("IMb3", InputMakerInputDecisions=["Fb3_O", "Fb3merged_O"], InputMakerOutputDecisions="IMb3_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Ha3", HypoInputDecisions="IMa3_O", HypoOutputDecisions="Ha3_O"))
+    acc.addEventAlgo(CompFactory.HLTTest.TestHypoAlg("Hb3", HypoInputDecisions="IMb3_O", HypoOutputDecisions="Hb3_O"))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHmerged3", HypoInputDecisions=["Ha3_O","Hb3_O"], HypoOutputDecisions=["CHOa3merged", "CHOb3merged"]))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHa3", HypoInputDecisions=["Ha3_O"], HypoOutputDecisions=["CHa3_O"]))
+    acc.addEventAlgo(CompFactory.ComboHypo("CHb3", HypoInputDecisions=["Hb3_O"], HypoOutputDecisions=["CHb3_O"]))
+
+    acc.printConfig()
+    acc.wasMerged()
+
+    # test traversing and collection of info (example hypo)
+    colls = []
+    def hypoInOut(outCol, alg):
+        if isHypoAlg(alg):
+            colls.append(alg.HypoInputDecisions)
+            colls.append(alg.HypoOutputDecisions)
+
+    traverse(acc,  "CHa2_O", hypoInOut) # start from ComboHypo  output
+    log.info(colls)
+    assert set(colls) == set(['Ha2_O', 'IMa2_O', 'Ha1_O', 'IMa1_O']), "Traversing could not find hypo outpus & outputs for branch 'a'"
+
+    colls = []
+    traverse(acc,  "Fb3_O", hypoInOut) # start from ComboHypo  output
+    log.info(colls)
+    assert set(colls) == set(['Hb1_O', 'IMb1_O', 'Hb2_O', 'IMb2_O']), "Traversing cound not find hypo output & input for branch 'b'"
+
+    colls = []
+    traverse(acc,  "CHa2altmerged_O", hypoInOut) # start from ComboHypo  output
+    log.info(colls)
+    assert 'Ha1_O' in colls
+    assert 'Hb2_0' not in colls, "This is another arm of selection 'b'"
+
+    colls = []
+    traverse(acc,  "CHb2altmerged_O", hypoInOut) # start from ComboHypo  output from where 2nd setep hypo is not reachable
+    log.info(colls)
+    assert 'Hb1_O' in colls
+    assert 'Hb2_0' not in colls, "Due to empty step, this shoudl nto be reachable"
+
+
+    colls = []
+    def collectFilterInputs( outColl, alg ):
+        if isFilterAlg(alg):
+            idx = alg.Output.index(outColl)
+            colls.append(alg.Input[idx])
+
+    traverse(acc, "CHa2_O", collectFilterInputs)
+    assert  set(colls) == set(["CHa1_O", "CHa1merged_O", "Fa1_I"])
+    colls = []
+    traverse(acc, "CHb2merged_O", collectFilterInputs)
+    assert  set(colls) == set(["CHb1_O", "CHb1merged_O", "Fb1_I"])
+    log.info("All ok")
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTMenuJSON.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTMenuJSON.py
index 3ec6987198d595e9bef54649e388d4ab08f986d4..6a5e441f9072220368940c15b72da42b0f48ff56 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTMenuJSON.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/HLTMenuJSON.py
@@ -44,7 +44,7 @@ def __getChainSequencers(stepsData, chainName):
         endOfChain = False
         for sequencer in step:
             sequencerFilter = getSequenceChildren( sequencer )[0] # Always the first child in the step
-            if any(chainName in chainNameFromLegName(fChain) for fChain in sequencerFilter.Chains):
+            if hasattr(sequencerFilter, "Chains") and any(chainName in chainNameFromLegName(fChain) for fChain in sequencerFilter.Chains):
                 if mySequencer is not None:
                     __log.error( "Multiple Filters found (corresponding Sequencers %s, %s) for %s in Step %i!",
                         mySequencer.getName(), sequencer.getName(), chainName, counter)
@@ -52,12 +52,12 @@ def __getChainSequencers(stepsData, chainName):
         if mySequencer is None:
             endOfChain = True
             if counter == 1 and  'noalg' not in chainName:
-                __log.warn("No Filter found for %s in Step 1", chainName)
+                __log.info("No Filter found for %s in Step 1", chainName)
         else:
             if endOfChain is True:
                 __log.error( "Found another Step, (Step %i) for chain %s "
                     "which looked like it had already finished after %i Steps!", 
-                    counter, chainName, sequencers.len())
+                    counter, chainName, len(sequencers))
             sequencers.append(mySequencer.getName())
     return sequencers
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1_newJO.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1_newJO.py
index 955836c64b6ade964589996b286a5786b1e9e928..b7b9f78ae77f567e8e13c49c2459a94aed9a8fc3 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1_newJO.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/LS2_v1_newJO.py
@@ -52,7 +52,6 @@ def setupMenu(flags):
     flags.Trigger.menu.electron = [
         ChainProp(name='HLT_e3_etcut_L1EM3', groups=SingleElectronGroup),
         ChainProp(name='HLT_2e3_etcut_L12EM3', groups=MultiElectronGroup),
-# this chain does not work yet        
         ChainProp(name='HLT_e5_etcut_e3_etcut_L12EM3', groups=MultiElectronGroup),
         ChainProp(name='HLT_e5_etcut_L1EM3', groups=SingleElectronGroup),
         ChainProp(name='HLT_e7_etcut_L1EM7', groups=SingleElectronGroup)
@@ -60,7 +59,7 @@ def setupMenu(flags):
 
     flags.Trigger.menu.photon = [
        ChainProp(name='HLT_g10_etcut_L1EM7',  groups=SinglePhotonGroup),
-       ChainProp(name='HLT_g15_etcut_L1EM12', groups=SinglePhotonGroup),
+       ChainProp(name='HLT_g11_etcut_L1EM7', groups=SinglePhotonGroup),
     ]
 
     flags.Trigger.menu.jet = [
@@ -71,7 +70,7 @@ def setupMenu(flags):
 
     flags.Trigger.menu.combined = [
         ChainProp(name='HLT_e7_etcut_mu10_L1EM7_MU10', groups=CombinedGroup),
-        ChainProp(name='HLT_e7_etcut_mu12_L1EM7_MU10', groups=CombinedGroup)
+        ChainProp(name='HLT_e7_etcut_mu10_msonly_L1EM7_MU10', groups=CombinedGroup)
     ]
 
 if __name__ == "__main__":
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/MenuComponents.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/MenuComponents.py
index 49e3c9685e2667a03ea34f226adce304d761f062..ad668929c63354652d1a70b3ac902763abbe5099 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/MenuComponents.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/MenuComponents.py
@@ -306,7 +306,8 @@ def isFilterAlg(alg):
 def isComboHypoAlg(alg):
     return  ('MultiplicitiesMap'  in alg.__class__.__dict__)
 
-
+def isHypoAlg(alg):
+    return isHypoBase(alg) and not isComboHypoAlg(alg)
 
 
 ##########################################################
@@ -515,6 +516,7 @@ class CAMenuSequence(MenuSequence):
         return self._hypo
 
 
+
 class Chain(object):
     """Basic class to define the trigger menu """
     __slots__ ='name','steps','nSteps','alignmentGroups','vseeds','L1decisions'
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/TriggerConfigHLT.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/TriggerConfigHLT.py
index f38ec5923883904704c02bea0145d6539de4b8fd..d55b693e617f915e2a4e6caa6097bc645bd2e7da 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/TriggerConfigHLT.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Menu/TriggerConfigHLT.py
@@ -44,6 +44,10 @@ class TriggerConfigHLT(object):
     def dicts(cls):
         return cls.__allChainDicts
 
+    @classmethod
+    def configs(cls):
+        return cls.__allChainConfigs
+
     @classmethod
     def dictsList(cls):
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Muon/generateMuon.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Muon/generateMuon.py
index 1db8754f0591af5659981532677918d5b9ab171f..35f432cec8d601ea752c803afdb3c24b70bcdbcb 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Muon/generateMuon.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Muon/generateMuon.py
@@ -1,6 +1,6 @@
 # Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
 
-from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import CAMenuSequence, ChainStep, Chain, createStepView
+from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import CAMenuSequence, ChainStep, Chain, createStepView, EmptyMenuSequence
 from AthenaConfiguration.ComponentAccumulator import ComponentAccumulator
 
 from TrigL2MuonSA.TrigL2MuonSAConfig_newJO import l2MuFastAlgCfg, l2MuFastHypoCfg
@@ -56,7 +56,7 @@ def MuCombViewDataVerifier():
     result.addEventAlgo(alg)
     return result
 
-#Not the ideal place to keep the track cnv alg configuration. Temproarily adding it here 
+#Not the ideal place to keep the track cnv alg configuration. Temproarily adding it here
 #until a better location can be found
 def MuonTrackCollectionCnvToolCfg(flags, name = "MuonTrackCollectionCnvTool", **kwargs):
     TrackCollectionCnvTool = CompFactory.xAODMaker.TrackCollectionCnvTool
@@ -121,7 +121,7 @@ def efMuMSHypoCfg(flags, name="UNSPECIFIED", inputMuons="UNSPECIFIED"):
 
 def generateChains( flags, chainDict ):
     chainDict = splitChainDict(chainDict)[0]
-    
+
     # Step 1 (L2MuonSA)
     stepName = 'L2MuonSA'
     stepReco, stepView = createStepView(stepName)
@@ -138,50 +138,50 @@ def generateChains( flags, chainDict ):
 
 
     # decoding
-    # Get RPC BS decoder 
+    # Get RPC BS decoder
     from MuonConfig.MuonBytestreamDecodeConfig import RpcBytestreamDecodeCfg
     rpcAcc = RpcBytestreamDecodeCfg( flags, forTrigger=True )
     rpcAcc.getEventAlgo("RpcRawDataProvider").RoIs = reco.name+"RoIs"
     reco.mergeReco( rpcAcc )
 
     # Get RPC BS->RDO convertor
-    from MuonConfig.MuonRdoDecodeConfig import RpcRDODecodeCfg    
+    from MuonConfig.MuonRdoDecodeConfig import RpcRDODecodeCfg
     rpcAcc = RpcRDODecodeCfg( flags, forTrigger=True )
     rpcAcc.getEventAlgo("RpcRdoToRpcPrepData").RoIs = reco.name+"RoIs"
     reco.mergeReco( rpcAcc )
 
-    # Get TGC BS decoder 
+    # Get TGC BS decoder
     from MuonConfig.MuonBytestreamDecodeConfig import TgcBytestreamDecodeCfg
     tgcAcc = TgcBytestreamDecodeCfg( flags, forTrigger=True )
     tgcAcc.getEventAlgo("TgcRawDataProvider").RoIs = reco.name+"RoIs"
     reco.mergeReco( tgcAcc )
 
     # Get TGC BS->RDO convertor
-    from MuonConfig.MuonRdoDecodeConfig import TgcRDODecodeCfg    
+    from MuonConfig.MuonRdoDecodeConfig import TgcRDODecodeCfg
     tgcAcc = TgcRDODecodeCfg( flags, forTrigger=True )
     tgcAcc.getEventAlgo("TgcRdoToTgcPrepData").RoIs = reco.name+"RoIs"
     reco.mergeReco( tgcAcc )
 
-    # Get MDT BS decoder 
+    # Get MDT BS decoder
     from MuonConfig.MuonBytestreamDecodeConfig import MdtBytestreamDecodeCfg
     mdtAcc = MdtBytestreamDecodeCfg( flags, forTrigger=True )
     mdtAcc.getEventAlgo("MdtRawDataProvider").RoIs = reco.name+"RoIs"
     reco.mergeReco( mdtAcc )
 
     # Get MDT BS->RDO convertor
-    from MuonConfig.MuonRdoDecodeConfig import MdtRDODecodeCfg    
+    from MuonConfig.MuonRdoDecodeConfig import MdtRDODecodeCfg
     mdtAcc = MdtRDODecodeCfg( flags, forTrigger=True )
     mdtAcc.getEventAlgo("MdtRdoToMdtPrepData").RoIs = reco.name+"RoIs"
     reco.mergeReco( mdtAcc )
 
-    # Get CSC BS decoder 
+    # Get CSC BS decoder
     from MuonConfig.MuonBytestreamDecodeConfig import CscBytestreamDecodeCfg
     cscAcc = CscBytestreamDecodeCfg( flags, forTrigger=True )
     cscAcc.getEventAlgo("CscRawDataProvider").RoIs = reco.name+"RoIs"
     reco.mergeReco( cscAcc )
 
     # Get CSC BS->RDO convertor
-    from MuonConfig.MuonRdoDecodeConfig import CscRDODecodeCfg    
+    from MuonConfig.MuonRdoDecodeConfig import CscRDODecodeCfg
     cscAcc = CscRDODecodeCfg( flags, forTrigger=True )
     cscAcc.getEventAlgo("CscRdoToCscPrepData").RoIs = reco.name+"RoIs"
     reco.mergeReco( cscAcc )
@@ -198,7 +198,7 @@ def generateChains( flags, chainDict ):
 
     l2MuFastAlgAcc = ComponentAccumulator()
     l2MuFastAlgAcc.addEventAlgo(alg)
-    
+
     reco.mergeReco( l2MuFastAlgAcc )
     reco.merge( algAcc )
     #    l2muFastReco = l2MuFastRecoCfg(flags)
@@ -219,7 +219,7 @@ def generateChains( flags, chainDict ):
 
     l2muFastStep = ChainStep( name=stepName, Sequences=[l2muFastSequence], chainDicts=[chainDict] )
 
-    if 'msonly' not in chainDict['chainName']: 
+    if 'msonly' not in chainDict['chainName']:
         #only run in combined muon chains
         ### Set muon step2 - L2muComb ###
         stepL2CBName = 'L2MuonCB'
@@ -227,7 +227,7 @@ def generateChains( flags, chainDict ):
 
         accL2CB = ComponentAccumulator()
         accL2CB.addSequence(stepL2CBView)
-        
+
         # Set EventViews for L2MuonCB step
         recoL2CB = l2MuCombRecoCfg(flags)
         #external data loading to view
@@ -237,7 +237,7 @@ def generateChains( flags, chainDict ):
         #ID tracking
         accID = TrigInDetConfig( flags, roisKey=recoL2CB.inputMaker().InViewRoIs, signatureName="Muon" )
         recoL2CB.mergeReco(accID)
-        
+
         accL2CB.merge(recoL2CB, sequenceName = stepL2CBReco.getName())
 
         l2muCombHypo = l2MuCombHypoCfg( flags,
@@ -253,9 +253,9 @@ def generateChains( flags, chainDict ):
                                            CA = accL2CB )
 
         l2muCombStep = ChainStep( name=stepL2CBName, Sequences=[l2muCombSequence], chainDicts=[chainDict] )
-    
 
-    if 'msonly' in chainDict['chainName']: 
+
+    if 'msonly' in chainDict['chainName'] or True:
         #only runningn in MS-only chains for now
         #EF MS only
         stepEFMSName = 'EFMSMuon'
@@ -268,31 +268,31 @@ def generateChains( flags, chainDict ):
         muonflags.Muon.MuonTrigger=True
         muonflags.Muon.SAMuonTrigger=True
         muonflags.lock()
-        
+
         accMS = ComponentAccumulator()
         accMS.addSequence(stepEFMSView)
 
         from TriggerMenuMT.HLTMenuConfig.Menu.MenuComponents import InViewReco
         recoMS = InViewReco("EFMuMSReco")
         recoMS.inputMaker().RequireParentView = True
-    
+
         #Probably this block will eventually need to move somewhere more central
         from BeamPipeGeoModel.BeamPipeGMConfig import BeamPipeGeometryCfg
-        accMS.merge( BeamPipeGeometryCfg(flags) ) 
-        
+        accMS.merge( BeamPipeGeometryCfg(flags) )
+
         from PixelGeoModel.PixelGeoModelConfig import PixelGeometryCfg
         accMS.merge(PixelGeometryCfg(flags))
-    
+
         from SCT_GeoModel.SCT_GeoModelConfig import SCT_GeometryCfg
         accMS.merge(SCT_GeometryCfg(flags))
-        
+
         from TRT_GeoModel.TRT_GeoModelConfig import TRT_GeometryCfg
         accMS.merge(TRT_GeometryCfg(flags))
-    
+
         from TrkConfig.AtlasTrackingGeometrySvcConfig import TrackingGeometrySvcCfg
         accMS.merge(TrackingGeometrySvcCfg(flags))
         ###################
-    
+
         EFMuonViewDataVerifier = EFMuonViewDataVerifierCfg()
         recoMS.mergeReco(EFMuonViewDataVerifier)
 
@@ -325,7 +325,7 @@ def generateChains( flags, chainDict ):
 
         efmuMSSequence = CAMenuSequence( Sequence = recoMS.sequence(),
                                          Maker = recoMS.inputMaker(),
-                                         Hypo = efmuMSHypo, 
+                                         Hypo = efmuMSHypo,
                                          HypoToolGen = TrigMuonEFMSonlyHypoToolFromDict,
                                          CA = accMS )
 
@@ -334,12 +334,13 @@ def generateChains( flags, chainDict ):
     l1Thresholds=[]
     for part in chainDict['chainParts']:
         l1Thresholds.append(part['L1threshold'])
-    
+
     log.debug('dictionary is: %s\n', pprint.pformat(chainDict))
 
-    if 'msonly' in chainDict['chainName']: 
-        chain = Chain( name=chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[ l2muFastStep, efmuMSStep ] )
+    if 'msonly' in chainDict['chainName']:
+        emptyStep = ChainStep(name="EmptyNoL2MuComb", Sequences=[EmptyMenuSequence("EmptyNoL2MuComb")], chainDicts=[chainDict])
+        chain = Chain( name=chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[ l2muFastStep, emptyStep, efmuMSStep ] )
     else:
-        chain = Chain( name=chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[ l2muFastStep, l2muCombStep ] )
+        chain = Chain( name=chainDict['chainName'], L1Thresholds=l1Thresholds, ChainSteps=[ l2muFastStep, l2muCombStep, efmuMSStep ] )
     return chain