diff --git a/cmd/kyverno/main.go b/cmd/kyverno/main.go
index 62cefceeb9..5066591e1e 100644
--- a/cmd/kyverno/main.go
+++ b/cmd/kyverno/main.go
@@ -15,6 +15,7 @@ import (
 	"github.com/nirmata/kyverno/pkg/generate"
 	generatecleanup "github.com/nirmata/kyverno/pkg/generate/cleanup"
 	"github.com/nirmata/kyverno/pkg/policy"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 	"github.com/nirmata/kyverno/pkg/policystore"
 	"github.com/nirmata/kyverno/pkg/policyviolation"
 	"github.com/nirmata/kyverno/pkg/signal"
@@ -135,12 +136,18 @@ func main() {
 		client,
 		pInformer.Kyverno().V1().ClusterPolicies())
 
+	// Policy Status Handler - deals with all logic related to policy status
+	statusSync := policystatus.NewSync(
+		pclient,
+		policyMetaStore)
+
 	// POLICY VIOLATION GENERATOR
 	// -- generate policy violation
 	pvgen := policyviolation.NewPVGenerator(pclient,
 		client,
 		pInformer.Kyverno().V1().ClusterPolicyViolations(),
-		pInformer.Kyverno().V1().PolicyViolations())
+		pInformer.Kyverno().V1().PolicyViolations(),
+		statusSync.Listener)
 
 	// POLICY CONTROLLER
 	// - reconciliation policy and policy violation
@@ -174,6 +181,7 @@ func main() {
 		egen,
 		pvgen,
 		kubedynamicInformer,
+		statusSync.Listener,
 	)
 	// GENERATE REQUEST CLEANUP
 	// -- cleans up the generate requests that have not been processed(i.e. state = [Pending, Failed]) for more than defined timeout
