diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.h b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.h
index 756d4de7603190b837b6102fb4377fcac2b90e23..03b6c3775b868935b38a1291aa6868746184435f 100644
--- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.h
+++ b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.h
@@ -47,8 +47,7 @@ This is a base class for HLT InputMakers to reduce boilerplate and enforce the c
   /// does the standard handling of input decisions: read from handles with all the checks, create merged output handles and link them, copies links and return outputHandles
   StatusCode decisionInputToOutput(const EventContext& context, SG::WriteHandle<TrigCompositeUtils::DecisionContainer>& outputHandle) const;
 
-  /// Checks for merge-able Decision objects coming from N upstream filters. Check based on stored element link in typed CONTAINER with 'linkNameToMatch'
-  template<typename CONTAINER>
+  /// Checks for merge-able Decision objects coming from N upstream filters. Check based on most-recent element link with name 'linkNameToMatch'. Works for any link type.
   size_t matchDecision(const TrigCompositeUtils::DecisionContainer* outDecisions, 
     const TrigCompositeUtils::Decision* toMatch, 
     const std::string& linkNameToMatch) const;
@@ -68,6 +67,4 @@ This is a base class for HLT InputMakers to reduce boilerplate and enforce the c
   
 };
 
-#include "InputMakerBase.icc"
-
 #endif // DECISIONHANDLING_INPUTMAKERBASE_H
