2026-06-16 20:51:01 +01:00
param (
[ string ] $VoidBotRoot = " E:\Projects\VoidBot " ,
[ int ] $MaxOrchestratorAgeSeconds = 180 ,
[ int ] $MaxCultMeshStoreAgeSeconds = 180
)
2026-06-07 02:43:24 +01:00
Set-StrictMode -Version Latest
$ErrorActionPreference = " Stop "
2026-06-16 20:51:01 +01:00
function Fail-IdunnHealth {
param (
[ string ] $State ,
[ string ] $Message
)
Write-Output " idunn.health.state= $State "
throw $Message
}
function Read-JsonFile {
param ( [ Parameter ( Mandatory = $true ) ] [ string ] $Path )
if ( -not ( Test-Path -LiteralPath $Path ) ) {
return $null
}
$raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
if ( [ string ] :: IsNullOrWhiteSpace ( $raw ) ) {
return $null
}
return $raw | ConvertFrom-Json
}
2026-06-17 02:58:20 +01:00
function Get-JsonPropertyValue {
param (
[ Parameter ( Mandatory = $false ) ] $Object ,
[ Parameter ( Mandatory = $true ) ] [ string ] $Name ,
[ Parameter ( Mandatory = $false ) ] $Default = $null
)
if ( $null -eq $Object ) {
return $Default
}
$property = $Object . PSObject . Properties [ $Name ]
if ( $null -eq $property ) {
return $Default
}
return $property . Value
}
2026-06-16 20:51:01 +01:00
function Get-ProcessOrFail {
param (
[ Parameter ( Mandatory = $true ) ] [ string ] $Role ,
[ Parameter ( Mandatory = $true ) ] [ int ] $ProcessId
)
$process = Get-Process -Id $ProcessId -ErrorAction SilentlyContinue
if ( $null -eq $process ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot $Role process PID $ProcessId is not running. "
}
return $process
}
function Assert-StoreContains {
param (
[ Parameter ( Mandatory = $true ) ] [ string ] $StorePath ,
[ Parameter ( Mandatory = $true ) ] [ string[] ] $Tokens
)
$rg = ( Get-Command rg . exe -ErrorAction SilentlyContinue ) . Source
if ( [ string ] :: IsNullOrWhiteSpace ( $rg ) ) {
Fail-IdunnHealth -State " degraded " -Message " ripgrep is required to inspect the VoidBot CultMesh witness store. "
}
foreach ( $token in $Tokens ) {
& $rg -a -q - -fixed -strings - - $token $StorePath
if ( $LASTEXITCODE -ne 0 ) {
Fail-IdunnHealth -State " degraded " -Message " VoidBot CultMesh witness store is missing ' $token '. "
}
}
}
$runtimePath = Join-Path $VoidBotRoot " .voidbot\status\runtime-stack.json "
$orchestratorPath = Join-Path $VoidBotRoot " .voidbot\status\gamecult-orchestrator.json "
$cultMeshStorePath = Join-Path $VoidBotRoot " .voidbot\status\cultmesh\voidbot-swarm-state.cc "
2026-06-07 02:43:24 +01:00
2026-06-16 20:51:01 +01:00
$runtime = Read-JsonFile -Path $runtimePath
if ( $null -eq $runtime ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot runtime state is missing at $runtimePath . "
2026-06-07 02:43:24 +01:00
}
2026-06-17 02:58:20 +01:00
if ( -not [ bool ] ( Get-JsonPropertyValue -Object $runtime -Name " ready " -Default $false ) ) {
2026-06-16 20:51:01 +01:00
Fail-IdunnHealth -State " failed " -Message " VoidBot runtime stack reports ready=false. "
}
2026-06-17 02:58:20 +01:00
$runtimeStage = [ string ] ( Get-JsonPropertyValue -Object $runtime -Name " stage " -Default " unknown " )
if ( $runtimeStage -ne " ready " ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot runtime stack stage is ' $runtimeStage ', expected 'ready'. "
2026-06-16 20:51:01 +01:00
}
2026-06-17 02:58:20 +01:00
$bot = Get-JsonPropertyValue -Object $runtime -Name " bot "
$worker = Get-JsonPropertyValue -Object $runtime -Name " worker "
$botPid = if ( $null -ne $bot ) { [ int ] ( Get-JsonPropertyValue -Object $bot -Name " pid " -Default 0 ) } else { 0 }
$workerPid = if ( $null -ne $worker ) { [ int ] ( Get-JsonPropertyValue -Object $worker -Name " pid " -Default 0 ) } else { 0 }
2026-06-16 20:51:01 +01:00
if ( $botPid -le 0 ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot runtime state does not contain a live bot PID. "
}
if ( $workerPid -le 0 ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot runtime state does not contain a live worker PID. "
2026-06-07 02:43:24 +01:00
}
2026-06-16 20:51:01 +01:00
$botProcess = Get-ProcessOrFail -Role " bot " -ProcessId $botPid
$workerProcess = Get-ProcessOrFail -Role " worker " -ProcessId $workerPid
$orchestrator = Read-JsonFile -Path $orchestratorPath
if ( $null -eq $orchestrator -or $null -eq $orchestrator . organs ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot orchestrator state is missing at $orchestratorPath . "
}
$surfaceState = $orchestrator . organs . PSObject . Properties [ " voidbot-swarm-surface " ]
if ( $null -eq $surfaceState ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot orchestrator does not report the voidbot-swarm-surface organ. "
}
$surfaceState = $surfaceState . Value
if ( $surfaceState . lastStatus -ne " ok " ) {
Fail-IdunnHealth -State " degraded " -Message " VoidBot swarm surface organ lastStatus is ' $( $surfaceState . lastStatus ) ', expected 'ok'. "
}
if ( [ string ] :: IsNullOrWhiteSpace ( $surfaceState . lastFinishedAt ) ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot swarm surface organ has no lastFinishedAt timestamp. "
}
$surfaceFinishedAt = [ DateTimeOffset ] :: Parse ( $surfaceState . lastFinishedAt ) . ToUniversalTime ( )
$surfaceAgeSeconds = ( [ DateTimeOffset ] :: UtcNow - $surfaceFinishedAt ) . TotalSeconds
if ( $surfaceAgeSeconds -gt $MaxOrchestratorAgeSeconds ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot swarm surface organ is stale ( $( [ math ] :: Round ( $surfaceAgeSeconds ) ) s old). "
}
$orchestratorFile = Get-Item -LiteralPath $orchestratorPath -ErrorAction SilentlyContinue
if ( $null -eq $orchestratorFile ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot orchestrator state file is missing at $orchestratorPath . "
}
$orchestratorAgeSeconds = ( [ DateTimeOffset ] :: UtcNow - [ DateTimeOffset ] $orchestratorFile . LastWriteTimeUtc ) . TotalSeconds
if ( $orchestratorAgeSeconds -gt $MaxOrchestratorAgeSeconds ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot orchestrator state file is stale ( $( [ math ] :: Round ( $orchestratorAgeSeconds ) ) s old). "
}
$cultMeshStore = Get-Item -LiteralPath $cultMeshStorePath -ErrorAction SilentlyContinue
if ( $null -eq $cultMeshStore ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot CultMesh witness store is missing at $cultMeshStorePath . "
}
if ( $cultMeshStore . Length -le 0 ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot CultMesh witness store at $cultMeshStorePath is empty. "
}
$cultMeshStoreAgeSeconds = ( [ DateTimeOffset ] :: UtcNow - [ DateTimeOffset ] $cultMeshStore . LastWriteTimeUtc ) . TotalSeconds
if ( $cultMeshStoreAgeSeconds -gt $MaxCultMeshStoreAgeSeconds ) {
Fail-IdunnHealth -State " failed " -Message " VoidBot CultMesh witness store is stale ( $( [ math ] :: Round ( $cultMeshStoreAgeSeconds ) ) s old). "
}
Assert-StoreContains -StorePath $cultMeshStorePath -Tokens @ (
" voidbot.provider_advertisement_catalog.v0 " ,
" idunn.command_boundary.v1 " ,
" idunn.daemon_transport_profile.v1 " ,
" voidbot.providers " ,
" voidbot.discord " ,
" voidbot.reddit " ,
" voidbot.archive " ,
" voidbot.source " ,
" voidbot.repo_face " ,
" voidbot.swarm " ,
" command-boundary:voidbot " ,
" transport:voidbot "
)
Write-Host " VoidBot runtime is ready, bot PID $( $botProcess . Id ) and worker PID $( $workerProcess . Id ) are live, and the daemon-owned CultMesh witness store is fresh. "