Payload/scripts/kaden/harness/quality-gate/Invoke-RepositoryQualityPrePush.ps1

#Requires -Version 5.1
<#
.SYNOPSIS
    Pre-push gate: reuse or re-run comprehensive Pester via the quality decision port.

.DESCRIPTION
    Asks Invoke-RepositoryQualityDecision (evaluate-check comprehensive-pester) whether
    local ledger evidence is reusable for the paths in the push range. When the decision
    rejects reuse, runs tests/harness/run-harness-tests.ps1 and writes
    .harness/quality-gate/comprehensive-pester.json on success.

    Commit stays fast (PSA only). This script is for pre-push / npm run quality:pre-push.

.PARAMETER Force
    Always run the Pester suite and refresh the ledger.

.PARAMETER DryDecision
    Print the evaluate-check decision and exit without running Pester.

.PARAMETER UpstreamRef
    Optional git revision range (e.g. origin/main..HEAD). Default: @{upstream}..HEAD.

.PARAMETER RepoRoot
    Repository root (defaults to discovery from this script).

.EXAMPLE
    pwsh -File scripts/kaden/harness/quality-gate/Invoke-RepositoryQualityPrePush.ps1

.EXAMPLE
    pwsh -File scripts/kaden/harness/quality-gate/Invoke-RepositoryQualityPrePush.ps1 -DryDecision
#>

[CmdletBinding()]
param(
    [switch]$Force,
    [switch]$DryDecision,
    [string]$UpstreamRef = '',
    [string]$RepoRoot = ''
)

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

$gateDir = $PSScriptRoot
Import-Module (Join-Path $gateDir 'RepositoryQualityGate.psm1') -Force
Import-Module (Join-Path $gateDir 'RepositoryQualityPrePush.psm1') -Force

if ([string]::IsNullOrWhiteSpace($RepoRoot)) {
    $RepoRoot = Get-RepositoryQualityRepoRoot -StartPath $gateDir
}

$requestedAt = [datetime]::UtcNow
$head = Get-RepositoryQualityHeadRevision -RepoRoot $RepoRoot
$targets = @(Get-RepositoryQualityPesterTargetSet -RepoRoot $RepoRoot)
$changed = @(Get-RepositoryQualityPushChangedPath -RepoRoot $RepoRoot -UpstreamRef $UpstreamRef)
$prior = Read-RepositoryQualityPesterEvidence -RepoRoot $RepoRoot

Write-Output "[quality:pre-push] HEAD=$head"
Write-Output "[quality:pre-push] changed_paths=$($changed.Count) pester_targets=$($targets.Count)"
if ($changed.Count -gt 0) {
    $preview = @($changed | Select-Object -First 12) -join ', '
    Write-Output "[quality:pre-push] sample: $preview"
}

$evidence = New-RepositoryQualityPesterCheckEvidence `
    -CurrentTargetSet $targets `
    -ChangedPaths $changed `
    -RequestedAt $requestedAt `
    -ProtectedRevision $head `
    -PriorEvidence $prior

$decision = $null
$mustRun = [bool]$Force
if (-not $mustRun) {
    if ($null -eq $prior -or $null -eq $prior.CompletedAt) {
        $mustRun = $true
        Write-Output '[quality:pre-push] no local comprehensive-pester ledger; Pester required'
    }
    else {
        $decision = Test-RepositoryQualityPesterReuse -CheckEvidence $evidence
        Write-Output ("[quality:pre-push] decision CheckAccepted={0} EvidenceClass={1} Freshness={2} RequiredRun={3}" -f `
                $decision.CheckAccepted, $decision.EvidenceClass, $decision.Freshness, $decision.RequiredRun)
        if ($decision.Reasons) {
            Write-Output ("[quality:pre-push] reasons: {0}" -f (($decision.Reasons | ForEach-Object { [string]$_ }) -join ', '))
        }
        if (-not $decision.CheckAccepted) {
            $mustRun = $true
        }
    }
}

if ($DryDecision) {
    if ($mustRun) {
        Write-Output '[quality:pre-push] DryDecision: would run Pester'
        exit 0
    }
    Write-Output '[quality:pre-push] DryDecision: would reuse evidence (skip Pester)'
    exit 0
}

if (-not $mustRun) {
    Write-Output '[quality:pre-push] reusing comprehensive-pester evidence; Pester skipped'
    exit 0
}

$runner = Join-Path $RepoRoot (Join-Path 'tests' (Join-Path 'harness' 'run-harness-tests.ps1'))
Write-Output "[quality:pre-push] running $runner"
$hostPath = $null
try {
    Import-Module (Join-Path (Join-Path (Join-Path (Join-Path $RepoRoot 'scripts') 'kaden') 'harness') 'lib\HarnessContract.psm1') -Force -ErrorAction SilentlyContinue
    if (Get-Command Get-HarnessPowerShellExePath -ErrorAction SilentlyContinue) {
        $hostPath = Get-HarnessPowerShellExePath
    }
}
catch {
    $hostPath = $null
}
if ([string]::IsNullOrWhiteSpace($hostPath)) {
    $pwshCmd = Get-Command pwsh -ErrorAction SilentlyContinue
    if ($pwshCmd) {
        $hostPath = $pwshCmd.Source
    }
    else {
        $hostPath = (Get-Command powershell.exe -ErrorAction Stop).Source
    }
}

$proc = Start-Process -FilePath $hostPath -ArgumentList @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $runner) `
    -WorkingDirectory $RepoRoot -Wait -PassThru -NoNewWindow
$code = 1
if ($null -ne $proc -and $null -ne $proc.ExitCode) {
    $code = [int]$proc.ExitCode
}
if ($code -ne 0) {
    Write-Error "[quality:pre-push] Pester suite failed (exit $code); push blocked"
    exit $code
}

$completedAt = [datetime]::UtcNow
$ledger = Write-RepositoryQualityPesterEvidence -RepoRoot $RepoRoot -TargetSet $targets `
    -CompletedAt $completedAt -ProtectedRevision $head -FailedCount 0
Write-Output "[quality:pre-push] wrote evidence $ledger"
exit 0