diff --git a/Event/xAOD/xAODTrigger/Root/TrigComposite_v1.cxx b/Event/xAOD/xAODTrigger/Root/TrigComposite_v1.cxx
index b9c293d6a2cfeb8a51ebbfd648ca4cf2b7b2291f..3d916bee953856ab315ffaf8a2ed195525ac06d1 100644
--- a/Event/xAOD/xAODTrigger/Root/TrigComposite_v1.cxx
+++ b/Event/xAOD/xAODTrigger/Root/TrigComposite_v1.cxx
@@ -199,8 +199,8 @@ namespace xAOD {
    bool TrigComposite_v1::hasObjectLinkExact(const std::string& name, const uint32_t key, const uint16_t index, const uint32_t clid) const {
       for (size_t i = 0; i < this->linkColNames().size(); ++i) {
          if (this->linkColNames().at(i) != name) continue;
-         if (this->linkColKeys().at(i) != key) continue;
-         if (this->linkColIndices().at(i) != index) continue;
+         if (this->linkColKeysNoRemap().at(i) != key) continue;
+         if (this->linkColIndicesNoRemap().at(i) != index) continue;
          if (this->linkColClids().at(i) != clid) continue;
          return true;
       } 
@@ -214,13 +214,39 @@ namespace xAOD {
 
    AUXSTORE_OBJECT_GETTER( TrigComposite_v1, std::vector< std::string >,
                            linkColNames )
-   AUXSTORE_OBJECT_GETTER( TrigComposite_v1, std::vector< uint32_t >,
-                           linkColKeys )
-   AUXSTORE_OBJECT_GETTER( TrigComposite_v1, std::vector< uint16_t >,
-                           linkColIndices )
    AUXSTORE_OBJECT_GETTER( TrigComposite_v1, std::vector< uint32_t >,
                            linkColClids )
 
+   const std::vector< uint32_t >& TrigComposite_v1::linkColKeys() const {
+      if (isRemapped()) {
+        static const Accessor< std::vector< uint32_t > > acc_remap( "remap_linkColKeys" );
+        return acc_remap( *this );
+      }
+      static const Accessor< std::vector< uint32_t > > acc_builtin( "linkColKeys" );
+      return acc_builtin( *this );
+   }
+
+   const std::vector< uint16_t >& TrigComposite_v1::linkColIndices() const {
+      if (isRemapped()) {
+        static const Accessor< std::vector< uint16_t > > acc_remap( "remap_linkColIndices" );
+        return acc_remap( *this );
+      }
+      static const Accessor< std::vector< uint16_t > > acc_builtin( "linkColIndices" );
+      return acc_builtin( *this );
+   }
+
+   const std::vector< uint32_t >& TrigComposite_v1::linkColKeysNoRemap() const {
+      static const Accessor< std::vector< uint32_t > > acc( "linkColKeys" );
+      return acc( *this );
+   }
+
+   const std::vector< uint16_t >& TrigComposite_v1::linkColIndicesNoRemap() const {
+      static const Accessor< std::vector< uint16_t > > acc( "linkColIndices" );
+      return acc( *this );
+   }
+
+   ////////
+
    std::vector< std::string >& TrigComposite_v1::linkColNamesNC() {
 
       static const Accessor< std::vector< std::string > > acc( "linkColNames" );
@@ -314,18 +340,35 @@ namespace xAOD {
      }
    }
 
+   bool TrigComposite_v1::isRemapped() const {
+      static const Accessor< std::vector< uint32_t > > key_remap( "remap_linkColKeys" );
+      static const Accessor< std::vector< uint16_t > > index_remap( "remap_linkColIndices" );
+      size_t nDecorations = 0;
+      if (key_remap.isAvailable( *this )) ++nDecorations;
+      if (index_remap.isAvailable( *this )) ++nDecorations;
+      if (nDecorations == 1) {
+        throw std::runtime_error("TrigComposite_v1::isRemapped Only one of the 'remap_linkColKeys' and 'remap_linkColIndices' "
+          "decorations were found on this object. This should never happen, a remapped element link must have both of these collections.");
+      }
+      return static_cast<bool>(nDecorations); //0=Fasle, 2=True
+   }
+
+
    //
    /////////////////////////////////////////////////////////////////////////////
 
 
 std::ostream& operator<<(std::ostream& os, const xAOD::TrigComposite_v1& tc) {
   os << "TrigComposite_v1 name:'" << tc.name() << "'" << std::endl;
-  os << "  N Lnks:" << tc.linkColNames().size();
+  const bool isRemapped = tc.isRemapped();
+  os << "  N Links:" << tc.linkColNames().size() << ", isRemapped:" << (isRemapped ? "YES" : "NO");
   for (size_t i=0; i<tc.linkColNames().size(); ++i){
     if (!i) os << std::endl;
     os << "    Link Name:"  << tc.linkColNames()[i];
     os << ", Key:"   << tc.linkColKeys()[i];
+    if (isRemapped) os << ", OldKey:"   << tc.linkColKeysNoRemap()[i];
     os << ", Index:" << tc.linkColIndices()[i];
+    if (isRemapped) os << ", OldIndex:" << tc.linkColIndicesNoRemap()[i];
     os << ", CLID:"  << tc.linkColClids()[i];
     if (i != tc.linkColNames().size() - 1) os << std::endl;
   }
diff --git a/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.h b/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.h
index 54420895b95664410a65a3bd6d1467a063052713..434ec8485befeb1818bfa321b228765e412de1ca 100644
--- a/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.h
+++ b/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.h
@@ -166,13 +166,22 @@ namespace xAOD {
 
       /// Raw access to the persistent link names
       const std::vector< std::string >& linkColNames() const;
-      /// Raw access to the persistent link labels
+      /// Raw access to the persistent link labels. Will use remapped data, if available.
       const std::vector< uint32_t >& linkColKeys() const;
-      /// Raw access to the persistent link indices
+      /// Raw access to the persistent link indices. Will use remapped data, if available.
       const std::vector< uint16_t >& linkColIndices() const;
       /// Raw access to the persistent link CLIDs
       const std::vector< uint32_t >& linkColClids() const;
 
+      /// Information on if linkColKeys() and linkColIndices() are able to access remapped link data
+      /// Remapping happens at the end of HLT execution when EDM objects are copied out of their per-EventView
+      /// containers and into the global Trigger EDM containers.
+      bool isRemapped() const;
+
+      /// Raw access to the persistent link labels. Will not attempt to access remapped link data.
+      const std::vector< uint32_t >& linkColKeysNoRemap() const;
+      /// Raw access to the persistent link indices. Will not attempt to access remapped link data.
+      const std::vector< uint16_t >& linkColIndicesNoRemap() const;
 
       /// @}
 
diff --git a/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.icc b/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.icc
index 3af13d04d63d733e37aa2339404b2a4af3c724ff..6c3bf8b4c6de0712f02818843b4768e7e4c4d1b0 100644
--- a/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.icc
+++ b/Event/xAOD/xAODTrigger/xAODTrigger/versions/TrigComposite_v1.icc
@@ -119,9 +119,16 @@ namespace xAOD {
       for( size_t i = 0; i < names.size(); ++i ) {
          if( names[ i ] != name ) continue;
          checkTypes< CONTAINER >(linkColClids()[ i ], name);
-         // Construct the link:
-         return ElementLink< CONTAINER >( linkColKeys()[ i ],
-                                          linkColIndices()[ i ] );
+         // Construct the link. Try to remap, if remap collections are available:
+         ElementLink< CONTAINER > linkTryRemap( linkColKeys()[ i ],
+                                                linkColIndices()[ i ] );
+         if (linkTryRemap.isValid()) {
+            return linkTryRemap;
+         }
+
+         // Try again, without remapping
+         return ElementLink< CONTAINER >( linkColKeysNoRemap()[ i ],
+                                          linkColIndicesNoRemap()[ i ] );
       }
 
       // We didn't find the link. :-(
@@ -145,13 +152,19 @@ namespace xAOD {
          // Check that it is of the right type:
          checkTypes< DataVector< OBJECT > >(linkColClids()[ i ], name);
          // Create a temporary ElementLink:
-         ElementLink< DataVector< OBJECT > > link( linkColKeys()[ i ],
-                                                   linkColIndices()[ i ] );
-         if( ! link.isValid() ) {
-            return 0;
+         ElementLink< DataVector< OBJECT > > linkTryRemap( linkColKeys()[ i ],
+                                                           linkColIndices()[ i ] );
+         if( linkTryRemap.isValid() ) {
+            return *linkTryRemap;
+         }
+
+         // This time forbid remapping
+         ElementLink< DataVector< OBJECT > > link( linkColKeysNoRemap()[ i ],
+                                                   linkColIndicesNoRemap()[ i ] );
+
+         if( link.isValid() ) {
+            return *link;
          }
-         // Get the pointer:
-         return *link;
       }
 
       // There was an internal error. :-(
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
index 176351abd4feae4f7ac0d62c3c78ee12efa81702..f2b6c8ecffa77837ee73916fb3b99eb76e68809c 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.h
@@ -121,10 +121,12 @@ namespace Trig {
        * @param[in] eventStore Event store pointer. To migrate to readHandles with the rest of the TDT soon
        * @param[in] condition Condition requirement. Only physics currently supported.
        * @param[in] oneFeaturePerLeg If true, only collects the final feature from each object which passed the event for the Chain Group.
-       * @return ElementLinkVector with one entry per located feature for the ChainGroup's chain(s)
+       * @return Vector of LinkInfo, where each entry wraps an ElementLink to the feature, and the Decision object it came from.
        **/  
       template<class CONTAINER>
-      const ElementLinkVector<CONTAINER> features(EventPtr_t eventStore, unsigned int condition = TrigDefs::Physics, const bool oneFeaturePerLeg = true) const;
+      const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > features(EventPtr_t eventStore,
+                unsigned int condition = TrigDefs::Physics,
+                const bool oneFeaturePerLeg = true) const;
 
       // 
       const std::vector< std::string >& patterns() const {return m_patterns;}
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
index 67f865230b0dad2619902e22fb01d41eb27d371a..b5f2841f4d3baf7e921350e87be3c44c9e8bb04d 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/ChainGroup.icc
@@ -1,56 +1,64 @@
-template<class CONTAINER>
-const ElementLinkVector<CONTAINER> Trig::ChainGroup::features(EventPtr_t eventStore, unsigned int condition, const bool oneFeaturePerLeg) const {
-
-  // Proper adherence to the condition bits in Run 3 is to follow.
-
-  bool errState = false;
-  if ( !(condition & TrigDefs::Physics) ) {
-    ATH_MSG_ERROR("Only TrigDefs::Physics is currently supported for Run 3 feature retrieval");
-    errState = true;
-  }
-  if ( condition & TrigDefs::allowResurrectedDecision ) {
-    ATH_MSG_ERROR("TrigDefs::allowResurrectedDecision is not yet supported for feature retrieval for Run 3");
-    errState = true;
-  }
-
-  // TODO when we decide what happens to CacheGlobalMemory - this needs to be updated to use a ReadHandle
-  const TrigCompositeUtils::DecisionContainer* navigationSummaryContainer = nullptr;
-  if (eventStore->retrieve(navigationSummaryContainer, "HLTSummary").isFailure() || navigationSummaryContainer == nullptr) {
-    ATH_MSG_ERROR("Unable to read Run 3 trigger navigation. Cannot retrieve features.");
-    errState = true;
-  }
-
-  // We just support Physics decision for now
-  const TrigCompositeUtils::Decision* terminusNode = nullptr;
-
-  if (!errState) {
-    for (const TrigCompositeUtils::Decision* decision : *navigationSummaryContainer) {
-      if (decision->name() == "HLTPassRaw") {
-        terminusNode = decision;
-        break;
-      }
-    }
-    if (terminusNode == nullptr) {
-      ATH_MSG_ERROR("Unable to locate HLTPassRaw element of HLTSummary");
-      errState = true;
-    }
-  }
-
-  if (errState) {
-    return ElementLinkVector<CONTAINER>();
-  }
-
-  // For each chain, collect Navigation information
-  std::vector< ElementLinkVector<TrigCompositeUtils::DecisionContainer> > allLinearNavigationPaths;
-
-  // Loop over HLT chains
-  std::set<const TrigConf::HLTChain*>::const_iterator chIt;
-  for (chIt=conf_chain_begin(); chIt != conf_chain_end(); ++chIt) { 
-    const HLT::Chain* fchain = cgm()->chain(**chIt);
-    if (fchain) {
-      TrigCompositeUtils::recursiveGetDecisions(terminusNode, allLinearNavigationPaths, fchain->getChainHashId());
-    }
-  }
-
-  return TrigCompositeUtils::getFeaturesOfType<CONTAINER>(allLinearNavigationPaths, oneFeaturePerLeg);
-}
+template<class CONTAINER>
+const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::ChainGroup::features(EventPtr_t eventStore,
+        unsigned int condition, const bool oneFeaturePerLeg) const {
+
+  // Proper adherence to the condition bits in Run 3 is to follow.
+  bool errState = false;
+  if ( !(condition & TrigDefs::Physics) ) {
+    ATH_MSG_ERROR("Only TrigDefs::Physics is currently supported for Run 3 feature retrieval");
+    errState = true;
+  }
+  if ( condition & TrigDefs::allowResurrectedDecision ) {
+    ATH_MSG_ERROR("TrigDefs::allowResurrectedDecision is not yet supported for feature retrieval for Run 3");
+    errState = true;
+  }
+
+  // TODO when we decide what happens to CacheGlobalMemory - this needs to be updated to use a ReadHandle
+  const TrigCompositeUtils::DecisionContainer* navigationSummaryContainer = nullptr;
+  if (eventStore->retrieve(navigationSummaryContainer, "HLTSummary").isFailure() || navigationSummaryContainer == nullptr) {
+    ATH_MSG_ERROR("Unable to read Run 3 trigger navigation. Cannot retrieve features.");
+    errState = true;
+  }
+
+  // We just support Physics decision for now
+  const TrigCompositeUtils::Decision* terminusNode = nullptr;
+
+  if (!errState) {
+    for (const TrigCompositeUtils::Decision* decision : *navigationSummaryContainer) {
+      if (decision->name() == "HLTPassRaw") {
+        terminusNode = decision;
+        break;
+      }
+    }
+    if (terminusNode == nullptr) {
+      ATH_MSG_ERROR("Unable to locate HLTPassRaw element of HLTSummary");
+      errState = true;
+    }
+  }
+
+  if (errState) {
+    ATH_MSG_ERROR("Encountered one or more errors in Trig::ChainGroup::features. Returning empty vector.");
+    return std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> >();
+  }
+
+  // For each chain, collect Navigation information
+  std::vector< ElementLinkVector<TrigCompositeUtils::DecisionContainer> > allLinearNavigationPaths;
+
+  // Loop over HLT chains
+  std::set<const TrigConf::HLTChain*>::const_iterator chIt;
+  for (chIt=conf_chain_begin(); chIt != conf_chain_end(); ++chIt) {
+    const HLT::Chain* fchain = cgm()->chain(**chIt);
+    if (fchain) {
+      TrigCompositeUtils::recursiveGetDecisions(terminusNode, allLinearNavigationPaths, fchain->getChainHashId());
+      ATH_MSG_DEBUG("Added all navigation paths for chain " << fchain->getChainName() << ", total paths:" << allLinearNavigationPaths.size());
+    } else {
+      ATH_MSG_ERROR("Cannot access configuration for one of the ChainGroup's chains");
+    }
+  }
+
+  if (allLinearNavigationPaths.size() == 0) {
+    ATH_MSG_WARNING("No navigation paths found for this chain group of " << names().size() << " chains.");
+  }
+
+  return TrigCompositeUtils::getFeaturesOfType<CONTAINER>(allLinearNavigationPaths, oneFeaturePerLeg);
+}
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.h
index fb2e7d9001d9252e13b11af400fbeafbd9dc5ed0..f69d411b4a4bbc8b6643cad2a70bb55e3692a4e0 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.h
@@ -107,17 +107,27 @@ namespace Trig {
 
     /**
      * @brief Runs 3+. Returns all features related to given chain group
+     * @param[in] group Chain group to return features for.
+     * @param[in] condition Condition bits which the chain group must satisfy.
+     * @param[in] oneFeaturePerLeg If true, only collects the final feature from each object which passed the event for the Chain Group.
+     * @return Vector of LinkInfo, where each entry wraps an ElementLink to the feature, and the Decision object it came from.
      **/
     template<class CONTAINER>
-    const ElementLinkVector<CONTAINER> features(const Trig::ChainGroup* group, 
-                                                unsigned int condition = TrigDefs::Physics) const;
+    const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > features(const Trig::ChainGroup* group,
+                                                                          const unsigned int condition = TrigDefs::Physics,
+                                                                          const bool oneFeaturePerLeg = true) const;
 
     /**
      * @brief Runs 3+. Returns features related to given chain
+     * @param[in] group Chain group to return features for.
+     * @param[in] condition Condition bits which the chain group must satisfy.
+     * @param[in] oneFeaturePerLeg If true, only collects the final feature from each object which passed the event for the Chain Group.
+     * @return Vector of LinkInfo, where each entry wraps an ElementLink to the feature, and the Decision object it came from.
      **/
     template<class CONTAINER>
-    const ElementLinkVector<CONTAINER> features(const std::string& chainName = "HLT_.*", 
-                                                unsigned int condition = TrigDefs::Physics) const;
+    const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > features(const std::string& chainName = "HLT_.*",
+                                                                          const unsigned int condition = TrigDefs::Physics,
+                                                                          const bool oneFeaturePerLeg = true) const;
 
     /**
      * @brief gives back feature matching (by seeding relation)
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
index 7071fca4c1bcf341e3182e777f5d5b506f818f1b..0d0f091f5e8b96e24868616fe84b28b0a4a47c2f 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
@@ -1,57 +1,59 @@
-#if defined(ASGTOOL_ATHENA) && !defined(XAOD_ANALYSIS)
-
-template<class T> 
-const Trig::Feature<T> Trig::DecisionAccess::ancestor(const HLT::TriggerElement* te, std::string label) const {
-  Trig::Feature<T> f;
-  std::vector<Trig::Feature<T> > data;
-  FeatureAccessImpl::collect<T>(te, data, label, TrigDefs::alsoDeactivateTEs, "", const_cast<HLT::TrigNavStructure*>(cgm()->navigation()));
-
-  BOOST_FOREACH( Feature<T>& f, data ) {
-    if ( f.owned() ) {
-      cgm()->deleteAtTheEndOfEvent( const_cast<T*>( f.cptr() ) );
-    }
-  }
-
-  if (data.size() == 1)
-    f = data[0];  
-  return f;
-}
-
-
-template<class T>
-const std::vector<Trig::Feature<T> > Trig::DecisionAccess::ancestors(const HLT::TriggerElement* te, std::string label, unsigned int condition, const std::string& teName) const {
-  std::vector<Trig::Feature<T> > data;
-  FeatureAccessImpl::collect<T>(te, data, label, condition, teName, const_cast<HLT::TrigNavStructure*>(cgm()->navigation()));
-  BOOST_FOREACH( Feature<T>& f, data ) {
-    if ( f.owned() ) {
-      cgm()->deleteAtTheEndOfEvent(const_cast<T*>( f.cptr() ));
-    }
-  }
-  return data;
-}
-#else
-template<class T> 
-const Trig::Feature<T> Trig::DecisionAccess::ancestor(const HLT::TriggerElement* /*te*/, std::string /*label*/) const {
-  ATH_MSG_WARNING("DecisionAccess::ancestor not implemented in Standalone mode, since it needs compile-time type information. Returning empty Feature");  
-  return Trig::Feature<T>();
-}
-template<class T>
-const std::vector<Trig::Feature<T> > Trig::DecisionAccess::ancestors(const HLT::TriggerElement* /*te*/, std::string /*label*/, unsigned int /*condition*/, const std::string& /*teName*/) const {
-  ATH_MSG_WARNING("DecisionAccess::ancestor not implemented in Standalone mode, since it needs compile-time type information. Returning empty vector");  
-  return  std::vector<Trig::Feature<T> >();
-}
-
-#endif // ASGTOOL_ATHENA
-
-template<class CONTAINER>
-const ElementLinkVector<CONTAINER> Trig::DecisionAccess::features(const Trig::ChainGroup* group, 
-                                                                  unsigned int condition) const {
-  return group->features<CONTAINER>(cgm()->store(), condition, /*one feature per leg*/ true);
-}
-
-template<class CONTAINER>
-const ElementLinkVector<CONTAINER> Trig::DecisionAccess::features(const std::string& chainName, 
-                                                                  unsigned int condition) const {
-  const Trig::ChainGroup *g = cgm()->createChainGroup(Trig::convertStringToVector(chainName));
-  return features<CONTAINER>(g,condition);
-}
\ No newline at end of file
+#if defined(ASGTOOL_ATHENA) && !defined(XAOD_ANALYSIS)
+
+template<class T> 
+const Trig::Feature<T> Trig::DecisionAccess::ancestor(const HLT::TriggerElement* te, std::string label) const {
+  Trig::Feature<T> f;
+  std::vector<Trig::Feature<T> > data;
+  FeatureAccessImpl::collect<T>(te, data, label, TrigDefs::alsoDeactivateTEs, "", const_cast<HLT::TrigNavStructure*>(cgm()->navigation()));
+
+  BOOST_FOREACH( Feature<T>& f, data ) {
+    if ( f.owned() ) {
+      cgm()->deleteAtTheEndOfEvent( const_cast<T*>( f.cptr() ) );
+    }
+  }
+
+  if (data.size() == 1)
+    f = data[0];  
+  return f;
+}
+
+
+template<class T>
+const std::vector<Trig::Feature<T> > Trig::DecisionAccess::ancestors(const HLT::TriggerElement* te, std::string label, unsigned int condition, const std::string& teName) const {
+  std::vector<Trig::Feature<T> > data;
+  FeatureAccessImpl::collect<T>(te, data, label, condition, teName, const_cast<HLT::TrigNavStructure*>(cgm()->navigation()));
+  BOOST_FOREACH( Feature<T>& f, data ) {
+    if ( f.owned() ) {
+      cgm()->deleteAtTheEndOfEvent(const_cast<T*>( f.cptr() ));
+    }
+  }
+  return data;
+}
+#else
+template<class T> 
+const Trig::Feature<T> Trig::DecisionAccess::ancestor(const HLT::TriggerElement* /*te*/, std::string /*label*/) const {
+  ATH_MSG_WARNING("DecisionAccess::ancestor not implemented in Standalone mode, since it needs compile-time type information. Returning empty Feature");  
+  return Trig::Feature<T>();
+}
+template<class T>
+const std::vector<Trig::Feature<T> > Trig::DecisionAccess::ancestors(const HLT::TriggerElement* /*te*/, std::string /*label*/, unsigned int /*condition*/, const std::string& /*teName*/) const {
+  ATH_MSG_WARNING("DecisionAccess::ancestor not implemented in Standalone mode, since it needs compile-time type information. Returning empty vector");  
+  return  std::vector<Trig::Feature<T> >();
+}
+
+#endif // ASGTOOL_ATHENA
+
+template<class CONTAINER>
+const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::DecisionAccess::features(const Trig::ChainGroup* group,
+                                                                  const unsigned int condition,
+                                                                  const bool oneFeaturePerLeg) const {
+  return group->features<CONTAINER>(cgm()->store(), condition, oneFeaturePerLeg);
+}
+
+template<class CONTAINER>
+const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::DecisionAccess::features(const std::string& chainName,
+                                                                  const unsigned int condition,
+                                                                  const bool oneFeaturePerLeg) const {
+  const Trig::ChainGroup *g = cgm()->createChainGroup(Trig::convertStringToVector(chainName));
+  return features<CONTAINER>(g,condition,oneFeaturePerLeg);
+}
diff --git a/Trigger/TrigConfiguration/TrigConfigSvc/src/HLTConfigSvc.cxx b/Trigger/TrigConfiguration/TrigConfigSvc/src/HLTConfigSvc.cxx
index c734a5a6d790e2bba3dd533f3dd171ff658ea191..d67a53b5fcda1e7bd7322eb089f1a92b654884b6 100644
--- a/Trigger/TrigConfiguration/TrigConfigSvc/src/HLTConfigSvc.cxx
+++ b/Trigger/TrigConfiguration/TrigConfigSvc/src/HLTConfigSvc.cxx
@@ -146,6 +146,35 @@ HLTConfigSvc::initialize() {
       dummyChains["HLT_e3_etcut"] = "L1_EM3";
       dummyChains["HLT_e5_etcut"] = "L1_EM3";
       dummyChains["HLT_e7_etcut"] = "L1_EM7";
+      // for menu test
+      dummyChains["HLT_2mu4_bBmumu_L12MU4"] = "L1_2MU4";
+      dummyChains["HLT_2mu4_bJpsimumu_L12MU4"] = "L1_2MU4";
+      dummyChains["HLT_2mu4_bUpsimumu_L12MU4"] = "L1_2MU4";
+      dummyChains["HLT_2mu6"] = "L1_2MU6";
+      dummyChains["HLT_2mu6Comb"] = "L1_2MU6";
+      dummyChains["HLT_2mu6_bJpsimumu_L12MU6"] = "L1_2MU6";
+      dummyChains["HLT_3j200"] = "L1_J50";
+      dummyChains["HLT_5j70_0eta240"] = "L1_J50";
+      dummyChains["HLT_e3_etcut1step"] = "L1_EM3";
+      dummyChains["HLT_e3_etcut_mu6"] = "L1_EM8I_MU10";
+      dummyChains["HLT_g5_etcut"] = "L1_EM3";
+      dummyChains["HLT_j0_vbenfSEP30etSEP34mass35SEP50fbet20"] = "L1_RD0_FILLED";
+      dummyChains["HLT_j225_gsc420_boffperf_split20"] = "L1_J100";
+      dummyChains["HLT_j260_320eta490"] = "L1_J100";
+      dummyChains["HLT_j420"] = "L1_J100";
+      dummyChains["HLT_j45"] = "L1_J20";
+      dummyChains["HLT_j460_a10_lcw_subjes"] = "L1_J100";
+      dummyChains["HLT_j460_a10r"] = "L1_J100";
+      dummyChains["HLT_j85"] = "L1_J20";
+      dummyChains["HLT_mu20_ivar"] = "L1_MU20";
+      dummyChains["HLT_mu6"] = "L1_MU6";
+      dummyChains["HLT_mu6Comb"] = "L1_MU6";
+      dummyChains["HLT_mu6fast"] = "L1_MU6";
+      dummyChains["HLT_mu6msonly"] = "L1_MU6";
+      dummyChains["HLT_mu6nol1"] = "L1_MU6";
+      dummyChains["HLT_xe30_L1XE10"] = "L1_XE20";
+      dummyChains["HLT_xe65_L1XE50"] = "L1_XE20";
+
       m_HLTFrame.setMergedHLT( m_setMergedHLT );
       for (const auto& mapPair : dummyChains) {
          const std::string& chainName = mapPair.first;
diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
index 28032b4f2e981d1ee23c7ddbfe7a1596ef36382a..d0e0ed08bc36ebcd08471c279b4b9dab3cebdc15 100644
--- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
+++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
@@ -225,15 +225,6 @@ namespace TrigCompositeUtils {
    **/
   void recursiveGetDecisionsInternal( const Decision* start, const size_t location, std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const DecisionID id = 0);
 
-  /**
-   * @brief Extract features from the supplied linkVector (obtained through recursiveGetDecisions).
-   * @param[in] linkVector Vector of paths through the navigation which are to be considered.
-   * @param[oneFeaturePerLeg] oneFeaturePerLeg If True, stops at the first feature (of the correct type) found per path through the navigation.
-   * @return Typed vector of element links to all features found on the supplied linkVector.
-   **/ 
-  template<class CONTAINER>
-  ElementLinkVector<CONTAINER> getFeaturesOfType( const std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const bool oneFeaturePerLeg = true );
-
   /**
    * @brief Helper to keep the TC & object it has linked together (for convenience)
    **/
@@ -256,6 +247,15 @@ namespace TrigCompositeUtils {
     ElementLink<T> link;
   };
 
+  /**
+   * @brief Extract features from the supplied linkVector (obtained through recursiveGetDecisions).
+   * @param[in] linkVector Vector of paths through the navigation which are to be considered.
+   * @param[oneFeaturePerLeg] oneFeaturePerLeg If True, stops at the first feature (of the correct type) found per path through the navigation.
+   * @return Typed vector of LinkInfo. Each LinkInfo wraps an ElementLink to a feature and a pointer to the feature's Decision object in the navigation.
+   **/
+  template<class CONTAINER>
+  const std::vector< LinkInfo<CONTAINER> > getFeaturesOfType( const std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const bool oneFeaturePerLeg = true );
+
   /**
    * @brief search back the TC links for the object of type T linked to the one of TC (recursively)
    * @arg start the TC  from where the link back is to be looked for
diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.icc b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.icc
index 784b207047d608b9d7110e7eaa600ce9ab0ced36..549323c16767eb914c716bf48f658d3199408ba4 100644
--- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.icc
+++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.icc
@@ -60,20 +60,20 @@ namespace TrigCompositeUtils {
   }
 
   template<class CONTAINER>
-  ElementLinkVector<CONTAINER> getFeaturesOfType(const std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const bool oneFeaturePerLeg) {
-    ElementLinkVector<CONTAINER> features;
+  const std::vector< LinkInfo<CONTAINER> > getFeaturesOfType(const std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const bool oneFeaturePerLeg) {
+    std::vector< LinkInfo<CONTAINER> > features;
     // For each unique path through the navigation for a given chain
     for (const ElementLinkVector<DecisionContainer>& decisionPath : linkVector) {
       // For each step along this path, starting at the terminus and working back towards L1
       for (const ElementLink<DecisionContainer>& decisionLink : decisionPath) {
         const Decision* decision = (*decisionLink);
-        if (decision->hasObjectLink("feature", ClassID_traits< CONTAINER >::ID())) {
+        if (decision->hasObjectLink(featureString(), ClassID_traits< CONTAINER >::ID())) {
 
           // This try block protects against ExcCLIDMismatch throws from 
           // features which do not derive from IParticle, when an IParticle interface is requested.
           try {
-            const ElementLink<CONTAINER> featureLink = decision->objectLink<CONTAINER>("feature");
-            features.push_back( featureLink );
+            const ElementLink<CONTAINER> featureLink = decision->objectLink<CONTAINER>( featureString() );
+            features.push_back( LinkInfo<CONTAINER>(decision, featureLink) );
             if (oneFeaturePerLeg) {
               break;
             }
diff --git a/Trigger/TrigSteer/DecisionHandling/share/TrigCompositeUtils_test.ref b/Trigger/TrigSteer/DecisionHandling/share/TrigCompositeUtils_test.ref
index 7592d0dbb9c221812978d81293782199efd62d61..bee27322b7d9e515ba226410f7ff0a38f7965adf 100644
--- a/Trigger/TrigSteer/DecisionHandling/share/TrigCompositeUtils_test.ref
+++ b/Trigger/TrigSteer/DecisionHandling/share/TrigCompositeUtils_test.ref
@@ -35,26 +35,26 @@ xAOD::TrigComposite_v1::setObjectLink ERROR link is not valid
 
 New decision d3b with name & context 
 d3b: TrigComposite_v1 name:'d3b'
-  N Lnks:1
+  N Links:1, isRemapped:NO
     Link Name:testlink, Key:1042135810, Index:1, CLID:1333228823
 el2 1042135810 1
 d3: TrigComposite_v1 name:''
-  N Lnks:1
+  N Links:1, isRemapped:NO
     Link Name:seed__COLL, Key:1042135810, Index:1, CLID:1333228823
   N Decisions:2
     95, 99, 
 d4: TrigComposite_v1 name:''
-  N Lnks:1
+  N Links:1, isRemapped:NO
     Link Name:seed__COLL, Key:1042135810, Index:1, CLID:1333228823
 el: key 1042135810 index 1
 create d5 
 set link 
 d5: TrigComposite_v1 name:'d5'
-  N Lnks:1
+  N Links:1, isRemapped:NO
     Link Name:feature, Key:1042135810, Index:1, CLID:1333228823
 create d6 
 d6: TrigComposite_v1 name:'d6'
-  N Lnks:1
+  N Links:1, isRemapped:NO
     Link Name:seed__COLL, Key:1042135810, Index:5, CLID:1333228823
 get d5 feature link 
 get d6 feature link 
diff --git a/Trigger/TrigSteer/DecisionHandling/share/TrigTraversal_test.ref b/Trigger/TrigSteer/DecisionHandling/share/TrigTraversal_test.ref
index bb1a91f9ff5119b6a124f65487875477af718712..ea15ceb53b591dff3e2c78aac9bc21988ffe78cd 100644
--- a/Trigger/TrigSteer/DecisionHandling/share/TrigTraversal_test.ref
+++ b/Trigger/TrigSteer/DecisionHandling/share/TrigTraversal_test.ref
@@ -7,24 +7,25 @@ ApplicationMgr Ready
 Context: s: 0  e: 0
 Current context: s: 0  e: 0
 1 paths for HLT_mufast_chain
-  Path 0[HLTPassRaw -> END_MU_H_1__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
-1 paths for HLT_mu_chain
-  Path 0[HLTPassRaw -> END_MU_H_2__MU1 -> MU_H_2__MU1 F(21) -> MU_IM_2__MU1 -> MU_F_2__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
+  Path 0[HLTPassRaw -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
+2 paths for HLT_mu_chain
+  Path 0[HLTPassRaw -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
+  Path 1[HLTPassRaw -> MU_H_2__MU1 F(21) -> MU_IM_2__MU1 -> MU_F_2__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
 2 paths for HLT_mu_em_chain
-  Path 0[HLTPassRaw -> END_MUEM_CH_2__MU1 -> MUEM_CH_2__MU1 -> MUEM_H_2__MU1 F(21) -> MUEM_IM_2__MU1 -> MUEM_F_2__MU1 -> MUEM_CH_1__MU1 -> MUEM_H_1__MU1 F(20) -> MUEM_IM_1__MU1 -> MUEM_F_1__MU1 -> MU1]
-  Path 1[HLTPassRaw -> END_MUEM_CH_2__EM0 -> MUEM_CH_2__EM0 -> MUEM_H_2__EM0 F(31) -> MUEM_IM_2__EM0 -> MUEM_F_2__EM0 -> MUEM_CH_1__EM0 -> MUEM_H_1__EM0 F(30) -> MUEM_IM_1__EM0 -> MUEM_F_1__EM0 -> EM0]
+  Path 0[HLTPassRaw -> MUEM_CH_2__MU1 -> MUEM_H_2__MU1 F(21) -> MUEM_IM_2__MU1 -> MUEM_F_2__MU1 -> MUEM_CH_1__MU1 -> MUEM_H_1__MU1 F(20) -> MUEM_IM_1__MU1 -> MUEM_F_1__MU1 -> MU1]
+  Path 1[HLTPassRaw -> MUEM_CH_2__EM0 -> MUEM_H_2__EM0 F(31) -> MUEM_IM_2__EM0 -> MUEM_F_2__EM0 -> MUEM_CH_1__EM0 -> MUEM_H_1__EM0 F(30) -> MUEM_IM_1__EM0 -> MUEM_F_1__EM0 -> EM0]
 1 paths for HLT_em_chain
-  Path 0[HLTPassRaw -> END_EM_H_2__EM0 -> EM_H_2__EM0 F(31) -> EM_IM_2__EM0 -> EM_F_2__EM0 -> EM_H_1__EM0 F(30) -> EM_IM_1__EM0 -> EM_F_1__EM0 -> EM0]
+  Path 0[HLTPassRaw -> EM_H_2__EM0 F(31) -> EM_IM_2__EM0 -> EM_F_2__EM0 -> EM_H_1__EM0 F(30) -> EM_IM_1__EM0 -> EM_F_1__EM0 -> EM0]
 5 paths for All
-  Path 0[HLTPassRaw -> END_MU_H_1__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
-  Path 1[HLTPassRaw -> END_MU_H_2__MU1 -> MU_H_2__MU1 F(21) -> MU_IM_2__MU1 -> MU_F_2__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
-  Path 2[HLTPassRaw -> END_MUEM_CH_2__MU1 -> MUEM_CH_2__MU1 -> MUEM_H_2__MU1 F(21) -> MUEM_IM_2__MU1 -> MUEM_F_2__MU1 -> MUEM_CH_1__MU1 -> MUEM_H_1__MU1 F(20) -> MUEM_IM_1__MU1 -> MUEM_F_1__MU1 -> MU1]
-  Path 3[HLTPassRaw -> END_EM_H_2__EM0 -> EM_H_2__EM0 F(31) -> EM_IM_2__EM0 -> EM_F_2__EM0 -> EM_H_1__EM0 F(30) -> EM_IM_1__EM0 -> EM_F_1__EM0 -> EM0]
-  Path 4[HLTPassRaw -> END_MUEM_CH_2__EM0 -> MUEM_CH_2__EM0 -> MUEM_H_2__EM0 F(31) -> MUEM_IM_2__EM0 -> MUEM_F_2__EM0 -> MUEM_CH_1__EM0 -> MUEM_H_1__EM0 F(30) -> MUEM_IM_1__EM0 -> MUEM_F_1__EM0 -> EM0]
+  Path 0[HLTPassRaw -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
+  Path 1[HLTPassRaw -> MU_H_2__MU1 F(21) -> MU_IM_2__MU1 -> MU_F_2__MU1 -> MU_H_1__MU1 F(20) -> MU_IM_1__MU1 -> MU_F_1__MU1 -> MU1]
+  Path 2[HLTPassRaw -> MUEM_CH_2__MU1 -> MUEM_H_2__MU1 F(21) -> MUEM_IM_2__MU1 -> MUEM_F_2__MU1 -> MUEM_CH_1__MU1 -> MUEM_H_1__MU1 F(20) -> MUEM_IM_1__MU1 -> MUEM_F_1__MU1 -> MU1]
+  Path 3[HLTPassRaw -> EM_H_2__EM0 F(31) -> EM_IM_2__EM0 -> EM_F_2__EM0 -> EM_H_1__EM0 F(30) -> EM_IM_1__EM0 -> EM_F_1__EM0 -> EM0]
+  Path 4[HLTPassRaw -> MUEM_CH_2__EM0 -> MUEM_H_2__EM0 F(31) -> MUEM_IM_2__EM0 -> MUEM_F_2__EM0 -> MUEM_CH_1__EM0 -> MUEM_H_1__EM0 F(30) -> MUEM_IM_1__EM0 -> MUEM_F_1__EM0 -> EM0]
 [All features] HLT_mufast_chain features size:1
  Feature 0:20,
-[All features] HLT_mu_chain features size:2
- Feature 0:21, Feature 1:20,
+[All features] HLT_mu_chain features size:3
+ Feature 0:20, Feature 1:21, Feature 2:20,
 [All features] HLT_mu_em_chain features size:4
  Feature 0:21, Feature 1:20, Feature 2:31, Feature 3:30,
 [All features] HLT_em_chain features size:2
@@ -33,8 +34,8 @@ Current context: s: 0  e: 0
  Feature 0:20, Feature 1:21, Feature 2:20, Feature 3:21, Feature 4:20, Feature 5:31, Feature 6:30, Feature 7:31, Feature 8:30,
 [Final feature] HLT_mufast_chain features size:1
  Feature 0:20,
-[Final feature] HLT_mu_chain features size:1
- Feature 0:21,
+[Final feature] HLT_mu_chain features size:2
+ Feature 0:20, Feature 1:21,
 [Final feature] HLT_mu_em_chain features size:2
  Feature 0:21, Feature 1:31,
 [Final feature] HLT_em_chain features size:1
diff --git a/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx b/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
index ffd4959b554d5b743547b929bd9319488b5d900d..d83e54bbe65947f99db8cedd23fc278d98360820 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
@@ -169,12 +169,9 @@ namespace TrigCompositeUtils {
 
   void recursiveGetDecisionsInternal(const Decision* start, const size_t location, std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const DecisionID id) {
     // Does this Decision satisfy the chain requirement?
-    // Don't check this for HLTPassRaw or HLTRerun. These inital nodes have empty sets
-    if (start->name() != "HLTPassRaw" && start->name() != "HLTRerun") {
-      DecisionIDContainer idSet = {id};
-      if (id != 0 && !isAnyIDPassing(start, idSet)) {
-        return; // Stop propagating down this leg. It does not concern the chain with DecisionID = id
-      }
+    DecisionIDContainer idSet = {id};
+    if (id != 0 && !isAnyIDPassing(start, idSet)) {
+      return; // Stop propagating down this leg. It does not concern the chain with DecisionID = id
     }
 
     // This Decision object is part of this linear path through the Navigation
@@ -211,7 +208,7 @@ namespace TrigCompositeUtils {
   }
 
   void recursiveGetDecisions(const Decision* start, std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const DecisionID id) {
-    // Note: we do not require recursiveGetDecisions to be an empty vector. We can append to it.
+    // Note: we do not require linkVector to be an empty vector. We can append to it.
     linkVector.push_back( ElementLinkVector<DecisionContainer>() ); // Our starting point
     const size_t startingElement = linkVector.size() - 1;
     recursiveGetDecisionsInternal(start, startingElement, linkVector, id);
diff --git a/Trigger/TrigSteer/DecisionHandling/test/TrigTraversal_test.cxx b/Trigger/TrigSteer/DecisionHandling/test/TrigTraversal_test.cxx
index bf9b0841d146486933d01edf9aae2fd371e4709a..edecd00b0bee1ae79a0cf987cf591f3e86079fe8 100644
--- a/Trigger/TrigSteer/DecisionHandling/test/TrigTraversal_test.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/test/TrigTraversal_test.cxx
@@ -28,7 +28,7 @@ using TrigCompositeUtils::DecisionContainer;
 void printPaths(const std::vector<ElementLinkVector<DecisionContainer>>& paths, const std::string& name);
 
 template<class CONTAINER>
-void printFeatures(const ElementLinkVector<CONTAINER>& featureContainer, const std::string& name);
+void printFeatures(const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> >& featureContainer, const std::string& name);
 
 /// @brief Test to check traversal functions of a graph of interconnect TrigComposite objects
 ///
@@ -205,12 +205,23 @@ int main ATLAS_NOT_THREAD_SAFE () {
     MU_H_1__MU1->setObjectLink<xAOD::MuonContainer>("feature", rec_1__mu1_link);
     addDecisionID(HLT_mufast_chain, MU_H_1__MU1);
     addDecisionID(HLT_mu_chain, MU_H_1__MU1);
-
     // HLT_mufast_chain passes the event
-    Decision* END_MU_H_1__MU1 = newDecisionIn(decisionContainerPtr, "END_MU_H_1__MU1");
-    linkToPrevious(END_MU_H_1__MU1, MU_H_1__MU1);
-    addDecisionID(HLT_mufast_chain, END_MU_H_1__MU1);
-    linkToPrevious(END, END_MU_H_1__MU1);
+    addDecisionID(HLT_mufast_chain, END);
+    linkToPrevious(END, MU_H_1__MU1);
+
+    /// !!!
+    /// !!!
+    /// !!! When finding the first feature for the HLT_mu_chain, the navigation is here given two entry points into the graph,
+    /// !!! the correct one (MU_H_2__MU1) and an incorrect one (MU_H_1__MU1) due to HLT_mufast_chain also passing this event
+    /// !!! at this earlier Step.
+    /// !!!
+    /// !!! The feature access is currently "dumb", it doesn't know what should be the final step for each chain. Hence here
+    /// !!! it will return both the Step1 muon and the Step2 muon when asked for the first feature down each leg for HLT_mu_chain,
+    /// !!! Rather than just the Step2 muon.
+    /// !!!
+    /// !!! This will be corrected later, once the Trigger Decision Tool has access to more menu reflection information.
+    /// !!!
+    /// !!!
 
     Decision* MU_F_2__MU1 = newDecisionIn(decisionContainerPtr, "MU_F_2__MU1");
     linkToPrevious(MU_F_2__MU1, MU_H_1__MU1);
@@ -224,12 +235,9 @@ int main ATLAS_NOT_THREAD_SAFE () {
     linkToPrevious(MU_H_2__MU1, MU_IM_2__MU1);
     MU_H_2__MU1->setObjectLink<xAOD::MuonContainer>("feature", rec_2__mu1_link);
     addDecisionID(HLT_mu_chain, MU_H_2__MU1);
-
     // HLT_mu_chain passes the event
-    Decision* END_MU_H_2__MU1 = newDecisionIn(decisionContainerPtr, "END_MU_H_2__MU1");
-    linkToPrevious(END_MU_H_2__MU1, MU_H_2__MU1);
-    addDecisionID(HLT_mu_chain, END_MU_H_2__MU1);
-    linkToPrevious(END, END_MU_H_2__MU1);
+    addDecisionID(HLT_mu_chain, END);
+    linkToPrevious(END, MU_H_2__MU1);
   }
 
   ///
@@ -272,12 +280,10 @@ int main ATLAS_NOT_THREAD_SAFE () {
     Decision* MUEM_CH_2__MU1 = newDecisionIn(decisionContainerPtr, "MUEM_CH_2__MU1");
     linkToPrevious(MUEM_CH_2__MU1, MUEM_H_2__MU1);
     addDecisionID(HLT_mu_em_chain, MUEM_CH_2__MU1);
-
     // HLT_mu_em_chain passes the event
-    Decision* END_MUEM_CH_2__MU1 = newDecisionIn(decisionContainerPtr, "END_MUEM_CH_2__MU1");
-    linkToPrevious(END_MUEM_CH_2__MU1, MUEM_CH_2__MU1);
-    addDecisionID(HLT_mu_em_chain, END_MUEM_CH_2__MU1);
-    linkToPrevious(END, END_MUEM_CH_2__MU1);
+    addDecisionID(HLT_mu_em_chain, END);
+    linkToPrevious(END, MUEM_CH_2__MU1);
+
   }
 
   ///
@@ -312,12 +318,9 @@ int main ATLAS_NOT_THREAD_SAFE () {
     linkToPrevious(EM_H_2__EM0, EM_IM_2__EM0);
     EM_H_2__EM0->setObjectLink<xAOD::ElectronContainer>("feature", rec_2__em0_link);
     addDecisionID(HLT_em_chain, EM_H_2__EM0);
-
     // HLT_em_chain passes the event
-    Decision* END_EM_H_2__EM0 = newDecisionIn(decisionContainerPtr, "END_EM_H_2__EM0");
-    linkToPrevious(END_EM_H_2__EM0, EM_H_2__EM0);
-    addDecisionID(HLT_em_chain, END_EM_H_2__EM0);
-    linkToPrevious(END, END_EM_H_2__EM0);
+    linkToPrevious(END, EM_H_2__EM0);
+    addDecisionID(HLT_em_chain, END);
   }
 
   ///
@@ -360,12 +363,9 @@ int main ATLAS_NOT_THREAD_SAFE () {
     Decision* MUEM_CH_2__EM0 = newDecisionIn(decisionContainerPtr, "MUEM_CH_2__EM0");
     linkToPrevious(MUEM_CH_2__EM0, MUEM_H_2__EM0);
     addDecisionID(HLT_mu_em_chain, MUEM_CH_2__EM0);
-
     // HLT_mu_em_chain passes the event
-    Decision* END_MUEM_CH_2__EM0 = newDecisionIn(decisionContainerPtr, "END_MUEM_CH_2__EM0");
-    linkToPrevious(END_MUEM_CH_2__EM0, MUEM_CH_2__EM0);
-    addDecisionID(HLT_mu_em_chain, END_MUEM_CH_2__EM0);
-    linkToPrevious(END, END_MUEM_CH_2__EM0);
+    addDecisionID(HLT_mu_em_chain, END);
+    linkToPrevious(END, MUEM_CH_2__EM0);
   }
 
   // Test the graph
@@ -388,11 +388,11 @@ int main ATLAS_NOT_THREAD_SAFE () {
   printPaths(paths_HLT_em_chain, "HLT_em_chain");
   printPaths(paths_HLT_all, "All");
 
-  ElementLinkVector<xAOD::IParticleContainer> features_all_HLT_mufast_chain = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mufast_chain, false);
-  ElementLinkVector<xAOD::IParticleContainer> features_all_HLT_mu_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_chain, false);
-  ElementLinkVector<xAOD::IParticleContainer> features_all_HLT_mu_em_chain  = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_em_chain, false);
-  ElementLinkVector<xAOD::IParticleContainer> features_all_HLT_em_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_em_chain, false);
-  ElementLinkVector<xAOD::IParticleContainer> features_all_HLT_all          = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_all, false);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_all_HLT_mufast_chain = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mufast_chain, false);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_all_HLT_mu_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_chain, false);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_all_HLT_mu_em_chain  = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_em_chain, false);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_all_HLT_em_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_em_chain, false);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_all_HLT_all          = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_all, false);
 
   printFeatures(features_all_HLT_mufast_chain, "[All features] HLT_mufast_chain");
   printFeatures(features_all_HLT_mu_chain, "[All features] HLT_mu_chain");
@@ -400,11 +400,11 @@ int main ATLAS_NOT_THREAD_SAFE () {
   printFeatures(features_all_HLT_em_chain, "[All features] HLT_em_chain");
   printFeatures(features_all_HLT_all, "[All features] All chains");
 
-  ElementLinkVector<xAOD::IParticleContainer> features_final_HLT_mufast_chain = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mufast_chain, true);
-  ElementLinkVector<xAOD::IParticleContainer> features_final_HLT_mu_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_chain, true);
-  ElementLinkVector<xAOD::IParticleContainer> features_final_HLT_mu_em_chain  = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_em_chain, true);
-  ElementLinkVector<xAOD::IParticleContainer> features_final_HLT_em_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_em_chain, true);
-  ElementLinkVector<xAOD::IParticleContainer> features_final_HLT_all          = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_all, true);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_final_HLT_mufast_chain = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mufast_chain, true);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_final_HLT_mu_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_chain, true);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_final_HLT_mu_em_chain  = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_mu_em_chain, true);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_final_HLT_em_chain     = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_em_chain, true);
+  std::vector< LinkInfo<xAOD::IParticleContainer> > features_final_HLT_all          = getFeaturesOfType<xAOD::IParticleContainer>(paths_HLT_all, true);
 
   printFeatures(features_final_HLT_mufast_chain, "[Final feature] HLT_mufast_chain");
   printFeatures(features_final_HLT_mu_chain, "[Final feature] HLT_mu_chain");
@@ -413,8 +413,8 @@ int main ATLAS_NOT_THREAD_SAFE () {
   printFeatures(features_final_HLT_all, "[Final feature] All chains");  
 
   // Check typed retrieval too
-  ElementLinkVector<xAOD::MuonContainer>     features_final_mu  = getFeaturesOfType<xAOD::MuonContainer>(paths_HLT_mu_em_chain);
-  ElementLinkVector<xAOD::ElectronContainer> features_final_em  = getFeaturesOfType<xAOD::ElectronContainer>(paths_HLT_mu_em_chain);
+  std::vector< LinkInfo<xAOD::MuonContainer> >     features_final_mu  = getFeaturesOfType<xAOD::MuonContainer>(paths_HLT_mu_em_chain);
+  std::vector< LinkInfo<xAOD::ElectronContainer> > features_final_em  = getFeaturesOfType<xAOD::ElectronContainer>(paths_HLT_mu_em_chain);
   printFeatures(features_final_mu, "[Explicit Final Muon Features] HLT_mu_em_chain");
   printFeatures(features_final_em, "[Explicit Final Electron Features] HLT_mu_em_chain");  
 
@@ -427,11 +427,11 @@ int main ATLAS_NOT_THREAD_SAFE () {
 }
 
 template<class CONTAINER>
-void printFeatures(const ElementLinkVector<CONTAINER>& featureContainer, const std::string& name) {
+void printFeatures(const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> >& featureContainer, const std::string& name) {
   std::cout << name << " features size:" << featureContainer.size() << std::endl;
   size_t count = 0;
-  for (ElementLink<CONTAINER> featureLink : featureContainer) {
-    std::cout << " Feature " << count++ << ":" << (*featureLink)->pt() << ",";
+  for (const TrigCompositeUtils::LinkInfo<CONTAINER>& featureLinkInfo : featureContainer) {
+    std::cout << " Feature " << count++ << ":" << (*featureLinkInfo.link)->pt() << ",";
   }
   std::cout << std::endl;
 }
diff --git a/Trigger/TrigSteer/TrigOutputHandling/src/DecisionSummaryMakerAlg.cxx b/Trigger/TrigSteer/TrigOutputHandling/src/DecisionSummaryMakerAlg.cxx
index 122b4fcb734e1d53bba86bbd9c3ffcbc71d92095..e3f91b0a20e9b7b1fa9eb0c523609b65de666d2c 100644
--- a/Trigger/TrigSteer/TrigOutputHandling/src/DecisionSummaryMakerAlg.cxx
+++ b/Trigger/TrigSteer/TrigOutputHandling/src/DecisionSummaryMakerAlg.cxx
@@ -76,16 +76,17 @@ StatusCode DecisionSummaryMakerAlg::execute(const EventContext& context) const {
         continue;
       }
 
-      // Copy decisions set into passRawOutput's persistent vector
-      decisionIDs(passRawOutput).insert( decisionIDs(passRawOutput).end(),
-        passingFinalIDs.begin(), passingFinalIDs.end() ); 
-
-      if (msgLvl(MSG::DEBUG)) {
-        allPassingFinalIDs.insert( passingFinalIDs.begin(), passingFinalIDs.end() );
-      }
+      // Accumulate and de-duplicate passed IDs for which this hypo was the Chain's final step
+      allPassingFinalIDs.insert( passingFinalIDs.begin(), passingFinalIDs.end() );
+      // Create seed links for the navigation to follow
+      linkToPrevious(passRawOutput, decisionObject, context);
     }
   }
 
+  // Copy decisions set into passRawOutput's persistent vector
+  decisionIDs(passRawOutput).insert( decisionIDs(passRawOutput).end(),
+    allPassingFinalIDs.begin(), allPassingFinalIDs.end() );
+
   if (msgLvl(MSG::DEBUG)) {
     ATH_MSG_DEBUG( "Number of positive decisions " <<  allPassingFinalIDs.size() << " passing chains");
     for ( auto d: allPassingFinalIDs ) {
diff --git a/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.cxx b/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.cxx
index e3237abefebfa56706ae26df4113501909ed9dbb..30b5de790b6c8104bbd5baa414fc4670434bb9df 100644
--- a/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.cxx
+++ b/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.cxx
@@ -10,8 +10,8 @@
 #include "StoreGate/WriteDecorHandle.h"
 
 HLTEDMCreator::HLTEDMCreator( const std::string& type, 
-			      const std::string& name, 
-			      const IInterface* parent )
+            const std::string& name,
+            const IInterface* parent )
   : base_class( type, name, parent ) {}
 
 template<typename T>
@@ -65,12 +65,16 @@ StatusCode HLTEDMCreator::initialize()
 #undef INIT
 #undef INIT_XAOD
 
-  if ( m_fixLinks ) {
-    for ( auto writeHandleKey: m_TrigCompositeContainer ) {
-      m_remapLinkCollKeys.emplace_back( writeHandleKey.key()+".remap_linkCollKeys" );
-      m_remapLinkColIndices.emplace_back( writeHandleKey.key()+".remap_linkCollIndices" );
+  if ( m_fixLinks.size() > 0 ) {
+    for ( const auto& writeHandleKey: m_TrigCompositeContainer ) {
+      const bool doFixLinks = std::any_of(m_fixLinks.begin(), m_fixLinks.end(), [&](const std::string& s) { return s == writeHandleKey.key(); } );
+      if (doFixLinks) {
+        // This writeHandleKey is being included in the element link remapping
+        m_remapLinkColKeys.emplace_back( writeHandleKey.key()+".remap_linkColKeys" );
+        m_remapLinkColIndices.emplace_back( writeHandleKey.key()+".remap_linkColIndices" );
+      }
     }
-    ATH_CHECK( m_remapLinkCollKeys.initialize() ) ;
+    ATH_CHECK( m_remapLinkColKeys.initialize() ) ;
     ATH_CHECK( m_remapLinkColIndices.initialize() );
   }
   
@@ -106,7 +110,7 @@ struct xAODGenerator {
 
 template<typename T>
 StatusCode  HLTEDMCreator::noMerge( ViewContainer const&, const SG::ReadHandleKey<T>&,
-				    EventContext const&, T & ) const {
+            EventContext const&, T & ) const {
   //  if we are called it means views merging is requested but Type T does not support it (i.e. missing copy c'tor)
   return StatusCode::FAILURE;
 
@@ -114,7 +118,7 @@ StatusCode  HLTEDMCreator::noMerge( ViewContainer const&, const SG::ReadHandleKe
 
 template<typename T>
 StatusCode  HLTEDMCreator::viewsMerge( ViewContainer const& views, const SG::ReadHandleKey<T>& inViewKey,
-				       EventContext const& context, T & output ) const {
+               EventContext const& context, T & output ) const {
   
   typedef typename T::base_value_type type_in_container;
   StoreGateSvc* sg = evtStore().operator->(); // why the get() method is returing a null ptr is a puzzle, we have to use this ugly call to operator instead of it
@@ -128,68 +132,75 @@ StatusCode  HLTEDMCreator::viewsMerge( ViewContainer const& views, const SG::Rea
  
 StatusCode HLTEDMCreator::fixLinks( const ConstHandlesGroup< xAOD::TrigCompositeContainer >& handles ) const {
 
-  // Make a list of the collections we're going to mess with
-  // std::set< std::string > remappedCollections;
-  // for ( auto writeHandleKey : handles.out ) {
-  //   remappedCollections.insert( writeHandleKey.key() );
-  // }
-
   static const SG::AuxElement::ConstAccessor< std::vector< uint32_t > > keyAccessor( "linkColKeys" );
-  static const SG::AuxElement::ConstAccessor< std::vector< uint16_t > > offsetAccessor( "linkColIndices" );
+  static const SG::AuxElement::ConstAccessor< std::vector< uint16_t > > indexAccessor( "linkColIndices" );
 
   ATH_MSG_DEBUG("Fixing links called for " << handles.out.size() << " object(s)");
 
   // Do the remapping
-  int index = -1;
+  int writeHandleArrayIndex = -1;
   for ( auto writeHandleKey : handles.out ) {
-    index++;
-    ATH_MSG_DEBUG("Fixing links: see if collection is there: " << writeHandleKey.key() << " index " << index);
+    ++writeHandleArrayIndex;
+    // Check if we are re-mapping this handle
+    const bool doFixLinks = std::any_of(m_fixLinks.begin(), m_fixLinks.end(), [&](const std::string& s) { return s == writeHandleKey.key(); } );
+    if ( not doFixLinks ) {
+      ATH_MSG_DEBUG("Not requested to fix TrigComposite ElementLinks for " << writeHandleKey.key());
+      continue;
+    }
+
+    ATH_MSG_DEBUG("Fixing links: see if collection is there: " << writeHandleKey.key() << ", write hand array index: " << writeHandleArrayIndex);
     SG::ReadHandle<xAOD::TrigCompositeContainer> readHandle( writeHandleKey.key() );
     if ( not readHandle.isValid() ) { // object missing, ok, may be early rejection
       continue;
     }
 
-    ATH_MSG_DEBUG("Fixing links: collection is there: " << writeHandleKey.key() );
-    ATH_MSG_DEBUG("Adding decorations: " << m_remapLinkCollKeys.at( index ).key() << " and " << m_remapLinkColIndices.at( index ).key() );
+    ATH_MSG_DEBUG("Fixing links: collection exists: " << writeHandleKey.key() << " with size " << readHandle->size() << " Decision objects" );
+    ATH_MSG_DEBUG("Adding decorations: " << m_remapLinkColKeys.at( writeHandleArrayIndex ).key() << " and " << m_remapLinkColIndices.at( writeHandleArrayIndex ).key() );
     
-    SG::WriteDecorHandle<xAOD::TrigCompositeContainer, std::vector<uint32_t> > keyDecor( m_remapLinkCollKeys.at( index ) );
-    SG::WriteDecorHandle<xAOD::TrigCompositeContainer, std::vector<uint16_t> > offsetDecor( m_remapLinkColIndices.at( index ) );
+    SG::WriteDecorHandle<xAOD::TrigCompositeContainer, std::vector<uint32_t> > keyDecor( m_remapLinkColKeys.at( writeHandleArrayIndex ) );
+    SG::WriteDecorHandle<xAOD::TrigCompositeContainer, std::vector<uint16_t> > indexDecor( m_remapLinkColIndices.at( writeHandleArrayIndex ) );
 
-    
     // Examine each input TC
+    int decisionObjectIndex = -1;
     for ( auto inputDecision : *( readHandle.cptr() ) ) {
+      ++decisionObjectIndex;
 
-      
       // Retrieve the link information for remapping
       std::vector< uint32_t > remappedKeys = keyAccessor( *inputDecision );
-      std::vector< uint16_t > remappedOffsets = offsetAccessor( *inputDecision );
+      std::vector< uint16_t > remappedIndexes = indexAccessor( *inputDecision );
 
       // Search the linked collections for remapping
-      unsigned int const collectionTotal = inputDecision->linkColNames().size();
-      for ( unsigned int collectionIndex = 0; collectionIndex < collectionTotal; ++collectionIndex ) {
-
-  	// Load identifiers
-  	std::string const collectionName = inputDecision->linkColNames()[ collectionIndex ];
-  	uint32_t const collectionKey = inputDecision->linkColKeys()[ collectionIndex ];
-  	std::string const keyString = *( evtStore()->keyToString( collectionKey ) );
-  	uint16_t const collectionOffset = inputDecision->linkColIndices()[ collectionIndex ];
-	
-  	// Check for remapping in a merge
-  	uint32_t newKey = 0;
-  	size_t newOffset = 0;
-  	bool isRemapped = evtStore()->tryELRemap( collectionKey, collectionOffset, newKey, newOffset);
-  	if ( isRemapped ) {
-	  
-  	  ATH_MSG_DEBUG( "Remap link from " << *( evtStore()->keyToString( collectionKey ) ) << " to " << *( evtStore()->keyToString( newKey ) ) );
-  	  remappedKeys[ collectionIndex ] = newKey;
-  	  remappedOffsets[ collectionIndex ] = newOffset;
-  	}
-	
+      size_t const collectionTotal = inputDecision->linkColNames().size();
+      ATH_MSG_DEBUG("Decision object #" << decisionObjectIndex << " has " << collectionTotal << " links");
+      for ( size_t elementLinkIndex = 0; elementLinkIndex < collectionTotal; ++elementLinkIndex ) {
+
+        // Load ElementLink identifiers (except for CLID)
+        std::string const collectionName = inputDecision->linkColNames().at(elementLinkIndex);
+        uint32_t const collectionKey = remappedKeys.at(elementLinkIndex); //Note: This is the existing before-remap key
+        std::string const keyString = *( evtStore()->keyToString( collectionKey ) );
+        uint16_t const collectionIndex = remappedIndexes.at(elementLinkIndex); //Note: This is the existing before-remap index
+
+        // Check for remapping in a merge
+        uint32_t newKey = 0;
+        size_t newIndex = 0;
+        bool isRemapped = evtStore()->tryELRemap( collectionKey, collectionIndex, newKey, newIndex);
+        if ( isRemapped ) {
+
+          ATH_MSG_DEBUG( "Remap link [" << collectionName <<"] from " << keyString << " to " << *( evtStore()->keyToString( newKey ) ) << ", from index " << collectionIndex << " to index " << newIndex );
+          remappedKeys[ elementLinkIndex ] = newKey;
+          remappedIndexes[ elementLinkIndex ] = newIndex;
+
+        } else {
+
+          ATH_MSG_DEBUG( "StoreGate did not remap link [" << collectionName << "] from  " << keyString << " index " << collectionIndex );
+
+        }
+
       }
       
       // Save the remaps
       keyDecor( *inputDecision ) = remappedKeys;
-      offsetDecor( *inputDecision ) = remappedOffsets;
+      indexDecor( *inputDecision ) = remappedIndexes;
 
     }    
   }  
@@ -203,33 +214,33 @@ StatusCode HLTEDMCreator::createIfMissing( const EventContext& context, const Co
   for ( auto writeHandleKey : handles.out ) {
     
     SG::ReadHandle<T> readHandle( writeHandleKey.key() );
-    
+
     if ( readHandle.isValid() ) {
       ATH_MSG_DEBUG( "The " << writeHandleKey.key() << " already present" );
-    } else {      
+    } else {
       ATH_MSG_DEBUG( "The " << writeHandleKey.key() << " was absent, creating it" );
       generator.create();      
       if ( handles.views.size() != 0 ) {
 
-	ATH_MSG_DEBUG("Will be trying to merge from " << handles.views.size() << " view containers into that output");
-	auto viewCollKeyIter = handles.views.begin();
-	auto inViewCollKeyIter = handles.in.begin();
-	
-	for ( ; viewCollKeyIter != handles.views.end(); ++viewCollKeyIter, ++inViewCollKeyIter ) {
-	  // get the views handle
-
-	  auto viewsHandle = SG::makeHandle( *viewCollKeyIter, context );
-	  if ( viewsHandle.isValid() ) {	    
-	    ATH_MSG_DEBUG("Will be merging from " << viewsHandle->size() << " views " << viewCollKeyIter->key() << " view container using key " << inViewCollKeyIter->key() );
-	    CHECK( (this->*merger)( *viewsHandle, *inViewCollKeyIter , context, *generator.data.get() ) );
-	  } else {
-	    ATH_MSG_DEBUG("Views " << viewCollKeyIter->key() << " are missing");
-	  }
-	}      
+        ATH_MSG_DEBUG("Will be trying to merge from " << handles.views.size() << " view containers into that output");
+        auto viewCollKeyIter = handles.views.begin();
+        auto inViewCollKeyIter = handles.in.begin();
+
+        for ( ; viewCollKeyIter != handles.views.end(); ++viewCollKeyIter, ++inViewCollKeyIter ) {
+          // get the views handle
+
+          auto viewsHandle = SG::makeHandle( *viewCollKeyIter, context );
+          if ( viewsHandle.isValid() ) {
+            ATH_MSG_DEBUG("Will be merging from " << viewsHandle->size() << " views " << viewCollKeyIter->key() << " view container using key " << inViewCollKeyIter->key() );
+            CHECK( (this->*merger)( *viewsHandle, *inViewCollKeyIter , context, *generator.data.get() ) );
+          } else {
+            ATH_MSG_DEBUG("Views " << viewCollKeyIter->key() << " are missing");
+          }
+        }
       }
       auto writeHandle = SG::makeHandle( writeHandleKey, context );
       CHECK( generator.record( writeHandle ) );
-    }     
+    }
   }
   return StatusCode::SUCCESS;
 }
@@ -237,12 +248,13 @@ StatusCode HLTEDMCreator::createIfMissing( const EventContext& context, const Co
 
 
 StatusCode HLTEDMCreator::createOutput(const EventContext& context) const {
+  ATH_MSG_DEBUG("Confirming / Creating this tool's output");
   if ( m_dumpSGBefore )  
     ATH_MSG_DEBUG( evtStore()->dump() );
 
 #define CREATE(__TYPE) \
-    {									\
-      plainGenerator<__TYPE> generator;					\
+    {                 \
+      plainGenerator<__TYPE> generator;         \
       CHECK( createIfMissing<__TYPE>( context, ConstHandlesGroup<__TYPE>( m_##__TYPE, m_##__TYPE##InViews, m_##__TYPE##Views ), generator, &HLTEDMCreator::noMerge<__TYPE> ) ); \
     }
 
@@ -257,7 +269,7 @@ StatusCode HLTEDMCreator::createOutput(const EventContext& context) const {
   }
 
 
-#define CREATE_XAOD_NO_MERGE(__TYPE, __STORE_TYPE)			\
+#define CREATE_XAOD_NO_MERGE(__TYPE, __STORE_TYPE)      \
   { \
     xAODGenerator<xAOD::__TYPE, xAOD::__STORE_TYPE> generator; \
     CHECK( createIfMissing<xAOD::__TYPE>( context, ConstHandlesGroup<xAOD::__TYPE>( m_##__TYPE, m_##__TYPE##InViews, m_##__TYPE##Views ), generator, &HLTEDMCreator::noMerge<xAOD::__TYPE> )  ); \
@@ -279,7 +291,7 @@ StatusCode HLTEDMCreator::createOutput(const EventContext& context) const {
 
   CREATE_XAOD( CaloClusterContainer, CaloClusterAuxContainer );
   // After view collections are merged, need to update collection links
-  if ( m_fixLinks ) {
+  if ( m_fixLinks.size() > 0 ) {
     ATH_CHECK( fixLinks( ConstHandlesGroup<xAOD::TrigCompositeContainer>( m_TrigCompositeContainer, m_TrigCompositeContainerInViews, m_TrigCompositeContainerViews ) ) );
   }
   
diff --git a/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.h b/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.h
index 09e0c9ab702d3cfa434b9acfcbbbb12e2c6ca8db..0acdd10f7edd36a0234d26357e7d2e65475461ef 100644
--- a/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.h
+++ b/Trigger/TrigSteer/TrigOutputHandling/src/HLTEDMCreator.h
@@ -75,9 +75,9 @@ class HLTEDMCreator: public extends<AthAlgTool, IHLTOutputTool>  {
  private: 
 
   HLTEDMCreator();
-  Gaudi::Property<bool> m_fixLinks{ this, "FixLinks", false, "Fix links that may be pointing objects in views"};
-  SG::WriteDecorHandleKeyArray<xAOD::TrigCompositeContainer, std::vector<uint32_t> > m_remapLinkCollKeys{ this, "DoNotSet_RemapLinkCollKeys", {}, "Do not set, it is configured accordingly to FixLinks & TC output property"};
-  SG::WriteDecorHandleKeyArray<xAOD::TrigCompositeContainer, std::vector<uint16_t> > m_remapLinkColIndices{ this, "DoNotSet_RemapLinkCollIndices", {}, "Do not set, it is configured accordingly to FixLinks & TC output property"};
+  Gaudi::Property<std::vector<std::string>> m_fixLinks{ this, "FixLinks", {}, "Which keys of the TrigCompositeContainer WriteHandleKeyArray might need to have their (e.g. feature) element links re-mapped outside of views"};
+  SG::WriteDecorHandleKeyArray<xAOD::TrigCompositeContainer, std::vector<uint32_t> > m_remapLinkColKeys{ this, "DoNotSet_RemapLinkColKeys", {}, "Do not set, it is configured accordingly to FixLinks & TC output property"};
+  SG::WriteDecorHandleKeyArray<xAOD::TrigCompositeContainer, std::vector<uint16_t> > m_remapLinkColIndices{ this, "DoNotSet_RemapLinkColIndices", {}, "Do not set, it is configured accordingly to FixLinks & TC output property"};
 
   Gaudi::Property<bool> m_dumpSGBefore{ this, "dumpSGBefore", false, "Dump SG content before the merging"}; // for debugging 
   Gaudi::Property<bool> m_dumpSGAfter { this, "dumpSGAfter", false, "Dump SG content after the merging"};
diff --git a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
index c878ea1781440c17cf68a4faf844a9e31a92d552..bbaf5295faa100bc2c6e56b97dcb1d3c352d8896 100644
--- a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
+++ b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
@@ -2,6 +2,9 @@
 #  Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration
 #
 
+from AthenaCommon.Logging import logging
+__log = logging.getLogger('full_menu')
+
 # import flags
 from RecExConfig.RecFlags  import rec
 rec.doESD=True
@@ -270,8 +273,8 @@ for a in AthSequencer("HLTAllSteps").getChildren():
 # this part uses parts from the NewJO configuration, it is very hacky for the moment
 
 from TriggerJobOpts.TriggerConfig import collectHypos, collectFilters, collectDecisionObjects, triggerOutputStreamCfg
-hypos = collectHypos(topSequence)
-filters = collectFilters(topSequence)
+hypos = collectHypos(AthSequencer("HLTAllSteps"))
+filters = collectFilters(AthSequencer("HLTAllSteps"))
 
 # try to find L1Decoder
 from AthenaCommon.CFElements import findAlgorithm,findSubSequence
@@ -281,7 +284,8 @@ if not l1decoder:
 
 if l1decoder:
     decObj = collectDecisionObjects( hypos, filters, l1decoder )
-    print decObj
+    __log.debug("Decision Objects to export to ESD [hack method - should be replaced with triggerRunCfg()]")
+    __log.debug(decObj)
 
     from TrigEDMConfig.TriggerEDMRun3 import TriggerHLTList
     ItemList  = [ 'xAOD::TrigCompositeContainer#{}'.format(d) for d in decObj ]
diff --git a/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx b/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
index 51e75508b14633e75b5ca0b289ad14fed36238b5..bb371fdab6106a83c52d468ab8a98825d7d2c6ee 100644
--- a/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
+++ b/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
@@ -55,8 +55,6 @@
 #include "tauEvent/TauJetContainer.h"
 #include "tauEvent/TauJet.h"
 
-#include "xAODEventInfo/EventInfo.h"
-
 #include "xAODMuon/MuonContainer.h"
 #include "MuonCombinedToolInterfaces/IMuonPrintingTool.h"
 
@@ -99,6 +97,8 @@
 
 #include "TrigT1Interfaces/RecEmTauRoI.h"
 
+
+
 #include "AthViews/ViewHelper.h"
 #include "AthViews/View.h"
 
@@ -559,12 +559,8 @@ StatusCode TrigEDMChecker::execute() {
   if (m_doDumpAll || m_doDumpTrigCompsiteNavigation) {
     std::string trigCompositeSteering;
     ATH_CHECK(TrigCompositeNavigationToDot(trigCompositeSteering));
-    const xAOD::EventInfo* evtInfo = nullptr;
-    if (evtStore()->contains<xAOD::EventInfo>("EventInfo")) {
-      ATH_CHECK( evtStore()->retrieve(evtInfo) );
-    }
-    static int eventStatic = 0; // Might not always have EventInfo (early testing of Run-3 software)
-    const std::string evtNumber = (evtInfo == nullptr ? std::to_string(eventStatic++) : std::to_string(evtInfo->eventNumber()));
+    const EventContext& context = Gaudi::Hive::currentContext();
+    const std::string evtNumber = std::to_string(context.eventID().event_number());
     std::ofstream ofile(std::string("NavigationGraph_" + evtNumber + ".dot").c_str());
     ofile << trigCompositeSteering;
   }
@@ -4015,17 +4011,24 @@ StatusCode TrigEDMChecker::dumpxAODVertex() {
 }
 
 StatusCode TrigEDMChecker::dumpTDT() {
+  using namespace TrigCompositeUtils; // LinkInfo
   ATH_MSG_INFO( "REGTEST ==========START of TDT DUMP===========" );
   // Note: This minimal TDT dumper is for use during run-3 dev
   std::vector<std::string> confChains = m_trigDec->getListOfTriggers("HLT_.*");
   for (const auto& item : confChains) {
     bool passed = m_trigDec->isPassed(item, TrigDefs::requireDecision);
-    ATH_MSG_INFO("  HLT Item " << item << " passed raw? " << passed);
-    // TODO Enable this section after !22102 is merged 
-    // if (passed) {
-    //   ElementLinkVector<IParticleContainer> features = m_trigDec->features<IParticleContainer>(item);
-    //   ATH_MSG_INFO("    " << item << " features size: " << features.size());
-    // }
+    ATH_MSG_INFO("  HLT Item " << item << " (numeric ID " << TrigConf::HLTUtils::string2hash(item, "Identifier") << ") passed raw? " << passed);
+    if (passed) {
+      std::vector< LinkInfo<xAOD::IParticleContainer> > features = m_trigDec->features<xAOD::IParticleContainer>(item);
+      ATH_MSG_INFO("    " << item << " IParticle features size: " << features.size());
+      for (const LinkInfo<xAOD::IParticleContainer>& li : features) {
+        if (!li.isValid()) {
+          ATH_MSG_WARNING("      Unable to access feature - link invalid.");
+        } else {
+          ATH_MSG_INFO("      IParticle Feature pt:" << (*li.link)->pt() << " eta:" << (*li.link)->eta() << " phi:" << (*li.link)->phi());
+        }
+      }
+    }
   }
   ATH_MSG_INFO( "REGTEST ==========END of TDT DUMP===========" );
   return StatusCode::SUCCESS;
@@ -4099,6 +4102,25 @@ StatusCode TrigEDMChecker::checkTrigCompositeElementLink(const xAOD::TrigComposi
     if (!elementLink.isValid()) ATH_MSG_WARNING("  Invalid element link to View, link name:'" << name << "'");
     else ATH_MSG_DEBUG("  Dereferenced link '" << name << "' to View:'" << *elementLink);
 
+   } else if (name == "feature") {
+
+    if (clid == ClassID_traits< xAOD::TrigEMClusterContainer >::ID()) {
+
+      const ElementLink<xAOD::TrigEMClusterContainer> elementLink = tc->objectLink<xAOD::TrigEMClusterContainer>(name);
+      if (!elementLink.isValid()) ATH_MSG_WARNING("  Invalid element link to xAOD::TrigEMClusterContainer 'feature'");
+      else ATH_MSG_DEBUG("  Dereferenced xAOD::TrigEMClusterContainer link 'feature', Energy:" << (*elementLink)->energy());
+
+    } else {
+
+      try {
+        const ElementLink<xAOD::IParticleContainer> elementLink = tc->objectLink<xAOD::IParticleContainer>(name);
+        if (!elementLink.isValid()) ATH_MSG_WARNING("  Invalid element link to 'feature'");
+        else ATH_MSG_DEBUG("  Dereferenced IParticle link 'feature', pt:" << (*elementLink)->pt() << " eta:" << (*elementLink)->eta() << " phi:" <<  (*elementLink)->phi());
+      } catch(std::runtime_error& e) {
+        ATH_MSG_WARNING("  Cannot dereference 'feature' as IParticle: '" << e.what() << "'");
+      }
+
+    }
 
   } else {
     ATH_MSG_DEBUG("  Ignoring link to '" << name << "' with link CLID " << clid);
@@ -4162,7 +4184,7 @@ StatusCode TrigEDMChecker::TrigCompositeNavigationToDot(std::string& returnValue
       const uint32_t selfIndex = selfEL.index();
       ss << "    \"" << selfKey << "_" << selfIndex << "\" [label=\"Container=" << typeNameTC; 
       if (tc->name() != "") ss << "\\nName=" << tc->name();
-      ss << "\\nKey=" << key << "\\nIndex=" << selfIndex;
+      ss << "\\nKey=" << key << "\\nIndex=" << selfIndex << " linksRemapped=" << (tc->isRemapped() ? "Y" : "N");
       const std::vector<DecisionID> decisions = tc->decisions();
       if (decisions.size() > 0) {
         ss << "\\nPass=";
diff --git a/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py b/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
index 7b0b71ea61921e9c4b35fb2a10178a020ee2be5c..793eabb90c10c7073023f494d6e6bdf297300d50 100644
--- a/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
+++ b/Trigger/TriggerCommon/TriggerJobOpts/python/TriggerConfig.py
@@ -72,6 +72,8 @@ def collectFilters( steps ):
     for stepSeq in steps.getChildren():
         if "filter" in stepSeq.name():
             filters[stepSeq.name()] = stepSeq.getChildren()
+            __log.info("Found Filters in Step {} : {}".format(stepSeq.name(), stepSeq.getChildren()))
+
     return filters
 
 
@@ -80,12 +82,12 @@ def collectDecisionObjects(  hypos, filters, l1decoder ):
     Returns the set of all decision objects of HLT
     """
     decisionObjects = set()
-    __log.info("Collecting decision obejcts from L1 decoder instance")
+    __log.info("Collecting decision objects from L1 decoder instance")
     decisionObjects.update([ d.Decisions for d in l1decoder.roiUnpackers ])
     decisionObjects.update([ d.Decisions for d in l1decoder.rerunRoiUnpackers ])
 
 
-    __log.info("Collecting decision obejcts from hypos")
+    __log.info("Collecting decision objects from hypos")
     __log.info(hypos)
     for step, stepHypos in hypos.iteritems():
         for hypoAlg in stepHypos:
@@ -97,7 +99,7 @@ def collectDecisionObjects(  hypos, filters, l1decoder ):
                 decisionObjects.add( hypoAlg.HypoInputDecisions )
                 decisionObjects.add( hypoAlg.HypoOutputDecisions )
 
-    __log.info("Collecting decision obejcts from filters")
+    __log.info("Collecting decision objects from filters")
     for step, stepFilters in filters.iteritems():
         for filt in stepFilters:
             decisionObjects.update( filt.Input )
@@ -108,7 +110,7 @@ def collectDecisionObjects(  hypos, filters, l1decoder ):
 
 def triggerSummaryCfg(flags, hypos):
     """
-    Configures an algorithm(s) that should be run after the slection process
+    Configures an algorithm(s) that should be run after the section process
     Returns: ca, algorithm
     """
     acc = ComponentAccumulator()
@@ -169,7 +171,7 @@ def triggerMonitoringCfg(flags, hypos, filters, l1Decoder):
 def triggerOutputStreamCfg( flags, decObj, outputType ):
     """
     Configure output stream according to the menu setup (decision objects)
-    and TrigEDMCOnfig
+    and TrigEDMConfig
     """
     from OutputStreamAthenaPool.OutputStreamConfig import OutputStreamCfg
     itemsToRecord = []
@@ -185,10 +187,10 @@ def triggerOutputStreamCfg( flags, decObj, outputType ):
     itemsToRecord.extend( [ el[0] for el in EDMCollectionsToRecord ] )
 
     # summary objects
-    __log.debug( outputType + " trigger content "+str( itemsToRecord ) )
+    __log.info( outputType + " trigger content "+str( itemsToRecord ) )
     acc = OutputStreamCfg( flags, outputType, ItemList=itemsToRecord )
     streamAlg = acc.getEventAlgo("OutputStream"+outputType)
-    streamAlg.ExtraInputs = [("xAOD::TrigCompositeContainer", "HLTSummary")]
+    streamAlg.ExtraInputs = [("xAOD::TrigCompositeContainer", "HLTSummary")] # OutputStream has a data dependency on HLTSummary
 
     return acc
 
@@ -268,13 +270,37 @@ def triggerMergeViewsAndAddMissingEDMCfg( edmSet, hypos, viewMakers, decObj ):
                         continue
 
             producer = producer[0]
-            tool.TrigCompositeContainer = producer.InputMakerOutputDecisions
-            tool.FixLinks = True
+            # NOTE: The below loop is going to find the HypoAlg which consume this output, and collate these HypoAlg's output
+            # It might be nicer to instead split up the GapFiller below to have a different GapFiller for Hypos and for Filters
+            # Then we could go back to having FixLinks be a boolean, rather than specifying a sub-set of the write handles
+            # attached to the TrigCompositeContainer parameter.
+
+            # We have a InputMaker with configured outputs, we need to identify all HypoAlg which consume these inputs.
+            # These HypoAlg all need to have their output element link collections remapped via FixLinks
+            TCHypoOutputCollections = list()
+            for step, stepHypos in hypos.iteritems():
+                for hypoAlg in stepHypos:
+                    if isinstance( hypoAlg.HypoInputDecisions, list): # Support multiple inputs and outputs to this HypoAlg
+                        counter = -1
+                        for inputDecision in hypoAlg.HypoInputDecisions:
+                            ++counter
+                            if inputDecision in producer.InputMakerOutputDecisions:
+                                TCHypoOutputCollections.append(hypoAlg.HypoOutputDecisions[counter])
+                    else: # Normal case - consume one input, produce one output
+                        if hypoAlg.HypoInputDecisions in producer.InputMakerOutputDecisions:
+                            TCHypoOutputCollections.append(hypoAlg.HypoOutputDecisions)
+            tool.FixLinks = TCHypoOutputCollections
+
+            # We need to register in storegate these Hypo outputs, but also the InputMaker outputs as well so we add them to the list here too
+            TCOutputCollections = TCHypoOutputCollections[:] # copy the list
+            TCOutputCollections.extend(producer.InputMakerOutputDecisions)
+            tool.TrigCompositeContainer = TCOutputCollections
+            tool.dumpSGBefore = False
+            tool.dumpSGAfter = False
         alg.OutputTools += [ tool ]
 
-
+    tool = HLTEDMCreator( "GapFiller" )
     if len(edmSet) != 0:
-        tool = HLTEDMCreator( "GapFiller" )
         from collections import defaultdict
         groupedByType = defaultdict( list )
     
@@ -291,13 +317,14 @@ def triggerMergeViewsAndAddMissingEDMCfg( edmSet, hypos, viewMakers, decObj ):
             propName = collType.split(":")[-1]
             if hasattr( tool, propName ):
                 setattr( tool, propName, collNameList )
+                __log.info("GapFiller will create EDM collection type '{}' for '{}'".format( collType, collNameList ))
             else:
-                __log.info("The {} is not going to be added if missing".format( collType ))
+                __log.info("EDM collections of type {} are not going to be added to StoreGate, if not created by the HLT".format( collType ))
+    # append all decision objects
+    __log.debug("The GapFiller is ensuring the creation of all the decision object collections: '{}'".format( decObj ) )
+    tool.TrigCompositeContainer += list(decObj)
+    alg.OutputTools += [tool]
 
-        # append all decision objects
-        tool.TrigCompositeContainer += list(decObj)
-        alg.OutputTools += [tool]
-        
     return alg
 
 
@@ -365,10 +392,12 @@ def triggerRunCfg( flags, menu=None ):
     edmSet = []
 
     if flags.Output.ESDFileName != "":
+        __log.debug( "Setting up trigger EDM output for ESD" )
         acc.merge( triggerOutputStreamCfg( flags, decObj, "ESD" ) )
         edmSet.append('ESD')
 
     if flags.Output.AODFileName != "":
+        __log.debug( "Setting up trigger EDM output for AOD" )
         acc.merge( triggerOutputStreamCfg( flags, decObj, "AOD" ) )
         edmSet.append('AOD')
         
@@ -378,6 +407,7 @@ def triggerRunCfg( flags, menu=None ):
 
     # configure actual streams
     if flags.Trigger.writeBS:
+        __log.debug( "Setting up trigger output for ByteStream" )
         acc.merge( triggerBSOutputCfg( flags, decObj ) )
 
     return acc