scripts/commit-bug-review.ps1

#!/usr/bin/env pwsh
param(
    [int]$MaxIterations = 148,
    [ValidateSet('claude', 'copilot', 'codex')]
    [string]$Type = 'copilot',
    [string]$Model = 'gpt-5.4',
    [ValidateSet('low', 'medium', 'high', 'max', 'xhigh')]
    [string]$Effort = 'max',
    [switch]$DryRun
)

$ErrorActionPreference = 'Stop'
if (Get-Variable -Name PSNativeCommandUseErrorActionPreference -ErrorAction SilentlyContinue) {
    $PSNativeCommandUseErrorActionPreference = $false
}
$RepoRoot = Split-Path $PSScriptRoot -Parent
$TrackerPath = Join-Path $RepoRoot "docs\trackers\features\commit-bug-review-TRACKER.md"
$PromptPath = Join-Path $RepoRoot "scripts\commit-bug-review-prompt.md"

# Resolve engine binary
if ($Type -eq 'copilot') {
    # Try common Copilot CLI locations on Windows
    $candidatePaths = @(
        "copilot",
        "gh copilot",
        "$env:LOCALAPPDATA\Programs\copilot\copilot.exe",
        "/home/vscode/.local/bin/copilot"
    )
    $EngineBin = "copilot"
    foreach ($candidate in $candidatePaths) {
        if (Get-Command $candidate -ErrorAction SilentlyContinue) {
            $EngineBin = $candidate
            break
        }
    }
    Write-Host "Using Copilot CLI: $EngineBin" -ForegroundColor DarkGray
} elseif ($Type -eq 'codex') {
    $EngineBin = "codex"
} else {
    $EngineBin = "claude"
}

function Get-PendingCount {
    $tracker = Get-Content $TrackerPath -Raw
    return ([regex]::Matches($tracker, '\| PENDING \|')).Count
}

function Get-CompletedCount {
    $tracker = Get-Content $TrackerPath -Raw
    $done = ([regex]::Matches($tracker, '\| DONE \|')).Count
    $needsWork = ([regex]::Matches($tracker, '\| NEEDS_WORK \|')).Count
    $blocked = ([regex]::Matches($tracker, '\| BLOCKED \|')).Count
    return $done + $needsWork + $blocked
}

function Invoke-Iteration {
    param([int]$Iteration)

    Write-Host ""
    Write-Host ("=" * 50) -ForegroundColor Yellow
    Write-Host " Iteration $Iteration of $MaxIterations ($Type / $Model)" -ForegroundColor Yellow
    Write-Host ("=" * 50) -ForegroundColor Yellow

    $prompt = Get-Content $PromptPath -Raw

    if ($Type -eq 'copilot') {
        $copilotEffort = if ($Effort -eq 'max') { 'xhigh' } else { $Effort }

        $engineArgs = @(
            '-p', $prompt
            '--model', $Model
            '--allow-all'
            '--reasoning-effort', $copilotEffort
        )

        if ($DryRun) {
            Write-Host " [DRY RUN] Would run: $EngineBin $($engineArgs -join ' ')" -ForegroundColor DarkGray
            return $true
        }

        & $EngineBin @engineArgs | ForEach-Object {
            $line = "$PSItem"
            if ($line -and $line -notmatch '^\s*$') {
                Write-Host " $line" -ForegroundColor White
            }
        }
    } elseif ($Type -eq 'codex') {
        $codexEffort = if ($Effort -eq 'max') { 'xhigh' } else { $Effort }
        $engineArgs = @(
            'exec'
            '--model', $Model
            '--config', "model_reasoning_effort=`"$codexEffort`""
            '--dangerously-bypass-approvals-and-sandbox'
            '-'
        )

        if ($DryRun) {
            Write-Host " [DRY RUN] Would run: codex exec --model $Model --config model_reasoning_effort=`"$codexEffort`" --dangerously-bypass-approvals-and-sandbox - < prompt" -ForegroundColor DarkGray
            return $true
        }

        $prompt | & $EngineBin @engineArgs
    } else {
        $sessionId = [guid]::NewGuid().ToString()
        $engineArgs = @(
            '--dangerously-skip-permissions'
            '--session-id', $sessionId
            '--no-session-persistence'
            '--model', $Model
            '--effort', $Effort
            '--verbose'
            '--output-format', 'stream-json'
            '-p', $prompt
        )

        if ($DryRun) {
            Write-Host " [DRY RUN] Would run: claude $($engineArgs -join ' ')" -ForegroundColor DarkGray
            return $true
        }

        # Stream and parse JSON - show tool use with file details
        & $EngineBin @engineArgs | ForEach-Object {
            $line = $PSItem
            try {
                $obj = $line | ConvertFrom-Json -ErrorAction Stop
                switch ($obj.type) {
                    'assistant' {
                        if ($obj.message.content) {
                            foreach ($content in $obj.message.content) {
                                switch ($content.type) {
                                    'tool_use' {
                                        $toolName = $content.name
                                        $detail = ""
                                        if ($content.input) {
                                            switch ($toolName) {
                                                'Read'   { $detail = Split-Path $content.input.file_path -Leaf }
                                                'Write'  { $detail = Split-Path $content.input.file_path -Leaf }
                                                'Edit'   { $detail = Split-Path $content.input.file_path -Leaf }
                                                'Glob'   { $detail = $content.input.pattern -replace '.*/',''}
                                                'Grep'   { $detail = $content.input.pattern.Substring(0, [Math]::Min(30, $content.input.pattern.Length)) }
                                                'Bash'   { $detail = ($content.input.command -split '\n')[0].Substring(0, [Math]::Min(40, ($content.input.command -split '\n')[0].Length)) }
                                            }
                                        }
                                        if ($detail) {
                                            Write-Host " 🔧 $toolName " -ForegroundColor DarkCyan -NoNewline
                                            Write-Host $detail -ForegroundColor DarkGray
                                        } else {
                                            Write-Host " 🔧 $toolName" -ForegroundColor DarkCyan
                                        }
                                    }
                                    'text' {
                                        if ($content.text) { Write-Host $content.text -ForegroundColor White }
                                    }
                                }
                            }
                        }
                    }
                    'result' {
                        $duration = [math]::Round($obj.duration_ms / 1000, 1)
                        $cost = [math]::Round($obj.total_cost_usd, 4)
                        Write-Host ""
                        Write-Host "✓ Completed in ${duration}s (`$$cost)" -ForegroundColor Green
                    }
                }
            } catch {
                if ($line -and $line -notmatch '^\s*$') { Write-Host $line -ForegroundColor DarkGray }
            }
        }
    }

    return $LASTEXITCODE -eq 0
}

