package com.correx.core.kernel.retry

// Collapses whitespace runs and digit runs so line numbers / byte counts / timestamps embedded in a
// gate's failure reason don't make two otherwise-identical failures look like "progress". Gate
// reasons already list the failing assertion/finding ids verbatim (see runContractGate,
// runStaticAnalysis, runReviewGate in SessionOrchestrator), so this normalisation is sufficient to
// treat "same root cause" retries as no-progress without needing per-gate parsing.
private val VOLATILE_RUN = Regex("""[\s\d]+""")

/**
 * Stable fingerprint of a [StageExecutionResult.Failure] reason (design
 * 2026-07-06-per-gate-retry-budgets.md §5). Two failures with the same fingerprint are treated as
 * "no progress" for per-gate retry budgeting; a changed fingerprint means the run is moving and the
 * retry is free.
 */
object FailureFingerprint {
    fun of(reason: String): String {
        val normalized = reason.trim().lowercase().replace(VOLATILE_RUN, " ").trim()
        return Integer.toHexString(normalized.hashCode())
    }
}
