package com.correx.core.kernel.orchestration

import com.correx.core.events.events.CapabilityGapVerdict
import com.correx.core.events.types.SessionId
import com.correx.core.tools.contract.ToolCapability

/** One capability gap finding from [com.correx.infrastructure.workflow.CapabilityGapDetector], as
 * plain data so this module does not depend on infrastructure:workflow. */
data class CapabilityGapInput(
    val stageId: String,
    val impliedCapability: ToolCapability,
    val phrase: String,
    val grantedCapabilities: Set<ToolCapability>,
)

/** Result of reflecting on a single [CapabilityGapInput]. */
data class CapabilityGapReflection(
    val stageId: String,
    val impliedCapability: ToolCapability,
    val verdict: CapabilityGapVerdict,
    val detail: String,
)

/**
 * Seam for the Vikunja #30 part 2 reflection rung: consulted once, bounded, on the capability gaps
 * a plan-compile pass turned up, before any of them are surfaced to the operator. Like the other gate
 * seams ([SemanticReviewer], [SalvageJudge]), the inference-touching implementation is injected so
 * the deterministic core never runs inference itself and the caller stays unit-testable with a fake.
 *
 * A single call covers the whole batch of gaps for one plan — there is no per-gap loop and no retry;
 * the caller records the result as a [com.correx.core.events.events.CapabilityGapReflectedEvent] per
 * gap and never re-invokes this on replay (invariant #8 — the reflection is nondeterministic,
 * invariant #9 requires recording the outcome, not re-observing it).
 */
fun interface CapabilityGapReflector {
    suspend fun reflect(sessionId: SessionId, gaps: List<CapabilityGapInput>): List<CapabilityGapReflection>
}
