← Files Chronos for CodexARCHIVED FILE
skills/chronos/scripts/heartbeat.ps1
177 KB · Oct 4, 2026 · 12:30 UTC
param(
[string]$Action = 'cycle',
[string]$InputPath,
[string]$InspectorOutputPath,
[switch]$InspectorAuthorized,
[string]$StatePath,
[string]$CodexHome,
[string]$Scope,
[string]$EventId,
[string]$CorroboratingEventId,
[string]$InterventionId,
[int]$InterventionVersion = 0,
[string]$TargetId,
[string]$TargetGeneration,
[string]$GovernorId,
[string]$ClaimToken,
[ValidateSet('', 'accepted', 'definite_failure', 'unknown')]
[string]$TransportResult = '',
[ValidateSet('', 'acknowledged', 'outcome_reported', 'declined', 'user_authority_required', 'remediation_failed')]
[string]$TaskResponse = '',
[ValidateSet('', 'host_inventory', 'host_test', 'host_git')]
[string]$VerificationSource = '',
[ValidateSet('', 'resolved', 'active', 'failed')]
[string]$VerificationResult = '',
[ValidateSet('', 'ambiguous_target', 'target_not_live', 'transport_unavailable', 'user_authority_required', 'unsupported_action')]
[string]$FailureReason = '',
[ValidateRange(5, 1440)]
[int]$StallMinutes = 20
)
$ErrorActionPreference = 'Stop'
$script:PublicFamilies = @('agent_stall', 'guardian', 'usage', 'sessions', 'tests', 'machines', 'tasks', 'git_build')
$script:Families = @($script:PublicFamilies + 'heartbeat')
$script:PublicFamilyCount = $script:PublicFamilies.Count
$script:CadenceSeconds = [ordered]@{
agent_stall = 300
guardian = 300
usage = 300
sessions = 900
tests = 600
machines = 1800
tasks = 300
git_build = 600
heartbeat = 300
}
$script:SeverityRank = @{ INFO = 1; WARNING = 2; HIGH = 3; CRITICAL = 4 }
$script:ConditionLimit = 256
$script:EventLimit = 50
$script:RunIdLimit = 32
$script:OutboxLimit = 64
$script:OutboxRetrySeconds = 900
$script:OutboxMaxAttempts = 2
$script:InterventionLimit = 64
$script:InterventionClaimSeconds = 900
$script:InputByteLimit = 262144
$script:InspectorByteLimit = 65536
$script:StateByteLimit = 262144
$script:JsonNodeLimit = 32768
$script:StateStoreMode = 'unknown'
$script:StateStoreMigration = 'not_applicable'
$script:StateStoreWriteReady = $false
$script:StateStoreProtection = 'hashed_metadata'
$script:PriorStateDisposition = 'not_applicable'
$script:PriorStateWriteAttempted = $false
$script:CodexHomeSource = 'unresolved'
$script:CodexHomeIdentity = 'unresolved'
$script:AllowUnscopedLegacyMigration = $false
if (-not ('ChronosHeartbeatPathIdentity' -as [type])) {
$pathIdentitySource = @'
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32.SafeHandles;
public static class ChronosHeartbeatPathIdentity {
private const uint FILE_SHARE_READ = 1;
private const uint FILE_SHARE_WRITE = 2;
private const uint FILE_SHARE_DELETE = 4;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;
[StructLayout(LayoutKind.Sequential)]
private struct BY_HANDLE_FILE_INFORMATION {
public uint FileAttributes;
public System.Runtime.InteropServices.ComTypes.FILETIME CreationTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastAccessTime;
public System.Runtime.InteropServices.ComTypes.FILETIME LastWriteTime;
public uint VolumeSerialNumber;
public uint FileSizeHigh;
public uint FileSizeLow;
public uint NumberOfLinks;
public uint FileIndexHigh;
public uint FileIndexLow;
}
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern SafeFileHandle CreateFile(
string fileName, uint desiredAccess, uint shareMode, IntPtr securityAttributes,
uint creationDisposition, uint flagsAndAttributes, IntPtr templateFile);
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint GetFinalPathNameByHandle(
SafeFileHandle handle, StringBuilder path, uint length, uint flags);
[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool GetFileInformationByHandle(
SafeFileHandle handle, out BY_HANDLE_FILE_INFORMATION information);
public static string FinalDirectoryPath(string path) {
using (SafeFileHandle handle = CreateFile(path, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, IntPtr.Zero,
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, IntPtr.Zero)) {
if (handle.IsInvalid) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
StringBuilder buffer = new StringBuilder(32768);
uint length = GetFinalPathNameByHandle(handle, buffer, (uint)buffer.Capacity, 0);
if (length == 0 || length >= buffer.Capacity) throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
return buffer.ToString();
}
}
public static uint LinkCount(string path) {
using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read,
FileShare.ReadWrite | FileShare.Delete)) {
BY_HANDLE_FILE_INFORMATION information;
if (!GetFileInformationByHandle(stream.SafeFileHandle, out information))
throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
return information.NumberOfLinks;
}
}
}
'@
try { [void](Add-Type -TypeDefinition $pathIdentitySource -Language CSharp -ErrorAction Stop) } catch { throw 'heartbeat_runtime_unavailable' }
}
function Get-Value {
param($Object, [string]$Name, $Default = $null)
if ($null -eq $Object) { return $Default }
if ($Object -is [System.Collections.IDictionary]) {
if ($Object.Contains($Name) -and $null -ne $Object[$Name]) { return $Object[$Name] }
return $Default
}
$property = $Object.PSObject.Properties[$Name]
if ($null -eq $property -or $null -eq $property.Value) { return $Default }
return $property.Value
}
function Has-Value {
param($Object, [string]$Name)
return ($Object -is [System.Collections.IDictionary] -and $Object.Contains($Name))
}
function ConvertTo-Hashtable {
param($Value)
if ($null -eq $Value) { return $null }
if ($Value -is [System.Collections.IDictionary]) {
$copy = @{}
foreach ($key in $Value.Keys) { $copy[[string]$key] = ConvertTo-Hashtable $Value[$key] }
return $copy
}
# Windows PowerShell 5.1 can report piped scalar values as [pscustomobject].
# The concrete type check avoids converting strings into @{ Length = ... }.
if ($Value.GetType().FullName -eq 'System.Management.Automation.PSCustomObject') {
$copy = @{}
foreach ($property in $Value.PSObject.Properties) { $copy[$property.Name] = ConvertTo-Hashtable $property.Value }
return $copy
}
if ($Value -is [System.Collections.IEnumerable] -and -not ($Value -is [string])) {
$items = @($Value | ForEach-Object { ConvertTo-Hashtable $_ })
return ,$items
}
return $Value
}
function As-Array {
param($Value)
if ($null -eq $Value) { return @() }
return @($Value)
}
function Get-StableHash {
param($Value)
if ($null -eq $Value) { $Value = '__null__' }
$json = $Value | ConvertTo-Json -Compress -Depth 16
if ($null -eq $json) { $json = '[]' }
$bytes = [Text.Encoding]::UTF8.GetBytes($json)
$sha = [Security.Cryptography.SHA256]::Create()
try {
return ([BitConverter]::ToString($sha.ComputeHash($bytes))).Replace('-', '').ToLowerInvariant()
} finally {
$sha.Dispose()
}
}
function Format-Number {
param([double]$Value)
return $Value.ToString('0.###', [Globalization.CultureInfo]::InvariantCulture)
}
function ConvertTo-UtcTimestamp {
param($Value, [string]$ErrorCode = 'heartbeat_input_invalid')
if (-not ($Value -is [string]) -or $Value.Length -gt 40) { throw $ErrorCode }
$parsed = [DateTimeOffset]::MinValue
if (-not [DateTimeOffset]::TryParse($Value, [Globalization.CultureInfo]::InvariantCulture, [Globalization.DateTimeStyles]::AssumeUniversal, [ref]$parsed)) {
throw $ErrorCode
}
return $parsed.ToUniversalTime()
}
function Test-IsNumber {
param($Value)
return ($Value -is [byte] -or $Value -is [sbyte] -or $Value -is [int16] -or
$Value -is [uint16] -or $Value -is [int32] -or $Value -is [uint32] -or
$Value -is [int64] -or $Value -is [uint64] -or $Value -is [single] -or
$Value -is [double] -or $Value -is [decimal])
}
function Test-IsInteger {
param($Value)
return ($Value -is [byte] -or $Value -is [sbyte] -or $Value -is [int16] -or
$Value -is [uint16] -or $Value -is [int32] -or $Value -is [uint32] -or
$Value -is [int64] -or $Value -is [uint64])
}
function Move-JsonWhitespace {
param([string]$Text, [ref]$Index)
while ($Index.Value -lt $Text.Length -and $Text[$Index.Value] -in @(' ', "`t", "`r", "`n")) { $Index.Value++ }
}
function Read-StrictJsonString {
param([string]$Text, [ref]$Index, [string]$ErrorCode)
if ($Index.Value -ge $Text.Length -or $Text[$Index.Value] -ne '"') { throw $ErrorCode }
$start = $Index.Value
$Index.Value++
while ($Index.Value -lt $Text.Length) {
$character = $Text[$Index.Value]
if ([int][char]$character -lt 0x20) { throw $ErrorCode }
if ($character -eq '"') {
$Index.Value++
$literal = $Text.Substring($start, $Index.Value - $start)
try {
$decoded = $literal | ConvertFrom-Json -ErrorAction Stop
if (-not ($decoded -is [string])) { throw $ErrorCode }
return $decoded.Normalize([Text.NormalizationForm]::FormC)
} catch { throw $ErrorCode }
}
if ($character -eq '\') {
$Index.Value++
if ($Index.Value -ge $Text.Length) { throw $ErrorCode }
$escape = $Text[$Index.Value]
if ($escape -eq 'u') {
if ($Index.Value + 4 -ge $Text.Length -or $Text.Substring($Index.Value + 1, 4) -notmatch '^[0-9A-Fa-f]{4}$') { throw $ErrorCode }
$Index.Value += 5
continue
}
if ($escape -notin @('"', '\', '/', 'b', 'f', 'n', 'r', 't')) { throw $ErrorCode }
}
$Index.Value++
}
throw $ErrorCode
}
function Assert-StrictJsonValue {
param([string]$Text, [ref]$Index, [ref]$NodeCount, [int]$Depth, [string]$ErrorCode)
if ($Depth -gt 20) { throw $ErrorCode }
Move-JsonWhitespace $Text $Index
if ($Index.Value -ge $Text.Length) { throw $ErrorCode }
$NodeCount.Value++
if ($NodeCount.Value -gt $script:JsonNodeLimit) { throw $ErrorCode }
$character = $Text[$Index.Value]
if ($character -eq '"') {
[void](Read-StrictJsonString $Text $Index $ErrorCode)
return
}
if ($character -eq '{') {
$Index.Value++
Move-JsonWhitespace $Text $Index
$keys = New-Object 'Collections.Generic.HashSet[string]' ([StringComparer]::OrdinalIgnoreCase)
if ($Index.Value -lt $Text.Length -and $Text[$Index.Value] -eq '}') { $Index.Value++; return }
while ($true) {
Move-JsonWhitespace $Text $Index
$key = Read-StrictJsonString $Text $Index $ErrorCode
if (-not $keys.Add($key)) { throw $ErrorCode }
Move-JsonWhitespace $Text $Index
if ($Index.Value -ge $Text.Length -or $Text[$Index.Value] -ne ':') { throw $ErrorCode }
$Index.Value++
Assert-StrictJsonValue $Text $Index $NodeCount ($Depth + 1) $ErrorCode
Move-JsonWhitespace $Text $Index
if ($Index.Value -ge $Text.Length) { throw $ErrorCode }
if ($Text[$Index.Value] -eq '}') { $Index.Value++; return }
if ($Text[$Index.Value] -ne ',') { throw $ErrorCode }
$Index.Value++
}
}
if ($character -eq '[') {
$Index.Value++
Move-JsonWhitespace $Text $Index
if ($Index.Value -lt $Text.Length -and $Text[$Index.Value] -eq ']') { $Index.Value++; return }
while ($true) {
Assert-StrictJsonValue $Text $Index $NodeCount ($Depth + 1) $ErrorCode
Move-JsonWhitespace $Text $Index
if ($Index.Value -ge $Text.Length) { throw $ErrorCode }
if ($Text[$Index.Value] -eq ']') { $Index.Value++; return }
if ($Text[$Index.Value] -ne ',') { throw $ErrorCode }
$Index.Value++
}
}
$start = $Index.Value
while ($Index.Value -lt $Text.Length -and $Text[$Index.Value] -notin @(',', ']', '}', ' ', "`t", "`r", "`n")) { $Index.Value++ }
$token = $Text.Substring($start, $Index.Value - $start)
if ($token -notmatch '^(?:true|false|null|-?(?:0|[1-9][0-9]*)(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?)$') { throw $ErrorCode }
}
function Assert-StrictJson {
param([string]$Text, [string]$ErrorCode)
if ([string]::IsNullOrWhiteSpace($Text)) { throw $ErrorCode }
$index = 0
$nodeCount = 0
Assert-StrictJsonValue $Text ([ref]$index) ([ref]$nodeCount) 0 $ErrorCode
Move-JsonWhitespace $Text ([ref]$index)
if ($index -ne $Text.Length) { throw $ErrorCode }
}
function Read-StrictUtf8JsonFile {
param([string]$Path, [int64]$ByteLimit, [string]$ErrorCode)
try {
$bytes = [IO.File]::ReadAllBytes($Path)
if ($bytes.Length -gt $ByteLimit) { throw $ErrorCode }
$text = [Text.UTF8Encoding]::new($false, $true).GetString($bytes)
Assert-StrictJson $text $ErrorCode
return $text
} catch { throw $ErrorCode }
}
function Assert-AllowedKeys {
param($Object, [string[]]$Allowed)
if (-not ($Object -is [System.Collections.IDictionary])) { throw 'heartbeat_input_invalid' }
foreach ($key in $Object.Keys) {
if ($Allowed -notcontains [string]$key) { throw 'heartbeat_input_invalid' }
$normalized = ([string]$key -replace '[-_]', '').ToLowerInvariant()
if ($normalized -in @('password', 'secret', 'apikey', 'accesstoken', 'refreshtoken', 'credential', 'credentials', 'authorization', 'cookie', 'privatekey')) {
throw 'heartbeat_input_invalid'
}
}
}
function Test-SecretShapedValue {
param([string]$Value)
if ($Value -match '(?i)(password|passwd|secret|api[_-]?key|access[_-]?key|access[_-]?token|refresh[_-]?token|client[_-]?secret|credential|authorization|private[_-]?key)\s*[:=]') { return $true }
return $Value -match '(?i)(?:sk-(?:proj-)?[a-z0-9_-]{12,}|gh[pousr]_[a-z0-9]{20,}|nfp_[a-z0-9]{20,}|AKIA[0-9A-Z]{16})'
}
function Assert-Identifier {
param($Value)
if (-not ($Value -is [string]) -or $Value.Length -lt 1 -or $Value.Length -gt 128) { throw 'heartbeat_input_invalid' }
if (Test-SecretShapedValue $Value) { throw 'heartbeat_input_invalid' }
if ($Value -match '[\\]' -or $Value -match '^[A-Za-z]:[/\\]' -or $Value -match '(^|/)\.\.($|/)') { throw 'heartbeat_input_invalid' }
if ($Value -notmatch '^(?:[A-Za-z0-9][A-Za-z0-9._:@-]{0,127}|/root(?:/[A-Za-z0-9._-]{1,64}){0,7})$') {
throw 'heartbeat_input_invalid'
}
}
function Assert-Label {
param($Value, [int]$MaximumLength = 96)
if (-not ($Value -is [string]) -or $Value.Length -lt 1 -or $Value.Length -gt $MaximumLength) { throw 'heartbeat_input_invalid' }
if (Test-SecretShapedValue $Value) { throw 'heartbeat_input_invalid' }
if ($Value -match '[\\]' -or $Value -match '^[A-Za-z]:[/\\]' -or $Value -match '(^|/)\.\.($|/)' -or $Value -notmatch '^[A-Za-z0-9][A-Za-z0-9 ._:@+/-]*$') {
throw 'heartbeat_input_invalid'
}
}
function Assert-Opaque {
param($Value)
if (-not ($Value -is [string]) -or $Value.Length -lt 1 -or $Value.Length -gt 128 -or (Test-SecretShapedValue $Value) -or $Value -notmatch '^[A-Za-z0-9][A-Za-z0-9._:@+-]*$') {
throw 'heartbeat_input_invalid'
}
}
function Assert-Version {
param($Value)
if (-not ($Value -is [string]) -or $Value.Length -lt 1 -or $Value.Length -gt 64 -or (Test-SecretShapedValue $Value) -or $Value -notmatch '^[A-Za-z0-9][A-Za-z0-9.+_-]*$') {
throw 'heartbeat_input_invalid'
}
}
function Assert-Commit {
param($Value)
if (-not ($Value -is [string]) -or ($Value -ne 'unknown' -and $Value -notmatch '^[A-Fa-f0-9]{7,64}$')) {
throw 'heartbeat_input_invalid'
}
}
function Assert-Field {
param(
$Object,
[string]$Name,
[ValidateSet('bool', 'number', 'integer', 'id', 'label', 'opaque', 'version', 'commit', 'time', 'enum')]
[string]$Kind,
[bool]$Required = $false,
[double]$Minimum = [double]::MinValue,
[double]$Maximum = [double]::MaxValue,
[string[]]$Allowed = @()
)
if (-not (Has-Value $Object $Name)) {
if ($Required) { throw 'heartbeat_input_invalid' }
return
}
$value = $Object[$Name]
if ($null -eq $value) { throw 'heartbeat_input_invalid' }
switch ($Kind) {
'bool' {
if (-not ($value -is [bool])) { throw 'heartbeat_input_invalid' }
}
'number' {
if (-not (Test-IsNumber $value)) { throw 'heartbeat_input_invalid' }
$number = [double]$value
if ([double]::IsNaN($number) -or [double]::IsInfinity($number) -or $number -lt $Minimum -or $number -gt $Maximum) { throw 'heartbeat_input_invalid' }
}
'integer' {
if (-not (Test-IsInteger $value)) { throw 'heartbeat_input_invalid' }
$number = [double]$value
if ($number -lt $Minimum -or $number -gt $Maximum) {
throw 'heartbeat_input_invalid'
}
}
'id' { Assert-Identifier $value }
'label' { Assert-Label $value }
'opaque' { Assert-Opaque $value }
'version' { Assert-Version $value }
'commit' { Assert-Commit $value }
'time' { [void](ConvertTo-UtcTimestamp $value) }
'enum' {
if (-not ($value -is [string]) -or $Allowed -notcontains $value) { throw 'heartbeat_input_invalid' }
}
}
}
function Assert-StringArray {
param($Object, [string]$Name, [int]$MaximumCount, [string[]]$Allowed = @())
if (-not (Has-Value $Object $Name)) { return }
$value = $Object[$Name]
if ($value -is [string] -or -not ($value -is [System.Collections.IEnumerable])) { throw 'heartbeat_input_invalid' }
$items = @($value)
if ($items.Count -gt $MaximumCount) { throw 'heartbeat_input_invalid' }
foreach ($item in $items) {
if ($Allowed.Count -gt 0) {
if (-not ($item -is [string]) -or $Allowed -notcontains $item) { throw 'heartbeat_input_invalid' }
} else {
Assert-Label $item 64
}
}
}
function Assert-AgentRecord {
param($Record)
Assert-AllowedKeys $Record @('id', 'generation', 'owner', 'ownerGeneration', 'owningSolThread', 'active', 'repeatedEquivalentActions', 'minutesSinceMeaningfulChange', 'tokensSinceMeaningfulChange', 'longRunningOperation', 'progressHash', 'lastToolHash', 'lastCommandHash', 'lastFileChangeUtc', 'lastGitHash', 'lastTestHash', 'totalTokens', 'operationClass', 'status')
Assert-Field $Record 'id' id $true
Assert-Field $Record 'generation' opaque
Assert-Field $Record 'owner' id
Assert-Field $Record 'ownerGeneration' opaque
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'active' bool $true
Assert-Field $Record 'repeatedEquivalentActions' integer $false 0 1000000
Assert-Field $Record 'minutesSinceMeaningfulChange' number $false 0 525600
Assert-Field $Record 'tokensSinceMeaningfulChange' integer $false 0 9000000000000000
Assert-Field $Record 'longRunningOperation' bool
foreach ($name in @('progressHash', 'lastToolHash', 'lastCommandHash', 'lastGitHash', 'lastTestHash')) { Assert-Field $Record $name opaque }
Assert-Field $Record 'lastFileChangeUtc' time
Assert-Field $Record 'totalTokens' integer $false 0 9000000000000000
Assert-Field $Record 'operationClass' label
Assert-Field $Record 'status' enum $false 0 0 @('active', 'waiting', 'blocked', 'completed', 'failed', 'unknown')
}
function Assert-GuardianRecord {
param($Record)
Assert-AllowedKeys $Record @('reviewerSessionId', 'parentThreadId', 'owner', 'owningSolThread', 'reviewerModel', 'reviewCount', 'reviewsPerMinute', 'reviewsPerHour', 'averageReviewIntervalSeconds', 'reviewerTokens', 'mainTokens', 'reviewerUsageRatio', 'reviewerTurnShare', 'equivalentApprovalRequests', 'approvalExecutionCount', 'allowedPendingPostconditionCount', 'reviewAcceleration', 'reviewerRecursion', 'progressHash')
Assert-Field $Record 'reviewerSessionId' id $true
Assert-Field $Record 'parentThreadId' id
Assert-Field $Record 'owner' id
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'reviewerModel' label
foreach ($name in @('reviewCount', 'reviewerTokens', 'mainTokens', 'equivalentApprovalRequests', 'approvalExecutionCount', 'allowedPendingPostconditionCount')) { Assert-Field $Record $name integer $false 0 9000000000000000 }
foreach ($name in @('reviewsPerMinute', 'reviewsPerHour', 'averageReviewIntervalSeconds', 'reviewAcceleration')) { Assert-Field $Record $name number $false 0 1000000000 }
foreach ($name in @('reviewerUsageRatio', 'reviewerTurnShare')) { Assert-Field $Record $name number $false 0 1 }
Assert-Field $Record 'reviewerRecursion' bool
Assert-Field $Record 'progressHash' opaque
}
function Assert-UsageRecord {
param($Record)
Assert-AllowedKeys $Record @('owner', 'dominantThread', 'owningSolThread', 'role', 'totalTokens', 'windowTokens', 'windowMinutes', 'ratePerMinute', 'baselineRatePerMinute', 'projectedExhaustionMinutes', 'reviewerShare', 'meaningfulProgress', 'progressHash', 'completedCycles', 'stateChanges', 'acknowledgedEvents', 'failedCycles', 'duplicateRuns')
Assert-Field $Record 'owner' id
Assert-Field $Record 'dominantThread' id
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'role' enum $false 0 0 @('governor', 'worker', 'coordinator', 'unknown')
foreach ($name in @('totalTokens', 'windowTokens', 'completedCycles', 'stateChanges', 'acknowledgedEvents', 'failedCycles', 'duplicateRuns')) { Assert-Field $Record $name integer $false 0 9000000000000000 }
foreach ($name in @('windowMinutes', 'ratePerMinute', 'baselineRatePerMinute', 'projectedExhaustionMinutes')) { Assert-Field $Record $name number $false 0 1000000000000 }
Assert-Field $Record 'reviewerShare' number $false 0 1
Assert-Field $Record 'meaningfulProgress' bool
Assert-Field $Record 'progressHash' opaque
}
function Assert-SessionRecord {
param($Record)
Assert-AllowedKeys $Record @('id', 'parentId', 'owner', 'owningSolThread', 'childCount', 'forkDepth', 'contextOverlap', 'compactionCount', 'rolloutBytes', 'storageGrowthBytesPerHour', 'childCreationRate', 'recursive', 'progressHash')
Assert-Field $Record 'id' id $true
Assert-Field $Record 'parentId' id
Assert-Field $Record 'owner' id
Assert-Field $Record 'owningSolThread' id
foreach ($name in @('childCount', 'forkDepth', 'compactionCount', 'rolloutBytes')) { Assert-Field $Record $name integer $false 0 9000000000000000 }
Assert-Field $Record 'contextOverlap' number $false 0 1
foreach ($name in @('storageGrowthBytesPerHour', 'childCreationRate')) { Assert-Field $Record $name number $false 0 1000000000000000 }
Assert-Field $Record 'recursive' bool
Assert-Field $Record 'progressHash' opaque
}
function Assert-TestRecord {
param($Record)
$statuses = @('passed', 'failed', 'skipped', 'not_run', 'unavailable', 'unknown')
Assert-AllowedKeys $Record @('name', 'generation', 'owner', 'ownerGeneration', 'owningSolThread', 'status', 'commit', 'repairAttempts', 'failureCount', 'required', 'ran', 'environmentStatuses', 'buildId')
Assert-Field $Record 'name' label $true
Assert-Field $Record 'generation' opaque
Assert-Field $Record 'owner' id
Assert-Field $Record 'ownerGeneration' opaque
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'status' enum $true 0 0 $statuses
Assert-Field $Record 'commit' commit
foreach ($name in @('repairAttempts', 'failureCount')) { Assert-Field $Record $name integer $false 0 1000000 }
Assert-Field $Record 'required' bool
Assert-Field $Record 'ran' bool
Assert-Field $Record 'buildId' opaque
if (Has-Value $Record 'environmentStatuses') {
$values = $Record.environmentStatuses
if ($values -is [System.Collections.IDictionary]) {
if ($values.Count -gt 16) { throw 'heartbeat_input_invalid' }
foreach ($key in $values.Keys) {
Assert-Identifier ([string]$key)
if ($statuses -notcontains [string]$values[$key]) { throw 'heartbeat_input_invalid' }
}
} elseif ($values -is [System.Collections.IEnumerable] -and -not ($values -is [string])) {
$items = @($values)
if ($items.Count -gt 16) { throw 'heartbeat_input_invalid' }
foreach ($item in $items) { if ($statuses -notcontains [string]$item) { throw 'heartbeat_input_invalid' } }
} else { throw 'heartbeat_input_invalid' }
}
}
function Assert-MachineRecord {
param($Record)
Assert-AllowedKeys $Record @('id', 'owner', 'owningSolThread', 'role', 'version', 'commit', 'pluginVersion', 'marketplaceVersion', 'manifestVersion', 'skills', 'missingSkills', 'mcpConfigured', 'testStatus', 'installStatus', 'intendedVersion', 'intendedCommit')
Assert-Field $Record 'id' id $true
Assert-Field $Record 'owner' id
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'role' enum $false 0 0 @('development', 'installer', 'canary', 'production', 'unknown')
foreach ($name in @('version', 'pluginVersion', 'marketplaceVersion', 'manifestVersion', 'intendedVersion')) { Assert-Field $Record $name version }
foreach ($name in @('commit', 'intendedCommit')) { Assert-Field $Record $name commit }
Assert-StringArray $Record 'skills' 32
Assert-StringArray $Record 'missingSkills' 32
Assert-Field $Record 'mcpConfigured' bool
Assert-Field $Record 'testStatus' enum $false 0 0 @('passed', 'failed', 'not_run', 'unavailable', 'unknown')
Assert-Field $Record 'installStatus' enum $false 0 0 @('installed', 'failed', 'pending', 'stale', 'unknown')
}
function Assert-TaskRecord {
param($Record)
Assert-AllowedKeys $Record @('id', 'generation', 'owner', 'ownerGeneration', 'owningSolThread', 'status', 'dependsOn', 'dependencyStatus', 'ageHours', 'requiredCommit', 'requiredPush', 'requiredValidation', 'validationStatus', 'acknowledgedBug', 'assigned', 'updatedAt')
Assert-Field $Record 'id' id $true
Assert-Field $Record 'generation' opaque
Assert-Field $Record 'owner' id
Assert-Field $Record 'ownerGeneration' opaque
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'status' enum $true 0 0 @('todo', 'active', 'waiting', 'blocked', 'completed', 'failed', 'cancelled')
Assert-Field $Record 'dependsOn' id
Assert-Field $Record 'dependencyStatus' enum $false 0 0 @('todo', 'active', 'waiting', 'blocked', 'completed', 'failed', 'cancelled', 'unknown')
Assert-Field $Record 'ageHours' number $false 0 1000000
foreach ($name in @('requiredCommit', 'requiredPush', 'requiredValidation', 'acknowledgedBug', 'assigned')) { Assert-Field $Record $name bool }
Assert-Field $Record 'validationStatus' enum $false 0 0 @('passed', 'failed', 'not_run', 'unavailable', 'unknown')
Assert-Field $Record 'updatedAt' time
}
function Assert-GitRecord {
param($Record)
Assert-AllowedKeys $Record @('owner', 'owningSolThread', 'dirty', 'completedTaskIdle', 'requiresCommit', 'requiresPush', 'idleMinutes', 'ahead', 'behind', 'mergeConflict', 'branchChanged', 'destructiveOperation', 'expectedCommitPushed', 'conflictingScopes')
Assert-Field $Record 'owner' id
Assert-Field $Record 'owningSolThread' id
foreach ($name in @('dirty', 'completedTaskIdle', 'requiresCommit', 'requiresPush', 'mergeConflict', 'branchChanged', 'destructiveOperation', 'expectedCommitPushed')) { Assert-Field $Record $name bool }
Assert-Field $Record 'idleMinutes' number $false 0 1000000
foreach ($name in @('ahead', 'behind', 'conflictingScopes')) { Assert-Field $Record $name integer $false 0 1000000 }
}
function Assert-BuildRecord {
param($Record)
Assert-AllowedKeys $Record @('owner', 'owningSolThread', 'status', 'artifactCommit', 'expectedCommit', 'artifactVersion', 'expectedVersion', 'missingFiles', 'missingFileCount', 'manifestMatches', 'packageSizeBytes', 'previousPackageSizeBytes', 'installerArtifactHashMatches')
Assert-Field $Record 'owner' id
Assert-Field $Record 'owningSolThread' id
Assert-Field $Record 'status' enum $false 0 0 @('passed', 'failed', 'missing', 'not_run', 'unknown')
foreach ($name in @('artifactCommit', 'expectedCommit')) { Assert-Field $Record $name commit }
foreach ($name in @('artifactVersion', 'expectedVersion')) { Assert-Field $Record $name version }
Assert-StringArray $Record 'missingFiles' 32
foreach ($name in @('missingFileCount', 'packageSizeBytes', 'previousPackageSizeBytes')) { Assert-Field $Record $name integer $false 0 9000000000000000 }
foreach ($name in @('manifestMatches', 'installerArtifactHashMatches')) { Assert-Field $Record $name bool }
}
function Assert-HeartbeatActivityRecord {
param($Record)
Assert-AllowedKeys $Record @('origin', 'schedulerDuplicates', 'runtimeSeconds', 'runtimeMilliseconds', 'runtimeBudgetMilliseconds', 'runId', 'parentRunId')
Assert-Field $Record 'origin' enum $false 0 0 @('host', 'heartbeat', 'heartbeat_notification', 'test')
Assert-Field $Record 'schedulerDuplicates' integer $false 0 1000000
Assert-Field $Record 'runtimeSeconds' number $false 0 86400
foreach ($name in @('runtimeMilliseconds', 'runtimeBudgetMilliseconds')) { Assert-Field $Record $name number $false 0 86400000 }
Assert-Field $Record 'runId' id
Assert-Field $Record 'parentRunId' id
}
function Assert-Input {
param($Snapshot)
$allowed = @('schemaVersion', 'capturedAtUtc', 'sourceEpoch', 'sourceSequence', 'runId', 'origin', 'collectorCoverage', 'forceCadence', 'allowMachineDrift', 'isHeartbeatGenerated', 'agents', 'guardian', 'usage', 'sessions', 'tests', 'machines', 'tasks', 'git', 'build', 'heartbeatActivity')
Assert-AllowedKeys $Snapshot $allowed
Assert-Field $Snapshot 'schemaVersion' integer $false 1 2
$strictCollector = (Has-Value $Snapshot 'schemaVersion') -and [int]$Snapshot.schemaVersion -eq 2
Assert-Field $Snapshot 'capturedAtUtc' time $strictCollector
Assert-Field $Snapshot 'sourceEpoch' opaque $strictCollector
Assert-Field $Snapshot 'sourceSequence' integer $strictCollector 0 9000000000000000
Assert-Field $Snapshot 'runId' id
Assert-Field $Snapshot 'origin' enum $false 0 0 @('host', 'inspector', 'heartbeat', 'heartbeat_notification', 'test')
Assert-Field $Snapshot 'forceCadence' bool
Assert-Field $Snapshot 'allowMachineDrift' bool
Assert-Field $Snapshot 'isHeartbeatGenerated' bool
if (Has-Value $Snapshot 'collectorCoverage') {
Assert-AllowedKeys $Snapshot.collectorCoverage $script:Families
foreach ($key in $Snapshot.collectorCoverage.Keys) {
if ([string]$Snapshot.collectorCoverage[$key] -notin @('observed', 'partial', 'unsupported')) { throw 'heartbeat_input_invalid' }
}
} elseif ($strictCollector) {
throw 'heartbeat_input_invalid'
}
if ($strictCollector) {
foreach ($family in $script:PublicFamilies) {
if (-not (Has-Value $Snapshot.collectorCoverage $family)) { throw 'heartbeat_input_invalid' }
if ([string]$Snapshot.collectorCoverage[$family] -eq 'observed' -and -not (Test-FamilyPresent $Snapshot $family)) { throw 'heartbeat_input_invalid' }
}
}
$totalRecords = 0
foreach ($family in @('agents', 'sessions', 'tests', 'machines', 'tasks')) {
if (-not (Has-Value $Snapshot $family)) { continue }
$value = $Snapshot[$family]
if ($value -is [string] -or -not ($value -is [System.Collections.IEnumerable]) -or $value -is [System.Collections.IDictionary]) { throw 'heartbeat_input_invalid' }
$records = @($value)
if ($records.Count -gt 64) { throw 'heartbeat_input_invalid' }
$totalRecords += $records.Count
foreach ($record in $records) {
switch ($family) {
'agents' { Assert-AgentRecord $record }
'sessions' { Assert-SessionRecord $record }
'tests' { Assert-TestRecord $record }
'machines' { Assert-MachineRecord $record }
'tasks' { Assert-TaskRecord $record }
}
}
}
if ($totalRecords -gt 128) { throw 'heartbeat_input_invalid' }
if (Has-Value $Snapshot 'guardian') { Assert-GuardianRecord $Snapshot.guardian }
if (Has-Value $Snapshot 'usage') { Assert-UsageRecord $Snapshot.usage }
if (Has-Value $Snapshot 'git') { Assert-GitRecord $Snapshot.git }
if (Has-Value $Snapshot 'build') { Assert-BuildRecord $Snapshot.build }
if (Has-Value $Snapshot 'heartbeatActivity') { Assert-HeartbeatActivityRecord $Snapshot.heartbeatActivity }
}
function Test-ContainedPath {
param([string]$Path, [string]$Root)
try {
$full = [IO.Path]::GetFullPath($Path)
$base = [IO.Path]::GetFullPath($Root).TrimEnd([char[]]@([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar))
} catch { return $false }
if ($full.Equals($base, [StringComparison]::OrdinalIgnoreCase)) { return $true }
$prefix = $base + [IO.Path]::DirectorySeparatorChar
return $full.StartsWith($prefix, [StringComparison]::OrdinalIgnoreCase)
}
function Test-NoReparseAncestors {
param([string]$Path)
try { $full = [IO.Path]::GetFullPath($Path) } catch { return $false }
$cursorPath = $full
while (-not (Test-Path -LiteralPath $cursorPath)) {
$parent = Split-Path -Parent $cursorPath
if ([string]::IsNullOrWhiteSpace($parent) -or $parent -eq $cursorPath) { break }
$cursorPath = $parent
}
$cursor = Get-Item -LiteralPath $cursorPath -Force -ErrorAction SilentlyContinue
while ($cursor) {
if ($cursor.Attributes -band [IO.FileAttributes]::ReparsePoint) { return $false }
if ($cursor -is [IO.DirectoryInfo]) { $cursor = $cursor.Parent } else { $cursor = $cursor.Directory }
}
return $true
}
function Get-CanonicalStateIdentity {
param([string]$Path)
try {
$full = [IO.Path]::GetFullPath($Path)
if (Test-Path -LiteralPath $full -PathType Leaf) {
if ([ChronosHeartbeatPathIdentity]::LinkCount($full) -ne 1) { throw 'heartbeat_state_path_invalid' }
}
$directory = Split-Path -Parent $full
$missing = [Collections.Generic.List[string]]::new()
while (-not (Test-Path -LiteralPath $directory -PathType Container)) {
$leaf = Split-Path -Leaf $directory
if ([string]::IsNullOrWhiteSpace($leaf)) { throw 'heartbeat_state_path_invalid' }
$missing.Add($leaf) | Out-Null
$parent = Split-Path -Parent $directory
if ([string]::IsNullOrWhiteSpace($parent) -or $parent -eq $directory) { throw 'heartbeat_state_path_invalid' }
$directory = $parent
}
$canonical = [ChronosHeartbeatPathIdentity]::FinalDirectoryPath($directory)
for ($index = $missing.Count - 1; $index -ge 0; $index--) { $canonical = [IO.Path]::Combine($canonical, $missing[$index]) }
$canonical = [IO.Path]::Combine($canonical, (Split-Path -Leaf $full))
return $canonical.Replace('/', '\').TrimEnd('\').ToUpperInvariant()
} catch {
if ([string]$_.Exception.Message -eq 'heartbeat_state_path_invalid') { throw }
throw 'heartbeat_mutex_identity_invalid'
}
}
function Resolve-CodexHomeDirectory {
param([string]$Requested)
$hasRequested = -not [string]::IsNullOrWhiteSpace($Requested)
$hasEnvironment = -not [string]::IsNullOrWhiteSpace([string]$env:CODEX_HOME)
$source = if ($hasRequested) { 'explicit' } elseif ($hasEnvironment) { 'environment' } else { 'default' }
$candidate = if ($hasRequested) { $Requested } elseif ($hasEnvironment) { [string]$env:CODEX_HOME } else { Join-Path $HOME '.codex' }
try {
$full = [IO.Path]::GetFullPath($candidate)
if ($source -ne 'default') {
$item = Get-Item -LiteralPath $full -Force -ErrorAction Stop
if (-not $item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
throw 'heartbeat_codex_home_invalid'
}
$full = [IO.Path]::GetFullPath($item.FullName)
}
if (-not (Test-NoReparseAncestors $full)) { throw 'heartbeat_codex_home_invalid' }
} catch {
if ([string]$_.Exception.Message -eq 'heartbeat_codex_home_invalid') { throw }
$exception = $_.Exception
while ($exception) {
if ($exception -is [UnauthorizedAccessException] -or $exception -is [IO.IOException]) { throw 'heartbeat_codex_home_unavailable' }
$exception = $exception.InnerException
}
throw 'heartbeat_codex_home_invalid'
}
$full = $full.TrimEnd([char[]]@([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar))
if ([string]::IsNullOrWhiteSpace($full)) { throw 'heartbeat_codex_home_invalid' }
return [pscustomobject]@{ Path = $full; Identity = $full.ToUpperInvariant(); Source = $source }
}
function Resolve-StatePath {
param([string]$Requested, [string]$RequestedScope)
$localRoot = Join-Path $env:LOCALAPPDATA 'Chronos\Heartbeat'
$tempRoot = [IO.Path]::GetFullPath([IO.Path]::GetTempPath())
$priorTempRoot = Join-Path $tempRoot 'Chronos\Heartbeat'
$privateTempRoot = Join-Path $tempRoot 'Chronos\Heartbeat-v2'
$defaultScope = [string]::IsNullOrWhiteSpace($RequestedScope)
$priorDefaultScopeHash = $null
$priorStableScopeHash = $null
if ($defaultScope) {
$resolvedCodexHome = Resolve-CodexHomeDirectory $CodexHome
$script:CodexHomeSource = [string]$resolvedCodexHome.Source
$script:CodexHomeIdentity = (Get-StableHash ([string]$resolvedCodexHome.Identity)).Substring(0, 16)
$RequestedScope = '{0}|{1}' -f $env:COMPUTERNAME.ToUpperInvariant(), ([string]$resolvedCodexHome.Identity)
# Older default scopes predate CODEX_HOME isolation. A custom home must not
# clone their pending events, delivery state, or intervention ownership.
$script:AllowUnscopedLegacyMigration = $script:CodexHomeSource -eq 'default'
if ($script:AllowUnscopedLegacyMigration) {
$legacyCodexHome = [IO.Path]::GetFullPath((Join-Path $HOME '.codex')).TrimEnd([char[]]@([IO.Path]::DirectorySeparatorChar, [IO.Path]::AltDirectorySeparatorChar))
$priorStableScope = '{0}|{1}' -f $env:COMPUTERNAME, $legacyCodexHome
$priorDefaultScope = '{0}|{1}|{2}' -f $env:COMPUTERNAME, $legacyCodexHome, ([IO.Path]::GetFullPath((Get-Location).Path))
$priorStableScopeHash = Get-StableHash $priorStableScope
$priorDefaultScopeHash = Get-StableHash $priorDefaultScope
}
}
if ($RequestedScope.Length -gt 4096) { throw 'heartbeat_scope_invalid' }
if ([string]::IsNullOrWhiteSpace($Requested)) {
$scopeHash = Get-StableHash $RequestedScope
$script:StateStoreMode = 'temp_private'
$script:StateStoreMigration = 'not_needed'
$defaultPath = Join-Path $privateTempRoot (Join-Path $scopeHash 'heartbeat-state.json')
if (-not (Test-NoReparseAncestors $defaultPath)) { throw 'heartbeat_state_path_invalid' }
return [pscustomobject]@{
Path = $defaultPath
ScopeHash = $scopeHash
PriorStableV2Directory = if ($priorStableScopeHash -and $priorStableScopeHash -ne $scopeHash) { Join-Path $privateTempRoot $priorStableScopeHash } else { $null }
PriorStableV2Path = if ($priorStableScopeHash -and $priorStableScopeHash -ne $scopeHash) { Join-Path $privateTempRoot (Join-Path $priorStableScopeHash 'heartbeat-state.json') } else { $null }
PriorStableV2ScopeHash = $priorStableScopeHash
PriorV2Directory = if ($priorDefaultScopeHash) { Join-Path $privateTempRoot $priorDefaultScopeHash } else { $null }
PriorV2Path = if ($priorDefaultScopeHash) { Join-Path $privateTempRoot (Join-Path $priorDefaultScopeHash 'heartbeat-state.json') } else { $null }
PriorV2ScopeHash = $priorDefaultScopeHash
PriorTempDirectory = (Join-Path $priorTempRoot $(if ($priorStableScopeHash) { $priorStableScopeHash } else { $scopeHash }))
PriorTempPath = (Join-Path $priorTempRoot (Join-Path $(if ($priorStableScopeHash) { $priorStableScopeHash } else { $scopeHash }) 'heartbeat-state.json'))
LegacyPath = (Join-Path $localRoot (Join-Path $(if ($priorStableScopeHash) { $priorStableScopeHash } else { $scopeHash }) 'heartbeat-state.json'))
}
}
$script:StateStoreMode = 'explicit'
try { $full = [IO.Path]::GetFullPath($Requested) } catch { throw 'heartbeat_state_path_invalid' }
if ([IO.Path]::GetExtension($full) -ne '.json') { throw 'heartbeat_state_path_invalid' }
if (-not (Test-ContainedPath $full $localRoot) -and -not (Test-ContainedPath $full $privateTempRoot)) { throw 'heartbeat_state_path_invalid' }
if (-not (Test-NoReparseAncestors $full)) { throw 'heartbeat_state_path_invalid' }
return [pscustomobject]@{ Path = $full; ScopeHash = (Get-StableHash $RequestedScope); PriorStableV2Directory = $null; PriorStableV2Path = $null; PriorStableV2ScopeHash = $null; PriorV2Directory = $null; PriorV2Path = $null; PriorV2ScopeHash = $null; PriorTempDirectory = $null; PriorTempPath = $null; LegacyPath = $null }
}
function Initialize-StateStore {
param([string]$ResolvedStatePath)
$directory = Split-Path -Parent $ResolvedStatePath
try {
if (-not (Test-NoReparseAncestors $directory)) { throw 'heartbeat_state_store_unwritable' }
if (-not (Test-Path -LiteralPath $directory)) {
$missing = [Collections.Generic.List[string]]::new()
$cursor = $directory
while (-not (Test-Path -LiteralPath $cursor -PathType Container)) {
$missing.Add($cursor) | Out-Null
$parent = Split-Path -Parent $cursor
if ([string]::IsNullOrWhiteSpace($parent) -or $parent -eq $cursor) { throw 'heartbeat_state_store_unwritable' }
$cursor = $parent
}
for ($index = $missing.Count - 1; $index -ge 0; $index--) {
if (-not (Test-NoReparseAncestors $cursor)) { throw 'heartbeat_state_store_unwritable' }
$next = $missing[$index]
if (-not (Test-Path -LiteralPath $next -PathType Container)) { New-Item -ItemType Directory -Path $next | Out-Null }
$nextItem = Get-Item -LiteralPath $next -Force
if (-not $nextItem.PSIsContainer -or ($nextItem.Attributes -band [IO.FileAttributes]::ReparsePoint)) { throw 'heartbeat_state_store_unwritable' }
$cursor = $next
}
}
$item = Get-Item -LiteralPath $directory -Force
if (-not $item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or -not (Test-NoReparseAncestors $directory)) {
throw 'heartbeat_state_store_unwritable'
}
if ($script:StateStoreMode -eq 'temp_private') {
# User TEMP already inherits the host user's boundary. Do not replace its
# ACL with a transient sandbox SID or a later Codex identity can be locked
# out of its own upgrade state.
$script:StateStoreProtection = 'user_temp_inherited_hashed_metadata'
}
if (-not (Test-Path -LiteralPath $ResolvedStatePath -PathType Leaf)) {
$probe = Join-Path $directory ('.heartbeat-probe-' + [guid]::NewGuid().ToString('N') + '.tmp')
try {
$stream = New-Object IO.FileStream($probe, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None)
$stream.Dispose()
} finally {
Remove-Item -LiteralPath $probe -Force -ErrorAction SilentlyContinue
}
}
$script:StateStoreWriteReady = $true
} catch {
if ([string]$_.Exception.Message -eq 'heartbeat_state_store_unwritable') { throw }
throw 'heartbeat_state_store_unwritable'
}
}
function Resolve-InputFile {
param([string]$Path, [int64]$ByteLimit, [string[]]$Extensions)
if ([string]::IsNullOrWhiteSpace($Path)) { return $null }
try { $full = [IO.Path]::GetFullPath($Path) } catch { throw 'heartbeat_input_invalid' }
if ($Extensions -notcontains [IO.Path]::GetExtension($full).ToLowerInvariant()) { throw 'heartbeat_input_invalid' }
$item = Get-Item -LiteralPath $full -Force -ErrorAction SilentlyContinue
if (-not $item -or $item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or $item.Length -gt $ByteLimit) { throw 'heartbeat_input_invalid' }
if (-not (Test-NoReparseAncestors $full)) { throw 'heartbeat_input_invalid' }
return $full
}
function Read-NormalizedInput {
param([string]$Path)
if ([string]::IsNullOrWhiteSpace($Path)) { return @{} }
$safePath = Resolve-InputFile $Path $script:InputByteLimit @('.json')
try {
$text = Read-StrictUtf8JsonFile $safePath $script:InputByteLimit 'heartbeat_input_invalid'
$parsed = $text | ConvertFrom-Json -ErrorAction Stop
$result = ConvertTo-Hashtable $parsed
} catch { throw 'heartbeat_input_invalid' }
if (-not ($result -is [System.Collections.IDictionary])) { throw 'heartbeat_input_invalid' }
Write-Output -NoEnumerate $result
}
function Convert-InspectorNumber {
param($Value)
if ($null -eq $Value) { return $null }
$number = 0.0
$text = ([string]$Value).Replace(',', '.')
if ([double]::TryParse($text, [Globalization.NumberStyles]::Float, [Globalization.CultureInfo]::InvariantCulture, [ref]$number) -and -not [double]::IsNaN($number) -and -not [double]::IsInfinity($number)) { return $number }
return $null
}
function Convert-InspectorInteger {
param($Value, [double]$Maximum = 9000000000000000)
$number = Convert-InspectorNumber $Value
if ($null -eq $number -or $number -lt 0 -or $number -gt $Maximum -or [math]::Floor($number) -ne $number) { return $null }
return [int64]$number
}
function Merge-InspectorOutput {
param($Snapshot, [string]$Path, [switch]$Authorized)
if ([string]::IsNullOrWhiteSpace($Path)) { return }
if (-not $Authorized.IsPresent) { throw 'heartbeat_inspector_authorization_required' }
if (-not (Has-Value $Snapshot 'schemaVersion') -or [int]$Snapshot.schemaVersion -ne 2) { throw 'heartbeat_inspector_snapshot_required' }
$safePath = Resolve-InputFile $Path $script:InspectorByteLimit @('.txt', '.out', '.log')
$allowed = @(
'inspectionEvidenceVersion', 'inspectionRunId', 'inspectionCapturedAtUtc',
'pluginVersion', 'approvalReviewCoverage', 'approvalReviewObservation', 'approvalReviewsPerHour',
'approvalAverageIntervalSeconds', 'approvalReviewTurnsObserved', 'primaryTurnsObserved',
'approvalReviewTurnShare', 'approvalReviewerMainInputRatio', 'approvalRepeatedRequests',
'approvalRepeatedPrefixRequests', 'approvalPersistenceRetries', 'approvalPersistenceFailures',
'approvalPeakPerMinute', 'approvalConcurrentPeak', 'nestedReviewerSessionsObserved',
'approvalRecursionRisk', 'tokenIntervalInputM', 'tokenIntervalStartUtc', 'tokenIntervalEndUtc',
'tokenIntervalObservation', 'rolloutForkFilesObserved', 'rolloutReplayPct',
'rolloutGrowthMiBPerHour', 'tokenCompactions'
)
$fields = @{}
foreach ($line in (Get-Content -LiteralPath $safePath)) {
if ($line -notmatch '^CHRONOS(?: EFFICIENCY)? ') { continue }
foreach ($match in [regex]::Matches($line, '(?:^|\s)(?<key>[A-Za-z][A-Za-z0-9]+)=(?<value>[^\s]{1,128})')) {
$key = $match.Groups['key'].Value
if ($allowed -contains $key) { $fields[$key] = $match.Groups['value'].Value }
}
}
if ($fields.Count -eq 0) { throw 'heartbeat_inspector_input_invalid' }
if ([string]$fields.inspectionEvidenceVersion -ne '1' -or [string]$fields.inspectionRunId -notmatch '^[a-f0-9]{32}$' -or [string]$fields.pluginVersion -notmatch '^\d+\.\d+\.\d+$') {
throw 'heartbeat_inspector_input_invalid'
}
try {
$manifestPath = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\..\..\.codex-plugin\plugin.json'))
$expectedPluginVersion = [string](Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json).version
} catch { throw 'heartbeat_inspector_incompatible' }
if ([string]$fields.pluginVersion -ne $expectedPluginVersion) { throw 'heartbeat_inspector_incompatible' }
try {
$inspectionCaptured = ConvertTo-UtcTimestamp $fields.inspectionCapturedAtUtc 'heartbeat_inspector_input_invalid'
$snapshotCaptured = ConvertTo-UtcTimestamp $Snapshot.capturedAtUtc 'heartbeat_inspector_input_invalid'
} catch { throw 'heartbeat_inspector_input_invalid' }
if ([math]::Abs(($snapshotCaptured - $inspectionCaptured).TotalMinutes) -gt 15) { throw 'heartbeat_inspector_evidence_stale' }
if (-not (Has-Value $Snapshot 'collectorCoverage')) { $Snapshot['collectorCoverage'] = @{} }
if (-not (Has-Value $Snapshot 'guardian') -and $fields.Contains('approvalReviewTurnsObserved')) {
$reviewCount = Convert-InspectorInteger $fields.approvalReviewTurnsObserved
$reviewsPerHour = Convert-InspectorNumber $fields.approvalReviewsPerHour
$turnShare = Convert-InspectorNumber $fields.approvalReviewTurnShare
$inputRatio = Convert-InspectorNumber $fields.approvalReviewerMainInputRatio
$repeatValues = @('approvalRepeatedRequests', 'approvalRepeatedPrefixRequests', 'approvalPersistenceRetries') | ForEach-Object { Convert-InspectorInteger $fields[$_] }
$nested = if ($fields.Contains('nestedReviewerSessionsObserved')) { Convert-InspectorInteger $fields.nestedReviewerSessionsObserved } else { $null }
$coverage = [string]$fields.approvalReviewCoverage
$completeRequested = $coverage -match '^(complete|observed)$'
$requiredGuardianFields = @('approvalReviewTurnsObserved', 'approvalReviewsPerHour', 'approvalReviewTurnShare', 'approvalReviewerMainInputRatio', 'approvalRepeatedRequests', 'approvalRepeatedPrefixRequests', 'approvalPersistenceRetries', 'nestedReviewerSessionsObserved')
$requiredPresent = @($requiredGuardianFields | Where-Object { -not $fields.Contains($_) }).Count -eq 0
$requiredValid = $null -ne $reviewCount -and $null -ne $reviewsPerHour -and $reviewsPerHour -ge 0 -and $reviewsPerHour -le 1000000000 -and
$null -ne $turnShare -and $turnShare -ge 0 -and $turnShare -le 100 -and
$null -ne $inputRatio -and $inputRatio -ge 0 -and $inputRatio -le 1000000000 -and
@($repeatValues | Where-Object { $null -ne $_ }).Count -eq 3 -and $null -ne $nested
if ($completeRequested -and (-not $requiredPresent -or -not $requiredValid)) { throw 'heartbeat_inspector_input_invalid' }
if ($null -ne $turnShare -and $turnShare -gt 1 -and $turnShare -le 100) { $turnShare = $turnShare / 100.0 }
$inputShare = if ($null -ne $inputRatio -and $inputRatio -ge 0 -and $inputRatio -le 1000000000) { $inputRatio / (1.0 + $inputRatio) } else { $null }
$validRepeatValues = @($repeatValues | Where-Object { $null -ne $_ })
$repeats = if ($validRepeatValues.Count -gt 0) { [int64](($validRepeatValues | Measure-Object -Maximum).Maximum) } else { $null }
if ($null -eq $reviewCount) { throw 'heartbeat_inspector_input_invalid' }
$metricFields = @{}
foreach ($key in $fields.Keys) {
if ([string]$key -notin @('inspectionEvidenceVersion', 'inspectionRunId', 'inspectionCapturedAtUtc')) { $metricFields[$key] = $fields[$key] }
}
$guardian = @{
reviewerSessionId = 'chronos-inspector-window'
owner = 'governor'
owningSolThread = 'governor'
reviewCount = [int64]$reviewCount
progressHash = (Get-StableHash $metricFields).Substring(0, 32)
}
if ($null -ne $repeats) { $guardian.equivalentApprovalRequests = $repeats }
if ($null -ne $reviewsPerHour -and $reviewsPerHour -ge 0 -and $reviewsPerHour -le 1000000000) { $guardian.reviewsPerHour = $reviewsPerHour }
if ($null -ne $turnShare -and $turnShare -ge 0 -and $turnShare -le 1) { $guardian.reviewerTurnShare = $turnShare }
if ($null -ne $inputShare -and $inputShare -ge 0 -and $inputShare -le 1) { $guardian.reviewerUsageRatio = $inputShare }
if ($fields.Contains('approvalAverageIntervalSeconds')) {
$interval = Convert-InspectorNumber $fields.approvalAverageIntervalSeconds
if ($null -ne $interval -and $interval -ge 0) { $guardian.averageReviewIntervalSeconds = $interval }
}
if ($null -ne $nested) { $guardian.reviewerRecursion = ($nested -gt 0) }
$Snapshot['guardian'] = $guardian
$Snapshot.collectorCoverage['guardian'] = if ($completeRequested -and $requiredPresent -and $requiredValid) { 'observed' } else { 'partial' }
}
if (-not (Has-Value $Snapshot 'usage') -and $fields.Contains('tokenIntervalInputM')) {
$millions = Convert-InspectorNumber $fields.tokenIntervalInputM
if ($null -ne $millions -and $millions -ge 0) {
$usage = @{ owner = 'governor'; owningSolThread = 'governor'; meaningfulProgress = $true; windowTokens = [int64]($millions * 1000000) }
if ($fields.Contains('tokenIntervalStartUtc') -and $fields.Contains('tokenIntervalEndUtc')) {
try {
$start = ConvertTo-UtcTimestamp $fields.tokenIntervalStartUtc 'heartbeat_inspector_input_invalid'
$end = ConvertTo-UtcTimestamp $fields.tokenIntervalEndUtc 'heartbeat_inspector_input_invalid'
$minutes = ($end - $start).TotalMinutes
if ($minutes -gt 0) { $usage.windowMinutes = $minutes; $usage.ratePerMinute = [double]$usage.windowTokens / $minutes }
} catch {}
}
$Snapshot['usage'] = $usage
$Snapshot.collectorCoverage['usage'] = 'partial'
}
}
if (-not (Has-Value $Snapshot 'machines') -and $fields.Contains('pluginVersion')) {
$Snapshot['machines'] = @(@{ id = 'local-inspector'; role = 'development'; pluginVersion = [string]$fields.pluginVersion; owner = 'development' })
$Snapshot.collectorCoverage['machines'] = 'partial'
}
}
function New-CollectorState {
param([string]$Family)
return [ordered]@{
cadenceSeconds = [int]$script:CadenceSeconds[$Family]
lastAttempt = $null
lastRun = $null
lastSuccess = $null
coverage = 'unsupported'
skippedCadence = 0
backoffUntilUtc = $null
sourceEpochHash = $null
lastSequence = $null
}
}
function New-DefaultState {
param([string]$ScopeHash)
$collectors = [ordered]@{}
$previous = [ordered]@{}
foreach ($family in $script:Families) {
$collectors[$family] = New-CollectorState $family
$previous[$family] = @{}
}
return [ordered]@{
schema = 7
revision = 0
scopeHash = $ScopeHash
previous = $previous
conditions = @{}
events = @()
outbox = @()
interventions = @()
collectors = $collectors
health = [ordered]@{
runs = 0
suppressedDuplicates = 0
routesEmitted = 0
resolutionEvents = 0
duplicateRuns = 0
acknowledgedEvents = 0
failedCycles = 0
mutexContention = 0
lastCycleUtc = $null
lastSuccessUtc = $null
lastDurationMs = 0
lastRunIdHash = $null
lastError = $null
backoffUntilUtc = $null
deliveryAttempts = 0
runtimeBudgetMs = 0
runtimeObservedMs = 0
runtimeOverrunMs = 0
runtimeOverrunPercent = 0
runtimeBaselineMs = 0
runtimeClassification = 'unobserved'
runtimeOverrunStreak = 0
runtimeBackoffApplied = $false
sourceEpochHash = $null
lastSourceSequence = $null
}
runIds = @()
}
}
function Test-PersistedScalar {
param($Value)
if ($null -eq $Value -or $Value -is [bool] -or (Test-IsNumber $Value)) { return $true }
if (-not ($Value -is [string]) -or $Value.Length -gt 160) { return $false }
if ($Value -match '[\\/]' -or $Value -match '(?i)(password|secret|api[_-]?key|access[_-]?token|refresh[_-]?token|credential|authorization|cookie|private[_-]?key)') { return $false }
return $true
}
function Assert-StateRecord {
param($Record, [string[]]$Allowed)
if (-not ($Record -is [System.Collections.IDictionary])) { throw 'heartbeat_state_invalid' }
$eventTypes = @('AGENT_STALL', 'GUARDIAN_RUNAWAY', 'USAGE_BURN', 'SESSION_EXPLOSION', 'TEST_REGRESSION', 'TEST_ENVIRONMENT_DRIFT', 'TEST_VALIDATION_MISSING', 'MACHINE_DRIFT', 'TASK_ACTIONABLE', 'ZOMBIE_TASK', 'TASK_HANDOFF_INCOMPLETE', 'GIT_BUILD_STATE', 'GIT_STATE_RISK', 'BUILD_ARTIFACT_DRIFT', 'HEARTBEAT_SELF_HEALTH')
$statusValues = @('active', 'waiting', 'blocked', 'completed', 'failed', 'unknown', 'passed', 'skipped', 'not_run', 'unavailable', 'installed', 'pending', 'stale', 'todo', 'cancelled')
$interventionStates = @('queued', 'send_claimed', 'retry_queued', 'delivery_unknown', 'awaiting_task_ack', 'remediating', 'verification_pending', 'active_violation', 'verified_resolved', 'declined', 'user_authority_required', 'remediation_failed', 'undelivered', 'failed_closed', 'superseded')
$interventionTemplates = @('contain_agent_work', 'stop_review_amplification', 'contain_usage_growth', 'stop_recursive_workers_and_checkpoint', 'rerun_known_narrow_test_once', 'run_known_required_validation_once', 'resume_one_bounded_step', 'reconcile_owned_child', 'complete_existing_handoff', 'preserve_and_verify_handoff', 'stop_new_writes_and_verify_ownership', 'preserve_and_verify_artifact', 'governor_local_only')
$postconditions = @('agent_progress_or_terminal', 'review_activity_stable', 'usage_progress_stable', 'session_growth_stable', 'known_test_passed', 'required_validation_observed', 'task_progress_observed', 'ownership_reconciled', 'handoff_verified', 'git_state_stable', 'artifact_identity_stable', 'none')
foreach ($key in $Record.Keys) {
if ($Allowed -notcontains [string]$key -or -not (Test-PersistedScalar $Record[$key])) { throw 'heartbeat_state_invalid' }
if ($null -eq $Record[$key] -or -not ($Record[$key] -is [string])) { continue }
$text = [string]$Record[$key]
if ([string]$key -match 'Hash$' -or [string]$key -in @('eventId', 'interventionId')) {
if ($text -notmatch '^[a-f0-9]{16,64}$') { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -in @('lastAttempt', 'lastRun', 'lastSuccess', 'backoffUntilUtc', 'firstObserved', 'lastObserved', 'lastNotified', 'resolvedAt', 'detected', 'lastCycleUtc', 'lastSuccessUtc', 'createdAt', 'updatedAt', 'claimExpiresAt')) {
[void](ConvertTo-UtcTimestamp $text 'heartbeat_state_invalid')
} elseif ([string]$key -eq 'role') {
if ($text -notin @('governor', 'worker', 'coordinator', 'unknown')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'family') {
if ($script:Families -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'type') {
if ($eventTypes -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'event') {
if ($text -notin @('HEARTBEAT_EVENT', 'HEARTBEAT_RESOLVED')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'severity') {
if (-not $script:SeverityRank.ContainsKey($text)) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'routeClass') {
if ($text -notin @('governor', 'installer', 'development', 'owned')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'coverage') {
if ($text -notin @('observed', 'partial', 'unsupported')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'state') {
if ($interventionStates -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'template') {
if ($interventionTemplates -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'postcondition') {
if ($postconditions -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'failureReason') {
if ($text -notin @('none', 'ambiguous_target', 'target_not_live', 'target_policy_mismatch', 'target_generation_mismatch', 'target_generation_unavailable', 'transport_unavailable', 'user_authority_required', 'unsupported_action', 'self_target_forbidden', 'governor_usage_uncorroborated', 'retry_budget_exhausted', 'legacy_delivery_ambiguous', 'claim_expired_delivery_ambiguous')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'transportResult') {
if ($text -notin @('none', 'accepted', 'definite_failure', 'unknown')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'taskResponse') {
if ($text -notin @('none', 'acknowledged', 'outcome_reported', 'declined', 'user_authority_required', 'remediation_failed')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'verificationSource') {
if ($text -notin @('none', 'heartbeat_engine', 'host_inventory', 'host_test', 'host_git')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'verificationResult') {
if ($text -notin @('none', 'resolved', 'active', 'failed')) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -in @('status', 'testStatus', 'installStatus', 'dependencyStatus', 'taskStatus', 'validationStatus', 'buildStatus')) {
if ($statusValues -notcontains $text) { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'lastError') {
if ($text -notmatch '^heartbeat_[a-z_]+$') { throw 'heartbeat_state_invalid' }
} elseif ([string]$key -eq 'runtimeClassification') {
if ($text -notin @('unobserved', 'within_budget', 'normal_variance', 'elevated_variance', 'sustained_overrun', 'material_overrun')) { throw 'heartbeat_state_invalid' }
} else {
throw 'heartbeat_state_invalid'
}
}
}
function Assert-State {
param($State, [string]$ExpectedScopeHash)
if (-not ($State -is [System.Collections.IDictionary])) { throw 'heartbeat_state_invalid' }
$top = @('schema', 'revision', 'scopeHash', 'previous', 'conditions', 'events', 'outbox', 'interventions', 'collectors', 'health', 'runIds')
foreach ($key in $State.Keys) { if ($top -notcontains [string]$key) { throw 'heartbeat_state_invalid' } }
if ([int]$State.schema -ne 7 -or [int64]$State.revision -lt 0 -or [string]$State.scopeHash -ne $ExpectedScopeHash) { throw 'heartbeat_state_invalid' }
if (-not ($State.previous -is [System.Collections.IDictionary]) -or -not ($State.conditions -is [System.Collections.IDictionary]) -or -not ($State.collectors -is [System.Collections.IDictionary]) -or -not ($State.health -is [System.Collections.IDictionary])) { throw 'heartbeat_state_invalid' }
if ($State.conditions.Count -gt $script:ConditionLimit -or @($State.events).Count -gt $script:EventLimit -or @($State.outbox).Count -gt $script:OutboxLimit -or @($State.interventions).Count -gt $script:InterventionLimit -or @($State.runIds).Count -gt $script:RunIdLimit) { throw 'heartbeat_state_invalid' }
$snapshotFields = @('generationHash', 'active', 'progressHash', 'repeated', 'idleMinutes', 'tokens', 'totalTokens', 'longRunning', 'status', 'reviewCount', 'reviewsPerHour', 'ratio', 'turnShare', 'repeats', 'executions', 'pendingAllowed', 'acceleration', 'recursion', 'rate', 'baselineRate', 'projectionMinutes', 'reviewerShare', 'meaningfulProgress', 'role', 'progressCountersObserved', 'completedCycles', 'stateChanges', 'acknowledgedEvents', 'failedCycles', 'duplicateRuns', 'childCount', 'forkDepth', 'overlap', 'compactions', 'rolloutBytes', 'growthRate', 'childRate', 'recursive', 'commitHash', 'suiteHash', 'repairAttempts', 'failureCount', 'environmentHash', 'required', 'ran', 'regressionActive', 'versionHash', 'intendedHash', 'drift', 'testStatus', 'installStatus', 'missingSkills', 'mcpConfigured', 'dependencyStatus', 'taskStatus', 'ageHours', 'assigned', 'acknowledgedBug', 'requiredCommit', 'requiredPush', 'requiredValidation', 'validationStatus', 'actionableActive', 'dirty', 'completedTaskIdle', 'requiresCommit', 'requiresPush', 'idleMinutes', 'ahead', 'behind', 'mergeConflict', 'branchChanged', 'destructiveOperation', 'expectedCommitPushed', 'conflictingScopes', 'buildStatus', 'identityMismatch', 'missingFiles', 'manifestMatches', 'sizeRatio', 'artifactHashMatches', 'schedulerDuplicates', 'runtimeSeconds', 'runtimeMs', 'runtimeBudgetMs', 'runtimeOverrunMs', 'runtimeOverrunPercent', 'runtimeBaselineMs', 'runtimeClassification', 'runtimeOverrunStreak', 'runtimeBackoffApplied')
foreach ($family in $script:Families) {
if (-not $State.previous.Contains($family) -or -not ($State.previous[$family] -is [System.Collections.IDictionary]) -or $State.previous[$family].Count -gt 128) { throw 'heartbeat_state_invalid' }
foreach ($recordKey in $State.previous[$family].Keys) {
if ([string]$recordKey -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_state_invalid' }
Assert-StateRecord $State.previous[$family][$recordKey] $snapshotFields
}
if (-not $State.collectors.Contains($family)) { throw 'heartbeat_state_invalid' }
Assert-StateRecord $State.collectors[$family] @('cadenceSeconds', 'lastAttempt', 'lastRun', 'lastSuccess', 'coverage', 'skippedCadence', 'backoffUntilUtc', 'sourceEpochHash', 'lastSequence')
}
foreach ($conditionKey in $State.conditions.Keys) {
if ([string]$conditionKey -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_state_invalid' }
Assert-StateRecord $State.conditions[$conditionKey] @('family', 'type', 'severity', 'open', 'firstObserved', 'lastObserved', 'lastNotified', 'occurrences', 'episode', 'sourceEpochHash', 'signatureHash', 'generationBindingHash', 'subjectHash', 'ownerHash', 'routeClass', 'resolutionNotified', 'resolvedAt')
if ([string]$State.conditions[$conditionKey].family -notin $script:Families -or [string]$State.conditions[$conditionKey].severity -notin $script:SeverityRank.Keys) { throw 'heartbeat_state_invalid' }
}
foreach ($event in @($State.events)) {
Assert-StateRecord $event @('type', 'severity', 'routeClass', 'detected', 'dedupHash')
}
foreach ($item in @($State.outbox)) {
Assert-StateRecord $item @('eventId', 'event', 'type', 'severity', 'subjectHash', 'ownerHash', 'conditionHash', 'routeHash', 'routeClass', 'detected', 'attempts', 'lastAttempt', 'governorOrigin', 'expectedTargetGenerationHash', 'expectedSubjectGenerationHash', 'expectedOwnerGenerationHash')
if ([string]$item.eventId -notmatch '^[a-f0-9]{64}$' -or [string]$item.conditionHash -notmatch '^[a-f0-9]{64}$' -or [string]$item.routeHash -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_state_invalid' }
if ([string]$item.event -notin @('HEARTBEAT_EVENT', 'HEARTBEAT_RESOLVED')) { throw 'heartbeat_state_invalid' }
}
foreach ($item in @($State.interventions)) {
Assert-StateRecord $item @('interventionId', 'eventId', 'conditionHash', 'type', 'severity', 'version', 'targetHash', 'targetGenerationHash', 'governorHash', 'state', 'attempts', 'claimHash', 'claimExpiresAt', 'createdAt', 'updatedAt', 'template', 'postcondition', 'resolutionNoticeRequired', 'escalationSent', 'coalescedCount', 'failureReason', 'transportResult', 'taskResponse', 'verificationSource', 'verificationResult')
if ([int]$item.version -lt 1 -or [int]$item.attempts -lt 0 -or [int]$item.attempts -gt 2 -or [int]$item.coalescedCount -lt 0) { throw 'heartbeat_state_invalid' }
}
Assert-StateRecord $State.health @('runs', 'suppressedDuplicates', 'routesEmitted', 'resolutionEvents', 'duplicateRuns', 'acknowledgedEvents', 'failedCycles', 'mutexContention', 'lastCycleUtc', 'lastSuccessUtc', 'lastDurationMs', 'lastRunIdHash', 'lastError', 'backoffUntilUtc', 'deliveryAttempts', 'runtimeBudgetMs', 'runtimeObservedMs', 'runtimeOverrunMs', 'runtimeOverrunPercent', 'runtimeBaselineMs', 'runtimeClassification', 'runtimeOverrunStreak', 'runtimeBackoffApplied', 'sourceEpochHash', 'lastSourceSequence')
foreach ($runId in @($State.runIds)) { if ([string]$runId -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_state_invalid' } }
}
function Upgrade-State {
param($State)
if ([int](Get-Value $State 'schema' 0) -eq 4) {
if (-not (Has-Value $State 'interventions')) { $State['interventions'] = @() }
foreach ($item in @($State.outbox)) {
if (-not (Has-Value $item 'ownerHash')) { $item['ownerHash'] = Get-StableHash '' }
if (-not (Has-Value $item 'governorOrigin')) {
# Legacy usage delivery cannot prove a non-Governor owner. Require
# corroboration instead of permitting a worker wake after upgrade.
$item['governorOrigin'] = ([string]$item.type -eq 'USAGE_BURN')
}
}
$State.schema = 5
}
if ([int](Get-Value $State 'schema' 0) -eq 5) {
if (-not (Has-Value $State.health 'acknowledgedEvents')) { $State.health['acknowledgedEvents'] = 0L }
if (-not (Has-Value $State.health 'failedCycles')) { $State.health['failedCycles'] = 0L }
$State.schema = 6
}
if ([int](Get-Value $State 'schema' 0) -eq 6) {
$runtimeDefaults = [ordered]@{ runtimeBudgetMs = 0; runtimeObservedMs = 0; runtimeOverrunMs = 0; runtimeOverrunPercent = 0; runtimeBaselineMs = 0; runtimeClassification = 'unobserved'; runtimeOverrunStreak = 0; runtimeBackoffApplied = $false }
foreach ($name in $runtimeDefaults.Keys) { if (-not (Has-Value $State.health $name)) { $State.health[$name] = $runtimeDefaults[$name] } }
$legacyGovernorHash = Get-StableHash 'legacy-governor-unassigned'
foreach ($item in @($State.interventions)) {
if (-not (Has-Value $item 'governorHash')) { $item['governorHash'] = $legacyGovernorHash }
if (-not (Has-Value $item 'claimExpiresAt')) { $item['claimExpiresAt'] = $null }
if ([string]$item.state -eq 'send_claimed' -and -not $item.claimExpiresAt) {
# A legacy claimed send may already have reached the host. Preserve the
# ambiguity and make it visible, but never synthesize a retry.
$item.state = 'delivery_unknown'
$item.claimHash = $null
$item.transportResult = 'unknown'
$item.failureReason = 'legacy_delivery_ambiguous'
}
}
$State.schema = 7
}
return $State
}
function Read-State {
param([string]$Path, [string]$ScopeHash, [switch]$ReadOnly)
if (-not (Test-Path -LiteralPath $Path)) { return New-DefaultState $ScopeHash }
try {
$item = Get-Item -LiteralPath $Path -Force
if ($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or $item.Length -gt $script:StateByteLimit) { throw 'invalid' }
$text = Read-StrictUtf8JsonFile $Path $script:StateByteLimit 'heartbeat_state_invalid'
$state = ConvertTo-Hashtable ($text | ConvertFrom-Json -ErrorAction Stop)
$originalSchema = [int](Get-Value $state 'schema' 0)
$state = Upgrade-State $state
Assert-State $state $ScopeHash
if ([int]$state.schema -ne $originalSchema -and -not $ReadOnly) { Write-State $state $Path }
return $state
} catch { throw 'heartbeat_state_invalid' }
}
function Write-State {
param($State, [string]$Path)
$directory = Split-Path -Parent $Path
if (-not (Test-Path -LiteralPath $directory)) { New-Item -ItemType Directory -Force -Path $directory | Out-Null }
if (-not (Test-NoReparseAncestors $directory)) { throw 'heartbeat_state_path_invalid' }
$temporary = Join-Path $directory ('.heartbeat-' + [guid]::NewGuid().ToString('N') + '.tmp')
$backup = Join-Path $directory ('.heartbeat-' + [guid]::NewGuid().ToString('N') + '.bak')
try {
$json = $State | ConvertTo-Json -Compress -Depth 16
$bytes = [Text.UTF8Encoding]::new($false).GetBytes($json)
if ($bytes.Length -gt $script:StateByteLimit) { throw 'heartbeat_state_too_large' }
$stream = New-Object IO.FileStream($temporary, [IO.FileMode]::CreateNew, [IO.FileAccess]::Write, [IO.FileShare]::None, 4096, [IO.FileOptions]::WriteThrough)
try { $stream.Write($bytes, 0, $bytes.Length); $stream.Flush($true) } finally { $stream.Dispose() }
$written = Read-StrictUtf8JsonFile $temporary $script:StateByteLimit 'heartbeat_state_invalid'
[void]($written | ConvertFrom-Json -ErrorAction Stop)
if (Test-Path -LiteralPath $Path -PathType Leaf) {
[IO.File]::Replace($temporary, $Path, $backup, $true)
} else {
[IO.File]::Move($temporary, $Path)
}
} catch {
if ([string]$_.Exception.Message -match '^heartbeat_state_') { throw }
throw 'heartbeat_state_store_unwritable'
} finally {
Remove-Item -LiteralPath $temporary -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $backup -Force -ErrorAction SilentlyContinue
}
}
function Import-LegacyStateIfPresent {
param($Resolved)
if ($script:StateStoreMode -ne 'temp_private' -or
(Test-Path -LiteralPath $Resolved.Path)) {
if ($script:StateStoreMode -eq 'temp_private') { $script:PriorStateDisposition = 'not_checked_existing_v2' }
return
}
$unavailable = $false
$invalid = $false
foreach ($candidate in @(
[pscustomobject]@{ Path = [string]$Resolved.PriorStableV2Path; Directory = [string]$Resolved.PriorStableV2Directory; Imported = 'prior_stable_default_state_imported'; DetectDirectory = $true; ExpectedScopeHash = [string]$Resolved.PriorStableV2ScopeHash; RebindScope = $true },
[pscustomobject]@{ Path = [string]$Resolved.PriorV2Path; Directory = [string]$Resolved.PriorV2Directory; Imported = 'prior_v2_default_state_imported'; DetectDirectory = $true; ExpectedScopeHash = [string]$Resolved.PriorV2ScopeHash; RebindScope = $true },
[pscustomobject]@{ Path = [string]$Resolved.PriorTempPath; Directory = [string]$Resolved.PriorTempDirectory; Imported = 'prior_temp_state_imported'; DetectDirectory = $true; ExpectedScopeHash = $(if ($Resolved.PriorStableV2ScopeHash) { [string]$Resolved.PriorStableV2ScopeHash } else { [string]$Resolved.ScopeHash }); RebindScope = [bool]$Resolved.PriorStableV2ScopeHash },
[pscustomobject]@{ Path = [string]$Resolved.LegacyPath; Directory = $null; Imported = 'legacy_state_imported'; DetectDirectory = $false; ExpectedScopeHash = $(if ($Resolved.PriorStableV2ScopeHash) { [string]$Resolved.PriorStableV2ScopeHash } else { [string]$Resolved.ScopeHash }); RebindScope = [bool]$Resolved.PriorStableV2ScopeHash }
)) {
if ([string]::IsNullOrWhiteSpace($candidate.Path)) { continue }
if (-not (Test-NoReparseAncestors $candidate.Path)) {
$invalid = $true
if ($candidate.DetectDirectory) { break }
continue
}
try {
$item = Get-Item -LiteralPath $candidate.Path -Force -ErrorAction Stop
} catch {
$accessDenied = $_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::PermissionDenied -or
$_.Exception -is [UnauthorizedAccessException] -or
$_.Exception.InnerException -is [UnauthorizedAccessException]
if ($accessDenied) { $unavailable = $true }
elseif ($_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::ObjectNotFound -and $candidate.DetectDirectory) {
# Windows can normalize a child lookup beneath a sandbox-owned directory
# to ObjectNotFound. The exact scoped directory is a safe existence
# signal; Chronos never changes or takes ownership of it.
try {
$priorDirectory = Get-Item -LiteralPath $candidate.Directory -Force -ErrorAction Stop
if (-not $priorDirectory.PSIsContainer -or ($priorDirectory.Attributes -band [IO.FileAttributes]::ReparsePoint)) { $invalid = $true }
else {
try {
# Force one bounded directory enumeration. An accessible empty
# directory means the old state is absent; an access failure means
# Windows hid the child beneath an unavailable scoped directory.
[void]@(Get-ChildItem -LiteralPath $candidate.Directory -Force -ErrorAction Stop | Select-Object -First 1)
} catch {
$enumerationDenied = $_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::PermissionDenied -or
$_.Exception -is [UnauthorizedAccessException] -or
$_.Exception.InnerException -is [UnauthorizedAccessException]
if ($enumerationDenied) { $unavailable = $true }
else { $invalid = $true }
}
}
} catch {
$directoryAccessDenied = $_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::PermissionDenied -or
$_.Exception -is [UnauthorizedAccessException] -or
$_.Exception.InnerException -is [UnauthorizedAccessException]
if ($directoryAccessDenied) { $unavailable = $true }
elseif ($_.CategoryInfo.Category -ne [Management.Automation.ErrorCategory]::ObjectNotFound) { $invalid = $true }
}
} elseif ($_.CategoryInfo.Category -ne [Management.Automation.ErrorCategory]::ObjectNotFound) { $invalid = $true }
if ($candidate.DetectDirectory -and ($unavailable -or $invalid)) { break }
continue
}
if ($item.PSIsContainer) {
$invalid = $true
if ($candidate.DetectDirectory) { break }
continue
}
try {
$probe = New-Object IO.FileStream($candidate.Path, [IO.FileMode]::Open, [IO.FileAccess]::Read, [IO.FileShare]::ReadWrite)
$probe.Dispose()
} catch {
if ($_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::PermissionDenied -or
$_.Exception -is [UnauthorizedAccessException] -or
$_.Exception.InnerException -is [UnauthorizedAccessException]) { $unavailable = $true }
else { $invalid = $true }
if ($candidate.DetectDirectory) { break }
continue
}
try {
$legacy = Read-State $candidate.Path $candidate.ExpectedScopeHash -ReadOnly
if ($candidate.RebindScope) {
$legacy.scopeHash = [string]$Resolved.ScopeHash
Assert-State $legacy $Resolved.ScopeHash
}
Write-State $legacy $Resolved.Path
$script:StateStoreMigration = $candidate.Imported
$script:PriorStateDisposition = 'read_only_imported'
return
} catch {
if ($_.CategoryInfo.Category -eq [Management.Automation.ErrorCategory]::PermissionDenied -or
$_.Exception -is [UnauthorizedAccessException] -or
$_.Exception.InnerException -is [UnauthorizedAccessException]) { $unavailable = $true }
else { $invalid = $true }
if ($candidate.DetectDirectory) { break }
}
}
if ($unavailable) {
$script:StateStoreMigration = 'prior_state_unavailable_new_root'
$script:PriorStateDisposition = 'unavailable_preserved'
} elseif ($invalid) {
$script:StateStoreMigration = 'prior_state_invalid_rebuilt'
$script:PriorStateDisposition = 'invalid_preserved'
} else {
$script:StateStoreMigration = 'namespace_v2_new_root'
$script:PriorStateDisposition = 'not_found'
}
}
function New-DetectorResult {
return [pscustomobject]@{
Candidates = [Collections.Generic.List[object]]::new()
Routes = @{}
Snapshot = @{}
}
}
function Get-ConditionIdentity {
param([string]$Family, [string]$Subject, [string]$Reason)
$subjectHash = Get-StableHash $Subject
$dedupKey = '{0}:{1}:{2}' -f $Family, $subjectHash.Substring(0, 16), $Reason
return [pscustomobject]@{ DedupKey = $dedupKey; ConditionHash = (Get-StableHash $dedupKey); SubjectHash = $subjectHash }
}
function Add-RouteHint {
param($Result, $Identity, $Subject, $Owner, $Thread)
$Result.Routes[$Identity.ConditionHash] = [pscustomobject]@{ Subject = $Subject; Owner = $Owner; Thread = $Thread }
}
function New-Candidate {
param([string]$Family, [string]$Type, [string]$Severity, [string]$Subject, $Owner, $Thread, [string]$Reason, [string[]]$Changed, [string[]]$Evidence, [string]$Cause, [string]$RecommendedAction, [DateTimeOffset]$Detected, [string]$SubjectGenerationHash = $null, [string]$OwnerGenerationHash = $null)
$identity = Get-ConditionIdentity $Family $Subject $Reason
return [pscustomobject]@{
Family = $Family
ConditionHash = $identity.ConditionHash
SubjectHash = $identity.SubjectHash
Type = $Type
Severity = $Severity
Subject = $Subject
Owner = $Owner
OwningSolThread = $Thread
Detected = $Detected.ToString('o')
Changed = @($Changed)
Evidence = @($Evidence)
LikelyCause = $Cause
RecommendedAction = $RecommendedAction
DedupKey = $identity.DedupKey
SubjectGenerationHash = $SubjectGenerationHash
OwnerGenerationHash = $OwnerGenerationHash
}
}
function Add-Candidate {
param($Result, $Candidate)
$Result.Candidates.Add($Candidate) | Out-Null
$Result.Routes[$Candidate.ConditionHash] = [pscustomobject]@{ Subject = $Candidate.Subject; Owner = $Candidate.Owner; Thread = $Candidate.OwningSolThread }
}
function Get-ProgressHash {
param($Record, [string[]]$Fields)
$values = [ordered]@{}
foreach ($field in $Fields) { if (Has-Value $Record $field) { $values[$field] = $Record[$field] } }
return Get-StableHash $values
}
function Get-AgentDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
foreach ($agent in (As-Array (Get-Value $Snapshot 'agents'))) {
$id = [string]$agent.id
$idHash = Get-StableHash $id
$generationHash = Get-StableHash ([string](Get-Value $agent 'generation' 'generation-unavailable'))
$observedGenerationHash = if (Has-Value $agent 'generation') { $generationHash } else { $null }
$observedOwnerGenerationHash = if (Has-Value $agent 'ownerGeneration') { Get-StableHash ([string]$agent.ownerGeneration) } else { $null }
$progressHash = Get-ProgressHash $agent @('progressHash', 'lastToolHash', 'lastCommandHash', 'lastFileChangeUtc', 'lastGitHash', 'lastTestHash', 'status')
$current = [ordered]@{
generationHash = $generationHash
active = [bool]$agent.active
progressHash = $progressHash
repeated = [int](Get-Value $agent 'repeatedEquivalentActions' 0)
idleMinutes = [double](Get-Value $agent 'minutesSinceMeaningfulChange' 0)
tokens = [int64](Get-Value $agent 'tokensSinceMeaningfulChange' 0)
totalTokens = [int64](Get-Value $agent 'totalTokens' 0)
longRunning = [bool](Get-Value $agent 'longRunningOperation' $false)
status = [string](Get-Value $agent 'status' 'unknown')
}
$result.Snapshot[$idHash] = $current
$conditionReason = 'progress:' + $generationHash
$identity = Get-ConditionIdentity 'agent_stall' $id $conditionReason
Add-RouteHint $result $identity $id (Get-Value $agent 'owner') (Get-Value $agent 'owningSolThread')
if (-not $current.active -or $current.longRunning) { continue }
$prior = Get-Value $Previous $idHash
$compatiblePrior = $null -ne $prior -and [string](Get-Value $prior 'generationHash' '') -eq $generationHash
$noProgress = $compatiblePrior -and [string]$prior.progressHash -eq $progressHash
$tokenDelta = $current.tokens
if ($compatiblePrior -and $current.totalTokens -ge [int64]$prior.totalTokens) { $tokenDelta = [math]::Max($tokenDelta, $current.totalTokens - [int64]$prior.totalTokens) }
$absoluteExtreme = $current.repeated -ge 8 -and $current.idleMinutes -ge ($StallMinutes * 2)
$stalled = ($noProgress -and $current.idleMinutes -ge $StallMinutes -and ($current.repeated -ge 3 -or $tokenDelta -ge 20000)) -or $absoluteExtreme
if (-not $stalled) { continue }
$severity = if ($current.repeated -ge 20 -or $tokenDelta -ge 500000) { 'CRITICAL' } elseif ($current.repeated -ge 8 -or $tokenDelta -ge 100000) { 'HIGH' } else { 'WARNING' }
Add-Candidate $result (New-Candidate 'agent_stall' 'AGENT_STALL' $severity $id (Get-Value $agent 'owner') (Get-Value $agent 'owningSolThread') $conditionReason @('meaningfulProgress=unchanged', ('idleMinutes=' + (Format-Number $current.idleMinutes))) @('equivalentActions=' + $current.repeated, 'tokensWithoutProgress=' + $tokenDelta, 'generationHash=' + $generationHash.Substring(0, 16)) 'The active agent repeated work without a meaningful state delta.' 'Inspect the blocked operation. Stop or re-scope only the affected work.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
return $result
}
function Get-GuardianDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
$guardian = Get-Value $Snapshot 'guardian'
if ($null -eq $guardian) { return $result }
$id = [string]$guardian.reviewerSessionId
$idHash = Get-StableHash $id
$ratio = [math]::Max([double](Get-Value $guardian 'reviewerUsageRatio' 0), [double](Get-Value $guardian 'reviewerTurnShare' 0))
$current = [ordered]@{
reviewCount = [int64](Get-Value $guardian 'reviewCount' 0)
reviewsPerHour = [double](Get-Value $guardian 'reviewsPerHour' 0)
ratio = $ratio
turnShare = [double](Get-Value $guardian 'reviewerTurnShare' 0)
repeats = [int](Get-Value $guardian 'equivalentApprovalRequests' 0)
executions = [int](Get-Value $guardian 'approvalExecutionCount' 0)
pendingAllowed = [int](Get-Value $guardian 'allowedPendingPostconditionCount' 0)
acceleration = [double](Get-Value $guardian 'reviewAcceleration' 0)
recursion = [bool](Get-Value $guardian 'reviewerRecursion' $false)
progressHash = Get-ProgressHash $guardian @('progressHash', 'reviewCount', 'approvalExecutionCount')
}
$result.Snapshot[$idHash] = $current
$identity = Get-ConditionIdentity 'guardian' $id 'runaway'
Add-RouteHint $result $identity $id (Get-Value $guardian 'owner' 'governor') (Get-Value $guardian 'owningSolThread')
$prior = Get-Value $Previous $idHash
$reviewDelta = if ($prior) { [math]::Max(0, $current.reviewCount - [int64]$prior.reviewCount) } else { $current.reviewCount }
$repeatDelta = if ($prior) { [math]::Max(0, $current.repeats - [int]$prior.repeats) } else { $current.repeats }
$velocity = if ($prior -and [double]$prior.reviewsPerHour -gt 0) { $current.reviewsPerHour / [double]$prior.reviewsPerHour } elseif ($current.acceleration -gt 0) { $current.acceleration } else { 1 }
$postconditionBroken = $current.pendingAllowed -gt 0
$absoluteExtreme = $current.ratio -ge .85 -and $current.repeats -ge 5
$runaway = ($current.ratio -ge .75 -and $repeatDelta -ge 3 -and $velocity -ge 1.5) -or $absoluteExtreme -or $current.recursion -or $postconditionBroken
if ($runaway) {
$severity = if ($current.recursion -and $current.repeats -ge 20) { 'CRITICAL' } elseif ($postconditionBroken -or $current.ratio -ge .85 -or $current.repeats -ge 8) { 'HIGH' } else { 'WARNING' }
Add-Candidate $result (New-Candidate 'guardian' 'GUARDIAN_RUNAWAY' $severity $id (Get-Value $guardian 'owner' 'governor') (Get-Value $guardian 'owningSolThread') 'runaway' @('reviewVelocity=' + (Format-Number $velocity), 'reviewerShare=' + (Format-Number $current.ratio)) @('reviewDelta=' + $reviewDelta, 'equivalentApprovalDelta=' + $repeatDelta, 'allowedPendingPostconditions=' + $current.pendingAllowed) 'Automatic review or approval activity is amplifying without a matching execution postcondition.' 'Stop equivalent retries and inspect the approval state, rule match, and retry budget.' $Now)
}
return $result
}
function Get-UsageDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
$usage = Get-Value $Snapshot 'usage'
if ($null -eq $usage) { return $result }
$subject = [string](Get-Value $usage 'dominantThread' 'usage-window')
$owner = [string](Get-Value $usage 'owner' 'governor')
$role = [string](Get-Value $usage 'role' $(if ($owner -eq 'governor') { 'governor' } else { 'unknown' }))
$isGovernor = $role -eq 'governor' -or $owner -eq 'governor'
$progressCounterNames = @('completedCycles', 'stateChanges', 'acknowledgedEvents', 'failedCycles', 'duplicateRuns')
$progressCountersObserved = @($progressCounterNames | Where-Object { Has-Value $usage $_ }).Count -eq $progressCounterNames.Count
$idHash = Get-StableHash $subject
$rate = [double](Get-Value $usage 'ratePerMinute' 0)
if ($rate -le 0 -and (Get-Value $usage 'windowMinutes' 0) -gt 0) { $rate = [double](Get-Value $usage 'windowTokens' 0) / [double]$usage.windowMinutes }
$current = [ordered]@{
totalTokens = [int64](Get-Value $usage 'totalTokens' 0)
rate = $rate
baselineRate = [double](Get-Value $usage 'baselineRatePerMinute' 0)
projectionMinutes = [double](Get-Value $usage 'projectedExhaustionMinutes' 0)
reviewerShare = [double](Get-Value $usage 'reviewerShare' 0)
meaningfulProgress = [bool](Get-Value $usage 'meaningfulProgress' $true)
progressHash = Get-ProgressHash $usage @('progressHash')
role = $role
progressCountersObserved = $progressCountersObserved
completedCycles = [int64](Get-Value $usage 'completedCycles' 0)
stateChanges = [int64](Get-Value $usage 'stateChanges' 0)
acknowledgedEvents = [int64](Get-Value $usage 'acknowledgedEvents' 0)
failedCycles = [int64](Get-Value $usage 'failedCycles' 0)
duplicateRuns = [int64](Get-Value $usage 'duplicateRuns' 0)
}
$result.Snapshot[$idHash] = $current
$identity = Get-ConditionIdentity 'usage' $subject 'burn'
Add-RouteHint $result $identity $subject $owner (Get-Value $usage 'owningSolThread')
$prior = Get-Value $Previous $idHash
$reference = if ($current.baselineRate -gt 0) { $current.baselineRate } elseif ($prior -and [double]$prior.rate -gt 0) { [double]$prior.rate } else { 0 }
$rateMultiple = if ($reference -gt 0) { $current.rate / $reference } else { 1 }
$governorCounterProgress = $prior -and $current.progressCountersObserved -and [bool](Get-Value $prior 'progressCountersObserved' $false) -and (
$current.completedCycles -gt [int64](Get-Value $prior 'completedCycles' 0) -or
$current.stateChanges -gt [int64](Get-Value $prior 'stateChanges' 0) -or
$current.acknowledgedEvents -gt [int64](Get-Value $prior 'acknowledgedEvents' 0)
)
$hashProgress = $prior -and [string]$prior.progressHash -ne $current.progressHash
$progressObserved = $current.meaningfulProgress -or $hashProgress -or $governorCounterProgress
$governorProgressComparable = $prior -and $current.progressCountersObserved -and [bool](Get-Value $prior 'progressCountersObserved' $false)
$projectionWorsened = $prior -and $current.projectionMinutes -gt 0 -and [double]$prior.projectionMinutes -gt 0 -and $current.projectionMinutes -le ([double]$prior.projectionMinutes * .7) -and (-not $isGovernor -or $governorProgressComparable)
$noProgressDelta = if ($isGovernor) { $governorProgressComparable -and -not $progressObserved } else { -not $current.meaningfulProgress -and (-not $prior -or -not $hashProgress) }
$burn = $current.rate -ge 10000 -and $rateMultiple -ge 2 -and $noProgressDelta
if ($burn -or $projectionWorsened) {
$severity = if ($current.projectionMinutes -gt 0 -and $current.projectionMinutes -le 10 -and $noProgressDelta) { 'CRITICAL' } elseif ($rateMultiple -ge 3 -or $current.reviewerShare -ge .75 -or $projectionWorsened) { 'HIGH' } else { 'WARNING' }
$cause = if ($isGovernor) { 'Governor usage velocity worsened across comparable supervision cycles without polling, reconciliation, acknowledgement, or state-change progress.' } else { 'Usage velocity worsened without a matching progress signal.' }
$action = if ($isGovernor) { 'Keep the condition Governor-local and increase only the Governor recurrence to the idle cadence until a comparable cycle shows progress.' } else { 'Bound the dominant task and inspect reviewer, fork, and repeated-tool contribution.' }
Add-Candidate $result (New-Candidate 'usage' 'USAGE_BURN' $severity $subject $owner (Get-Value $usage 'owningSolThread') 'burn' @('rateMultiple=' + (Format-Number $rateMultiple), 'meaningfulProgress=' + $progressObserved, 'governorProgressComparable=' + $governorProgressComparable) @('tokensPerMinute=' + (Format-Number $current.rate), 'reviewerShare=' + (Format-Number $current.reviewerShare), 'projectedExhaustionMinutes=' + (Format-Number $current.projectionMinutes)) $cause $action $Now)
}
return $result
}
function Get-SessionDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
foreach ($session in (As-Array (Get-Value $Snapshot 'sessions'))) {
$id = [string]$session.id
$idHash = Get-StableHash $id
$current = [ordered]@{
childCount = [int](Get-Value $session 'childCount' 0)
forkDepth = [int](Get-Value $session 'forkDepth' 0)
overlap = [double](Get-Value $session 'contextOverlap' 0)
compactions = [int64](Get-Value $session 'compactionCount' 0)
rolloutBytes = [int64](Get-Value $session 'rolloutBytes' 0)
growthRate = [double](Get-Value $session 'storageGrowthBytesPerHour' 0)
childRate = [double](Get-Value $session 'childCreationRate' 0)
recursive = [bool](Get-Value $session 'recursive' $false)
progressHash = Get-ProgressHash $session @('progressHash', 'childCount', 'forkDepth', 'rolloutBytes')
}
$result.Snapshot[$idHash] = $current
$identity = Get-ConditionIdentity 'sessions' $id 'explosion'
Add-RouteHint $result $identity $id (Get-Value $session 'owner') (Get-Value $session 'owningSolThread')
$prior = Get-Value $Previous $idHash
$childDelta = if ($prior) { [math]::Max(0, $current.childCount - [int]$prior.childCount) } else { 0 }
$growthDelta = if ($prior) { [math]::Max(0, $current.rolloutBytes - [int64]$prior.rolloutBytes) } else { 0 }
$absolute = $current.childCount -ge 8 -and $current.forkDepth -ge 3 -and $current.overlap -ge .75
$transition = $prior -and $childDelta -ge 4 -and $current.overlap -ge .75 -and ($current.recursive -or $current.forkDepth -ge 3)
if ($absolute -or $transition) {
$severity = if ($current.childCount -ge 24 -and $current.overlap -ge .9) { 'CRITICAL' } elseif ($current.childCount -ge 12 -or $current.recursive -or $growthDelta -ge 1073741824) { 'HIGH' } else { 'WARNING' }
Add-Candidate $result (New-Candidate 'sessions' 'SESSION_EXPLOSION' $severity $id (Get-Value $session 'owner') (Get-Value $session 'owningSolThread') 'explosion' @('childDelta=' + $childDelta, 'forkDepth=' + $current.forkDepth) @('children=' + $current.childCount, 'contextOverlap=' + (Format-Number $current.overlap), 'rolloutGrowthBytes=' + $growthDelta) 'Repeated forks are replaying highly overlapping context.' 'Stop recursive forks and reduce inherited context before creating more workers.' $Now)
}
}
return $result
}
function Get-EnvironmentStatusValues {
param($Value)
if ($null -eq $Value) { return @() }
if ($Value -is [System.Collections.IDictionary]) { return @($Value.Values) }
return @($Value)
}
function Get-TestEnvironmentIdentityHash {
param($Value)
if ($Value -is [System.Collections.IDictionary]) {
return Get-StableHash (@($Value.Keys | ForEach-Object { ([string]$_).Trim().ToLowerInvariant() } | Sort-Object))
}
if ($null -eq $Value) { return Get-StableHash 'environment-unavailable' }
return Get-StableHash 'environment-unidentified'
}
function Get-TestDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
foreach ($test in (As-Array (Get-Value $Snapshot 'tests'))) {
$name = [string]$test.name
$idHash = Get-StableHash $name
$prior = Get-Value $Previous $idHash
$environmentStatuses = Get-Value $test 'environmentStatuses'
$environmentValues = @(Get-EnvironmentStatusValues $environmentStatuses)
$environmentHash = Get-TestEnvironmentIdentityHash $environmentStatuses
$commitHash = if (Has-Value $test 'commit') { Get-StableHash ([string]$test.commit) } else { Get-StableHash 'unknown' }
$suiteHash = if (Has-Value $test 'buildId') { Get-StableHash ([string]$test.buildId) } else { Get-StableHash ('test-suite:' + $name) }
$generationHash = Get-StableHash ([string](Get-Value $test 'generation' 'generation-unavailable'))
$observedGenerationHash = if (Has-Value $test 'generation') { $generationHash } else { $null }
$observedOwnerGenerationHash = if (Has-Value $test 'ownerGeneration') { Get-StableHash ([string]$test.ownerGeneration) } else { $null }
$current = [ordered]@{
status = [string]$test.status
commitHash = $commitHash
suiteHash = $suiteHash
generationHash = $generationHash
repairAttempts = [int](Get-Value $test 'repairAttempts' 0)
failureCount = [int](Get-Value $test 'failureCount' 0)
environmentHash = $environmentHash
required = [bool](Get-Value $test 'required' $false)
ran = [bool](Get-Value $test 'ran' $true)
regressionActive = $false
}
foreach ($reason in @('regression', 'environment-drift', 'validation-missing')) {
$identityReason = if ($reason -eq 'regression') { '{0}:{1}:{2}:{3}' -f $reason, $environmentHash, $suiteHash, $generationHash } else { '{0}:{1}' -f $reason, $generationHash }
$identity = Get-ConditionIdentity 'tests' $name $identityReason
Add-RouteHint $result $identity $name (Get-Value $test 'owner') (Get-Value $test 'owningSolThread')
}
$compatiblePrior = $prior -and [string]$prior.environmentHash -eq $environmentHash -and [string](Get-Value $prior 'suiteHash' '') -eq $suiteHash -and [string](Get-Value $prior 'generationHash' '') -eq $generationHash
$regressed = $compatiblePrior -and [string]$prior.status -eq 'passed' -and $current.status -eq 'failed'
$repairWorsened = $compatiblePrior -and $current.status -eq 'failed' -and $current.repairAttempts -gt [int]$prior.repairAttempts -and $current.repairAttempts -ge 2
$current.regressionActive = $current.status -eq 'failed' -and ($regressed -or $repairWorsened -or ($compatiblePrior -and [bool](Get-Value $prior 'regressionActive' $false)))
$result.Snapshot[$idHash] = $current
if ($current.regressionActive) {
$severity = if ($current.repairAttempts -ge 4 -or $current.failureCount -ge 5) { 'CRITICAL' } else { 'HIGH' }
Add-Candidate $result (New-Candidate 'tests' 'TEST_REGRESSION' $severity $name (Get-Value $test 'owner') (Get-Value $test 'owningSolThread') ('regression:{0}:{1}:{2}' -f $environmentHash, $suiteHash, $generationHash) @('status=' + $(if ($regressed) { 'passed->failed' } else { 'failed->failed' })) @('repairAttempts=' + $current.repairAttempts, 'failureCount=' + $current.failureCount, 'commitHash=' + $commitHash.Substring(0, 16), 'environmentHash=' + $environmentHash.Substring(0, 16), 'suiteHash=' + $suiteHash.Substring(0, 16), 'generationHash=' + $generationHash.Substring(0, 16)) 'A known-good test regressed or remained broken after additional repair work.' 'Investigate the introducing change and preserve the failing evidence.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
if (($environmentValues | Select-Object -Unique).Count -gt 1) {
Add-Candidate $result (New-Candidate 'tests' 'TEST_ENVIRONMENT_DRIFT' 'WARNING' $name (Get-Value $test 'owner') (Get-Value $test 'owningSolThread') ('environment-drift:' + $generationHash) @('environmentResults=disagree') @('resultVariants=' + (($environmentValues | Select-Object -Unique).Count), 'generationHash=' + $generationHash.Substring(0, 16)) 'The same validation differs across supplied environments.' 'Compare installed version, configuration, and artifact identity.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
if ($current.required -and -not $current.ran) {
Add-Candidate $result (New-Candidate 'tests' 'TEST_VALIDATION_MISSING' 'WARNING' $name (Get-Value $test 'owner') (Get-Value $test 'owningSolThread') ('validation-missing:' + $generationHash) @('requiredValidation=not_run') @('status=' + $current.status, 'generationHash=' + $generationHash.Substring(0, 16)) 'Required validation was not run.' 'Run the required validation before release or handoff.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
}
return $result
}
function Get-MachineDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
$machines = As-Array (Get-Value $Snapshot 'machines')
$fleetIdentities = @()
foreach ($machine in $machines) {
$id = [string]$machine.id
$idHash = Get-StableHash $id
$actual = '{0}|{1}|{2}|{3}' -f (Get-Value $machine 'version' ''), (Get-Value $machine 'commit' ''), (Get-Value $machine 'pluginVersion' ''), (Get-Value $machine 'manifestVersion' '')
$intended = '{0}|{1}' -f (Get-Value $machine 'intendedVersion' ''), (Get-Value $machine 'intendedCommit' '')
$versionMismatch = (Has-Value $machine 'intendedVersion') -and ([string]$machine.intendedVersion -ne [string](Get-Value $machine 'pluginVersion' (Get-Value $machine 'version')))
$commitMismatch = (Has-Value $machine 'intendedCommit') -and ([string]$machine.intendedCommit -ne [string](Get-Value $machine 'commit'))
$manifestMismatch = (Has-Value $machine 'pluginVersion') -and (Has-Value $machine 'manifestVersion') -and ([string]$machine.pluginVersion -ne [string]$machine.manifestVersion)
$missingSkills = @(Get-Value $machine 'missingSkills' @()).Count
$drift = $versionMismatch -or $commitMismatch -or $manifestMismatch -or $missingSkills -gt 0 -or [string](Get-Value $machine 'installStatus') -in @('failed', 'stale')
$current = [ordered]@{
versionHash = Get-StableHash $actual
intendedHash = Get-StableHash $intended
drift = $drift
testStatus = [string](Get-Value $machine 'testStatus' 'unknown')
installStatus = [string](Get-Value $machine 'installStatus' 'unknown')
missingSkills = $missingSkills
mcpConfigured = [bool](Get-Value $machine 'mcpConfigured' $false)
}
$result.Snapshot[$idHash] = $current
$fleetIdentities += $current.versionHash
$identity = Get-ConditionIdentity 'machines' $id 'identity'
Add-RouteHint $result $identity $id (Get-Value $machine 'owner') (Get-Value $machine 'owningSolThread')
if ($drift) {
$severity = if ($current.installStatus -eq 'failed' -or $current.testStatus -eq 'failed') { 'HIGH' } else { 'WARNING' }
Add-Candidate $result (New-Candidate 'machines' 'MACHINE_DRIFT' $severity $id (Get-Value $machine 'owner') (Get-Value $machine 'owningSolThread') 'identity' @('installedIdentity=unexpected') @('versionMismatch=' + $versionMismatch, 'commitMismatch=' + $commitMismatch, 'manifestMismatch=' + $manifestMismatch, 'missingSkills=' + $missingSkills) 'The installed machine state does not match its intended state.' 'Install the intended artifact or correct the intended deployment record.' $Now)
}
}
$fleetIdentity = Get-ConditionIdentity 'machines' 'machine-fleet' 'fleet'
if ($machines.Count -gt 0) { Add-RouteHint $result $fleetIdentity 'machine-fleet' (Get-Value $machines[0] 'owner' 'development') (Get-Value $machines[0] 'owningSolThread') }
if ($machines.Count -gt 1) {
$fleetIdentity = Get-ConditionIdentity 'machines' 'machine-fleet' 'fleet'
Add-RouteHint $result $fleetIdentity 'machine-fleet' (Get-Value $machines[0] 'owner' 'development') (Get-Value $machines[0] 'owningSolThread')
if (-not [bool](Get-Value $Snapshot 'allowMachineDrift' $false) -and ($fleetIdentities | Select-Object -Unique).Count -gt 1) {
Add-Candidate $result (New-Candidate 'machines' 'MACHINE_DRIFT' 'WARNING' 'machine-fleet' (Get-Value $machines[0] 'owner' 'development') (Get-Value $machines[0] 'owningSolThread') 'fleet' @('fleetIdentity=diverged') @('identityVariants=' + (($fleetIdentities | Select-Object -Unique).Count)) 'Machines do not share the expected build identity.' 'Confirm whether the drift is intentional, then align the stale installation.' $Now)
}
}
return $result
}
function Get-TaskDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
foreach ($task in (As-Array (Get-Value $Snapshot 'tasks'))) {
$id = [string]$task.id
$idHash = Get-StableHash $id
$generationHash = Get-StableHash ([string](Get-Value $task 'generation' 'generation-unavailable'))
$observedGenerationHash = if (Has-Value $task 'generation') { $generationHash } else { $null }
$observedOwnerGenerationHash = if (Has-Value $task 'ownerGeneration') { Get-StableHash ([string]$task.ownerGeneration) } else { $null }
$prior = Get-Value $Previous $idHash
$current = [ordered]@{
generationHash = $generationHash
dependencyStatus = [string](Get-Value $task 'dependencyStatus' 'unknown')
taskStatus = [string]$task.status
ageHours = [double](Get-Value $task 'ageHours' 0)
assigned = [bool](Get-Value $task 'assigned' (Has-Value $task 'owner'))
acknowledgedBug = [bool](Get-Value $task 'acknowledgedBug' $false)
requiredCommit = [bool](Get-Value $task 'requiredCommit' $false)
requiredPush = [bool](Get-Value $task 'requiredPush' $false)
requiredValidation = [bool](Get-Value $task 'requiredValidation' $false)
validationStatus = [string](Get-Value $task 'validationStatus' 'unknown')
actionableActive = $false
}
foreach ($reason in @('actionable', 'zombie', 'unfinished-handoff')) {
$identity = Get-ConditionIdentity 'tasks' $id ($reason + ':' + $generationHash)
Add-RouteHint $result $identity $id (Get-Value $task 'owner') (Get-Value $task 'owningSolThread')
}
$compatiblePrior = $prior -and [string](Get-Value $prior 'generationHash' '') -eq $generationHash
$dependencyTransition = $compatiblePrior -and [string]$prior.dependencyStatus -ne 'completed' -and $current.dependencyStatus -eq 'completed'
$current.actionableActive = $current.taskStatus -eq 'waiting' -and ($dependencyTransition -or ($compatiblePrior -and [bool](Get-Value $prior 'actionableActive' $false)))
$result.Snapshot[$idHash] = $current
if ($current.actionableActive) {
Add-Candidate $result (New-Candidate 'tasks' 'TASK_ACTIONABLE' 'WARNING' $id (Get-Value $task 'owner') (Get-Value $task 'owningSolThread') ('actionable:' + $generationHash) @('dependencyStatus=incomplete->completed') @('dependencyHash=' + (Get-StableHash (Get-Value $task 'dependsOn' 'unknown')).Substring(0, 16), 'generationHash=' + $generationHash.Substring(0, 16)) 'A dependency completed while this task remained waiting.' 'Resume the owning task or explicitly reclassify it.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
$zombie = (($current.taskStatus -eq 'todo' -or $current.acknowledgedBug) -and -not $current.assigned -and $current.ageHours -ge 24)
if ($zombie) {
Add-Candidate $result (New-Candidate 'tasks' 'ZOMBIE_TASK' 'WARNING' $id (Get-Value $task 'owner') (Get-Value $task 'owningSolThread') ('zombie:' + $generationHash) @('ownership=unassigned') @('ageHours=' + (Format-Number $current.ageHours), 'acknowledgedBug=' + $current.acknowledgedBug, 'generationHash=' + $generationHash.Substring(0, 16)) 'Recorded work has no active owner.' 'Assign the work or close it explicitly.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
$unfinished = $current.taskStatus -eq 'completed' -and (($current.requiredValidation -and $current.validationStatus -ne 'passed') -or $current.requiredCommit -or $current.requiredPush)
if ($unfinished) {
Add-Candidate $result (New-Candidate 'tasks' 'TASK_HANDOFF_INCOMPLETE' 'WARNING' $id (Get-Value $task 'owner') (Get-Value $task 'owningSolThread') ('unfinished-handoff:' + $generationHash) @('task=completed', 'handoff=incomplete') @('validation=' + $current.validationStatus, 'commitRequired=' + $current.requiredCommit, 'pushRequired=' + $current.requiredPush, 'generationHash=' + $generationHash.Substring(0, 16)) 'Completed work still has a required handoff action.' 'Complete or explicitly waive the remaining commit, push, or validation step.' $Now $observedGenerationHash $observedOwnerGenerationHash)
}
}
return $result
}
function Get-GitBuildDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
$git = Get-Value $Snapshot 'git'
$build = Get-Value $Snapshot 'build'
$current = [ordered]@{}
if ($git) {
$current.dirty = [bool](Get-Value $git 'dirty' $false)
$current.completedTaskIdle = [bool](Get-Value $git 'completedTaskIdle' $false)
$current.requiresCommit = [bool](Get-Value $git 'requiresCommit' $false)
$current.requiresPush = [bool](Get-Value $git 'requiresPush' $false)
$current.idleMinutes = [double](Get-Value $git 'idleMinutes' 0)
$current.ahead = [int](Get-Value $git 'ahead' 0)
$current.behind = [int](Get-Value $git 'behind' 0)
$current.mergeConflict = [bool](Get-Value $git 'mergeConflict' $false)
$current.branchChanged = [bool](Get-Value $git 'branchChanged' $false)
$current.destructiveOperation = [bool](Get-Value $git 'destructiveOperation' $false)
$current.expectedCommitPushed = [bool](Get-Value $git 'expectedCommitPushed' $true)
$current.conflictingScopes = [int](Get-Value $git 'conflictingScopes' 0)
}
if ($build) {
$identityMismatch = ((Has-Value $build 'artifactCommit') -and (Has-Value $build 'expectedCommit') -and [string]$build.artifactCommit -ne [string]$build.expectedCommit) -or ((Has-Value $build 'artifactVersion') -and (Has-Value $build 'expectedVersion') -and [string]$build.artifactVersion -ne [string]$build.expectedVersion)
$missingFiles = if (Has-Value $build 'missingFileCount') { [int]$build.missingFileCount } else { @(Get-Value $build 'missingFiles' @()).Count }
$sizeRatio = 1.0
if ((Get-Value $build 'previousPackageSizeBytes' 0) -gt 0) { $sizeRatio = [double](Get-Value $build 'packageSizeBytes' 0) / [double]$build.previousPackageSizeBytes }
$current.buildStatus = [string](Get-Value $build 'status' 'unknown')
$current.identityMismatch = $identityMismatch
$current.missingFiles = $missingFiles
$current.manifestMatches = [bool](Get-Value $build 'manifestMatches' $true)
$current.sizeRatio = $sizeRatio
$current.artifactHashMatches = [bool](Get-Value $build 'installerArtifactHashMatches' $true)
}
$result.Snapshot[(Get-StableHash 'repository-state')] = $current
if ($git) {
foreach ($reason in @('uncommitted', 'unsafe-git')) {
$identity = Get-ConditionIdentity 'git_build' 'repository-state' $reason
Add-RouteHint $result $identity 'repository-state' (Get-Value $git 'owner') (Get-Value $git 'owningSolThread')
}
}
if ($build) {
$identity = Get-ConditionIdentity 'git_build' 'repository-state' 'artifact'
Add-RouteHint $result $identity 'repository-state' (Get-Value $build 'owner') (Get-Value $build 'owningSolThread')
}
if ($git -and $current.dirty -and $current.completedTaskIdle -and $current.requiresCommit) {
Add-Candidate $result (New-Candidate 'git_build' 'GIT_BUILD_STATE' 'WARNING' 'repository-state' (Get-Value $git 'owner') (Get-Value $git 'owningSolThread') 'uncommitted' @('completedWork=uncommitted') @('idleMinutes=' + (Format-Number $current.idleMinutes)) 'Completed work remains uncommitted beyond its expected handoff.' 'Review, validate, and commit only the intended changes.' $Now)
}
if ($git -and ($current.mergeConflict -or $current.destructiveOperation -or $current.branchChanged -or $current.conflictingScopes -gt 0 -or ($current.requiresPush -and -not $current.expectedCommitPushed))) {
$severity = if ($current.destructiveOperation -or $current.mergeConflict) { 'CRITICAL' } else { 'HIGH' }
Add-Candidate $result (New-Candidate 'git_build' 'GIT_STATE_RISK' $severity 'repository-state' (Get-Value $git 'owner') (Get-Value $git 'owningSolThread') 'unsafe-git' @('repositoryState=unexpected') @('mergeConflict=' + $current.mergeConflict, 'branchChanged=' + $current.branchChanged, 'conflictingScopes=' + $current.conflictingScopes, 'expectedCommitPushed=' + $current.expectedCommitPushed) 'Repository state conflicts with the expected task handoff.' 'Stop new writes and verify ownership, branch, and intended commit before continuing.' $Now)
}
if ($build -and ($current.buildStatus -in @('failed', 'missing') -or $current.identityMismatch -or $current.missingFiles -gt 0 -or -not $current.manifestMatches -or -not $current.artifactHashMatches -or $current.sizeRatio -lt .5 -or $current.sizeRatio -gt 1.5)) {
$severity = if (-not $current.artifactHashMatches -or $current.identityMismatch) { 'HIGH' } else { 'WARNING' }
Add-Candidate $result (New-Candidate 'git_build' 'BUILD_ARTIFACT_DRIFT' $severity 'repository-state' (Get-Value $build 'owner') (Get-Value $build 'owningSolThread') 'artifact' @('artifactState=unexpected') @('buildStatus=' + $current.buildStatus, 'identityMismatch=' + $current.identityMismatch, 'missingFiles=' + $current.missingFiles, 'manifestMatches=' + $current.manifestMatches, 'sizeRatio=' + (Format-Number $current.sizeRatio)) 'The build or package does not match its intended source state.' 'Rebuild from the intended commit and verify the manifest and artifact checksum.' $Now)
}
return $result
}
function Get-HeartbeatDetector {
param($Snapshot, $Previous, [DateTimeOffset]$Now)
$result = New-DetectorResult
$activity = Get-Value $Snapshot 'heartbeatActivity'
if ($null -eq $activity) { return $result }
$recordKey = Get-StableHash 'heartbeat-engine'
$prior = Get-Value $Previous $recordKey @{}
$runtimeMs = if (Has-Value $activity 'runtimeMilliseconds') { [double]$activity.runtimeMilliseconds } else { [double](Get-Value $activity 'runtimeSeconds' 0) * 1000.0 }
$runtimeBudgetMs = if (Has-Value $activity 'runtimeBudgetMilliseconds') { [double]$activity.runtimeBudgetMilliseconds } else { 30000.0 }
if ($runtimeBudgetMs -le 0) { $runtimeBudgetMs = 30000.0 }
$priorBaselineMs = [double](Get-Value $prior 'runtimeBaselineMs' 0)
if ($priorBaselineMs -le 0) { $priorBaselineMs = [math]::Min($runtimeMs, $runtimeBudgetMs) }
$overrunMs = [math]::Max(0.0, $runtimeMs - $runtimeBudgetMs)
$overrunPercent = if ($runtimeBudgetMs -gt 0) { [math]::Round(($overrunMs / $runtimeBudgetMs) * 100.0, 1) } else { 0.0 }
$overrunStreak = if ($overrunMs -gt 0) { [int](Get-Value $prior 'runtimeOverrunStreak' 0) + 1 } else { 0 }
$varianceLimitMs = [math]::Max($runtimeBudgetMs + [math]::Max(1000.0, $runtimeBudgetMs * 0.10), $priorBaselineMs * 1.25)
$materialLimitMs = [math]::Max($runtimeBudgetMs * 1.50, $priorBaselineMs * 1.50)
$runtimeClassification = if ($overrunMs -le 0) {
'within_budget'
} elseif ($runtimeMs -ge $materialLimitMs) {
'material_overrun'
} elseif ($overrunStreak -ge 3) {
'sustained_overrun'
} elseif ($runtimeMs -le $varianceLimitMs) {
'normal_variance'
} else {
'elevated_variance'
}
$runtimeBackoffApplied = $runtimeClassification -in @('material_overrun', 'sustained_overrun')
$runtimeBaselineMs = $priorBaselineMs
if ($runtimeClassification -in @('within_budget', 'normal_variance')) {
$runtimeBaselineMs = if ($priorBaselineMs -gt 0) { [math]::Round(($priorBaselineMs * 0.75) + ($runtimeMs * 0.25), 1) } else { [math]::Round($runtimeMs, 1) }
}
$current = [ordered]@{
schedulerDuplicates = [int](Get-Value $activity 'schedulerDuplicates' 0)
runtimeSeconds = [math]::Round($runtimeMs / 1000.0, 3)
runtimeMs = [math]::Round($runtimeMs, 1)
runtimeBudgetMs = [math]::Round($runtimeBudgetMs, 1)
runtimeOverrunMs = [math]::Round($overrunMs, 1)
runtimeOverrunPercent = $overrunPercent
runtimeBaselineMs = $runtimeBaselineMs
runtimeClassification = $runtimeClassification
runtimeOverrunStreak = $overrunStreak
runtimeBackoffApplied = $runtimeBackoffApplied
}
$result.Snapshot[$recordKey] = $current
$identity = Get-ConditionIdentity 'heartbeat' 'heartbeat-engine' 'self-health'
Add-RouteHint $result $identity 'heartbeat-engine' 'governor' 'governor'
$shouldBackoff = $current.schedulerDuplicates -ge 2 -or $runtimeBackoffApplied
if ($shouldBackoff) {
$severity = if ($current.schedulerDuplicates -ge 5 -or $runtimeMs -ge ($runtimeBudgetMs * 2.0)) { 'CRITICAL' } else { 'HIGH' }
$reason = if ($current.schedulerDuplicates -ge 2) { 'duplicate_scheduler' } else { $runtimeClassification }
$changed = @('monitoringHealth=degraded', ('classification={0}' -f $reason))
$evidence = @(
('schedulerDuplicates={0}' -f $current.schedulerDuplicates)
('runtimeBudgetMs={0}' -f (Format-Number $current.runtimeBudgetMs))
('runtimeObservedMs={0}' -f (Format-Number $current.runtimeMs))
('runtimeOverrunMs={0}' -f (Format-Number $current.runtimeOverrunMs))
('runtimeOverrunPercent={0}' -f (Format-Number $current.runtimeOverrunPercent))
('runtimeBaselineMs={0}' -f (Format-Number $current.runtimeBaselineMs))
('runtimeOverrunStreak={0}' -f $current.runtimeOverrunStreak)
'backoffApplied=true'
)
Add-Candidate $result (New-Candidate 'heartbeat' 'HEARTBEAT_SELF_HEALTH' $severity 'heartbeat-engine' 'governor' 'governor' 'self-health' $changed $evidence 'Heartbeat scheduling or sustained runtime is abnormal.' 'Keep one scheduler owner and back off only sustained or material runtime degradation.' $Now)
}
return $result
}
function Invoke-Detector {
param([string]$Family, $Snapshot, $Previous, [DateTimeOffset]$Now)
switch ($Family) {
'agent_stall' { return Get-AgentDetector $Snapshot $Previous $Now }
'guardian' { return Get-GuardianDetector $Snapshot $Previous $Now }
'usage' { return Get-UsageDetector $Snapshot $Previous $Now }
'sessions' { return Get-SessionDetector $Snapshot $Previous $Now }
'tests' { return Get-TestDetector $Snapshot $Previous $Now }
'machines' { return Get-MachineDetector $Snapshot $Previous $Now }
'tasks' { return Get-TaskDetector $Snapshot $Previous $Now }
'git_build' { return Get-GitBuildDetector $Snapshot $Previous $Now }
'heartbeat' { return Get-HeartbeatDetector $Snapshot $Previous $Now }
}
throw 'heartbeat_family_invalid'
}
function Test-FamilyPresent {
param($Snapshot, [string]$Family)
switch ($Family) {
'agent_stall' { return Has-Value $Snapshot 'agents' }
'guardian' { return Has-Value $Snapshot 'guardian' }
'usage' { return Has-Value $Snapshot 'usage' }
'sessions' { return Has-Value $Snapshot 'sessions' }
'tests' { return Has-Value $Snapshot 'tests' }
'machines' { return Has-Value $Snapshot 'machines' }
'tasks' { return Has-Value $Snapshot 'tasks' }
'git_build' { return (Has-Value $Snapshot 'git') -or (Has-Value $Snapshot 'build') }
'heartbeat' { return Has-Value $Snapshot 'heartbeatActivity' }
}
return $false
}
function Test-FamilyDue {
param($Collector, [DateTimeOffset]$Now, [bool]$Force)
if ($Force) { return $true }
if (-not [string]::IsNullOrWhiteSpace([string]$Collector.backoffUntilUtc)) {
$backoff = ConvertTo-UtcTimestamp ([string]$Collector.backoffUntilUtc) 'heartbeat_state_invalid'
if ($Now -lt $backoff) { return $false }
}
if ([string]::IsNullOrWhiteSpace([string]$Collector.lastRun)) { return $true }
$last = ConvertTo-UtcTimestamp ([string]$Collector.lastRun) 'heartbeat_state_invalid'
return (($Now - $last).TotalSeconds -ge [int]$Collector.cadenceSeconds)
}
function Get-RouteTarget {
param($Candidate)
# Heartbeats use one Governor inbox. Owner and subject remain event hints.
return 'governor'
}
function Get-RouteClass {
param([string]$Route)
if ($Route -eq 'governor') { return 'governor' }
if ($Route -match '^installer') { return 'installer' }
if ($Route -match '^(development|dev)') { return 'development' }
return 'owned'
}
function Get-InterventionContract {
param([string]$Type)
$contract = switch ($Type) {
'AGENT_STALL' {
@('agent_stall', 'contain_agent_work', 'verified_subject_or_owner', 'agent_progress_or_terminal', $true, $true,
'Stop creating workers. Finish or checkpoint one bounded step, return completed workers, and report the categorical result.')
}
'GUARDIAN_RUNAWAY' {
@('review_amplification', 'stop_review_amplification', 'verified_subject_or_owner', 'review_activity_stable', $true, $true,
'Stop equivalent review and approval retries. Batch remaining inspection, checkpoint, and report the categorical result.')
}
'USAGE_BURN' {
@('usage_growth', 'contain_usage_growth', 'verified_non_governor_subject', 'usage_progress_stable', $true, $true,
'Stop creating workers, reduce task-controlled parallel work, checkpoint, and report the categorical result.')
}
'SESSION_EXPLOSION' {
@('context_growth', 'stop_recursive_workers_and_checkpoint', 'verified_subject', 'session_growth_stable', $true, $true,
'Stop recursive worker creation. Checkpoint the current work and prepare a fresh-task handoff without copying working history.')
}
'TEST_REGRESSION' {
@('test_regression', 'rerun_known_narrow_test_once', 'verified_owner', 'known_test_passed', $true, $false,
'Rerun the already identified narrow test once. Do not broaden the test scope. Report only the categorical result.')
}
'TEST_VALIDATION_MISSING' {
@('test_validation', 'run_known_required_validation_once', 'verified_owner', 'required_validation_observed', $true, $false,
'Run the already identified required validation once. Do not broaden the scope. Report only the categorical result.')
}
'TASK_ACTIONABLE' {
@('task_progress', 'resume_one_bounded_step', 'verified_subject', 'task_progress_observed', $true, $false,
'Resume one existing bounded step within the current assignment, checkpoint, and report the categorical result.')
}
'ZOMBIE_TASK' {
@('task_ownership', 'reconcile_owned_child', 'verified_owner', 'ownership_reconciled', $true, $false,
'Reconcile only the child task you already own. Return completed work or report the dependency state. Do not close unrelated tasks.')
}
'TASK_HANDOFF_INCOMPLETE' {
@('task_handoff', 'complete_existing_handoff', 'verified_owner', 'handoff_verified', $true, $false,
'Complete only the already authorized handoff step, verify it, and report the categorical result.')
}
'GIT_BUILD_STATE' {
@('git_handoff', 'preserve_and_verify_handoff', 'verified_owner', 'git_state_stable', $true, $false,
'Preserve current work and verify the existing bounded handoff scope. Do not reset, clean, merge, push, publish, or delete.')
}
'GIT_STATE_RISK' {
@('git_risk', 'stop_new_writes_and_verify_ownership', 'verified_owner', 'git_state_stable', $true, $true,
'Stop new writes. Preserve current work and verify ownership, branch, and intended commit. Do not reset, clean, merge, push, or delete.')
}
'BUILD_ARTIFACT_DRIFT' {
@('artifact_drift', 'preserve_and_verify_artifact', 'verified_owner', 'artifact_identity_stable', $true, $false,
'Preserve the current artifact and verify its source identity. Do not publish, replace, or delete artifacts.')
}
default {
@('governor_local', 'governor_local_only', 'none', 'none', $false, $false,
'Keep this condition in Governor-local state. Do not message a monitored task.')
}
}
return [pscustomobject]@{
Class = [string]$contract[0]
Template = [string]$contract[1]
TargetPolicy = [string]$contract[2]
Postcondition = [string]$contract[3]
Autonomous = [bool]$contract[4]
ResolutionNoticeRequired = [bool]$contract[5]
Instruction = [string]$contract[6]
SelfTargetAllowed = $false
}
}
function Convert-CandidateToEvent {
param($Candidate, [string]$EventId)
$route = Get-RouteTarget $Candidate
$intervention = Get-InterventionContract ([string]$Candidate.Type)
$governorOrigin = [string]$Candidate.Owner -eq 'governor'
$governorLocalUsage = [string]$Candidate.Type -eq 'USAGE_BURN' -and $governorOrigin
$event = [ordered]@{
Event = 'HEARTBEAT_EVENT'
EventId = $EventId
Delivery = 'new'
Type = $Candidate.Type
Severity = $Candidate.Severity
Owner = $Candidate.Owner
OwningSolThread = $route
Subject = $Candidate.Subject
Detected = $Candidate.Detected
Changed = @($Candidate.Changed)
Evidence = @($Candidate.Evidence)
LikelyCause = $Candidate.LikelyCause
RecommendedAction = $Candidate.RecommendedAction
DedupKey = $Candidate.DedupKey
InterventionClass = if ($governorLocalUsage) { 'governor_usage_control' } else { $intervention.Class }
InterventionTemplate = if ($governorLocalUsage) { 'governor_local_only' } else { $intervention.Template }
TargetPolicy = if ($governorLocalUsage) { 'none' } else { $intervention.TargetPolicy }
Postcondition = $intervention.Postcondition
Autonomous = $intervention.Autonomous
SelfTargetAllowed = $false
InterventionInstruction = if ($governorLocalUsage) { 'Update only the Governor recurrence to the returned cadence, verify one active recurrence remains, and do not message a monitored task.' } else { $intervention.Instruction }
CostImpact = if ([string]$Candidate.Type -eq 'USAGE_BURN') { 'unknown' } else { 'not_applicable' }
QuotaImpact = if ([string]$Candidate.Type -eq 'USAGE_BURN') { 'unknown' } else { 'not_applicable' }
GovernorOrigin = $governorOrigin
CorroborationRequired = $governorLocalUsage
CorroborationScope = if ($governorLocalUsage) { 'same_subject_and_window_for_monitored_task_message' } else { 'none' }
GovernorLocalAction = if ($governorLocalUsage) { 'throttle_recurrence_to_idle_cadence' } else { 'none' }
GovernorCadenceMinutes = if ($governorLocalUsage) { 360 } else { 0 }
RequiredHostAction = if ($governorLocalUsage) { 'update_governor_recurrence_and_verify' } else { 'evaluate_intervention_contract' }
MonitoredTaskMessage = if ($governorLocalUsage) { 'forbidden_without_independent_corroboration' } else { 'eligible_after_target_verification' }
UserActionRequired = $false
}
if ($Candidate.SubjectGenerationHash) { $event['SubjectGenerationHash'] = [string]$Candidate.SubjectGenerationHash }
if ($Candidate.OwnerGenerationHash) { $event['OwnerGenerationHash'] = [string]$Candidate.OwnerGenerationHash }
return $event
}
function New-ResolutionEvent {
param($Condition, [string]$ConditionHash, $RouteHint, [DateTimeOffset]$Now, [string]$EventId)
$owner = if ($RouteHint) { $RouteHint.Owner } else { $null }
$subject = if ($RouteHint) { [string]$RouteHint.Subject } else { [string]$Condition.subjectHash }
$intervention = Get-InterventionContract ([string]$Condition.type)
$governorOrigin = [string]$Condition.type -eq 'USAGE_BURN' -and [string]$Condition.ownerHash -eq (Get-StableHash 'governor')
return [ordered]@{
Event = 'HEARTBEAT_RESOLVED'
EventId = $EventId
Delivery = 'new'
Type = [string]$Condition.type
Severity = 'INFO'
Owner = $owner
OwningSolThread = 'governor'
Subject = $subject
Detected = $Now.ToString('o')
Changed = @('condition=open->resolved')
Evidence = @('conditionHash=' + $ConditionHash.Substring(0, 16))
LikelyCause = 'The prior condition is no longer present in an observed, due collector result.'
RecommendedAction = 'No action is required unless the condition returns.'
DedupKey = 'resolved:' + $ConditionHash.Substring(0, 16)
InterventionClass = $intervention.Class
InterventionTemplate = 'release_if_active_restriction'
TargetPolicy = $intervention.TargetPolicy
Postcondition = $intervention.Postcondition
Autonomous = $false
SelfTargetAllowed = $false
InterventionInstruction = 'Close the Governor-local condition. Notify the target only when an acknowledged temporary restriction must be lifted.'
CostImpact = 'not_applicable'
QuotaImpact = 'not_applicable'
GovernorOrigin = $governorOrigin
CorroborationRequired = $false
CorroborationScope = 'none'
GovernorLocalAction = if ($governorOrigin) { 'restore_supervision_recommended_cadence' } else { 'none' }
GovernorCadenceMinutes = 0
RequiredHostAction = if ($governorOrigin) { 'reconcile_governor_recurrence_and_verify' } else { 'close_local_condition' }
MonitoredTaskMessage = 'none_unless_release_notice_eligible'
UserActionRequired = $false
}
}
function New-OutboxRecord {
param($Event, [string]$ConditionHash)
$record = [ordered]@{
eventId = [string]$Event.EventId
event = [string]$Event.Event
type = [string]$Event.Type
severity = [string]$Event.Severity
subjectHash = Get-StableHash ([string]$Event.Subject)
ownerHash = Get-StableHash ([string]$Event.Owner)
conditionHash = $ConditionHash
routeHash = Get-StableHash ([string]$Event.OwningSolThread)
routeClass = Get-RouteClass ([string]$Event.OwningSolThread)
detected = [string]$Event.Detected
attempts = 0
lastAttempt = $null
governorOrigin = [bool]$Event.GovernorOrigin
}
if ($Event -is [System.Collections.IDictionary]) {
if ($Event.Contains('SubjectGenerationHash') -and $Event['SubjectGenerationHash']) {
$record['expectedSubjectGenerationHash'] = [string]$Event['SubjectGenerationHash']
}
if ($Event.Contains('OwnerGenerationHash') -and $Event['OwnerGenerationHash']) {
$record['expectedOwnerGenerationHash'] = [string]$Event['OwnerGenerationHash']
}
}
return $record
}
function Convert-OutboxToRetryEvent {
param($Record)
$intervention = Get-InterventionContract ([string]$Record.type)
$governorLocalUsage = [string]$Record.type -eq 'USAGE_BURN' -and [bool]$Record.governorOrigin
return [ordered]@{
Event = [string]$Record.event
EventId = [string]$Record.eventId
Delivery = 'retry'
Type = [string]$Record.type
Severity = [string]$Record.severity
Owner = $null
OwningSolThread = 'governor'
Subject = 'hash:' + ([string]$Record.subjectHash).Substring(0, 16)
Detected = [string]$Record.detected
Changed = @('delivery=pending')
Evidence = @('conditionHash=' + ([string]$Record.conditionHash).Substring(0, 16), 'routeHash=' + ([string]$Record.routeHash).Substring(0, 16))
LikelyCause = 'A prior Heartbeat transition remains unacknowledged by the invoking host.'
RecommendedAction = 'Deduplicate by EventId, deliver once, then acknowledge the event.'
DedupKey = 'event:' + [string]$Record.eventId
InterventionClass = if ($governorLocalUsage) { 'governor_usage_control' } else { $intervention.Class }
InterventionTemplate = if ($governorLocalUsage) { 'governor_local_only' } else { $intervention.Template }
TargetPolicy = if ($governorLocalUsage) { 'none' } else { $intervention.TargetPolicy }
Postcondition = $intervention.Postcondition
Autonomous = $intervention.Autonomous
SelfTargetAllowed = $false
InterventionInstruction = if ($governorLocalUsage) { 'Update only the Governor recurrence to the returned cadence, verify one active recurrence remains, and do not message a monitored task.' } else { $intervention.Instruction }
CostImpact = if ([string]$Record.type -eq 'USAGE_BURN') { 'unknown' } else { 'not_applicable' }
QuotaImpact = if ([string]$Record.type -eq 'USAGE_BURN') { 'unknown' } else { 'not_applicable' }
GovernorOrigin = [bool]$Record.governorOrigin
CorroborationRequired = $governorLocalUsage
CorroborationScope = if ($governorLocalUsage) { 'same_subject_and_window_for_monitored_task_message' } else { 'none' }
GovernorLocalAction = if ($governorLocalUsage) { 'throttle_recurrence_to_idle_cadence' } else { 'none' }
GovernorCadenceMinutes = if ($governorLocalUsage) { 360 } else { 0 }
RequiredHostAction = if ($governorLocalUsage) { 'update_governor_recurrence_and_verify' } else { 'evaluate_intervention_contract' }
MonitoredTaskMessage = if ($governorLocalUsage) { 'forbidden_without_independent_corroboration' } else { 'eligible_after_target_verification' }
UserActionRequired = $false
}
}
function Get-DueOutboxEvents {
param($State, [DateTimeOffset]$Now)
$result = [Collections.Generic.List[object]]::new()
foreach ($record in @($State.outbox)) {
$due = [string]::IsNullOrWhiteSpace([string]$record.lastAttempt)
if (-not $due) {
$lastAttempt = ConvertTo-UtcTimestamp ([string]$record.lastAttempt) 'heartbeat_state_invalid'
$due = ($Now - $lastAttempt).TotalSeconds -ge $script:OutboxRetrySeconds
}
if ($due -and [int]$record.attempts -lt $script:OutboxMaxAttempts) { $result.Add((Convert-OutboxToRetryEvent $record)) | Out-Null }
}
return @($result)
}
function Mark-OutboxAttempts {
param($State, [string[]]$EventIds, [string]$ResolvedStatePath, [DateTimeOffset]$Now)
if ($EventIds.Count -eq 0) { return }
foreach ($record in @($State.outbox)) {
if ($EventIds -contains [string]$record.eventId) {
$record.attempts = [int64]$record.attempts + 1
$record.lastAttempt = $Now.ToString('o')
$State.health.deliveryAttempts = [int64]$State.health.deliveryAttempts + 1
}
}
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
}
function Acknowledge-OutboxEvent {
param($State, [string]$AcknowledgedEventId, [string]$ResolvedStatePath)
if ($AcknowledgedEventId -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_event_id_invalid' }
$before = @($State.outbox).Count
$State.outbox = @($State.outbox | Where-Object { [string]$_.eventId -ne $AcknowledgedEventId })
$acknowledged = @($State.outbox).Count -lt $before
if ($acknowledged) {
$State.health.acknowledgedEvents = [int64]$State.health.acknowledgedEvents + 1
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
}
return $acknowledged
}
function Get-InterventionRecord {
param($State, [string]$RequestedInterventionId)
return @($State.interventions | Where-Object { [string]$_.interventionId -eq $RequestedInterventionId } | Select-Object -First 1)[0]
}
function Get-PendingEventRecord {
param($State, [string]$RequestedEventId)
if ($RequestedEventId -notmatch '^[a-f0-9]{64}$') { throw 'heartbeat_event_id_invalid' }
return @($State.outbox | Where-Object { [string]$_.eventId -eq $RequestedEventId } | Select-Object -First 1)[0]
}
function Test-InterventionTerminal {
param([string]$StateName)
return $StateName -in @('verified_resolved', 'declined', 'user_authority_required', 'remediation_failed', 'undelivered', 'failed_closed', 'superseded')
}
function Assert-InterventionRuntimeId {
param([string]$Value)
try { Assert-Identifier $Value } catch { throw 'heartbeat_intervention_input_invalid' }
}
function Remove-PendingEvent {
param($State, [string]$RequestedEventId)
$State.outbox = @($State.outbox | Where-Object { [string]$_.eventId -ne $RequestedEventId })
}
function New-InterventionRecord {
param($EventRecord, [string]$TargetHash, [string]$GenerationHash, [string]$GovernorHash, [int]$Version, [string]$StateName, [string]$Reason, [DateTimeOffset]$Now, [bool]$EscalationSent)
$contract = Get-InterventionContract ([string]$EventRecord.type)
$id = Get-StableHash ('chronos-intervention-v1|{0}|{1}|{2}|{3}' -f [string]$EventRecord.eventId, $Version, $TargetHash, $GenerationHash)
return [ordered]@{
interventionId = $id
eventId = [string]$EventRecord.eventId
conditionHash = [string]$EventRecord.conditionHash
type = [string]$EventRecord.type
severity = [string]$EventRecord.severity
version = $Version
targetHash = $TargetHash
targetGenerationHash = $GenerationHash
governorHash = $GovernorHash
state = $StateName
attempts = 0
claimHash = $null
claimExpiresAt = $null
createdAt = $Now.ToString('o')
updatedAt = $Now.ToString('o')
template = $contract.Template
postcondition = $contract.Postcondition
resolutionNoticeRequired = [bool]$contract.ResolutionNoticeRequired
escalationSent = $EscalationSent
coalescedCount = 0
failureReason = $Reason
transportResult = 'none'
taskResponse = 'none'
verificationSource = 'none'
verificationResult = 'none'
}
}
function Get-InterventionPayload {
param($Record, [string]$ActionName, [string]$Decision, [string]$ClaimTokenValue = $null)
$contract = Get-InterventionContract ([string]$Record.type)
$failureReason = [string]$Record.failureReason
$nextHostAction = switch ($failureReason) {
'ambiguous_target' { 'retry_host_discovery_next_cycle' }
'target_not_live' { 'retry_host_discovery_next_cycle' }
'target_generation_unavailable' { 'retry_host_discovery_next_cycle' }
'transport_unavailable' { 'retain_condition_without_user_handoff' }
'governor_usage_uncorroborated' { 'apply_governor_local_throttle_only' }
'user_authority_required' { 'surface_once_for_user_authority' }
default {
if ($Decision -eq 'send') { 'send_once_to_verified_target' }
elseif ($Decision -eq 'deferred_incompatible') { 'complete_active_intervention_then_plan_pending_event' }
else { 'none' }
}
}
$payload = [ordered]@{
ok = $true
action = $ActionName
decision = $Decision
interventionId = [string]$Record.interventionId
eventId = [string]$Record.eventId
version = [int]$Record.version
state = [string]$Record.state
type = [string]$Record.type
severity = [string]$Record.severity
template = [string]$Record.template
postcondition = [string]$Record.postcondition
targetHash = ([string]$Record.targetHash).Substring(0, 16)
instruction = $contract.Instruction
selfTargetAllowed = $false
maxSendAttempts = 2
requiresIndependentVerification = $true
nextHostAction = $nextHostAction
userActionRequired = ($failureReason -eq 'user_authority_required')
routineFailureHandling = 'governor_local_fail_closed'
replyFormat = ('CHRONOS INTERVENTION RESULT id={0} version={1} state=<acknowledged|outcome_reported|declined|user_authority_required|remediation_failed>' -f [string]$Record.interventionId, [int]$Record.version)
}
if (-not [string]::IsNullOrWhiteSpace($ClaimTokenValue)) { $payload.claimToken = $ClaimTokenValue }
if ($failureReason -ne 'none') { $payload.reason = $failureReason }
return $payload
}
function Write-InterventionPayload {
param($Payload)
Write-Output ('CHRONOS INTERVENTION ' + ($Payload | ConvertTo-Json -Compress -Depth 6))
}
function Test-UsageCorroboration {
param($State, $UsageEvent, [string]$RequestedCorroboratingEventId)
if ([string]::IsNullOrWhiteSpace($RequestedCorroboratingEventId) -or $RequestedCorroboratingEventId -notmatch '^[a-f0-9]{64}$') { return $false }
$other = Get-PendingEventRecord $State $RequestedCorroboratingEventId
if ($null -eq $other -or [string]$other.event -ne 'HEARTBEAT_EVENT' -or [string]$other.eventId -eq [string]$UsageEvent.eventId) { return $false }
if ([string]$other.type -notin @('AGENT_STALL', 'GUARDIAN_RUNAWAY', 'MACHINE_DRIFT')) { return $false }
$sameSubject = [string]$other.subjectHash -eq [string]$UsageEvent.subjectHash -or [string]$other.ownerHash -eq [string]$UsageEvent.subjectHash
if (-not $sameSubject) { return $false }
try {
$usageTime = ConvertTo-UtcTimestamp ([string]$UsageEvent.detected) 'heartbeat_state_invalid'
$otherTime = ConvertTo-UtcTimestamp ([string]$other.detected) 'heartbeat_state_invalid'
return [math]::Abs(($usageTime - $otherTime).TotalSeconds) -le 900
} catch { return $false }
}
function Test-InterventionTargetPolicy {
param($EventRecord, [string]$TargetHash, [string]$Policy)
$subjectMatch = $TargetHash -eq [string]$EventRecord.subjectHash
$ownerMatch = $TargetHash -eq [string]$EventRecord.ownerHash
switch ($Policy) {
'verified_subject' { return $subjectMatch }
'verified_non_governor_subject' { return $subjectMatch }
'verified_owner' { return $ownerMatch }
'verified_subject_or_owner' { return $subjectMatch -or $ownerMatch }
default { return $false }
}
}
function Add-BoundedIntervention {
param($State, $Record)
if (@($State.interventions).Count -ge $script:InterventionLimit) {
$oldestTerminal = @($State.interventions | Where-Object { Test-InterventionTerminal ([string]$_.state) } | Sort-Object updatedAt | Select-Object -First 1)[0]
if ($oldestTerminal) {
$State.interventions = @($State.interventions | Where-Object { [string]$_.interventionId -ne [string]$oldestTerminal.interventionId })
}
}
if (@($State.interventions).Count -ge $script:InterventionLimit) { throw 'heartbeat_intervention_capacity' }
$State.interventions = @($State.interventions + $Record)
}
function Invoke-InterventionPlan {
param($State, [string]$ResolvedStatePath, [string]$RequestedEventId, [string]$RequestedCorroboratingEventId, [string]$RequestedTargetId, [string]$RequestedGeneration, [string]$CurrentGovernorId, [DateTimeOffset]$Now)
Assert-InterventionRuntimeId $RequestedTargetId
Assert-InterventionRuntimeId $RequestedGeneration
Assert-InterventionRuntimeId $CurrentGovernorId
$eventRecord = Get-PendingEventRecord $State $RequestedEventId
if ($null -eq $eventRecord -or [string]$eventRecord.event -ne 'HEARTBEAT_EVENT') { throw 'heartbeat_intervention_event_unavailable' }
$targetHash = Get-StableHash $RequestedTargetId
$generationHash = Get-StableHash $RequestedGeneration
$governorHash = Get-StableHash $CurrentGovernorId
$contract = Get-InterventionContract ([string]$eventRecord.type)
$failure = $null
$expectedGenerationHash = $null
if ($targetHash -eq [string]$eventRecord.subjectHash -and $eventRecord.expectedSubjectGenerationHash) {
$expectedGenerationHash = [string]$eventRecord.expectedSubjectGenerationHash
} elseif ($targetHash -eq [string]$eventRecord.ownerHash -and $eventRecord.expectedOwnerGenerationHash) {
$expectedGenerationHash = [string]$eventRecord.expectedOwnerGenerationHash
} elseif ($eventRecord.expectedTargetGenerationHash) {
# Compatibility for pre-release schema-7 canary state.
$expectedGenerationHash = [string]$eventRecord.expectedTargetGenerationHash
}
if ($targetHash -eq [string]$eventRecord.ownerHash -and
$targetHash -ne [string]$eventRecord.subjectHash -and
$eventRecord.expectedSubjectGenerationHash -and
-not $eventRecord.expectedOwnerGenerationHash) {
$failure = 'target_generation_unavailable'
} elseif ($expectedGenerationHash -and $expectedGenerationHash -ne $generationHash) { $failure = 'target_generation_mismatch' }
elseif ($targetHash -eq $governorHash) { $failure = 'self_target_forbidden' }
elseif (-not [bool]$contract.Autonomous) { $failure = 'unsupported_action' }
elseif ([string]$eventRecord.type -eq 'USAGE_BURN' -and [bool]$eventRecord.governorOrigin -and -not (Test-UsageCorroboration $State $eventRecord $RequestedCorroboratingEventId)) { $failure = 'governor_usage_uncorroborated' }
elseif (-not (Test-InterventionTargetPolicy $eventRecord $targetHash ([string]$contract.TargetPolicy))) { $failure = 'target_policy_mismatch' }
if ($failure) {
$reason = $failure
$record = New-InterventionRecord $eventRecord $targetHash $generationHash $governorHash 1 'failed_closed' $reason $Now $false
Add-BoundedIntervention $State $record
Remove-PendingEvent $State $RequestedEventId
Trim-State $State
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'plan' 'failed_closed'
}
foreach ($staleGeneration in @($State.interventions | Where-Object {
[string]$_.targetHash -eq $targetHash -and
[string]$_.targetGenerationHash -ne $generationHash -and
-not (Test-InterventionTerminal ([string]$_.state))
})) {
$staleGeneration.state = 'superseded'
$staleGeneration.updatedAt = $Now.ToString('o')
}
$active = @($State.interventions | Where-Object {
[string]$_.targetHash -eq $targetHash -and
[string]$_.targetGenerationHash -eq $generationHash -and
-not (Test-InterventionTerminal ([string]$_.state))
} | Sort-Object updatedAt -Descending | Select-Object -First 1)[0]
if ($active) {
if ([string]$active.template -ne [string]$contract.Template -or [string]$active.postcondition -ne [string]$contract.Postcondition) {
return Get-InterventionPayload $active 'plan' 'deferred_incompatible'
}
$newRank = [int]$script:SeverityRank[[string]$eventRecord.severity]
$activeRank = [int]$script:SeverityRank[[string]$active.severity]
if ([string]$active.eventId -eq $RequestedEventId -or $newRank -le $activeRank -or ([bool]$active.escalationSent -and [int]$active.attempts -gt 0)) {
$active.coalescedCount = [int]$active.coalescedCount + 1
$active.updatedAt = $Now.ToString('o')
Remove-PendingEvent $State $RequestedEventId
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $active 'plan' 'coalesced'
}
$nextVersion = [int]$active.version + 1
$alreadySent = [int]$active.attempts -gt 0
$active.state = 'superseded'
$active.escalationSent = $alreadySent
$active.updatedAt = $Now.ToString('o')
$record = New-InterventionRecord $eventRecord $targetHash $generationHash $governorHash $nextVersion 'queued' 'none' $Now $alreadySent
} else {
$record = New-InterventionRecord $eventRecord $targetHash $generationHash $governorHash 1 'queued' 'none' $Now $false
}
Add-BoundedIntervention $State $record
Remove-PendingEvent $State $RequestedEventId
Trim-State $State
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'plan' 'send'
}
function Invoke-InterventionFailClosed {
param($State, [string]$ResolvedStatePath, [string]$RequestedEventId, [string]$Reason, [DateTimeOffset]$Now)
if ($Reason -notin @('ambiguous_target', 'target_not_live', 'transport_unavailable', 'user_authority_required', 'unsupported_action')) { throw 'heartbeat_intervention_input_invalid' }
$eventRecord = Get-PendingEventRecord $State $RequestedEventId
if ($null -eq $eventRecord) { throw 'heartbeat_intervention_event_unavailable' }
$record = New-InterventionRecord $eventRecord (Get-StableHash 'unresolved-target') (Get-StableHash 'unresolved-generation') (Get-StableHash 'unresolved-governor') 1 'failed_closed' $Reason $Now $false
Add-BoundedIntervention $State $record
Remove-PendingEvent $State $RequestedEventId
Trim-State $State
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'fail-closed' 'failed_closed'
}
function Assert-CurrentTarget {
param($Record, [string]$RequestedTargetId, [string]$RequestedGeneration, [string]$CurrentGovernorId)
Assert-InterventionRuntimeId $RequestedTargetId
Assert-InterventionRuntimeId $RequestedGeneration
Assert-InterventionRuntimeId $CurrentGovernorId
$targetHash = Get-StableHash $RequestedTargetId
$governorHash = Get-StableHash $CurrentGovernorId
if ($targetHash -eq $governorHash) { throw 'heartbeat_intervention_self_target' }
if ($targetHash -ne [string]$Record.targetHash -or (Get-StableHash $RequestedGeneration) -ne [string]$Record.targetGenerationHash) { throw 'heartbeat_intervention_target_changed' }
if ($governorHash -ne [string]$Record.governorHash) {
$legacyGovernorHash = Get-StableHash 'legacy-governor-unassigned'
if ([string]$Record.governorHash -eq $legacyGovernorHash -and [string]$Record.state -in @('queued', 'retry_queued')) {
# Schema-6 did not persist Governor ownership. An unsent legacy record
# can be adopted only after target and generation hashes match.
$Record.governorHash = $governorHash
} else { throw 'heartbeat_intervention_governor_changed' }
}
}
function Invoke-InterventionClaim {
param($State, [string]$ResolvedStatePath, [string]$RequestedInterventionId, [int]$RequestedVersion, [string]$RequestedTargetId, [string]$RequestedGeneration, [string]$CurrentGovernorId, [DateTimeOffset]$Now)
$record = Get-InterventionRecord $State $RequestedInterventionId
if ($null -eq $record -or [int]$record.version -ne $RequestedVersion) { throw 'heartbeat_intervention_not_found' }
Assert-CurrentTarget $record $RequestedTargetId $RequestedGeneration $CurrentGovernorId
if ([string]$record.state -notin @('queued', 'retry_queued')) { throw 'heartbeat_intervention_transition_invalid' }
if ([int]$record.attempts -ge 2) {
$record.state = 'undelivered'
$record.failureReason = 'retry_budget_exhausted'
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'claim' 'undelivered'
}
$token = [guid]::NewGuid().ToString('N')
$record.claimHash = Get-StableHash $token
$record.attempts = [int]$record.attempts + 1
$record.state = 'send_claimed'
$record.claimExpiresAt = $Now.AddSeconds($script:InterventionClaimSeconds).ToString('o')
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'claim' 'send' $token
}
function Assert-InterventionClaim {
param($Record, [int]$RequestedVersion, [string]$RequestedClaimToken)
if ($RequestedVersion -ne [int]$Record.version -or [string]::IsNullOrWhiteSpace($RequestedClaimToken) -or (Get-StableHash $RequestedClaimToken) -ne [string]$Record.claimHash) { throw 'heartbeat_intervention_claim_invalid' }
}
function Invoke-InterventionTransport {
param($State, [string]$ResolvedStatePath, [string]$RequestedInterventionId, [int]$RequestedVersion, [string]$RequestedClaimToken, [string]$Result, [DateTimeOffset]$Now)
$record = Get-InterventionRecord $State $RequestedInterventionId
if ($null -eq $record) { throw 'heartbeat_intervention_not_found' }
Assert-InterventionClaim $record $RequestedVersion $RequestedClaimToken
if ([string]$record.state -notin @('send_claimed', 'delivery_unknown')) { throw 'heartbeat_intervention_transition_invalid' }
if ([string]$record.state -eq 'delivery_unknown' -and $Result -ne 'accepted') { throw 'heartbeat_intervention_transition_invalid' }
$record.transportResult = $Result
switch ($Result) {
'accepted' { $record.state = 'awaiting_task_ack'; $record.claimExpiresAt = $null }
'definite_failure' {
if ([int]$record.attempts -lt 2) { $record.state = 'retry_queued' }
else { $record.state = 'undelivered'; $record.failureReason = 'retry_budget_exhausted' }
$record.claimHash = $null
$record.claimExpiresAt = $null
}
'unknown' { $record.state = 'delivery_unknown' }
default { throw 'heartbeat_intervention_input_invalid' }
}
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'transport' ([string]$record.state)
}
function Invoke-InterventionResponse {
param($State, [string]$ResolvedStatePath, [string]$RequestedInterventionId, [int]$RequestedVersion, [string]$ReportingTaskId, [string]$ReportingGeneration, [string]$Response, [DateTimeOffset]$Now)
$record = Get-InterventionRecord $State $RequestedInterventionId
if ($null -eq $record -or [int]$record.version -ne $RequestedVersion) { throw 'heartbeat_intervention_not_found' }
Assert-InterventionRuntimeId $ReportingTaskId
Assert-InterventionRuntimeId $ReportingGeneration
if ((Get-StableHash $ReportingTaskId) -ne [string]$record.targetHash -or (Get-StableHash $ReportingGeneration) -ne [string]$record.targetGenerationHash) { throw 'heartbeat_intervention_reporter_mismatch' }
if ([string]$record.state -notin @('awaiting_task_ack', 'remediating')) { throw 'heartbeat_intervention_transition_invalid' }
$record.taskResponse = $Response
switch ($Response) {
'acknowledged' { $record.state = 'remediating' }
'outcome_reported' { $record.state = 'verification_pending' }
'declined' { $record.state = 'declined' }
'user_authority_required' { $record.state = 'user_authority_required'; $record.failureReason = 'user_authority_required' }
'remediation_failed' { $record.state = 'remediation_failed' }
default { throw 'heartbeat_intervention_input_invalid' }
}
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'response' ([string]$record.state)
}
function Test-VerificationSourceAllowed {
param([string]$Type, [string]$Source)
switch ($Type) {
{ $_ -in @('AGENT_STALL', 'TASK_ACTIONABLE', 'ZOMBIE_TASK', 'TASK_HANDOFF_INCOMPLETE') } { return $Source -eq 'host_inventory' }
{ $_ -in @('TEST_REGRESSION', 'TEST_VALIDATION_MISSING') } { return $Source -eq 'host_test' }
{ $_ -in @('GIT_BUILD_STATE', 'GIT_STATE_RISK', 'BUILD_ARTIFACT_DRIFT') } { return $Source -eq 'host_git' }
default { return $false }
}
}
function Invoke-InterventionVerify {
param($State, [string]$ResolvedStatePath, [string]$RequestedInterventionId, [int]$RequestedVersion, [string]$RequestedTargetId, [string]$RequestedGeneration, [string]$Source, [string]$Result, [DateTimeOffset]$Now)
$record = Get-InterventionRecord $State $RequestedInterventionId
if ($null -eq $record -or [int]$record.version -ne $RequestedVersion) { throw 'heartbeat_intervention_not_found' }
Assert-InterventionRuntimeId $RequestedTargetId
Assert-InterventionRuntimeId $RequestedGeneration
if ((Get-StableHash $RequestedTargetId) -ne [string]$record.targetHash -or (Get-StableHash $RequestedGeneration) -ne [string]$record.targetGenerationHash) { throw 'heartbeat_intervention_target_changed' }
if ([string]$record.state -notin @('verification_pending', 'active_violation')) { throw 'heartbeat_intervention_transition_invalid' }
if (-not (Test-VerificationSourceAllowed ([string]$record.type) $Source)) { throw 'heartbeat_intervention_verification_invalid' }
$record.verificationSource = $Source
$record.verificationResult = $Result
switch ($Result) {
'resolved' { $record.state = 'verified_resolved' }
'active' { $record.state = 'active_violation' }
'failed' { $record.state = 'remediation_failed' }
default { throw 'heartbeat_intervention_input_invalid' }
}
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'verify' ([string]$record.state)
}
function Get-InterventionNextAction {
param($Record, [DateTimeOffset]$Now)
switch ([string]$Record.state) {
'queued' { return 'claim' }
'retry_queued' { return 'claim' }
'send_claimed' {
if ($Record.claimExpiresAt -and (ConvertTo-UtcTimestamp $Record.claimExpiresAt 'heartbeat_state_invalid') -le $Now) { return 'mark_delivery_unknown' }
return 'wait_for_claim_expiry'
}
'delivery_unknown' { return 'reconcile_delivery_without_retry' }
'awaiting_task_ack' { return 'wait_for_task_response' }
'remediating' { return 'wait_for_task_outcome' }
'verification_pending' { return 'verify_host_postcondition' }
'active_violation' { return 'verify_host_postcondition' }
default { return 'none' }
}
}
function Invoke-InterventionList {
param($State, [string]$CurrentGovernorId, [DateTimeOffset]$Now)
Assert-InterventionRuntimeId $CurrentGovernorId
$governorHash = Get-StableHash $CurrentGovernorId
$legacyGovernorHash = Get-StableHash 'legacy-governor-unassigned'
$records = [Collections.Generic.List[object]]::new()
foreach ($record in @($State.interventions | Where-Object {
[string]$_.governorHash -in @($governorHash, $legacyGovernorHash) -and -not (Test-InterventionTerminal ([string]$_.state))
} | Sort-Object updatedAt | Select-Object -First 16)) {
$records.Add([ordered]@{
interventionId = [string]$record.interventionId
version = [int]$record.version
state = [string]$record.state
type = [string]$record.type
severity = [string]$record.severity
targetHash = ([string]$record.targetHash).Substring(0, 16)
generationHash = ([string]$record.targetGenerationHash).Substring(0, 16)
template = [string]$record.template
postcondition = [string]$record.postcondition
attempts = [int]$record.attempts
updatedAt = [string]$record.updatedAt
permittedNextAction = Get-InterventionNextAction $record $Now
}) | Out-Null
}
return [ordered]@{ ok = $true; action = 'list'; count = $records.Count; interventions = @($records) }
}
function Invoke-InterventionReclaim {
param($State, [string]$ResolvedStatePath, [string]$RequestedInterventionId, [int]$RequestedVersion, [string]$CurrentGovernorId, [DateTimeOffset]$Now)
Assert-InterventionRuntimeId $CurrentGovernorId
$record = Get-InterventionRecord $State $RequestedInterventionId
if ($null -eq $record -or [int]$record.version -ne $RequestedVersion) { throw 'heartbeat_intervention_not_found' }
if ((Get-StableHash $CurrentGovernorId) -ne [string]$record.governorHash) { throw 'heartbeat_intervention_governor_changed' }
if ([string]$record.state -ne 'send_claimed' -or -not $record.claimExpiresAt -or (ConvertTo-UtcTimestamp $record.claimExpiresAt 'heartbeat_state_invalid') -gt $Now) { throw 'heartbeat_intervention_transition_invalid' }
$record.claimHash = $null
$record.claimExpiresAt = $null
$record.state = 'delivery_unknown'
$record.transportResult = 'unknown'
$record.failureReason = 'claim_expired_delivery_ambiguous'
$record.updatedAt = $Now.ToString('o')
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return Get-InterventionPayload $record 'reclaim' ([string]$record.state)
}
function Resolve-ConditionInterventions {
param($State, [string]$ConditionHash, [DateTimeOffset]$Now)
$noticeEligible = $false
foreach ($record in @($State.interventions | Where-Object { [string]$_.conditionHash -eq $ConditionHash -and -not (Test-InterventionTerminal ([string]$_.state)) })) {
if ([bool]$record.resolutionNoticeRequired -and [string]$record.state -in @('awaiting_task_ack', 'remediating', 'verification_pending', 'active_violation')) { $noticeEligible = $true }
$record.state = 'verified_resolved'
$record.verificationSource = 'heartbeat_engine'
$record.verificationResult = 'resolved'
$record.updatedAt = $Now.ToString('o')
}
return $noticeEligible
}
function Test-FamilyCounterContinuity {
param([string]$Family, $Previous, $Current)
$monotonic = switch ($Family) {
'agent_stall' { @('totalTokens') }
'guardian' { @('reviewCount', 'repeats', 'executions') }
'usage' { @('totalTokens') }
'sessions' { @('childCount', 'compactions', 'rolloutBytes') }
default { @() }
}
foreach ($recordKey in $Current.Keys) {
$prior = Get-Value $Previous $recordKey
if (-not $prior) { continue }
foreach ($field in $monotonic) {
if ((Has-Value $prior $field) -and (Has-Value $Current[$recordKey] $field) -and [double]$Current[$recordKey][$field] -lt [double]$prior[$field]) { return $false }
}
}
return $true
}
function Trim-State {
param($State)
$openCount = @($State.conditions.Values | Where-Object { [bool]$_.open }).Count
if ($openCount -gt $script:ConditionLimit) { throw 'heartbeat_condition_capacity' }
if ($State.conditions.Count -gt $script:ConditionLimit) {
$ordered = @($State.conditions.GetEnumerator() | Sort-Object @{Expression={ if ($_.Value.open) { 1 } else { 0 } };Descending=$true}, @{Expression={$_.Value.lastObserved};Descending=$true})
$keep = @($ordered | Select-Object -First $script:ConditionLimit | ForEach-Object { $_.Key })
foreach ($key in @($State.conditions.Keys)) { if ($keep -notcontains $key) { $State.conditions.Remove($key) } }
}
$State.events = @($State.events | Select-Object -Last $script:EventLimit)
if (@($State.interventions).Count -gt $script:InterventionLimit) {
$State.interventions = @($State.interventions | Sort-Object @{Expression={ if (Test-InterventionTerminal ([string]$_.state)) { 0 } else { 1 } };Descending=$true}, @{Expression={$_.updatedAt};Descending=$true} | Select-Object -First $script:InterventionLimit)
}
$State.runIds = @($State.runIds | Select-Object -Last $script:RunIdLimit)
}
function Invoke-Cycle {
param(
$Snapshot,
$State,
[string]$ResolvedStatePath,
[DateTimeOffset]$EvidenceNow,
[DateTimeOffset]$DeliveryNow
)
$started = Get-Date
if ([bool](Get-Value $Snapshot 'isHeartbeatGenerated' $false) -or [string](Get-Value $Snapshot 'origin' 'host') -in @('heartbeat', 'heartbeat_notification')) { return @() }
$routed = [Collections.Generic.List[object]]::new()
foreach ($pendingEvent in @(Get-DueOutboxEvents $State $DeliveryNow)) { $routed.Add($pendingEvent) | Out-Null }
$runId = [string](Get-Value $Snapshot 'runId' '')
$runHash = $null
$duplicateRun = $false
if (-not [string]::IsNullOrWhiteSpace($runId)) {
$runHash = Get-StableHash $runId
$duplicateRun = @($State.runIds) -contains $runHash
}
if ($duplicateRun) {
# A duplicate run is a delivery replay, not a newly accepted collector
# snapshot. It may drain the existing outbox but cannot evaluate detectors
# or advance the schema-v2 source watermark.
$State.health.duplicateRuns = [int64]$State.health.duplicateRuns + 1
$State.revision = [int64]$State.revision + 1
Write-State $State $ResolvedStatePath
return @($routed)
}
if (-not [string]::IsNullOrWhiteSpace([string]$State.health.lastCycleUtc)) {
$lastCycle = ConvertTo-UtcTimestamp ([string]$State.health.lastCycleUtc) 'heartbeat_state_invalid'
if ($EvidenceNow -lt $lastCycle) {
if ($routed.Count -gt 0) {
$State.health.lastError = 'heartbeat_time_out_of_order'
return @($routed)
}
throw 'heartbeat_time_out_of_order'
}
}
if ([int](Get-Value $Snapshot 'schemaVersion' 1) -eq 2) {
$sourceEpochHash = Get-StableHash ([string]$Snapshot.sourceEpoch)
$sourceSequence = [int64]$Snapshot.sourceSequence
$priorSourceEpochHash = [string](Get-Value $State.health 'sourceEpochHash' '')
$priorSourceSequence = Get-Value $State.health 'lastSourceSequence' $null
$hasSourceWatermark = -not [string]::IsNullOrWhiteSpace($priorSourceEpochHash) -and $null -ne $priorSourceSequence
$sameSourceEpoch = $hasSourceWatermark -and $priorSourceEpochHash -eq $sourceEpochHash
if ($sameSourceEpoch -and ($sourceSequence -lt [int64]$priorSourceSequence -or ($sourceSequence -eq [int64]$priorSourceSequence -and -not $duplicateRun))) {
throw 'heartbeat_source_out_of_order'
}
if ($hasSourceWatermark -and -not $sameSourceEpoch) {
foreach ($family in $script:Families) {
$State.collectors[$family].sourceEpochHash = $null
$State.collectors[$family].lastSequence = $null
}
}
$State.health.sourceEpochHash = $sourceEpochHash
$State.health.lastSourceSequence = $sourceSequence
}
if ($runHash) {
$State.runIds = @($State.runIds + $runHash)
$State.health.lastRunIdHash = $runHash
}
$coverage = Get-Value $Snapshot 'collectorCoverage' @{}
$force = [bool](Get-Value $Snapshot 'forceCadence' $false)
$newEvents = [Collections.Generic.List[object]]::new()
$newOutbox = [Collections.Generic.List[object]]::new()
foreach ($family in $script:Families) {
$familyPresent = Test-FamilyPresent $Snapshot $family
$coverageExplicit = Has-Value $coverage $family
if (-not $familyPresent -and -not $coverageExplicit) { continue }
$collector = $State.collectors[$family]
$collector.lastAttempt = $EvidenceNow.ToString('o')
$collector.coverage = [string](Get-Value $coverage $family 'unsupported')
if ($collector.coverage -ne 'observed') {
$collector.sourceEpochHash = $null
$collector.lastSequence = $null
}
if (-not (Test-FamilyDue $collector $EvidenceNow $force)) {
$collector.skippedCadence = [int64]$collector.skippedCadence + 1
continue
}
$collector.lastRun = $EvidenceNow.ToString('o')
if ($collector.coverage -ne 'observed') { continue }
if (-not $familyPresent) { throw 'heartbeat_input_invalid' }
$hasEpoch = Has-Value $Snapshot 'sourceEpoch'
$hasSequence = Has-Value $Snapshot 'sourceSequence'
$epochHash = if ($hasEpoch) { Get-StableHash ([string]$Snapshot.sourceEpoch) } else { $null }
$sequence = if ($hasSequence) { [int64]$Snapshot.sourceSequence } else { $null }
$priorEpoch = [string]$collector.sourceEpochHash
$hasPriorContinuity = -not [string]::IsNullOrWhiteSpace($priorEpoch) -and $null -ne $collector.lastSequence
if ($hasPriorContinuity -and $hasEpoch -and $hasSequence -and $priorEpoch -eq $epochHash -and $sequence -le [int64]$collector.lastSequence) {
throw 'heartbeat_source_out_of_order'
}
$continuity = $hasPriorContinuity -and $hasEpoch -and $hasSequence -and $priorEpoch -eq $epochHash -and $sequence -gt [int64]$collector.lastSequence
$previousForDetector = if ($continuity) { $State.previous[$family] } else { @{} }
$detector = Invoke-Detector $family $Snapshot $previousForDetector $EvidenceNow
if ($family -eq 'heartbeat') {
$runtimeState = Get-Value $detector.Snapshot (Get-StableHash 'heartbeat-engine') @{}
$State.health.runtimeBudgetMs = [double](Get-Value $runtimeState 'runtimeBudgetMs' 0)
$State.health.runtimeObservedMs = [double](Get-Value $runtimeState 'runtimeMs' 0)
$State.health.runtimeOverrunMs = [double](Get-Value $runtimeState 'runtimeOverrunMs' 0)
$State.health.runtimeOverrunPercent = [double](Get-Value $runtimeState 'runtimeOverrunPercent' 0)
$State.health.runtimeBaselineMs = [double](Get-Value $runtimeState 'runtimeBaselineMs' 0)
$State.health.runtimeClassification = [string](Get-Value $runtimeState 'runtimeClassification' 'unobserved')
$State.health.runtimeOverrunStreak = [int](Get-Value $runtimeState 'runtimeOverrunStreak' 0)
$State.health.runtimeBackoffApplied = ([bool](Get-Value $runtimeState 'runtimeBackoffApplied' $false) -or @($detector.Candidates).Count -gt 0)
if (@($detector.Candidates).Count -eq 0) {
$collector.backoffUntilUtc = $null
$State.health.backoffUntilUtc = $null
}
}
$counterContinuity = $continuity -and (Test-FamilyCounterContinuity $family $State.previous[$family] $detector.Snapshot)
$seen = @{}
foreach ($candidate in @($detector.Candidates)) {
$conditionHash = [string]$candidate.ConditionHash
$seen[$conditionHash] = $true
$old = Get-Value $State.conditions $conditionHash
$oldRank = if ($old -and $script:SeverityRank.ContainsKey([string]$old.severity)) { [int]$script:SeverityRank[[string]$old.severity] } else { 0 }
$newRank = [int]$script:SeverityRank[[string]$candidate.Severity]
$subjectGenerationHash = [string](Get-Value $candidate 'SubjectGenerationHash' '')
$ownerGenerationHash = [string](Get-Value $candidate 'OwnerGenerationHash' '')
$generationBindingHash = if ($subjectGenerationHash -or $ownerGenerationHash) {
Get-StableHash @{ subject = $subjectGenerationHash; owner = $ownerGenerationHash }
} else { $null }
$oldGenerationBindingHash = if ($old) { [string](Get-Value $old 'generationBindingHash' '') } else { $null }
$generationChanged = $old -and [bool]$old.open -and $oldGenerationBindingHash -ne [string]$generationBindingHash
$notify = $null -eq $old -or -not [bool]$old.open -or $newRank -gt $oldRank -or $generationChanged
$episodeSeverity = if ($old -and [bool]$old.open -and $oldRank -gt $newRank) { [string]$old.severity } else { [string]$candidate.Severity }
$episode = if ($old) { if ([bool]$old.open) { [int64](Get-Value $old 'episode' 1) } else { [int64](Get-Value $old 'episode' 1) + 1 } } else { 1 }
if ($notify) {
if ($generationChanged) {
# Corrected host identity replaces the stale pending envelope. Only
# the Governor sees the new plan candidate; no monitored task is sent
# anything until planning validates the new target generation.
$State.outbox = @($State.outbox | Where-Object { [string]$_.conditionHash -ne $conditionHash })
}
$eventId = Get-StableHash ('heartbeat-event-v1|{0}|{1}|{2}|{3}|open' -f $conditionHash, $episode, $candidate.Severity, $generationBindingHash)
$event = Convert-CandidateToEvent $candidate $eventId
$routed.Add($event) | Out-Null
$newEvents.Add($event) | Out-Null
$newOutbox.Add((New-OutboxRecord $event $conditionHash)) | Out-Null
$State.health.routesEmitted = [int64]$State.health.routesEmitted + 1
} else {
$State.health.suppressedDuplicates = [int64]$State.health.suppressedDuplicates + 1
}
$route = Get-RouteTarget $candidate
$conditionRecord = [ordered]@{
family = $family
type = [string]$candidate.Type
severity = $episodeSeverity
open = $true
firstObserved = if ($old) { [string]$old.firstObserved } else { [string]$candidate.Detected }
lastObserved = [string]$candidate.Detected
lastNotified = if ($notify) { [string]$candidate.Detected } elseif ($old) { [string]$old.lastNotified } else { $null }
occurrences = if ($old) { [int64]$old.occurrences + 1 } else { 1 }
episode = $episode
sourceEpochHash = if ($old -and [bool]$old.open) { $old.sourceEpochHash } else { $epochHash }
signatureHash = Get-StableHash @{ type = $candidate.Type; severity = $episodeSeverity; generationBindingHash = $generationBindingHash }
subjectHash = [string]$candidate.SubjectHash
ownerHash = Get-StableHash (Get-Value $candidate 'Owner' 'unknown')
routeClass = Get-RouteClass $route
resolutionNotified = $false
resolvedAt = $null
}
if ($generationBindingHash) { $conditionRecord.generationBindingHash = $generationBindingHash }
$State.conditions[$conditionHash] = $conditionRecord
if ($family -eq 'heartbeat') {
$backoff = $EvidenceNow.AddMinutes(15).ToString('o')
$collector.backoffUntilUtc = $backoff
$State.health.backoffUntilUtc = $backoff
}
}
foreach ($conditionHash in @($State.conditions.Keys)) {
$condition = $State.conditions[$conditionHash]
if ([string]$condition.family -ne $family -or -not [bool]$condition.open -or $seen.ContainsKey($conditionHash) -or -not $counterContinuity -or -not $detector.Routes.ContainsKey($conditionHash) -or [string]::IsNullOrWhiteSpace([string]$condition.sourceEpochHash) -or [string]$condition.sourceEpochHash -ne $epochHash) { continue }
$condition.open = $false
$condition.resolvedAt = $EvidenceNow.ToString('o')
$releaseNoticeEligible = Resolve-ConditionInterventions $State $conditionHash $EvidenceNow
if (-not [bool]$condition.resolutionNotified) {
$hint = Get-Value $detector.Routes $conditionHash
$resolutionEventId = Get-StableHash ('heartbeat-event-v1|{0}|{1}|resolved' -f $conditionHash, [int64](Get-Value $condition 'episode' 1))
$resolutionEvent = New-ResolutionEvent $condition $conditionHash $hint $EvidenceNow $resolutionEventId
$resolutionEvent['ReleaseNoticeEligible'] = [bool]$releaseNoticeEligible
$routed.Add($resolutionEvent) | Out-Null
$newEvents.Add($resolutionEvent) | Out-Null
$newOutbox.Add((New-OutboxRecord $resolutionEvent $conditionHash)) | Out-Null
$condition.resolutionNotified = $true
$State.health.routesEmitted = [int64]$State.health.routesEmitted + 1
$State.health.resolutionEvents = [int64]$State.health.resolutionEvents + 1
}
}
$State.previous[$family] = $detector.Snapshot
if ($hasEpoch -and $hasSequence) {
$collector.sourceEpochHash = $epochHash
$collector.lastSequence = $sequence
} else {
$collector.sourceEpochHash = $null
$collector.lastSequence = $null
}
$collector.lastSuccess = $EvidenceNow.ToString('o')
}
$State.health.runs = [int64]$State.health.runs + 1
$State.health.lastCycleUtc = $EvidenceNow.ToString('o')
$State.health.lastSuccessUtc = $EvidenceNow.ToString('o')
$State.health.lastDurationMs = [int][math]::Max(0, ((Get-Date) - $started).TotalMilliseconds)
$State.health.lastError = $null
$State.revision = [int64]$State.revision + 1
if (@($State.outbox).Count + $newOutbox.Count -gt $script:OutboxLimit) { throw 'heartbeat_outbox_capacity' }
$State.outbox = @($State.outbox + @($newOutbox))
foreach ($event in @($newEvents)) {
$State.events = @($State.events + [ordered]@{
type = [string]$event.Type
severity = [string]$event.Severity
routeClass = Get-RouteClass ([string]$event.OwningSolThread)
detected = [string]$event.Detected
dedupHash = Get-StableHash ([string]$event.DedupKey)
})
}
Trim-State $State
Write-State $State $ResolvedStatePath
return @($routed)
}
function Write-Status {
param($State, [DateTimeOffset]$Now)
$open = @($State.conditions.Values | Where-Object { [bool]$_.open })
$engine = if ([int64]$State.health.runs -eq 0) { 'uninitialized' } else { 'healthy' }
if (-not [string]::IsNullOrWhiteSpace([string]$State.health.lastError)) { $engine = 'degraded' }
if (-not [string]::IsNullOrWhiteSpace([string]$State.health.backoffUntilUtc)) {
try { if ($Now -lt (ConvertTo-UtcTimestamp ([string]$State.health.backoffUntilUtc) 'heartbeat_state_invalid')) { $engine = 'backoff' } } catch { $engine = 'degraded' }
}
$coverageCounts = @{ observed = 0; partial = 0; unsupported = 0 }
$coverageLabels = [Collections.Generic.List[string]]::new()
foreach ($family in $script:PublicFamilies) {
$value = [string]$State.collectors[$family].coverage
if ($coverageCounts.ContainsKey($value)) { $coverageCounts[$value]++ } else { $coverageCounts.unsupported++ }
$coverageLabels.Add($family + ':' + $(if ($coverageCounts.ContainsKey($value)) { $value } else { 'unsupported' })) | Out-Null
}
$evaluation = if ($coverageCounts.observed -eq $script:PublicFamilyCount) { 'observed' } elseif ($coverageCounts.unsupported -eq $script:PublicFamilyCount) { 'unsupported' } else { 'partial' }
$lastCycle = if ([string]::IsNullOrWhiteSpace([string]$State.health.lastCycleUtc)) { 'never' } else { [string]$State.health.lastCycleUtc }
$activeInterventions = @($State.interventions | Where-Object { -not (Test-InterventionTerminal ([string]$_.state)) })
$deliveryUnknown = @($activeInterventions | Where-Object { [string]$_.state -eq 'delivery_unknown' }).Count
$outboxExhausted = @($State.outbox | Where-Object { [int]$_.attempts -ge $script:OutboxMaxAttempts }).Count
$backoffUntil = if ([string]::IsNullOrWhiteSpace([string]$State.health.backoffUntilUtc)) { 'none' } else { [string]$State.health.backoffUntilUtc }
Write-Output ('CHRONOS HEARTBEATS engine={0} evaluation={1} activeTypes={2} statusMode=prior_state open={3} outboxPending={4} outboxExhausted={5} interventionsActive={6} deliveryUnknown={7} coverageObserved={8} coveragePartial={9} coverageUnsupported={10} coverageByFamily={11} suppressed={12} routesEmitted={13} deliveryAttempts={14} lastCycle={15} durationMs={16} stateStoreMode={17} stateStoreWriteReady={18} stateStoreProtection={19} stateStoreMigration={20} priorStateDisposition={21} priorStateWriteAttempted={22} completedCycles={23} stateChanges={24} acknowledgedEvents={25} failedCycles={26} duplicateRuns={27} runtimeBudgetMs={28} runtimeObservedMs={29} runtimeOverrunMs={30} runtimeOverrunPercent={31} runtimeBaselineMs={32} runtimeClassification={33} runtimeOverrunStreak={34} runtimeBackoffApplied={35} backoffUntil={36} codexHomeSource={37} codexHomeIdentity={38}' -f $engine, $evaluation, $script:PublicFamilyCount, $open.Count, @($State.outbox).Count, $outboxExhausted, $activeInterventions.Count, $deliveryUnknown, $coverageCounts.observed, $coverageCounts.partial, $coverageCounts.unsupported, ($coverageLabels -join ','), $State.health.suppressedDuplicates, $State.health.routesEmitted, $State.health.deliveryAttempts, $lastCycle, $State.health.lastDurationMs, $script:StateStoreMode, $script:StateStoreWriteReady.ToString().ToLowerInvariant(), $script:StateStoreProtection, $script:StateStoreMigration, $script:PriorStateDisposition, $script:PriorStateWriteAttempted.ToString().ToLowerInvariant(), $State.health.runs, $State.revision, $State.health.acknowledgedEvents, $State.health.failedCycles, $State.health.duplicateRuns, (Format-Number $State.health.runtimeBudgetMs), (Format-Number $State.health.runtimeObservedMs), (Format-Number $State.health.runtimeOverrunMs), (Format-Number $State.health.runtimeOverrunPercent), (Format-Number $State.health.runtimeBaselineMs), $State.health.runtimeClassification, $State.health.runtimeOverrunStreak, ([bool]$State.health.runtimeBackoffApplied).ToString().ToLowerInvariant(), $backoffUntil, $script:CodexHomeSource, $script:CodexHomeIdentity)
foreach ($condition in @($open | Sort-Object @{Expression={ $script:SeverityRank[[string]$_.severity] };Descending=$true}, lastObserved | Select-Object -First 8)) {
Write-Output ('CHRONOS HEARTBEAT CONDITION severity={0} type={1} subjectHash={2} route={3} firstObserved={4} lastObserved={5}' -f $condition.severity, $condition.type, ([string]$condition.subjectHash).Substring(0, 16), $condition.routeClass, $condition.firstObserved, $condition.lastObserved)
}
}
function Write-SafeError {
param([string]$Code, [string]$RequestedAction)
$payload = [ordered]@{ ok = $false; error = $Code; action = $RequestedAction }
Write-Output ('CHRONOS HEARTBEATS ' + ($payload | ConvertTo-Json -Compress))
}
function New-HeartbeatMutex {
param([string]$ResolvedStatePath)
$identity = Get-CanonicalStateIdentity $ResolvedStatePath
$name = 'Global\ChronosHeartbeat-' + (Get-StableHash $identity).Substring(0, 24)
try {
$sid = [Security.Principal.WindowsIdentity]::GetCurrent().User
$security = New-Object Security.AccessControl.MutexSecurity
$rule = New-Object Security.AccessControl.MutexAccessRule($sid, [Security.AccessControl.MutexRights]::FullControl, [Security.AccessControl.AccessControlType]::Allow)
$security.AddAccessRule($rule)
$created = $false
return New-Object Threading.Mutex($false, $name, [ref]$created, $security)
} catch {
throw 'heartbeat_mutex_unavailable'
}
}
$mutex = $null
$acquired = $false
$state = $null
$resolved = $null
try {
if ($Action -notin @('cycle', 'status', 'acknowledge', 'intervention-list', 'intervention-plan', 'intervention-fail-closed', 'intervention-claim', 'intervention-reclaim', 'intervention-transport', 'intervention-response', 'intervention-verify')) { throw 'heartbeat_action_invalid' }
$resolved = Resolve-StatePath $StatePath $Scope
Initialize-StateStore $resolved.Path
$input = $null
$deliveryNow = [DateTimeOffset]::UtcNow
$evidenceNow = $deliveryNow
if ($Action -eq 'cycle') {
if ([string]::IsNullOrWhiteSpace($InputPath) -and [string]::IsNullOrWhiteSpace($InspectorOutputPath)) { throw 'heartbeat_input_required' }
$input = Read-NormalizedInput $InputPath
Merge-InspectorOutput $input $InspectorOutputPath -Authorized:$InspectorAuthorized
Assert-Input $input
$evidenceNow = if (Has-Value $input 'capturedAtUtc') { ConvertTo-UtcTimestamp $input.capturedAtUtc } else { $deliveryNow }
if ($evidenceNow -gt $deliveryNow.AddMinutes(10)) { throw 'heartbeat_time_in_future' }
}
$mutex = New-HeartbeatMutex $resolved.Path
$abandoned = $false
$mutexWaitMilliseconds = if ($Action -eq 'cycle') { 5000 } else { 1000 }
try { $acquired = $mutex.WaitOne($mutexWaitMilliseconds) } catch [Threading.AbandonedMutexException] { $acquired = $true; $abandoned = $true }
if (-not $acquired) {
if ($Action -eq 'cycle') {
Write-Output 'CHRONOS HEARTBEATS {"ok":false,"error":"heartbeat_mutex_busy","cycleDropped":false,"retryRequired":true}'
exit 1
}
throw 'heartbeat_mutex_busy'
}
# Always reopen and validate authoritative state after acquisition. This is
# mandatory after an abandoned mutex because the prior owner may have died.
Import-LegacyStateIfPresent $resolved
$state = Read-State $resolved.Path $resolved.ScopeHash
if ($Action -eq 'status') {
Write-Status $state ([DateTimeOffset]::UtcNow)
exit 0
}
if ($Action -eq 'acknowledge') {
$acknowledged = Acknowledge-OutboxEvent $state $EventId $resolved.Path
$payload = [ordered]@{ ok = $true; action = 'acknowledge'; eventId = $EventId; acknowledged = $acknowledged }
Write-Output ('CHRONOS HEARTBEATS ' + ($payload | ConvertTo-Json -Compress))
exit 0
}
if ($Action -eq 'intervention-list') {
Write-Output ('CHRONOS INTERVENTIONS ' + ((Invoke-InterventionList $state $GovernorId $deliveryNow) | ConvertTo-Json -Compress -Depth 6))
exit 0
}
if ($Action -eq 'intervention-plan') {
Write-InterventionPayload (Invoke-InterventionPlan $state $resolved.Path $EventId $CorroboratingEventId $TargetId $TargetGeneration $GovernorId $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-fail-closed') {
Write-InterventionPayload (Invoke-InterventionFailClosed $state $resolved.Path $EventId $FailureReason $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-claim') {
Write-InterventionPayload (Invoke-InterventionClaim $state $resolved.Path $InterventionId $InterventionVersion $TargetId $TargetGeneration $GovernorId $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-reclaim') {
Write-InterventionPayload (Invoke-InterventionReclaim $state $resolved.Path $InterventionId $InterventionVersion $GovernorId $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-transport') {
Write-InterventionPayload (Invoke-InterventionTransport $state $resolved.Path $InterventionId $InterventionVersion $ClaimToken $TransportResult $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-response') {
Write-InterventionPayload (Invoke-InterventionResponse $state $resolved.Path $InterventionId $InterventionVersion $TargetId $TargetGeneration $TaskResponse $deliveryNow)
exit 0
}
if ($Action -eq 'intervention-verify') {
Write-InterventionPayload (Invoke-InterventionVerify $state $resolved.Path $InterventionId $InterventionVersion $TargetId $TargetGeneration $VerificationSource $VerificationResult $deliveryNow)
exit 0
}
$events = @(Invoke-Cycle $input $state $resolved.Path $evidenceNow $deliveryNow)
if ($events.Count -gt 0) {
$payload = [ordered]@{ ok = $true; eventCount = $events.Count; events = $events }
Write-Output ('CHRONOS HEARTBEATS ' + ($payload | ConvertTo-Json -Compress -Depth 10))
Mark-OutboxAttempts $state @($events | ForEach-Object { [string]$_.EventId }) $resolved.Path $deliveryNow
}
exit 0
} catch {
$code = [string]$_.Exception.Message
if ($code -notmatch '^heartbeat_[a-z_]+$') { $code = 'heartbeat_internal_error' }
if ($Action -eq 'cycle' -and $acquired -and $null -ne $resolved) {
try {
$committed = Read-State $resolved.Path $resolved.ScopeHash
$committed.health.failedCycles = [int64]$committed.health.failedCycles + 1
$committed.health.lastError = $code
$committed.revision = [int64]$committed.revision + 1
Write-State $committed $resolved.Path
} catch {
# Preserve the original privacy-safe cycle error if failure accounting
# cannot be committed.
}
}
Write-SafeError $code $Action
exit 1
} finally {
if ($mutex) {
if ($acquired) { try { $mutex.ReleaseMutex() | Out-Null } catch {} }
$mutex.Dispose()
}
}
SHA-256: d173767f5de89205dfb7627fcf34d507a26107c22c0d32228e2b6dcfa9877d33