diff --git a/Reconstruction/Jet/JetInterface/JetInterface/IJetProvider.h b/Reconstruction/Jet/JetInterface/JetInterface/IJetProvider.h
index ac0d0eab46c424b99b374e2cef128e80ebf371ea..abf24cf50ab6ed1acbcb835760231497f5d37cb3 100644
--- a/Reconstruction/Jet/JetInterface/JetInterface/IJetProvider.h
+++ b/Reconstruction/Jet/JetInterface/JetInterface/IJetProvider.h
@@ -53,6 +53,17 @@ public:
   /// from the templated JetProvider class provided below.
   virtual StatusCode getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetHandle) const = 0;
 
+  /// Method to allow the client to pass in a WriteHandle during
+  /// initialisation, in case this is needed for anything...
+  ///
+  /// The main (only?) use case is for copying jets, and propagating
+  /// any decorations already on the original to the copy in StoreGate
+  ///
+  /// Quietly return success in the general case -- the JetRecAlg
+  /// will always call this, so as to remain agnostic as to the
+  /// concrete type.
+  virtual StatusCode initWithOutput(const SG::WriteHandleKey<xAOD::JetContainer>&) {return StatusCode::SUCCESS;};
+
 };
 
 
@@ -65,8 +76,8 @@ template <typename CONCRETEAUX> class JetProvider
   : virtual public IJetProvider
 {
 
-public:
-
+  public:
+  
   StatusCode getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetHandle) const {
     std::unique_ptr<xAOD::JetContainer> jets(nullptr);
     std::unique_ptr<SG::IAuxStore> auxCont(nullptr);
diff --git a/Reconstruction/Jet/JetRec/CMakeLists.txt b/Reconstruction/Jet/JetRec/CMakeLists.txt
index f67252d4a75554d2d39649967657a4f4e4cff236..767c7545bc1890ba5453b871b06cc801277ea2b2 100644
--- a/Reconstruction/Jet/JetRec/CMakeLists.txt
+++ b/Reconstruction/Jet/JetRec/CMakeLists.txt
@@ -8,7 +8,7 @@ set( extra_libs )
 if( NOT GENERATIONBASE )
    list( APPEND extra_libs xAODPFlow )
    if( NOT XAOD_STANDALONE )
-      list( APPEND extra_libs AthenaMonitoringKernelLib )
+      list( APPEND extra_libs AthenaMonitoringKernelLib StoreGateLib )
    endif()
 endif()
 
diff --git a/Reconstruction/Jet/JetRec/JetRec/JetCopier.h b/Reconstruction/Jet/JetRec/JetRec/JetCopier.h
index bc7534bca579c46d1bc190ae24a13f26fd7a4909..5d6f7f9855c4ce7ab7dad90e8cf15b36bfc16916 100644
--- a/Reconstruction/Jet/JetRec/JetRec/JetCopier.h
+++ b/Reconstruction/Jet/JetRec/JetRec/JetCopier.h
@@ -19,10 +19,17 @@
 #include "AsgTools/PropertyWrapper.h"
 #include "AsgTools/AsgTool.h"
 #include "AsgDataHandles/ReadHandleKey.h"
+#include "AsgDataHandles/WriteHandleKey.h"
 #include "JetInterface/IJetProvider.h"
 #include "xAODJet/JetContainer.h"
 #include "xAODCore/ShallowAuxContainer.h"
 
+// This class doesn't (yet) exist for AnalysisBase, so in that release
+// we will simply have to rerun modifiers if we need them.
+#ifndef XAOD_ANALYSIS
+#include "StoreGate/ShallowCopyDecorDeps.h"
+#endif
+
 class JetCopier
   : public asg::AsgTool,
     virtual public IJetProvider
@@ -32,20 +39,35 @@ class JetCopier
   public:
     using asg::AsgTool::AsgTool;
 
+    // Called in parent initialize()
     virtual StatusCode initialize() override;
 
-    virtual StatusCode getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetHandle) const override;
+#ifndef XAOD_ANALYSIS
+    // Needed to initialise the ShallowCopyDecorDeps object, which propagates
+    // decorations on the original into the copy in StoreGate.
+    // Override interface implementation in Athena only
+    virtual StatusCode initWithOutput(const SG::WriteHandleKey<xAOD::JetContainer>& outputJets) override;
+#endif
 
+    // Called during execution
+    virtual StatusCode getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetHandle) const override;
     virtual std::pair<std::unique_ptr<xAOD::JetContainer>, std::unique_ptr<SG::IAuxStore> > getJets() const override;
+
+  private:
+
     virtual std::pair<std::unique_ptr<xAOD::JetContainer>, std::unique_ptr<SG::IAuxStore> > ShallowCopyJets() const;
     virtual std::pair<std::unique_ptr<xAOD::JetContainer>, std::unique_ptr<SG::IAuxStore> > DeepCopyJets() const;
 
