diff --git a/internal/datalakes/inmemory/policyhub.go b/internal/datalakes/inmemory/policyhub.go
index 8dd79cad..46f927e6 100644
--- a/internal/datalakes/inmemory/policyhub.go
+++ b/internal/datalakes/inmemory/policyhub.go
@@ -522,7 +522,7 @@ func (db *Db) entityGraphExecutionChecksum(ctx context.Context, mrn string) (str
 		}
 	}
 
-	return policy.BundleExecutionChecksum(policyObj, framework), nil
+	return policy.BundleExecutionChecksum(ctx, policyObj, framework), nil
 }
 
 // EntityGraphContentChecksum retrieves the content checksum for a given entity.
diff --git a/policy/bundle.go b/policy/bundle.go
index b6778aac..ccf2c6d4 100644
--- a/policy/bundle.go
+++ b/policy/bundle.go
@@ -91,7 +91,7 @@ func (l *BundleLoader) BundleFromPaths(paths ...string) (*Bundle, error) {
 
 // BundleExecutionChecksum creates a combined execution checksum from a policy
 // and framework. Either may be nil.
-func BundleExecutionChecksum(policy *Policy, framework *Framework) string {
+func BundleExecutionChecksum(ctx context.Context, policy *Policy, framework *Framework) string {
 	res := checksums.New
 	if policy != nil {
 		res = res.Add(policy.GraphExecutionChecksum)
@@ -102,7 +102,11 @@ func BundleExecutionChecksum(policy *Policy, framework *Framework) string {
 	// So far the checksum only includes the policy and the framework
 	// It does not change if any of the jobs changes, only if the policy or the framework changes
 	// To update the resolved policy, when we change how it is generated, change the incoporated version of the resolver
-	res = res.Add(RESOLVER_VERSION)
+	if IsNextGenResolver(ctx) {
+		res = res.Add(RESOLVER_VERSION_NG)
+	} else {
+		res = res.Add(RESOLVER_VERSION)
+	}
 
 	return res.String()
 }
diff --git a/policy/cnspec_policy.pb.go b/policy/cnspec_policy.pb.go
index 6ee12a6a..dc59039c 100644
--- a/policy/cnspec_policy.pb.go
+++ b/policy/cnspec_policy.pb.go
@@ -493,33 +493,36 @@ type ReportingJob_Type int32
 
 const (
 	ReportingJob_UNSPECIFIED ReportingJob_Type = 0
-	ReportingJob_CHECK       ReportingJob_Type = 1
-	ReportingJob_DATA_QUERY  ReportingJob_Type = 2
 	ReportingJob_CONTROL     ReportingJob_Type = 3
 	ReportingJob_POLICY      ReportingJob_Type = 4
 	ReportingJob_FRAMEWORK   ReportingJob_Type = 5
 	ReportingJob_RISK_FACTOR ReportingJob_Type = 6
+	// DO NOT USE CHECK OR DATA_QUERY, THEY ARE DEPRECATED
+	// Here's the reason why:
+	// A query can be either or both. We cannot pick one in all cases
+	ReportingJob_CHECK      ReportingJob_Type = 1
+	ReportingJob_DATA_QUERY ReportingJob_Type = 2
 )
 
 // Enum value maps for ReportingJob_Type.
 var (
 	ReportingJob_Type_name = map[int32]string{
 		0: "UNSPECIFIED",
-		1: "CHECK",
-		2: "DATA_QUERY",
 		3: "CONTROL",
 		4: "POLICY",
 		5: "FRAMEWORK",
 		6: "RISK_FACTOR",
+		1: "CHECK",
+		2: "DATA_QUERY",
 	}
 	ReportingJob_Type_value = map[string]int32{
 		"UNSPECIFIED": 0,
-		"CHECK":       1,
-		"DATA_QUERY":  2,
 		"CONTROL":     3,
 		"POLICY":      4,
 		"FRAMEWORK":   5,
 		"RISK_FACTOR": 6,
+		"CHECK":       1,
+		"DATA_QUERY":  2,
 	}
 )
 
@@ -7078,12 +7081,12 @@ var file_cnspec_policy_proto_rawDesc = []byte{
 	0x63, 0x6e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x65, 0x78, 0x70, 0x6c, 0x6f, 0x72, 0x65, 0x72,
 	0x2e, 0x49, 0x6d, 0x70, 0x61, 0x63, 0x74, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02,
 	0x38, 0x01, 0x22, 0x6b, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0f, 0x0a, 0x0b, 0x55, 0x4e,
-	0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x43,
-	0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x12, 0x0e, 0x0a, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x51,
-	0x55, 0x45, 0x52, 0x59, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x43, 0x4f, 0x4e, 0x54, 0x52, 0x4f,
-	0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x04, 0x12,
-	0x0d, 0x0a, 0x09, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x57, 0x4f, 0x52, 0x4b, 0x10, 0x05, 0x12, 0x0f,
-	0x0a, 0x0b, 0x52, 0x49, 0x53, 0x4b, 0x5f, 0x46, 0x41, 0x43, 0x54, 0x4f, 0x52, 0x10, 0x06, 0x22,
+	0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x0b, 0x0a, 0x07, 0x43,
+	0x4f, 0x4e, 0x54, 0x52, 0x4f, 0x4c, 0x10, 0x03, 0x12, 0x0a, 0x0a, 0x06, 0x50, 0x4f, 0x4c, 0x49,
+	0x43, 0x59, 0x10, 0x04, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x52, 0x41, 0x4d, 0x45, 0x57, 0x4f, 0x52,
+	0x4b, 0x10, 0x05, 0x12, 0x0f, 0x0a, 0x0b, 0x52, 0x49, 0x53, 0x4b, 0x5f, 0x46, 0x41, 0x43, 0x54,
+	0x4f, 0x52, 0x10, 0x06, 0x12, 0x09, 0x0a, 0x05, 0x43, 0x48, 0x45, 0x43, 0x4b, 0x10, 0x01, 0x12,
+	0x0e, 0x0a, 0x0a, 0x44, 0x41, 0x54, 0x41, 0x5f, 0x51, 0x55, 0x45, 0x52, 0x59, 0x10, 0x02, 0x22,
 	0xcc, 0x07, 0x0a, 0x06, 0x52, 0x65, 0x70, 0x6f, 0x72, 0x74, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x63,
 	0x6f, 0x72, 0x69, 0x6e, 0x67, 0x5f, 0x6d, 0x72, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
 	0x0a, 0x73, 0x63, 0x6f, 0x72, 0x69, 0x6e, 0x67, 0x4d, 0x72, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x65,
diff --git a/policy/cnspec_policy.proto b/policy/cnspec_policy.proto
index 6d94d4a5..b8623acc 100644
--- a/policy/cnspec_policy.proto
+++ b/policy/cnspec_policy.proto
@@ -556,12 +556,16 @@ message ReportingJob {
 
   enum Type {
     UNSPECIFIED = 0;
-    CHECK = 1;
-    DATA_QUERY = 2;
     CONTROL = 3;
     POLICY = 4;
     FRAMEWORK = 5;
     RISK_FACTOR = 6;
+
+    // DO NOT USE CHECK OR DATA_QUERY, THEY ARE DEPRECATED
+    // Here's the reason why:
+    // A query can be either or both. We cannot pick one in all cases
+    CHECK = 1;
+    DATA_QUERY = 2;
   }
 
   string checksum = 1;
diff --git a/policy/resolved_policy_builder.go b/policy/resolved_policy_builder.go
new file mode 100644
index 00000000..87239dee
--- /dev/null
+++ b/policy/resolved_policy_builder.go
@@ -0,0 +1,1151 @@
+// Copyright (c) Mondoo, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+
+package policy
+
+import (
+	"context"
+	"fmt"
+	"slices"
+	"time"
+
+	"github.com/pkg/errors"
+	"github.com/rs/zerolog/log"
+	"go.mondoo.com/cnquery/v11/explorer"
+	"go.mondoo.com/cnquery/v11/llx"
+	"go.mondoo.com/cnquery/v11/mqlc"
+	"go.mondoo.com/cnquery/v11/mrn"
+)
+
+// buildResolvedPolicy builds a resolved policy from a bundle
+func buildResolvedPolicy(ctx context.Context, bundleMrn string, bundle *Bundle, assetFilters []*explorer.Mquery, now time.Time, compilerConf mqlc.CompilerConfig) (*ResolvedPolicy, error) {
+	bundleMap := bundle.ToMap()
+	assetFilterMap := make(map[string]struct{}, len(assetFilters))
+	for _, f := range assetFilters {
+		assetFilterMap[f.CodeId] = struct{}{}
+	}
+
+	policyObj := bundleMap.Policies[bundleMrn]
+	frameworkObj := bundleMap.Frameworks[bundleMrn]
+
+	builder := &resolvedPolicyBuilder{
+		bundleMrn:            bundleMrn,
+		bundleMap:            bundleMap,
+		assetFilters:         assetFilterMap,
+		nodes:                map[string]rpBuilderNode{},
+		reportsToEdges:       map[string][]string{},
+		reportsFromEdges:     map[string][]edgeImpact{},
+		policyScoringSystems: map[string]explorer.ScoringSystem{},
+		actionOverrides:      map[string]explorer.Action{},
+		impactOverrides:      map[string]*explorer.Impact{},
+		riskMagnitudes:       map[string]*RiskMagnitude{},
+		propsCache:           explorer.NewPropsCache(),
+		queryTypes:           map[string]queryType{},
+		now:                  now,
+	}
+
+	builder.gatherGlobalInfoFromPolicy(policyObj)
+	builder.gatherGlobalInfoFromFramework(frameworkObj)
+	builder.collectQueryTypes(bundleMrn, builder.queryTypes)
+
+	builder.addPolicy(policyObj)
+
+	if frameworkObj != nil {
+		builder.addFramework(frameworkObj)
+	}
+
+	resolvedPolicyExecutionChecksum := BundleExecutionChecksum(ctx, policyObj, frameworkObj)
+	assetFiltersChecksum, err := ChecksumAssetFilters(assetFilters, compilerConf)
+	if err != nil {
+		return nil, err
+	}
+
+	builderData := &rpBuilderData{
+		baseChecksum: checksumStrings(resolvedPolicyExecutionChecksum, assetFiltersChecksum, "v2"),
+		propsCache:   builder.propsCache,
+		compilerConf: compilerConf,
+	}
+
+	resolvedPolicy := &ResolvedPolicy{
+		ExecutionJob: &ExecutionJob{
+			Checksum: "",
+			Queries:  map[string]*ExecutionQuery{},
+		},
+		CollectorJob: &CollectorJob{
+			Checksum:         "",
+			ReportingJobs:    map[string]*ReportingJob{},
+			ReportingQueries: map[string]*StringArray{},
+			Datapoints:       map[string]*DataQueryInfo{},
+			RiskMrns:         map[string]*StringArray{},
+			RiskFactors:      map[string]*RiskFactor{},
+		},
+		Filters:                assetFilters,
+		GraphExecutionChecksum: resolvedPolicyExecutionChecksum,
+		FiltersChecksum:        assetFiltersChecksum,
+	}
+
+	// We will walk the graph from the non prunable nodes out. This means that if something is not connected
+	// to a non prunable node, it will not be included in the resolved policy
+	nonPrunables := make([]rpBuilderNode, 0, len(builder.nodes))
+
+	for _, n := range builder.nodes {
+		if !n.isPrunable() {
+			nonPrunables = append(nonPrunables, n)
+		}
+	}
+
+	visited := make(map[string]struct{}, len(builder.nodes))
+	var walk func(node rpBuilderNode) error
+	walk = func(node rpBuilderNode) error {
+		// Check if we've already visited this node
+		if _, ok := visited[node.getId()]; ok {
+			return nil
+		}
+		visited[node.getId()] = struct{}{}
+
+		// Build the necessary parts of the resolved policy for each node
+		if err := node.build(resolvedPolicy, builderData); err != nil {
+			log.Error().Err(err).Str("node", node.getId()).Msg("error building node")
+			return err
+		}
+		// Walk to each parent node and recurse
+		for _, edge := range builder.reportsToEdges[node.getId()] {
+			if edgeNode, ok := builder.nodes[edge]; ok {
+				if err := walk(edgeNode); err != nil {
+					return err
+				}
+
+			} else {
+				log.Debug().Str("from", node.getId()).Str("to", edge).Msg("edge not found")
+			}
+		}
+		return nil
+	}
+
+	for _, n := range nonPrunables {
+		if err := walk(n); err != nil {
+			return nil, err
+		}
+	}
+
+	// We need to connect the reporting jobs. We've stored them by uuid in the collector job. However,
+	// our graph uses the qr id to connect them.
+	reportingJobsByQrId := make(map[string]*ReportingJob, len(resolvedPolicy.CollectorJob.ReportingJobs))
+	for _, rj := range resolvedPolicy.CollectorJob.ReportingJobs {
+		if _, ok := reportingJobsByQrId[rj.QrId]; ok {
+			// We should never have multiple reporting jobs with the same qr id. Scores are stored
+			// by qr id, not by uuid. This would cause issues where scores would flop around
+			log.Error().Str("qr_id", rj.QrId).Msg("multipe reporting jobs with the same qr id")
+			return nil, errors.New("multiple reporting jobs with the same qr id")
+		}
+		reportingJobsByQrId[rj.QrId] = rj
+	}
+
+	// For each parent qr id, we need to connect the child reporting jobs with the impact.
+	// connectReportingJobNotifies will add the link from the child to the parent, and
+	// the parent to the child with the impact
+	for parentQrId, edges := range builder.reportsFromEdges {
+		for _, edge := range edges {
+			parent := reportingJobsByQrId[parentQrId]
+			if parent == nil {
+				// It's possible that the parent reporting job was not included in the resolved policy
+				// because it was not connected to a leaf node (e.g. a policy that was not connected to
+				// any check or data query). In this case, we can just skip it
+				log.Debug().Str("parent", parentQrId).Msg("reporting job not found")
+				continue
+			}
+
+			if child, ok := reportingJobsByQrId[edge.edge]; ok {
+				// Also possible a child was not included in the resolved policy
+				connectReportingJobNotifies(child, parent, edge.impact)
+			}
+		}
+	}
+
+	rootReportingJob := reportingJobsByQrId[bundleMrn]
+	if rootReportingJob == nil {
+		return nil, explorer.NewAssetMatchError(bundleMrn, "policies", "no-matching-policy", assetFilters, policyObj.ComputedFilters)
+	}
+	rootReportingJob.QrId = "root"
+
+	resolvedPolicy.ReportingJobUuid = rootReportingJob.Uuid
+
+	refreshChecksums(resolvedPolicy.ExecutionJob, resolvedPolicy.CollectorJob)
+	for _, rj := range resolvedPolicy.CollectorJob.ReportingJobs {
+		rj.RefreshChecksum()
+	}
+
+	return resolvedPolicy, nil
+}
+
+// resolvedPolicyBuilder contains data that helps build the resolved policy. It maintains a graph of nodes.
+// These nodes are the policies, controls, frameworks, checks, data queries, and execution queries. They
+// get a chance to add themselves to the resolved policy in the way that they need to be added. They all
+// add reporting jobs. Some nodes do other things like add the compiled query to the resolved policy. These nodes
+// are connected by edges. These edges are the edges used to connect the reporting jobs in the resolved policy.
+// Edges are added using the addEdge method. This will take care of maintaining the notifies edge and the childJobs
+// edge from the reporting jobs simultaneously so that they are in sync.
+type resolvedPolicyBuilder struct {
+	// bundleMrn is the mrn of the bundle that is being resolved. It will be replaced by "root" in the
+	// resolved policy's reporting jobs so that it can be reused by other bundles that are identical in
+	// everything except the mrn of the root.
+	bundleMrn string
+	// bundleMap is the bundle that is being resolved converted into a PolicyBundleMap
+	bundleMap *PolicyBundleMap
+
+	// nodes is a map of all the nodes that are in the graph. These nodes will build the resolved
+	// policy. nodes is walked from the non prunable nodes out. This means that if something is not
+	// connected to a non prunable node, it will not be included in the resolved policy
+	nodes map[string]rpBuilderNode
+	// reportsToEdges maintains the notifies edges from the reporting jobs.
+	reportsToEdges map[string][]string
+	// reportsFromEdges maintains the childJobs edges from the reporting jobs. This is where the impact
+	// is stored.
+	reportsFromEdges map[string][]edgeImpact
+
+	// assetFilters is the asset filters that are used to select the policies and queries that are
+	// run
+	assetFilters map[string]struct{}
+	// policyScoringSystems is a map of the scoring systems for each policy
+	policyScoringSystems map[string]explorer.ScoringSystem
+	// actionOverrides is a map of the actions that are overridden by the policies
+	actionOverrides map[string]explorer.Action
+	// impactOverrides is a map of the impacts that are overridden by the policies. The worst impact
+	// is used
+	impactOverrides map[string]*explorer.Impact
+	// riskMagnitudes is a map of the risk magnitudes that are set for risk factors
+	riskMagnitudes map[string]*RiskMagnitude
+	// queryTypes is a map of the query types for each query. A query can be a scoring query, a data query,
+	// or both. We analyze all matching policies to determine the query type. If a query shows up in checks,
+	// it is a scoring query. If it shows up in data queries, it is a data query. If it shows up in both, it is
+	// set to both.
+	queryTypes map[string]queryType
+	// propsCache is a cache of the properties that are used in the queries
+	propsCache explorer.PropsCache
+	// now is the time that the resolved policy is being built
+	now time.Time
+}
+
+type edgeImpact struct {
+	edge   string
+	impact *explorer.Impact
+}
+
+// rpBuilderNode is a node in the graph. It represents a policy, control, framework, check, data query, or execution query.
+// Each node implementation decides how it needs to be added to the resolved policy. It is currently assumed that
+// each node will add a reporting job to the resolved policy, as the edges are used to automatically connect the reporting jobs.
+type rpBuilderNode interface {
+	// getId returns the id of the node. This is used to identify the node in the graph, a mrn or code id
+	getId() string
+	// isPrunable returns whether the node can be pruned from the graph. It will be pruned if it a non-prunable node
+	// doesn't have a path TO it. In context of building the resolved policy, this means that the node is not connected
+	// to an executable query, or is the root node.
+	isPrunable() bool
+	// build is responsible for updating the resolved policy. It will add things like reporting jobs, connect datapoints,
+	// adding the compiled query, etc.
+	build(*ResolvedPolicy, *rpBuilderData) error
+}
+
+// rpBuilderData is the data that is used to build the resolved policy
+type rpBuilderData struct {
+	baseChecksum string
+	propsCache   explorer.PropsCache
+	compilerConf mqlc.CompilerConfig
+}
+
+func (d *rpBuilderData) relativeChecksum(s string) string {
+	return checksumStrings(d.baseChecksum, s)
+}
+
+// rpBuilderPolicyNode is a node that represents a policy in the graph. It will add a reporting job to the resolved policy
+// for the policy
+type rpBuilderPolicyNode struct {
+	policy        *Policy
+	scoringSystem explorer.ScoringSystem
+	isRoot        bool
+}
+
+func (n *rpBuilderPolicyNode) getId() string {
+	return n.policy.Mrn
+}
+
+func (n *rpBuilderPolicyNode) isPrunable() bool {
+	// We do not allow pruning the root node. This covers cases where the policy matches the asset filters,
+	// but we have no active checks or queries. This will end up reporting a U for the score
+	return !n.isRoot
+}
+
+func (n *rpBuilderPolicyNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	if n.isRoot {
+		// If the policy is the root, we need a different checksum for the reporting job because we want it
+		// to be reusable by other bundles that are identical in everything except the root mrn
+		addReportingJob(n.policy.Mrn, true, data.relativeChecksum(n.policy.GraphExecutionChecksum), ReportingJob_POLICY, rp)
+	} else {
+		// the uuid used to be a checksum of the policy mrn, impact, and action
+		// I don't think this can be correct in all cases as you could at some point
+		// have a policy report to multiple other policies with different impacts
+		// (we don't have that case right now)
+		// These checksum changes should be accounted for in the root
+		rj := addReportingJob(n.policy.Mrn, true, data.relativeChecksum(n.policy.Mrn), ReportingJob_POLICY, rp)
+		rj.ScoringSystem = n.scoringSystem
+	}
+
+	return nil
+}
+
+// rpBuilderGenericQueryNode is a node that represents a query by mrn in the graph. It will add a reporting job,
+// and fill out the reporting queries in the resolved policy
+type rpBuilderGenericQueryNode struct {
+	// queryMrn is the mrn of the query
+	queryMrn string
+	// queryType is the type of query. It can be a scoring query, a data query, or both
+	queryType queryType
+	// selectedCodeId is the code id that actually gets executed. It is the code id of the specific query
+	// that is run, traversed down the variants if necessary. We keep track of this because we need to connect
+	// controls to the specific query that is run so they are not influenced by impacts
+	selectedCodeId string
+}
+
+func (n *rpBuilderGenericQueryNode) getId() string {
+	return n.queryMrn
+}
+
+func (n *rpBuilderGenericQueryNode) isPrunable() bool {
+	return true
+}
+
+func (n *rpBuilderGenericQueryNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	reportingJobUUID := data.relativeChecksum(n.queryMrn)
+
+	// Because a query can be both a scoring query and a data query, UNSPECIFIED is used
+	// for the reporting job type. We need to get rid of the specific types for check and
+	// data query and have something that can be both
+	addReportingJob(n.queryMrn, true, reportingJobUUID, ReportingJob_UNSPECIFIED, rp)
+
+	// Add scoring queries to the reporting queries section
+	if n.queryType == queryTypeScoring || n.queryType == queryTypeBoth {
+		codeIdReportingJobUUID := data.relativeChecksum(n.selectedCodeId)
+
+		if _, ok := rp.CollectorJob.ReportingQueries[n.selectedCodeId]; !ok {
+			rp.CollectorJob.ReportingQueries[n.selectedCodeId] = &StringArray{}
+		}
+
+		// Add the reporting job to the reporting queries if it does not already exist
+		if !slices.Contains(rp.CollectorJob.ReportingQueries[n.selectedCodeId].Items, codeIdReportingJobUUID) {
+			rp.CollectorJob.ReportingQueries[n.selectedCodeId].Items = append(rp.CollectorJob.ReportingQueries[n.selectedCodeId].Items, codeIdReportingJobUUID)
+		}
+	}
+
+	return nil
+}
+
+// rpBuilderExecutionQueryNode is a node that represents a executable query in the graph. It will add a reporting job to the resolved policy,
+// and add the compiled query to the execution job, and connect the datapoints to the reporting job.
+// This node is a leaf. Anything connected to an executable query will not be pruned.
+// This node is represented by a code id in the reporting jobs. We do not apply impact at this point so
+// any scores will be either 0 or 100
+type rpBuilderExecutionQueryNode struct {
+	query *explorer.Mquery
+}
+
+func (n *rpBuilderExecutionQueryNode) getId() string {
+	return n.query.CodeId
+}
+
+func (n *rpBuilderExecutionQueryNode) isPrunable() bool {
+	// Executable queries are leaf nodes in the graph. They cannot be pruned
+	// If something is connected to an executable query, we want to keep it around
+	return false
+}
+
+func (n *rpBuilderExecutionQueryNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	// Compile the properties
+	propTypes, propToChecksums, err := compileProps(n.query, rp, data)
+	if err != nil {
+		return err
+	}
+	// Add the compiled query to the execution job. This also collects the datapoints into the collector job
+	executionQuery, _, err := mquery2executionQuery(n.query, propTypes, propToChecksums, rp.CollectorJob, false, data.compilerConf)
+	if err != nil {
+		return err
+	}
+	rp.ExecutionJob.Queries[n.query.CodeId] = executionQuery
+
+	codeIdReportingJobUUID := data.relativeChecksum(n.query.CodeId)
+
+	// Create a reporting job for the code id
+	codeIdReportingJob := addReportingJob(n.query.CodeId, false, codeIdReportingJobUUID, ReportingJob_UNSPECIFIED, rp)
+	// Connect the datapoints to the reporting job
+	err = connectDatapointsToReportingJob(executionQuery, codeIdReportingJob, rp.CollectorJob.Datapoints)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
+// rpBuilderFrameworkNode is a node that represents a framework in the graph. It will add a reporting job to the resolved policy
+type rpBuilderFrameworkNode struct {
+	frameworkMrn string
+}
+
+func (n *rpBuilderFrameworkNode) getId() string {
+	return n.frameworkMrn
+}
+
+func (n *rpBuilderFrameworkNode) isPrunable() bool {
+	return true
+}
+
+func (n *rpBuilderFrameworkNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	addReportingJob(n.frameworkMrn, true, data.relativeChecksum(n.frameworkMrn), ReportingJob_FRAMEWORK, rp)
+	return nil
+}
+
+// rpBuilderControlNode is a node that represents a control in the graph. It will add a reporting job to the resolved policy
+type rpBuilderControlNode struct {
+	controlMrn string
+}
+
+func (n *rpBuilderControlNode) getId() string {
+	return n.controlMrn
+}
+
+func (n *rpBuilderControlNode) isPrunable() bool {
+	return true
+}
+
+func (n *rpBuilderControlNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	addReportingJob(n.controlMrn, true, data.relativeChecksum(n.controlMrn), ReportingJob_CONTROL, rp)
+	return nil
+}
+
+// rpBuilderRiskFactorNode is a node that represents a risk factor in the graph. It will add a reporting job to the resolved policy,
+// and fill out the RiskFactors and RiskMrns sections in the collector job
+type rpBuilderRiskFactorNode struct {
+	riskFactor      *RiskFactor
+	magnitude       *RiskMagnitude
+	selectedCodeIds []string
+}
+
+func (n *rpBuilderRiskFactorNode) getId() string {
+	return n.riskFactor.Mrn
+}
+
+func (n *rpBuilderRiskFactorNode) isPrunable() bool {
+	return true
+}
+
+func (n *rpBuilderRiskFactorNode) build(rp *ResolvedPolicy, data *rpBuilderData) error {
+	risk := n.riskFactor
+	if n.magnitude != nil {
+		risk.Magnitude = n.magnitude
+	}
+	rp.CollectorJob.RiskFactors[risk.Mrn] = &RiskFactor{
+		Scope:                   risk.Scope,
+		Magnitude:               risk.Magnitude,
+		Resources:               risk.Resources,
+		DeprecatedV11Magnitude:  risk.Magnitude.GetValue(),
+		DeprecatedV11IsAbsolute: risk.Magnitude.GetIsToxic(),
+	}
+	reportingJobUUID := data.relativeChecksum(risk.Mrn)
+	addReportingJob(risk.Mrn, true, reportingJobUUID, ReportingJob_RISK_FACTOR, rp)
+
+	for _, codeId := range n.selectedCodeIds {
+		uuid := data.relativeChecksum(codeId)
+		if _, ok := rp.CollectorJob.RiskMrns[uuid]; !ok {
+			rp.CollectorJob.RiskMrns[uuid] = &StringArray{
+				Items: []string{},
+			}
+		}
+		rp.CollectorJob.RiskMrns[uuid].Items = append(rp.CollectorJob.RiskMrns[uuid].Items, risk.Mrn)
+	}
+	return nil
+}
+
+func (b *resolvedPolicyBuilder) addEdge(from, to string, impact *explorer.Impact) {
+	if _, ok := b.reportsToEdges[from]; !ok {
+		b.reportsToEdges[from] = make([]string, 0, 1)
+	}
+	for _, e := range b.reportsToEdges[from] {
+		// If the edge already exists, don't add it
+		if e == to {
+			return
+		}
+	}
+	b.reportsToEdges[from] = append(b.reportsToEdges[from], to)
+
+	if _, ok := b.reportsFromEdges[to]; !ok {
+		b.reportsFromEdges[to] = make([]edgeImpact, 0, 1)
+	}
+
+	b.reportsFromEdges[to] = append(b.reportsFromEdges[to], edgeImpact{edge: from, impact: impact})
+}
+
+func (b *resolvedPolicyBuilder) addNode(node rpBuilderNode) {
+	b.nodes[node.getId()] = node
+}
+
+type queryType int
+
+const (
+	queryTypeScoring queryType = iota
+	queryTypeData
+	queryTypeBoth
+)
+
+// collectQueryTypes collects the query types for each query in the policy. A query can be a scoring query, a data query,
+// or both. We analyze all matching policies to determine the query type. If a query shows up in checks, it is a scoring query.
+// If it shows up in data queries, it is a data query. If it shows up in both, it is set to both.
+func (b *resolvedPolicyBuilder) collectQueryTypes(policyMrn string, acc map[string]queryType) {
+	policy := b.bundleMap.Policies[policyMrn]
+	if policy == nil {
+		return
+	}
+
+	var accumulate func(queryMrn string, t queryType)
+	accumulate = func(queryMrn string, t queryType) {
+		if existing, ok := acc[queryMrn]; !ok {
+			// If it doesn't exist, add it
+			acc[queryMrn] = t
+		} else {
+			if existing != t && existing != queryTypeBoth {
+				// If it exists, but is different, set it to both
+				acc[queryMrn] = queryTypeBoth
+			}
+		}
+		q := b.bundleMap.Queries[queryMrn]
+		if q == nil {
+			return
+		}
+
+		for _, v := range q.Variants {
+			accumulate(v.Mrn, t)
+		}
+	}
+
+	for _, g := range policy.Groups {
+		if !b.isGroupMatching(g) {
+			// skip groups that don't match
+			continue
+		}
+
+		for _, c := range g.Checks {
+			accumulate(c.Mrn, queryTypeScoring)
+		}
+
+		for _, q := range g.Queries {
+			accumulate(q.Mrn, queryTypeData)
+		}
+
+		for _, pRef := range g.Policies {
+			// recursively collect query types from referenced policies
+			b.collectQueryTypes(pRef.Mrn, acc)
+		}
+	}
+
+	// queries in risk factors are checks
+	for _, r := range policy.RiskFactors {
+		for _, c := range r.Checks {
+			accumulate(c.Mrn, queryTypeScoring)
+		}
+	}
+}
+
+func (b *resolvedPolicyBuilder) gatherGlobalInfoFromFramework(framework *Framework) {
+	actions := b.actionOverrides
+
+	if framework == nil {
+		return
+	}
+
+	for _, fRef := range framework.Dependencies {
+		f := b.bundleMap.Frameworks[fRef.Mrn]
+		if f == nil {
+			continue
+		}
+		b.gatherGlobalInfoFromFramework(f)
+	}
+
+	for _, g := range framework.Groups {
+		if !b.isGroupMatching(g) {
+			continue
+		}
+
+		for _, c := range g.Controls {
+			action := normalizeAction(g.Type, c.Action, nil)
+			if action != explorer.Action_UNSPECIFIED && action != explorer.Action_MODIFY {
+				actions[c.Mrn] = action
+			}
+		}
+	}
+}
+
+// gatherGlobalInfoFromPolicy gathers the action, impact, scoring system, and risk magnitude overrides from the policy. We
+// apply this information in a second pass when building the nodes
+func (b *resolvedPolicyBuilder) gatherGlobalInfoFromPolicy(policy *Policy) {
+	actions := b.actionOverrides
+	impacts := b.impactOverrides
+	scoringSystems := b.policyScoringSystems
+	riskMagnitudes := b.riskMagnitudes
+
+	for _, g := range policy.Groups {
+		if !b.isGroupMatching(g) {
+			continue
+		}
+		for _, pRef := range g.Policies {
+			p := b.bundleMap.Policies[pRef.Mrn]
+
+			b.gatherGlobalInfoFromPolicy(p)
+
+			action := normalizeAction(g.Type, pRef.Action, pRef.Impact)
+			if action != explorer.Action_UNSPECIFIED && action != explorer.Action_MODIFY {
+				actions[pRef.Mrn] = action
+			}
+
+			if pRef.Impact != nil {
+				impacts[pRef.Mrn] = pRef.Impact
+			}
+			scoringSystem := pRef.ScoringSystem
+
+			if scoringSystem != explorer.ScoringSystem_SCORING_UNSPECIFIED {
+				scoringSystems[pRef.Mrn] = pRef.ScoringSystem
+			} else {
+				if p, ok := b.bundleMap.Policies[pRef.Mrn]; ok {
+					scoringSystems[pRef.Mrn] = p.ScoringSystem
+				}
+			}
+		}
+
+		// We always want to select the worst impact that we find
+		getWorstImpact := func(impact1 *explorer.Impact, impact2 *explorer.Impact) *explorer.Impact {
+			if impact1 == nil {
+				return impact2
+			}
+			if impact2 == nil {
+				return impact1
+			}
+
+			if impact1.Value.GetValue() > impact2.Value.GetValue() {
+				return impact1
+			}
+			return impact2
+		}
+
+		for _, c := range g.Checks {
+			impact := c.Impact
+			if qBundle, ok := b.bundleMap.Queries[c.Mrn]; ok {
+				// Check the impact defined on the query
+				impact = getWorstImpact(impact, qBundle.Impact)
+			}
+
+			impact = getWorstImpact(impact, impacts[c.Mrn])
+
+			action := normalizeAction(g.Type, c.Action, impact)
+			if action != explorer.Action_UNSPECIFIED && action != explorer.Action_MODIFY {
+				actions[c.Mrn] = action
+			}
+
+			if impact != nil {
+				impacts[c.Mrn] = impact
+			}
+		}
+
+		for _, q := range g.Queries {
+			if q.Action != explorer.Action_UNSPECIFIED {
+				action := normalizeAction(g.Type, q.Action, q.Impact)
+				if action != explorer.Action_UNSPECIFIED && action != explorer.Action_MODIFY {
+					actions[q.Mrn] = action
+				}
+			}
+		}
+	}
+
+	for _, r := range policy.RiskFactors {
+		if r.Magnitude != nil {
+			riskMagnitudes[r.Mrn] = r.Magnitude
+		}
+
+		if r.Action != explorer.Action_UNSPECIFIED && r.Action != explorer.Action_MODIFY {
+			actions[r.Mrn] = r.Action
+		}
+	}
+}
+
+func canRun(action explorer.Action) bool {
+	return !(action == explorer.Action_DEACTIVATE || action == explorer.Action_OUT_OF_SCOPE)
+}
+
+type group interface {
+	GetReviewStatus() ReviewStatus
+	GetEndDate() int64
+}
+
+type groupWithFilters interface {
+	group
+	GetFilters() *explorer.Filters
+}
+
+// isGroupMatching checks if the policy group is matching. A policy group is matching if it is not rejected,
+// and it is not expired. If it has filters, it must have at least one filter that matches the asset filters
+func (b *resolvedPolicyBuilder) isGroupMatching(group group) bool {
+	if group.GetReviewStatus() == ReviewStatus_REJECTED {
+		return false
+	}
+
+	if group.GetEndDate() != 0 {
+		endDate := time.Unix(group.GetEndDate(), 0)
+		if endDate.Before(b.now) {
+			return false
+		}
+	}
+
+	if groupWithFilters, ok := group.(groupWithFilters); ok {
+		if groupWithFilters.GetFilters() == nil || len(groupWithFilters.GetFilters().Items) == 0 {
+			return true
+		}
+
+		for _, filter := range groupWithFilters.GetFilters().Items {
+			if _, ok := b.assetFilters[filter.CodeId]; ok {
+				return true
+			}
+		}
+	} else {
+		return true
+	}
+
+	return false
+}
+
+// addPolicy recurses a policy and adds all the nodes and edges to the graph. It will add the policy, its dependent policies, checks, and queries
+func (b *resolvedPolicyBuilder) addPolicy(policy *Policy) bool {
+	action := b.actionOverrides[policy.Mrn]
+
+	// Check if we can run this policy. If not, then we do not add it to the graph
+	if !canRun(action) {
+		return false
+	}
+
+	if !b.anyFilterMatches(policy.ComputedFilters) {
+		return false
+	}
+
+	b.propsCache.Add(policy.Props...)
+
+	// Add node for policy
+	scoringSystem := b.policyScoringSystems[policy.Mrn]
+	b.addNode(&rpBuilderPolicyNode{policy: policy, scoringSystem: scoringSystem, isRoot: b.bundleMrn == policy.Mrn})
+	hasMatchingGroup := false
+	for _, g := range policy.Groups {
+		if !b.isGroupMatching(g) {
+			continue
+		}
+		hasMatchingGroup = true
+		for _, pRef := range g.Policies {
+			p := b.bundleMap.Policies[pRef.Mrn]
+			if b.addPolicy(p) {
+				var impact *explorer.Impact
+				if pRefAction, ok := b.actionOverrides[pRef.Mrn]; ok && pRefAction == explorer.Action_IGNORE {
+					impact = &explorer.Impact{
+						Scoring: explorer.ScoringSystem_IGNORE_SCORE,
+					}
+				} else if i, ok := b.impactOverrides[pRef.Mrn]; ok {
+					impact = i
+				}
+				b.addEdge(pRef.Mrn, policy.Mrn, impact)
+			}
+		}
+
+		for _, c := range g.Checks {
+			// Check the action. If its an override, we don't need to add the check
+			// because it will get included in a policy that wants it run.
+			// This will prevent the check from being connected to the policy that
+			// overrides its action
+			if isOverride(c.Action, g.Type) {
+				b.propsCache.Add(c.Props...)
+				continue
+			}
+
+			c, ok := b.bundleMap.Queries[c.Mrn]
+			if !ok {
+				log.Warn().Str("mrn", c.Mrn).Msg("check not found in bundle")
+				continue
+			}
+
+			if _, ok := b.addQuery(c); ok {
+				action := b.actionOverrides[c.Mrn]
+				var impact *explorer.Impact
+				if action == explorer.Action_IGNORE {
+					impact = &explorer.Impact{
+						Scoring: explorer.ScoringSystem_IGNORE_SCORE,
+					}
+				}
+				b.addEdge(c.Mrn, policy.Mrn, impact)
+			}
+		}
+
+		for _, q := range g.Queries {
+			// Check the action. If its an override, we don't need to add the query
+			// because it will get included in a policy that wants it run.
+			// This will prevent the query from being connected to the policy that
+			// overrides its action
+			if isOverride(q.Action, g.Type) {
+				b.propsCache.Add(q.Props...)
+				continue
+			}
+
+			q, ok := b.bundleMap.Queries[q.Mrn]
+			if !ok {
+				log.Warn().Str("mrn", q.Mrn).Msg("query not found in bundle")
+				continue
+			}
+
+			if _, ok := b.addQuery(q); ok {
+				b.addEdge(q.Mrn, policy.Mrn, &explorer.Impact{
+					Scoring: explorer.ScoringSystem_IGNORE_SCORE,
+				})
+			}
+		}
+	}
+
+	hasMatchingRiskFactor := false
+	for _, r := range policy.RiskFactors {
+		if len(r.Checks) == 0 || isOverride(r.Action, GroupType_UNCATEGORIZED) {
+			continue
+		}
+
+		added, err := b.addRiskFactor(r)
+		if err != nil {
+			log.Error().Err(err).Str("mrn", r.Mrn).Msg("error adding risk factor")
+			continue
+		}
+		if added {
+			b.addEdge(r.Mrn, policy.Mrn, &explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+			hasMatchingRiskFactor = true
+		}
+	}
+
+	return hasMatchingGroup || hasMatchingRiskFactor
+}
+
+// addQuery adds a query to the graph. It will add the query, its variants, and connect the query to the variants
+func (b *resolvedPolicyBuilder) addQuery(query *explorer.Mquery) (string, bool) {
+	action := b.actionOverrides[query.Mrn]
+	impact := b.impactOverrides[query.Mrn]
+	queryType := b.queryTypes[query.Mrn]
+
+	if !canRun(action) {
+		return "", false
+	}
+
+	if len(query.Variants) != 0 {
+		// If we have variants, we need to find the first matching variant.
+		// We will also recursively find the code id of the query that will
+		// be run
+		var matchingVariant *explorer.Mquery
+		var selectedCodeId string
+		for _, v := range query.Variants {
+			q, ok := b.bundleMap.Queries[v.Mrn]
+			if !ok {
+				log.Warn().Str("mrn", v.Mrn).Msg("variant not found in bundle")
+				continue
+			}
+			if codeId, added := b.addQuery(q); added {
+				// The first matching variant is selected
+				matchingVariant = q
+				selectedCodeId = codeId
+				break
+			}
+		}
+
+		if matchingVariant == nil {
+			return "", false
+		}
+
+		b.propsCache.Add(query.Props...)
+		b.propsCache.Add(matchingVariant.Props...)
+
+		// Add node for query
+		b.addNode(&rpBuilderGenericQueryNode{queryMrn: query.Mrn, selectedCodeId: selectedCodeId, queryType: queryType})
+
+		// Add edge from variant to query
+		b.addEdge(matchingVariant.Mrn, query.Mrn, impact)
+
+		return selectedCodeId, true
+	} else {
+		if !b.anyFilterMatches(query.Filters) {
+			return "", false
+		}
+
+		b.propsCache.Add(query.Props...)
+
+		// Add node for execution query
+		b.addNode(&rpBuilderExecutionQueryNode{query: query})
+		// Add node for query
+		b.addNode(&rpBuilderGenericQueryNode{queryMrn: query.Mrn, selectedCodeId: query.CodeId, queryType: queryType})
+
+		// Add edge from execution query to query
+		b.addEdge(query.CodeId, query.Mrn, impact)
+
+		return query.CodeId, true
+	}
+}
+
+// addRiskFactor adds a risk factor to the graph. It will add the risk factor, its checks, and connect the checks to the risk factor
+func (b *resolvedPolicyBuilder) addRiskFactor(riskFactor *RiskFactor) (bool, error) {
+	action := b.actionOverrides[riskFactor.Mrn]
+	if !canRun(action) {
+		return false, nil
+	}
+
+	if !b.anyFilterMatches(riskFactor.Filters) {
+		return false, nil
+	}
+
+	selectedCodeIds := make([]string, 0, len(riskFactor.Checks))
+	for _, c := range riskFactor.Checks {
+		if len(c.Variants) != 0 {
+			return false, fmt.Errorf("risk factor checks cannot have variants")
+		}
+		if !b.anyFilterMatches(c.Filters) {
+			continue
+		}
+
+		b.propsCache.Add(c.Props...)
+
+		// Add node for execution query
+		b.addNode(&rpBuilderExecutionQueryNode{query: c})
+		// TODO: we should just score the risk factor normally, I don't know why we ignore the score
+		b.addEdge(c.CodeId, riskFactor.Mrn, &explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+
+		selectedCodeIds = append(selectedCodeIds, c.CodeId)
+
+		// TODO: we cannot use addQuery here because of the way cnspec tries to filter out
+		// sending scores for queries that are risk factors. This code, which is in collector.go
+		// needs to be refactored in such a way that it is natively integrated into the graph
+		// the does the processing of the scores. The current implementation has a problem if
+		// we have a child job on the risk factor that is mrn of the query.
+		// if selectedCodeId, ok := b.addQuery(c); ok {
+		// 	selectedCodeIds = append(selectedCodeIds, selectedCodeId)
+		// 	b.addEdge(c.Mrn, riskFactor.Mrn, &explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+		// }
+	}
+
+	if len(selectedCodeIds) == 0 {
+		return false, nil
+	}
+
+	b.addNode(&rpBuilderRiskFactorNode{riskFactor: riskFactor, magnitude: b.riskMagnitudes[riskFactor.Mrn], selectedCodeIds: selectedCodeIds})
+
+	return true, nil
+}
+
+func (b *resolvedPolicyBuilder) anyFilterMatches(f *explorer.Filters) bool {
+	return f.Supports(b.assetFilters)
+}
+
+// addFramework adds a framework to the graph. It will add the framework, its dependent frameworks, its controls, and connect
+// the controls to the framework
+func (b *resolvedPolicyBuilder) addFramework(framework *Framework) bool {
+	action := b.actionOverrides[framework.Mrn]
+	if !canRun(action) {
+		return false
+	}
+
+	// Create a node for the framework, but only if its a valid framework mrn
+	// Otherwise, we have the asset / space policies which we will connect
+	// to. We need to do this because we cannot have a space framework and space
+	// policy reporting job because they would have the same qr id.
+	// If the node already exists, its represented by the asset or space policy
+	// and is not a valid framework mrn
+	var impact *explorer.Impact
+	if _, ok := b.nodes[framework.Mrn]; !ok {
+		b.addNode(&rpBuilderFrameworkNode{frameworkMrn: framework.Mrn})
+	} else {
+		impact = &explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE}
+	}
+
+	for _, fmap := range framework.FrameworkMaps {
+		for _, control := range fmap.Controls {
+			if b.addControl(control) {
+				b.addEdge(control.Mrn, fmap.FrameworkOwner.Mrn, b.actionToImpact(control.Mrn))
+			}
+		}
+	}
+
+	for _, fdep := range framework.Dependencies {
+		f, ok := b.bundleMap.Frameworks[fdep.Mrn]
+		if !ok {
+			log.Warn().Str("mrn", fdep.Mrn).Msg("framework not found in bundle")
+			continue
+		}
+		if b.addFramework(f) {
+			b.addEdge(fdep.Mrn, framework.Mrn, impact)
+		}
+	}
+
+	return true
+}
+
+// addControl adds a control to the graph and connect policies, controls, checks, and queries to the control
+func (b *resolvedPolicyBuilder) addControl(control *ControlMap) bool {
+	action := b.actionOverrides[control.Mrn]
+	if !canRun(action) {
+		return false
+	}
+
+	hasChild := false
+
+	for _, q := range control.Checks {
+		if _, ok := b.nodes[q.Mrn]; ok {
+			n := b.nodes[q.Mrn]
+			if n == nil {
+				continue
+			}
+			qNode, ok := n.(*rpBuilderGenericQueryNode)
+			if ok {
+				b.addEdge(qNode.selectedCodeId, control.Mrn, b.actionToImpact(q.Mrn))
+				hasChild = true
+			}
+		}
+	}
+
+	for _, q := range control.Queries {
+		if _, ok := b.nodes[q.Mrn]; ok {
+			n := b.nodes[q.Mrn]
+			if n == nil {
+				continue
+			}
+			qNode, ok := n.(*rpBuilderGenericQueryNode)
+			if ok {
+				b.addEdge(qNode.selectedCodeId, control.Mrn, nil)
+				hasChild = true
+			}
+		}
+	}
+
+	for _, p := range control.Policies {
+		if _, ok := b.nodes[p.Mrn]; ok {
+			// Add the edge from the control to the policy
+			b.addEdge(p.Mrn, control.Mrn, b.actionToImpact(p.Mrn))
+			hasChild = true
+		}
+	}
+
+	for _, c := range control.Controls {
+		// We will just assume that the control is in the graph
+		// If its not, it will get filtered out later when we build
+		// the resolved policy
+		// Doing this so we don't need to topologically sort the dependency
+		// tree for the controls
+		b.addEdge(c.Mrn, control.Mrn, b.actionToImpact(c.Mrn))
+		hasChild = true
+	}
+
+	if hasChild {
+		// Add node for control
+		b.addNode(&rpBuilderControlNode{controlMrn: control.Mrn})
+	}
+
+	return true
+}
+
+func (b *resolvedPolicyBuilder) actionToImpact(mrn string) *explorer.Impact {
+	action := b.actionOverrides[mrn]
+	if action == explorer.Action_IGNORE {
+		return &explorer.Impact{
+			Scoring: explorer.ScoringSystem_IGNORE_SCORE,
+		}
+	}
+	return nil
+}
+
+func addReportingJob(qrId string, qrIdIsMrn bool, uuid string, typ ReportingJob_Type, rp *ResolvedPolicy) *ReportingJob {
+	if _, ok := rp.CollectorJob.ReportingJobs[uuid]; !ok {
+		rp.CollectorJob.ReportingJobs[uuid] = &ReportingJob{
+			QrId:       qrId,
+			Uuid:       uuid,
+			ChildJobs:  map[string]*explorer.Impact{},
+			Datapoints: map[string]bool{},
+			Type:       typ,
+		}
+		if qrIdIsMrn {
+			rp.CollectorJob.ReportingJobs[uuid].Mrns = []string{qrId}
+		}
+	}
+	return rp.CollectorJob.ReportingJobs[uuid]
+}
+
+func compileProps(query *explorer.Mquery, rp *ResolvedPolicy, data *rpBuilderData) (map[string]*llx.Primitive, map[string]string, error) {
+	var propTypes map[string]*llx.Primitive
+	var propToChecksums map[string]string
+	if len(query.Props) != 0 {
+		propTypes = make(map[string]*llx.Primitive, len(query.Props))
+		propToChecksums = make(map[string]string, len(query.Props))
+		for j := range query.Props {
+			prop := query.Props[j]
+
+			// we only get this if there is an override higher up in the policy
+			override, name, _ := data.propsCache.Get(prop.Mrn)
+			if override != nil {
+				prop = override
+			}
+			if name == "" {
+				var err error
+				name, err = mrn.GetResource(prop.Mrn, MRN_RESOURCE_QUERY)
+				if err != nil {
+					return nil, nil, errors.New("failed to get property name")
+				}
+			}
+
+			executionQuery, dataChecksum, err := mquery2executionQuery(prop, nil, map[string]string{}, rp.CollectorJob, false, data.compilerConf)
+			if err != nil {
+				return nil, nil, errors.New("resolver> failed to compile query for MRN " + prop.Mrn + ": " + err.Error())
+			}
+			if dataChecksum == "" {
+				return nil, nil, errors.New("property returns too many value, cannot determine entrypoint checksum: '" + prop.Mql + "'")
+			}
+			rp.ExecutionJob.Queries[prop.CodeId] = executionQuery
+
+			propTypes[name] = &llx.Primitive{Type: prop.Type}
+			propToChecksums[name] = dataChecksum
+		}
+	}
+	return propTypes, propToChecksums, nil
+}
+
+// connectReportingJobNotifies adds the notifies and child jobs links in the reporting jobs
+func connectReportingJobNotifies(child *ReportingJob, parent *ReportingJob, impact *explorer.Impact) {
+	for _, n := range child.Notify {
+		if n == parent.Uuid {
+			fmt.Println("already connected")
+		}
+	}
+	child.Notify = append(child.Notify, parent.Uuid)
+	parent.ChildJobs[child.Uuid] = impact
+}
+
+// normalizeAction normalizes the action based on the group type and impact. We need to do this because
+// we've had different ways of representing actions in the past and we need to normalize them to the current
+func normalizeAction(groupType GroupType, action explorer.Action, impact *explorer.Impact) explorer.Action {
+	switch groupType {
+	case GroupType_DISABLE:
+		return explorer.Action_DEACTIVATE
+	case GroupType_OUT_OF_SCOPE:
+		return explorer.Action_OUT_OF_SCOPE
+	case GroupType_IGNORED:
+		return explorer.Action_IGNORE
+	default:
+		if impact != nil && impact.Scoring == explorer.ScoringSystem_IGNORE_SCORE {
+			return explorer.Action_IGNORE
+		}
+		return action
+	}
+}
+
+func isOverride(action explorer.Action, groupType GroupType) bool {
+	return action != explorer.Action_UNSPECIFIED ||
+		groupType == GroupType_DISABLE ||
+		groupType == GroupType_OUT_OF_SCOPE ||
+		groupType == GroupType_IGNORED
+}
diff --git a/policy/resolver.go b/policy/resolver.go
index bc12c8a3..2d4ebb66 100644
--- a/policy/resolver.go
+++ b/policy/resolver.go
@@ -32,7 +32,8 @@ const (
 	// This is used to change the checksum of the resolved policy when we want it to be recalculated
 	// This can be updated, e.g., when we change how the report jobs are generated
 	// A change of this string will force an update of all the stored resolved policies
-	RESOLVER_VERSION = "v2024-08-29"
+	RESOLVER_VERSION    = "v2024-08-29"
+	RESOLVER_VERSION_NG = "v2024-11-11"
 )
 
 type AssetMutation struct {
@@ -451,6 +452,16 @@ func (s *LocalServices) resolve(ctx context.Context, policyMrn string, assetFilt
 	return nil, errors.New("concurrent policy resolve")
 }
 
+type nextGenResolverFeature struct{}
+
+func WithNextGenResolver(context.Context) context.Context {
+	return context.WithValue(context.Background(), nextGenResolverFeature{}, true)
+}
+
+func IsNextGenResolver(ctx context.Context) bool {
+	return ctx.Value(nextGenResolverFeature{}) != nil
+}
+
 func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetFilters []*explorer.Mquery) (*ResolvedPolicy, error) {
 	logCtx := logger.FromContext(ctx)
 	now := time.Now()
@@ -477,11 +488,12 @@ func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetF
 	if err != nil {
 		return nil, err
 	}
+
 	bundleMap := bundle.ToMap()
 
 	frameworkObj := bundleMap.Frameworks[bundleMrn]
 	policyObj := bundleMap.Policies[bundleMrn]
-	resolvedPolicyExecutionChecksum := BundleExecutionChecksum(policyObj, frameworkObj)
+	resolvedPolicyExecutionChecksum := BundleExecutionChecksum(ctx, policyObj, frameworkObj)
 
 	matchingFilters, err := MatchingAssetFilters(bundleMrn, assetFilters, policyObj)
 	if err != nil {
@@ -491,6 +503,20 @@ func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetF
 		return nil, explorer.NewAssetMatchError(bundleMrn, "policies", "no-matching-policy", assetFilters, policyObj.ComputedFilters)
 	}
 
+	if IsNextGenResolver(ctx) {
+		resolvedPolicy, err := buildResolvedPolicy(ctx, bundleMrn, bundle, matchingFilters, time.Now(), conf)
+		if err != nil {
+			return nil, err
+		}
+
+		err = s.DataLake.SetResolvedPolicy(ctx, bundleMrn, resolvedPolicy, V2Code, false)
+		if err != nil {
+			return nil, err
+		}
+
+		return resolvedPolicy, nil
+	}
+
 	assetFiltersMap := make(map[string]struct{}, len(matchingFilters))
 	for i := range matchingFilters {
 		assetFiltersMap[matchingFilters[i].CodeId] = struct{}{}
@@ -519,7 +545,7 @@ func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetF
 		Msg("resolver> phase 1: no cached result, resolve the bundle now")
 
 	cache := &resolverCache{
-		baseChecksum:          BundleExecutionChecksum(policyObj, frameworkObj),
+		baseChecksum:          BundleExecutionChecksum(ctx, policyObj, frameworkObj),
 		assetFiltersChecksum:  assetFiltersChecksum,
 		assetFilters:          assetFiltersMap,
 		executionQueries:      map[string]*ExecutionQuery{},
@@ -655,7 +681,7 @@ func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetF
 		Msg("resolver> phase 5: resolve controls [ok]")
 
 	// phase 6: refresh all checksums
-	s.refreshChecksums(executionJob, collectorJob)
+	refreshChecksums(executionJob, collectorJob)
 
 	// the final phases are done in the DataLake
 	for _, rj := range collectorJob.ReportingJobs {
@@ -679,7 +705,7 @@ func (s *LocalServices) tryResolve(ctx context.Context, bundleMrn string, assetF
 	return &resolvedPolicy, nil
 }
 
-func (s *LocalServices) refreshChecksums(executionJob *ExecutionJob, collectorJob *CollectorJob) {
+func refreshChecksums(executionJob *ExecutionJob, collectorJob *CollectorJob) {
 	// execution job
 	{
 		queryKeys := sortx.Keys(executionJob.Queries)
diff --git a/policy/resolver_test.go b/policy/resolver_test.go
index 2b45811c..e441db81 100644
--- a/policy/resolver_test.go
+++ b/policy/resolver_test.go
@@ -71,6 +71,10 @@ func queryMrn(uid string) string {
 	return "//test.sth/queries/" + uid
 }
 
+func riskFactorMrn(uid string) string {
+	return "//test.sth/risks/" + uid
+}
+
 func TestResolve_EmptyPolicy(t *testing.T) {
 	b := parseBundle(t, `
 owner_mrn: //test.sth
diff --git a/policy/resolver_v2_test.go b/policy/resolver_v2_test.go
new file mode 100644
index 00000000..fb7b59d2
--- /dev/null
+++ b/policy/resolver_v2_test.go
@@ -0,0 +1,2010 @@
+// Copyright (c) Mondoo, Inc.
+// SPDX-License-Identifier: BUSL-1.1
+
+package policy_test
+
+import (
+	"context"
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/require"
+	"go.mondoo.com/cnquery/v11/explorer"
+	"go.mondoo.com/cnquery/v11/mqlc"
+	"go.mondoo.com/cnquery/v11/providers"
+	"go.mondoo.com/cnspec/v11/internal/datalakes/inmemory"
+	"go.mondoo.com/cnspec/v11/policy"
+)
+
+func collectQueriesFromRiskFactors(p *policy.Policy, query map[string]*explorer.Mquery) {
+	for _, rf := range p.RiskFactors {
+		for _, c := range rf.Checks {
+			query[c.Mrn] = c
+		}
+	}
+}
+
+func newResolvedPolicyTester(bundle *policy.Bundle, conf mqlc.CompilerConfig) *resolvedPolicyTester {
+	m := bundle.ToMap()
+	for _, p := range m.Policies {
+		collectQueriesFromRiskFactors(p, m.Queries)
+	}
+
+	return &resolvedPolicyTester{
+		bundleMap: m,
+		items:     []resolvedPolicyTesterItem{},
+		conf:      conf,
+	}
+}
+
+type resolvedPolicyTesterItem interface {
+	testIt(t *testing.T, resolvedPolicy *policy.ResolvedPolicy)
+}
+
+type resolvedPolicyTester struct {
+	bundleMap *policy.PolicyBundleMap
+	items     []resolvedPolicyTesterItem
+	conf      mqlc.CompilerConfig
+}
+
+func (r *resolvedPolicyTester) doTest(t *testing.T, rp *policy.ResolvedPolicy) {
+	for _, item := range r.items {
+		item.testIt(t, rp)
+	}
+}
+
+func (r *resolvedPolicyTester) ExecutesQuery(mrn string) *resolvedPolicyTesterExecutesQueryBuilder {
+	item := &resolvedPolicyTesterExecutesQueryBuilder{tester: r, mrn: mrn}
+	r.items = append(r.items, item)
+	return item
+}
+
+func (r *resolvedPolicyTester) DoesNotExecutesQuery(mrn string) {
+	item := &resolvedPolicyTesterExecutesQueryBuilder{tester: r, mrn: mrn, doesNotExecute: true}
+	r.items = append(r.items, item)
+}
+
+type resolvedPolicyTesterExecutesQueryBuilder struct {
+	tester         *resolvedPolicyTester
+	mrn            string
+	datapoints     *[]string
+	props          *map[string]string
+	doesNotExecute bool
+}
+
+func (r *resolvedPolicyTesterExecutesQueryBuilder) WithProps(props map[string]string) *resolvedPolicyTesterExecutesQueryBuilder {
+	r.props = &props
+	return r
+}
+
+func (r *resolvedPolicyTesterExecutesQueryBuilder) testIt(t *testing.T, rp *policy.ResolvedPolicy) {
+	q := r.tester.bundleMap.Queries[r.mrn]
+	require.NotNilf(t, q, "query not found in bundle: %s", r.mrn)
+	codeId := q.CodeId
+	require.NotEmptyf(t, codeId, "query %s doesn't have code id", r.mrn)
+
+	eq := rp.ExecutionJob.Queries[codeId]
+	if r.doesNotExecute {
+		require.Nil(t, eq, "query %s should not be executed", r.mrn)
+		return
+	}
+	require.NotNilf(t, eq, "query %s not found in ExecutionJob", r.mrn)
+
+	if r.datapoints != nil {
+		require.ElementsMatchf(t, *r.datapoints, eq.Datapoints, "datapoints mismatch for query %q", r.mrn)
+	}
+
+	if r.props != nil {
+		require.Lenf(t, eq.Properties, len(*r.props), "properties mismatch for query %q", r.mrn)
+		for propName, mql := range *r.props {
+			// Compile the property
+			codeBundle, err := mqlc.Compile(mql, nil, r.tester.conf)
+			require.NoErrorf(t, err, "failed to compile property %q for query %q", propName, r.mrn)
+			propCodeId := codeBundle.CodeV2.Id
+			require.NotEmptyf(t, propCodeId, "property %s doesn't have code id", propName)
+			propEq := rp.ExecutionJob.Queries[propCodeId]
+			require.NotNilf(t, propEq, "property %q not found in ExecutionJob with code id %q", propName, propCodeId)
+			require.Lenf(t, propEq.Datapoints, 1, "property %q should have exactly one datapoint", propName)
+			propDatapoint := propEq.Datapoints[0]
+			require.Equalf(t, eq.Properties[propName], propDatapoint, "property %q value mismatch", propName)
+		}
+	}
+}
+
+type resolvedPolicyTesterReportingJobNotifiesBuilder struct {
+	rjTester          *resolvedPolicyTesterReportingJobBuilder
+	childMrn          string
+	childMrnForCodeId string
+	parent            string
+	impact            *explorer.Impact
+	impactSet         bool
+}
+
+func (r *resolvedPolicyTesterReportingJobNotifiesBuilder) WithImpact(impact *explorer.Impact) *resolvedPolicyTesterReportingJobNotifiesBuilder {
+	r.impact = impact
+	r.impactSet = true
+	return r
+}
+
+func findReportingJobByQrId(rp *policy.ResolvedPolicy, qrId string) *policy.ReportingJob {
+	for _, rj := range rp.CollectorJob.ReportingJobs {
+		if rj.QrId == qrId {
+			return rj
+		}
+	}
+	return nil
+}
+
+func (r *resolvedPolicyTesterReportingJobNotifiesBuilder) testIt(t *testing.T, rp *policy.ResolvedPolicy) {
+	var qrId string
+	var extraInfo string
+	mrnsMatchesQrId := false
+	if r.childMrn != "" {
+		qrId = r.childMrn
+		mrnsMatchesQrId = true
+	} else {
+		q := r.rjTester.tester.bundleMap.Queries[r.childMrnForCodeId]
+		require.NotNilf(t, q, "query not found in bundle: %s", r.childMrnForCodeId)
+		require.NotEmptyf(t, q.CodeId, "query %s doesn't have code id", r.childMrnForCodeId)
+		qrId = q.CodeId
+		extraInfo = " (" + r.childMrnForCodeId + ")"
+	}
+	childRj := findReportingJobByQrId(rp, qrId)
+	require.NotNilf(t, childRj, "child reporting job %s%s not found", qrId, extraInfo)
+
+	if mrnsMatchesQrId {
+		require.Equalf(t, []string{qrId}, childRj.Mrns, "child reporting job %s%s mrns mismatch", qrId, extraInfo)
+	}
+
+	parentRj := findReportingJobByQrId(rp, r.parent)
+	require.NotNilf(t, parentRj, "parent reporting job %s not found", r.parent)
+	require.Containsf(t, childRj.Notify, parentRj.Uuid, "child reporting job %s%s doesn't notify parent reporting job %s", qrId, extraInfo, r.parent)
+
+	require.Containsf(t, parentRj.ChildJobs, childRj.Uuid, "parent reporting job %s doesn't have child reporting job %s%s", r.parent, qrId, extraInfo)
+	if r.impactSet {
+		require.EqualExportedValuesf(t, r.impact, parentRj.ChildJobs[childRj.Uuid], "impact mismatch for child reporting job %s%s", qrId, extraInfo)
+	}
+
+}
+
+type resolvedPolicyTesterReportingJobBuilder struct {
+	tester        *resolvedPolicyTester
+	mrn           string
+	mrnForCodeId  string
+	typ           *policy.ReportingJob_Type
+	scoringSystem *explorer.ScoringSystem
+	notifies      []*resolvedPolicyTesterReportingJobNotifiesBuilder
+	notifiesSet   bool
+	doesNotExist  bool
+}
+
+func (r *resolvedPolicyTester) CodeIdReportingJobForMrn(mrn string) *resolvedPolicyTesterReportingJobBuilder {
+	var item *resolvedPolicyTesterReportingJobBuilder
+	for _, existing := range r.items {
+		if existingItem, ok := existing.(*resolvedPolicyTesterReportingJobBuilder); ok && existingItem.mrnForCodeId == mrn {
+			item = existingItem
+			break
+		}
+	}
+	if item == nil {
+		item = &resolvedPolicyTesterReportingJobBuilder{tester: r, mrnForCodeId: mrn}
+		r.items = append(r.items, item)
+	}
+
+	return item
+}
+
+func (r *resolvedPolicyTester) ReportingJobByMrn(mrn string) *resolvedPolicyTesterReportingJobBuilder {
+	var item *resolvedPolicyTesterReportingJobBuilder
+	for _, existing := range r.items {
+		if existingItem, ok := existing.(*resolvedPolicyTesterReportingJobBuilder); ok && existingItem.mrn == mrn {
+			item = existingItem
+			break
+		}
+	}
+	if item == nil {
+		item = &resolvedPolicyTesterReportingJobBuilder{tester: r, mrn: mrn}
+		r.items = append(r.items, item)
+	}
+	return item
+}
+
+func (r *resolvedPolicyTesterReportingJobBuilder) DoesNotExist() {
+	r.doesNotExist = true
+}
+
+func (r *resolvedPolicyTesterReportingJobBuilder) WithType(typ policy.ReportingJob_Type) *resolvedPolicyTesterReportingJobBuilder {
+	r.typ = &typ
+	return r
+}
+
+func (r *resolvedPolicyTesterReportingJobBuilder) Notifies(qrId string) *resolvedPolicyTesterReportingJobNotifiesBuilder {
+	n := &resolvedPolicyTesterReportingJobNotifiesBuilder{rjTester: r, childMrnForCodeId: r.mrnForCodeId, childMrn: r.mrn, parent: qrId}
+	r.notifies = append(r.notifies, n)
+	r.notifiesSet = true
+	return n
+}
+
+func (r *resolvedPolicyTesterReportingJobBuilder) WithScoringSystem(scoringSystem explorer.ScoringSystem) *resolvedPolicyTesterReportingJobBuilder {
+	r.scoringSystem = &scoringSystem
+	return r
+}
+
+func (r *resolvedPolicyTesterReportingJobBuilder) testIt(t *testing.T, rp *policy.ResolvedPolicy) {
+	var qrId string
+	var extraInfo string
+	if r.mrn != "" {
+		qrId = r.mrn
+	} else {
+		q := r.tester.bundleMap.Queries[r.mrnForCodeId]
+		require.NotNilf(t, q, "query not found in bundle: %s", r.mrnForCodeId)
+		require.NotEmptyf(t, q.CodeId, "query %s doesn't have code id", r.mrnForCodeId)
+		qrId = q.CodeId
+		extraInfo = " (" + r.mrnForCodeId + ")"
+	}
+
+	rj := findReportingJobByQrId(rp, qrId)
+	if r.doesNotExist {
+		require.Nilf(t, rj, "reporting job %s%s should not exist", qrId, extraInfo)
+		return
+	}
+	require.NotNilf(t, rj, "reporting job %s%s not found", qrId, extraInfo)
+
+	if r.typ != nil {
+		require.Equalf(t, *r.typ, rj.Type, "reporting job %s%s type mismatch", qrId, extraInfo)
+	}
+
+	if r.scoringSystem != nil {
+		require.Equalf(t, *r.scoringSystem, rj.ScoringSystem, "reporting job %s%s scoring system mismatch", qrId, extraInfo)
+	}
+
+	if r.notifiesSet {
+		for _, n := range r.notifies {
+			n.testIt(t, rp)
+		}
+		require.Len(t, rj.Notify, len(r.notifies), "reporting job uuid=%s qrId=%s%s notify mismatch", rj.Uuid, qrId, extraInfo)
+	}
+}
+
+func contextResolverV2() context.Context {
+	return policy.WithNextGenResolver(context.Background())
+}
+
+func TestResolveV2_EmptyPolicy(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve w/o filters", func(t *testing.T) {
+		_, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn: policyMrn("policy1"),
+		})
+		assert.EqualError(t, err, "rpc error: code = InvalidArgument desc = asset doesn't support any policies")
+	})
+
+	t.Run("resolve with empty filters", func(t *testing.T) {
+		_, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{}},
+		})
+		assert.EqualError(t, err, "failed to compile query: failed to compile query '': query is not implemented ''")
+	})
+
+	t.Run("resolve with random filters", func(t *testing.T) {
+		_, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		assert.EqualError(t, err,
+			"rpc error: code = InvalidArgument desc = asset isn't supported by any policies\n"+
+				"policies didn't provide any filters\n"+
+				"asset supports: true\n")
+	})
+}
+
+func TestResolveV2_SimplePolicy(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset{*}
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+		require.Len(t, rp.ExecutionJob.Queries, 3)
+		require.Len(t, rp.Filters, 1)
+		require.Len(t, rp.CollectorJob.ReportingJobs, 5)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies("root")
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("resolve with many filters (one is correct)", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn: policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{
+				{Mql: "asset.family.contains(\"linux\")"},
+				{Mql: "true"},
+				{Mql: "asset.family.contains(\"windows\")"},
+			},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+	})
+
+	t.Run("resolve with incorrect filters", func(t *testing.T) {
+		_, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn: policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{
+				{Mql: "asset.family.contains(\"linux\")"},
+				{Mql: "false"},
+				{Mql: "asset.family.contains(\"windows\")"},
+			},
+		})
+		assert.EqualError(t, err,
+			"rpc error: code = InvalidArgument desc = asset isn't supported by any policies\n"+
+				"policies support: true\n"+
+				"asset supports: asset.family.contains(\"linux\"), asset.family.contains(\"windows\"), false\n")
+	})
+}
+
+func TestResolveV2_PolicyWithImpacts(t *testing.T) {
+	// For impacts, we always find the worst impact specified for a query in a policy bundle.
+	// All instances of the query use that impact
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy1
+    - uid: policy2
+      action: 4
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+    - uid: check2
+      impact: 10
+    - uid: check3
+      impact: 60
+    queries:
+    - uid: query1
+- uid: policy2
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check2
+      impact: 5
+    - uid: check3
+      impact: 80
+queries:
+- uid: check1
+  mql: asset.name == props.name
+  props:
+  - uid: name
+    mql: return "definitely not the asset name"
+- uid: check2
+  mql: true == false
+  impact: 70
+- uid: check3
+  mql: true == true
+  impact: 9
+- uid: query1
+  mql: asset{*}
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1"), policyMrn("policy2")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.ExecutesQuery(queryMrn("check2"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check2")).Notifies(queryMrn("check2")).WithImpact(&explorer.Impact{Value: &explorer.ImpactValue{Value: 70}})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check3")).Notifies(queryMrn("check3")).WithImpact(&explorer.Impact{Value: &explorer.ImpactValue{Value: 80}})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("check2")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("check3")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("check2")).Notifies(policyMrn("policy2"))
+		rpTester.ReportingJobByMrn(queryMrn("check3")).Notifies(policyMrn("policy2"))
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyWithScoringSystem(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy1
+- uid: policy1
+  scoring_system: highest impact
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset{*}
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).WithScoringSystem(explorer.ScoringSystem_WORST).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyWithScoringSystemOverride(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy1
+      scoring_system: banded
+- uid: policy1
+  scoring_system: highest impact
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset{*}
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).WithScoringSystem(explorer.ScoringSystem_BANDED).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyActionIgnore(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy-active
+    - uid: policy-ignored
+      action: 4
+- uid: policy-active
+  owner_mrn: //test.sth
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset.arch
+- uid: policy-ignored
+  owner_mrn: //test.sth
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset.arch
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy-active"), policyMrn("policy-ignored")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with ignored policy", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.ExecutesQuery(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy-active"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy-active"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy-ignored"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy-ignored"))
+		rpTester.ReportingJobByMrn(policyMrn("policy-active")).Notifies("root")
+		rpTester.ReportingJobByMrn(policyMrn("policy-ignored")).Notifies("root").WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyActionScoringSystem(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy-active
+      scoring_system: 6
+    - uid: policy-ignored
+      action: 4
+- uid: policy-active
+  owner_mrn: //test.sth
+  scoring_system: 2
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset.arch
+- uid: policy-ignored
+  owner_mrn: //test.sth
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset.arch
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy-active"), policyMrn("policy-ignored")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with scoring system", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.ExecutesQuery(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy-active"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy-active"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy-ignored"))
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy-ignored"))
+		rpTester.ReportingJobByMrn(policyMrn("policy-active")).WithScoringSystem(explorer.ScoringSystem_BANDED).Notifies("root")
+		rpTester.ReportingJobByMrn(policyMrn("policy-ignored")).Notifies("root").WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_IgnoredQuery(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy-1
+  owner_mrn: //test.sth
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: 1 == 1
+- mrn: asset1
+  owner_mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy-1
+  - checks:
+    - uid: check1
+      action: 4
+`)
+
+	_, srv, err := inmemory.NewServices(providers.DefaultRuntime(), nil)
+	require.NoError(t, err)
+
+	_, err = srv.SetBundle(ctx, b)
+	require.NoError(t, err)
+
+	rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+		PolicyMrn:    "asset1",
+		AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+	})
+
+	require.NoError(t, err)
+	require.NotNil(t, rp)
+
+	rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+	rpTester.ExecutesQuery(queryMrn("check1"))
+	rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+	rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies("policy-1").WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+	rpTester.ReportingJobByMrn(policyMrn("policy-1")).Notifies("root")
+}
+
+func TestResolveV2_Frameworks(t *testing.T) {
+	ctx := contextResolverV2()
+	bundleStr := `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - filters: "true"
+    checks:
+    - uid: check-fail
+      mql: 1 == 2
+    - uid: check-pass-1
+      mql: 1 == 1
+    - uid: check-pass-2
+      mql: 2 == 2
+    queries:
+    - uid: active-query
+      title: users
+      mql: users
+    - uid: active-query-2
+      title: users length
+      mql: users.length
+    - uid: check-overlap
+      title: overlaps with check
+      mql: 1 == 1
+- uid: policy-inactive
+  groups:
+  - filters: "false"
+    checks:
+    - uid: inactive-fail
+      mql: 1 == 2
+    - uid: inactive-pass
+      mql: 1 == 1
+    - uid: inactive-pass-2
+      mql: 2 == 2
+    queries:
+    - uid: inactive-query
+      title: users group
+      mql: users { group}
+frameworks:
+- uid: framework1
+  name: framework1
+  groups:
+  - title: group1
+    controls:
+    - uid: control1
+      title: control1
+    - uid: control2
+      title: control2
+    - uid: control3
+      title: control3
+    - uid: control4
+      title: control4
+    - uid: control5
+      title: control5
+- uid: framework2
+  name: framework2
+  groups:
+  - title: group1
+    controls:
+    - uid: control1
+      title: control1
+    - uid: control2
+      title: control2
+- uid: parent-framework
+  dependencies:
+  - mrn: ` + frameworkMrn("framework1") + `
+
+framework_maps:
+- uid: framework-map1
+  framework_owner:
+    uid: framework1
+  policy_dependencies:
+  - uid: policy1
+  controls:
+  - uid: control1
+    checks:
+    - uid: check-pass-1
+    queries:
+    - uid: active-query
+    - uid: active-query-2
+  - uid: control2
+    checks:
+    - uid: check-pass-2
+    - uid: check-fail
+  - uid: control4
+    controls:
+    - uid: control1
+- uid: framework-map2
+  framework_owner:
+    uid: framework1
+  policy_dependencies:
+  - uid: policy1
+  controls:
+  - uid: control4
+    controls:
+    - uid: control1
+  - uid: control5
+    controls:
+    - uid: control1
+`
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		b := parseBundle(t, bundleStr)
+
+		srv := initResolver(t, []*testAsset{
+			{asset: "asset1", policies: []string{policyMrn("policy1"), policyMrn("policy-inactive")}, frameworks: []string{frameworkMrn("parent-framework")}},
+		}, []*policy.Bundle{b})
+
+		bundle, err := srv.GetBundle(ctx, &policy.Mrn{Mrn: "asset1"})
+		require.NoError(t, err)
+
+		bundleMap, err := bundle.Compile(ctx, conf.Schema, nil)
+		require.NoError(t, err)
+
+		mrnToQueryId := map[string]string{}
+		for _, q := range bundleMap.Queries {
+			mrnToQueryId[q.Mrn] = q.CodeId
+		}
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("check-fail"))
+		rpTester.ExecutesQuery(queryMrn("check-pass-1"))
+		rpTester.ExecutesQuery(queryMrn("check-pass-2"))
+		rpTester.ExecutesQuery(queryMrn("active-query"))
+		rpTester.ExecutesQuery(queryMrn("active-query-2"))
+		rpTester.ExecutesQuery(queryMrn("check-overlap"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-fail")).Notifies(queryMrn("check-fail"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-fail")).Notifies(controlMrn("control2"))
+		rpTester.ReportingJobByMrn(queryMrn("check-fail")).Notifies(policyMrn("policy1"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-pass-1")).Notifies(queryMrn("check-pass-1"))
+		// This is a limitaion of the test framework. We lookup the code id from check-pass-1 because
+		// we need 1 tester that has all the notifies
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-pass-1")).Notifies(queryMrn("check-overlap"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-pass-1")).Notifies(controlMrn("control1"))
+		rpTester.ReportingJobByMrn(queryMrn("check-pass-1")).Notifies(policyMrn("policy1"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-pass-2")).Notifies(queryMrn("check-pass-2"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check-pass-2")).Notifies(controlMrn("control2"))
+		rpTester.ReportingJobByMrn(queryMrn("check-pass-2")).Notifies(policyMrn("policy1"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(queryMrn("active-query"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(controlMrn("control1"))
+		rpTester.ReportingJobByMrn(queryMrn("active-query")).Notifies(policyMrn("policy1"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query-2")).Notifies(queryMrn("active-query-2"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query-2")).Notifies(controlMrn("control1"))
+		rpTester.ReportingJobByMrn(queryMrn("active-query-2")).Notifies(policyMrn("policy1"))
+
+		rpTester.ReportingJobByMrn(queryMrn("check-overlap")).Notifies(policyMrn("policy1"))
+
+		rpTester.ReportingJobByMrn(controlMrn("control1")).Notifies(controlMrn("control4"))
+		rpTester.ReportingJobByMrn(controlMrn("control1")).Notifies(controlMrn("control5"))
+		rpTester.ReportingJobByMrn(controlMrn("control1")).Notifies(frameworkMrn("framework1"))
+		rpTester.ReportingJobByMrn(controlMrn("control2")).Notifies(frameworkMrn("framework1"))
+		rpTester.ReportingJobByMrn(controlMrn("control4")).Notifies(frameworkMrn("framework1"))
+
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).Notifies("root")
+		rpTester.ReportingJobByMrn(frameworkMrn("framework1")).Notifies(frameworkMrn("parent-framework"))
+		rpTester.ReportingJobByMrn(frameworkMrn("parent-framework")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("test resolving with inactive data queries", func(t *testing.T) {
+		// test that creating a bundle with inactive data queries  (where the packs/policies are inactive)
+		// will still end up in a successfully resolved policy for the asset
+		bundleStr := `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - filters: "true"
+    queries:
+    - uid: active-query
+      title: users
+      mql: users
+- uid: policy-inactive
+  groups:
+  - filters: "false"
+    queries:
+    - uid: inactive-query
+      title: users group
+      mql: users { group}
+frameworks:
+- uid: framework1
+  name: framework1
+  groups:
+  - title: group1
+    controls:
+    - uid: control1
+      title: control1
+    - uid: control2
+      title: control2
+- uid: parent-framework
+  dependencies:
+  - mrn: ` + frameworkMrn("framework1") + `
+
+framework_maps:
+- uid: framework-map1
+  framework_owner:
+    uid: framework1
+  policy_dependencies:
+  - uid: policy1
+  - uid: policy-inactive
+  controls:
+  - uid: control1
+    queries:
+    - uid: active-query
+  - uid: control2
+    queries:
+    - uid: inactive-query
+`
+		b := parseBundle(t, bundleStr)
+
+		// we do not activate policy-inactive, which means that its query should not get executed
+		srv := initResolver(t, []*testAsset{
+			{asset: "asset1", policies: []string{policyMrn("policy1")}, frameworks: []string{frameworkMrn("parent-framework")}},
+		}, []*policy.Bundle{b})
+
+		bundle, err := srv.GetBundle(ctx, &policy.Mrn{Mrn: "asset1"})
+		require.NoError(t, err)
+
+		bundleMap, err := bundle.Compile(ctx, conf.Schema, nil)
+		require.NoError(t, err)
+
+		mrnToQueryId := map[string]string{}
+		for _, q := range bundleMap.Queries {
+			mrnToQueryId[q.Mrn] = q.CodeId
+		}
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("active-query"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(queryMrn("active-query"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(controlMrn("control1"))
+		rpTester.ReportingJobByMrn(queryMrn("active-query")).Notifies(policyMrn("policy1"))
+
+		rpTester.ReportingJobByMrn(controlMrn("control1")).Notifies(frameworkMrn("framework1"))
+
+		rpTester.ReportingJobByMrn(frameworkMrn("framework1")).Notifies(frameworkMrn("parent-framework"))
+		rpTester.ReportingJobByMrn(frameworkMrn("parent-framework")).Notifies("root")
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("test resolving with non-matching data queries", func(t *testing.T) {
+		// test that creating a bundle with active data queries that do not match the asset, based on the
+		// policy asset filters, will still create a resolved policy for the asset
+		bundleStr := `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - filters: "false"
+    queries:
+    - uid: query-1
+      title: users
+      mql: users
+- uid: policy2
+  groups:
+  - filters: "true"
+    queries:
+    - uid: query-2
+      title: users length
+      mql: users.length
+
+frameworks:
+- uid: framework1
+  name: framework1
+  groups:
+  - title: group1
+    controls:
+    - uid: control1
+      title: control1
+- uid: parent-framework
+  dependencies:
+  - mrn: ` + frameworkMrn("framework1") + `
+
+framework_maps:
+- uid: framework-map1
+  framework_owner:
+    uid: framework1
+  policy_dependencies:
+  - uid: policy1
+  - uid: policy2
+  controls:
+  - uid: control1
+    queries:
+    - uid: query-1
+    - uid: query-2
+`
+		b := parseBundle(t, bundleStr)
+
+		srv := initResolver(t, []*testAsset{
+			{asset: "asset1", policies: []string{policyMrn("policy1"), policyMrn("policy2")}, frameworks: []string{frameworkMrn("parent-framework")}},
+		}, []*policy.Bundle{b})
+
+		bundle, err := srv.GetBundle(ctx, &policy.Mrn{Mrn: "asset1"})
+		require.NoError(t, err)
+
+		bundleMap, err := bundle.Compile(ctx, conf.Schema, nil)
+		require.NoError(t, err)
+
+		mrnToQueryId := map[string]string{}
+		for _, q := range bundleMap.Queries {
+			mrnToQueryId[q.Mrn] = q.CodeId
+		}
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query-2"))
+
+		rpTester.ReportingJobByMrn(queryMrn("query-1")).DoesNotExist()
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query-2")).Notifies(queryMrn("query-2"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query-2")).Notifies(controlMrn("control1"))
+		rpTester.ReportingJobByMrn(queryMrn("query-2")).Notifies(policyMrn("policy2"))
+
+		rpTester.ReportingJobByMrn(controlMrn("control1")).Notifies(frameworkMrn("framework1"))
+
+		rpTester.ReportingJobByMrn(frameworkMrn("framework1")).Notifies(frameworkMrn("parent-framework"))
+		rpTester.ReportingJobByMrn(frameworkMrn("parent-framework")).Notifies("root")
+		rpTester.ReportingJobByMrn(policyMrn("policy2")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("test checksumming", func(t *testing.T) {
+		bInitial := parseBundle(t, bundleStr)
+
+		srv := initResolver(t, []*testAsset{
+			{asset: "asset1", policies: []string{policyMrn("policy1")}, frameworks: []string{frameworkMrn("parent-framework")}},
+		}, []*policy.Bundle{bInitial})
+
+		rpInitial, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rpInitial)
+
+		bFrameworkUpdate := parseBundle(t, bundleStr)
+		bFrameworkUpdate.Frameworks[0].Groups[0].Controls = bFrameworkUpdate.Frameworks[0].Groups[0].Controls[:2]
+
+		srv = initResolver(t, []*testAsset{
+			{asset: "asset1", policies: []string{policyMrn("policy1")}, frameworks: []string{frameworkMrn("parent-framework")}},
+		}, []*policy.Bundle{bFrameworkUpdate})
+
+		rpFrameworkUpdate, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rpFrameworkUpdate)
+
+		require.NotEqual(t, rpInitial.GraphExecutionChecksum, rpFrameworkUpdate.GraphExecutionChecksum)
+	})
+}
+
+// TestResolve_PoliciesMatchingAgainstIncorrectPlatform tests that policies are not matched against
+// assets that do not match the asset filter. It was possible that the reporting structure had
+// a node for the policy, but no actual reporting job for it. To the user, this could look
+// like the policy was executed. The issue was that a policy was considered matching if either
+// the groups or any of its queries filters matched. This tests to ensure that if the policies
+// group filtered it out, it doesn't show up in the reporting structure
+func TestResolveV2_PoliciesMatchingAgainstIncorrectPlatform(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+- uid: policy2
+  groups:
+  - type: chapter
+    filters: "false"
+    checks:
+    - uid: check2
+- uid: pack1
+  groups:
+  - type: chapter
+    filters: "true"
+    queries:
+    - uid: dataquery1
+- uid: pack2
+  groups:
+  - type: chapter
+    filters: "false"
+    queries:
+    - uid: dataquery2
+
+queries:
+- uid: check1
+  title: check1
+  mql: true
+- uid: check2
+  title: check2
+  filters: |
+    true
+  mql: |
+    1 == 1
+- uid: dataquery1
+  title: dataquery1
+  mql: |
+    asset.name
+- uid: dataquery2
+  title: dataquery2
+  filters: |
+    true
+  mql: |
+    asset.version
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1"), policyMrn("policy2"), policyMrn("pack1"), policyMrn("pack2")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(policyMrn("policy2")).DoesNotExist()
+		rpTester.ReportingJobByMrn(policyMrn("pack2")).DoesNotExist()
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_NeverPruneRoot(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "false"
+    checks:
+    - uid: check1
+
+queries:
+- uid: check1
+  title: check1
+  filters: |
+    true
+  mql: |
+    1 == 1
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+		PolicyMrn:    "asset1",
+		AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+	})
+	require.NoError(t, err)
+	require.NotNil(t, rp)
+
+}
+
+func TestResolveV2_PoliciesMatchingFilters(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    checks:
+    - uid: check1
+    - uid: check2
+queries:
+- uid: check1
+  title: check1
+  filters:
+  - mql: asset.name == "asset1"
+  - mql: asset.name == "asset2"
+  mql: |
+    asset.version
+- uid: check2
+  title: check2
+  filters:
+  - mql: |
+      asset.name == "asset1"
+      asset.name == "asset2"
+  mql: |
+    asset.platform
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "asset.name == \"asset1\""}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ExecutesQuery(queryMrn("check1"))
+		rpTester.DoesNotExecutesQuery(queryMrn("check2"))
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_TwoMrns(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - filters:
+    - mql: asset.name == "asset1"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    - uid: check2
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve two MRNs to one codeID matching filter", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{Mql: "asset.name == \"asset1\""}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("check1"))
+		rpTester.ExecutesQuery(queryMrn("check2"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		// This is a limitaion of the test framework. We lookup the code id from check-pass-1 because
+		// we need 1 tester that has all the notifies
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check2"))
+
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies("root")
+		rpTester.ReportingJobByMrn(queryMrn("check2")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_TwoMrns_FilterMismatch(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+      filters:
+      - mql: asset.name == "asset1"
+    - uid: check2
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+      filters:
+      - mql: asset.name == "asset2"
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve two MRNs to one codeID matching filter", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{Mql: "asset.name == \"asset1\""}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("check1"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies("root")
+		rpTester.ReportingJobByMrn(queryMrn("check2")).DoesNotExist()
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_TwoMrns_DataQueries(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - filters:
+    - mql: asset.name == "asset1"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+  - queries:
+    - uid: active-query
+      title: users
+      mql: users
+    - uid: active-query-2
+      title: users length
+      mql: users
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve two MRNs to one codeID matching filter", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{{Mql: "asset.name == \"asset1\""}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("check1"))
+		rpTester.ExecutesQuery(queryMrn("active-query"))
+		rpTester.ExecutesQuery(queryMrn("active-query-2"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(queryMrn("active-query"))
+		// This is a limitaion of the test framework. We lookup the code id from active-query because
+		// we need 1 tester that has all the notifies
+		rpTester.CodeIdReportingJobForMrn(queryMrn("active-query")).Notifies(queryMrn("active-query-2"))
+
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies("root")
+		rpTester.ReportingJobByMrn(queryMrn("active-query")).Notifies("root")
+		rpTester.ReportingJobByMrn(queryMrn("active-query-2")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_TwoMrns_Variants(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - checks:
+    - uid: check-variants
+queries:
+  - uid: check-variants
+    variants:
+      - uid: variant1
+      - uid: variant2
+  - uid: variant1
+    mql: asset.name == "test1"
+    filters: asset.family.contains("unix")
+  - uid: variant2
+    mql: asset.name == "test1"
+    filters: asset.name == "asset1"
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve two variants to different codeIDs matching filter", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn: policyMrn("policy1"),
+			AssetFilters: []*explorer.Mquery{
+				{Mql: "asset.name == \"asset1\""},
+				{Mql: "asset.family.contains(\"unix\")"},
+			},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("variant1"))
+		rpTester.DoesNotExecutesQuery(queryMrn("variant2"))
+
+		rpTester.CodeIdReportingJobForMrn(queryMrn("variant1")).Notifies(queryMrn("variant1"))
+		rpTester.ReportingJobByMrn(queryMrn("variant1")).Notifies("check-variants")
+		rpTester.ReportingJobByMrn(queryMrn("variant2")).DoesNotExist()
+		rpTester.ReportingJobByMrn("check-variants").Notifies("root")
+	})
+}
+
+func TestResolveV2_Variants(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+  - uid: example2
+    name: Another policy
+    version: "1.0.0"
+    groups:
+      # Additionally it defines some queries of its own
+      - type: chapter
+        title: Some uname infos
+        queries:
+          # In this case, we are using a shared query that is defined below
+          - uid: uname
+        checks:
+          - uid: check-os
+            variants:
+              - uid: check-os-unix
+              - uid: check-os-windows
+
+queries:
+  # This is a composed query which has two variants: one for unix type systems
+  # and one for windows, where we don't run the additional argument.
+  # If you run the "uname" query, it will pick matching sub-queries for you.
+  - uid: uname
+    title: Collect uname info
+    variants:
+      - uid: unix-uname
+      - uid: windows-uname
+  - uid: unix-uname
+    mql: command("uname -a").stdout
+    filters: asset.family.contains("unix")
+  - uid: windows-uname
+    mql: command("uname").stdout
+    filters: asset.family.contains("windows")
+
+  - uid: check-os-unix
+    filters: asset.family.contains("unix")
+    title: A check only run on Linux/macOS
+    mql: users.contains(name == "root")
+  - uid: check-os-windows
+    filters: asset.family.contains("windows")
+    title: A check only run on Windows
+    mql: users.contains(name == "Administrator")`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("example2")}},
+	}, []*policy.Bundle{b})
+
+	_, err := srv.SetBundle(ctx, b)
+	require.NoError(t, err)
+
+	_, err = b.Compile(ctx, conf.Schema, nil)
+	require.NoError(t, err)
+
+	rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+		PolicyMrn:    policyMrn("example2"),
+		AssetFilters: []*explorer.Mquery{{Mql: "asset.family.contains(\"windows\")"}},
+	})
+
+	require.NoError(t, err)
+	require.NotNil(t, rp)
+
+	rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+	rpTester.ExecutesQuery(queryMrn("windows-uname"))
+	rpTester.ExecutesQuery(queryMrn("check-os-windows"))
+	rpTester.DoesNotExecutesQuery(queryMrn("unix-uname"))
+	rpTester.DoesNotExecutesQuery(queryMrn("check-os-unix"))
+
+	rpTester.CodeIdReportingJobForMrn(queryMrn("windows-uname")).Notifies(queryMrn("windows-uname"))
+	rpTester.ReportingJobByMrn(queryMrn("windows-uname")).Notifies(queryMrn("uname"))
+	rpTester.ReportingJobByMrn(queryMrn("uname")).Notifies("root")
+
+	rpTester.CodeIdReportingJobForMrn(queryMrn("check-os-windows")).Notifies(queryMrn("check-os-windows"))
+	rpTester.ReportingJobByMrn(queryMrn("check-os-windows")).Notifies(queryMrn("check-os"))
+	rpTester.ReportingJobByMrn(queryMrn("check-os")).Notifies("root")
+
+	rpTester.doTest(t, rp)
+}
+
+func TestResolveV2_RiskFactors(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+queries:
+- uid: query-1
+  title: query-1
+  mql: 3 == 3
+- uid: query-2
+  title: query-2
+  mql: 1 == 2
+policies:
+  - name: testpolicy1
+    uid: testpolicy1
+    risk_factors:
+    - uid: sshd-service
+      magnitude: 0.9
+    - uid: sshd-service-na
+      action: 2
+    groups:
+    - filters: asset.name == "asset1"
+      checks:
+      - uid: query-1
+      - uid: query-2
+      policies:
+      - uid: risk-factors-security
+  - uid: risk-factors-security
+    name: Mondoo Risk Factors analysis
+    version: "1.0.0"
+    risk_factors:
+      - uid: sshd-service
+        title: SSHd Service running
+        indicator: asset-in-use
+        magnitude: 0.6
+        filters:
+          - mql: |
+              asset.name == "asset1"
+        checks:
+          - uid: sshd-service-running
+            mql: 1 == 1
+      - uid: sshd-service-na
+        title: SSHd Service running
+        indicator: asset-in-use
+        magnitude: 0.5
+        filters:
+          - mql: |
+              asset.name == "asset1"
+        checks:
+          - uid: sshd-service-running-na
+            mql: 1 == 7
+      - uid: not-matching
+        title: Not Matching
+        indicator: asset-in-use
+        magnitude: 0.5
+        filters:
+          - mql: |
+              asset.name == "asset2"
+        checks:
+          - uid: not-matching
+            mql: true == false
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("testpolicy1")}},
+	}, []*policy.Bundle{b})
+
+	rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+		PolicyMrn:    "asset1",
+		AssetFilters: []*explorer.Mquery{{Mql: "asset.name == \"asset1\""}},
+	})
+	require.NoError(t, err)
+	require.NotNil(t, rp)
+
+	rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+	rpTester.ExecutesQuery(queryMrn("query-1"))
+	rpTester.ExecutesQuery(queryMrn("query-2"))
+	rpTester.ExecutesQuery(queryMrn("sshd-service-running"))
+	rpTester.DoesNotExecutesQuery(queryMrn("sshd-service-running-na"))
+	rpTester.DoesNotExecutesQuery(queryMrn("not-matching"))
+	rpTester.ReportingJobByMrn(queryMrn("sshd-service-running-na")).DoesNotExist()
+	rpTester.ReportingJobByMrn(queryMrn("not-matching")).DoesNotExist()
+	rpTester.ReportingJobByMrn(riskFactorMrn("not-matching")).DoesNotExist()
+
+	rpTester.CodeIdReportingJobForMrn(queryMrn("query-1")).Notifies(queryMrn("query-1"))
+	rpTester.CodeIdReportingJobForMrn(queryMrn("query-2")).Notifies(queryMrn("query-2"))
+
+	// rpTester.CodeIdReportingJobForMrn(queryMrn("sshd-service-running")).Notifies(queryMrn("sshd-service-running"))
+	rpTester.CodeIdReportingJobForMrn(queryMrn("sshd-service-running")).Notifies(riskFactorMrn("sshd-service")).WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+	rpTester.ReportingJobByMrn(riskFactorMrn("sshd-service")).WithType(policy.ReportingJob_RISK_FACTOR).Notifies(policyMrn("risk-factors-security"))
+
+	rpTester.ReportingJobByMrn(queryMrn("query-1")).Notifies(policyMrn("testpolicy1"))
+	rpTester.ReportingJobByMrn(queryMrn("query-2")).Notifies(policyMrn("testpolicy1"))
+
+	rpTester.ReportingJobByMrn(policyMrn("testpolicy1")).Notifies("root")
+
+	rpTester.doTest(t, rp)
+
+	require.Equal(t, float32(0.9), rp.CollectorJob.RiskFactors[riskFactorMrn("sshd-service")].Magnitude.GetValue())
+}
+
+func TestResolveV2_FrameworkExceptions(t *testing.T) {
+	ctx := contextResolverV2()
+	bundleString := `
+owner_mrn: //test.sth
+policies:
+- uid: ssh-policy
+  name: SSH Policy
+  groups:
+  - filters: "true"
+    checks:
+    - uid: sshd-ciphers-01
+      title: Prevent weaker CBC ciphers from being used
+      mql: sshd.config.ciphers.none( /cbc/ )
+      impact: 60
+    - uid: sshd-ciphers-02
+      title: Do not allow ciphers with few bits
+      mql: sshd.config.ciphers.none( /128/ )
+      impact: 60
+    - uid: sshd-config-permissions
+      title: SSH config editing should be limited to admins
+      mql: sshd.config.file.permissions.mode == 0644
+      impact: 100
+
+frameworks:
+- uid: mondoo-ucf
+  name: Unified Compliance Framework
+  groups:
+  - title: System hardening
+    controls:
+    - uid: mondoo-ucf-01
+      title: Only use strong ciphers
+    - uid: mondoo-ucf-02
+      title: Limit access to system configuration
+    - uid: mondoo-ucf-03
+      title: Only use ciphers with sufficient bits
+  - title: exception-1
+    type: 4
+    controls:
+    - uid: mondoo-ucf-02
+
+framework_maps:
+    - uid: compliance-to-ssh-policy
+      mrn: //test.sth/framework/compliance-to-ssh-policy
+      framework_owner:
+        uid: mondoo-ucf
+      policy_dependencies:
+      - uid: ssh-policy
+      controls:
+      - uid: mondoo-ucf-01
+        checks:
+        - uid: sshd-ciphers-01
+        - uid: sshd-ciphers-02
+      - uid: mondoo-ucf-02
+        checks:
+        - uid: sshd-config-permissions
+      - uid: mondoo-ucf-03
+        checks:
+        - uid: sshd-ciphers-02
+`
+
+	_, srv, err := inmemory.NewServices(providers.DefaultRuntime(), nil)
+	require.NoError(t, err)
+
+	t.Run("resolve with ignored control", func(t *testing.T) {
+		b := parseBundle(t, bundleString)
+
+		srv = initResolver(t, []*testAsset{
+			{
+				asset:      "asset1",
+				policies:   []string{policyMrn("ssh-policy")},
+				frameworks: []string{frameworkMrn("mondoo-ucf")},
+			},
+		}, []*policy.Bundle{b})
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(controlMrn("mondoo-ucf-02")).Notifies(frameworkMrn("mondoo-ucf")).WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("resolve with ignored control and validUntil", func(t *testing.T) {
+		b := parseBundle(t, bundleString)
+		b.Frameworks[0].Groups[1].EndDate = time.Now().Add(time.Hour).Unix()
+
+		srv = initResolver(t, []*testAsset{
+			{
+				asset:      "asset1",
+				policies:   []string{policyMrn("ssh-policy")},
+				frameworks: []string{frameworkMrn("mondoo-ucf")},
+			},
+		}, []*policy.Bundle{b})
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(controlMrn("mondoo-ucf-02")).Notifies(frameworkMrn("mondoo-ucf")).WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("resolve with expired validUntil", func(t *testing.T) {
+		b := parseBundle(t, bundleString)
+		b.Frameworks[0].Groups[1].EndDate = time.Now().Add(-time.Hour).Unix()
+
+		srv = initResolver(t, []*testAsset{
+			{
+				asset:      "asset1",
+				policies:   []string{policyMrn("ssh-policy")},
+				frameworks: []string{frameworkMrn("mondoo-ucf")},
+			},
+		}, []*policy.Bundle{b})
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(controlMrn("mondoo-ucf-02")).Notifies(frameworkMrn("mondoo-ucf")).WithImpact(nil)
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("resolve with expired validUntil", func(t *testing.T) {
+		b := parseBundle(t, bundleString)
+		b.Frameworks[0].Groups[1].EndDate = time.Now().Add(time.Hour).Unix()
+		b.Frameworks[0].Groups[1].Type = policy.GroupType_DISABLE
+		b.Frameworks[0].Groups[1].ReviewStatus = policy.ReviewStatus_REJECTED
+
+		srv = initResolver(t, []*testAsset{
+			{
+				asset:      "asset1",
+				policies:   []string{policyMrn("ssh-policy")},
+				frameworks: []string{frameworkMrn("mondoo-ucf")},
+			},
+		}, []*policy.Bundle{b})
+
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(controlMrn("mondoo-ucf-02")).Notifies(frameworkMrn("mondoo-ucf")).WithImpact(nil)
+
+		rpTester.doTest(t, rp)
+	})
+
+	t.Run("resolve with disabled control", func(t *testing.T) {
+		b := parseBundle(t, bundleString)
+		b.Frameworks = append(b.Frameworks, &policy.Framework{
+			Mrn: frameworkMrn("test"),
+			Dependencies: []*policy.FrameworkRef{
+				{
+					Mrn:    frameworkMrn("mondoo-ucf"),
+					Action: explorer.Action_ACTIVATE,
+				},
+			},
+			Groups: []*policy.FrameworkGroup{
+				{
+					Uid:  "test",
+					Type: policy.GroupType_DISABLE,
+					Controls: []*policy.Control{
+						{Uid: b.Frameworks[0].Groups[0].Controls[0].Uid},
+					},
+				},
+			},
+		})
+
+		srv = initResolver(t, []*testAsset{
+			{
+				asset:      "asset1",
+				policies:   []string{policyMrn("ssh-policy")},
+				frameworks: []string{frameworkMrn("mondoo-ucf"), frameworkMrn("test")},
+			},
+		}, []*policy.Bundle{b})
+
+		rp, err := srv.Resolve(context.Background(), &policy.ResolveReq{
+			PolicyMrn:    "asset1",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+
+		rpTester.ReportingJobByMrn(controlMrn("mondoo-ucf-01")).DoesNotExist()
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyExceptionIgnored(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset{*}
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy1
+  - type: ignored
+    uid: "exceptions-1"
+    checks:
+    - uid: check1
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.ExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.CodeIdReportingJobForMrn(queryMrn("query1")).Notifies(queryMrn("query1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy1")).WithImpact(&explorer.Impact{Scoring: explorer.ScoringSystem_IGNORE_SCORE})
+		rpTester.ReportingJobByMrn(queryMrn("query1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}
+
+func TestResolveV2_PolicyExceptionDisabled(t *testing.T) {
+	ctx := contextResolverV2()
+	b := parseBundle(t, `
+owner_mrn: //test.sth
+policies:
+- uid: policy1
+  groups:
+  - type: chapter
+    filters: "true"
+    checks:
+    - uid: check1
+      mql: asset.name == props.name
+      props:
+      - uid: name
+        mql: return "definitely not the asset name"
+    queries:
+    - uid: query1
+      mql: asset{*}
+- owner_mrn: //test.sth
+  mrn: //test.sth
+  groups:
+  - policies:
+    - uid: policy1
+  - type: disable
+    uid: "exceptions-1"
+    checks:
+    - uid: query1
+`)
+
+	srv := initResolver(t, []*testAsset{
+		{asset: "asset1", policies: []string{policyMrn("policy1")}},
+	}, []*policy.Bundle{b})
+
+	t.Run("resolve with correct filters", func(t *testing.T) {
+		rp, err := srv.Resolve(ctx, &policy.ResolveReq{
+			PolicyMrn:    "//test.sth",
+			AssetFilters: []*explorer.Mquery{{Mql: "true"}},
+		})
+		require.NoError(t, err)
+		require.NotNil(t, rp)
+
+		rpTester := newResolvedPolicyTester(b, srv.NewCompilerConfig())
+		rpTester.DoesNotExecutesQuery(queryMrn("query1"))
+		rpTester.
+			ExecutesQuery(queryMrn("check1")).
+			WithProps(map[string]string{"name": `return "definitely not the asset name"`})
+		rpTester.CodeIdReportingJobForMrn(queryMrn("check1")).Notifies(queryMrn("check1"))
+		rpTester.ReportingJobByMrn(queryMrn("check1")).Notifies(policyMrn("policy1"))
+		rpTester.ReportingJobByMrn(policyMrn("policy1")).Notifies("root")
+
+		rpTester.doTest(t, rp)
+	})
+}