Retries, downgrades, and deferral

Updated Jul 27, 2026

Flex calls retry under bounded exponential backoff (flexAttempts, default 3; backoff from flexInitialBackoffSeconds (30) up to flexMaxBackoffSeconds (120), with up to flexJitterSeconds (10) of deterministic jitter). Each retry attempt is admitted against the remaining budget before it runs and charged when admitted, so retries can stop for either of two reasons: exhausting the attempt count, or the remaining budget refusing the next attempt.

A finder pack that exhausts its retries — or whose next retry the budget refuses — downgrades: it is recorded in lunaDowngrades and metrics.lunaDowngradeCount, and the review continues with the surviving candidates. An audit call that exhausts its retries, or whose next retry the budget refuses, defers the whole review instead: the result is ok: false, stage: "deferred", no recordInput is present, and the CLI exits non-zero without recording anything, so the head remains unreviewed. A budget-refused retry keeps the actual spend within the hard ceiling rather than overrunning it; the deferral or downgrade reason names the budget so operators can tell it apart from a capacity exhaustion.

The dry run reports reservedAuditChainUsd alongside reservedAuditUsd: the former is the audit's chain-level worst case (one attempt's reserve times flexAttempts), shown so operators can see the full retry cost, while admission only ever reserves the single-attempt reservedAuditUsd.

A deferred review retried later re-pays its Luna finder calls: Luna output is not cached across separate dakar-review invocations, so a retry after a deferral repeats the finder phase's spend (roughly USD 0.04 worst case at default limits). Operators should space retries after a deferral rather than tight-looping them.

worstCaseReviewSeconds (2,020 s at default limits, shown in the dry run) is the worst-case wall clock for one review's finder and audit retry chains. Operators overriding --timeout should keep it above this figure.