-  private:
     // Handle Input JetContainer
     SG::ReadHandleKey<xAOD::JetContainer> m_inputJets {this, "InputJets", "", "Jet collection to be copied"};
 
     Gaudi::Property<bool> m_shallowCopy {this, "ShallowCopy", true, "True for shallow copy, false for deep copy"};
     Gaudi::Property<bool> m_shallowIO {this, "ShallowIO", false, "True for storing only modified data"};
+
+#ifndef XAOD_ANALYSIS
+    SG::ShallowCopyDecorDeps<xAOD::JetContainer> m_decorDeps { this, "DecorDeps", {},
+          "List of decorations to propagate through the shallow copy." };
+#endif
 };
 
 #endif
diff --git a/Reconstruction/Jet/JetRec/Root/JetCopier.cxx b/Reconstruction/Jet/JetRec/Root/JetCopier.cxx
index f27f31e349387dbebb9d0b139b9773718037c126..07297e9e3787ebbdc43af43af0827671695a6e59 100644
--- a/Reconstruction/Jet/JetRec/Root/JetCopier.cxx
+++ b/Reconstruction/Jet/JetRec/Root/JetCopier.cxx
@@ -35,6 +35,15 @@ StatusCode JetCopier::initialize() {
   return StatusCode::SUCCESS;
 }
 