@@ -215,7 +223,7 @@ func main() {
 		kubeInformer.Rbac().V1().ClusterRoleBindings(),
 		egen,
 		webhookRegistrationClient,
-		pc.GetPolicyStatusAggregator(),
+		statusSync.Listener,
 		configData,
 		policyMetaStore,
 		pvgen,
@@ -238,6 +246,7 @@ func main() {
 	go grc.Run(1, stopCh)
 	go grcc.Run(1, stopCh)
 	go pvgen.Run(1, stopCh)
+	go statusSync.Run(1, stopCh)
 
 	// verifys if the admission control is enabled and active
 	// resync: 60 seconds
diff --git a/pkg/api/kyverno/v1/types.go b/pkg/api/kyverno/v1/types.go
index 34a3273b18..29c44b4385 100644
--- a/pkg/api/kyverno/v1/types.go
+++ b/pkg/api/kyverno/v1/types.go
@@ -227,21 +227,24 @@ type CloneFrom struct {
 	Name      string `json:"name,omitempty"`
 }
 
-//PolicyStatus provides status for violations
+// PolicyStatus mostly contains statistics related to policy
 type PolicyStatus struct {
-	ViolationCount int `json:"violationCount"`
+	// average time required to process the policy rules on a resource
+	AvgExecutionTime string `json:"averageExecutionTime"`
+	// number of violations created by this policy
+	ViolationCount int `json:"violationCount,omitempty"`
+	// Count of rules that failed
+	RulesFailedCount int `json:"rulesFailedCount,omitempty"`
 	// Count of rules that were applied
-	RulesAppliedCount int `json:"rulesAppliedCount"`
-	// Count of resources for whom update/create api requests were blocked as the resoruce did not satisfy the policy rules
-	ResourcesBlockedCount int `json:"resourcesBlockedCount"`
-	// average time required to process the policy Mutation rules on a resource
-	AvgExecutionTimeMutation string `json:"averageMutationRulesExecutionTime"`
-	// average time required to process the policy Validation rules on a resource
-	AvgExecutionTimeValidation string `json:"averageValidationRulesExecutionTime"`
-	// average time required to process the policy Validation rules on a resource
-	AvgExecutionTimeGeneration string `json:"averageGenerationRulesExecutionTime"`
-	// statistics per rule
-	Rules []RuleStats `json:"ruleStatus"`
+	RulesAppliedCount int `json:"rulesAppliedCount,omitempty"`
+	// Count of resources that were blocked for failing a validate, across all rules
+	ResourcesBlockedCount int `json:"resourcesBlockedCount,omitempty"`
+	// Count of resources that were successfully mutated, across all rules
+	ResourcesMutatedCount int `json:"resourcesMutatedCount,omitempty"`
+	// Count of resources that were successfully generated, across all rules
+	ResourcesGeneratedCount int `json:"resourcesGeneratedCount,omitempty"`
+
+	Rules []RuleStats `json:"ruleStatus,omitempty"`
 }
 
 //RuleStats provides status per rule
@@ -249,13 +252,19 @@ type RuleStats struct {
 	// Rule name
 	Name string `json:"ruleName"`
 	// average time require to process the rule
-	ExecutionTime string `json:"averageExecutionTime"`
-	// Count of rules that were applied
-	AppliedCount int `json:"appliedCount"`
+	ExecutionTime string `json:"averageExecutionTime,omitempty"`
+	// number of violations created by this rule
+	ViolationCount int `json:"violationCount,omitempty"`
 	// Count of rules that failed
-	ViolationCount int `json:"violationCount"`
-	// Count of mutations
-	MutationCount int `json:"mutationsCount"`
+	FailedCount int `json:"failedCount,omitempty"`
+	// Count of rules that were applied
+	AppliedCount int `json:"appliedCount,omitempty"`
+	// Count of resources for whom update/create api requests were blocked as the resource did not satisfy the policy rules
+	ResourcesBlockedCount int `json:"resourcesBlockedCount,omitempty"`
+	// Count of resources that were successfully mutated
+	ResourcesMutatedCount int `json:"resourcesMutatedCount,omitempty"`
+	// Count of resources that were successfully generated
+	ResourcesGeneratedCount int `json:"resourcesGeneratedCount,omitempty"`
 }
 
 // PolicyList is a list of Policy resources
diff --git a/pkg/engine/generation.go b/pkg/engine/generation.go
index 25c7204851..0f8cea1937 100644
--- a/pkg/engine/generation.go
+++ b/pkg/engine/generation.go
@@ -1,6 +1,8 @@
 package engine
 
 import (
+	"time"
+
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
 	"github.com/nirmata/kyverno/pkg/engine/context"
@@ -25,6 +27,9 @@ func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admission
 	if !rule.HasGenerate() {
 		return nil
 	}
+
+	startTime := time.Now()
+
 	if err := MatchesResourceDescription(resource, rule, admissionInfo); err != nil {
 		glog.V(4).Infof(err.Error())
 		return nil
@@ -39,8 +44,12 @@ func filterRule(rule kyverno.Rule, resource unstructured.Unstructured, admission
 	}
 	// build rule Response
 	return &response.RuleResponse{
-		Name: rule.Name,
-		Type: "Generation",
+		Name:    rule.Name,
+		Type:    "Generation",
+		Success: true,
+		RuleStats: response.RuleStats{
+			ProcessingTime: time.Since(startTime),
+		},
 	}
 }
 
diff --git a/pkg/generate/controller.go b/pkg/generate/controller.go
index 944b8b49cb..d40ed2af0d 100644
--- a/pkg/generate/controller.go
+++ b/pkg/generate/controller.go
@@ -11,6 +11,7 @@ import (
 	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
 	dclient "github.com/nirmata/kyverno/pkg/dclient"
 	"github.com/nirmata/kyverno/pkg/event"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 	"github.com/nirmata/kyverno/pkg/policyviolation"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
@@ -57,6 +58,8 @@ type Controller struct {
 	//TODO: list of generic informers
 	// only support Namespaces for re-evalutation on resource updates
 	nsInformer informers.GenericInformer
+
+	policyStatusListener policystatus.Listener
 }
 
 //NewController returns an instance of the Generate-Request Controller
@@ -68,6 +71,7 @@ func NewController(
 	eventGen event.Interface,
 	pvGenerator policyviolation.GeneratorInterface,
 	dynamicInformer dynamicinformer.DynamicSharedInformerFactory,
+	policyStatus policystatus.Listener,
 ) *Controller {
 	c := Controller{
 		client:        client,
@@ -76,8 +80,9 @@ func NewController(
 		pvGenerator:   pvGenerator,
 		//TODO: do the math for worst case back off and make sure cleanup runs after that
 		// as we dont want a deleted GR to be re-queue
-		queue:           workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1, 30), "generate-request"),
-		dynamicInformer: dynamicInformer,
+		queue:                workqueue.NewNamedRateLimitingQueue(workqueue.NewItemExponentialFailureRateLimiter(1, 30), "generate-request"),
+		dynamicInformer:      dynamicInformer,
+		policyStatusListener: policyStatus,
 	}
 	c.statusControl = StatusControl{client: kyvernoclient}
 
diff --git a/pkg/generate/generate.go b/pkg/generate/generate.go
index cd9ab1b2fd..bff1dbbca6 100644
--- a/pkg/generate/generate.go
+++ b/pkg/generate/generate.go
@@ -3,6 +3,7 @@ package generate
 import (
 	"encoding/json"
 	"fmt"
+	"time"
 
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
@@ -80,7 +81,7 @@ func (c *Controller) applyGenerate(resource unstructured.Unstructured, gr kyvern
 	}
 
 	// Apply the generate rule on resource
-	return applyGeneratePolicy(c.client, policyContext)
+	return c.applyGeneratePolicy(policyContext, gr)
 }
 
 func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateRequest, err error, genResources []kyverno.ResourceSpec) error {
@@ -92,7 +93,7 @@ func updateStatus(statusControl StatusControlInterface, gr kyverno.GenerateReque
 	return statusControl.Success(gr, genResources)
 }
 
-func applyGeneratePolicy(client *dclient.Client, policyContext engine.PolicyContext) ([]kyverno.ResourceSpec, error) {
+func (c *Controller) applyGeneratePolicy(policyContext engine.PolicyContext, gr kyverno.GenerateRequest) ([]kyverno.ResourceSpec, error) {
 	// List of generatedResources
 	var genResources []kyverno.ResourceSpec
 	// Get the response as the actions to be performed on the resource
@@ -107,20 +108,70 @@ func applyGeneratePolicy(client *dclient.Client, policyContext engine.PolicyCont
 		return rcreationTime.Before(&pcreationTime)
 	}()
 
+	ruleNameToProcessingTime := make(map[string]time.Duration)
 	for _, rule := range policy.Spec.Rules {
 		if !rule.HasGenerate() {
 			continue
 		}
-		genResource, err := applyRule(client, rule, resource, ctx, processExisting)
+
+		startTime := time.Now()
+		genResource, err := applyRule(c.client, rule, resource, ctx, processExisting)
 		if err != nil {
 			return nil, err
 		}
+
+		ruleNameToProcessingTime[rule.Name] = time.Since(startTime)
 		genResources = append(genResources, genResource)
 	}
 
+	if gr.Status.State == "" {
+		c.policyStatusListener.Send(generateSyncStats{
+			policyName:               policy.Name,
+			ruleNameToProcessingTime: ruleNameToProcessingTime,
+		})
+	}
+
 	return genResources, nil
 }
 
+type generateSyncStats struct {
+	policyName               string
+	ruleNameToProcessingTime map[string]time.Duration
+}
+
+func (vc generateSyncStats) PolicyName() string {
+	return vc.policyName
+}
+
+func (vc generateSyncStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {
+
+	for i := range status.Rules {
+		if executionTime, exist := vc.ruleNameToProcessingTime[status.Rules[i].Name]; exist {
+			status.ResourcesGeneratedCount += 1
+			status.Rules[i].ResourcesGeneratedCount += 1
+			averageOver := int64(status.Rules[i].AppliedCount + status.Rules[i].FailedCount)
+			status.Rules[i].ExecutionTime = updateGenerateExecutionTime(
+				executionTime,
+				status.Rules[i].ExecutionTime,
+				averageOver,
+			).String()
+		}
+	}
+
+	return status
+}
+
+func updateGenerateExecutionTime(newTime time.Duration, oldAverageTimeString string, averageOver int64) time.Duration {
+	if averageOver == 0 {
+		return newTime
+	}
+	oldAverageExecutionTime, _ := time.ParseDuration(oldAverageTimeString)
+	numerator := (oldAverageExecutionTime.Nanoseconds() * averageOver) + newTime.Nanoseconds()
+	denominator := averageOver
+	newAverageTimeInNanoSeconds := numerator / denominator
+	return time.Duration(newAverageTimeInNanoSeconds) * time.Nanosecond
+}
+
 func applyRule(client *dclient.Client, rule kyverno.Rule, resource unstructured.Unstructured, ctx context.EvalInterface, processExisting bool) (kyverno.ResourceSpec, error) {
 	var rdata map[string]interface{}
 	var err error
diff --git a/pkg/generate/policyStatus_test.go b/pkg/generate/policyStatus_test.go
new file mode 100644
index 0000000000..b0fa04b591
--- /dev/null
+++ b/pkg/generate/policyStatus_test.go
@@ -0,0 +1,53 @@
+package generate
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+	"time"
+
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+)
+
+func Test_Stats(t *testing.T) {
+	testCase := struct {
+		generatedSyncStats []generateSyncStats
+		expectedOutput     []byte
+		existingStatus     map[string]v1.PolicyStatus
+	}{
+		expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"","resourcesGeneratedCount":2,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"23ns","resourcesGeneratedCount":1},{"ruleName":"rule2","averageExecutionTime":"44ns","resourcesGeneratedCount":1},{"ruleName":"rule3"}]}}`),
+		generatedSyncStats: []generateSyncStats{
+			{
+				policyName: "policy1",
+				ruleNameToProcessingTime: map[string]time.Duration{
+					"rule1": time.Nanosecond * 23,
+					"rule2": time.Nanosecond * 44,
+				},
+			},
+		},
+		existingStatus: map[string]v1.PolicyStatus{
+			"policy1": {
+				Rules: []v1.RuleStats{
+					{
+						Name: "rule1",
+					},
+					{
+						Name: "rule2",
+					},
+					{
+						Name: "rule3",
+					},
+				},
+			},
+		},
+	}
+
+	for _, generateSyncStat := range testCase.generatedSyncStats {
+		testCase.existingStatus[generateSyncStat.PolicyName()] = generateSyncStat.UpdateStatus(testCase.existingStatus[generateSyncStat.PolicyName()])
+	}
+
+	output, _ := json.Marshal(testCase.existingStatus)
+	if !reflect.DeepEqual(output, testCase.expectedOutput) {
+		t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
+	}
+}
diff --git a/pkg/policy/apply.go b/pkg/policy/apply.go
index ed4b72ac65..32d654346c 100644
--- a/pkg/policy/apply.go
+++ b/pkg/policy/apply.go
@@ -19,45 +19,14 @@ import (
 
 // applyPolicy applies policy on a resource
 //TODO: generation rules
-func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface) (responses []response.EngineResponse) {
+func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructured) (responses []response.EngineResponse) {
 	startTime := time.Now()
-	var policyStats []PolicyStat
+
 	glog.V(4).Infof("Started apply policy %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), startTime)
 	defer func() {
 		glog.V(4).Infof("Finished applying %s on resource %s/%s/%s (%v)", policy.Name, resource.GetKind(), resource.GetNamespace(), resource.GetName(), time.Since(startTime))
 	}()
 
-	// gather stats from the engine response
-	gatherStat := func(policyName string, policyResponse response.PolicyResponse) {
-		ps := PolicyStat{}
-		ps.PolicyName = policyName
-		ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
-		ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
-		// capture rule level stats
-		for _, rule := range policyResponse.Rules {
-			rs := RuleStatinfo{}
-			rs.RuleName = rule.Name
-			rs.ExecutionTime = rule.RuleStats.ProcessingTime
-			if rule.Success {
-				rs.RuleAppliedCount++
-			} else {
-				rs.RulesFailedCount++
-			}
-			if rule.Patches != nil {
-				rs.MutationCount++
-			}
-			ps.Stats.Rules = append(ps.Stats.Rules, rs)
-		}
-		policyStats = append(policyStats, ps)
-	}
-	// send stats for aggregation
-	sendStat := func(blocked bool) {
-		for _, stat := range policyStats {
-			stat.Stats.ResourceBlocked = utils.Btoi(blocked)
-			//SEND
-			policyStatus.SendStat(stat)
-		}
-	}
 	var engineResponses []response.EngineResponse
 	var engineResponse response.EngineResponse
 	var err error
@@ -66,27 +35,20 @@ func applyPolicy(policy kyverno.ClusterPolicy, resource unstructured.Unstructure
 	ctx.AddResource(transformResource(resource))
 
 	//MUTATION
-	engineResponse, err = mutation(policy, resource, policyStatus, ctx)
+	engineResponse, err = mutation(policy, resource, ctx)
 	engineResponses = append(engineResponses, engineResponse)
 	if err != nil {
 		glog.Errorf("unable to process mutation rules: %v", err)
 	}
-	gatherStat(policy.Name, engineResponse.PolicyResponse)
-	//send stats
-	sendStat(false)
 
 	//VALIDATION
 	engineResponse = engine.Validate(engine.PolicyContext{Policy: policy, Context: ctx, NewResource: resource})
 	engineResponses = append(engineResponses, engineResponse)
-	// gather stats
-	gatherStat(policy.Name, engineResponse.PolicyResponse)
-	//send stats
-	sendStat(false)
 
 	//TODO: GENERATION
 	return engineResponses
 }
-func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, policyStatus PolicyStatusInterface, ctx context.EvalInterface) (response.EngineResponse, error) {
+func mutation(policy kyverno.ClusterPolicy, resource unstructured.Unstructured, ctx context.EvalInterface) (response.EngineResponse, error) {
 
 	engineResponse := engine.Mutate(engine.PolicyContext{Policy: policy, NewResource: resource, Context: ctx})
 	if !engineResponse.IsSuccesful() {
diff --git a/pkg/policy/controller.go b/pkg/policy/controller.go
index 04200cd5a5..d303bebf52 100644
--- a/pkg/policy/controller.go
+++ b/pkg/policy/controller.go
@@ -2,7 +2,6 @@ package policy
 
 import (
 	"fmt"
-	"reflect"
 	"time"
 
 	"github.com/golang/glog"
@@ -67,8 +66,6 @@ type PolicyController struct {
 	rm resourceManager
 	// helpers to validate against current loaded configuration
 	configHandler config.Interface
-	// receives stats and aggregates details
-	statusAggregator *PolicyStatusAggregator
 	// store to hold policy meta data for faster lookup
 	pMetaStore policystore.UpdateInterface
 	// policy violation generator
@@ -144,10 +141,6 @@ func NewPolicyController(kyvernoClient *kyvernoclient.Clientset,
 	//TODO: pass the time in seconds instead of converting it internally
 	pc.rm = NewResourceManager(30)
 
-	// aggregator
-	// pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient, pInformer)
-	pc.statusAggregator = NewPolicyStatAggregator(kyvernoClient)
-
 	return &pc, nil
 }
 
@@ -264,9 +257,6 @@ func (pc *PolicyController) Run(workers int, stopCh <-chan struct{}) {
 	for i := 0; i < workers; i++ {
 		go wait.Until(pc.worker, time.Second, stopCh)
 	}
-	// policy status aggregator
-	//TODO: workers required for aggergation
-	pc.statusAggregator.Run(1, stopCh)
 	<-stopCh
 }
 
@@ -328,8 +318,6 @@ func (pc *PolicyController) syncPolicy(key string) error {
 		if err := pc.deleteNamespacedPolicyViolations(key); err != nil {
 			return err
 		}
-		// remove the recorded stats for the policy
-		pc.statusAggregator.RemovePolicyStats(key)
 
 		// remove webhook configurations if there are no policies
 		if err := pc.removeResourceWebhookConfiguration(); err != nil {
@@ -345,23 +333,12 @@ func (pc *PolicyController) syncPolicy(key string) error {
 
 	pc.resourceWebhookWatcher.RegisterResourceWebhook()
 
-	// cluster policy violations
-	cpvList, err := pc.getClusterPolicyViolationForPolicy(policy.Name)
-	if err != nil {
-		return err
-	}
-	// namespaced policy violation
-	nspvList, err := pc.getNamespacedPolicyViolationForPolicy(policy.Name)
-	if err != nil {
-		return err
-	}
-
 	// process policies on existing resources
 	engineResponses := pc.processExistingResources(*policy)
 	// report errors
 	pc.cleanupAndReport(engineResponses)
-	// sync active
-	return pc.syncStatusOnly(policy, cpvList, nspvList)
+
+	return nil
 }
 
 func (pc *PolicyController) deleteClusterPolicyViolations(policy string) error {
@@ -390,39 +367,6 @@ func (pc *PolicyController) deleteNamespacedPolicyViolations(policy string) erro
 	return nil
 }
 
-//syncStatusOnly updates the policy status subresource
-func (pc *PolicyController) syncStatusOnly(p *kyverno.ClusterPolicy, pvList []*kyverno.ClusterPolicyViolation, nspvList []*kyverno.PolicyViolation) error {
-	newStatus := pc.calculateStatus(p.Name, pvList, nspvList)
-	if reflect.DeepEqual(newStatus, p.Status) {
-		// no update to status
-		return nil
-	}
-	// update status
-	newPolicy := p
-	newPolicy.Status = newStatus
-	_, err := pc.kyvernoClient.KyvernoV1().ClusterPolicies().UpdateStatus(newPolicy)
-	return err
-}
-
-func (pc *PolicyController) calculateStatus(policyName string, pvList []*kyverno.ClusterPolicyViolation, nspvList []*kyverno.PolicyViolation) kyverno.PolicyStatus {
-	violationCount := len(pvList) + len(nspvList)
-	status := kyverno.PolicyStatus{
-		ViolationCount: violationCount,
-	}
-	// get stats
-	stats := pc.statusAggregator.GetPolicyStats(policyName)
-	if !reflect.DeepEqual(stats, (PolicyStatInfo{})) {
-		status.RulesAppliedCount = stats.RulesAppliedCount
-		status.ResourcesBlockedCount = stats.ResourceBlocked
-		status.AvgExecutionTimeMutation = stats.MutationExecutionTime.String()
-		status.AvgExecutionTimeValidation = stats.ValidationExecutionTime.String()
-		status.AvgExecutionTimeGeneration = stats.GenerationExecutionTime.String()
-		// update rule stats
-		status.Rules = convertRules(stats.Rules)
-	}
-	return status
-}
-
 func (pc *PolicyController) getNamespacedPolicyViolationForPolicy(policy string) ([]*kyverno.PolicyViolation, error) {
 	policySelector, err := buildPolicyLabel(policy)
 	if err != nil {
@@ -458,19 +402,3 @@ func (r RealPVControl) DeleteClusterPolicyViolation(name string) error {
 func (r RealPVControl) DeleteNamespacedPolicyViolation(ns, name string) error {
 	return r.Client.KyvernoV1().PolicyViolations(ns).Delete(name, &metav1.DeleteOptions{})
 }
-
-// convertRules converts the internal rule stats to one used in policy.stats struct
-func convertRules(rules []RuleStatinfo) []kyverno.RuleStats {
-	var stats []kyverno.RuleStats
-	for _, r := range rules {
-		stat := kyverno.RuleStats{
-			Name:           r.RuleName,
-			ExecutionTime:  r.ExecutionTime.String(),
-			AppliedCount:   r.RuleAppliedCount,
-			ViolationCount: r.RulesFailedCount,
-			MutationCount:  r.MutationCount,
-		}
-		stats = append(stats, stat)
-	}
-	return stats
-}
diff --git a/pkg/policy/existing.go b/pkg/policy/existing.go
index 9165978ca4..97c09affac 100644
--- a/pkg/policy/existing.go
+++ b/pkg/policy/existing.go
@@ -39,7 +39,7 @@ func (pc *PolicyController) processExistingResources(policy kyverno.ClusterPolic
 
 		// apply the policy on each
 		glog.V(4).Infof("apply policy %s with resource version %s on resource %s/%s/%s with resource version %s", policy.Name, policy.ResourceVersion, resource.GetKind(), resource.GetNamespace(), resource.GetName(), resource.GetResourceVersion())
-		engineResponse := applyPolicy(policy, resource, pc.statusAggregator)
+		engineResponse := applyPolicy(policy, resource)
 		// get engine response for mutation & validation independently
 		engineResponses = append(engineResponses, engineResponse...)
 		// post-processing, register the resource as processed
diff --git a/pkg/policy/report.go b/pkg/policy/report.go
index 206a6ebd61..3614da9b32 100644
--- a/pkg/policy/report.go
+++ b/pkg/policy/report.go
@@ -18,6 +18,10 @@ func (pc *PolicyController) cleanupAndReport(engineResponses []response.EngineRe
 	pc.eventGen.Add(eventInfos...)
 	// create policy violation
 	pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
+	for i := range pvInfos {
+		pvInfos[i].FromSync = true
+	}
+
 	pc.pvGenerator.Add(pvInfos...)
 	// cleanup existing violations if any
 	// if there is any error in clean up, we dont re-queue the resource
diff --git a/pkg/policy/status.go b/pkg/policy/status.go
deleted file mode 100644
index 8d4beb4f02..0000000000
--- a/pkg/policy/status.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package policy
-
-import (
-	"sync"
-	"time"
-
-	"github.com/golang/glog"
-	kyvernoclient "github.com/nirmata/kyverno/pkg/client/clientset/versioned"
-	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
-	"k8s.io/apimachinery/pkg/util/wait"
-)
-
-//PolicyStatusAggregator stores information abt aggregation
-type PolicyStatusAggregator struct {
-	// time since we start aggregating the stats
-	startTime time.Time
-	// channel to receive stats
-	ch chan PolicyStat
-	//TODO: lock based on key, possibly sync.Map ?
-	//sync RW for policyData
-	mux sync.RWMutex
-	// stores aggregated stats for policy
-	policyData map[string]PolicyStatInfo
-}
-
-//NewPolicyStatAggregator returns a new policy status
-func NewPolicyStatAggregator(client *kyvernoclient.Clientset) *PolicyStatusAggregator {
-	psa := PolicyStatusAggregator{
-		startTime:  time.Now(),
-		ch:         make(chan PolicyStat),
-		policyData: map[string]PolicyStatInfo{},
-	}
-	return &psa
-}
-
-//Run begins aggregator
-func (psa *PolicyStatusAggregator) Run(workers int, stopCh <-chan struct{}) {
-	defer utilruntime.HandleCrash()
-	glog.V(4).Info("Started aggregator for policy status stats")
-	defer func() {
-		glog.V(4).Info("Shutting down aggregator for policy status stats")
-	}()
-	for i := 0; i < workers; i++ {
-		go wait.Until(psa.process, time.Second, stopCh)
-	}
-	<-stopCh
-}
-
-func (psa *PolicyStatusAggregator) process() {
-	// As mutation and validation are handled separately
-	// ideally we need to combine the execution time from both for a policy
-	// but its tricky to detect here the type of rules policy contains
-	// so we dont combine the results, but instead compute the execution time for
-	// mutation & validation rules separately
-	for r := range psa.ch {
-		glog.V(4).Infof("received policy stats %v", r)
-		psa.aggregate(r)
-	}
-}
-
-func (psa *PolicyStatusAggregator) aggregate(ps PolicyStat) {
-	func() {
-		glog.V(4).Infof("write lock update policy %s", ps.PolicyName)
-		psa.mux.Lock()
-	}()
-	defer func() {
-		glog.V(4).Infof("write Unlock update policy %s", ps.PolicyName)
-		psa.mux.Unlock()
-	}()
-
-	if len(ps.Stats.Rules) == 0 {
-		glog.V(4).Infof("ignoring stats, as no rule was applied")
-		return
-	}
-
-	info, ok := psa.policyData[ps.PolicyName]
-	if !ok {
-		psa.policyData[ps.PolicyName] = ps.Stats
-		glog.V(4).Infof("added stats for policy %s", ps.PolicyName)
-		return
-	}
-	// aggregate policy information
-	info.RulesAppliedCount = info.RulesAppliedCount + ps.Stats.RulesAppliedCount
-	if ps.Stats.ResourceBlocked == 1 {
-		info.ResourceBlocked++
-	}
-	var zeroDuration time.Duration
-	if info.MutationExecutionTime != zeroDuration {
-		info.MutationExecutionTime = (info.MutationExecutionTime + ps.Stats.MutationExecutionTime) / 2
-		glog.V(4).Infof("updated avg mutation time %v", info.MutationExecutionTime)
-	} else {
-		info.MutationExecutionTime = ps.Stats.MutationExecutionTime
-	}
-	if info.ValidationExecutionTime != zeroDuration {
-		info.ValidationExecutionTime = (info.ValidationExecutionTime + ps.Stats.ValidationExecutionTime) / 2
-		glog.V(4).Infof("updated avg validation time %v", info.ValidationExecutionTime)
-	} else {
-		info.ValidationExecutionTime = ps.Stats.ValidationExecutionTime
-	}
-	if info.GenerationExecutionTime != zeroDuration {
-		info.GenerationExecutionTime = (info.GenerationExecutionTime + ps.Stats.GenerationExecutionTime) / 2
-		glog.V(4).Infof("updated avg generation time %v", info.GenerationExecutionTime)
-	} else {
-		info.GenerationExecutionTime = ps.Stats.GenerationExecutionTime
-	}
-	// aggregate rule details
-	info.Rules = aggregateRules(info.Rules, ps.Stats.Rules)
-	// update
-	psa.policyData[ps.PolicyName] = info
-	glog.V(4).Infof("updated stats for policy %s", ps.PolicyName)
-}
-
-func aggregateRules(old []RuleStatinfo, update []RuleStatinfo) []RuleStatinfo {
-	var zeroDuration time.Duration
-	searchRule := func(list []RuleStatinfo, key string) *RuleStatinfo {
-		for _, v := range list {
-			if v.RuleName == key {
-				return &v
-			}
-		}
-		return nil
-	}
-	newRules := []RuleStatinfo{}
-	// search for new rules in old rules and update it
-	for _, updateR := range update {
-		if updateR.ExecutionTime != zeroDuration {
-			if rule := searchRule(old, updateR.RuleName); rule != nil {
-				rule.ExecutionTime = (rule.ExecutionTime + updateR.ExecutionTime) / 2
-				rule.RuleAppliedCount = rule.RuleAppliedCount + updateR.RuleAppliedCount
-				rule.RulesFailedCount = rule.RulesFailedCount + updateR.RulesFailedCount
-				rule.MutationCount = rule.MutationCount + updateR.MutationCount
-				newRules = append(newRules, *rule)
-			} else {
-				newRules = append(newRules, updateR)
-			}
-		}
-	}
-	return newRules
-}
-
-//GetPolicyStats returns the policy stats
-func (psa *PolicyStatusAggregator) GetPolicyStats(policyName string) PolicyStatInfo {
-	func() {
-		glog.V(4).Infof("read lock update policy %s", policyName)
-		psa.mux.RLock()
-	}()
-	defer func() {
-		glog.V(4).Infof("read Unlock update policy %s", policyName)
-		psa.mux.RUnlock()
-	}()
-	glog.V(4).Infof("read stats for policy %s", policyName)
-	return psa.policyData[policyName]
-}
-
-//RemovePolicyStats rmves policy stats records
-func (psa *PolicyStatusAggregator) RemovePolicyStats(policyName string) {
-	func() {
-		glog.V(4).Infof("write lock update policy %s", policyName)
-		psa.mux.Lock()
-	}()
-	defer func() {
-		glog.V(4).Infof("write Unlock update policy %s", policyName)
-		psa.mux.Unlock()
-	}()
-	glog.V(4).Infof("removing stats for policy %s", policyName)
-	delete(psa.policyData, policyName)
-}
-
-//PolicyStatusInterface provides methods to modify policyStatus
-type PolicyStatusInterface interface {
-	SendStat(stat PolicyStat)
-	// UpdateViolationCount(policyName string, pvList []*kyverno.PolicyViolation) error
-}
-
-//PolicyStat stored stats for policy
-type PolicyStat struct {
-	PolicyName string
-	Stats      PolicyStatInfo
-}
-
-//PolicyStatInfo provides statistics for policy
-type PolicyStatInfo struct {
-	MutationExecutionTime   time.Duration
-	ValidationExecutionTime time.Duration
-	GenerationExecutionTime time.Duration
-	RulesAppliedCount       int
-	ResourceBlocked         int
-	Rules                   []RuleStatinfo
-}
-
-//RuleStatinfo provides statistics for rule
-type RuleStatinfo struct {
-	RuleName         string
-	ExecutionTime    time.Duration
-	RuleAppliedCount int
-	RulesFailedCount int
-	MutationCount    int
-}
-
-//SendStat sends the stat information for aggregation
-func (psa *PolicyStatusAggregator) SendStat(stat PolicyStat) {
-	glog.V(4).Infof("sending policy stats: %v", stat)
-	// Send over channel
-	psa.ch <- stat
-}
-
-//GetPolicyStatusAggregator returns interface to send policy status stats
-func (pc *PolicyController) GetPolicyStatusAggregator() PolicyStatusInterface {
-	return pc.statusAggregator
-}
diff --git a/pkg/policystatus/keyToMutex.go b/pkg/policystatus/keyToMutex.go
new file mode 100644
index 0000000000..801db2981a
--- /dev/null
+++ b/pkg/policystatus/keyToMutex.go
@@ -0,0 +1,31 @@
+package policystatus
+
+import "sync"
+
+// keyToMutex allows status to be updated
+//for different policies at the same time
+//while ensuring the status for same policies
+//are updated one at a time.
+type keyToMutex struct {
+	mu    sync.RWMutex
+	keyMu map[string]*sync.RWMutex
+}
+
+func newKeyToMutex() *keyToMutex {
+	return &keyToMutex{
+		mu:    sync.RWMutex{},
+		keyMu: make(map[string]*sync.RWMutex),
+	}
+}
+
+func (k *keyToMutex) Get(key string) *sync.RWMutex {
+	k.mu.Lock()
+	defer k.mu.Unlock()
+	mutex := k.keyMu[key]
+	if mutex == nil {
+		mutex = &sync.RWMutex{}
+		k.keyMu[key] = mutex
+	}
+
+	return mutex
+}
diff --git a/pkg/policystatus/main.go b/pkg/policystatus/main.go
new file mode 100644
index 0000000000..3e633aaf82
--- /dev/null
+++ b/pkg/policystatus/main.go
@@ -0,0 +1,146 @@
+package policystatus
+
+import (
+	"encoding/json"
+	"sync"
+	"time"
+
+	"github.com/golang/glog"
+
+	"k8s.io/apimachinery/pkg/util/wait"
+
+	"github.com/nirmata/kyverno/pkg/client/clientset/versioned"
+
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+)
+
+// Policy status implementation works in the following way,
+//Currently policy status maintains a cache of the status of
+//each policy.
+//Every x unit of time the status of policy is updated using
+//the data from the cache.
+//The sync exposes a listener which accepts a statusUpdater
+//interface which dictates how the status should be updated.
+//The status is updated by a worker that receives the interface
+//on a channel.
+//The worker then updates the current status using the methods
+//exposed by the interface.
+//Current implementation is designed to be threadsafe with optimised
+//locking for each policy.
+
+// statusUpdater defines a type to have a method which
+//updates the given status
+type statusUpdater interface {
+	PolicyName() string
+	UpdateStatus(status v1.PolicyStatus) v1.PolicyStatus
+}
+
+type policyStore interface {
+	Get(policyName string) (*v1.ClusterPolicy, error)
+}
+
+type Listener chan statusUpdater
+
+func (l Listener) Send(s statusUpdater) {
+	l <- s
+}
+
+// Sync is the object which is used to initialize
+//the policyStatus sync, can be considered the parent object
+//since it contains access to all the persistant data present
+//in this package.
+type Sync struct {
+	cache       *cache
+	Listener    Listener
+	client      *versioned.Clientset
+	policyStore policyStore
+}
+
+type cache struct {
+	dataMu     sync.RWMutex
+	data       map[string]v1.PolicyStatus
+	keyToMutex *keyToMutex
+}
+
+func NewSync(c *versioned.Clientset, p policyStore) *Sync {
+	return &Sync{
+		cache: &cache{
+			dataMu:     sync.RWMutex{},
+			data:       make(map[string]v1.PolicyStatus),
+			keyToMutex: newKeyToMutex(),
+		},
+		client:      c,
+		policyStore: p,
+		Listener:    make(chan statusUpdater, 20),
+	}
+}
+
+func (s *Sync) Run(workers int, stopCh <-chan struct{}) {
+	for i := 0; i < workers; i++ {
+		go s.updateStatusCache(stopCh)
+	}
+
+	wait.Until(s.updatePolicyStatus, 2*time.Second, stopCh)
+	<-stopCh
+}
+
+// updateStatusCache is a worker which updates the current status
+//using the statusUpdater interface
+func (s *Sync) updateStatusCache(stopCh <-chan struct{}) {
+	for {
+		select {
+		case statusUpdater := <-s.Listener:
+			s.cache.keyToMutex.Get(statusUpdater.PolicyName()).Lock()
+
+			s.cache.dataMu.RLock()
+			status, exist := s.cache.data[statusUpdater.PolicyName()]
+			s.cache.dataMu.RUnlock()
+			if !exist {
+				policy, _ := s.policyStore.Get(statusUpdater.PolicyName())
+				if policy != nil {
+					status = policy.Status
+				}
+			}
+
+			updatedStatus := statusUpdater.UpdateStatus(status)
+
+			s.cache.dataMu.Lock()
+			s.cache.data[statusUpdater.PolicyName()] = updatedStatus
+			s.cache.dataMu.Unlock()
+
+			s.cache.keyToMutex.Get(statusUpdater.PolicyName()).Unlock()
+			oldStatus, _ := json.Marshal(status)
+			newStatus, _ := json.Marshal(updatedStatus)
+
+			glog.V(4).Infof("\nupdated status of policy - %v\noldStatus:\n%v\nnewStatus:\n%v\n", statusUpdater.PolicyName(), string(oldStatus), string(newStatus))
+		case <-stopCh:
+			return
+		}
+	}
+}
+
+// updatePolicyStatus updates the status in the policy resource definition
+//from the status cache, syncing them
+func (s *Sync) updatePolicyStatus() {
+	s.cache.dataMu.Lock()
+	var nameToStatus = make(map[string]v1.PolicyStatus, len(s.cache.data))
+	for k, v := range s.cache.data {
+		nameToStatus[k] = v
+	}
+	s.cache.dataMu.Unlock()
+
+	for policyName, status := range nameToStatus {
+		policy, err := s.policyStore.Get(policyName)
+		if err != nil {
+			continue
+		}
+		policy.Status = status
+		_, err = s.client.KyvernoV1().ClusterPolicies().UpdateStatus(policy)
+		if err != nil {
+			s.cache.dataMu.Lock()
+			delete(s.cache.data, policyName)
+			s.cache.dataMu.Unlock()
+			glog.V(4).Info(err)
+		}
+	}
+}
diff --git a/pkg/policystatus/status_test.go b/pkg/policystatus/status_test.go
new file mode 100644
index 0000000000..310852cf2f
--- /dev/null
+++ b/pkg/policystatus/status_test.go
@@ -0,0 +1,50 @@
+package policystatus
+
+import (
+	"encoding/json"
+	"testing"
+	"time"
+
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+)
+
+type dummyStore struct {
+}
+
+func (d dummyStore) Get(policyName string) (*v1.ClusterPolicy, error) {
+	return &v1.ClusterPolicy{}, nil
+}
+
+type dummyStatusUpdater struct {
+}
+
+func (d dummyStatusUpdater) UpdateStatus(status v1.PolicyStatus) v1.PolicyStatus {
+	status.RulesAppliedCount++
+	return status
+}
+
+func (d dummyStatusUpdater) PolicyName() string {
+	return "policy1"
+}
+
+func TestKeyToMutex(t *testing.T) {
+	expectedCache := `{"policy1":{"averageExecutionTime":"","rulesAppliedCount":100}}`
+
+	stopCh := make(chan struct{})
+	s := NewSync(nil, dummyStore{})
+	for i := 0; i < 100; i++ {
+		go s.updateStatusCache(stopCh)
+	}
+
+	for i := 0; i < 100; i++ {
+		go s.Listener.Send(dummyStatusUpdater{})
+	}
+
+	<-time.After(time.Second * 3)
+	stopCh <- struct{}{}
+
+	cacheRaw, _ := json.Marshal(s.cache.data)
+	if string(cacheRaw) != expectedCache {
+		t.Errorf("\nTestcase Failed\nGot:\n%v\nExpected:\n%v\n", string(cacheRaw), expectedCache)
+	}
+}
diff --git a/pkg/policystore/policystore.go b/pkg/policystore/policystore.go
index 3d23b106e4..f8a8e30874 100644
--- a/pkg/policystore/policystore.go
+++ b/pkg/policystore/policystore.go
@@ -96,6 +96,10 @@ func (ps *PolicyStore) ListAll() ([]kyverno.ClusterPolicy, error) {
 	return policies, nil
 }
 
+func (ps *PolicyStore) Get(policyName string) (*kyverno.ClusterPolicy, error) {
+	return ps.pLister.Get(policyName)
+}
+
 //UnRegister Remove policy information
 func (ps *PolicyStore) UnRegister(policy kyverno.ClusterPolicy) error {
 	ps.mu.Lock()
diff --git a/pkg/policyviolation/clusterpv.go b/pkg/policyviolation/clusterpv.go
index 8b974594ae..c9336f8059 100644
--- a/pkg/policyviolation/clusterpv.go
+++ b/pkg/policyviolation/clusterpv.go
@@ -9,6 +9,7 @@ import (
 	kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
 	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
 	client "github.com/nirmata/kyverno/pkg/dclient"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -20,16 +21,20 @@ type clusterPV struct {
 	cpvLister kyvernolister.ClusterPolicyViolationLister
 	// policy violation interface
 	kyvernoInterface kyvernov1.KyvernoV1Interface
+	// update policy stats with violationCount
+	policyStatusListener policystatus.Listener
 }
 
 func newClusterPV(dclient *client.Client,
 	cpvLister kyvernolister.ClusterPolicyViolationLister,
 	kyvernoInterface kyvernov1.KyvernoV1Interface,
+	policyStatus policystatus.Listener,
 ) *clusterPV {
 	cpv := clusterPV{
-		dclient:          dclient,
-		cpvLister:        cpvLister,
-		kyvernoInterface: kyvernoInterface,
+		dclient:              dclient,
+		cpvLister:            cpvLister,
+		kyvernoInterface:     kyvernoInterface,
+		policyStatusListener: policyStatus,
 	}
 	return &cpv
 }
@@ -93,6 +98,11 @@ func (cpv *clusterPV) createPV(newPv *kyverno.ClusterPolicyViolation) error {
 		glog.V(4).Infof("failed to create Cluster Policy Violation: %v", err)
 		return err
 	}
+
+	if newPv.Annotations["fromSync"] != "true" {
+		cpv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})
+	}
+
 	glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
 	return nil
 }
@@ -115,5 +125,8 @@ func (cpv *clusterPV) updatePV(newPv, oldPv *kyverno.ClusterPolicyViolation) err
 	}
 	glog.Infof("cluster policy violation updated for resource %v", newPv.Spec.ResourceSpec)
 
+	if newPv.Annotations["fromSync"] != "true" {
+		cpv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})
+	}
 	return nil
 }
diff --git a/pkg/policyviolation/common.go b/pkg/policyviolation/common.go
index dfb4a704ac..6f077b25b8 100644
--- a/pkg/policyviolation/common.go
+++ b/pkg/policyviolation/common.go
@@ -7,6 +7,7 @@ import (
 	backoff "github.com/cenkalti/backoff"
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
 	client "github.com/nirmata/kyverno/pkg/dclient"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -70,3 +71,27 @@ func converLabelToSelector(labelMap map[string]string) (labels.Selector, error)
 
 	return policyViolationSelector, nil
 }
+
+type violationCount struct {
+	policyName    string
+	violatedRules []v1.ViolatedRule
+}
+
+func (vc violationCount) PolicyName() string {
+	return vc.policyName
+}
+
+func (vc violationCount) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {
+
+	var ruleNameToViolations = make(map[string]int)
+	for _, rule := range vc.violatedRules {
+		ruleNameToViolations[rule.Name]++
+	}
+
+	for i := range status.Rules {
+		status.ViolationCount += ruleNameToViolations[status.Rules[i].Name]
+		status.Rules[i].ViolationCount += ruleNameToViolations[status.Rules[i].Name]
+	}
+
+	return status
+}
diff --git a/pkg/policyviolation/generator.go b/pkg/policyviolation/generator.go
index 7d820f980b..db5ca63fcd 100644
--- a/pkg/policyviolation/generator.go
+++ b/pkg/policyviolation/generator.go
@@ -14,6 +14,7 @@ import (
 	kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
 	kyvernoinformer "github.com/nirmata/kyverno/pkg/client/informers/externalversions/kyverno/v1"
 	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 
 	dclient "github.com/nirmata/kyverno/pkg/dclient"
 	unstructured "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
@@ -37,9 +38,10 @@ type Generator struct {
 	// returns true if the cluster policy store has been synced at least once
 	pvSynced cache.InformerSynced
 	// returns true if the namespaced cluster policy store has been synced at at least once
-	nspvSynced cache.InformerSynced
-	queue      workqueue.RateLimitingInterface
-	dataStore  *dataStore
+	nspvSynced           cache.InformerSynced
+	queue                workqueue.RateLimitingInterface
+	dataStore            *dataStore
+	policyStatusListener policystatus.Listener
 }
 
 //NewDataStore returns an instance of data store
@@ -79,6 +81,7 @@ type Info struct {
 	PolicyName string
 	Resource   unstructured.Unstructured
 	Rules      []kyverno.ViolatedRule
+	FromSync   bool
 }
 
 func (i Info) toKey() string {
@@ -103,16 +106,18 @@ type GeneratorInterface interface {
 func NewPVGenerator(client *kyvernoclient.Clientset,
 	dclient *dclient.Client,
 	pvInformer kyvernoinformer.ClusterPolicyViolationInformer,
-	nspvInformer kyvernoinformer.PolicyViolationInformer) *Generator {
+	nspvInformer kyvernoinformer.PolicyViolationInformer,
+	policyStatus policystatus.Listener) *Generator {
 	gen := Generator{
-		kyvernoInterface: client.KyvernoV1(),
-		dclient:          dclient,
-		cpvLister:        pvInformer.Lister(),
-		pvSynced:         pvInformer.Informer().HasSynced,
-		nspvLister:       nspvInformer.Lister(),
-		nspvSynced:       nspvInformer.Informer().HasSynced,
-		queue:            workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName),
-		dataStore:        newDataStore(),
+		kyvernoInterface:     client.KyvernoV1(),
+		dclient:              dclient,
+		cpvLister:            pvInformer.Lister(),
+		pvSynced:             pvInformer.Informer().HasSynced,
+		nspvLister:           nspvInformer.Lister(),
+		nspvSynced:           nspvInformer.Informer().HasSynced,
+		queue:                workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), workQueueName),
+		dataStore:            newDataStore(),
+		policyStatusListener: policyStatus,
 	}
 	return &gen
 }
@@ -219,15 +224,21 @@ func (gen *Generator) syncHandler(info Info) error {
 	builder := newPvBuilder()
 	if info.Resource.GetNamespace() == "" {
 		// cluster scope resource generate a clusterpolicy violation
-		handler = newClusterPV(gen.dclient, gen.cpvLister, gen.kyvernoInterface)
+		handler = newClusterPV(gen.dclient, gen.cpvLister, gen.kyvernoInterface, gen.policyStatusListener)
 	} else {
 		// namespaced resources generated a namespaced policy violation in the namespace of the resource
-		handler = newNamespacedPV(gen.dclient, gen.nspvLister, gen.kyvernoInterface)
+		handler = newNamespacedPV(gen.dclient, gen.nspvLister, gen.kyvernoInterface, gen.policyStatusListener)
 	}
 
 	failure := false
 	pv := builder.generate(info)
 
+	if info.FromSync {
+		pv.Annotations = map[string]string{
+			"fromSync": "true",
+		}
+	}
+
 	// Create Policy Violations
 	glog.V(3).Infof("Creating policy violation: %s", info.toKey())
 	if err := handler.create(pv); err != nil {
diff --git a/pkg/policyviolation/namespacedpv.go b/pkg/policyviolation/namespacedpv.go
index 1967a0ba3f..ff32748d0b 100644
--- a/pkg/policyviolation/namespacedpv.go
+++ b/pkg/policyviolation/namespacedpv.go
@@ -9,6 +9,7 @@ import (
 	kyvernov1 "github.com/nirmata/kyverno/pkg/client/clientset/versioned/typed/kyverno/v1"
 	kyvernolister "github.com/nirmata/kyverno/pkg/client/listers/kyverno/v1"
 	client "github.com/nirmata/kyverno/pkg/dclient"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -20,16 +21,20 @@ type namespacedPV struct {
 	nspvLister kyvernolister.PolicyViolationLister
 	// policy violation interface
 	kyvernoInterface kyvernov1.KyvernoV1Interface
+	// update policy status with violationCount
+	policyStatusListener policystatus.Listener
 }
 
 func newNamespacedPV(dclient *client.Client,
 	nspvLister kyvernolister.PolicyViolationLister,
 	kyvernoInterface kyvernov1.KyvernoV1Interface,
+	policyStatus policystatus.Listener,
 ) *namespacedPV {
 	nspv := namespacedPV{
-		dclient:          dclient,
-		nspvLister:       nspvLister,
-		kyvernoInterface: kyvernoInterface,
+		dclient:              dclient,
+		nspvLister:           nspvLister,
+		kyvernoInterface:     kyvernoInterface,
+		policyStatusListener: policyStatus,
 	}
 	return &nspv
 }
@@ -92,6 +97,10 @@ func (nspv *namespacedPV) createPV(newPv *kyverno.PolicyViolation) error {
 		glog.V(4).Infof("failed to create Cluster Policy Violation: %v", err)
 		return err
 	}
+
+	if newPv.Annotations["fromSync"] != "true" {
+		nspv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})
+	}
 	glog.Infof("policy violation created for resource %v", newPv.Spec.ResourceSpec)
 	return nil
 }
@@ -112,6 +121,9 @@ func (nspv *namespacedPV) updatePV(newPv, oldPv *kyverno.PolicyViolation) error
 		return fmt.Errorf("failed to update namespaced policy violation: %v", err)
 	}
 
+	if newPv.Annotations["fromSync"] != "true" {
+		nspv.policyStatusListener.Send(violationCount{policyName: newPv.Spec.Policy, violatedRules: newPv.Spec.ViolatedRules})
+	}
 	glog.Infof("namespaced policy violation updated for resource %v", newPv.Spec.ResourceSpec)
 	return nil
 }
diff --git a/pkg/policyviolation/policyStatus_test.go b/pkg/policyviolation/policyStatus_test.go
new file mode 100644
index 0000000000..8db26ae4a6
--- /dev/null
+++ b/pkg/policyviolation/policyStatus_test.go
@@ -0,0 +1,74 @@
+package policyviolation
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+)
+
+func Test_Stats(t *testing.T) {
+	testCase := struct {
+		violationCountStats []struct {
+			policyName    string
+			violatedRules []v1.ViolatedRule
+		}
+		expectedOutput []byte
+		existingCache  map[string]v1.PolicyStatus
+	}{
+		existingCache: map[string]v1.PolicyStatus{
+			"policy1": {
+				Rules: []v1.RuleStats{
+					{
+						Name: "rule4",
+					},
+				},
+			},
+			"policy2": {
+				Rules: []v1.RuleStats{
+					{
+						Name: "rule4",
+					},
+				},
+			},
+		},
+		expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"","violationCount":1,"ruleStatus":[{"ruleName":"rule4","violationCount":1}]},"policy2":{"averageExecutionTime":"","violationCount":1,"ruleStatus":[{"ruleName":"rule4","violationCount":1}]}}`),
+		violationCountStats: []struct {
+			policyName    string
+			violatedRules []v1.ViolatedRule
+		}{
+			{
+				policyName: "policy1",
+				violatedRules: []v1.ViolatedRule{
+					{
+						Name: "rule4",
+					},
+				},
+			},
+			{
+				policyName: "policy2",
+				violatedRules: []v1.ViolatedRule{
+					{
+						Name: "rule4",
+					},
+				},
+			},
+		},
+	}
+
+	policyNameToStatus := testCase.existingCache
+
+	for _, violationCountStat := range testCase.violationCountStats {
+		receiver := &violationCount{
+			policyName:    violationCountStat.policyName,
+			violatedRules: violationCountStat.violatedRules,
+		}
+		policyNameToStatus[receiver.PolicyName()] = receiver.UpdateStatus(policyNameToStatus[receiver.PolicyName()])
+	}
+
+	output, _ := json.Marshal(policyNameToStatus)
+	if !reflect.DeepEqual(output, testCase.expectedOutput) {
+		t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
+	}
+}
diff --git a/pkg/webhooks/generation.go b/pkg/webhooks/generation.go
index 2dddf71e1c..956c568cac 100644
--- a/pkg/webhooks/generation.go
+++ b/pkg/webhooks/generation.go
@@ -1,8 +1,13 @@
 package webhooks
 
 import (
+	"reflect"
+	"sort"
+	"time"
+
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
 	"github.com/nirmata/kyverno/pkg/engine"
 	"github.com/nirmata/kyverno/pkg/engine/context"
 	"github.com/nirmata/kyverno/pkg/engine/response"
@@ -61,6 +66,9 @@ func (ws *WebhookServer) HandleGenerate(request *v1beta1.AdmissionRequest, polic
 		if len(engineResponse.PolicyResponse.Rules) > 0 {
 			// some generate rules do apply to the resource
 			engineResponses = append(engineResponses, engineResponse)
+			ws.statusListener.Send(generateStats{
+				resp: engineResponse,
+			})
 		}
 	}
 	// Adds Generate Request to a channel(queue size 1000) to generators
@@ -102,3 +110,73 @@ func transform(userRequestInfo kyverno.RequestInfo, er response.EngineResponse)
 	}
 	return gr
 }
