package com.correx.core.events.orchestration

import com.correx.core.events.execution.RetryPolicy
import com.correx.core.events.types.StageId
import kotlinx.serialization.Serializable

@Serializable
data class OrchestrationState(
    val workflowId: String = "",
    val currentStageId: StageId? = null,
    val status: OrchestrationStatus = OrchestrationStatus.IDLE,
    val retryCount: Int = 0,
    val pauseReason: String? = null,
    val pendingApproval: Boolean = false,
    val failureReason: String? = null,
    val retryPolicy: RetryPolicy? = null,
    val refinementIterations: Map<String, Int> = emptyMap(),
    // Per-gate retry budget (design 2026-07-06-per-gate-retry-budgets.md): each gate id (produces,
    // brief_grounding, brief_echo, contract, plan_compile, static_analysis, execution, review, ...)
    // exhausts its own budget rather than sharing retryCount session-wide. Attempts charged so far.
    val gateRetryBudgets: Map<String, Int> = emptyMap(),
    // Last-seen failure fingerprint per gate, used to tell a no-progress retry (same fingerprint,
    // charged) from a genuine-progress retry (changed fingerprint, free).
    val gateFailureFingerprints: Map<String, String> = emptyMap(),
    // Gates that have already spent their one hybrid-exhaustion salvage reset (review gate only,
    // see RetrySalvageDecidedEvent) — a second exhaustion for that gate is terminal.
    val gateSalvageUsed: Set<String> = emptySet(),
    // Recovery-routing budget (retry-agency / ticket model): how many times each stage id has been
    // routed to a recovery stage because it failed a gate it lacked the capability to fix (see
    // FailureTicketOpenedEvent). Deliberately NOT reset by TransitionExecutedEvent — the
    // verification→recovery→verification loop must stay bounded across those transitions, exactly as
    // refinementIterations does for back-edge loops.
    val recoveryRoutes: Map<String, Int> = emptyMap(),
    // Last-seen failure fingerprint per recovering stage id — the recovery-routing analogue of
    // gateFailureFingerprints. Lets routeToRecovery tell a no-progress route (same fingerprint,
    // charged against recoveryRoutes) from a genuine-progress one (changed fingerprint, free), so a
    // recovery that fixes one cause and surfaces the next is not killed by the small route budget.
    // Like recoveryRoutes, NOT reset by TransitionExecutedEvent — it must survive the
    // verification→recovery→verification round-trip.
    val recoveryFailureFingerprints: Map<String, String> = emptyMap(),
)