+
+#ifndef XAOD_ANALYSIS
+// Setup helper to propagate decorations from original to copy
+StatusCode JetCopier::initWithOutput(const SG::WriteHandleKey<xAOD::JetContainer>& outputJets) {
+  return m_decorDeps.initialize(m_inputJets, outputJets) ;
+}
+#endif
+
+
 StatusCode JetCopier::getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetHandle) const {
   std::unique_ptr<xAOD::JetContainer> jets(nullptr);
   std::unique_ptr<SG::IAuxStore> auxCont(nullptr);
@@ -44,12 +53,19 @@ StatusCode JetCopier::getAndRecordJets(SG::WriteHandle<xAOD::JetContainer>& jetH
 
   if(m_shallowCopy){
     std::unique_ptr<xAOD::ShallowAuxContainer> auxCont_derived(static_cast<xAOD::ShallowAuxContainer*>(auxCont.release()));
-    return jetHandle.record(std::move(jets), std::move(auxCont_derived));
+    ATH_CHECK( jetHandle.record(std::move(jets), std::move(auxCont_derived)) );
+#ifndef XAOD_ANALYSIS
+    ATH_CHECK( m_decorDeps.linkDecors (m_inputJets) );
+#endif
   }
   else{
     std::unique_ptr<xAOD::JetAuxContainer> auxCont_derived(static_cast<xAOD::JetAuxContainer*>(auxCont.release()));
-    return jetHandle.record(std::move(jets), std::move(auxCont_derived));
+    ATH_CHECK( jetHandle.record(std::move(jets), std::move(auxCont_derived)) );
+#ifndef XAOD_ANALYSIS
+    ATH_CHECK( m_decorDeps.linkDecors (m_inputJets) );
+#endif
   }
+  return StatusCode::SUCCESS;
 }
 
 std::pair<std::unique_ptr<xAOD::JetContainer>,std::unique_ptr<SG::IAuxStore> > JetCopier::getJets() const {
diff --git a/Reconstruction/Jet/JetRec/src/JetRecAlg.cxx b/Reconstruction/Jet/JetRec/src/JetRecAlg.cxx
index a15742dbb8e79f6a8efc67c30a7994e2365d8c99..5368dde708bc6f9a7bf1b9fb5219a3ddca67fb23 100644
--- a/Reconstruction/Jet/JetRec/src/JetRecAlg.cxx
+++ b/Reconstruction/Jet/JetRec/src/JetRecAlg.cxx
@@ -17,7 +17,13 @@ using std::string;
 
 StatusCode JetRecAlg::initialize() {
 
+  ATH_CHECK(m_output.initialize());
+
   ATH_CHECK(m_jetprovider.retrieve());
+  // Some providers (e.g. copy) need the output WriteHandle
+  // to be provided during initialisation
+  ATH_CHECK(m_jetprovider->initWithOutput(m_output));
+
   ATH_MSG_INFO(" Initialized  IJetProvider : "<< m_jetprovider->name());
   
   ATH_MSG_INFO(" Initialize .... List of modifiers: ");
@@ -26,7 +32,6 @@ StatusCode JetRecAlg::initialize() {
     ATH_MSG_INFO("    --> : "<< t->name());
   }
 
-  ATH_CHECK(m_output.initialize());
   return StatusCode::SUCCESS;
 }
 
diff --git a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
index 4e1950ae4a471c49ab38b9f3226829eae8220240..beeaf5bfbcbfdbdd42620d4f734887b199eeb358 100644
--- a/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
+++ b/Reconstruction/Jet/JetRecConfig/python/JetRecConfig.py
@@ -325,11 +325,15 @@ def getJetRecAlg( jetdef):
 # these may be set up already in the original jet collection
 # In future we may wish to add a toggle.
 #
-def getJetCopyAlg(jetsin, jetsoutdef, shallowcopy=True, shallowIO=True):
+# The decoration list can be set in order for the decorations
+# (jet moments) on the original jets to be propagated to the
+# copy collection. Beware of circular dependencies!
+def getJetCopyAlg(jetsin, jetsoutdef, decorations=[], shallowcopy=True, shallowIO=True):
 
     jcopy = CompFactory.JetCopier(
         "copier",
         InputJets = jetsin,
+        DecorDeps=decorations,
         ShallowCopy=shallowcopy,
         ShallowIO=shallowIO)
 
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
index 1f91b6668fff7de7ab9e72a799b63f4bcdc2b40d..6a11c5ec9050038a4c228efb0a0def56d2956407 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoConfiguration.py
@@ -263,3 +263,19 @@ def defineCalibMods(jetRecoDict,dataSource,rhoKey="auto"):
                          "Calib:"+calibSpec]
 
     return calibMods
+
+def getDecorList(doTracks,isPFlow):
+    # Basic jet info provided by the jet builder
+    decorlist = [ 'AlgorithmType', 'InputType' 
+                  'ActiveArea', 'ActiveArea4vec_eta', 'ActiveArea4vec_m',
+                  'ActiveArea4vec_phi', 'ActiveArea4vec_pt']
+
+    if doTracks:
+        decorlist += ["GhostTrack",
+                      "NumTrkPt500","NumTrkPt1000",
+                      "SumPtTrkPt500","SumPtTrkPt1000",
+                      "TrackWidthPt1000",
+                      "JVFCorr"]
+        if isPFlow:
+            decorlist += ["SumPtChargedPFOPt500"]
+    return decorlist
diff --git a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
index 97d40f5a0de1e22e94b2605009fb046dde463195..c9bb7f3ee3b315613984e1a341f2554afe2a215e 100644
--- a/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
+++ b/Trigger/TriggerCommon/TriggerMenuMT/python/HLTMenuConfig/Jet/JetRecoSequences.py
@@ -77,11 +77,16 @@ def jetRecoSequence( configFlags, clustersKey, **jetRecoDict ):
 def standardJetBuildSequence( configFlags, dataSource, clustersKey, **jetRecoDict ):
     jetDefString = jetRecoDictToString(jetRecoDict)
     buildSeq = parOR( "JetBuildSeq_"+jetDefString, [])
-    trkcolls = getTrkColls(jetRecoDict) if jetRecoDict["trkopt"]!="notrk" else {}
+    doesTracking = jetRecoDict["trkopt"]!="notrk"
+    trkcolls = getTrkColls(jetRecoDict) if doesTracking else {}
+    if doesTracking and not trkcolls:
+        raise RuntimeError("Failed to retrieve track collections for trkopt '{}'".format(jetRecoDict["trkopt"]))
+
+    isPFlow = "pf" in jetRecoDict["dataType"]
 
     # Add particle flow reconstruction if needed
-    if "pf" in jetRecoDict["dataType"]:
-        if not trkcolls:
+    if isPFlow:
+        if not doesTracking:
             raise RuntimeError("PFlow jet chain requested with no tracking option!")
         from eflowRec.PFHLTSequence import PFHLTSequence
         (pfseq, pfoPrefix) = RecoFragmentsPool.retrieve(
@@ -114,11 +119,10 @@ def standardJetBuildSequence( configFlags, dataSource, clustersKey, **jetRecoDic
     # Basic list of PseudoJets is just the constituents
     # Append ghosts (tracks) if desired
     pjs = [constitPJKey]
-    if trkcolls:
-        pjs.append(trkcolls["GhostTracks"])
-
+    # Also compile modifier list
     jetModList = []
-    if trkcolls:
+    if doesTracking:
+        pjs.append(trkcolls["GhostTracks"])
         trkMods = JetRecoConfiguration.defineTrackMods(jetRecoDict["trkopt"])
         jetModList += trkMods
 
@@ -177,8 +181,13 @@ def standardJetRecoSequence( configFlags, dataSource, clustersKey, **jetRecoDict
         rhoKey = str(eventShapeAlg.EventDensityTool.OutputContainer)
 
     jetDef.modifiers = JetRecoConfiguration.defineCalibMods(jetRecoDict,dataSource,rhoKey)
-    jetDef.modifiers += jetDefNoCalib.modifiers[:-2] # Leave off sort + filter
-    copyCalibAlg = JetRecConfig.getJetCopyAlg(jetsin=jetsNoCalib,jetsoutdef=jetDef)
+    # If we need JVT, just rerun the JVT modifier
+    doesTracking = jetRecoDict["trkopt"] != "notrk"
+    isPFlow = "pf" in jetRecoDict["dataType"]
+    if doesTracking:
+        jetDef.modifiers.append("JVT:"+jetRecoDict["trkopt"])
+    decorList = JetRecoConfiguration.getDecorList(doesTracking,isPFlow)
+    copyCalibAlg = JetRecConfig.getJetCopyAlg(jetsin=jetsNoCalib,jetsoutdef=jetDef,decorations=decorList)
 
     recoSeq += copyCalibAlg