+
+type generateStats struct {
+	resp response.EngineResponse
+}
+
+func (gs generateStats) PolicyName() string {
+	return gs.resp.PolicyResponse.Policy
+}
+
+func (gs generateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {
+	if reflect.DeepEqual(response.EngineResponse{}, gs.resp) {
+		return status
+	}
+
+	var nameToRule = make(map[string]v1.RuleStats)
+	for _, rule := range status.Rules {
+		nameToRule[rule.Name] = rule
+	}
+
+	for _, rule := range gs.resp.PolicyResponse.Rules {
+		ruleStat := nameToRule[rule.Name]
+		ruleStat.Name = rule.Name
+
+		averageOver := int64(ruleStat.AppliedCount + ruleStat.FailedCount)
+		ruleStat.ExecutionTime = updateAverageTime(
+			rule.ProcessingTime,
+			ruleStat.ExecutionTime,
+			averageOver).String()
+
+		if rule.Success {
+			status.RulesAppliedCount++
+			ruleStat.AppliedCount++
+		} else {
+			status.RulesFailedCount++
+			ruleStat.FailedCount++
+		}
+
+		nameToRule[rule.Name] = ruleStat
+	}
+
+	var policyAverageExecutionTime time.Duration
+	var ruleStats = make([]v1.RuleStats, 0, len(nameToRule))
+	for _, ruleStat := range nameToRule {
+		executionTime, err := time.ParseDuration(ruleStat.ExecutionTime)
+		if err == nil {
+			policyAverageExecutionTime += executionTime
+		}
+		ruleStats = append(ruleStats, ruleStat)
+	}
+
+	sort.Slice(ruleStats, func(i, j int) bool {
+		return ruleStats[i].Name < ruleStats[j].Name
+	})
+
+	status.AvgExecutionTime = policyAverageExecutionTime.String()
+	status.Rules = ruleStats
+
+	return status
+}
+
+func updateAverageTime(newTime time.Duration, oldAverageTimeString string, averageOver int64) time.Duration {
+	if averageOver == 0 {
+		return newTime
+	}
+	oldAverageExecutionTime, _ := time.ParseDuration(oldAverageTimeString)
+	numerator := (oldAverageExecutionTime.Nanoseconds() * averageOver) + newTime.Nanoseconds()
+	denominator := averageOver + 1
+	newAverageTimeInNanoSeconds := numerator / denominator
+	return time.Duration(newAverageTimeInNanoSeconds) * time.Nanosecond
+}
diff --git a/pkg/webhooks/mutation.go b/pkg/webhooks/mutation.go
index 650fce8374..2723b00110 100644
--- a/pkg/webhooks/mutation.go
+++ b/pkg/webhooks/mutation.go
@@ -1,17 +1,18 @@
 package webhooks
 
 import (
+	"reflect"
+	"sort"
 	"time"
 
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
 	"github.com/nirmata/kyverno/pkg/engine"
 	"github.com/nirmata/kyverno/pkg/engine/context"
 	"github.com/nirmata/kyverno/pkg/engine/response"
 	engineutils "github.com/nirmata/kyverno/pkg/engine/utils"
-	policyctr "github.com/nirmata/kyverno/pkg/policy"
 	"github.com/nirmata/kyverno/pkg/policyviolation"
-	"github.com/nirmata/kyverno/pkg/utils"
 	v1beta1 "k8s.io/api/admission/v1beta1"
 	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
 )
@@ -23,40 +24,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
 		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
 
 	var patches [][]byte
-	var policyStats []policyctr.PolicyStat
-
-	// gather stats from the engine response
-	gatherStat := func(policyName string, policyResponse response.PolicyResponse) {
-		ps := policyctr.PolicyStat{}
-		ps.PolicyName = policyName
-		ps.Stats.MutationExecutionTime = policyResponse.ProcessingTime
-		ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
-		// capture rule level stats
-		for _, rule := range policyResponse.Rules {
-			rs := policyctr.RuleStatinfo{}
-			rs.RuleName = rule.Name
-			rs.ExecutionTime = rule.RuleStats.ProcessingTime
-			if rule.Success {
-				rs.RuleAppliedCount++
-			} else {
-				rs.RulesFailedCount++
-			}
-			if rule.Patches != nil {
-				rs.MutationCount++
-			}
-			ps.Stats.Rules = append(ps.Stats.Rules, rs)
-		}
-		policyStats = append(policyStats, ps)
-	}
-	// send stats for aggregation
-	sendStat := func(blocked bool) {
-		for _, stat := range policyStats {
-			stat.Stats.ResourceBlocked = utils.Btoi(blocked)
-			//SEND
-			ws.policyStatus.SendStat(stat)
-		}
-	}
-
 	var engineResponses []response.EngineResponse
 
 	userRequestInfo := kyverno.RequestInfo{
@@ -91,12 +58,10 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
 	for _, policy := range policies {
 		glog.V(2).Infof("Handling mutation for Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
 			resource.GetKind(), resource.GetNamespace(), resource.GetName(), request.UID, request.Operation)
-
 		policyContext.Policy = policy
 		engineResponse := engine.Mutate(policyContext)
 		engineResponses = append(engineResponses, engineResponse)
-		// Gather policy application statistics
-		gatherStat(policy.Name, engineResponse.PolicyResponse)
+		ws.statusListener.Send(mutateStats{resp: engineResponse})
 		if !engineResponse.IsSuccesful() {
 			glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, resource.GetNamespace(), resource.GetName())
 			continue
@@ -131,8 +96,6 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
 	events := generateEvents(engineResponses, false, (request.Operation == v1beta1.Update))
 	ws.eventGen.Add(events...)
 
-	sendStat(false)
-
 	// debug info
 	func() {
 		if len(patches) != 0 {
@@ -152,3 +115,64 @@ func (ws *WebhookServer) HandleMutation(request *v1beta1.AdmissionRequest, resou
 	// patches holds all the successful patches, if no patch is created, it returns nil
 	return engineutils.JoinPatches(patches)
 }
+
+type mutateStats struct {
+	resp response.EngineResponse
+}
+
+func (ms mutateStats) PolicyName() string {
+	return ms.resp.PolicyResponse.Policy
+}
+
+func (ms mutateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {
+	if reflect.DeepEqual(response.EngineResponse{}, ms.resp) {
+		return status
+	}
+
+	var nameToRule = make(map[string]v1.RuleStats)
+	for _, rule := range status.Rules {
+		nameToRule[rule.Name] = rule
+	}
+
+	for _, rule := range ms.resp.PolicyResponse.Rules {
+		ruleStat := nameToRule[rule.Name]
+		ruleStat.Name = rule.Name
+
+		averageOver := int64(ruleStat.AppliedCount + ruleStat.FailedCount)
+		ruleStat.ExecutionTime = updateAverageTime(
+			rule.ProcessingTime,
+			ruleStat.ExecutionTime,
+			averageOver).String()
+
+		if rule.Success {
+			status.RulesAppliedCount++
+			status.ResourcesMutatedCount++
+			ruleStat.AppliedCount++
+			ruleStat.ResourcesMutatedCount++
+		} else {
+			status.RulesFailedCount++
+			ruleStat.FailedCount++
+		}
+
+		nameToRule[rule.Name] = ruleStat
+	}
+
+	var policyAverageExecutionTime time.Duration
+	var ruleStats = make([]v1.RuleStats, 0, len(nameToRule))
+	for _, ruleStat := range nameToRule {
+		executionTime, err := time.ParseDuration(ruleStat.ExecutionTime)
+		if err == nil {
+			policyAverageExecutionTime += executionTime
+		}
+		ruleStats = append(ruleStats, ruleStat)
+	}
+
+	sort.Slice(ruleStats, func(i, j int) bool {
+		return ruleStats[i].Name < ruleStats[j].Name
+	})
+
+	status.AvgExecutionTime = policyAverageExecutionTime.String()
+	status.Rules = ruleStats
+
+	return status
+}
diff --git a/pkg/webhooks/policyStatus_test.go b/pkg/webhooks/policyStatus_test.go
new file mode 100644
index 0000000000..6c71fc6222
--- /dev/null
+++ b/pkg/webhooks/policyStatus_test.go
@@ -0,0 +1,211 @@
+package webhooks
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+	"time"
+
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+	"github.com/nirmata/kyverno/pkg/engine/response"
+)
+
+func Test_GenerateStats(t *testing.T) {
+	testCase := struct {
+		generateStats  []response.EngineResponse
+		expectedOutput []byte
+	}{
+		expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule5","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"251ns","failedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule5","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule6","averageExecutionTime":"211ns","failedCount":1}]}}`),
+		generateStats: []response.EngineResponse{
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy: "policy1",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule5",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 243,
+							},
+						},
+						{
+							Name:    "rule6",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 251,
+							},
+						},
+					},
+				},
+			},
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy: "policy2",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule5",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 222,
+							},
+						},
+						{
+							Name:    "rule6",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 211,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	policyNameToStatus := map[string]v1.PolicyStatus{}
+
+	for _, generateStat := range testCase.generateStats {
+		receiver := generateStats{
+			resp: generateStat,
+		}
+		policyNameToStatus[receiver.PolicyName()] = receiver.UpdateStatus(policyNameToStatus[receiver.PolicyName()])
+	}
+
+	output, _ := json.Marshal(policyNameToStatus)
+	if !reflect.DeepEqual(output, testCase.expectedOutput) {
+		t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
+	}
+}
+
+func Test_MutateStats(t *testing.T) {
+	testCase := struct {
+		mutateStats    []response.EngineResponse
+		expectedOutput []byte
+	}{
+		expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesMutatedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"243ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"251ns","failedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesMutatedCount":1,"ruleStatus":[{"ruleName":"rule1","averageExecutionTime":"222ns","appliedCount":1,"resourcesMutatedCount":1},{"ruleName":"rule2","averageExecutionTime":"211ns","failedCount":1}]}}`),
+		mutateStats: []response.EngineResponse{
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy: "policy1",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule1",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 243,
+							},
+						},
+						{
+							Name:    "rule2",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 251,
+							},
+						},
+					},
+				},
+			},
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy: "policy2",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule1",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 222,
+							},
+						},
+						{
+							Name:    "rule2",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 211,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	policyNameToStatus := map[string]v1.PolicyStatus{}
+	for _, mutateStat := range testCase.mutateStats {
+		receiver := mutateStats{
+			resp: mutateStat,
+		}
+		policyNameToStatus[receiver.PolicyName()] = receiver.UpdateStatus(policyNameToStatus[receiver.PolicyName()])
+	}
+
+	output, _ := json.Marshal(policyNameToStatus)
+	if !reflect.DeepEqual(output, testCase.expectedOutput) {
+		t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
+	}
+}
+
+func Test_ValidateStats(t *testing.T) {
+	testCase := struct {
+		validateStats  []response.EngineResponse
+		expectedOutput []byte
+	}{
+		expectedOutput: []byte(`{"policy1":{"averageExecutionTime":"494ns","rulesFailedCount":1,"rulesAppliedCount":1,"resourcesBlockedCount":1,"ruleStatus":[{"ruleName":"rule3","averageExecutionTime":"243ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"251ns","failedCount":1,"resourcesBlockedCount":1}]},"policy2":{"averageExecutionTime":"433ns","rulesFailedCount":1,"rulesAppliedCount":1,"ruleStatus":[{"ruleName":"rule3","averageExecutionTime":"222ns","appliedCount":1},{"ruleName":"rule4","averageExecutionTime":"211ns","failedCount":1}]}}`),
+		validateStats: []response.EngineResponse{
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy:                  "policy1",
+					ValidationFailureAction: "enforce",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule3",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 243,
+							},
+						},
+						{
+							Name:    "rule4",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 251,
+							},
+						},
+					},
+				},
+			},
+			{
+				PolicyResponse: response.PolicyResponse{
+					Policy: "policy2",
+					Rules: []response.RuleResponse{
+						{
+							Name:    "rule3",
+							Success: true,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 222,
+							},
+						},
+						{
+							Name:    "rule4",
+							Success: false,
+							RuleStats: response.RuleStats{
+								ProcessingTime: time.Nanosecond * 211,
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	policyNameToStatus := map[string]v1.PolicyStatus{}
+	for _, validateStat := range testCase.validateStats {
+		receiver := validateStats{
+			resp: validateStat,
+		}
+		policyNameToStatus[receiver.PolicyName()] = receiver.UpdateStatus(policyNameToStatus[receiver.PolicyName()])
+	}
+
+	output, _ := json.Marshal(policyNameToStatus)
+	if !reflect.DeepEqual(output, testCase.expectedOutput) {
+		t.Errorf("\n\nTestcase has failed\nExpected:\n%v\nGot:\n%v\n\n", string(testCase.expectedOutput), string(output))
+	}
+}
diff --git a/pkg/webhooks/server.go b/pkg/webhooks/server.go
index d401cdc449..b0ca988449 100644
--- a/pkg/webhooks/server.go
+++ b/pkg/webhooks/server.go
@@ -18,7 +18,7 @@ import (
 	"github.com/nirmata/kyverno/pkg/config"
 	client "github.com/nirmata/kyverno/pkg/dclient"
 	"github.com/nirmata/kyverno/pkg/event"
-	"github.com/nirmata/kyverno/pkg/policy"
+	"github.com/nirmata/kyverno/pkg/policystatus"
 	"github.com/nirmata/kyverno/pkg/policystore"
 	"github.com/nirmata/kyverno/pkg/policyviolation"
 	tlsutils "github.com/nirmata/kyverno/pkg/tls"
@@ -55,7 +55,7 @@ type WebhookServer struct {
 	// webhook registration client
 	webhookRegistrationClient *webhookconfig.WebhookRegistrationClient
 	// API to send policy stats for aggregation
-	policyStatus policy.PolicyStatusInterface
+	statusListener policystatus.Listener
 	// helpers to validate against current loaded configuration
 	configHandler config.Interface
 	// channel for cleanup notification
@@ -82,7 +82,7 @@ func NewWebhookServer(
 	crbInformer rbacinformer.ClusterRoleBindingInformer,
 	eventGen event.Interface,
 	webhookRegistrationClient *webhookconfig.WebhookRegistrationClient,
-	policyStatus policy.PolicyStatusInterface,
+	statusSync policystatus.Listener,
 	configHandler config.Interface,
 	pMetaStore policystore.LookupInterface,
 	pvGenerator policyviolation.GeneratorInterface,
@@ -112,7 +112,7 @@ func NewWebhookServer(
 		crbSynced:                 crbInformer.Informer().HasSynced,
 		eventGen:                  eventGen,
 		webhookRegistrationClient: webhookRegistrationClient,
-		policyStatus:              policyStatus,
+		statusListener:            statusSync,
 		configHandler:             configHandler,
 		cleanUp:                   cleanUp,
 		lastReqTime:               resourceWebhookWatcher.LastReqTime,
diff --git a/pkg/webhooks/validation.go b/pkg/webhooks/validation.go
index cf30c61f17..54a7fbdf3e 100644
--- a/pkg/webhooks/validation.go
+++ b/pkg/webhooks/validation.go
@@ -2,16 +2,16 @@ package webhooks
 
 import (
 	"reflect"
+	"sort"
 	"time"
 
 	"github.com/golang/glog"
 	kyverno "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
+	v1 "github.com/nirmata/kyverno/pkg/api/kyverno/v1"
 	"github.com/nirmata/kyverno/pkg/engine"
 	"github.com/nirmata/kyverno/pkg/engine/context"
 	"github.com/nirmata/kyverno/pkg/engine/response"
-	policyctr "github.com/nirmata/kyverno/pkg/policy"
 	"github.com/nirmata/kyverno/pkg/policyviolation"
-	"github.com/nirmata/kyverno/pkg/utils"
 	v1beta1 "k8s.io/api/admission/v1beta1"
 )
 
@@ -22,36 +22,7 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
 	glog.V(4).Infof("Receive request in validating webhook: Kind=%s, Namespace=%s Name=%s UID=%s patchOperation=%s",
 		request.Kind.Kind, request.Namespace, request.Name, request.UID, request.Operation)
 
-	var policyStats []policyctr.PolicyStat
 	evalTime := time.Now()
-	// gather stats from the engine response
-	gatherStat := func(policyName string, policyResponse response.PolicyResponse) {
-		ps := policyctr.PolicyStat{}
-		ps.PolicyName = policyName
-		ps.Stats.ValidationExecutionTime = policyResponse.ProcessingTime
-		ps.Stats.RulesAppliedCount = policyResponse.RulesAppliedCount
-		// capture rule level stats
-		for _, rule := range policyResponse.Rules {
-			rs := policyctr.RuleStatinfo{}
-			rs.RuleName = rule.Name
-			rs.ExecutionTime = rule.RuleStats.ProcessingTime
-			if rule.Success {
-				rs.RuleAppliedCount++
-			} else {
-				rs.RulesFailedCount++
-			}
-			ps.Stats.Rules = append(ps.Stats.Rules, rs)
-		}
-		policyStats = append(policyStats, ps)
-	}
-	// send stats for aggregation
-	sendStat := func(blocked bool) {
-		for _, stat := range policyStats {
-			stat.Stats.ResourceBlocked = utils.Btoi(blocked)
-			//SEND
-			ws.policyStatus.SendStat(stat)
-		}
-	}
 
 	// Get new and old resource
 	newR, oldR, err := extractResources(patchedResource, request)
@@ -100,8 +71,9 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
 			continue
 		}
 		engineResponses = append(engineResponses, engineResponse)
-		// Gather policy application statistics
-		gatherStat(policy.Name, engineResponse.PolicyResponse)
+		ws.statusListener.Send(validateStats{
+			resp: engineResponse,
+		})
 		if !engineResponse.IsSuccesful() {
 			glog.V(4).Infof("Failed to apply policy %s on resource %s/%s\n", policy.Name, newR.GetNamespace(), newR.GetName())
 			continue
@@ -129,9 +101,6 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
 	ws.eventGen.Add(events...)
 	if blocked {
 		glog.V(4).Infof("resource %s/%s/%s is blocked\n", newR.GetKind(), newR.GetNamespace(), newR.GetName())
-		sendStat(true)
-		// EVENTS
-		// - event on the Policy
 		return false, getEnforceFailureErrorMsg(engineResponses)
 	}
 
@@ -139,8 +108,70 @@ func (ws *WebhookServer) HandleValidation(request *v1beta1.AdmissionRequest, pol
 	// violations are created with resource on "audit"
 	pvInfos := policyviolation.GeneratePVsFromEngineResponse(engineResponses)
 	ws.pvGenerator.Add(pvInfos...)
-	sendStat(false)
 	// report time end
 	glog.V(4).Infof("report: %v %s/%s/%s", time.Since(reportTime), request.Kind, request.Namespace, request.Name)
 	return true, ""
 }
+
+type validateStats struct {
+	resp response.EngineResponse
+}
+
+func (vs validateStats) PolicyName() string {
+	return vs.resp.PolicyResponse.Policy
+}
+
+func (vs validateStats) UpdateStatus(status kyverno.PolicyStatus) kyverno.PolicyStatus {
+	if reflect.DeepEqual(response.EngineResponse{}, vs.resp) {
+		return status
+	}
+
+	var nameToRule = make(map[string]v1.RuleStats)
+	for _, rule := range status.Rules {
+		nameToRule[rule.Name] = rule
+	}
+
+	for _, rule := range vs.resp.PolicyResponse.Rules {
+		ruleStat := nameToRule[rule.Name]
+		ruleStat.Name = rule.Name
+
+		averageOver := int64(ruleStat.AppliedCount + ruleStat.FailedCount)
+		ruleStat.ExecutionTime = updateAverageTime(
+			rule.ProcessingTime,
+			ruleStat.ExecutionTime,
+			averageOver).String()
+
+		if rule.Success {
+			status.RulesAppliedCount++
+			ruleStat.AppliedCount++
+		} else {
+			status.RulesFailedCount++
+			ruleStat.FailedCount++
+			if vs.resp.PolicyResponse.ValidationFailureAction == "enforce" {
+				status.ResourcesBlockedCount++
+				ruleStat.ResourcesBlockedCount++
+			}
+		}
+
+		nameToRule[rule.Name] = ruleStat
+	}
+
+	var policyAverageExecutionTime time.Duration
+	var ruleStats = make([]v1.RuleStats, 0, len(nameToRule))
+	for _, ruleStat := range nameToRule {
+		executionTime, err := time.ParseDuration(ruleStat.ExecutionTime)
+		if err == nil {
+			policyAverageExecutionTime += executionTime
+		}
+		ruleStats = append(ruleStats, ruleStat)
+	}
+
+	sort.Slice(ruleStats, func(i, j int) bool {
+		return ruleStats[i].Name < ruleStats[j].Name
+	})
+
+	status.AvgExecutionTime = policyAverageExecutionTime.String()
+	status.Rules = ruleStats
+
+	return status
+}