# Main loop
$total = 135  # 138 commits minus 3 pre-marked version bumps
Write-Host "Ralph Wiggum: commit-bug-review ($Type, $Model, effort=$Effort)" -ForegroundColor Cyan
Write-Host "Tracker: $TrackerPath" -ForegroundColor DarkGray
Write-Host "Prompt: $PromptPath" -ForegroundColor DarkGray
Write-Host ""

$iteration = 0
$stalledCount = 0
$maxStalled = 3

while ($iteration -lt $MaxIterations) {
    $iteration++
    $pending = Get-PendingCount
    $completed = Get-CompletedCount
    $completedBefore = $completed
    $percent = [math]::Min(100, [math]::Round(($completed / $total) * 100))

    $barFilled = [math]::Min(20, [math]::Floor($percent / 5))
    $barEmpty = [math]::Max(0, 20 - $barFilled)
    Write-Host "[$(('=' * $barFilled))$(('-' * $barEmpty))] $percent% ($completed/$total)" -ForegroundColor Cyan

    if ($pending -eq 0) {
        Write-Host "All items completed!" -ForegroundColor Green
        break
    }

    if (-not (Invoke-Iteration -Iteration $iteration)) {
        Write-Host "Iteration failed. Stopping." -ForegroundColor Red
        exit 1
    }

    if ($DryRun) {
        Write-Host "Dry run complete. Tracker was not modified." -ForegroundColor DarkGray
        break
    }

    # Zero-trust: verify the iteration actually progressed
    $completedAfter = Get-CompletedCount

    if ($completedAfter -le $completedBefore) {
        $stalledCount++
        Write-Host "⚠️ No progress detected (stalled $stalledCount/$maxStalled)" -ForegroundColor Yellow
        Write-Host " Before: $completedBefore completed, $pending pending" -ForegroundColor Yellow
        Write-Host " After: $completedAfter completed" -ForegroundColor Yellow

        if ($stalledCount -ge $maxStalled) {
            Write-Host "✗ Stalled $maxStalled times in a row. AI is claiming done without finishing." -ForegroundColor Red
            Write-Host " Check the tracker for items marked DONE that shouldn't be." -ForegroundColor Red
            exit 1
        }
    } else {
        $stalledCount = 0
        Write-Host "✓ Progress: $completedBefore → $completedAfter completed" -ForegroundColor Green
    }

    Start-Sleep -Seconds 2
}

Write-Host "Done. Completed: $(Get-CompletedCount) / $total" -ForegroundColor Green

