A consolidation batch can be rejected by the daemon with
bad-txns-shielded-requirements-not-met (REJECT_DUPLICATE) when it clashes with an
earlier, still-unconfirmed batch — the batch outran its predecessors into the
mempool. That isn't a real failure: once the earlier batches confirm, the same
merge succeeds. Previously it failed the whole address.
Now a merge OP failure whose message is a mempool conflict (shielded- /
joinsplit-requirements-not-met, txn-mempool-conflict, inputs-spent — new pure
helper isDeferrableMergeOpError) HOLDS the address for kRunMempoolClearSec (2 min)
instead of failing it, then retries from where it left off; up to
kRunMaxDeferRetries consecutive defers (reset on any success), after which it is a
genuine failure. Deferrals don't feed the halt breaker, and since the address
outcome stays non-failed the run verdict reads Clean when the retry succeeds.
applyOpState takes `now` to stamp the hold; next() waits on a deferred address
until its window passes.
Unit-tested (testConsolidationDefer): the classifier; and a driven run where an
op reports the conflict → the address is deferred (not failed), held before the
window and retried after it. Build + tests + hygiene green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>