diff --git a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.icc b/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.icc
deleted file mode 100644
index 496914524d5369504f9b01b286bdfb3b62066629..0000000000000000000000000000000000000000
--- a/Trigger/TrigSteer/DecisionHandling/DecisionHandling/InputMakerBase.icc
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
-  Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration
-*/
-
-template<typename CONTAINER>
-size_t InputMakerBase::matchDecision(const TrigCompositeUtils::DecisionContainer* outDecisions, 
-  const TrigCompositeUtils::Decision* toMatch, 
-  const std::string& linkNameToMatch) const
-{
-  const std::vector<TrigCompositeUtils::LinkInfo<CONTAINER>> myObject = TrigCompositeUtils::findLinks<CONTAINER>(toMatch, linkNameToMatch, TrigDefs::lastFeatureOfType);
-  
-  if (myObject.size() != 1) {
-    ATH_MSG_ERROR("InputMakerBase::matchDecision Did not locate exactly one object of type '" << ClassID_traits<CONTAINER>::typeName() << "' having searched for a link named '" << linkNameToMatch 
-      << "', found " << myObject.size() << ". Unable to match this Decision object.");
-    for (const auto& li : myObject) {
-      ATH_MSG_ERROR("  -- " << li.link.dataID() << ":" << li.link.index() << ". Dump:" << *(li.source)); 
-    }
-    return std::numeric_limits<std::size_t>::max();
-  }
-
-  // Look for match
-  for (size_t index = 0; index < outDecisions->size(); ++index) {
-    const TrigCompositeUtils::Decision* checkDecision = outDecisions->at(index);
-    if (checkDecision == nullptr) {
-      ATH_MSG_ERROR("Failed to get Decision object " << index << " of " << outDecisions->size());
-      return std::numeric_limits<std::size_t>::max();
-    }
-    const std::vector<TrigCompositeUtils::LinkInfo<CONTAINER>> checkObject = TrigCompositeUtils::findLinks<CONTAINER>(checkDecision, linkNameToMatch, TrigDefs::lastFeatureOfType);
-    if (checkObject.size() != 1) {
-      ATH_MSG_ERROR("Logic error. Expect myObject().size() == 1 and checkObject.size() == 1."
-        << " But have checkObject.size() = " << checkObject.size() << ". Unable to match this Decision object.");
-      return std::numeric_limits<std::size_t>::max();
-    }
-    if (myObject.at(0).link == checkObject.at(0).link) {
-      return index;
-    }
-  }
-
-  return std::numeric_limits<std::size_t>::max();
-}
diff --git a/Trigger/TrigSteer/DecisionHandling/src/InputMakerBase.cxx b/Trigger/TrigSteer/DecisionHandling/src/InputMakerBase.cxx
index 8b445be045acfeae315b53f2388803e992dc5039..c726f8b63a77eb51942f81a0d154a562654a4bbd 100644
--- a/Trigger/TrigSteer/DecisionHandling/src/InputMakerBase.cxx
+++ b/Trigger/TrigSteer/DecisionHandling/src/InputMakerBase.cxx
@@ -94,13 +94,55 @@ StatusCode InputMakerBase::decisionInputToOutput(const EventContext& context, SG
 
 bool InputMakerBase::matchInCollection(const DecisionContainer* toMatchAgainst, const Decision* toMatch, size_t& matchIndex) const {
   if ( m_mergeUsingFeature ) {
-    matchIndex = matchDecision<xAOD::IParticleContainer>(toMatchAgainst, toMatch, featureString());
+    matchIndex = matchDecision(toMatchAgainst, toMatch, featureString());
   } else {
-    matchIndex = matchDecision<TrigRoiDescriptorCollection>(toMatchAgainst, toMatch, m_roisLink.value());
+    matchIndex = matchDecision(toMatchAgainst, toMatch, m_roisLink.value());
   }
   return (matchIndex != std::numeric_limits<std::size_t>::max());
 }
 
+size_t InputMakerBase::matchDecision(const DecisionContainer* outDecisions, const Decision* toMatch, const std::string& linkNameToMatch) const {
+  
+  std::set<const Decision*> cache; //!< Used to accelerate the recursive typelessFindLinks. Should be cleared between uses.
+  std::vector<uint32_t> keysA;
+  std::vector<uint32_t> clidsA;
+  std::vector<uint16_t> indiciesA;
+  TrigCompositeUtils::typelessFindLinks(toMatch, linkNameToMatch, keysA, clidsA, indiciesA, TrigDefs::lastFeatureOfType, &cache);
+  
+  if (keysA.size() != 1) {
+    ATH_MSG_ERROR("InputMakerBase::matchDecision Did not locate exactly one object having searched for a link named '" << linkNameToMatch 
+      << "', found " << keysA.size() << ". Unable to match this Decision object.");
+    for (size_t i = 0; i < keysA.size(); ++i) {
+      ATH_MSG_ERROR("  -- Key:" << keysA.at(i) << " Index:" << indiciesA.at(i) << " CLID:" << clidsA.at(i)); 
+    }
+    return std::numeric_limits<std::size_t>::max();
+  }
+
+  // Look for match
+  for (size_t index = 0; index < outDecisions->size(); ++index) {
+    const TrigCompositeUtils::Decision* checkDecision = outDecisions->at(index);
+    if (checkDecision == nullptr) {
+      ATH_MSG_ERROR("Failed to get Decision object " << index << " of " << outDecisions->size());
+      return std::numeric_limits<std::size_t>::max();
+    }
+    cache.clear();
+    std::vector<uint32_t> keysB;
+    std::vector<uint32_t> clidsB;
+    std::vector<uint16_t> indiciesB;
+    TrigCompositeUtils::typelessFindLinks(checkDecision, linkNameToMatch, keysB, clidsB, indiciesB, TrigDefs::lastFeatureOfType, &cache);
+    if (keysB.size() != 1) {
+      ATH_MSG_ERROR("Logic error. Expect toMatch size == 1 (confirmed) and checkObject size == 1."
+        << " But have checkObject size = " << keysB.size() << ". Unable to match this Decision object.");
+      return std::numeric_limits<std::size_t>::max();
+    }
+    if (keysA.at(0) == keysB.at(0) and clidsA.at(0) == clidsB.at(0) and indiciesA.at(0) == indiciesB.at(0)) {
+      return index;
+    }
+  }
+
+  return std::numeric_limits<std::size_t>::max();
+}
+
 
 void InputMakerBase::debugPrintOut(const EventContext& context, SG::WriteHandle<TrigCompositeUtils::DecisionContainer>& outputHandle) const{
   size_t validInput=0;
diff --git a/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx b/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
index c22b1dc36611b63403237aa45c233ff6eda1bc86..51a8be09f3b36760bce5ffd47ace30050936ab0c 100644
--- a/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
+++ b/Trigger/TrigSteer/TrigCompositeUtils/Root/TrigCompositeUtilsRoot.cxx
@@ -351,24 +351,54 @@ namespace TrigCompositeUtils {
   {
     using namespace msgFindLink;
     if (visitedCache != nullptr) {
-      // We only need to recursivly explore back from each node in the graph once.
+      // We only need to recursively explore back from each node in the graph once.
       // We can keep a record of nodes which we have already explored, these we can safely skip over.
       if (visitedCache->count(start) == 1) {
         return false; // Early exit
       }
     }
-
+    // As the append vectors are user-supplied, perform some input validation. 
+    if (keyVec.size() != clidVec.size() or clidVec.size() != indexVec.size()) {
+      ANA_MSG_WARNING("In typelessFindLinks, keyVec, clidVec, indexVec must all be the same size. Instead have:"
+        << keyVec.size() << ", " << clidVec.size()  << ", " << indexVec.size());
+      return false;
+    }
+    // Locate named links. Both collections of links and individual links are supported.
     bool found = false;
+    std::vector<uint32_t> tmpKeyVec;
+    std::vector<uint32_t> tmpClidVec;
+    std::vector<uint16_t> tmpIndexVec;
     if (start->hasObjectCollectionLinks(linkName)) {
-      found = start->typelessGetObjectCollectionLinks(linkName, keyVec, clidVec, indexVec);
+      found = start->typelessGetObjectCollectionLinks(linkName, tmpKeyVec, tmpClidVec, tmpIndexVec);
     }
     if (start->hasObjectLink(linkName)) {
-      uint32_t key, clid;
-      uint16_t index;
-      found |= start->typelessGetObjectLink(linkName, key, clid, index);
-      keyVec.push_back(key);
-      clidVec.push_back(clid);
-      indexVec.push_back(index);
+      uint32_t tmpKey, tmpClid;
+      uint16_t tmpIndex;
+      found |= start->typelessGetObjectLink(linkName, tmpKey, tmpClid, tmpIndex);
+      tmpKeyVec.push_back(tmpKey);
+      tmpClidVec.push_back(tmpClid);
+      tmpIndexVec.push_back(tmpIndex);
+    }
+    // De-duplicate
+    for (size_t tmpi = 0; tmpi < tmpKeyVec.size(); ++tmpi) {
+      bool alreadyAdded = false;
+      const uint32_t tmpKey = tmpKeyVec.at(tmpi);
+      const uint32_t tmpClid = tmpClidVec.at(tmpi);
+      const uint16_t tmpIndex = tmpIndexVec.at(tmpi);
+      for (size_t veci = 0; veci < keyVec.size(); ++veci) {
+        if (keyVec.at(veci) == tmpKey 
+          and clidVec.at(veci) == tmpClid
+          and indexVec.at(veci) == tmpIndex)
+        {
+          alreadyAdded = true;
+          break;
+        }
+      }
+      if (!alreadyAdded) {
+        keyVec.push_back( tmpKey );
+        clidVec.push_back( tmpClid );
+        indexVec.push_back( tmpIndex );
+      }
     }
     // Early exit
     if (found && behaviour == TrigDefs::lastFeatureOfType) {
@@ -395,7 +425,7 @@ namespace TrigCompositeUtils {
     // only want the most recent.
     // Hence we can supply TrigDefs::lastFeatureOfType.                                                         /--> parent3(link)
     // We can still have more then one link found if there is a branch in the navigation. E.g. start --> parent1 --> parent2(link)
-    // If both parent2 and parent3 posessed an admisable ElementLink, then the warning below will trigger, and only one of the
+    // If both parent2 and parent3 possessed an admissible ElementLink, then the warning below will trigger, and only one of the
     // links will be returned (whichever of parent2 or parent3 happened to be the first seed of parent1).
     std::vector<uint32_t> keyVec;
     std::vector<uint32_t> clidVec;
diff --git a/Trigger/TrigSteer/TrigCompositeUtils/TrigCompositeUtils/TrigCompositeUtils.h b/Trigger/TrigSteer/TrigCompositeUtils/TrigCompositeUtils/TrigCompositeUtils.h
index 64ea8b34ea3e283ea8b555cad3643e731409b653..84594aade1ff0bc3903d427ed51ca618e4b25a2b 100644
--- a/Trigger/TrigSteer/TrigCompositeUtils/TrigCompositeUtils/TrigCompositeUtils.h
+++ b/Trigger/TrigSteer/TrigCompositeUtils/TrigCompositeUtils/TrigCompositeUtils.h
@@ -457,7 +457,7 @@ namespace TrigCompositeUtils {
                           branch once a link has been located and collected. 
    * @param[inout] visitedCache Optional cache used by the recursive algorithm to avoid exploring each node multiple times. 
    */
-  bool typelessfindLinks(const Decision* start, 
+  bool typelessFindLinks(const Decision* start, 
     const std::string& linkName,
     std::vector<uint32_t>& key,
     std::vector<uint32_t>& clid,