package com.correx.core.transitions.graph

import com.correx.core.artifacts.kind.TypedArtifactSlot
import com.correx.core.events.types.ArtifactId
import com.correx.core.inference.GenerationConfig
import com.correx.core.inference.ModelCapability

data class StageConfig(
    val requiredCapabilities: Set<ModelCapability> = emptySet(),
    val modelId: String? = null,
    val tokenBudget: Int = 4096,
    val generationConfig: GenerationConfig = GenerationConfig(
        temperature = 0.7,
        topP = 1.0,
        maxTokens = 2048,
    ),
    val allowedTools: Set<String> = emptySet(),
    val maxRetries: Int = 3,
    val produces: List<TypedArtifactSlot> = emptyList(),
    val needs: Set<ArtifactId> = emptySet(),
    // Workspace-relative globs the stage is permitted to write (role-reliability §2
    // diff manifest). Empty = unrestricted within the workspace. A FILE_WRITE outside
    // the declared set is scope creep — the dominant local-implementer failure — and is
    // blocked by the plane-2 ManifestContainmentRule.
    val writeManifest: List<String> = emptyList(),
    // Operator-configured static-analysis commands (compiler/detekt/formatters) run as a
    // deterministic harness step after this stage produces its output (role-reliability §5).
    // A non-clean command fails the stage retryably with its output fed back, so only
    // static-clean output reaches the downstream reviewer. Empty = no static-first step.
    val staticAnalysis: List<String> = emptyList(),
    // Execution-gate scope for this stage (staged-verification §2). NONE = no build gate (default,
    // e.g. early scaffold/types stages); MODULE/PROJECT/TESTS resolve to the bound project profile's
    // typecheck/build/test command and run it as a deterministic Gate 4 after the stage produces.
    val buildExpectation: BuildExpectation = BuildExpectation.NONE,
    // Concrete (non-glob) workspace-relative paths this stage declares it will produce (Gate 2
    // Option A, staged-verification §3.2). The contract gate checks each declared file exists and
    // satisfies its inferred-kind contract, so a file the plan promised but the stage never wrote
    // FAILS the gate — catching missing files, which the runtime write-manifest alone (it only
    // inspects files that WERE written) cannot. Derived from the concrete entries of PlanStage.writes.
    val expectedFiles: List<String> = emptyList(),
    // Opt-in to the Gate 3 semantic (LLM) reviewer for this stage (staged-verification § Gate 3). When
    // true and a reviewer is wired, the reviewer reads this stage's produced files after the
    // deterministic funnel passes and raises advisory PR-comment findings; a high-confidence
    // correctness FAIL can block retryably until the review-block budget is spent. Default off.
    val semanticReview: Boolean = false,
    val metadata: Map<String, String> = emptyMap(),
)
