diff --git a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/CMakeLists.txt b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/CMakeLists.txt
index 58755dc51f630ad7d83c79dc1301f7b7443e5c7a..f83096cb031831817ec385919b189bfaa4fee788 100644
--- a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/CMakeLists.txt
+++ b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/CMakeLists.txt
@@ -8,7 +8,9 @@ atlas_subdir( TrigPartialEventBuilding )
 # Declare the package's dependencies
 atlas_depends_on_subdirs( PUBLIC
                           Trigger/TrigSteer/DecisionHandling
-                          Trigger/TrigEvent/TrigSteeringEvent )
+                          Trigger/TrigEvent/TrigSteeringEvent
+                          PRIVATE
+                          Control/CxxUtils )
 
 # Component(s) in the package
 atlas_add_library( TrigPartialEventBuildingLib
diff --git a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/TrigPartialEventBuilding/PEBInfoWriterToolBase.h b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/TrigPartialEventBuilding/PEBInfoWriterToolBase.h
index ed48186827df7e5fd5d7f36d70b9cc0e5fc75bef..1b4fbce2cc175f835505eb7d2e1e90244886e363 100644
--- a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/TrigPartialEventBuilding/PEBInfoWriterToolBase.h
+++ b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/TrigPartialEventBuilding/PEBInfoWriterToolBase.h
@@ -25,15 +25,15 @@ public:
   struct Input {
     Input(TrigCompositeUtils::Decision* d,
           const EventContext& ctx,
-          const TrigRoiDescriptor* r,
+          const ElementLink<TrigRoiDescriptorCollection>& r,
           const TrigCompositeUtils::Decision* pd)
     : decision(d),
       eventContext(ctx),
-      roi(r),
+      roiEL(r),
       previousDecisionIDs(TrigCompositeUtils::decisionIDs(pd).begin(), TrigCompositeUtils::decisionIDs(pd).end()) {}
     TrigCompositeUtils::Decision* decision;
     const EventContext& eventContext;
-    const TrigRoiDescriptor* roi;
+    const ElementLink<TrigRoiDescriptorCollection> roiEL;
     const TrigCompositeUtils::DecisionIDContainer previousDecisionIDs;
   };
   /// Structure holding the list of ROBs and SubDets
@@ -61,6 +61,10 @@ public:
 protected:
   /// Creates the PEBInfo which is attached to the decision in \c decide. Has to be implemented by the derived class.
   virtual PEBInfo createPEBInfo(const Input& input) const = 0;
+  /// MaxRoIs property
+  Gaudi::Property<int> m_maxRoIs {
+    this, "MaxRoIs", -1, "Create PEB list only for the first N RoIs from input decisions (<0 means no limit)"
+  };
 
 private:
   /// The decision id of the tool instance
diff --git a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterAlg.cxx b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterAlg.cxx
index 28402562aeb2f2d35fad855fa664a30e3d2c925f..aa6fc55de13ebab010796d2d297f47d4d2c8b03a 100644
--- a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterAlg.cxx
+++ b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterAlg.cxx
@@ -77,13 +77,16 @@ StatusCode PEBInfoWriterAlg::execute(const EventContext& eventContext) const {
     auto roiELInfo = findLink<TrigRoiDescriptorCollection>(previousDecision, initialRoIString());
     auto roiEL = roiELInfo.link;
     ATH_CHECK(roiEL.isValid());
-    const TrigRoiDescriptor* roi = *roiEL;
 
     // Create new decision
     Decision* newd = newDecisionIn(decisions);
 
+    // Attach empty PEB Info lists to the new decision
+    ATH_CHECK(newd->setDetail(PEBInfoWriterToolBase::robListKey(), std::vector<uint32_t>()));
+    ATH_CHECK(newd->setDetail(PEBInfoWriterToolBase::subDetListKey(), std::vector<uint32_t>()));
+
     // Push_back to toolInput
-    toolInputs.emplace_back(newd, eventContext, roi, previousDecision);
+    toolInputs.emplace_back(newd, eventContext, roiEL, previousDecision);
 
     // Link to new decision
     linkToPrevious(newd, previousDecision, eventContext);
diff --git a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterToolBase.cxx b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterToolBase.cxx
index 3c57fc6922de0eea271334b59d531b44ff4a78b8..500dd708c0ec7da436b6ad38e2a0b0d09a12d075 100644
--- a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterToolBase.cxx
+++ b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/PEBInfoWriterToolBase.cxx
@@ -28,6 +28,7 @@ PEBInfoWriterToolBase::~PEBInfoWriterToolBase() {}
 // =============================================================================
 
 StatusCode PEBInfoWriterToolBase::decide(std::vector<Input>& inputs) const {
+  std::set<ElementLink<TrigRoiDescriptorCollection>> uniqueRoIs;
   for (Input& input : inputs) {
     // Skip if previous step for this chain didn't pass
     if (not TrigCompositeUtils::passed(m_decisionId.numeric(), input.previousDecisionIDs)) {
@@ -35,20 +36,33 @@ StatusCode PEBInfoWriterToolBase::decide(std::vector<Input>& inputs) const {
       continue;
     }
 
+    // Count unique RoIs
+    bool isUnique = uniqueRoIs.insert(input.roiEL).second;
+    ATH_MSG_DEBUG("RoI eta/phi = " << (*input.roiEL)->eta() << "/" << (*input.roiEL)->phi() << " has "
+                  << (isUnique ? "not yet" : "already") << " been processed. So far seen "
+                  << uniqueRoIs.size() << " unique RoIs");
+
+    // Skip processing if max RoIs limit reached
+    if (m_maxRoIs>=0 && static_cast<int>(uniqueRoIs.size()) > m_maxRoIs) {
+      ATH_MSG_DEBUG("Skipping this input decision because number of processed RoIs reached MaxRoIs ("
+                    << m_maxRoIs.value() << ")");
+      // Make an accept decision without adding PEB Info (PEB hypo is pass-through)
+      TrigCompositeUtils::addDecisionID(m_decisionId, input.decision);
+      continue;
+    }
+
     // Create new PEB Info for this input
     PEBInfo pebInfo = createPEBInfo(input);
 
     // Merge with previous ROBs    
     std::vector<uint32_t> previousRobs;
-    if (input.decision->getDetail(robListKey(), previousRobs)) {
-      pebInfo.robs.insert(previousRobs.begin(), previousRobs.end());
-    }
+    ATH_CHECK(input.decision->getDetail(robListKey(), previousRobs));
+    pebInfo.robs.insert(previousRobs.begin(), previousRobs.end());
 
     // Merge with previous SubDets
     std::vector<uint32_t> previousSubDets;
-    if (input.decision->getDetail(subDetListKey(), previousSubDets)) {
-      pebInfo.subdets.insert(previousSubDets.begin(), previousSubDets.end());
-    }
+    ATH_CHECK(input.decision->getDetail(subDetListKey(), previousSubDets));
+    pebInfo.subdets.insert(previousSubDets.begin(), previousSubDets.end());
 
     // Attach the PEB Info to the decision
     std::vector<uint32_t> robVec(pebInfo.robs.begin(), pebInfo.robs.end());
diff --git a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/RoIPEBInfoWriterTool.cxx b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/RoIPEBInfoWriterTool.cxx
index eb001c8efbdb8b67e9f20351716e4fbd7ada8b97..5823bcb2dd20a7c13b242f2549b89ad0e7fcc17e 100644
--- a/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/RoIPEBInfoWriterTool.cxx
+++ b/Trigger/TrigAlgorithms/TrigPartialEventBuilding/src/RoIPEBInfoWriterTool.cxx
@@ -3,7 +3,7 @@
 */
 
 #include "RoIPEBInfoWriterTool.h"
-#include "GaudiKernel/PhysicalConstants.h" // for Gaudi::Units::twopi
+#include "CxxUtils/phihelper.h"
 #include <algorithm>
 #include <unordered_map>
 #include <string_view>
@@ -66,13 +66,8 @@ PEBInfoWriterToolBase::PEBInfo RoIPEBInfoWriterTool::createPEBInfo(const PEBInfo
   // Create output PEBInfo starting from the static extra PEBInfo
   PEBInfo pebi = m_extraPebInfo;
 
-  if (!input.roi) {
-    ATH_MSG_DEBUG("No RoI descriptor in the input for decision, skipping this decision");
-    return pebi;
-  }
-  ATH_MSG_DEBUG("Processing RoI " << *(input.roi));
-
-  float eta = input.roi->eta();
+  ATH_MSG_DEBUG("Processing RoI " << **(input.roiEL));
+  float eta = (*input.roiEL)->eta();
   float etaMin = eta - m_etaWidth;
   float etaMax = eta + m_etaWidth;
   // Stop further execution if RoI is entirely outside the max |eta| range
@@ -85,9 +80,9 @@ PEBInfoWriterToolBase::PEBInfo RoIPEBInfoWriterTool::createPEBInfo(const PEBInfo
   etaMin = std::max(-m_etaEdge.value(), etaMin);
   etaMax = std::min( m_etaEdge.value(), etaMin);
 
-  float phi = input.roi->eta();
-  float phiMin = std::remainder(phi-m_phiWidth, Gaudi::Units::twopi); // range (-pi, pi)
-  float phiMax = std::remainder(phi+m_phiWidth, Gaudi::Units::twopi); // range (-pi, pi)
+  float phi = (*input.roiEL)->phi();
+  float phiMin = CxxUtils::wrapToPi(phi - m_phiWidth); // range (-pi, pi)
+  float phiMax = CxxUtils::wrapToPi(phi + m_phiWidth); // range (-pi, pi)
 
   TrigRoiDescriptor roiForPEB(eta, etaMin, etaMax, phi, phiMin, phiMax);
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/CommonSequences/EventBuildingSequenceSetup.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/CommonSequences/EventBuildingSequenceSetup.py
index 06d20f66a81e7215348da2e7a57e4cd6fd203c90..424e5702c415f5415676f33fb35754f754bdc1cc 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/CommonSequences/EventBuildingSequenceSetup.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/CommonSequences/EventBuildingSequenceSetup.py
@@ -46,7 +46,7 @@ def pebInfoWriterTool(name, eventBuildType):
     if 'LArPEB' in eventBuildType:
         tool = RoIPEBInfoWriterToolCfg(name)
         tool.DetNames = ['PIXEL', 'SCT', 'TRT', 'TTEM', 'TTHEC', 'FCALEM', 'FCALHAD']
-        # TODO: tool.MaxRoIsPerEvent = 5
+        tool.MaxRoIs = 5
         tool.addHLTResultToROBList()  # add the main (full) HLT result to the list
         tool.addCTPResultToROBList()  # add the CTP result to the list
     elif 'RPCPEBSecondaryReadout' in eventBuildType: