Payload/scripts/kaden/harness/quality-gate/RepositoryQualityGate.psm1

#Requires -Version 5.1
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$script:EvidenceFreshnessLimit = [timespan]::FromHours(24)
$script:ProtectedDestination = 'main'
$script:ProtectedIntegrationKinds = @('direct-update', 'merge')
$script:EmergencyMaintainerRole = 'repository-maintainer'
$script:EmergencyScope = 'local-advisory'

$script:RepositoryCheckCatalog = @(
    @{
        Id                  = 'comprehensive-pester'
        RulePrefix          = 'pester-runner-v1'
        FailureReason       = 'full-check-failed'
        OutcomeKind         = 'counter'
        CounterName         = 'FailedCount'
        MissingCounterInput = 'failed-count'
    }
    @{
        Id                  = 'pester-test-quality'
        RulePrefix          = 'pester-test-quality-v1'
        FailureReason       = 'test-quality-semgrep-failed'
        OutcomeKind         = 'counter'
        CounterName         = 'BlockingFindingCount'
        MissingCounterInput = 'finding-count'
        FindingCountName    = 'BlockingFindingCount'
    }
    @{
        Id                  = 'powershell-security'
        RulePrefix          = 'powershell-security.semgrep.yml'
        FailureReason       = 'security-semgrep-failed'
        OutcomeKind         = 'counter'
        CounterName         = 'ErrorFindingCount'
        MissingCounterInput = 'error-finding-count'
    }
    @{
        Id                  = 'trivy-secrets'
        RulePrefix          = 'trivy-secrets-v1'
        FailureReason       = 'secret-check-failed'
        OutcomeKind         = 'counter'
        CounterName         = 'SecretFindingCount'
        MissingCounterInput = 'secret-finding-count'
    }
    @{
        Id            = 'powershell-quality'
        RulePrefix    = 'PSScriptAnalyzerSettings.psd1'
        FailureReason = 'PowerShell-quality-failed'
        OutcomeKind   = 'quality'
    }
    @{
        Id                            = 'agent-skill-behavior'
        RulePrefix                    = 'deterministic-eval-runner-v1'
        FailureReason                 = 'behavioral-coverage-gap'
        OutcomeKind                   = 'behavioral'
        ApplicableWhenTargetNonEmpty  = $true
    }
    @{
        Id                            = 'ai-surface-sync'
        RulePrefix                    = 'ai-surface-pair-v1'
        FailureReason                 = 'AI-path-unclassified'
        OutcomeKind                   = 'surface'
        ApplicableWhenTargetNonEmpty  = $true
    }
)

function Get-QualityEvidenceEntry {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    if ($null -eq $Evidence) {
        return [pscustomobject]@{ Present = $false; Value = $null }
    }
    if (-not ($Evidence -is [System.Collections.IDictionary])) {
        return [pscustomobject]@{ Present = $false; Value = $null }
    }
    if (-not $Evidence.Contains($Name)) {
        return [pscustomobject]@{ Present = $false; Value = $null }
    }
    return [pscustomobject]@{ Present = $true; Value = $Evidence[$Name] }
}

function Get-RepositoryCheckDefinition {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check
    )
    foreach ($row in @($script:RepositoryCheckCatalog)) {
        if ([string]::Equals([string]$row.Id, $Check, [System.StringComparison]::Ordinal)) {
            return $row
        }
    }
    return $null
}

function Get-QualityCount {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name
    if (-not $entry.Present) {
        return $null
    }
    return [int]$entry.Value
}

function Get-QualityFlag {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name
    if (-not $entry.Present) {
        return $false
    }
    return [bool]$entry.Value
}

function Test-SuccessfulComprehensiveResult {
    param($Evidence)
    $count = Get-QualityCount -Evidence $Evidence -Name 'FailedCount'
    if ($null -eq $count) {
        return $false
    }
    return ($count -eq 0)
}

function Get-QualityText {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name
    if (-not $entry.Present -or $null -eq $entry.Value) {
        return $null
    }
    return [string]$entry.Value
}

function Get-QualityTime {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name
    if (-not $entry.Present) {
        return $null
    }
    $value = $entry.Value
    if ($null -eq $value) {
        return $null
    }
    if ($value -is [datetime]) {
        return [datetime]$value
    }
    try {
        return [datetime]$value
    }
    catch {
        return $null
    }
}

function Get-QualityList {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$Name
    )
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name
    if (-not $entry.Present -or $null -eq $entry.Value) {
        return $null
    }
    return @($entry.Value)
}

function ConvertTo-GatePath {
    param([string]$Path)
    if ([string]::IsNullOrWhiteSpace($Path)) {
        return ''
    }
    $text = $Path.Replace('\', '/')
    while ($text.StartsWith('./')) {
        $text = $text.Substring(2)
    }
    return $text
}

function Test-SameGatePath {
    param(
        [string]$Left,
        [string]$Right
    )
    $leftPath = ConvertTo-GatePath $Left
    $rightPath = ConvertTo-GatePath $Right
    return [string]::Equals($leftPath, $rightPath, [System.StringComparison]::OrdinalIgnoreCase)
}

function Test-SameTargetSet {
    param($Left, $Right)
    if ($null -eq $Left -or $null -eq $Right) {
        return $false
    }
    $leftItems = @($Left | ForEach-Object { ConvertTo-GatePath ([string]$_) })
    $rightItems = @($Right | ForEach-Object { ConvertTo-GatePath ([string]$_) })
    if ($leftItems.Count -ne $rightItems.Count) {
        return $false
    }
    $remaining = New-Object System.Collections.Generic.List[string]
    foreach ($item in $rightItems) {
        [void]$remaining.Add($item)
    }
    foreach ($item in $leftItems) {
        $found = -1
        for ($index = 0; $index -lt $remaining.Count; $index++) {
            if (Test-SameGatePath -Left $remaining[$index] -Right $item) {
                $found = $index
                break
            }
        }
        if ($found -lt 0) {
            return $false
        }
        $remaining.RemoveAt($found)
    }
    return ($remaining.Count -eq 0)
}

function Test-PrefixedGatePath {
    param(
        [string]$Path,
        [string[]]$Prefixes
    )
    $text = ConvertTo-GatePath $Path
    foreach ($prefix in $Prefixes) {
        $root = $prefix.TrimEnd('/')
        if (Test-SameGatePath -Left $text -Right $root) {
            return $true
        }
        if ($text.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) {
            return $true
        }
    }
    return $false
}

function Test-PowerShellScriptPath {
    param([string]$Path)
    $extension = [System.IO.Path]::GetExtension((ConvertTo-GatePath $Path))
    if ([string]::IsNullOrWhiteSpace($extension)) {
        return $false
    }
    return @('.ps1', '.psm1', '.psd1') -contains $extension.ToLowerInvariant()
}

function Test-PathInTargetSet {
    param(
        $TargetSet,
        [string]$Path
    )
    $needle = ConvertTo-GatePath $Path
    foreach ($target in @($TargetSet)) {
        $candidate = ConvertTo-GatePath ([string]$target)
        if (Test-SameGatePath -Left $candidate -Right $needle) {
            return $true
        }
    }
    return $false
}

function Test-PesterInvalidatingChange {
    param(
        $ChangedPaths,
        $CurrentTargetSet
    )
    $contractPrefixes = @(
        'tests/harness/',
        'scripts/',
        'templates/',
        '.github/',
        '.cursor/',
        '.husky/',
        '.devcontainer/',
        'docs/kaden/'
    )
    $documentationPrefixes = @(
        'docs/brand/',
        'docs/retros/',
        'docs/standards/'
    )
    foreach ($path in @($ChangedPaths)) {
        $text = [string]$path
        if ([string]::IsNullOrWhiteSpace($text)) {
            continue
        }
        $scriptChange = Test-PowerShellScriptPath $text
        $contractChange = Test-PrefixedGatePath -Path $text -Prefixes $contractPrefixes
        $documentationChange = Test-PrefixedGatePath -Path $text -Prefixes $documentationPrefixes
        $insideTargetSet = Test-PathInTargetSet -TargetSet $CurrentTargetSet -Path $text
        if ($scriptChange -or $contractChange) {
            return $true
        }
        if ($documentationChange -and $insideTargetSet) {
            return $true
        }
    }
    return $false
}

function Test-DeclaredRuleIdentity {
    param(
        [string]$Check,
        [string]$RuleIdentity
    )
    $definition = Get-RepositoryCheckDefinition -Check $Check
    if ($null -eq $definition) {
        return $false
    }
    if ([string]::IsNullOrWhiteSpace($RuleIdentity)) {
        return $false
    }
    return $RuleIdentity.StartsWith([string]$definition.RulePrefix, [System.StringComparison]::Ordinal)
}

function Get-CheckFailureReason {
    param([string]$Check)
    $definition = Get-RepositoryCheckDefinition -Check $Check
    if ($null -eq $definition) {
        return 'required-check-missing'
    }
    return [string]$definition.FailureReason
}

function Get-EvidenceFreshness {
    param(
        [datetime]$CompletedAt,
        [datetime]$RequestedAt
    )
    $age = $RequestedAt - $CompletedAt
    $limit = $script:EvidenceFreshnessLimit
    if ($age -lt [timespan]::Zero) {
        return 'unknown'
    }
    if ($age -gt $limit) {
        return 'stale'
    }
    return 'fresh'
}

function Get-RejectionValue {
    param(
        [hashtable]$Rejection,
        [string]$Name
    )
    if ($null -eq $Rejection -or -not $Rejection.Contains($Name)) {
        return $null
    }
    return $Rejection[$Name]
}

function Get-RejectedCheckResult {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        [hashtable]$Rejection
    )
    $reasons = @()
    $reasonValue = Get-RejectionValue -Rejection $Rejection -Name 'Reasons'
    if ($null -ne $reasonValue) {
        $reasons = @($reasonValue)
    }
    return [pscustomobject]@{
        Check                  = $Check
        CheckAccepted          = $false
        EvidenceClass          = 'failed-evidence'
        Freshness              = Get-RejectionValue -Rejection $Rejection -Name 'Freshness'
        ProvenanceRevision     = $null
        MissingInput           = Get-RejectionValue -Rejection $Rejection -Name 'MissingInput'
        RequiredRun            = Get-RejectionValue -Rejection $Rejection -Name 'RequiredRun'
        FailedCheck            = $Check
        AffectedTarget         = Get-RejectionValue -Rejection $Rejection -Name 'AffectedTarget'
        Condition              = Get-RejectionValue -Rejection $Rejection -Name 'Condition'
        Reasons                = $reasons
        TargetCount            = $null
        FindingCount           = $null
        ErrorFindingCount      = $null
        SecretFindingCount     = $null
        FixtureContents        = $null
        CapturedOutput         = $null
        DeterministicScore     = $null
        CompletedAt            = $null
        BehaviorClassification = $null
        BehaviorTargetSet      = $null
    }
}

function Get-AcceptedCheckResult {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        [Parameter(Mandatory = $true)]
        [string]$EvidenceClass,
        [Parameter(Mandatory = $true)]
        [string]$ProvenanceRevision,
        $Evidence
    )
    $findingCount = $null
    $definition = Get-RepositoryCheckDefinition -Check $Check
    if (($null -ne $definition) -and $definition.Contains('FindingCountName')) {
        $findingCount = Get-QualityCount -Evidence $Evidence -Name ([string]$definition.FindingCountName)
    }
    $behaviorTargetSet = $null
    $fixtureContents = $null
    $capturedOutput = $null
    $deterministicScore = $null
    $completedAt = $null
    $behaviorClassification = $null
    if ($null -ne $definition) {
        $kind = [string]$definition.OutcomeKind
        if (($kind -eq 'behavioral') -or ($kind -eq 'surface')) {
            $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
            if ($null -eq $targets) {
                $behaviorTargetSet = @()
            }
            else {
                $behaviorTargetSet = @($targets)
            }
            $behaviorClassification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification'
            $completedAt = Get-QualityTime -Evidence $Evidence -Name 'CompletedAt'
        }
        if ($kind -eq 'behavioral') {
            $fixtures = Get-QualityList -Evidence $Evidence -Name 'FixtureContents'
            if ($null -eq $fixtures) {
                $fixtureContents = @()
            }
            else {
                $fixtureContents = @($fixtures)
            }
            $capturedOutput = Get-QualityText -Evidence $Evidence -Name 'CapturedOutput'
            $deterministicScore = Get-QualityText -Evidence $Evidence -Name 'DeterministicScore'
        }
    }
    return [pscustomobject]@{
        Check                  = $Check
        CheckAccepted          = $true
        EvidenceClass          = $EvidenceClass
        Freshness              = 'fresh'
        ProvenanceRevision     = $ProvenanceRevision
        MissingInput           = $null
        RequiredRun            = $null
        FailedCheck            = $null
        AffectedTarget         = $null
        Condition              = $null
        Reasons                = @()
        TargetCount            = Get-QualityCount -Evidence $Evidence -Name 'TargetCount'
        FindingCount           = $findingCount
        ErrorFindingCount      = Get-QualityCount -Evidence $Evidence -Name 'ErrorFindingCount'
        SecretFindingCount     = Get-QualityCount -Evidence $Evidence -Name 'SecretFindingCount'
        FixtureContents        = $fixtureContents
        CapturedOutput         = $capturedOutput
        DeterministicScore     = $deterministicScore
        CompletedAt            = $completedAt
        BehaviorClassification = $behaviorClassification
        BehaviorTargetSet      = $behaviorTargetSet
    }
}

function Copy-CheckOntoDecision {
    param(
        $Decision,
        $Result
    )
    $Decision.CheckAccepted = $Result.CheckAccepted
    $Decision.EvidenceClass = $Result.EvidenceClass
    $Decision.Freshness = $Result.Freshness
    $Decision.ProvenanceRevision = $Result.ProvenanceRevision
    $Decision.MissingInput = $Result.MissingInput
    $Decision.RequiredRun = $Result.RequiredRun
    $Decision.FailedCheck = $Result.FailedCheck
    $Decision.AffectedTarget = $Result.AffectedTarget
    $Decision.Condition = $Result.Condition
    $Decision.Reasons = @($Result.Reasons)
    $Decision.TargetCount = $Result.TargetCount
    $Decision.FindingCount = $Result.FindingCount
    $Decision.ErrorFindingCount = $Result.ErrorFindingCount
    $Decision.SecretFindingCount = $Result.SecretFindingCount
    $Decision.FixtureContents = $Result.FixtureContents
    $Decision.CapturedOutput = $Result.CapturedOutput
    $Decision.DeterministicScore = $Result.DeterministicScore
    $Decision.CompletedAt = $Result.CompletedAt
    $Decision.BehaviorClassification = $Result.BehaviorClassification
    $Decision.BehaviorTargetSet = $Result.BehaviorTargetSet
}

function Get-CheckEvidenceRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    if ($null -eq $Evidence) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition = 'missing'
            Reasons   = @('required-check-missing')
        }
    }

    if (-not (Test-AiEvidenceContract -Check $Check -Evidence $Evidence)) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'trusted-adapter-evidence'
        }
    }

    if ((Get-ExpectedAiEvidenceSource -Check $Check) -and (Test-ChangedPathsIncludeAiSurface -Evidence $Evidence)) {
        $aiTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
        if (($null -eq $aiTargets) -or (@($aiTargets).Count -eq 0)) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition      = 'incomplete'
                Reasons        = @(Get-CheckFailureReason -Check $Check)
                AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
            }
        }
    }

    $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
    if ($null -eq $currentTargets) {
        $declaredCount = Get-QualityCount -Evidence $Evidence -Name 'TargetCount'
        if (($null -ne $declaredCount) -and ($declaredCount -le 0)) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition = 'incomplete'
                Reasons   = @(Get-CheckFailureReason -Check $Check)
            }
        }
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'target-set'
        }
    }

    $currentRule = Get-QualityText -Evidence $Evidence -Name 'CurrentRuleIdentity'
    $ruleKnown = -not [string]::IsNullOrWhiteSpace($currentRule)
    if ($ruleKnown) {
        $ruleKnown = Test-DeclaredRuleIdentity -Check $Check -RuleIdentity $currentRule
    }
    if (-not $ruleKnown) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'rule-identity'
        }
    }

    if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'CompletedAt')) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'completion-time'
        }
    }
    if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'RequestedAt')) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'requested-at'
        }
    }
    if ([string]::IsNullOrWhiteSpace((Get-QualityText -Evidence $Evidence -Name 'ProtectedRevision'))) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'protected-revision'
        }
    }
    return $null
}

function Get-CheckFreshnessRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $completedAt = Get-QualityTime -Evidence $Evidence -Name 'CompletedAt'
    $requestedAt = Get-QualityTime -Evidence $Evidence -Name 'RequestedAt'
    $freshness = Get-EvidenceFreshness -CompletedAt $completedAt -RequestedAt $requestedAt
    if ($freshness -eq 'unknown') {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'completion-time'
        }
    }
    if ($freshness -eq 'stale') {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition   = 'stale'
            Reasons     = @('evidence-stale')
            Freshness   = 'stale'
            RequiredRun = $Check
        }
    }
    return $null
}

function Get-CheckIdentityRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
    $recordedTargets = Get-QualityList -Evidence $Evidence -Name 'TargetSet'
    $sameTargets = Test-SameTargetSet -Left $recordedTargets -Right $currentTargets
    $sameRule = [string]::Equals(
        (Get-QualityText -Evidence $Evidence -Name 'RuleIdentity'),
        (Get-QualityText -Evidence $Evidence -Name 'CurrentRuleIdentity'),
        [System.StringComparison]::Ordinal
    )
    $presentedAgain = Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain'
    $changedPaths = Get-QualityList -Evidence $Evidence -Name 'ChangedPaths'
    if ($null -eq $changedPaths) {
        $changedPaths = @()
    }
    if (($Check -eq 'comprehensive-pester') -and $presentedAgain) {
        if (Test-PesterInvalidatingChange -ChangedPaths $changedPaths -CurrentTargetSet $currentTargets) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition   = 'stale'
                Reasons     = @('evidence-stale')
                Freshness   = 'stale'
                RequiredRun = 'comprehensive-pester'
            }
        }
    }
    if ((-not $sameTargets) -or (-not $sameRule)) {
        if ($presentedAgain) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition   = 'stale'
                Reasons     = @('evidence-stale')
                Freshness   = 'stale'
                RequiredRun = $Check
            }
        }
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition = 'incomplete'
            Reasons   = @(Get-CheckFailureReason -Check $Check)
        }
    }
    return $null
}

function Get-CheckCoverageRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
    $scannedTargets = Get-QualityList -Evidence $Evidence -Name 'ScannedTargetSet'
    $scannedComplete = $false
    if ($null -ne $scannedTargets) {
        $scannedComplete = Test-SameTargetSet -Left $scannedTargets -Right $currentTargets
    }
    if (-not $scannedComplete) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition = 'incomplete'
            Reasons   = @(Get-CheckFailureReason -Check $Check)
        }
    }
    $targetCount = Get-QualityCount -Evidence $Evidence -Name 'TargetCount'
    if ($null -eq $targetCount) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'target-count'
        }
    }
    if ($targetCount -le 0) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition = 'incomplete'
            Reasons   = @(Get-CheckFailureReason -Check $Check)
        }
    }
    return $null
}

function Get-FirstGateTarget {
    param($Evidence)
    $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
    if ($null -eq $targets) {
        return $null
    }
    foreach ($target in @($targets)) {
        $text = [string]$target
        if (-not [string]::IsNullOrWhiteSpace($text)) {
            return (ConvertTo-GatePath $text)
        }
    }
    return $null
}

function Test-BehaviorClassificationAllowed {
    param([string]$Classification)
    if ([string]::IsNullOrWhiteSpace($Classification)) {
        return $false
    }
    foreach ($allowed in @('behavior-bearing', 'non-behavior-bearing')) {
        if ([string]::Equals($Classification, $allowed, [System.StringComparison]::Ordinal)) {
            return $true
        }
    }
    return $false
}

function Get-ExpectedAiEvidenceSource {
    param([string]$Check)
    switch ($Check) {
        'agent-skill-behavior' { return 'deterministic-eval-runner' }
        'ai-surface-sync' { return 'canonical-generated-pair' }
        default { return $null }
    }
}

function Test-AiEvidenceContract {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $expectedSource = Get-ExpectedAiEvidenceSource -Check $Check
    if ($null -eq $expectedSource) {
        return $true
    }
    $adapterAttested = Get-QualityFlag -Evidence $Evidence -Name 'AdapterAttested'
    $applicabilitySource = Get-QualityText -Evidence $Evidence -Name 'ApplicabilitySource'
    $evidenceSource = Get-QualityText -Evidence $Evidence -Name 'EvidenceSource'
    return (
        $adapterAttested -and
        [string]::Equals($applicabilitySource, 'changed-path-policy', [System.StringComparison]::Ordinal) -and
        [string]::Equals($evidenceSource, $expectedSource, [System.StringComparison]::Ordinal)
    )
}

function Test-ChangedPathsIncludeAiSurface {
    param($Evidence)
    $changed = Get-QualityList -Evidence $Evidence -Name 'ChangedPaths'
    foreach ($path in @($changed)) {
        if (Test-PrefixedGatePath -Path ([string]$path) -Prefixes @('.github/', '.cursor/')) {
            return $true
        }
    }
    return $false
}

function Test-OptionalCheckWithoutTarget {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $definition = Get-RepositoryCheckDefinition -Check $Check
    if ($null -eq $definition) {
        return $false
    }
    if (-not (Test-AiEvidenceContract -Check $Check -Evidence $Evidence)) {
        return $false
    }
    if (Test-ChangedPathsIncludeAiSurface -Evidence $Evidence) {
        return $false
    }
    $optional = $false
    if ($definition.Contains('ApplicableWhenTargetNonEmpty')) {
        $optional = [bool]$definition.ApplicableWhenTargetNonEmpty
    }
    if (-not $optional) {
        return $false
    }
    $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet'
    if ($null -ne $targets) {
        return (@($targets).Count -eq 0)
    }
    $count = Get-QualityCount -Evidence $Evidence -Name 'TargetCount'
    return (($null -ne $count) -and ($count -le 0))
}

function Get-NotApplicableCheckResult {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check
    )
    return [pscustomobject]@{
        Check                  = $Check
        CheckAccepted          = $true
        EvidenceClass          = $null
        Freshness              = $null
        ProvenanceRevision     = $null
        MissingInput           = $null
        RequiredRun            = $null
        FailedCheck            = $null
        AffectedTarget         = $null
        Condition              = 'not-applicable'
        Reasons                = @()
        TargetCount            = 0
        FindingCount           = $null
        ErrorFindingCount      = $null
        SecretFindingCount     = $null
        FixtureContents        = $null
        CapturedOutput         = $null
        DeterministicScore     = $null
        CompletedAt            = $null
        BehaviorClassification = $null
        BehaviorTargetSet      = @()
    }
}

function Get-BehavioralCoverageRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        [Parameter(Mandatory = $true)]
        [string]$Condition,
        $Evidence
    )
    return Get-RejectedCheckResult -Check $Check -Rejection @{
        Condition      = $Condition
        Reasons        = @(Get-CheckFailureReason -Check $Check)
        AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
    }
}

function Get-BehavioralOutcomeRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    if (Get-QualityFlag -Evidence $Evidence -Name 'StaticContentCheckOnly') {
        return Get-BehavioralCoverageRejection -Check $Check -Condition 'static-content-check' -Evidence $Evidence
    }
    $fixtures = Get-QualityList -Evidence $Evidence -Name 'FixtureContents'
    $fixtureCount = 0
    if ($null -ne $fixtures) {
        $fixtureCount = @($fixtures).Count
    }
    if ($fixtureCount -le 0) {
        return Get-BehavioralCoverageRejection -Check $Check -Condition 'coverage-gap' -Evidence $Evidence
    }
    $captured = Get-QualityText -Evidence $Evidence -Name 'CapturedOutput'
    if ([string]::IsNullOrWhiteSpace($captured)) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition      = 'incomplete'
            Reasons        = @('evidence-input-unknown')
            MissingInput   = 'captured-output'
            AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
        }
    }
    $score = Get-QualityText -Evidence $Evidence -Name 'DeterministicScore'
    if ([string]::IsNullOrWhiteSpace($score)) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition      = 'incomplete'
            Reasons        = @('evidence-input-unknown')
            MissingInput   = 'deterministic-score'
            AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
        }
    }
    if (-not [string]::Equals($score, 'pass', [System.StringComparison]::Ordinal)) {
        return Get-BehavioralCoverageRejection -Check $Check -Condition 'score-failed' -Evidence $Evidence
    }
    $presentedAgain = Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain'
    $classification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification'
    $behaviorBearing = [string]::Equals($classification, 'behavior-bearing', [System.StringComparison]::Ordinal)
    if ($presentedAgain -and $behaviorBearing) {
        return Get-BehavioralCoverageRejection -Check $Check -Condition 'capture-not-new' -Evidence $Evidence
    }
    if ((-not $presentedAgain) -and (-not (Get-QualityFlag -Evidence $Evidence -Name 'NewlyCaptured'))) {
        return Get-BehavioralCoverageRejection -Check $Check -Condition 'capture-not-new' -Evidence $Evidence
    }
    return $null
}

function Get-SurfaceOutcomeRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $classification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification'
    if (-not (Test-BehaviorClassificationAllowed -Classification $classification)) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition      = 'unclassified'
            Reasons        = @(Get-CheckFailureReason -Check $Check)
            AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
        }
    }
    if (-not (Get-QualityFlag -Evidence $Evidence -Name 'SurfacesSynchronized')) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition      = 'unsynchronized'
            Reasons        = @('AI-surface-unsynchronized')
            AffectedTarget = Get-FirstGateTarget -Evidence $Evidence
        }
    }
    return $null
}

function Get-CheckOutcomeRejection {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    $definition = Get-RepositoryCheckDefinition -Check $Check
    if ($null -eq $definition) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition = 'missing'
            Reasons   = @('required-check-missing')
        }
    }
    $kind = [string]$definition.OutcomeKind
    if ($kind -eq 'behavioral') {
        return Get-BehavioralOutcomeRejection -Check $Check -Evidence $Evidence
    }
    if ($kind -eq 'surface') {
        return Get-SurfaceOutcomeRejection -Check $Check -Evidence $Evidence
    }
    if ($kind -eq 'counter') {
        $count = Get-QualityCount -Evidence $Evidence -Name ([string]$definition.CounterName)
        if ($null -eq $count) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition    = 'incomplete'
                Reasons      = @('evidence-input-unknown')
                MissingInput = [string]$definition.MissingCounterInput
            }
        }
        if ($count -gt 0) {
            return Get-RejectedCheckResult -Check $Check -Rejection @{
                Condition = 'failed'
                Reasons   = @([string]$definition.FailureReason)
            }
        }
        return $null
    }

    $analyzerCount = Get-QualityCount -Evidence $Evidence -Name 'AnalyzerFindingCount'
    $encodingCount = Get-QualityCount -Evidence $Evidence -Name 'EncodingFaultCount'
    if (($null -eq $analyzerCount) -or ($null -eq $encodingCount)) {
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition    = 'incomplete'
            Reasons      = @('evidence-input-unknown')
            MissingInput = 'quality-finding-count'
        }
    }
    if (($analyzerCount -gt 0) -or ($encodingCount -gt 0)) {
        $condition = Get-QualityText -Evidence $Evidence -Name 'Condition'
        if ([string]::IsNullOrWhiteSpace($condition)) {
            if ($analyzerCount -gt 0) {
                $condition = 'analysis-finding'
            }
            else {
                $condition = 'encoding-fault'
            }
        }
        return Get-RejectedCheckResult -Check $Check -Rejection @{
            Condition      = $condition
            Reasons        = @([string]$definition.FailureReason)
            AffectedTarget = Get-QualityText -Evidence $Evidence -Name 'AffectedTarget'
        }
    }
    return $null
}

function Get-RepositoryCheckResult {
    param(
        [Parameter(Mandatory = $true)]
        [string]$Check,
        $Evidence
    )
    if (Test-OptionalCheckWithoutTarget -Check $Check -Evidence $Evidence) {
        return Get-NotApplicableCheckResult -Check $Check
    }
    $incomplete = Get-CheckEvidenceRejection -Check $Check -Evidence $Evidence
    if ($null -ne $incomplete) {
        return $incomplete
    }
    $stale = Get-CheckFreshnessRejection -Check $Check -Evidence $Evidence
    if ($null -ne $stale) {
        return $stale
    }
    $identity = Get-CheckIdentityRejection -Check $Check -Evidence $Evidence
    if ($null -ne $identity) {
        return $identity
    }
    $coverage = Get-CheckCoverageRejection -Check $Check -Evidence $Evidence
    if ($null -ne $coverage) {
        return $coverage
    }
    $outcome = Get-CheckOutcomeRejection -Check $Check -Evidence $Evidence
    if ($null -ne $outcome) {
        return $outcome
    }

    $evidenceClass = 'new-full-evidence'
    if (Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain') {
        $evidenceClass = 'reused-full-evidence'
    }
    return Get-AcceptedCheckResult -Check $Check -EvidenceClass $evidenceClass -ProvenanceRevision (Get-QualityText -Evidence $Evidence -Name 'ProtectedRevision') -Evidence $Evidence
}

function Get-ManifestCheckResult {
    param([hashtable]$ManifestResults)
    foreach ($row in @($script:RepositoryCheckCatalog)) {
        $name = [string]$row.Id
        if (-not $ManifestResults.Contains($name)) {
            continue
        }
        Write-Output (Get-RepositoryCheckResult -Check $name -Evidence $ManifestResults[$name])
    }
}

function Test-ProtectedBoundary {
    param(
        [string]$Destination,
        [string]$IntegrationKind
    )
    $onMain = [string]::Equals($Destination, $script:ProtectedDestination, [System.StringComparison]::Ordinal)
    foreach ($kind in @($script:ProtectedIntegrationKinds)) {
        $kindMatches = [string]::Equals($IntegrationKind, [string]$kind, [System.StringComparison]::Ordinal)
        if ($onMain -and $kindMatches) {
            return $true
        }
    }
    return $false
}

function Get-RepositoryQualityCheckCatalog {
    foreach ($row in @($script:RepositoryCheckCatalog)) {
        Write-Output ([pscustomobject]@{
                Id         = [string]$row.Id
                RulePrefix = [string]$row.RulePrefix
            })
    }
}

function Get-FirstMissingCatalogCheck {
    param([hashtable]$ManifestResults)
    foreach ($row in @($script:RepositoryCheckCatalog)) {
        $name = [string]$row.Id
        $present = $false
        if ($null -ne $ManifestResults) {
            $present = $ManifestResults.Contains($name)
        }
        if (-not $present) {
            return Get-RejectedCheckResult -Check $name -Rejection @{
                Condition = 'missing'
                Reasons   = @('required-check-missing')
            }
        }
    }
    return $null
}

function Get-EmergencyPartyGap {
    param(
        $Evidence,
        [Parameter(Mandatory = $true)]
        [string]$NameKey,
        [Parameter(Mandatory = $true)]
        [string]$RoleKey,
        [Parameter(Mandatory = $true)]
        [string]$MissingNameToken,
        [Parameter(Mandatory = $true)]
        [string]$MissingRoleToken
    )
    $name = Get-QualityText -Evidence $Evidence -Name $NameKey
    if ([string]::IsNullOrWhiteSpace($name)) {
        return $MissingNameToken
    }
    $role = Get-QualityText -Evidence $Evidence -Name $RoleKey
    $roleKnown = [string]::Equals($role, $script:EmergencyMaintainerRole, [System.StringComparison]::Ordinal)
    if (-not $roleKnown) {
        return $MissingRoleToken
    }
    return $null
}

function Get-EmergencyRecordGap {
    param($Evidence)
    $revision = Get-QualityText -Evidence $Evidence -Name 'Revision'
    if ([string]::IsNullOrWhiteSpace($revision)) {
        return 'revision'
    }
    $reason = Get-QualityText -Evidence $Evidence -Name 'Reason'
    if ([string]::IsNullOrWhiteSpace($reason)) {
        return 'reason'
    }

    $requesterGap = Get-EmergencyPartyGap -Evidence $Evidence -NameKey 'Requester' -RoleKey 'RequesterRole' -MissingNameToken 'requester' -MissingRoleToken 'requester'
    if ($null -ne $requesterGap) {
        return $requesterGap
    }
    $approverGap = Get-EmergencyPartyGap -Evidence $Evidence -NameKey 'Approver' -RoleKey 'ApproverRole' -MissingNameToken 'approver' -MissingRoleToken 'approver-role'
    if ($null -ne $approverGap) {
        return $approverGap
    }

    $requester = Get-QualityText -Evidence $Evidence -Name 'Requester'
    $approver = Get-QualityText -Evidence $Evidence -Name 'Approver'
    $samePerson = [string]::Equals($requester, $approver, [System.StringComparison]::OrdinalIgnoreCase)
    if ($samePerson) {
        return 'approver-same-as-requester'
    }
    if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'Timestamp')) {
        return 'timestamp'
    }

    $scope = Get-QualityText -Evidence $Evidence -Name 'Scope'
    if (-not [string]::Equals($scope, $script:EmergencyScope, [System.StringComparison]::Ordinal)) {
        return 'scope'
    }
    $followUp = Get-QualityText -Evidence $Evidence -Name 'FollowUpValidation'
    if ([string]::IsNullOrWhiteSpace($followUp)) {
        return 'follow-up-validation'
    }
    return $null
}

function Get-VisibleEmergencyRecord {
    param($Evidence)
    $quality = $null
    $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name 'QualityResults'
    if ($entry.Present) {
        $quality = $entry.Value
    }
    return [pscustomobject]@{
        Visible            = $true
        Revision           = Get-QualityText -Evidence $Evidence -Name 'Revision'
        Reason             = Get-QualityText -Evidence $Evidence -Name 'Reason'
        Requester          = Get-QualityText -Evidence $Evidence -Name 'Requester'
        Approver           = Get-QualityText -Evidence $Evidence -Name 'Approver'
        Timestamp          = Get-QualityTime -Evidence $Evidence -Name 'Timestamp'
        Scope              = $script:EmergencyScope
        FollowUpValidation = Get-QualityText -Evidence $Evidence -Name 'FollowUpValidation'
        QualityResults     = $quality
    }
}

function Get-EmergencyAssessment {
    param($Evidence)
    if ($null -eq $Evidence) {
        return [pscustomobject]@{
            Accepted     = $false
            MissingField = 'revision'
            Record       = $null
        }
    }
    $gap = Get-EmergencyRecordGap -Evidence $Evidence
    if ($null -ne $gap) {
        return [pscustomobject]@{
            Accepted     = $false
            MissingField = $gap
            Record       = $null
        }
    }
    return [pscustomobject]@{
        Accepted     = $true
        MissingField = $null
        Record       = (Get-VisibleEmergencyRecord -Evidence $Evidence)
    }
}

function Copy-ProtectedRequestContext {
    param(
        $Decision,
        [string]$Destination,
        [string]$IntegrationKind,
        $EmergencyRecord,
        [bool]$LocalAdvisoryAvoided
    )
    if ($LocalAdvisoryAvoided) {
        $Decision.LocalAdvisory = 'avoided'
    }
    $Decision.RequestedDestination = $Destination
    $Decision.IntegrationKind = $IntegrationKind
    $emergency = Get-EmergencyAssessment -Evidence $EmergencyRecord
    if ($emergency.Accepted) {
        $Decision.EmergencyRecord = $emergency.Record
    }
}

function Get-ProtectedBaselineState {
    param($ComprehensiveSuite)
    $baselineCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount'
    if ($null -eq $baselineCount) {
        return 'unknown'
    }
    if ($baselineCount -gt 0) {
        return 'red'
    }
    return 'clear'
}

function Copy-ProtectedBaselineRejection {
    param(
        $Decision,
        [string]$State
    )
    if ($State -eq 'unknown') {
        $Decision.ProtectedIntegration = 'rejected'
        $Decision.EvidenceClass = 'failed-evidence'
        $Decision.Reasons = @('evidence-input-unknown')
        return $true
    }
    if ($State -eq 'red') {
        $Decision.ProtectedIntegration = 'rejected'
        $Decision.EvidenceClass = 'failed-evidence'
        $Decision.Recovery = 'baseline-recovery'
        $Decision.Reasons = @('baseline-red')
        return $true
    }
    return $false
}

function Get-ProtectedFullEvidenceClass {
    param(
        $FullEvidence,
        $PriorFullEvidence
    )
    if (Test-SuccessfulComprehensiveResult -Evidence $FullEvidence) {
        return 'new-full-evidence'
    }
    if (Test-SuccessfulComprehensiveResult -Evidence $PriorFullEvidence) {
        return 'reused-full-evidence'
    }
    return $null
}

function Copy-ProtectedMissingFullEvidence {
    param(
        $Decision,
        $FastFeedback
    )
    $Decision.ProtectedIntegration = 'rejected'
    $Decision.MissingEvidence = 'full-evidence'
    $Decision.Reasons = @('required-check-missing')
    if (Get-QualityFlag -Evidence $FastFeedback -Name 'Succeeded') {
        $Decision.EvidenceClass = 'fast-advisory-evidence'
    }
    else {
        $Decision.EvidenceClass = 'failed-evidence'
    }
}

function Copy-ProtectedCompositionOutcome {
    param(
        $Decision,
        [hashtable]$ManifestResults,
        [string]$ProtectedRevision
    )
    $evaluated = @()
    if ($null -ne $ManifestResults) {
        $evaluated = @(Get-ManifestCheckResult -ManifestResults $ManifestResults)
    }
    $Decision.CheckResults = $evaluated
    $failure = $null
    foreach ($item in $evaluated) {
        if (-not $item.CheckAccepted) {
            $failure = $item
            break
        }
    }
    if ($null -eq $failure) {
        $failure = Get-FirstMissingCatalogCheck -ManifestResults $ManifestResults
        if ($null -ne $failure) {
            $Decision.CheckResults = @($evaluated + $failure)
        }
    }
    if ($null -ne $failure) {
        Copy-CheckOntoDecision -Decision $Decision -Result $failure
        $Decision.ProtectedIntegration = 'rejected'
        $Decision.MissingEvidence = $failure.FailedCheck
        $Decision.FullEvidenceAccepted = $false
        return
    }
    $Decision.ProtectedIntegration = 'allowed'
    $Decision.MissingEvidence = $null
    $Decision.Reasons = @()
    $Decision.FullEvidenceAccepted = $true
    if (-not [string]::IsNullOrWhiteSpace($ProtectedRevision)) {
        $Decision.ProvenanceRevision = $ProtectedRevision
    }
    elseif ($evaluated.Count -gt 0) {
        $Decision.ProvenanceRevision = $evaluated[0].ProvenanceRevision
    }
}

function Get-ProtectedIntegrationDecision {
    param(
        $Decision,
        [string]$Destination,
        [string]$IntegrationKind,
        $ComprehensiveSuite,
        $FullEvidence,
        $PriorFullEvidence,
        $FastFeedback,
        [hashtable]$ManifestResults,
        [string]$ProtectedRevision,
        $EmergencyRecord,
        [bool]$LocalAdvisoryAvoided
    )
    Copy-ProtectedRequestContext -Decision $Decision -Destination $Destination -IntegrationKind $IntegrationKind -EmergencyRecord $EmergencyRecord -LocalAdvisoryAvoided $LocalAdvisoryAvoided
    $insideBoundary = Test-ProtectedBoundary -Destination $Destination -IntegrationKind $IntegrationKind
    if (-not $insideBoundary) {
        $Decision.ProtectedIntegration = 'rejected'
        $Decision.EvidenceClass = 'failed-evidence'
        $Decision.Reasons = @('protected-boundary-rejected')
        return $Decision
    }
    $baselineState = Get-ProtectedBaselineState -ComprehensiveSuite $ComprehensiveSuite
    if (Copy-ProtectedBaselineRejection -Decision $Decision -State $baselineState) {
        return $Decision
    }
    $evidenceClass = Get-ProtectedFullEvidenceClass -FullEvidence $FullEvidence -PriorFullEvidence $PriorFullEvidence
    if ($null -eq $evidenceClass) {
        Copy-ProtectedMissingFullEvidence -Decision $Decision -FastFeedback $FastFeedback
        return $Decision
    }
    $Decision.EnforcementReadiness = 'evaluable'
    $Decision.EvidenceClass = $evidenceClass
    Copy-ProtectedCompositionOutcome -Decision $Decision -ManifestResults $ManifestResults -ProtectedRevision $ProtectedRevision
    return $Decision
}

function Invoke-RepositoryQualityDecision {
    <#
    .SYNOPSIS
        Classify repository quality evidence and decide baseline readiness, protected integration, and emergency exception records.

    .DESCRIPTION
        Driving port for baseline recovery, fast-versus-full evidence,
        per-check identity, protected composition, and emergency records.
        A red comprehensive baseline stays enforcement-disabled. A recorded
        green baseline may be evaluated. Fast advisory evidence never
        authorizes protected integration. Each manifest check is reused
        only when its target set and rule identity match and the result is
        at most 24 hours old. A direct update or merge into main is allowed
        only when every catalog row is successful and fresh. A behavior-bearing
        AI change needs newly captured output scored by the deterministic eval
        runner. A missing fixture is a behavioral coverage gap. Cosmetic prose
        reuses that evidence only when it is classified non-behavior-bearing
        and the paired AI surfaces are synchronized. An empty behavioral or
        AI surface target set is not applicable. An emergency record is
        visible and never waives those checks. The rollout flag stays
        disabled; ProtectedIntegration is the server decision.

    .PARAMETER Request
        prepare-protected-enforcement, evaluate-enforcement-readiness,
        record-fast-feedback, request-protected-integration, record-full-evidence,
        evaluate-check, or record-emergency-exception.

    .PARAMETER Check
        comprehensive-pester, pester-test-quality, powershell-security,
        trivy-secrets, powershell-quality, agent-skill-behavior, or
        ai-surface-sync. Used by evaluate-check.

    .PARAMETER ComprehensiveSuite
        Recorded comprehensive suite. FailedCount is required for baseline
        requests. RecordedAsBaseline marks a green run as the baseline.
        Evaluate requests also carry PriorFailedCount and PriorRecordedAsBaseline.

    .PARAMETER FastFeedback
        Staged local result. Succeeded is true when the fast gate passed.

    .PARAMETER FullEvidence
        A comprehensive result recorded from a new run. FailedCount is the
        failure total.

    .PARAMETER PriorFullEvidence
        A previously recorded comprehensive result presented again with no new run.

    .PARAMETER CheckEvidence
        One check result. Callers supply CurrentTargetSet, TargetSet,
        ScannedTargetSet, CurrentRuleIdentity, RuleIdentity, CompletedAt,
        RequestedAt, ProtectedRevision, TargetCount, PresentedAgain,
        ChangedPaths, FixtureContents, CapturedOutput, DeterministicScore,
        NewlyCaptured, StaticContentCheckOnly, BehaviorClassification,
        SurfacesSynchronized, and the check-specific finding counters FailedCount,
        BlockingFindingCount, ErrorFindingCount, SecretFindingCount,
        AnalyzerFindingCount, and EncodingFaultCount. BehaviorClassification,
        SurfacesSynchronized, DeterministicScore, CapturedOutput, and
        FixtureContents are caller-asserted. AI checks additionally require
        AdapterAttested = true, ApplicabilitySource =
        changed-path-policy, and EvidenceSource =
        deterministic-eval-runner for agent-skill-behavior or
        canonical-generated-pair for ai-surface-sync. This port does not
        cryptographically attest those claims. A trusted adapter derives
        CurrentTargetSet from the real changed-path set, derives
        SurfacesSynchronized from a real canonical and generated pair,
        BehaviorClassification from an explicit path policy, and the
        score, capture, and fixtures from the deterministic eval runner.
        It rejects evidence that omits the adapter contract, invents a
        passing score, or invents a synchronized pair. Adapters must not
        write full CapturedOutput to console or CI logs.

    .PARAMETER ManifestResults
        Named check results for a protected integration request. A null row
        is a missing required result. A catalog row omitted from the
        hashtable is the missing companion when every presented row passes.

    .PARAMETER Destination
        Protected integration target. Only main is inside the protected boundary.

    .PARAMETER IntegrationKind
        direct-update or merge. Any other kind is outside the protected boundary.

    .PARAMETER ProtectedRevision
        Provenance recorded on an allowed composed result. It is not a check
        identity. A difference from check-row revisions does not reject the
        decision. Callers pass the git SHA that identifies the composed
        integration.

    .PARAMETER EmergencyRecord
        Exception record with Revision, Reason, Requester, Approver,
        RequesterRole, ApproverRole, Timestamp, Scope, FollowUpValidation,
        and optional QualityResults. Both roles must be repository-maintainer.
        The requester and approver are different people. Scope must be
        local-advisory. This port does not accept IdentityProof. Requester,
        Approver, and roles are caller-asserted strings; an adapter binds
        those names from an authenticated identity and rejects records that
        invent role strings. A complete record is visible and never waives
        protected checks, and it is never successful full evidence. EvidenceClass
        stays emergency-bypass-record.

    .PARAMETER LocalAdvisoryAvoided
        Observational metadata. The maintainer disabled or skipped a local
        advisory check. The switch does not authorize integration. The server
        still requires the manifest rows.

    .OUTPUTS
        A decision object. EvidenceClass is fast-advisory-evidence, new-full-evidence,
        reused-full-evidence, failed-evidence, or emergency-bypass-record.
        ProtectedEnforcement stays disabled. EnforcementReadiness is disabled,
        evaluable, or paused. ProtectedIntegration is not-requested, rejected, or
        allowed. Reasons include baseline-red, protected-boundary-rejected,
        full-check-failed, required-check-missing,
        evidence-input-unknown, evidence-stale, test-quality-semgrep-failed,
        security-semgrep-failed, secret-check-failed, PowerShell-quality-failed,
        behavioral-coverage-gap, AI-surface-unsynchronized, AI-path-unclassified, or
        emergency-record-incomplete. MissingEvidence is full-evidence when no full
        Pester evidence exists, or the failed check name when a manifest row is
        rejected or absent. Recovery is baseline-recovery or required when recovery
        work remains. LocalFeedback is available or unavailable. LocalAdvisory is
        used or avoided. MissingField is revision, reason, requester, approver,
        approver-role, approver-same-as-requester, timestamp, scope, or
        follow-up-validation. MissingInput may be target-set, rule-identity,
        completion-time, requested-at, protected-revision, or
        trusted-adapter-evidence when an AI check lacks the adapter contract.
        EmergencyRecord is the visible exception when the record is complete.
        evaluate-check also sets CheckAccepted, Freshness (fresh, stale, or null),
        ProvenanceRevision, MissingInput, RequiredRun, FailedCheck, AffectedTarget,
        Condition, CheckResults, TargetCount, FindingCount, ErrorFindingCount,
        SecretFindingCount, FixtureContents, CapturedOutput, DeterministicScore,
        CompletedAt, BehaviorClassification, and BehaviorTargetSet.

    .EXAMPLE
        Invoke-RepositoryQualityDecision -Request prepare-protected-enforcement -ComprehensiveSuite @{ FailedCount = 8; RecordedAsBaseline = $true }
    #>

    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [ValidateSet(
            'prepare-protected-enforcement',
            'evaluate-enforcement-readiness',
            'record-fast-feedback',
            'request-protected-integration',
            'record-full-evidence',
            'evaluate-check',
            'record-emergency-exception'
        )]
        [string]$Request,

        [ValidateScript({
                $null -ne (Get-RepositoryCheckDefinition -Check $_)
            })]
        [string]$Check,

        [hashtable]$ComprehensiveSuite,

        [hashtable]$FastFeedback,

        [hashtable]$FullEvidence,

        [hashtable]$PriorFullEvidence,

        [hashtable]$CheckEvidence,

        [hashtable]$ManifestResults,

        [string]$Destination = $script:ProtectedDestination,

        [string]$IntegrationKind = $(@($script:ProtectedIntegrationKinds)[0]),

        [string]$ProtectedRevision,

        [hashtable]$EmergencyRecord,

        [switch]$LocalAdvisoryAvoided
    )

    $decision = [pscustomobject]@{
        Request                             = $Request
        EvidenceClass                       = $null
        ProtectedEnforcement                = 'disabled'
        EnforcementReadiness                = 'disabled'
        ProtectedIntegration                = 'not-requested'
        LocalFeedback                       = 'unavailable'
        ComprehensiveSuiteRequiredForCommit = $false
        FullEvidenceAccepted                = $false
        Recovery                            = $null
        MissingEvidence                     = $null
        Reasons                             = @()
        CheckAccepted                       = $false
        Freshness                           = $null
        ProvenanceRevision                  = $null
        MissingInput                        = $null
        RequiredRun                         = $null
        FailedCheck                         = $null
        AffectedTarget                      = $null
        Condition                           = $null
        CheckResults                        = @()
        TargetCount                         = $null
        FindingCount                        = $null
        ErrorFindingCount                   = $null
        SecretFindingCount                  = $null
        LocalAdvisory                       = 'used'
        RequestedDestination                = $null
        IntegrationKind                     = $null
        MissingField                        = $null
        EmergencyRecord                     = $null
        FixtureContents                     = $null
        CapturedOutput                      = $null
        DeterministicScore                  = $null
        CompletedAt                         = $null
        BehaviorClassification              = $null
        BehaviorTargetSet                   = $null
    }

    switch ($Request) {
        'prepare-protected-enforcement' {
            $count = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount'
            if ($null -eq $count) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Reasons = @('evidence-input-unknown')
                return $decision
            }
            if ($count -gt 0) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Recovery = 'baseline-recovery'
                $decision.Reasons = @('baseline-red')
                return $decision
            }
            if (-not (Get-QualityFlag -Evidence $ComprehensiveSuite -Name 'RecordedAsBaseline')) {
                return $decision
            }
            $decision.EvidenceClass = 'new-full-evidence'
            $decision.EnforcementReadiness = 'evaluable'
            $decision.FullEvidenceAccepted = $true
            return $decision
        }
        'evaluate-enforcement-readiness' {
            $laterCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount'
            $priorCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'PriorFailedCount'
            $priorRecorded = Get-QualityFlag -Evidence $ComprehensiveSuite -Name 'PriorRecordedAsBaseline'
            if (($null -eq $laterCount) -or ($null -eq $priorCount) -or (-not $priorRecorded)) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Reasons = @('evidence-input-unknown')
                return $decision
            }
            if ($priorCount -gt 0) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Recovery = 'baseline-recovery'
                $decision.Reasons = @('baseline-red')
                return $decision
            }
            if ($laterCount -gt 0) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.EnforcementReadiness = 'paused'
                $decision.Recovery = 'required'
                $decision.Reasons = @('full-check-failed')
                return $decision
            }
            $decision.EvidenceClass = 'new-full-evidence'
            $decision.EnforcementReadiness = 'evaluable'
            $decision.FullEvidenceAccepted = $true
            return $decision
        }
        'record-fast-feedback' {
            $decision.ComprehensiveSuiteRequiredForCommit = $false
            if (Get-QualityFlag -Evidence $FastFeedback -Name 'Succeeded') {
                $decision.EvidenceClass = 'fast-advisory-evidence'
                $decision.LocalFeedback = 'available'
                return $decision
            }
            $decision.EvidenceClass = 'failed-evidence'
            $decision.LocalFeedback = 'unavailable'
            return $decision
        }
        'request-protected-integration' {
            return Get-ProtectedIntegrationDecision -Decision $decision -Destination $Destination -IntegrationKind $IntegrationKind -ComprehensiveSuite $ComprehensiveSuite -FullEvidence $FullEvidence -PriorFullEvidence $PriorFullEvidence -FastFeedback $FastFeedback -ManifestResults $ManifestResults -ProtectedRevision $ProtectedRevision -EmergencyRecord $EmergencyRecord -LocalAdvisoryAvoided:$LocalAdvisoryAvoided
        }
        'record-emergency-exception' {
            $emergency = Get-EmergencyAssessment -Evidence $EmergencyRecord
            if (-not $emergency.Accepted) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Reasons = @('emergency-record-incomplete')
                $decision.MissingField = $emergency.MissingField
                $decision.FullEvidenceAccepted = $false
                return $decision
            }
            $decision.EvidenceClass = 'emergency-bypass-record'
            $decision.EmergencyRecord = $emergency.Record
            $decision.FullEvidenceAccepted = $false
            return $decision
        }
        'evaluate-check' {
            if ([string]::IsNullOrWhiteSpace($Check)) {
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Reasons = @('evidence-input-unknown')
                $decision.MissingInput = 'check'
                return $decision
            }
            $result = Get-RepositoryCheckResult -Check $Check -Evidence $CheckEvidence
            Copy-CheckOntoDecision -Decision $decision -Result $result
            $decision.CheckResults = @($result)
            return $decision
        }
        'record-full-evidence' {
            $newCount = Get-QualityCount -Evidence $FullEvidence -Name 'FailedCount'
            if ($null -ne $newCount) {
                if ($newCount -eq 0) {
                    $decision.EvidenceClass = 'new-full-evidence'
                    $decision.FullEvidenceAccepted = $true
                    return $decision
                }
                $decision.EvidenceClass = 'failed-evidence'
                $decision.Reasons = @('full-check-failed')
                return $decision
            }
            $priorCount = Get-QualityCount -Evidence $PriorFullEvidence -Name 'FailedCount'
            if (($null -ne $priorCount) -and ($priorCount -eq 0)) {
                $decision.EvidenceClass = 'reused-full-evidence'
                $decision.FullEvidenceAccepted = $true
                return $decision
            }
            $decision.EvidenceClass = 'failed-evidence'
            $decision.Reasons = @('evidence-input-unknown')
            return $decision
        }
    }

    return $decision
}

Export-ModuleMember -Function Invoke-RepositoryQualityDecision, Get-RepositoryQualityCheckCatalog