Files
awoooi/agent99-contract-check.ps1

96 lines
5.0 KiB
PowerShell

param(
[string]$SourceRoot = (Split-Path -Parent $MyInvocation.MyCommand.Path),
[string]$OutputPath = ""
)
$ErrorActionPreference = "Stop"
$checks = @()
$failures = @()
function Add-Check {
param([string]$Name, [bool]$Ok, [string]$Detail)
$item = [pscustomobject]@{ name = $Name; ok = $Ok; detail = $Detail }
$script:checks += $item
if (-not $Ok) { $script:failures += $item }
}
$requiredFiles = @(
"agent99-bootstrap.ps1",
"agent99-contract-check.ps1",
"agent99-control-plane.ps1",
"agent99-deploy.ps1",
"agent99-register-tasks.ps1",
"agent99-sre-alert-inbox.ps1",
"agent99-sre-alert-relay.ps1",
"agent99-submit-request.ps1",
"agent99-synthetic-tests.ps1",
"agent99-telegram-inbox.ps1",
"agent99-windows-update-policy.ps1",
"host-reboot-scorecard.ps1"
)
foreach ($name in $requiredFiles) {
$path = Join-Path $SourceRoot $name
Add-Check "file:$name" (Test-Path $path) $path
}
foreach ($file in Get-ChildItem -Path $SourceRoot -Filter "*.ps1" -File) {
$tokens = $null
$errors = $null
$sourceText = [IO.File]::ReadAllText($file.FullName, [Text.Encoding]::UTF8)
[System.Management.Automation.Language.Parser]::ParseInput($sourceText, [ref]$tokens, [ref]$errors) | Out-Null
Add-Check "powershell_parse:$($file.Name)" ($errors.Count -eq 0) $(if ($errors.Count -eq 0) { "parse_ok" } else { "parse_errors=$($errors.Count)" })
}
foreach ($name in @("agent99.config.example.json", "agent99.config.99.example.json")) {
$path = Join-Path $SourceRoot $name
try {
$config = Get-Content $path -Raw | ConvertFrom-Json
$hasHosts = @($config.hosts).Count -eq 5
$hasRoutes = @($config.publicUrls).Count -ge 5
$hasSlo = [bool]($config.recoverySlo -and [int]$config.recoverySlo.targetMinutes -eq 10)
$hasIdentity = [bool]($config.sshIdentityFile -and $config.sshIdentityFile -match "agent99_ed25519$")
$hasJump = [bool]($config.k3s -and $config.k3s.jumpHost -and $config.k3s.preferJumpHost -eq $true)
$hasAutoRecovery = [bool]($config.autoRecovery -and $config.autoRecovery.enabled -eq $true)
$ok = $hasHosts -and $hasRoutes -and $hasSlo -and $hasIdentity -and $hasJump -and $hasAutoRecovery
Add-Check "config:$name" $ok "hosts=$(@($config.hosts).Count) routes=$(@($config.publicUrls).Count) slo10m=$hasSlo identity=$hasIdentity jump=$hasJump autoRecovery=$hasAutoRecovery"
} catch {
Add-Check "config:$name" $false "json_parse_failed: $($_.Exception.Message)"
}
}
$control = [IO.File]::ReadAllText((Join-Path $SourceRoot "agent99-control-plane.ps1"), [Text.Encoding]::UTF8)
$inbox = [IO.File]::ReadAllText((Join-Path $SourceRoot "agent99-sre-alert-inbox.ps1"), [Text.Encoding]::UTF8)
$telegram = [IO.File]::ReadAllText((Join-Path $SourceRoot "agent99-telegram-inbox.ps1"), [Text.Encoding]::UTF8)
$bootstrap = [IO.File]::ReadAllText((Join-Path $SourceRoot "agent99-bootstrap.ps1"), [Text.Encoding]::UTF8)
Add-Check "transport:dedicated_identity" ($control.Contains("sshIdentityFile") -and $control.Contains("IdentitiesOnly=yes")) "dedicated SSH identity is enforced"
Add-Check "transport:jump_first" ($control.Contains("preferJumpHost") -and $control.Contains("ssh_jump_fallback")) "preferred jump with bounded fallback is present"
Add-Check "recovery:auto_trigger" ($control.Contains("Get-AgentBootState") -and $control.Contains("Start-AgentRecoveryFromObservation") -and $control.Contains("recovery_already_queued")) "boot and host-down recovery use a single-flight queue"
Add-Check "recovery:slo" ($control.Contains("recovery_slo_result") -and $control.Contains("withinSlo")) "10-minute recovery evidence is present"
Add-Check "outcome:verified" ($control.Contains("Get-AgentOutcomeContract") -and $control.Contains('schemaVersion = "agent99_outcome_contract_v1"')) "verified outcome contract is present"
Add-Check "problem_counts:v2" ($control.Contains('schemaVersion = "agent99_problem_counts_v2"') -and $control.Contains("totalVerifiedResolved")) "verified problem counts are present"
Add-Check "runtime:manifest" ($control.Contains("Test-AgentRuntimeManifest") -and $bootstrap.Contains('schemaVersion = "agent99_runtime_manifest_v1"')) "runtime source drift is self-checked"
Add-Check "routing:structured" ($inbox.Contains("Resolve-AgentAlertRoute") -and $inbox.Contains('schemaVersion = "agent99_sre_alert_single_flight_v1"')) "structured route and local single-flight are present"
Add-Check "routing:telegram" ($telegram.Contains('schemaVersion = "agent99_alert_route_v1"') -and $telegram.Contains("autoIngestMonitoringAlerts")) "Telegram structured ingress is present"
$result = [pscustomobject]@{
timestamp = (Get-Date -Format o)
sourceRoot = $SourceRoot
ok = [bool](@($failures).Count -eq 0)
checkCount = @($checks).Count
failureCount = @($failures).Count
checks = $checks
failures = $failures
}
if ($OutputPath) {
$parent = Split-Path -Parent $OutputPath
if ($parent) { New-Item -ItemType Directory -Force -Path $parent | Out-Null }
$result | ConvertTo-Json -Depth 8 | Set-Content -Path $OutputPath -Encoding UTF8
}
$result | ConvertTo-Json -Depth 8
if (-not $result.ok) { exit 1 }
exit 0