diff --git a/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql b/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
index 8dc0c3d7f6b1..d49ace967ca8 100644
--- a/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
+++ b/cpp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
@@ -9,5 +9,5 @@
 import internal.CaptureModels
 
 from DataFlowSummaryTargetApi api, string flow
-where flow = ContentSensitive::captureFlow(api, _)
+where flow = ContentSensitive::captureFlow(api, _, _)
 select flow order by flow
diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql
index 0156eaaeb988..a73cc1631989 100644
--- a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql
+++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureContentSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import InlineModelsAsDataTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(MadRelevantFunction c) { result = ContentSensitive::captureFlow(c, _) }
+  string getCapturedModel(MadRelevantFunction c) { result = ContentSensitive::captureFlow(c, _, _) }
 
   string getKind() { result = "contentbased-summary" }
 }
diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
index 3ab1dc6c4710..14423e8c078c 100644
--- a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
+++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import InlineModelsAsDataTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(MadRelevantFunction c) { result = Heuristic::captureFlow(c) }
+  string getCapturedModel(MadRelevantFunction c) { result = Heuristic::captureHeuristicFlow(c, _) }
 
   string getKind() { result = "heuristic-summary" }
 }
diff --git a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp
index 74869a69994e..d4f96f67ff35 100644
--- a/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp
+++ b/cpp/ql/test/library-tests/dataflow/modelgenerator/dataflow/summaries.cpp
@@ -10,32 +10,32 @@ namespace Models {
         //No model as destructors are excluded from model generation.
         ~BasicFlow() = default;
 
-        //heuristic-summary=Models;BasicFlow;true;returnThis;(int *);;Argument[-1];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnThis;(int *);;Argument[-1];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;BasicFlow;true;returnThis;(int *);;Argument[-1];ReturnValue[*];value;dfc-generated
         BasicFlow* returnThis(int* input) {
             return this;
         }
 
-        //heuristic-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[0];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[*0];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[0];ReturnValue;value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[*0];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[0];ReturnValue;value;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnParam0;(int *,int *);;Argument[*0];ReturnValue[*];value;dfc-generated
         int* returnParam0(int* input0, int* input1) {
             return input0;
         }
 
-        //heuristic-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[1];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[*1];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[1];ReturnValue;value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[*1];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[1];ReturnValue;value;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnParam1;(int *,int *);;Argument[*1];ReturnValue[*];value;dfc-generated
         int* returnParam1(int* input0, int* input1) {
             return input1;
         }
 
-        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[1];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*1];ReturnValue[*];taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[2];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*2];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[1];ReturnValue;value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*1];ReturnValue[*];value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[2];ReturnValue;value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*2];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[1];ReturnValue;value;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[*1];ReturnValue[*];value;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnParamMultiple;(bool,int *,int *);;Argument[2];ReturnValue;value;dfc-generated
@@ -46,9 +46,9 @@ namespace Models {
 
         //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];Argument[*1];taint;df-generated
         //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];ReturnValue[*];taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];ReturnValue[*];taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[1];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];Argument[*1];taint;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];ReturnValue[*];value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[1];ReturnValue;value;df-generated
+        //heuristic-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];Argument[*1];value;df-generated
         //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];Argument[*1];taint;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[0];ReturnValue[*];taint;dfc-generated
         //contentbased-summary=Models;BasicFlow;true;returnSubstring;(const char *,char *);;Argument[*0];ReturnValue[*];value;dfc-generated
@@ -79,14 +79,14 @@ namespace Models {
     struct TemplatedFlow {
         T tainted;
 
-        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnThis;(T);;Argument[-1];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnThis;(T);;Argument[-1];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;TemplatedFlow<T>;true;template_returnThis;(T);;Argument[-1];ReturnValue[*];value;dfc-generated
         TemplatedFlow<T>* template_returnThis(T input) {
             return this;
         }
 
-        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[0];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[*0];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[0];ReturnValue;value;df-generated
+        //heuristic-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[*0];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[0];ReturnValue;value;dfc-generated
         //contentbased-summary=Models;TemplatedFlow<T>;true;template_returnParam0;(T *,T *);;Argument[*0];ReturnValue[*];value;dfc-generated
         T* template_returnParam0(T* input0, T* input1) {
@@ -105,8 +105,8 @@ namespace Models {
             return tainted;
         }
 
-        //heuristic-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[0];ReturnValue;taint;df-generated
-        //heuristic-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[*0];ReturnValue[*];taint;df-generated
+        //heuristic-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[0];ReturnValue;value;df-generated
+        //heuristic-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[*0];ReturnValue[*];value;df-generated
         //contentbased-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[0];ReturnValue;value;dfc-generated
         //contentbased-summary=Models;TemplatedFlow<T>;true;templated_function<U>;(U *,T *);;Argument[*0];ReturnValue[*];value;dfc-generated
         template<typename U>
@@ -130,7 +130,7 @@ namespace Models {
 }
 
 //heuristic-summary=;;true;toplevel_function;(int *);;Argument[0];ReturnValue;taint;df-generated
-//heuristic-summary=;;true;toplevel_function;(int *);;Argument[*0];ReturnValue;taint;df-generated
+//heuristic-summary=;;true;toplevel_function;(int *);;Argument[*0];ReturnValue;value;df-generated
 //heuristic-summary=;;true;toplevel_function;(int *);;Argument[0];Argument[*0];taint;df-generated
 //contentbased-summary=;;true;toplevel_function;(int *);;Argument[0];Argument[*0];taint;dfc-generated
 //contentbased-summary=;;true;toplevel_function;(int *);;Argument[0];ReturnValue;taint;dfc-generated
@@ -145,13 +145,13 @@ static int static_toplevel_function(int* p) {
 }
 
 struct NonFinalStruct {
-    //heuristic-summary=;NonFinalStruct;true;public_not_final_member_function;(int);;Argument[0];ReturnValue;taint;df-generated
+    //heuristic-summary=;NonFinalStruct;true;public_not_final_member_function;(int);;Argument[0];ReturnValue;value;df-generated
     //contentbased-summary=;NonFinalStruct;true;public_not_final_member_function;(int);;Argument[0];ReturnValue;value;dfc-generated
     virtual int public_not_final_member_function(int x) {
         return x;
     }
 
-    //heuristic-summary=;NonFinalStruct;false;public_final_member_function;(int);;Argument[0];ReturnValue;taint;df-generated
+    //heuristic-summary=;NonFinalStruct;false;public_final_member_function;(int);;Argument[0];ReturnValue;value;df-generated
     //contentbased-summary=;NonFinalStruct;false;public_final_member_function;(int);;Argument[0];ReturnValue;value;dfc-generated
     virtual int public_final_member_function(int x) final {
         return x;
@@ -171,13 +171,13 @@ struct NonFinalStruct {
 };
 
 struct FinalStruct final {
-    //heuristic-summary=;FinalStruct;false;public_not_final_member_function_2;(int);;Argument[0];ReturnValue;taint;df-generated
+    //heuristic-summary=;FinalStruct;false;public_not_final_member_function_2;(int);;Argument[0];ReturnValue;value;df-generated
     //contentbased-summary=;FinalStruct;false;public_not_final_member_function_2;(int);;Argument[0];ReturnValue;value;dfc-generated
     virtual int public_not_final_member_function_2(int x) {
         return x;
     }
 
-    //heuristic-summary=;FinalStruct;false;public_final_member_function_2;(int);;Argument[0];ReturnValue;taint;df-generated
+    //heuristic-summary=;FinalStruct;false;public_final_member_function_2;(int);;Argument[0];ReturnValue;value;df-generated
     //contentbased-summary=;FinalStruct;false;public_final_member_function_2;(int);;Argument[0];ReturnValue;value;dfc-generated
     virtual int public_final_member_function_2(int x) final {
         return x;
@@ -211,7 +211,7 @@ struct HasInt {
 //contentbased-summary=;;true;copy_struct;(HasInt *,const HasInt *);;Argument[*1];Argument[*0];value;dfc-generated
 //heuristic-summary=;;true;copy_struct;(HasInt *,const HasInt *);;Argument[1];Argument[*0];taint;df-generated
 //heuristic-summary=;;true;copy_struct;(HasInt *,const HasInt *);;Argument[1];Argument[*1];taint;df-generated
-//heuristic-summary=;;true;copy_struct;(HasInt *,const HasInt *);;Argument[*1];Argument[*0];taint;df-generated
+//heuristic-summary=;;true;copy_struct;(HasInt *,const HasInt *);;Argument[*1];Argument[*0];value;df-generated
 int copy_struct(HasInt *out, const HasInt *in) {
     *out = *in;
     return 1;
diff --git a/csharp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql b/csharp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
index 039c96a9a0bc..c108029e3df3 100644
--- a/csharp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
+++ b/csharp/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
@@ -9,5 +9,5 @@
 import internal.CaptureModels
 
 from DataFlowSummaryTargetApi api, string flow
-where flow = ContentSensitive::captureFlow(api, _)
+where flow = ContentSensitive::captureFlow(api, _, _)
 select flow order by flow
diff --git a/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql b/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
index beb14cd8e627..979a129e5652 100644
--- a/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
+++ b/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
@@ -14,7 +14,7 @@ import PartialFlow::PartialPathGraph
 
 int explorationLimit() { result = 3 }
 
-module PartialFlow = Heuristic::PropagateFlow::FlowExplorationFwd<explorationLimit/0>;
+module PartialFlow = Heuristic::PropagateTaintFlow::FlowExplorationFwd<explorationLimit/0>;
 
 from
   PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink,
diff --git a/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql b/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
index e3de78767eaa..9d51b60ec2ec 100644
--- a/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
+++ b/csharp/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
@@ -11,15 +11,15 @@
 import csharp
 import utils.modelgenerator.internal.CaptureModels
 import Heuristic
-import PropagateFlow::PathGraph
+import PropagateTaintFlow::PathGraph
 
 from
-  PropagateFlow::PathNode source, PropagateFlow::PathNode sink, DataFlowSummaryTargetApi api,
-  DataFlow::Node p, DataFlow::Node returnNodeExt
+  PropagateTaintFlow::PathNode source, PropagateTaintFlow::PathNode sink,
+  DataFlowSummaryTargetApi api, DataFlow::Node p, DataFlow::Node returnNodeExt
 where
-  PropagateFlow::flowPath(source, sink) and
+  PropagateTaintFlow::flowPath(source, sink) and
   p = source.getNode() and
   returnNodeExt = sink.getNode() and
-  exists(captureThroughFlow0(api, p, returnNodeExt))
+  captureThroughFlow0(api, p, returnNodeExt)
 select sink.getNode(), source, sink, "There is flow from $@ to the $@.", source.getNode(),
   "parameter", sink.getNode(), "return value"
diff --git a/csharp/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql b/csharp/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
index 0d9e4cd52d9f..fe575790af0e 100644
--- a/csharp/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
+++ b/csharp/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import utils.test.InlineMadTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(Callable c) { result = ContentSensitive::captureFlow(c, _) }
+  string getCapturedModel(Callable c) { result = ContentSensitive::captureFlow(c, _, _) }
 
   string getKind() { result = "contentbased-summary" }
 }
diff --git a/csharp/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql b/csharp/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
index 24cb66e427e7..d8e71b5e7208 100644
--- a/csharp/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
+++ b/csharp/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import utils.test.InlineMadTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(Callable c) { result = Heuristic::captureFlow(c) }
+  string getCapturedModel(Callable c) { result = Heuristic::captureHeuristicFlow(c, _) }
 
   string getKind() { result = "heuristic-summary" }
 }
diff --git a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
index f1dbc02512ab..4fc6fe05c0db 100644
--- a/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
+++ b/csharp/ql/test/utils/modelgenerator/dataflow/Summaries.cs
@@ -19,22 +19,22 @@ public BasicFlow ReturnThis(object input)
         return this;
     }
 
-    // heuristic-summary=Models;BasicFlow;false;ReturnParam0;(System.String,System.Object);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;BasicFlow;false;ReturnParam0;(System.String,System.Object);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;BasicFlow;false;ReturnParam0;(System.String,System.Object);;Argument[0];ReturnValue;value;dfc-generated
     public string ReturnParam0(string input0, object input1)
     {
         return input0;
     }
 
-    // heuristic-summary=Models;BasicFlow;false;ReturnParam1;(System.String,System.Object);;Argument[1];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;BasicFlow;false;ReturnParam1;(System.String,System.Object);;Argument[1];ReturnValue;value;df-generated
     // contentbased-summary=Models;BasicFlow;false;ReturnParam1;(System.String,System.Object);;Argument[1];ReturnValue;value;dfc-generated
     public object ReturnParam1(string input0, object input1)
     {
         return input1;
     }
 
-    // heuristic-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[0];ReturnValue;taint;df-generated
-    // heuristic-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[1];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[0];ReturnValue;value;df-generated
+    // heuristic-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[1];ReturnValue;value;df-generated
     // contentbased-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[0];ReturnValue;value;dfc-generated
     // contentbased-summary=Models;BasicFlow;false;ReturnParamMultiple;(System.Object,System.Object);;Argument[1];ReturnValue;value;dfc-generated
     public object ReturnParamMultiple(object input0, object input1)
@@ -133,35 +133,35 @@ public List<string> ReturnFieldInAList()
         return new List<string> { tainted };
     }
 
-    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnComplexTypeArray;(System.String[]);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnComplexTypeArray;(System.String[]);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;CollectionFlow;false;ReturnComplexTypeArray;(System.String[]);;Argument[0];ReturnValue;value;dfc-generated
     public string[] ReturnComplexTypeArray(string[] a)
     {
         return a;
     }
 
-    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnBulkTypeList;(System.Collections.Generic.List<System.Byte>);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnBulkTypeList;(System.Collections.Generic.List<System.Byte>);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;CollectionFlow;false;ReturnBulkTypeList;(System.Collections.Generic.List<System.Byte>);;Argument[0];ReturnValue;value;dfc-generated
     public List<byte> ReturnBulkTypeList(List<byte> a)
     {
         return a;
     }
 
-    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnComplexTypeDictionary;(System.Collections.Generic.Dictionary<System.Int32,System.String>);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnComplexTypeDictionary;(System.Collections.Generic.Dictionary<System.Int32,System.String>);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;CollectionFlow;false;ReturnComplexTypeDictionary;(System.Collections.Generic.Dictionary<System.Int32,System.String>);;Argument[0];ReturnValue;value;dfc-generated
     public Dictionary<int, string> ReturnComplexTypeDictionary(Dictionary<int, string> a)
     {
         return a;
     }
 
-    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnUntypedArray;(System.Array);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnUntypedArray;(System.Array);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;CollectionFlow;false;ReturnUntypedArray;(System.Array);;Argument[0];ReturnValue;value;dfc-generated
     public Array ReturnUntypedArray(Array a)
     {
         return a;
     }
 
-    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnUntypedList;(System.Collections.IList);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;CollectionFlow;false;ReturnUntypedList;(System.Collections.IList);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;CollectionFlow;false;ReturnUntypedList;(System.Collections.IList);;Argument[0];ReturnValue;value;dfc-generated
     public IList ReturnUntypedList(IList a)
     {
@@ -202,7 +202,7 @@ public IEnumerableFlow(string s)
         tainted = s;
     }
 
-    // SPURIOUS-heuristic-summary=Models;IEnumerableFlow;false;ReturnIEnumerable;(System.Collections.Generic.IEnumerable<System.String>);;Argument[0].Element;ReturnValue;taint;df-generated
+    // SPURIOUS-heuristic-summary=Models;IEnumerableFlow;false;ReturnIEnumerable;(System.Collections.Generic.IEnumerable<System.String>);;Argument[0].Element;ReturnValue;value;df-generated
     // contentbased-summary=Models;IEnumerableFlow;false;ReturnIEnumerable;(System.Collections.Generic.IEnumerable<System.String>);;Argument[0];ReturnValue;value;dfc-generated
     public IEnumerable<string> ReturnIEnumerable(IEnumerable<string> input)
     {
@@ -256,7 +256,7 @@ public List<T> ReturnFieldInGenericList()
         return new List<T> { tainted };
     }
 
-    // heuristic-summary=Models;GenericFlow<T>;false;ReturnGenericParam<S>;(S);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;GenericFlow<T>;false;ReturnGenericParam<S>;(S);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;GenericFlow<T>;false;ReturnGenericParam<S>;(S);;Argument[0];ReturnValue;value;dfc-generated
     public S ReturnGenericParam<S>(S input)
     {
@@ -280,7 +280,7 @@ public void AddToGenericList<S>(List<S> input, S data)
 
 public abstract class BaseClassFlow
 {
-    // heuristic-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;value;dfc-generated
     public virtual object ReturnParam(object input)
     {
@@ -290,7 +290,7 @@ public virtual object ReturnParam(object input)
 
 public class DerivedClass1Flow : BaseClassFlow
 {
-    // heuristic-summary=Models;DerivedClass1Flow;false;ReturnParam1;(System.String,System.String);;Argument[1];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;DerivedClass1Flow;false;ReturnParam1;(System.String,System.String);;Argument[1];ReturnValue;value;df-generated
     // contentbased-summary=Models;DerivedClass1Flow;false;ReturnParam1;(System.String,System.String);;Argument[1];ReturnValue;value;dfc-generated
     public string ReturnParam1(string input0, string input1)
     {
@@ -300,14 +300,14 @@ public string ReturnParam1(string input0, string input1)
 
 public class DerivedClass2Flow : BaseClassFlow
 {
-    // heuristic-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;BaseClassFlow;true;ReturnParam;(System.Object);;Argument[0];ReturnValue;value;dfc-generated
     public override object ReturnParam(object input)
     {
         return input;
     }
 
-    // heuristic-summary=Models;DerivedClass2Flow;false;ReturnParam0;(System.String,System.Int32);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;DerivedClass2Flow;false;ReturnParam0;(System.String,System.Int32);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;DerivedClass2Flow;false;ReturnParam0;(System.String,System.Int32);;Argument[0];ReturnValue;value;dfc-generated
     public string ReturnParam0(string input0, int input1)
     {
@@ -327,7 +327,7 @@ public OperatorFlow(object o)
     }
 
     // Flow Summary.
-    // heuristic-summary=Models;OperatorFlow;false;op_Addition;(Models.OperatorFlow,Models.OperatorFlow);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;OperatorFlow;false;op_Addition;(Models.OperatorFlow,Models.OperatorFlow);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;OperatorFlow;false;op_Addition;(Models.OperatorFlow,Models.OperatorFlow);;Argument[0];ReturnValue;value;dfc-generated
     public static OperatorFlow operator +(OperatorFlow a, OperatorFlow b)
     {
@@ -368,7 +368,7 @@ public override bool Equals(object obj)
         return boolTainted;
     }
 
-    // heuristic-summary=Models;EqualsGetHashCodeNoFlow;false;Equals;(System.String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;EqualsGetHashCodeNoFlow;false;Equals;(System.String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=Models;EqualsGetHashCodeNoFlow;false;Equals;(System.String);;Argument[0];ReturnValue;value;dfc-generated
     public string Equals(string s)
     {
@@ -606,7 +606,7 @@ public abstract class BasePublic
 
     public class AImplBasePublic : BasePublic
     {
-        // heuristic-summary=Models;Inheritance+BasePublic;true;Id;(System.String);;Argument[0];ReturnValue;taint;df-generated
+        // heuristic-summary=Models;Inheritance+BasePublic;true;Id;(System.String);;Argument[0];ReturnValue;value;df-generated
         // contentbased-summary=Models;Inheritance+BasePublic;true;Id;(System.String);;Argument[0];ReturnValue;value;dfc-generated
         public override string Id(string x)
         {
@@ -636,7 +636,7 @@ private abstract class C : IPublic2
 
     public class BImpl : B
     {
-        // heuristic-summary=Models;Inheritance+IPublic1;true;Id;(System.String);;Argument[0];ReturnValue;taint;df-generated
+        // heuristic-summary=Models;Inheritance+IPublic1;true;Id;(System.String);;Argument[0];ReturnValue;value;df-generated
         // contentbased-summary=Models;Inheritance+IPublic1;true;Id;(System.String);;Argument[0];ReturnValue;value;dfc-generated
         public override string Id(string x)
         {
@@ -646,7 +646,7 @@ public override string Id(string x)
 
     private class CImpl : C
     {
-        // heuristic-summary=Models;Inheritance+IPublic2;true;Id;(System.String);;Argument[0];ReturnValue;taint;df-generated
+        // heuristic-summary=Models;Inheritance+IPublic2;true;Id;(System.String);;Argument[0];ReturnValue;value;df-generated
         // contentbased-summary=Models;Inheritance+IPublic2;true;Id;(System.String);;Argument[0];ReturnValue;value;dfc-generated
         public override string Id(string x)
         {
@@ -656,13 +656,11 @@ public override string Id(string x)
 
     public interface IPublic3
     {
-        // neutral=Models;Inheritance+IPublic3;get_Prop;();summary;df-generated
         string Prop { get; }
     }
 
     public abstract class D : IPublic3
     {
-        // neutral=Models;Inheritance+D;get_Prop;();summary;df-generated
         public abstract string Prop { get; }
     }
 
@@ -929,7 +927,6 @@ public class Fanout
 
     public abstract class Base1
     {
-        // neutral=Models;Fanout+Base1;GetValue;();summary;df-generated
         public abstract string GetValue();
     }
 
@@ -1035,14 +1032,14 @@ public override object GetValue()
 public class ParameterModifiers
 {
     // contentbased-summary=Models;ParameterModifiers;false;Copy;(System.Object,System.Object);;Argument[0];Argument[1];value;dfc-generated
-    // heuristic-summary=Models;ParameterModifiers;false;Copy;(System.Object,System.Object);;Argument[0];Argument[1];taint;df-generated
+    // heuristic-summary=Models;ParameterModifiers;false;Copy;(System.Object,System.Object);;Argument[0];Argument[1];value;df-generated
     public void Copy(object key, out object value)
     {
         value = key;
     }
 
     // contentbased-summary=Models;ParameterModifiers;false;CopyToRef;(System.Object,System.Object);;Argument[0];Argument[1];value;dfc-generated
-    // heuristic-summary=Models;ParameterModifiers;false;CopyToRef;(System.Object,System.Object);;Argument[0];Argument[1];taint;df-generated
+    // heuristic-summary=Models;ParameterModifiers;false;CopyToRef;(System.Object,System.Object);;Argument[0];Argument[1];value;df-generated
     public void CopyToRef(object key, ref object value)
     {
         value = key;
@@ -1062,7 +1059,7 @@ public void RefParamUse(ref object value)
     }
 
     // contentbased-summary=Models;ParameterModifiers;false;InReturn;(System.Object);;Argument[0];ReturnValue;value;dfc-generated
-    // heuristic-summary=Models;ParameterModifiers;false;InReturn;(System.Object);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=Models;ParameterModifiers;false;InReturn;(System.Object);;Argument[0];ReturnValue;value;df-generated
     public object InReturn(in object v)
     {
         return v;
diff --git a/java/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql b/java/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
index b1340e2c0d33..6d209a4c50d7 100644
--- a/java/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
+++ b/java/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
@@ -9,5 +9,5 @@
 import internal.CaptureModels
 
 from DataFlowSummaryTargetApi api, string flow
-where flow = ContentSensitive::captureFlow(api, _)
+where flow = ContentSensitive::captureFlow(api, _, _)
 select flow order by flow
diff --git a/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql b/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
index 8895fdaefbb3..16d202f27888 100644
--- a/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
+++ b/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
@@ -15,7 +15,7 @@ import PartialFlow::PartialPathGraph
 
 int explorationLimit() { result = 3 }
 
-module PartialFlow = Heuristic::PropagateFlow::FlowExplorationFwd<explorationLimit/0>;
+module PartialFlow = Heuristic::PropagateTaintFlow::FlowExplorationFwd<explorationLimit/0>;
 
 from
   PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink,
diff --git a/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql b/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
index 8f6bf1c1f531..57468f6ac051 100644
--- a/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
+++ b/java/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
@@ -12,15 +12,15 @@ import java
 import semmle.code.java.dataflow.DataFlow
 import utils.modelgenerator.internal.CaptureModels
 import Heuristic
-import PropagateFlow::PathGraph
+import PropagateTaintFlow::PathGraph
 
 from
-  PropagateFlow::PathNode source, PropagateFlow::PathNode sink, DataFlowSummaryTargetApi api,
-  DataFlow::Node p, DataFlow::Node returnNodeExt
+  PropagateTaintFlow::PathNode source, PropagateTaintFlow::PathNode sink,
+  DataFlowSummaryTargetApi api, DataFlow::Node p, DataFlow::Node returnNodeExt
 where
-  PropagateFlow::flowPath(source, sink) and
+  PropagateTaintFlow::flowPath(source, sink) and
   p = source.getNode() and
   returnNodeExt = sink.getNode() and
-  exists(captureThroughFlow0(api, p, returnNodeExt))
+  captureThroughFlow0(api, p, returnNodeExt)
 select sink.getNode(), source, sink, "There is flow from $@ to the $@.", source.getNode(),
   "parameter", sink.getNode(), "return value"
diff --git a/java/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql b/java/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
index 8dd23714fb79..1ee494a849a2 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
+++ b/java/ql/test/utils/modelgenerator/dataflow/CaptureContentSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import utils.test.InlineMadTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(Callable c) { result = ContentSensitive::captureFlow(c, _) }
+  string getCapturedModel(Callable c) { result = ContentSensitive::captureFlow(c, _, _) }
 
   string getKind() { result = "contentbased-summary" }
 }
diff --git a/java/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql b/java/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
index 45485a8009a5..6b07aa87da8d 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
+++ b/java/ql/test/utils/modelgenerator/dataflow/CaptureHeuristicSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import utils.test.InlineMadTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(Callable c) { result = Heuristic::captureFlow(c) }
+  string getCapturedModel(Callable c) { result = Heuristic::captureHeuristicFlow(c, _) }
 
   string getKind() { result = "heuristic-summary" }
 }
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/FinalClass.java b/java/ql/test/utils/modelgenerator/dataflow/p/FinalClass.java
index 993248f9bf80..431140c51541 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/FinalClass.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/FinalClass.java
@@ -4,7 +4,7 @@ public final class FinalClass {
 
   private static final String C = "constant";
 
-  // heuristic-summary=p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;taint;df-generated
+  // heuristic-summary=p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;value;df-generated
   // contentbased-summary=p;FinalClass;false;returnsInput;(String);;Argument[0];ReturnValue;value;dfc-generated
   public String returnsInput(String input) {
     return input;
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/ImmutablePojo.java b/java/ql/test/utils/modelgenerator/dataflow/p/ImmutablePojo.java
index 711d49cc1fc3..0f784ae859f8 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/ImmutablePojo.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/ImmutablePojo.java
@@ -24,7 +24,7 @@ public long getX() {
     return x;
   }
 
-  // heuristic-summary=p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;taint;df-generated
+  // heuristic-summary=p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;value;df-generated
   // heuristic-summary=p;ImmutablePojo;false;or;(String);;Argument[this];ReturnValue;taint;df-generated
   // contentbased-summary=p;ImmutablePojo;false;or;(String);;Argument[0];ReturnValue;value;dfc-generated
   // contentbased-summary=p;ImmutablePojo;false;or;(String);;Argument[this].SyntheticField[p.ImmutablePojo.value];ReturnValue;value;dfc-generated
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/Inheritance.java b/java/ql/test/utils/modelgenerator/dataflow/p/Inheritance.java
index 4253ee0d6ead..096c186cdc8a 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/Inheritance.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/Inheritance.java
@@ -10,7 +10,7 @@ public abstract class BasePublic {
   }
 
   public class AImplBasePrivateImpl extends BasePrivate {
-    // heuristic-summary=p;Inheritance$AImplBasePrivateImpl;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$AImplBasePrivateImpl;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$AImplBasePrivateImpl;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
@@ -19,7 +19,7 @@ public String id(String s) {
   }
 
   public class AImplBasePublic extends BasePublic {
-    // heuristic-summary=p;Inheritance$BasePublic;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$BasePublic;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$BasePublic;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
@@ -60,7 +60,7 @@ private abstract class E implements IPrivate2 {
   }
 
   public class BImpl extends B {
-    // heuristic-summary=p;Inheritance$IPublic1;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$IPublic1;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$IPublic1;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
@@ -69,7 +69,7 @@ public String id(String s) {
   }
 
   public class CImpl extends C {
-    // heuristic-summary=p;Inheritance$C;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$C;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$C;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
@@ -78,7 +78,7 @@ public String id(String s) {
   }
 
   public class DImpl extends D {
-    // heuristic-summary=p;Inheritance$IPublic2;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$IPublic2;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$IPublic2;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
@@ -87,7 +87,7 @@ public String id(String s) {
   }
 
   public class EImpl extends E {
-    // heuristic-summary=p;Inheritance$EImpl;true;id;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;Inheritance$EImpl;true;id;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;Inheritance$EImpl;true;id;(String);;Argument[0];ReturnValue;value;dfc-generated
     @Override
     public String id(String s) {
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/InnerClasses.java b/java/ql/test/utils/modelgenerator/dataflow/p/InnerClasses.java
index 283bcfd5c6e2..e0db1227b48f 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/InnerClasses.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/InnerClasses.java
@@ -9,14 +9,14 @@ public String no(String input) {
   }
 
   public class CaptureMe {
-    // heuristic-summary=p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;InnerClasses$CaptureMe;true;yesCm;(String);;Argument[0];ReturnValue;value;dfc-generated
     public String yesCm(String input) {
       return input;
     }
   }
 
-  // heuristic-summary=p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;taint;df-generated
+  // heuristic-summary=p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;value;df-generated
   // contentbased-summary=p;InnerClasses;true;yes;(String);;Argument[0];ReturnValue;value;dfc-generated
   public String yes(String input) {
     return input;
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/MultiPaths.java b/java/ql/test/utils/modelgenerator/dataflow/p/MultiPaths.java
index 11d2f8f76f83..fb03061eaaf7 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/MultiPaths.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/MultiPaths.java
@@ -2,7 +2,7 @@
 
 public class MultiPaths {
 
-  // heuristic-summary=p;MultiPaths;true;cond;(String,String);;Argument[0];ReturnValue;taint;df-generated
+  // heuristic-summary=p;MultiPaths;true;cond;(String,String);;Argument[0];ReturnValue;value;df-generated
   // contentbased-summary=p;MultiPaths;true;cond;(String,String);;Argument[0];ReturnValue;value;dfc-generated
   public String cond(String x, String other) {
     if (x == other) {
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpl2.java b/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpl2.java
index d0fd31613d65..7256e5345baa 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpl2.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpl2.java
@@ -16,7 +16,7 @@ public Object m(Object value) {
   }
 
   public class Impl2 implements IInterface {
-    // heuristic-summary=p;MultipleImpl2$IInterface;true;m;(Object);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;MultipleImpl2$IInterface;true;m;(Object);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;MultipleImpl2$IInterface;true;m;(Object);;Argument[0];ReturnValue;value;dfc-generated
     public Object m(Object value) {
       return value;
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpls.java b/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpls.java
index 5bdbb47fa483..60e16fbeb165 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpls.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/MultipleImpls.java
@@ -9,7 +9,7 @@ public static interface Strategy {
   }
 
   public static class Strat1 implements Strategy {
-    // heuristic-summary=p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;taint;df-generated
+    // heuristic-summary=p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;value;df-generated
     // contentbased-summary=p;MultipleImpls$Strategy;true;doSomething;(String);;Argument[0];ReturnValue;value;dfc-generated
     public String doSomething(String value) {
       return value;
diff --git a/java/ql/test/utils/modelgenerator/dataflow/p/ParamFlow.java b/java/ql/test/utils/modelgenerator/dataflow/p/ParamFlow.java
index 81b9602e5577..4c9d7427d75d 100644
--- a/java/ql/test/utils/modelgenerator/dataflow/p/ParamFlow.java
+++ b/java/ql/test/utils/modelgenerator/dataflow/p/ParamFlow.java
@@ -7,7 +7,7 @@
 
 public class ParamFlow {
 
-  // heuristic-summary=p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;taint;df-generated
+  // heuristic-summary=p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;value;df-generated
   // contentbased-summary=p;ParamFlow;true;returnsInput;(String);;Argument[0];ReturnValue;value;dfc-generated
   public String returnsInput(String input) {
     return input;
@@ -18,8 +18,8 @@ public int ignorePrimitiveReturnValue(String input) {
     return input.length();
   }
 
-  // heuristic-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;taint;df-generated
-  // heuristic-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;taint;df-generated
+  // heuristic-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;value;df-generated
+  // heuristic-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;value;df-generated
   // contentbased-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[0];ReturnValue;value;dfc-generated
   // contentbased-summary=p;ParamFlow;true;returnMultipleParameters;(String,String);;Argument[1];ReturnValue;value;dfc-generated
   public String returnMultipleParameters(String one, String two) {
diff --git a/rust/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql b/rust/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
index da90465197e5..a19a1b2398c7 100644
--- a/rust/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
+++ b/rust/ql/src/utils/modelgenerator/CaptureContentSummaryModels.ql
@@ -9,5 +9,5 @@
 import internal.CaptureModels
 
 from DataFlowSummaryTargetApi api, string flow
-where flow = ContentSensitive::captureFlow(api, _)
+where flow = ContentSensitive::captureFlow(api, _, _)
 select flow order by flow
diff --git a/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql b/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
index eb0cd638b534..49b8d56fdff0 100644
--- a/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
+++ b/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPartialPath.ql
@@ -14,7 +14,7 @@ import PartialFlow::PartialPathGraph
 
 int explorationLimit() { result = 3 }
 
-module PartialFlow = Heuristic::PropagateFlow::FlowExplorationFwd<explorationLimit/0>;
+module PartialFlow = Heuristic::PropagateTaintFlow::FlowExplorationFwd<explorationLimit/0>;
 
 from
   PartialFlow::PartialPathNode source, PartialFlow::PartialPathNode sink,
diff --git a/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql b/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
index 1ddec1ff618b..611faae5b410 100644
--- a/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
+++ b/rust/ql/src/utils/modelgenerator/debug/CaptureSummaryModelsPath.ql
@@ -11,15 +11,15 @@
 private import codeql.rust.dataflow.DataFlow
 import utils.modelgenerator.internal.CaptureModels
 import Heuristic
-import PropagateFlow::PathGraph
+import PropagateTaintFlow::PathGraph
 
 from
-  PropagateFlow::PathNode source, PropagateFlow::PathNode sink, DataFlowSummaryTargetApi api,
-  DataFlow::Node p, DataFlow::Node returnNodeExt
+  PropagateTaintFlow::PathNode source, PropagateTaintFlow::PathNode sink,
+  DataFlowSummaryTargetApi api, DataFlow::Node p, DataFlow::Node returnNodeExt
 where
-  PropagateFlow::flowPath(source, sink) and
+  PropagateTaintFlow::flowPath(source, sink) and
   p = source.getNode() and
   returnNodeExt = sink.getNode() and
-  exists(captureThroughFlow0(api, p, returnNodeExt))
+  captureThroughFlow0(api, p, returnNodeExt)
 select sink.getNode(), source, sink, "There is flow from $@ to the $@.", source.getNode(),
   "parameter", sink.getNode(), "return value"
diff --git a/rust/ql/test/utils-tests/modelgenerator/CaptureSummaryModels.ql b/rust/ql/test/utils-tests/modelgenerator/CaptureSummaryModels.ql
index 002689a20390..2ea8bd1ce6de 100644
--- a/rust/ql/test/utils-tests/modelgenerator/CaptureSummaryModels.ql
+++ b/rust/ql/test/utils-tests/modelgenerator/CaptureSummaryModels.ql
@@ -3,7 +3,7 @@ import utils.modelgenerator.internal.CaptureModels
 import utils.test.InlineMadTest
 
 module InlineMadTestConfig implements InlineMadTestConfigSig {
-  string getCapturedModel(Function f) { result = ContentSensitive::captureFlow(f, _) }
+  string getCapturedModel(Function f) { result = ContentSensitive::captureFlow(f, _, _) }
 
   string getKind() { result = "summary" }
 }
diff --git a/shared/mad/codeql/mad/modelgenerator/internal/ModelGeneratorImpl.qll b/shared/mad/codeql/mad/modelgenerator/internal/ModelGeneratorImpl.qll
index b9592964f931..719c69283eb1 100644
--- a/shared/mad/codeql/mad/modelgenerator/internal/ModelGeneratorImpl.qll
+++ b/shared/mad/codeql/mad/modelgenerator/internal/ModelGeneratorImpl.qll
@@ -11,6 +11,7 @@ private import codeql.dataflow.internal.ContentDataFlowImpl
 private import codeql.dataflow.internal.DataFlowImplCommon as DataFlowImplCommon
 private import codeql.util.Location
 private import ModelPrinting
+private import codeql.util.Unit
 
 /**
  * Provides language-specific model generator parameters.
@@ -464,14 +465,22 @@ module MakeModelGenerator<
       override string toString() { result = "TaintStore(" + step + ")" }
     }
 
-    /**
-     * A data flow configuration for tracking flow through APIs.
-     * The sources are the parameters of an API and the sinks are the return values (excluding `this`) and parameters.
-     *
-     * This can be used to generate Flow summaries for APIs from parameter to return.
-     */
-    private module PropagateFlowConfig implements DataFlow::StateConfigSig {
-      class FlowState = TaintState;
+    private signature module PropagateFlowConfigInputSig {
+      class FlowState;
+
+      FlowState initialState();
+
+      default predicate isAdditionalFlowStep(
+        DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
+      ) {
+        none()
+      }
+    }
+
+    private module PropagateFlowConfig<PropagateFlowConfigInputSig PropagateFlowConfigInput>
+      implements DataFlow::StateConfigSig
+    {
+      import PropagateFlowConfigInput
 
       predicate isSource(DataFlow::Node source, FlowState state) {
         source instanceof DataFlow::ParameterNode and
@@ -480,7 +489,7 @@ module MakeModelGenerator<
           c instanceof DataFlowSummaryTargetApi and
           not isUninterestingForHeuristicDataFlowModels(c)
         ) and
-        state.(TaintRead).getStep() = 0
+        state = initialState()
       }
 
       predicate isSink(DataFlow::Node sink, FlowState state) {
@@ -494,6 +503,31 @@ module MakeModelGenerator<
         not exists(captureQualifierFlow(getAsExprEnclosingCallable(sink)))
       }
 
+      predicate isAdditionalFlowStep = PropagateFlowConfigInput::isAdditionalFlowStep/4;
+
+      predicate isBarrier(DataFlow::Node n) {
+        exists(Type t | t = n.(NodeExtended).getType() and not isRelevantType(t))
+      }
+
+      DataFlow::FlowFeature getAFeature() {
+        result instanceof DataFlow::FeatureEqualSourceSinkCallContext
+      }
+    }
+
+    /**
+     * A module used to construct a data flow configuration for tracking taint-
+     * flow through APIs.
+     * The sources are the parameters of an API and the sinks are the return
+     * values (excluding `this`) and parameters.
+     *
+     * This can be used to generate flow summaries for APIs from parameter to
+     * return.
+     */
+    module PropagateFlowConfigInputTaintInput implements PropagateFlowConfigInputSig {
+      class FlowState = TaintState;
+
+      FlowState initialState() { result.(TaintRead).getStep() = 0 }
+
       predicate isAdditionalFlowStep(
         DataFlow::Node node1, FlowState state1, DataFlow::Node node2, FlowState state2
       ) {
@@ -515,51 +549,107 @@ module MakeModelGenerator<
           state1.(TaintRead).getStep() + 1 = state2.(TaintRead).getStep()
         )
       }
+    }
 
-      predicate isBarrier(DataFlow::Node n) {
-        exists(Type t | t = n.(NodeExtended).getType() and not isRelevantType(t))
-      }
+    /**
+     * A data flow configuration for tracking taint-flow through APIs.
+     * The sources are the parameters of an API and the sinks are the return
+     * values (excluding `this`) and parameters.
+     *
+     * This can be used to generate flow summaries for APIs from parameter to
+     * return.
+     */
+    private module PropagateTaintFlowConfig =
+      PropagateFlowConfig<PropagateFlowConfigInputTaintInput>;
 
-      DataFlow::FlowFeature getAFeature() {
-        result instanceof DataFlow::FeatureEqualSourceSinkCallContext
-      }
+    module PropagateTaintFlow = TaintTracking::GlobalWithState<PropagateTaintFlowConfig>;
+
+    /**
+     * A module used to construct a data flow configuration for tracking
+     * data flow through APIs.
+     * The sources are the parameters of an API and the sinks are the return
+     * values (excluding `this`) and parameters.
+     *
+     * This can be used to generate value-preserving flow summaries for APIs
+     * from parameter to return.
+     */
+    module PropagateFlowConfigInputDataFlowInput implements PropagateFlowConfigInputSig {
+      class FlowState = Unit;
+
+      FlowState initialState() { any() }
     }
 
-    module PropagateFlow = TaintTracking::GlobalWithState<PropagateFlowConfig>;
+    /**
+     * A data flow configuration for tracking data flow through APIs.
+     * The sources are the parameters of an API and the sinks are the return
+     * values (excluding `this`) and parameters.
+     *
+     * This can be used to generate flow summaries for APIs from parameter to
+     * return.
+     */
+    private module PropagateDataFlowConfig =
+      PropagateFlowConfig<PropagateFlowConfigInputDataFlowInput>;
+
+    module PropagateDataFlow = DataFlow::GlobalWithState<PropagateDataFlowConfig>;
 
     /**
-     * Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
+     * Holds if there should be a summary of `api` specifying flow from `p`
+     * to `returnNodeExt`.
      */
-    string captureThroughFlow0(
+    predicate captureThroughFlow0(
       DataFlowSummaryTargetApi api, DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt
     ) {
-      exists(string input, string output |
-        getEnclosingCallable(p) = api and
-        getEnclosingCallable(returnNodeExt) = api and
-        input = parameterNodeAsInput(p) and
-        output = getOutput(returnNodeExt) and
-        input != output and
-        result = ModelPrinting::asLiftedTaintModel(api, input, output)
-      )
+      captureThroughFlow0(api, p, _, returnNodeExt, _, _)
+    }
+
+    /**
+     * Holds if there should be a summary of `api` specifying flow
+     * from `p` (with summary component `input`) to `returnNodeExt` (with
+     * summary component `output`).
+     *
+     * `preservesValue` is true if the summary is value-preserving, or `false`
+     * otherwise.
+     */
+    private predicate captureThroughFlow0(
+      DataFlowSummaryTargetApi api, DataFlow::ParameterNode p, string input,
+      ReturnNodeExt returnNodeExt, string output, boolean preservesValue
+    ) {
+      (
+        PropagateDataFlow::flow(p, returnNodeExt) and preservesValue = true
+        or
+        not PropagateDataFlow::flow(p, returnNodeExt) and
+        PropagateTaintFlow::flow(p, returnNodeExt) and
+        preservesValue = false
+      ) and
+      getEnclosingCallable(p) = api and
+      getEnclosingCallable(returnNodeExt) = api and
+      input = parameterNodeAsInput(p) and
+      output = getOutput(returnNodeExt) and
+      input != output
     }
 
     /**
      * Gets the summary model(s) of `api`, if there is flow from parameters to return value or parameter.
+     *
+     * `preservesValue` is `true` if the summary is value-preserving, and `false` otherwise.
      */
-    private string captureThroughFlow(DataFlowSummaryTargetApi api) {
-      exists(DataFlow::ParameterNode p, ReturnNodeExt returnNodeExt |
-        PropagateFlow::flow(p, returnNodeExt) and
-        result = captureThroughFlow0(api, p, returnNodeExt)
+    private string captureThroughFlow(DataFlowSummaryTargetApi api, boolean preservesValue) {
+      exists(string input, string output |
+        preservesValue = max(boolean b | captureThroughFlow0(api, _, input, _, output, b)) and
+        result = ModelPrinting::asLiftedTaintModel(api, input, output, preservesValue)
       )
     }
 
     /**
      * Gets the summary model(s) of `api`, if there is flow from parameters to the
      * return value or parameter or if `api` is a fluent API.
+     *
+     * `preservesValue` is `true` if the summary is value-preserving, and `false` otherwise.
      */
-    string captureFlow(DataFlowSummaryTargetApi api) {
-      result = captureQualifierFlow(api) or
-      result = captureThroughFlow(api)
+    string captureHeuristicFlow(DataFlowSummaryTargetApi api, boolean preservesValue) {
+      result = captureQualifierFlow(api) and preservesValue = true
+      or
+      result = captureThroughFlow(api, preservesValue)
     }
 
     /**
@@ -569,7 +659,7 @@ module MakeModelGenerator<
      */
     string captureNoFlow(DataFlowSummaryTargetApi api) {
       not exists(DataFlowSummaryTargetApi api0 |
-        exists(captureFlow(api0)) and api0.lift() = api.lift()
+        exists(captureFlow(api0, _)) and api0.lift() = api.lift()
       ) and
       api.isRelevant() and
       result = ModelPrinting::asNeutralSummaryModel(api)
@@ -1024,12 +1114,13 @@ module MakeModelGenerator<
     /**
      * Gets the content based summary model(s) of the API `api` (if there is flow from a parameter to
      * the return value or a parameter). `lift` is true, if the model should be lifted, otherwise false.
+     * `preservesValue` is `true` if the summary is value-preserving, and `false` otherwise.
      *
      * Models are lifted to the best type in case the read and store access paths do not
      * contain a field or synthetic field access.
      */
-    string captureFlow(ContentDataFlowSummaryTargetApi api, boolean lift) {
-      exists(string input, string output, boolean preservesValue |
+    string captureFlow(ContentDataFlowSummaryTargetApi api, boolean lift, boolean preservesValue) {
+      exists(string input, string output |
         captureFlow0(api, input, output, _, lift) and
         preservesValue = max(boolean p | captureFlow0(api, input, output, p, lift)) and
         result = ContentModelPrinting::asModel(api, input, output, preservesValue, lift)
@@ -1046,17 +1137,19 @@ module MakeModelGenerator<
    * generate flow summaries using the heuristic based summary generator.
    */
   string captureFlow(DataFlowSummaryTargetApi api, boolean lift) {
-    result = ContentSensitive::captureFlow(api, lift)
-    or
-    not exists(DataFlowSummaryTargetApi api0 |
-      (api0 = api or api.lift() = api0) and
-      exists(ContentSensitive::captureFlow(api0, false))
+    exists(boolean preservesValue |
+      result = ContentSensitive::captureFlow(api, lift, preservesValue)
       or
-      api0.lift() = api.lift() and
-      exists(ContentSensitive::captureFlow(api0, true))
-    ) and
-    result = Heuristic::captureFlow(api) and
-    lift = true
+      not exists(DataFlowSummaryTargetApi api0 |
+        (api0 = api or api.lift() = api0) and
+        exists(ContentSensitive::captureFlow(api0, false, preservesValue))
+        or
+        api0.lift() = api.lift() and
+        exists(ContentSensitive::captureFlow(api0, true, preservesValue))
+      ) and
+      result = Heuristic::captureHeuristicFlow(api, preservesValue) and
+      lift = true
+    )
   }
 
   /**
diff --git a/shared/mad/codeql/mad/modelgenerator/internal/ModelPrinting.qll b/shared/mad/codeql/mad/modelgenerator/internal/ModelPrinting.qll
index 0ab92f7032b4..0bce2ed50d17 100644
--- a/shared/mad/codeql/mad/modelgenerator/internal/ModelPrinting.qll
+++ b/shared/mad/codeql/mad/modelgenerator/internal/ModelPrinting.qll
@@ -86,9 +86,11 @@ module ModelPrintingImpl<ModelPrintingLangSig Lang> {
     /**
      * Gets the lifted taint summary model for `api` with `input` and `output`.
      */
-    bindingset[input, output]
-    string asLiftedTaintModel(Printing::SummaryApi api, string input, string output) {
-      result = asModel(api, input, output, false, true)
+    bindingset[input, output, preservesValue]
+    string asLiftedTaintModel(
+      Printing::SummaryApi api, string input, string output, boolean preservesValue
+    ) {
+      result = asModel(api, input, output, preservesValue, true)
     }
 
     /**