diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx b/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
index f2935a0b5587eadad568a37fb437601845aa960c..4b09d09e4e0f14c88fb23c713727a246909aba18 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/Root/CacheGlobalMemory.cxx
@@ -60,6 +60,7 @@ Trig::CacheGlobalMemory::CacheGlobalMemory() :
 {}
 
 Trig::CacheGlobalMemory::~CacheGlobalMemory() {
+   std::lock_guard<std::mutex> lock(m_cgmMutex);
 
    delete m_unpacker;
 
@@ -82,6 +83,7 @@ Trig::CacheGlobalMemory::~CacheGlobalMemory() {
 
 const Trig::ChainGroup* Trig::CacheGlobalMemory::createChainGroup(const std::vector< std::string >& triggerNames,
                                                                   const std::string& alias) {
+  std::lock_guard<std::mutex> lock(m_cgmMutex);  
   // create a proper key
   std::vector< std::string > key=Trig::keyWrap(triggerNames);
 
@@ -112,7 +114,7 @@ void Trig::CacheGlobalMemory::updateChainGroup(Trig::ChainGroup* chainGroup) {
 
 void Trig::CacheGlobalMemory::update(const TrigConf::HLTChainList* confChains,
                                      const TrigConf::CTPConfig* ctp) {
-
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   ATH_MSG_DEBUG( "Updating configuration" );
   // store a global reference to the initial answer
   m_confChains = confChains;
@@ -178,33 +180,30 @@ void Trig::CacheGlobalMemory::update(const TrigConf::HLTChainList* confChains,
 
     for(auto ch : *m_confChains) {
       if (( ch->level() == "EF" || ch->level() == "HLT") && ch->streams().size()>0 ) {
-	ATH_MSG_DEBUG( "Stream: " << ch->chain_name() << " "
-		       << ch->streams().size() );
-	for(auto stream : ch->streams()) {
-	    if( msgLvl( MSG::DEBUG ) ) {
-	      msg() << " " << stream->stream();
-	    }
-             m_streams[stream->stream()].push_back(ch->chain_name());
-          }
+        ATH_MSG_DEBUG( "Stream: " << ch->chain_name() << " " << ch->streams().size() );
+        for(auto stream : ch->streams()) {
           if( msgLvl( MSG::DEBUG ) ) {
-	    msg() << endmsg;
-	  }
+            msg() << " " << stream->stream();
+          }
+          m_streams[stream->stream()].push_back(ch->chain_name());
+        }
+        if( msgLvl( MSG::DEBUG ) ) {
+          msg() << endmsg;
+        }
       }
       if ( ( ch->level() == "EF" || ch->level() == "HLT") && ch->groups().size()>0 ) {
-	ATH_MSG_DEBUG( "Groups: " << ch->chain_name()
-		       << " " << ch->groups().size() );
-	for(auto& group : ch->groups()) {
-	    if( msgLvl( MSG::DEBUG ) ) {
-	      msg() << " " << group;
-	    }
-	    m_groups[group].push_back(ch->chain_name());
+        ATH_MSG_DEBUG( "Groups: " << ch->chain_name() << " " << ch->groups().size() );
+        for(auto& group : ch->groups()) {
+          if( msgLvl( MSG::DEBUG ) ) {
+            msg() << " " << group;
           }
-	  if( msgLvl( MSG::DEBUG ) ) {
-	    msg() << endmsg;
-	  }
+          m_groups[group].push_back(ch->chain_name());
+        }
+        if( msgLvl( MSG::DEBUG ) ) {
+	        msg() << endmsg;
+	      }
       }
     }
-   
     //
     std::map<std::string, std::vector<std::string> >::iterator mstIt;
     for (mstIt=m_streams.begin(); mstIt != m_streams.end(); mstIt++) {      
@@ -212,8 +211,8 @@ void Trig::CacheGlobalMemory::update(const TrigConf::HLTChainList* confChains,
       std::vector< std::string > key_alias=Trig::keyWrap(Trig::convertStringToVector(alias));
       ChGrIt preIt = m_chainGroupsRef.find(key_alias);
       if (  preIt != m_chainGroupsRef.end()) {
-	ATH_MSG_INFO( "Replacing predefined, stream based, chain group: "
-		      << alias );
+        ATH_MSG_INFO( "Replacing predefined, stream based, chain group: "
+          << alias );
         // cg already exists (from previous config, we need to update it)
         preIt->second->m_patterns = mstIt->second;
         updateChainGroup(preIt->second);	
@@ -227,7 +226,7 @@ void Trig::CacheGlobalMemory::update(const TrigConf::HLTChainList* confChains,
       std::vector< std::string > key_alias=Trig::keyWrap(Trig::convertStringToVector(alias));
       ChGrIt preIt = m_chainGroupsRef.find(key_alias);
       if (preIt != m_chainGroupsRef.end()) {
-	ATH_MSG_INFO( "Replacing predefined, config group based, chain "
+        ATH_MSG_INFO( "Replacing predefined, config group based, chain "
 		      << "group: " << alias );
         preIt->second->m_patterns = mstIt->second;
         updateChainGroup(preIt->second);
@@ -253,11 +252,11 @@ void Trig::CacheGlobalMemory::update(const TrigConf::HLTChainList* confChains,
 
 
 const HLT::Chain* Trig::CacheGlobalMemory::chain(const std::string& name) const {
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   auto i = m_efchainsByName.find(name);
   if (i != m_efchainsByName.end()) {
     return i->second;
   }
-
   i = m_l2chainsByName.find(name);
   if ( i != m_l2chainsByName.end() ) {
     return i->second;
@@ -293,6 +292,7 @@ const TrigConf::TriggerItem* Trig::CacheGlobalMemory::config_item(const std::str
 }
 
 float Trig::CacheGlobalMemory::item_prescale(int ctpid) const {
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   // find items in cache
   if ( m_itemsCache.count(ctpid) == 0 ) {
     ATH_MSG_ERROR( "item of CTP: " << ctpid
@@ -317,6 +317,7 @@ const LVL1CTP::Lvl1Item* Trig::CacheGlobalMemory::item(const std::string& name)
 }
 
 const xAOD::TrigCompositeContainer* Trig::CacheGlobalMemory::expressStreamContainer() const {
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   if(!m_expressStreamContainer){
     StatusCode sc = store()->retrieve(m_expressStreamContainer, "HLT_Express_stream_HLT");
     if(sc.isFailure()){
@@ -327,7 +328,7 @@ const xAOD::TrigCompositeContainer* Trig::CacheGlobalMemory::expressStreamContai
 }
 
 bool Trig::CacheGlobalMemory::assert_decision() {
-
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   ATH_MSG_VERBOSE("asserting decision with unpacker " << m_unpacker);
 
   // here we unpack the decision. Note: the navigation will be unpacked only on demand (see navigation())
@@ -389,23 +390,21 @@ bool Trig::CacheGlobalMemory::assert_decision() {
 }
 
 StatusCode Trig::CacheGlobalMemory::unpackDecision() {
-  
   ATH_MSG_DEBUG("Unpacking TrigDecision ");
   ATH_MSG_DEBUG("clearing the delete-end-of-event store");
   m_deleteAtEndOfEvent.clear();
   
   bool unpackHLT = ( m_confChains != 0 );
- ATH_CHECK( m_unpacker->unpackDecision( m_itemsByName, m_itemsCache,
+  ATH_CHECK( m_unpacker->unpackDecision( m_itemsByName, m_itemsCache,
 					m_l2chainsByName, m_l2chainsCache,
 					m_efchainsByName, m_efchainsCache,
 					m_bgCode, unpackHLT ) );
- 
   return StatusCode::SUCCESS;
 }
 
 
 StatusCode Trig::CacheGlobalMemory::unpackNavigation() {
-  
+  std::lock_guard<std::mutex> lock(m_cgmMutex);
   // Navigation
   // protect from unpacking in case HLT was not run
   // (i.e. configuration chains are 0)
@@ -422,7 +421,6 @@ StatusCode Trig::CacheGlobalMemory::unpackNavigation() {
       warningPrinted = true;
     }
   }
-  
   // Return gracefully:
   return StatusCode::SUCCESS;
 }
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx b/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
index fe262391681fdda13dac3c459fc9a7d91ecba8ed..140e78922e4c05e67151496dd4bb35a7a95ad446 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/Root/TrigDecisionTool.cxx
@@ -102,6 +102,15 @@ StatusCode
 Trig::TrigDecisionTool::initialize() {
    TrigDecisionToolCore::initialize().ignore();
 
+   if (m_navigationFormat != "TriggerElement" && m_navigationFormat != "TrigComposite") {
+     ATH_MSG_ERROR("NavigationFormat property must be one of 'TriggerElement' for Run 1, 2 triggered input or 'TrigComposite' for Run 3+ triggered input");
+     return StatusCode::FAILURE;
+   }
+
+   if (m_navigationFormat == "TrigComposite") {
+     ATH_CHECK(m_HLTSummaryKeyIn.initialize());
+   }
+
    s_instances.push_back(name());
    if ( s_instances.size() > 1) {
        ATH_MSG_WARNING("Several TrigDecisionTool instances" );
@@ -289,4 +298,9 @@ Trig::TrigDecisionTool::isPassedBits( const std::string& chain ) const {
    return TrigDecisionToolCore::isPassedBits( chain );
 }
 
+const std::string&
+Trig::TrigDecisionTool::getNavigationFormat() const {
+   return m_navigationFormat;
+}
+
 
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
index e2a8e097d9dd6dd0d413cedf6fe56942ee305d6f..5a7bcd70d800a5322ba3f8c643d9c1dacc7815e9 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/CacheGlobalMemory.h
@@ -23,6 +23,7 @@
 #include<map>
 #include<unordered_map>
 #include<string>
+#include<mutex>
 #include "boost/foreach.hpp"
 
 #include "TrigConfHLTData/HLTChain.h"
@@ -247,6 +248,8 @@ namespace Trig {
     
     mutable AnyTypeDeleter m_deleteAtEndOfEvent;
 
+    mutable std::mutex m_cgmMutex; //!< Temporary R3 MT protection only against --threads > 1, not against events in flight > 1
+
 
 
   };
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
index 0d0f091f5e8b96e24868616fe84b28b0a4a47c2f..dc01c1d0a1be30e7b4c034f12a05144fac2a886a 100644
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/DecisionAccess.icc
@@ -47,6 +47,7 @@ template<class CONTAINER>
 const std::vector< TrigCompositeUtils::LinkInfo<CONTAINER> > Trig::DecisionAccess::features(const Trig::ChainGroup* group,
                                                                   const unsigned int condition,
                                                                   const bool oneFeaturePerLeg) const {
+  // const TrigCompositeUtils::DecisionContainer* terminusNode = SG::get(m_HLTSummaryKeyIn/*, context*/);
   return group->features<CONTAINER>(cgm()->store(), condition, oneFeaturePerLeg);
 }
 
diff --git a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/TrigDecisionTool.h b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/TrigDecisionTool.h
index 0b7af32e354d2ba1ecc0236fc26efe109e57bed1..3d1c6570be199ad6ecf4a5cdd219646f72a8bb24 100755
--- a/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/TrigDecisionTool.h
+++ b/Trigger/TrigAnalysis/TrigDecisionTool/TrigDecisionTool/TrigDecisionTool.h
@@ -38,6 +38,7 @@
 
 #endif
 
+#include "DecisionHandling/TrigCompositeUtils.h"
 
 // interface to implement for offline access (outside AtlasTrigger)
 #include "TrigDecisionInterface/ITrigDecisionTool.h"
@@ -108,6 +109,8 @@ namespace Trig {
 
     bool msgLvl(const MSG::Level lvl) const { return Logger::msgLvl(lvl); }
 
+    const std::string& getNavigationFormat() const; //!< Note: Temporary
+
   private:
       
     bool configKeysMatch(uint32_t smk, uint32_t lvl1psk, uint32_t hltpsk);
@@ -128,6 +131,13 @@ namespace Trig {
     bool m_useAODDecision;
     std::string m_decisionKey;
 
+    /// @name Run 3 properties
+    /// @{
+    Gaudi::Property<std::string> m_navigationFormat{this, "NavigationFormat", "TrigComposite", "Allowed tokens are 'TriggerElement' or 'TrigComposite'"}; //!< Note: Temporary property
+
+    SG::ReadHandleKey<TrigCompositeUtils::DecisionContainer> m_HLTSummaryKeyIn {this, "HLTSummary", "HLTSummary", "HLT summary container Key"};
+    /// @}
+
     TrigDecisionTool& operator= (const TrigDecisionTool&);
 
     std::map<std::string, std::string> m_publicChainGroups;
diff --git a/Trigger/TrigEvent/TrigDecisionInterface/TrigDecisionInterface/Conditions.h b/Trigger/TrigEvent/TrigDecisionInterface/TrigDecisionInterface/Conditions.h
index eb9eafce9f159521827390ea114407121457a066..a83093c8cad696eb860aa9d8bf070f25e81d55e4 100644
--- a/Trigger/TrigEvent/TrigDecisionInterface/TrigDecisionInterface/Conditions.h
+++ b/Trigger/TrigEvent/TrigDecisionInterface/TrigDecisionInterface/Conditions.h
@@ -37,6 +37,7 @@ namespace TrigDefs {
   //
   const static unsigned int ignoreIOV = 0x1 << 15;
   const static unsigned int alsoDeactivateTEs = 0x1 << 16;
+  const static unsigned int alsoDeactivateTCs = 0x1 << 16; //!< Run3 synonym of alsoDeactivateTEs.
   const static unsigned int ignoreFDR = 0x1 << 17;
   //
   const static unsigned int Physics = requireDecision | enforceLogicalFlow;
diff --git a/Trigger/TrigMonitoring/TrigMuonMonitoring/src/MuFastMon.cxx b/Trigger/TrigMonitoring/TrigMuonMonitoring/src/MuFastMon.cxx
index b0b03db9d426f0137d044707ed366ae8432c4966..c74714334e4fe7b523cfe0214dcbdfc5eead18ea 100644
--- a/Trigger/TrigMonitoring/TrigMuonMonitoring/src/MuFastMon.cxx
+++ b/Trigger/TrigMonitoring/TrigMuonMonitoring/src/MuFastMon.cxx
@@ -612,9 +612,19 @@ StatusCode HLTMuonMonTool::fillL2MuonSADQA()
   // Get mu6 and mu10 chains using TDT
   // ---------------------------------
 
-  Trig::FeatureContainer fc_mu6 = getTDT()->features("HLT_mu6");
-  std::vector<Trig::Combination> combs_mu6 = fc_mu6.getCombinations();
-  std::vector<Trig::Combination>::const_iterator p_comb_mu6;
+  std::vector<Trig::Combination> combs_mu6; //!< R2. To be deprecated
+  using TrigCompositeUtils::LinkInfo;
+  using TrigCompositeUtils::findLink;
+  using TrigCompositeUtils::Decision;
+  std::vector< LinkInfo<xAOD::L2StandAloneMuonContainer> > features_mu6; //!< R3.
+  if (getTDT()->getNavigationFormat() == "TriggerElement") {
+    Trig::FeatureContainer fc_mu6 = getTDT()->features("HLT_mu6");
+    std::vector<Trig::Combination> combs_mu6 = fc_mu6.getCombinations();
+  } else {
+    features_mu6 =  getTDT()->features<xAOD::L2StandAloneMuonContainer>("HLT_mu6", TrigDefs::alsoDeactivateTCs);
+    // TODO only want to get features from container of name "MuonL2SAInfo"
+  }
+
 
   ATH_MSG_DEBUG("isPassed mu6="  << getTDT()->isPassed("HLT_mu6",  TrigDefs::Physics));
 
@@ -792,23 +802,55 @@ StatusCode HLTMuonMonTool::fillL2MuonSADQA()
     float dRmin_mu6 = 1000;
     float forIDroi_eta_mu6 = 0;
     float forIDroi_phi_mu6 = 0;
-	 for(p_comb_mu6=combs_mu6.begin();p_comb_mu6!=combs_mu6.end();++p_comb_mu6) {
-       std::vector<Trig::Feature<xAOD::L2StandAloneMuonContainer> > fs_MF = 
-	      (*p_comb_mu6).get<xAOD::L2StandAloneMuonContainer>("MuonL2SAInfo",TrigDefs::alsoDeactivateTEs);	
-       if(!fs_MF.size()) continue;
-       const xAOD::L2StandAloneMuonContainer* mf_cont = fs_MF[0];
-       float eta_mf = mf_cont->at(0)->eta();
-       float phi_mf = mf_cont->at(0)->phi();
-       float dR = calc_dR(eta_mf, phi_mf, eta_offl, phi_offl);
-       if( dR < dRmin_mu6 ) {
-	      dRmin_mu6 = dR;
-	      std::vector<Trig::Feature<TrigRoiDescriptor> > fs_roi = 
-	        p_comb_mu6->get<TrigRoiDescriptor>("forID",TrigDefs::alsoDeactivateTEs);	
-       if(!fs_roi.size()) continue;
-	      forIDroi_eta_mu6 = fs_roi.at(0).cptr()->eta();
-	      forIDroi_phi_mu6 = fs_roi.at(0).cptr()->phi();
-       }
+    
+    if (getTDT()->getNavigationFormat() == "TriggerElement") {
+    
+      std::vector<Trig::Combination>::const_iterator p_comb_mu6;
+      for(p_comb_mu6=combs_mu6.begin();p_comb_mu6!=combs_mu6.end();++p_comb_mu6) {
+        std::vector<Trig::Feature<xAOD::L2StandAloneMuonContainer> > fs_MF = 
+            (*p_comb_mu6).get<xAOD::L2StandAloneMuonContainer>("MuonL2SAInfo",TrigDefs::alsoDeactivateTEs);	
+        if(!fs_MF.size()) {
+          continue;
+        }
+        const xAOD::L2StandAloneMuonContainer* mf_cont = fs_MF[0];
+        float eta_mf = mf_cont->at(0)->eta();
+        float phi_mf = mf_cont->at(0)->phi();
+        float dR = calc_dR(eta_mf, phi_mf, eta_offl, phi_offl);
+        if( dR < dRmin_mu6 ) {
+          dRmin_mu6 = dR;
+          std::vector<Trig::Feature<TrigRoiDescriptor> > fs_roi = 
+              p_comb_mu6->get<TrigRoiDescriptor>("forID",TrigDefs::alsoDeactivateTEs);	
+          if(!fs_roi.size()) {
+            continue;
+          }
+          forIDroi_eta_mu6 = fs_roi.at(0).cptr()->eta();
+          forIDroi_phi_mu6 = fs_roi.at(0).cptr()->phi();
+        }
+      }
+    
+    } else { // getTDT()->getNavigationFormat() == "TrigComposite"
+
+      for(const LinkInfo<xAOD::L2StandAloneMuonContainer>& muLinkInfo : features_mu6) {
+        ATH_CHECK( muLinkInfo.isValid() );
+        const ElementLink<xAOD::L2StandAloneMuonContainer> muEL = muLinkInfo.link;
+        const Decision* muDecision = muLinkInfo.source; 
+        float eta_mf = (*muEL)->eta();
+        float phi_mf = (*muEL)->phi();
+        float dR = calc_dR(eta_mf, phi_mf, eta_offl, phi_offl);
+        if( dR < dRmin_mu6 ) {
+          dRmin_mu6 = dR;
+          const LinkInfo<TrigRoiDescriptorCollection> roiLinkInfo = findLink<TrigRoiDescriptorCollection>(muDecision, "initialRoI");
+          // TODO - check if the R3 trigger will still create the "forID" ROIDescriptor.
+          // (SShaw: In Run2 this was a special ROIDescriptor centred on the L2 muon)
+          ATH_CHECK( roiLinkInfo.isValid() );
+          const ElementLink<TrigRoiDescriptorCollection> roiEL = roiLinkInfo.link;
+          forIDroi_eta_mu6 = (*roiEL)->eta();
+          forIDroi_phi_mu6 = (*roiEL)->phi();
+        }
+      }
     }
+
+
     if( dRmin_mu6 < DR_MATCHED ) {
        float dr   = calc_dR(eta_offl,phi_offl,forIDroi_eta_mu6,forIDroi_phi_mu6);
        float deta = forIDroi_eta_mu6-eta_offl;
diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
index d0e0ed08bc36ebcd08471c279b4b9dab3cebdc15..338bda04603b20266bebf7f5ee68db3067101ba9 100644
--- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
+++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/TrigCompositeUtils.h
@@ -210,12 +210,20 @@ namespace TrigCompositeUtils {
   private:
     std::string m_name;
   };
+
+  /**
+   * @brief
+   * @param[in] eventStore 
+   * @param[out] output
+   * @param[in] id
+   **/
+  StatusCode getRejectedDecisionNodes(EventPtr_t eventStore, std::vector<const Decision*>& output, const DecisionID id = 0) {
   
   /**
    * @brief Search back in time from "start" and locate all linear paths back through Decision objects for a given chain.
    * @param[in] start The Decision object to start the search from. Typically this will be one of the terminus objects from the HLTSummary (regular or rerun).
-   * @param[in] id Optional DecisionID of a Chain to trace through the navigation. If omitted, no chain requirement will be applied.
    * @param[out] linkVector Each entry in the outer vector represents a path through the graph. For each path, a vector of ElementLinks describing the path is returned.
+   * @param[in] id Optional DecisionID of a Chain to trace through the navigation. If omitted, no chain requirement will be applied.
    **/
   void recursiveGetDecisions( const Decision* start, std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const DecisionID id = 0 );
 
diff --git a/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx b/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
index d83e54bbe65947f99db8cedd23fc278d98360820..06bee82a77c5e94bcfb345605fc8d08e15a152ca 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/src/TrigCompositeUtils.cxx
@@ -126,15 +126,15 @@ namespace TrigCompositeUtils {
     }    
 
     const ElementLink<DecisionContainer> seedLink = ElementLink<DecisionContainer>(*container, dOld->index(), ctx);
-    d->addObjectCollectionLink("seed", seedLink);
+    d->addObjectCollectionLink(seedString(), seedLink);
   }
 
   bool hasLinkToPrevious( const Decision* d ) {
-    return d->hasObjectCollectionLinks( "seed" );
+    return d->hasObjectCollectionLinks( seedString() );
   }
 
   const ElementLinkVector<DecisionContainer> getLinkToPrevious( const Decision* d ) {
-    return d->objectCollectionLinks<DecisionContainer>( "seed" );
+    return d->objectCollectionLinks<DecisionContainer>( seedString() );
   }
 
 
@@ -166,6 +166,76 @@ namespace TrigCompositeUtils {
     return composite->hasObjectCollectionLinks( m_name );
   }
 
+  StatusCode getRejectedDecisionNodes(EventPtr_t eventStore, std::vector<const Decision*>& output, const DecisionID id) {
+    std::vector<const Decision*> output;
+
+    // Good in the future to have the TDT populate a ReadHandleKeyArray with *ALL* Decision containers in the input files (there will be many, and the list will change often, file by file)
+    // For now, we are old-fashoned and we ask SG directly on an event-by-event basis.
+
+    std::vector<std::string> keys;
+    eventStore->keys(static_cast<CLID>( ClassID_traits< DecisionContainer >::ID() ), keys);
+
+    // Loop over each DecisionContainer, only looking for collections produced by HypoAlg
+    for (const std::string& key : keys) {
+      if (key.find("Hypo") == std::string::npos) {
+        continue;
+      }
+      // Get and check this container
+      const DecisionContainer* container = nullptr;
+      ATH_CHECK( eventStore->retrieve( container, key ) );
+      ATH_MSG_DEBUG("Looking for failed Decisions for ChainID " << id << " in HypoCollection " << key);
+      for (const Decision* d : *container) {
+        if (!d->hasObjectLink(featureString())) {
+          ATH_MSG_DEBUG("Decision did not have feature with name:" << featureString() << "?!");
+          ATH_MSG_DEBUG(*d);
+          // TODO add logic for CombiHypo where this is expected
+          continue;
+        }
+        const ElementLinkVector<DecisionContainer> mySeeds = d->objectCollectionLinks<DecisionContainer>(seedString());
+        ATH_MSG_DEBUG("  HypoAlg Decision has " << mySeeds.size() << " direct parents");
+        if (mySeeds.size() == 0) {
+          ATH_MSG_WARNING("Got a HypoAlg Decision with no parents - this is a clear sign of a triggr misconfiguration!");
+          continue;
+        }
+        DecisionIDContainer activeChainsIntoThisDecision;
+        decisionIDs(mySeeds.at(0), activeChainsIntoThisDecision); // Get list of active chains from the first parent
+        if (mySeeds.size() > 1) {
+          for (size_t i = 1; i < mySeeds.size(); ++i) {
+            // If there are more than one parent, we only want to keep the intersection of all of the seeds
+            DecisionIDContainer moreActiveChains;
+            decisionIDs(mySeeds.at(i), moreActiveChains);
+            DecisionIDContainer intersection;
+            std::set_intersection(activeChainsIntoThisDecision.begin(), activeChainsIntoThisDecision.end(),
+              moreActiveChains.begin(), moreActiveChains.end(),
+              std::inserter(intersection, intersection.begin()));
+            activeChainsIntoThisDecision = intersection; // Update the output to only be the intersetion and continue to any other seeds
+          }
+        }
+        // We now know what chains were active coming into this Decision (d) from ALL seeds
+        // This is the logic required for each HypoTool to have activated and checked if its chain passes
+        // So the size of activeChainsIntoThisDecision corresponds to the number of HypoTools which will have run
+        // What do we care about? A chain, or all chains?
+        DecisionIDContainer chainsToCheck;
+        if (id == 0) { // We care about *all* chains
+          chainsToCheck = activeChainsIntoThisDecision;
+        } else { // We care about *one* chain
+          chainsToCheck.insert(id);
+        }
+        // We have found a rejected decision node *iff* [an] chainID to check is *not* present here
+        // I.e. the HypoTool for the chain returned a NEGATIVE decision
+        DecisionIDContainer activeChainsPassedByThisDecision;
+        decisionIDs(d, activeChainsPassedByThisDecision);
+        for (const DecisionID id : chainsToCheck) {
+          if (activeChainsPassedByThisDecision.count(id) == 0) { // I was REJECTED
+            ATH_MSG_DEBUG("Chain ID " << id << " was rejected here. TrigDefs::alsoDeactivateTCs 'feature' source located.");
+            output.push_back(d);
+            break;
+          }
+        }
+      }
+    }
+    return StatusCode::SUCCESS;
+  }
 
   void recursiveGetDecisionsInternal(const Decision* start, const size_t location, std::vector<ElementLinkVector<DecisionContainer>>& linkVector, const DecisionID id) {
     // Does this Decision satisfy the chain requirement?
diff --git a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
index d37618b2032dcc623019814888af1c65470a2f83..dceaab603d2b4c7b58e532fd85cb0f2c8be32f5e 100644
--- a/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
+++ b/Trigger/TrigValidation/TrigUpgradeTest/share/full_menu.py
@@ -68,7 +68,7 @@ if l1decoder:
     ItemList += [ k[0] for k in TriggerHLTList if 'ESD' in k[1] and "TrigComposite" in k[0] ]
     ItemList += [ 'xAOD::TrigCompositeAuxContainer#{}Aux.'.format(k[0].split("#")[1]) for k in TriggerHLTList if 'ESD' in k[1] and "TrigComposite" in k[0] ]
     ItemList += [ "xAOD::EventInfo#EventInfo" ]
-
+    ItemList += [ "xAOD::EventAuxInfo#EventInfoAux." ]
     ItemList = list(set(ItemList))
 
 else:
diff --git a/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx b/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
index a7a52847a2aad025c0e45cefcfd825d621b0dc26..247cd232396446d258396906efa7895810f7ebcd 100644
--- a/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
+++ b/Trigger/TrigValidation/TrigValAlgs/src/TrigEDMChecker.cxx
@@ -246,6 +246,7 @@ StatusCode TrigEDMChecker::initialize() {
   if (m_doTDTCheck) {
     ATH_CHECK( m_trigDec.retrieve() );
     m_trigDec->ExperimentalAndExpertMethods()->enable();
+    ATH_MSG_INFO("TDT Executing with navigation format: " << m_trigDec->getNavigationFormat());
   }
 
 	return StatusCode::SUCCESS;
@@ -4018,8 +4019,7 @@ StatusCode TrigEDMChecker::dumpTDT() {
   for (const auto& item : confChains) {
     bool passed = m_trigDec->isPassed(item, TrigDefs::requireDecision);
     ATH_MSG_INFO("  HLT Item " << item << " (numeric ID " << TrigConf::HLTUtils::string2hash(item, "Identifier") << ") passed raw? " << passed);
-    const bool isRunThree = evtStore()->contains<xAOD::TrigCompositeContainer>("HLT_Summary");
-    if (isRunThree && passed) {
+    if (m_trigDec->getNavigationFormat() == "TrigComposite" && 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) {