# SIG # Begin signature block
# MIItZAYJKoZIhvcNAQcCoIItVTCCLVECAQMxDTALBglghkgBZQMEAgEwewYKKwYB
# BAGCNwIBBKBtBGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCA79zbEIs8pRuI8
# 6ehxAk1VZFuQZU7tv4LliESmkcyhO6CCFWUwggaTMIIEe6ADAgECAhMzAAETuC/6
# W6KVT30LAAAAARO4MA0GCSqGSIb3DQEBDAUAMFoxCzAJBgNVBAYTAlVTMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKzApBgNVBAMTIk1pY3Jvc29mdCBJ
# RCBWZXJpZmllZCBDUyBBT0MgQ0EgMDQwHhcNMjYwNTE2MDI0NzA4WhcNMjYwNTE5
# MDI0NzA4WjBXMQswCQYDVQQGEwJVUzERMA8GA1UECBMIVmlyZ2luaWExDzANBgNV
# BAcTBlZpZW5uYTERMA8GA1UEChMIZGJhdG9vbHMxETAPBgNVBAMTCGRiYXRvb2xz
# MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAi0CmhZOKS+I8l6jOfDPy
# 09NiQe+pHwccTKxFgHS3fq5TELNtpPb7MZoAUKjlA/umZg8w0zDtYupFRoNwxDJd
# Mxaxu0EWseEccYpEYdjRugEeyvMaOpn2oo8rLQygdMFXr1A/qq4lZUZKusqeGEQF
# 1sYdYtUmDufCqKVUFbdrvPJMH5X8yItjYWPcI8u4HVZPDmOmKUdnB/uWwoK63e85
# CQrrfVOyNOhRUbk+c3nVJovl1dQ74XvXlf6QizI6aj9XpLDWp1CqcCemhrVDh/gU
# +K/ZJUekWb02u/ifZfcm3N5fFH8qg8/D7wrw8jROSsCp7dxqaWORCNqR1Rmj4Udf
# ocgsLAUw1JTZGdrJbGj0p9IP4XJMUlELTSQ2NwBaRchOhmusbdPXVmtHbintVUq0
# 8xm26IacbhgXbPr9dcyckBIpbxkXqWf9Ckwqhi+CGfNFqpIHefThmD+N8y+Zj06G
# G6hwzwaP7qxzjB9lq5mJP1zRKf3AJ1s3NbUG4LbvzEnvAgMBAAGjggHTMIIBzzAM
# BgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIHgDA6BgNVHSUEMzAxBgorBgEEAYI3
# YQEABggrBgEFBQcDAwYZKwYBBAGCN2H5+cEspPS4DoOuxLIcm56wGDAdBgNVHQ4E
# FgQUK60uP2XKiAHCh4ob85+cb/VysEAwHwYDVR0jBBgwFoAUayVB3vtrfP0YgAot
# f492XapzPbgwZwYDVR0fBGAwXjBcoFqgWIZWaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwSUQlMjBWZXJpZmllZCUyMENTJTIw
# QU9DJTIwQ0ElMjAwNC5jcmwwdAYIKwYBBQUHAQEEaDBmMGQGCCsGAQUFBzAChlho
# dHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUy
# MElEJTIwVmVyaWZpZWQlMjBDUyUyMEFPQyUyMENBJTIwMDQuY3J0MFQGA1UdIARN
# MEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wDQYJKoZIhvcNAQEMBQADggIB
# AJNbVP6F8rmpLtPR1BQbzpOqD/0jdgfECBbtzTjT/etuIiZCUYmwSeZXZIAVAxln
# P8d8ikNhiDJTyCr8D6KzhgzKsOsMG4IpmeEm00uo4R9NueT5AJjATBTh5/1Jj41s
# pFRxqzwjX2byKxPCtP9cBUGGhnfLD4RGITFeJ/L9/7TpdZvPxqRtfqt6KN0hqCFh
# JOEd3jadqH4zUEJDaNQ/FH49Wn73mgoXA6QMdYwC1diUHXrdXQb56kmhJD0a48QQ
# eaeOi1hH8ZPOfb1V0m7ILvFJvYu7vwECoUqA7FkskaT1ZGffPZg7n9CoXMw2vsfQ
# FFvntvcVfQYQM6VANwOCbhyK68elzS9qx2V606Fg1gfk1YOb8Cl9nt2y11l6pCQy
# Yum+Fb/4sHRcQc5QB45OpIoNni86PZ51aKObBbWceyt7qyT15YdsIJ5OTg1aLRxT
# RW+dQugdj1tDHGBjWVOWh3aOJkjBEpDtNrivfiz0bX+Clvf5zVtcjj0RJDuuNa4i
# UxoKe1yFpUKeEqzCP2VYtOzMFn2Dq1n4VMD8axr1J3KjIcIVqHbFkMmDw0OFk2dg
# y8NJPibU3nspC1fbKUss2JNt3WUGqRnFe10bdRPFHRSI1bZgI4NwPQLKBIpzphhA
# YODAJuaJFk1d0OEJv5V3u1lF/xBwPuhGyojp9o2sb9F2MIIHKDCCBRCgAwIBAgIT
# MwAAABYxko2SAmV7mgAAAAAAFjANBgkqhkiG9w0BAQwFADBjMQswCQYDVQQGEwJV
# UzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTQwMgYDVQQDEytNaWNy
# b3NvZnQgSUQgVmVyaWZpZWQgQ29kZSBTaWduaW5nIFBDQSAyMDIxMB4XDTI2MDMy
# NjE4MTEyOVoXDTMxMDMyNjE4MTEyOVowWjELMAkGA1UEBhMCVVMxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjErMCkGA1UEAxMiTWljcm9zb2Z0IElEIFZl
# cmlmaWVkIENTIEFPQyBDQSAwNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
# ggIBAMpV+sjb6Akwz/RtDk5Uo1284BLMttRQy9e5/5WXtga6h89pkdWjeAwcXmKd
# P4YxKkhx8hn2q1dTVQbryvLy81tC04vg2bfSJ9emojX4HKIBYs7VhPRafMbtc866
# hN55aw1m/kWaPEKxF4Fm/LPLMLJdlu7URB8nFZMfh5tTC0CJb2uox/14OP/BiGIR
# 8214lXdkV6JsPbO0Iev0mEV133tducIeBChMipzTZfnGVEq1QYFr7460cEGOIn+7
# AGfNOSq7gWOlmNB4m2uZ1r66vUJPaN+VYgH/Kmfu7tX229b3Alsli38fYS0nQY41
# bElntMS7yNY+Kd047eXM8/tS3NL+ZUNX3ge5xZqW2aytrrNIbwGgQnzsgzxBJvu9
# +b87jUWFiRS/z1YiPkRLY7iTHKIxJ973kIyK8K5itE/aEq/Ht6A8ytaAMMGTEwuC
# spk72FE1Qyby+TfDlv1KiAc7IlWHHIWbxoVd8jGCoMXhLSDhuFuGfOWOZKUIuW6Y
# xlxcUYOOkXa02gC7dUYUZi0e1NI1Uq9mmAHdvjKdqgORs9/5aDjnbeO3hWd5qwGB
# mELWytkjY0KcIEF+CMurTimoQoBYSiHJbYrb0pKSQe+3lVoTVpzdi36jKI7MxLnu
# 1fBAMksa8uD85cU7RU29zMOd18h9dgTEH9pvY+4xLB3lUgE7AgMBAAGjggHcMIIB
# 2DAOBgNVHQ8BAf8EBAMCAYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGsl
# Qd77a3z9GIAKLX+Pdl2qcz24MFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUF
# BwIBFjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3Np
# dG9yeS5odG0wGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwEgYDVR0TAQH/BAgw
# BgEB/wIBADAfBgNVHSMEGDAWgBTZQSmwDw9jbO9p1/XNKZ6kSGow5jBwBgNVHR8E
# aTBnMGWgY6Bhhl9odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9N
# aWNyb3NvZnQlMjBJRCUyMFZlcmlmaWVkJTIwQ29kZSUyMFNpZ25pbmclMjBQQ0El
# MjAyMDIxLmNybDB9BggrBgEFBQcBAQRxMG8wbQYIKwYBBQUHMAKGYWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwSUQlMjBW
# ZXJpZmllZCUyMENvZGUlMjBTaWduaW5nJTIwUENBJTIwMjAyMS5jcnQwDQYJKoZI
# hvcNAQEMBQADggIBAAbVUF5UdNVEGWOVxkPciIzHA/IyNDIs1oSdW7mY4ObaFEEl
# 8fwawEsBOascBzwXjuOaTkKelQ+IiC4JvDpn8pWPgOyiKrbbw1Iswt7AV8YyuLu7
# A0YshILlxyny9R0AMMQixLZ0T5Aj07CQOm/de5QPt36ECBwtVww6XUCx8Rm2xl6e
# Ea9JhSub8W4urQGoQYv0Zczlz4ej2ryj5Wf9L4ZZA3bL6CRE7XmzSQjTmdSmr904
# PiuL2uBWzq2KkR3RhmoaAP4Jk2JplasNM5Bs0+dx3YX2o6xRrbSaJiu6hPk/AkBo
# j5BCJMTZkl4wk6Q6nOFNSCpUxnBmJ0RRkoKq51p5ADTxbCeRAx8rIfNpTyPjxQtx
# TiFTC8yy/t9K6s570YB1FAI+8XxZxIrmgMd7xVUkzi2/oooKb3UeovH0lqYGBEjp
# HZJ9jee7boQbOe7SsKD/vr3PzFabg9VwLZlovpWUpWoWnU2w3xiozJ/m35tsZVvT
# 2egUpwkb9TDIaXFGZAGV94FRDHn/K2XNnOwSeGskX19MB+N7yPc51fmUSd49MVAt
# GW/NkUGpRech5Aq/d8dCML2bZ+j9nTUMgj+qnd3wuEZVRbQEIbTh9HAck0fyKqoI
# b+qh8y/6TKbYDEEOcfM2BGMvrQk4WIGCK6u8uFhA2EH3kpaiMUDqyFCeCCQxMIIH
# njCCBYagAwIBAgITMwAAAAeHozSje6WOHAAAAAAABzANBgkqhkiG9w0BAQwFADB3
# MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMUgw
# RgYDVQQDEz9NaWNyb3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJvb3QgQ2Vy
# dGlmaWNhdGUgQXV0aG9yaXR5IDIwMjAwHhcNMjEwNDAxMjAwNTIwWhcNMzYwNDAx
# MjAxNTIwWjBjMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBv
# cmF0aW9uMTQwMgYDVQQDEytNaWNyb3NvZnQgSUQgVmVyaWZpZWQgQ29kZSBTaWdu
# aW5nIFBDQSAyMDIxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsvDA
# rxmIKOLdVHpMSWxpCFUJtFL/ekr4weslKPdnF3cpTeuV8veqtmKVgok2rO0D05Bp
# yvUDCg1wdsoEtuxACEGcgHfjPF/nZsOkg7c0mV8hpMT/GvB4uhDvWXMIeQPsDgCz
# UGzTvoi76YDpxDOxhgf8JuXWJzBDoLrmtThX01CE1TCCvH2sZD/+Hz3RDwl2MsvD
# SdX5rJDYVuR3bjaj2QfzZFmwfccTKqMAHlrz4B7ac8g9zyxlTpkTuJGtFnLBGaso
# Onn5NyYlf0xF9/bjVRo4Gzg2Yc7KR7yhTVNiuTGH5h4eB9ajm1OCShIyhrKqgOkc
# 4smz6obxO+HxKeJ9bYmPf6KLXVNLz8UaeARo0BatvJ82sLr2gqlFBdj1sYfqOf00
# Qm/3B4XGFPDK/H04kteZEZsBRc3VT2d/iVd7OTLpSH9yCORV3oIZQB/Qr4nD4YT/
# lWkhVtw2v2s0TnRJubL/hFMIQa86rcaGMhNsJrhysLNNMeBhiMezU1s5zpusf54q
# lYu2v5sZ5zL0KvBDLHtL8F9gn6jOy3v7Jm0bbBHjrW5yQW7S36ALAt03QDpwW1JG
# 1Hxu/FUXJbBO2AwwVG4Fre+ZQ5Od8ouwt59FpBxVOBGfN4vN2m3fZx1gqn52Gvai
# Bz6ozorgIEjn+PhUXILhAV5Q/ZgCJ0u2+ldFGjcCAwEAAaOCAjUwggIxMA4GA1Ud
# DwEB/wQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQU2UEpsA8PY2zv
# adf1zSmepEhqMOYwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUHAgEWM2h0
# dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0b3J5Lmh0
# bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTAPBgNVHRMBAf8EBTADAQH/MB8G
# A1UdIwQYMBaAFMh+0mqFKhvKGZgEByfPUBBPaKiiMIGEBgNVHR8EfTB7MHmgd6B1
# hnNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NybC9NaWNyb3NvZnQl
# MjBJZGVudGl0eSUyMFZlcmlmaWNhdGlvbiUyMFJvb3QlMjBDZXJ0aWZpY2F0ZSUy
# MEF1dGhvcml0eSUyMDIwMjAuY3JsMIHDBggrBgEFBQcBAQSBtjCBszCBgQYIKwYB
# BQUHMAKGdWh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY2VydHMvTWlj
# cm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIwQ2VydGlm
# aWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNydDAtBggrBgEFBQcwAYYhaHR0cDov
# L29uZW9jc3AubWljcm9zb2Z0LmNvbS9vY3NwMA0GCSqGSIb3DQEBDAUAA4ICAQB/
# JSqe/tSr6t1mCttXI0y6XmyQ41uGWzl9xw+WYhvOL47BV09Dgfnm/tU4ieeZ7NAR
# 5bguorTCNr58HOcA1tcsHQqt0wJsdClsu8bpQD9e/al+lUgTUJEV80Xhco7xdgRr
# ehbyhUf4pkeAhBEjABvIUpD2LKPho5Z4DPCT5/0TlK02nlPwUbv9URREhVYCtsDM
# +31OFU3fDV8BmQXv5hT2RurVsJHZgP4y26dJDVF+3pcbtvh7R6NEDuYHYihfmE2H
# dQRq5jRvLE1Eb59PYwISFCX2DaLZ+zpU4bX0I16ntKq4poGOFaaKtjIA1vRElIta
# OKcwtc04CBrXSfyL2Op6mvNIxTk4OaswIkTXbFL81ZKGD+24uMCwo/pLNhn7VHLf
# nxlMVzHQVL+bHa9KhTyzwdG/L6uderJQn0cGpLQMStUuNDArxW2wF16QGZ1NtBWg
# KA8Kqv48M8HfFqNifN6+zt6J0GwzvU8g0rYGgTZR8zDEIJfeZxwWDHpSxB5FJ1VV
# U1LIAtB7o9PXbjXzGifaIMYTzU4YKt4vMNwwBmetQDHhdAtTPplOXrnI9SI6HeTt
# jDD3iUN/7ygbahmYOHk7VB7fwT4ze+ErCbMh6gHV1UuXPiLciloNxH6K4aMfZN1o
# LVk6YFeIJEokuPgNPa6EnTiOL60cPqfny+Fq8UiuZzGCF1UwghdRAgEBMHEwWjEL
# MAkGA1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjErMCkG
# A1UEAxMiTWljcm9zb2Z0IElEIFZlcmlmaWVkIENTIEFPQyBDQSAwNAITMwABE7gv
# +luilU99CwAAAAETuDALBglghkgBZQMEAgGgfDAQBgorBgEEAYI3AgEMMQIwADAZ
# BgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYB
# BAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQgO0Fz07AhorzFbqfJ6dLxEZIsbdp+71Kj
# fWtOid43wv4wCwYJKoZIhvcNAQEBBIIBgCMmU7LHsg6kdOHYxLlYsSVGhaOlRP25
# uhOUlAcmYemaQKejx8tOfiQf5qNZ7m3MGx8TUafiP8DX1+tqn9J1zPRnrFPerT4K
# vcfWgJb+QHizqIZIDAIWkvFkscqQOHFrwcKkqPX2braY2BvS8dgSTM3Nhkzy45ki
# xmVpt8Vh/1YOxDJbJ1QdYzx3wLLTdPHqZ0fJPj5FVCqNcukSbYIk+kRKv7phhmyq
# MZUH3Q0djBm6SiXfwoLdBDBAhq001CMPRmCRa8rVFrmeV77Y3ByWvC8sIuxL+B5N
# gruAQTez+lZcKJ2jOyGGWbHzlE4nsGr1unead8OenXGz37aJNf8PRnD34QhKuLzE
# LB6LK16Dc/ME/8yO4tb9U/529Nmj7qQrYn0P4DDZ0R67w/f5Svln+fANsOsT0AcE
# R7CLRO8CfLiIhHAfqq8nG6RrKHVcmQCDdnDXDD5iifqAy5/GL8zI1S7R3EyrA05x
# 6p30ozlPt4DEKMBCSP/d9GBBum4G6/aRNqGCFLswghS3BgorBgEEAYI3AwMBMYIU
# pzCCFKMGCSqGSIb3DQEHAqCCFJQwghSQAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFz
# BgsqhkiG9w0BCRABBKCCAWIEggFeMIIBWgIBAQYKKwYBBAGEWQoDATAxMA0GCWCG
# SAFlAwQCAQUABCBBNXOLfsP2ppAJrUDCXzZzZMivdXsuu3OfRZSt4UTNIgIGafSH
# ByCLGBIyMDI2MDUxNzAxMTIwMy42N1owBIACAfQCCH747mRa04BLoIHppIHmMIHj
# MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVk
# bW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRN
# aWNyb3NvZnQgSXJlbGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5T
# aGllbGQgVFNTIEVTTjo0OTFBLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0
# IFB1YmxpYyBSU0EgVGltZSBTdGFtcGluZyBBdXRob3JpdHmggg8pMIIHgjCCBWqg
# AwIBAgITMwAAAAXlzw//Zi7JhwAAAAAABTANBgkqhkiG9w0BAQwFADB3MQswCQYD
# VQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMUgwRgYDVQQD
# Ez9NaWNyb3NvZnQgSWRlbnRpdHkgVmVyaWZpY2F0aW9uIFJvb3QgQ2VydGlmaWNh
# dGUgQXV0aG9yaXR5IDIwMjAwHhcNMjAxMTE5MjAzMjMxWhcNMzUxMTE5MjA0MjMx
# WjBhMQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9u
# MTIwMAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0Eg
# MjAyMDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAJ5851Jj/eDFnwV9
# Y7UGIqMcHtfnlzPREwW9ZUZHd5HBXXBvf7KrQ5cMSqFSHGqg2/qJhYqOQxwuEQXG
# 8kB41wsDJP5d0zmLYKAY8Zxv3lYkuLDsfMuIEqvGYOPURAH+Ybl4SJEESnt0MbPE
# oKdNihwM5xGv0rGofJ1qOYSTNcc55EbBT7uq3wx3mXhtVmtcCEr5ZKTkKKE1CxZv
# NPWdGWJUPC6e4uRfWHIhZcgCsJ+sozf5EeH5KrlFnxpjKKTavwfFP6XaGZGWUG8T
# ZaiTogRoAlqcevbiqioUz1Yt4FRK53P6ovnUfANjIgM9JDdJ4e0qiDRm5sOTiEQt
# BLGd9Vhd1MadxoGcHrRCsS5rO9yhv2fjJHrmlQ0EIXmp4DhDBieKUGR+eZ4CNE3c
# tW4uvSDQVeSp9h1SaPV8UWEfyTxgGjOsRpeexIveR1MPTVf7gt8hY64XNPO6iyUG
# sEgt8c2PxF87E+CO7A28TpjNq5eLiiunhKbq0XbjkNoU5JhtYUrlmAbpxRjb9tSr
# eDdtACpm3rkpxp7AQndnI0Shu/fk1/rE3oWsDqMX3jjv40e8KN5YsJBnczyWB4Jy
# eeFMW3JBfdeAKhzohFe8U5w9WuvcP1E8cIxLoKSDzCCBOu0hWdjzKNu8Y5SwB1lt
# 5dQhABYyzR3dxEO/T1K/BVF3rV69AgMBAAGjggIbMIICFzAOBgNVHQ8BAf8EBAMC
# AYYwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFGtpKDo1L0hjQM972K9J6T7Z
# PdshMFQGA1UdIARNMEswSQYEVR0gADBBMD8GCCsGAQUFBwIBFjNodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9yeS5odG0wEwYDVR0l
# BAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEwDwYDVR0T
# AQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTIftJqhSobyhmYBAcnz1AQT2ioojCBhAYD
# VR0fBH0wezB5oHegdYZzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9j
# cmwvTWljcm9zb2Z0JTIwSWRlbnRpdHklMjBWZXJpZmljYXRpb24lMjBSb290JTIw
# Q2VydGlmaWNhdGUlMjBBdXRob3JpdHklMjAyMDIwLmNybDCBlAYIKwYBBQUHAQEE
# gYcwgYQwgYEGCCsGAQUFBzAChnVodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
# b3BzL2NlcnRzL01pY3Jvc29mdCUyMElkZW50aXR5JTIwVmVyaWZpY2F0aW9uJTIw
# Um9vdCUyMENlcnRpZmljYXRlJTIwQXV0aG9yaXR5JTIwMjAyMC5jcnQwDQYJKoZI
# hvcNAQEMBQADggIBAF+Idsd+bbVaFXXnTHho+k7h2ESZJRWluLE0Oa/pO+4ge/XE
# izXvhs0Y7+KVYyb4nHlugBesnFqBGEdC2IWmtKMyS1OWIviwpnK3aL5JedwzbeBF
# 7POyg6IGG/XhhJ3UqWeWTO+Czb1c2NP5zyEh89F72u9UIw+IfvM9lzDmc2O2END7
# MPnrcjWdQnrLn1Ntday7JSyrDvBdmgbNnCKNZPmhzoa8PccOiQljjTW6GePe5sGF
# uRHzdFt8y+bN2neF7Zu8hTO1I64XNGqst8S+w+RUdie8fXC1jKu3m9KGIqF4aldr
# YBamyh3g4nJPj/LR2CBaLyD+2BuGZCVmoNR/dSpRCxlot0i79dKOChmoONqbMI8m
# 04uLaEHAv4qwKHQ1vBzbV/nG89LDKbRSSvijmwJwxRxLLpMQ/u4xXxFfR4f/gksS
# kbJp7oqLwliDm/h+w0aJ/U5ccnYhYb7vPKNMN+SZDWycU5ODIRfyoGl59BsXR/Hp
# RGtiJquOYGmvA/pk5vC1lcnbeMrcWD/26ozePQ/TWfNXKBOmkFpvPE8CH+EeGGWz
# qTCjdAsno2jzTeNSxlx3glDGJgcdz5D/AAxw9Sdgq/+rY7jjgs7X6fqPTXPmaCAJ
# KVHAP19oEjJIBwD1LyHbaEgBxFCogYSOiUIr0Xqcr1nJfiWG2GwYe6ZoAF1bMIIH
# nzCCBYegAwIBAgITMwAAAFr2DWeMhe3dCAAAAAAAWjANBgkqhkiG9w0BAQwFADBh
# MQswCQYDVQQGEwJVUzEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMTIw
# MAYDVQQDEylNaWNyb3NvZnQgUHVibGljIFJTQSBUaW1lc3RhbXBpbmcgQ0EgMjAy
# MDAeFw0yNjAxMDgxODU5MDNaFw0yNzAxMDcxODU5MDNaMIHjMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# Tjo0OTFBLTA1RTAtRDk0NzE1MDMGA1UEAxMsTWljcm9zb2Z0IFB1YmxpYyBSU0Eg
# VGltZSBTdGFtcGluZyBBdXRob3JpdHkwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQDv9DtHlo4FG/a5x3EC0XX1jzeB/vdWt2J5Wj+OhNZuLg/iSsenLFjE
# rV+/X8SyVAKhyakFhNfVJTTIUvYxAd9nQCeMrlrB8lLjPnaYYw8+BOY5eIGBmRJm
# jqyrzbfiqpRWssoP6E4NwZS0buGgavOuvAOapR3H7Loeg3UqhSA9YsSRWcx62RRt
# YhgRu1NQZ9jjSw6O428F+azHD3bkwFMP3OzN1oUsUAbmaUIs/EUBiiKginyMYEJC
# nc6QqVNElkDhPw4E12893NeSjEQnZBpS9s2/pZ7kleLLHkJt6n5WYmXJC8p9sSSP
# GVw5KviIPm/67DzyHyPHYttvFrytY+uyV6cnlQmklDvRK85S506540JHl1UCKe98
# blQVa5r6E3/7+GuzJ65riksiF3ObyVBxBgd+OfjvKJhbdcPG/l4PQ/TyiEagzxL+
# x0ZNAvmL8bvBbxyb0qHEiGSvr/xZihToqWJ6T++sgJiTZ8oXrnEoToJPEIlOd1Ep
# //gMjG+8VvdOYGZ8jam9vR3lXUNe+aQxyRhM/AsecIh3lZYhs+YQAbnBQ8pUfc9y
# 0k5gevt2biMXhvQWUuOj+gDT5Llbg+ZvMIHOxiy84O9wrAxdPbpLfFH/HU3DAV96
# 6Pu/5PTOl7fFjxuyC/b/+A78jGNN7ZG/WPUYuh1mr10T2EQlHj7KTwIDAQABo4IB
# yzCCAccwHQYDVR0OBBYEFI5jskOrcDHD9WW0crScSOH515nCMB8GA1UdIwQYMBaA
# FGtpKDo1L0hjQM972K9J6T7ZPdshMGwGA1UdHwRlMGMwYaBfoF2GW2h0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY3Jvc29mdCUyMFB1YmxpYyUy
# MFJTQSUyMFRpbWVzdGFtcGluZyUyMENBJTIwMjAyMC5jcmwweQYIKwYBBQUHAQEE
# bTBrMGkGCCsGAQUFBzAChl1odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NlcnRzL01pY3Jvc29mdCUyMFB1YmxpYyUyMFJTQSUyMFRpbWVzdGFtcGluZyUy
# MENBJTIwMjAyMC5jcnQwDAYDVR0TAQH/BAIwADAWBgNVHSUBAf8EDDAKBggrBgEF
# BQcDCDAOBgNVHQ8BAf8EBAMCB4AwZgYDVR0gBF8wXTBRBgwrBgEEAYI3TIN9AQEw
# QTA/BggrBgEFBQcCARYzaHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9E
# b2NzL1JlcG9zaXRvcnkuaHRtMAgGBmeBDAEEAjANBgkqhkiG9w0BAQwFAAOCAgEA
# gJTPK/rd4SG9odZQ0wMfomJEJcRblO0DjXsiBSAWdvcbxpfKXoSsRKw8yJ4jqSF/
# 3pblAMECqGiNM8LigHUdJq0h1wp4jKCzZKOVvua+7FzWpil+0DFdwXxjl94IvalX
# 8clHuwB126kPCgUBzWApzRbUEB4AEa5WIbcgCqJeXLTNAgHP8t6GO20zLFSb5wOb
# uw1Vj3l6Ek9ihDA3iwbyWKtCCWesjKQzli2eFD/Nm5LMkoSAf13WsIqyBi3bHrna
# 8kcVTJN9d5gHIxkW+ffiLbbeqLVb2EFqh/jxq01MxHHs/GkLjt7pqDWYyrhaF+Vb
# Dz/4EmbHvqK4Rt832ZPSA8hNw31Ba4b5L+h9LYoytQ9LiBocDAVkpvZLUOqHlPmr
# q2RcdzCPizg5x7G0RWQMtdbjL8CqvmTQtUnuLSDNbvaYlgIZ0z6IeplCyopBzlYR
# 2jved7ZMwwrY7LHuRlCjsfcoPZ6hyljIJzg6etv7jz8wv8gxCEq1wnFO1Ae7QE69
# 81jRHbTOHdNPYl1iOnVf0nXCCF+OK6aC8gi8Pp148afn2P3coCzu1HUkGlWlBVY9
# ytp1crroz7KbeS7p3ORb7mD6pov8/JAEppsG4hfltD9FE1hWgvODFn1NoejA4ObN
# KMZnTRSu+o1698GX3UqFexQin6uRnXhqMuVMA0MDAtAxggPUMIID0AIBATB4MGEx
# CzAJBgNVBAYTAlVTMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xMjAw
# BgNVBAMTKU1pY3Jvc29mdCBQdWJsaWMgUlNBIFRpbWVzdGFtcGluZyBDQSAyMDIw
# AhMzAAAAWvYNZ4yF7d0IAAAAAABaMA0GCWCGSAFlAwQCAQUAoIIBLTAaBgkqhkiG
# 9w0BCQMxDQYLKoZIhvcNAQkQAQQwLwYJKoZIhvcNAQkEMSIEIMFgtBq/PWDDaXHm
# 7f7F8R6n+ponBlAM8ZoHM9T1gqsKMIHdBgsqhkiG9w0BCRACLzGBzTCByjCBxzCB
# oAQgYrlkQIvqfej0bAbd08Ft4zaM4D0EvkHKNQlZvzWlNEMwfDBlpGMwYTELMAkG
# A1UEBhMCVVMxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UE
# AxMpTWljcm9zb2Z0IFB1YmxpYyBSU0EgVGltZXN0YW1waW5nIENBIDIwMjACEzMA
# AABa9g1njIXt3QgAAAAAAFowIgQgKQwCtB9cB75lcSb2HP5OSTznFZ4fMbLJpwWo
# Ouq4ri8wDQYJKoZIhvcNAQELBQAEggIAq9ooI5+8ZVhPjAAq13rpUGUcHPDfy0yK
# dHgt/tMflvF8eWt223dWtOsOZMuXZzUika96QCZddAtwbWNs7+4ci51pIpQmpS17
# saxirr0MFeFvAXs85hyPGQit8w9QSNiY8OJAAigvUqpocL6mXi58g8pEovFNAIeN
# EcDhBcDCXw6rzc7XxVJlzsMQC+1bzgTTvD26duZ9Cig3ol20ShQbuP5I7kSqRkVp
# 6aU+omJ8y1ikB7IbfZMGHxPw9bIMGEjjxPGUSVN/+upVQo16ifFCEulANy3IdP6e
# JhfJ2vcNpi3LMqDD9kRBloiy2ysu6In0XG5XcaQ0HKbN07twDITWDbp3ErCGrRwa
# 5rt1mvJL3Hwd9x3pw0fWHZ/UI/vYCRWrcQNUNN+SOpcwwnUVGIZsIDZs2OITit1U
# h6iNxU3nRbwvuJtauMtNpMFKT8O1QjlOAfEkTSSRkUbW+GPF2sQv/l9x77imDDBw
# dbhPBJ3crJC+LBYnNFmO9OHNEC5RL7lWmtj4/ufWKx+bPuNFzkDjRz7wyygKX8Kq
# 1csCMFkPRGMlhXB3bIa6MWExWtqe2s1mA+H3zWlnRm244ZNlibGy448aTP8mgJNV
# /pVQbUx5oQIr1qp387hnudL3TvIcFPb1vNWWwZSYuZwgWAFVlp6gpk4yf9Csq1xg
# zXvZk3phv28=
# SIG # End signature block