diff --git a/lib/resolve.c b/lib/resolve.c
index f7f6f0aa86ae8a359996644ac3f74043ee211109..ae623568de2947767b15208390abad847d4369b1 100644
--- a/lib/resolve.c
+++ b/lib/resolve.c
@@ -322,46 +322,36 @@ int kr_resolve_consume(struct kr_request *request, knot_pkt_t *packet)
 	return kr_rplan_empty(&request->rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
 }
 
-int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet)
+/** @internal Spawn subrequest in current zone cut (no minimization or lookup). */
+static int zone_cut_subreq(struct kr_rplan *rplan, struct kr_query *parent,
+                           const knot_dname_t *qname, uint16_t qtype)
 {
-	struct kr_rplan *rplan = &request->rplan;
-	struct kr_query *qry = kr_rplan_current(rplan);
-	unsigned ns_election_iter = 0;
-	
-	/* No query left for resolution */
-	if (kr_rplan_empty(rplan)) {
-		return KNOT_STATE_FAIL;
-	}
-
-	/* Resolve current query and produce dependent or finish */
-	ITERATE_LAYERS(request, produce, packet);
-	if (request->state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) {
-		/* Produced an answer, consume it. */
-		qry->secret = 0;
-		request->state = KNOT_STATE_CONSUME;
-		ITERATE_LAYERS(request, consume, packet);
+	struct kr_query *next = kr_rplan_push(rplan, parent, qname, parent->sclass, qtype);
+	if (!next) {
+		return kr_error(ENOMEM);
 	}
-	switch(request->state) {
-	case KNOT_STATE_FAIL: return request->state; break;
-	case KNOT_STATE_CONSUME: break;
-	case KNOT_STATE_DONE:
-	default: /* Current query is done */
-		if (qry->flags & QUERY_RESOLVED) {
-			kr_rplan_pop(rplan, qry);
-		}
-		ITERATE_LAYERS(request, reset);
-		return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
+	kr_zonecut_set(&next->zone_cut, parent->zone_cut.name);
+	if (kr_zonecut_copy(&next->zone_cut, &parent->zone_cut) != 0 ||
+	    kr_zonecut_copy_trust(&next->zone_cut, &parent->zone_cut) != 0) {
+		return kr_error(ENOMEM);
 	}
+	next->flags |= QUERY_NO_MINIMIZE;
+	return kr_ok();
+}
 
-	/* The query wasn't resolved from cache,
-	 * now it's the time to look up closest zone cut from cache.
-	 */
-	 /* Always try with DNSSEC if it finds island of trust. */
-	 /* @todo this interface is going to change */
-	if (kr_ta_contains(&global_trust_anchors, qry->zone_cut.name)) {
+/** @internal Check current zone cut status and credibility, spawn subrequests if needed. */
+static int zone_cut_check(struct kr_request *request, struct kr_query *qry, knot_pkt_t *packet)
+{
+	struct kr_rplan *rplan = &request->rplan;
+
+	/* Always try with DNSSEC if it finds an island of trust. */
+	if (!(request->options & QUERY_DNSSEC_WANT) &&
+	    kr_ta_contains(&global_trust_anchors, qry->zone_cut.name)) {
 		request->options |= QUERY_DNSSEC_WANT;
 		DEBUG_MSG(">< entered island of trust\n");
 	}
+	/* The query wasn't resolved from cache,
+	 * now it's the time to look up closest zone cut from cache. */
 	bool want_secured = (request->options & QUERY_DNSSEC_WANT);
 	if (qry->flags & QUERY_AWAIT_CUT) {
 		int ret = ns_fetch_cut(qry, request, want_secured);
@@ -377,18 +367,9 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
 		qry->flags &= ~QUERY_AWAIT_CUT;
 	}
 
-	/* fetch missing DS record. */
+	/* Missing delegation signature, fetch it first. */
 	if ((qry->flags & QUERY_AWAIT_DS) && (qry->zone_cut.missing_name)) {
-		struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.missing_name, KNOT_CLASS_IN, KNOT_RRTYPE_DS);
-		if (!next) {
-			return KNOT_STATE_FAIL;
-		}
-		kr_zonecut_set(&next->zone_cut, qry->zone_cut.parent_name);
-		int ret = kr_zonecut_copy(&next->zone_cut, &qry->zone_cut);
-		if (ret != 0) {
-			return KNOT_STATE_FAIL;
-		}
-		ret = kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut);
+		int ret = zone_cut_subreq(rplan, qry, qry->zone_cut.missing_name, KNOT_RRTYPE_DS);
 		if (ret != 0) {
 			return KNOT_STATE_FAIL;
 		}
@@ -396,33 +377,67 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t
 		knot_rrset_free(&qry->zone_cut.key, qry->zone_cut.pool);
 		knot_rrset_free(&qry->zone_cut.trust_anchor, qry->zone_cut.pool);
 		qry->flags &= ~QUERY_AWAIT_DS;
-		return KNOT_STATE_PRODUCE;
+		return KNOT_STATE_DONE;
 	}
 
 	/* Try to fetch missing DNSKEY. */
 	if (want_secured && !qry->zone_cut.key && qry->stype != KNOT_RRTYPE_DNSKEY) {
-		struct kr_query *next = kr_rplan_push(rplan, qry, qry->zone_cut.name, KNOT_CLASS_IN, KNOT_RRTYPE_DNSKEY);
-		if (!next) {
-			return KNOT_STATE_FAIL;
-		}
-		kr_zonecut_set(&next->zone_cut, qry->zone_cut.name);
-		int ret = kr_zonecut_copy(&next->zone_cut, &qry->zone_cut);
+		int ret = zone_cut_subreq(rplan, qry, qry->zone_cut.name, KNOT_RRTYPE_DNSKEY);
 		if (ret != 0) {
 			return KNOT_STATE_FAIL;
 		}
-		ret = kr_zonecut_copy_trust(&next->zone_cut, &qry->zone_cut);
-		if (ret != 0) {
-			return KNOT_STATE_FAIL;
+		return KNOT_STATE_DONE;
+	}
+
+	return KNOT_STATE_PRODUCE;	
+}
+
+int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *type, knot_pkt_t *packet)
+{
+	struct kr_rplan *rplan = &request->rplan;
+	struct kr_query *qry = kr_rplan_current(rplan);
+	unsigned ns_election_iter = 0;
+	
+	/* No query left for resolution */
+	if (kr_rplan_empty(rplan)) {
+		return KNOT_STATE_FAIL;
+	}
+
+	/* Resolve current query and produce dependent or finish */
+	ITERATE_LAYERS(request, produce, packet);
+	if (request->state != KNOT_STATE_FAIL && knot_wire_get_qr(packet->wire)) {
+		/* Produced an answer, consume it. */
+		qry->secret = 0;
+		request->state = KNOT_STATE_CONSUME;
+		ITERATE_LAYERS(request, consume, packet);
+	}
+	switch(request->state) {
+	case KNOT_STATE_FAIL: return request->state;
+	case KNOT_STATE_CONSUME: break;
+	case KNOT_STATE_DONE:
+	default: /* Current query is done */
+		if (qry->flags & QUERY_RESOLVED) {
+			kr_rplan_pop(rplan, qry);
 		}
-		return KNOT_STATE_PRODUCE;
+		ITERATE_LAYERS(request, reset);
+		return kr_rplan_empty(rplan) ? KNOT_STATE_DONE : KNOT_STATE_PRODUCE;
 	}
+
+	/* Update zone cut, spawn new subrequests. */
+	int state = zone_cut_check(request, qry, packet);
+	switch(state) {
+	case KNOT_STATE_FAIL: return KNOT_STATE_FAIL;
+	case KNOT_STATE_DONE: return KNOT_STATE_PRODUCE;
+	default: break;
+	}
+
 ns_election:
 
 	/* If the query has already selected a NS and is waiting for IPv4/IPv6 record,
 	 * elect best address only, otherwise elect a completely new NS.
 	 */
 	if(++ns_election_iter >= KR_ITER_LIMIT) {
-		DEBUG_MSG("=> couldn't agree NS decision, report this\n");
+		DEBUG_MSG("=> couldn't converge NS selection, bail out\n");
 		return KNOT_STATE_FAIL;
 	}
 	if (qry->flags & (QUERY_AWAIT_IPV4|QUERY_AWAIT_IPV6)) {