scripts/internal/continuous-co-review/review-authority-core.ps1
|
$ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest # F-198 / T042-T044: dependency-free PURE review authority core. This file intentionally performs # no filesystem, Git, process, environment, or clock I/O. Callers pass immutable facts and observed # timestamps in; adapters execute the returned decisions. $script:ReviewAuthorityMaxInvocationTimeoutSeconds = 7200 $script:ReviewAuthorityMaxTerminationGraceSeconds = 10 $script:ReviewAuthorityOrchestrationOverheadAllowanceSeconds = 120 $script:ReviewAuthorityCandidateLimits = [ordered]@{ max_candidate_bytes = 262144 max_summary_characters = 4000 max_findings = 100 max_local_id_characters = 64 max_title_characters = 200 max_description_characters = 4000 max_location_characters = 1000 } # A terminal duration includes the invocation timeout, termination grace, and bounded controller # overhead. Keep this value derived here so changing any contributing ceiling changes the schema # bound in the same edit; measured duration evidence is validated as observed and is never clamped. $script:ReviewAuthorityMaxDurationMilliseconds = [long]( $script:ReviewAuthorityMaxInvocationTimeoutSeconds + $script:ReviewAuthorityMaxTerminationGraceSeconds + $script:ReviewAuthorityOrchestrationOverheadAllowanceSeconds ) * 1000L function Get-ReviewAuthorityTimingLimits { return [pscustomobject][ordered]@{ max_invocation_timeout_seconds = $script:ReviewAuthorityMaxInvocationTimeoutSeconds max_termination_grace_seconds = $script:ReviewAuthorityMaxTerminationGraceSeconds orchestration_overhead_allowance_seconds = $script:ReviewAuthorityOrchestrationOverheadAllowanceSeconds max_duration_ms = $script:ReviewAuthorityMaxDurationMilliseconds } } function Get-ReviewAuthorityCandidateLimits { return [pscustomobject][ordered]@{ max_candidate_bytes = $script:ReviewAuthorityCandidateLimits.max_candidate_bytes max_summary_characters = $script:ReviewAuthorityCandidateLimits.max_summary_characters max_findings = $script:ReviewAuthorityCandidateLimits.max_findings max_local_id_characters = $script:ReviewAuthorityCandidateLimits.max_local_id_characters max_title_characters = $script:ReviewAuthorityCandidateLimits.max_title_characters max_description_characters = $script:ReviewAuthorityCandidateLimits.max_description_characters max_location_characters = $script:ReviewAuthorityCandidateLimits.max_location_characters } } function Get-ReviewAuthorityPropertyNames { param([AllowNull()]$Object) if ($null -eq $Object) { return @() } if ($Object -is [System.Collections.IDictionary]) { return @($Object.Keys) } return @($Object.PSObject.Properties.Name) } function Get-ReviewAuthorityProperty { param([AllowNull()]$Object, [Parameter(Mandatory)][string]$Name) if ($null -eq $Object) { return $null } if ($Object -is [System.Collections.IDictionary]) { if ($Object.Contains($Name)) { $value = $Object[$Name] if ($value -is [System.Collections.IEnumerable] -and $value -isnot [string] -and $value -isnot [System.Collections.IDictionary]) { Write-Output -NoEnumerate $value; return } return $value } return $null } $property = $Object.PSObject.Properties[$Name] if ($null -eq $property) { return $null } if ($property.Value -is [System.Collections.IEnumerable] -and $property.Value -isnot [string] -and $property.Value -isnot [System.Collections.IDictionary]) { Write-Output -NoEnumerate $property.Value; return } return $property.Value } function Test-ReviewAuthorityIdentifier { param( [AllowNull()]$Value, [Parameter(Mandatory)][ValidateSet('campaign', 'run', 'grant', 'reservation', 'finding', 'lineage', 'disposition')][string]$Kind ) if ($Value -isnot [string]) { return $false } $prefix = switch ($Kind) { 'campaign' { 'cmp' } 'run' { 'run' } 'grant' { 'grant' } 'reservation' { 'res' } 'finding' { 'finding' } 'lineage' { 'lin' } 'disposition' { 'disposition' } } return ([string]$Value -cmatch ('^{0}-[a-z0-9][a-z0-9-]{{0,63}}$' -f $prefix)) } function Test-ReviewCampaignFeatureIdentity { param([AllowNull()]$Value) return ($Value -is [string] -and [string]$Value -cmatch '^[a-z0-9][a-z0-9-]{0,127}$') } function Test-ReviewCampaignIterationIdentity { param([AllowNull()]$Value) return ($Value -is [string] -and [string]$Value -cmatch '^[0-9]{3,}$') } function Test-ReviewCampaignScopeIdentity { param( [AllowNull()]$FeatureId, [AllowNull()]$IterationNumber ) return ((Test-ReviewCampaignFeatureIdentity -Value $FeatureId) -and (Test-ReviewCampaignIterationIdentity -Value $IterationNumber)) } function Add-ReviewAuthorityError { param( [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors, [Parameter(Mandatory)][string]$Message ) $Errors.Add($Message) | Out-Null } function ConvertTo-ReviewAuthorityBoundedText { param( [AllowNull()]$Value, [Parameter(Mandatory)][ValidateRange(16, 1000000)][int]$MaximumLength ) if ($null -eq $Value) { return $null } $text = [string]$Value if ($text.Length -le $MaximumLength) { return $text } $marker = '...[truncated]' return $text.Substring(0, $MaximumLength - $marker.Length) + $marker } function Test-ReviewAuthorityStringField { param( [Parameter(Mandatory)]$Object, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors, [int]$MaxLength = 4096, [string[]]$Enum, [switch]$Optional, [switch]$AllowEmpty ) $names = Get-ReviewAuthorityPropertyNames -Object $Object if ($names -notcontains $Name) { if (-not $Optional) { Add-ReviewAuthorityError -Errors $Errors -Message "missing-required:$Name" } return } $value = Get-ReviewAuthorityProperty -Object $Object -Name $Name if ($null -eq $value -and $Optional) { return } if ($value -isnot [string] -and $value -isnot [datetime] -and $value -isnot [datetimeoffset]) { Add-ReviewAuthorityError -Errors $Errors -Message ('wrong-type:{0}:string' -f $Name) return } if ((-not $AllowEmpty) -and [string]::IsNullOrWhiteSpace([string]$value)) { Add-ReviewAuthorityError -Errors $Errors -Message "empty-value:$Name" } if (([string]$value).Length -gt $MaxLength) { Add-ReviewAuthorityError -Errors $Errors -Message ('too-long:{0}:{1}' -f $Name, $MaxLength) } if ($null -ne $Enum -and @($Enum).Count -gt 0 -and ([string]$value -cnotin @($Enum))) { Add-ReviewAuthorityError -Errors $Errors -Message "invalid-enum:$Name" } } function Test-ReviewAuthorityBooleanField { param( [Parameter(Mandatory)]$Object, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors, [switch]$Optional ) $names = Get-ReviewAuthorityPropertyNames -Object $Object if ($names -notcontains $Name) { if (-not $Optional) { Add-ReviewAuthorityError -Errors $Errors -Message "missing-required:$Name" } return } if ((Get-ReviewAuthorityProperty -Object $Object -Name $Name) -isnot [bool]) { Add-ReviewAuthorityError -Errors $Errors -Message ('wrong-type:{0}:boolean' -f $Name) } } function Test-ReviewAuthorityIntegerField { param( [Parameter(Mandatory)]$Object, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors, [long]$Minimum = 0, [long]$Maximum = [long]::MaxValue, [switch]$Optional ) $names = Get-ReviewAuthorityPropertyNames -Object $Object if ($names -notcontains $Name) { if (-not $Optional) { Add-ReviewAuthorityError -Errors $Errors -Message "missing-required:$Name" } return } $value = Get-ReviewAuthorityProperty -Object $Object -Name $Name if (($value -isnot [byte]) -and ($value -isnot [int16]) -and ($value -isnot [int]) -and ($value -isnot [long])) { Add-ReviewAuthorityError -Errors $Errors -Message ('wrong-type:{0}:integer' -f $Name) return } if ([long]$value -lt $Minimum -or [long]$value -gt $Maximum) { Add-ReviewAuthorityError -Errors $Errors -Message "out-of-range:$Name" } } function Test-ReviewAuthorityIdField { param( [Parameter(Mandatory)]$Object, [Parameter(Mandatory)][string]$Name, [Parameter(Mandatory)][ValidateSet('campaign', 'run', 'grant', 'reservation', 'finding', 'lineage', 'disposition')][string]$Kind, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors ) Test-ReviewAuthorityStringField -Object $Object -Name $Name -Errors $Errors -MaxLength 68 $value = Get-ReviewAuthorityProperty -Object $Object -Name $Name if ($null -ne $value -and -not (Test-ReviewAuthorityIdentifier -Value $value -Kind $Kind)) { Add-ReviewAuthorityError -Errors $Errors -Message ('invalid-id:{0}:{1}' -f $Name, $Kind) } } function Test-ReviewAuthorityClosedShape { param( [Parameter(Mandatory)]$Object, [Parameter(Mandatory)][string[]]$Allowed, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors ) if ($null -eq $Object -or (($Object -isnot [System.Collections.IDictionary]) -and ($Object -isnot [pscustomobject]))) { Add-ReviewAuthorityError -Errors $Errors -Message 'wrong-type:$:object' return $false } foreach ($name in (Get-ReviewAuthorityPropertyNames -Object $Object)) { if ([string]$name -cnotin $Allowed) { Add-ReviewAuthorityError -Errors $Errors -Message "unknown-field:$name" } } return $true } function Test-ReviewAuthorityFinding { param( [Parameter(Mandatory)]$Finding, [Parameter(Mandatory)][ValidateSet('candidate', 'terminal')][string]$Kind, [Parameter(Mandatory)][AllowEmptyCollection()][System.Collections.Generic.List[string]]$Errors, [Parameter(Mandatory)][int]$Index ) $limits = Get-ReviewAuthorityCandidateLimits $prefix = "findings[$Index]" $allowed = if ($Kind -ceq 'candidate') { @('local_id', 'severity', 'title', 'description', 'location') } else { @('finding_id', 'source_local_id', 'lineage_id', 'severity', 'title', 'description', 'location', 'relevance', 'resolution') } $nested = [System.Collections.Generic.List[string]]::new() if (-not (Test-ReviewAuthorityClosedShape -Object $Finding -Allowed $allowed -Errors $nested)) { foreach ($error in $nested) { Add-ReviewAuthorityError -Errors $Errors -Message "$prefix.$error" } return } if ($Kind -ceq 'candidate') { Test-ReviewAuthorityStringField -Object $Finding -Name 'local_id' -Errors $nested -MaxLength $limits.max_local_id_characters } else { Test-ReviewAuthorityIdField -Object $Finding -Name 'finding_id' -Kind finding -Errors $nested Test-ReviewAuthorityStringField -Object $Finding -Name 'source_local_id' -Errors $nested -MaxLength $limits.max_local_id_characters Test-ReviewAuthorityIdField -Object $Finding -Name 'lineage_id' -Kind lineage -Errors $nested Test-ReviewAuthorityStringField -Object $Finding -Name 'relevance' -Errors $nested -MaxLength 32 -Enum @('current', 'snapshot-moved', 'unknown') Test-ReviewAuthorityStringField -Object $Finding -Name 'resolution' -Errors $nested -MaxLength 32 -Enum @('open', 'resolved', 'superseded') } Test-ReviewAuthorityStringField -Object $Finding -Name 'severity' -Errors $nested -MaxLength 16 -Enum @('blocking', 'major', 'minor', 'note') Test-ReviewAuthorityStringField -Object $Finding -Name 'title' -Errors $nested -MaxLength $limits.max_title_characters Test-ReviewAuthorityStringField -Object $Finding -Name 'description' -Errors $nested -MaxLength $limits.max_description_characters Test-ReviewAuthorityStringField -Object $Finding -Name 'location' -Errors $nested -MaxLength $limits.max_location_characters -Optional foreach ($error in $nested) { Add-ReviewAuthorityError -Errors $Errors -Message "$prefix.$error" } } function Test-ReviewAuthorityContractObject { [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet( 'ReviewCampaign', 'ReviewRun', 'ReviewInvocation', 'ReviewerCandidate', 'ReviewResult', 'GrantFact', 'ReservationFact', 'SpendFact', 'ReleaseFact', 'ClaimFact', 'HumanDispositionFact', 'RecoveryFact', 'ReviewFinalizationFact' )][string]$ContractName, [Parameter(Mandatory)]$InputObject, [string]$ExpectedCampaignId, [string]$ExpectedRunId, [string]$ExpectedTargetDigest ) $errors = [System.Collections.Generic.List[string]]::new() $candidateLimits = Get-ReviewAuthorityCandidateLimits $fields = switch ($ContractName) { 'ReviewCampaign' { @('schema_version', 'campaign_id', 'target_lineage', 'created_at') } 'ReviewRun' { @('schema_version', 'campaign_id', 'run_id', 'target_digest', 'harness_id', 'state') } 'ReviewInvocation' { @('schema_version', 'campaign_id', 'run_id', 'target_digest', 'snapshot_path', 'review_scope', 'prompt_path', 'candidate_result_path', 'candidate_report_path', 'deadline') } 'ReviewerCandidate' { @('schema_version', 'run_id', 'target_digest', 'completion', 'verdict', 'summary', 'findings') } 'ReviewResult' { @('schema_version', 'campaign_id', 'run_id', 'target_digest', 'harness_id', 'completion', 'verdict', 'runtime_outcome', 'termination_verified', 'containment', 'currentness', 'validation', 'can_approve_current', 'failure_reason', 'summary', 'findings', 'started_at', 'ended_at', 'duration_ms') } 'GrantFact' { @('schema_version', 'fact_type', 'campaign_id', 'grant_id', 'slots', 'authority_kind', 'authorization_ref', 'observed_at') } 'ReservationFact' { @('schema_version', 'fact_type', 'campaign_id', 'reservation_id', 'grant_id', 'slot', 'run_id', 'observed_at') } 'SpendFact' { @('schema_version', 'fact_type', 'campaign_id', 'reservation_id', 'run_id', 'invocation_started_at') } 'ReleaseFact' { @('schema_version', 'fact_type', 'campaign_id', 'reservation_id', 'run_id', 'reason', 'observed_at') } 'ClaimFact' { @('schema_version', 'fact_type', 'campaign_id', 'run_id', 'target_lineage', 'generation', 'disposition', 'observed_at') } 'HumanDispositionFact' { @('schema_version', 'fact_type', 'disposition_id', 'campaign_id', 'run_id', 'target_digest', 'decision', 'authority_kind', 'authorized_by', 'authorization_ref', 'rationale', 'observed_at') } 'ReviewFinalizationFact' { @('schema_version', 'fact_type', 'campaign_id', 'run_id', 'reviewed_digest', 'finalization_commit') } 'RecoveryFact' { @( 'schema_version', 'fact_type', 'campaign_id', 'run_id', 'target_digest', 'harness_id', 'target_lineage', 'runtime_id', 'platform', 'containment_kind', 'containment_id', 'process_id', 'process_started_at', 'invocation_started_at', 'invocation_started_monotonic_ms', 'target_kind', 'snapshot_path', 'workspace_root', 'origin_repo', 'git_root', 'origin_head_before', 'staging_root', 'verification_plan_present', 'verification_plan_sha256', 'machinery_paths', 'machinery_paths_sha256', 'excluded_path_patterns', 'excluded_path_patterns_sha256' ) } } if (-not (Test-ReviewAuthorityClosedShape -Object $InputObject -Allowed $fields -Errors $errors)) { return [pscustomobject]@{ valid = $false; category = 'schema-invalid'; errors = @($errors) } } Test-ReviewAuthorityStringField -Object $InputObject -Name 'schema_version' -Errors $errors -MaxLength 8 $version = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'schema_version') if (-not [string]::IsNullOrWhiteSpace($version) -and $version -cne '1.0') { Add-ReviewAuthorityError -Errors $errors -Message 'unsupported-version:schema_version' } if ($fields -contains 'campaign_id') { Test-ReviewAuthorityIdField -Object $InputObject -Name 'campaign_id' -Kind campaign -Errors $errors } if ($fields -contains 'run_id') { Test-ReviewAuthorityIdField -Object $InputObject -Name 'run_id' -Kind run -Errors $errors } if ($fields -contains 'target_digest') { Test-ReviewAuthorityStringField -Object $InputObject -Name 'target_digest' -Errors $errors -MaxLength 128 } if ($fields -contains 'harness_id') { Test-ReviewAuthorityStringField -Object $InputObject -Name 'harness_id' -Errors $errors -MaxLength 64 } if ($fields -contains 'disposition_id') { Test-ReviewAuthorityIdField -Object $InputObject -Name 'disposition_id' -Kind disposition -Errors $errors } switch ($ContractName) { 'ReviewCampaign' { Test-ReviewAuthorityIdField -Object $InputObject -Name 'target_lineage' -Kind lineage -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'created_at' -Errors $errors -MaxLength 64 } 'ReviewRun' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'state' -Errors $errors -MaxLength 32 -Enum @('requested', 'reserved', 'preflighted', 'claimed', 'invoked', 'validating', 'terminal') } 'ReviewInvocation' { foreach ($name in @('snapshot_path', 'prompt_path', 'candidate_result_path', 'candidate_report_path')) { Test-ReviewAuthorityStringField -Object $InputObject -Name $name -Errors $errors -MaxLength 2048 } Test-ReviewAuthorityStringField -Object $InputObject -Name 'review_scope' -Errors $errors -MaxLength 16000 Test-ReviewAuthorityStringField -Object $InputObject -Name 'deadline' -Errors $errors -MaxLength 64 } 'ReviewerCandidate' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'completion' -Errors $errors -MaxLength 16 -Enum @('complete', 'partial') Test-ReviewAuthorityStringField -Object $InputObject -Name 'verdict' -Errors $errors -MaxLength 16 -Enum @('pass', 'findings', 'incomplete') Test-ReviewAuthorityStringField -Object $InputObject -Name 'summary' -Errors $errors -MaxLength $candidateLimits.max_summary_characters } 'ReviewResult' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'completion' -Errors $errors -MaxLength 16 -Enum @('complete', 'partial', 'none') Test-ReviewAuthorityStringField -Object $InputObject -Name 'verdict' -Errors $errors -MaxLength 16 -Enum @('pass', 'findings', 'incomplete', 'failed') Test-ReviewAuthorityStringField -Object $InputObject -Name 'runtime_outcome' -Errors $errors -MaxLength 32 -Enum @('completed', 'preflight-failed', 'claim-contended', 'launch-failed', 'timed-out', 'terminated', 'invalid-output', 'identity-mismatch', 'containment-violated', 'abandoned') Test-ReviewAuthorityBooleanField -Object $InputObject -Name 'termination_verified' -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'containment' -Errors $errors -MaxLength 16 -Enum @('verified', 'violated', 'unknown') Test-ReviewAuthorityStringField -Object $InputObject -Name 'currentness' -Errors $errors -MaxLength 32 -Enum @('current', 'snapshot-moved', 'unknown') Test-ReviewAuthorityStringField -Object $InputObject -Name 'validation' -Errors $errors -MaxLength 16 -Enum @('valid', 'invalid', 'not-produced') Test-ReviewAuthorityBooleanField -Object $InputObject -Name 'can_approve_current' -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'failure_reason' -Errors $errors -MaxLength 2000 -Optional Test-ReviewAuthorityStringField -Object $InputObject -Name 'summary' -Errors $errors -MaxLength $candidateLimits.max_summary_characters Test-ReviewAuthorityStringField -Object $InputObject -Name 'started_at' -Errors $errors -MaxLength 64 Test-ReviewAuthorityStringField -Object $InputObject -Name 'ended_at' -Errors $errors -MaxLength 64 Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'duration_ms' -Errors $errors -Minimum 0 -Maximum $script:ReviewAuthorityMaxDurationMilliseconds } 'GrantFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('grant') Test-ReviewAuthorityIdField -Object $InputObject -Name 'grant_id' -Kind grant -Errors $errors Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'slots' -Errors $errors -Minimum 1 -Maximum 100 Test-ReviewAuthorityStringField -Object $InputObject -Name 'authority_kind' -Errors $errors -MaxLength 16 -Enum @('human') Test-ReviewAuthorityStringField -Object $InputObject -Name 'authorization_ref' -Errors $errors -MaxLength 256 Test-ReviewAuthorityStringField -Object $InputObject -Name 'observed_at' -Errors $errors -MaxLength 64 } 'ReservationFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('reservation') Test-ReviewAuthorityIdField -Object $InputObject -Name 'reservation_id' -Kind reservation -Errors $errors Test-ReviewAuthorityIdField -Object $InputObject -Name 'grant_id' -Kind grant -Errors $errors Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'slot' -Errors $errors -Minimum 1 -Maximum 100 Test-ReviewAuthorityStringField -Object $InputObject -Name 'observed_at' -Errors $errors -MaxLength 64 } 'SpendFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('spend') Test-ReviewAuthorityIdField -Object $InputObject -Name 'reservation_id' -Kind reservation -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'invocation_started_at' -Errors $errors -MaxLength 64 } 'ReleaseFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('release') Test-ReviewAuthorityIdField -Object $InputObject -Name 'reservation_id' -Kind reservation -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'reason' -Errors $errors -MaxLength 512 Test-ReviewAuthorityStringField -Object $InputObject -Name 'observed_at' -Errors $errors -MaxLength 64 } 'ClaimFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('claim-held', 'claim-released', 'claim-abandoned') Test-ReviewAuthorityIdField -Object $InputObject -Name 'target_lineage' -Kind lineage -Errors $errors Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'generation' -Errors $errors -Minimum 1 -Maximum ([int]::MaxValue) Test-ReviewAuthorityStringField -Object $InputObject -Name 'disposition' -Errors $errors -MaxLength 16 -Enum @('held', 'released', 'abandoned') Test-ReviewAuthorityStringField -Object $InputObject -Name 'observed_at' -Errors $errors -MaxLength 64 } 'HumanDispositionFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 32 -Enum @('human-disposition') Test-ReviewAuthorityIdField -Object $InputObject -Name 'disposition_id' -Kind disposition -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'decision' -Errors $errors -MaxLength 32 -Enum @('accept-current', 'require-correction') Test-ReviewAuthorityStringField -Object $InputObject -Name 'authority_kind' -Errors $errors -MaxLength 16 -Enum @('human') Test-ReviewAuthorityStringField -Object $InputObject -Name 'authorized_by' -Errors $errors -MaxLength 200 Test-ReviewAuthorityStringField -Object $InputObject -Name 'authorization_ref' -Errors $errors -MaxLength 256 Test-ReviewAuthorityStringField -Object $InputObject -Name 'rationale' -Errors $errors -MaxLength 2000 Test-ReviewAuthorityStringField -Object $InputObject -Name 'observed_at' -Errors $errors -MaxLength 64 } 'ReviewFinalizationFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 32 -Enum @('review-finalization') Test-ReviewAuthorityStringField -Object $InputObject -Name 'reviewed_digest' -Errors $errors -MaxLength 64 Test-ReviewAuthorityStringField -Object $InputObject -Name 'finalization_commit' -Errors $errors -MaxLength 64 foreach ($name in @('reviewed_digest', 'finalization_commit')) { $value = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name $name) if (-not [string]::IsNullOrWhiteSpace($value) -and $value -cnotmatch '^[0-9a-f]{40,64}$') { Add-ReviewAuthorityError -Errors $errors -Message "invalid-git-object-id:$name" } } } 'RecoveryFact' { Test-ReviewAuthorityStringField -Object $InputObject -Name 'fact_type' -Errors $errors -MaxLength 16 -Enum @('recovery') Test-ReviewAuthorityIdField -Object $InputObject -Name 'target_lineage' -Kind lineage -Errors $errors Test-ReviewAuthorityStringField -Object $InputObject -Name 'runtime_id' -Errors $errors -MaxLength 64 Test-ReviewAuthorityStringField -Object $InputObject -Name 'platform' -Errors $errors -MaxLength 16 -Enum @('fixture', 'windows', 'linux', 'macos') Test-ReviewAuthorityStringField -Object $InputObject -Name 'containment_kind' -Errors $errors -MaxLength 32 -Enum @('fixture', 'job-object', 'cgroup-v2', 'process-group') Test-ReviewAuthorityStringField -Object $InputObject -Name 'containment_id' -Errors $errors -MaxLength 4096 Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'process_id' -Errors $errors -Minimum 1 -Maximum ([int]::MaxValue) Test-ReviewAuthorityStringField -Object $InputObject -Name 'process_started_at' -Errors $errors -MaxLength 64 Test-ReviewAuthorityStringField -Object $InputObject -Name 'invocation_started_at' -Errors $errors -MaxLength 64 Test-ReviewAuthorityIntegerField -Object $InputObject -Name 'invocation_started_monotonic_ms' -Errors $errors -Minimum 0 Test-ReviewAuthorityStringField -Object $InputObject -Name 'target_kind' -Errors $errors -MaxLength 64 foreach ($name in @('snapshot_path', 'workspace_root', 'origin_repo', 'git_root', 'staging_root')) { Test-ReviewAuthorityStringField -Object $InputObject -Name $name -Errors $errors -MaxLength 4096 } Test-ReviewAuthorityStringField -Object $InputObject -Name 'origin_head_before' -Errors $errors -MaxLength 128 $names = Get-ReviewAuthorityPropertyNames -Object $InputObject $bindingNames = @( 'verification_plan_present', 'verification_plan_sha256', 'machinery_paths', 'machinery_paths_sha256', 'excluded_path_patterns', 'excluded_path_patterns_sha256' ) $historicalBindingNames = @( 'verification_plan_present', 'verification_plan_sha256', 'machinery_paths', 'machinery_paths_sha256' ) $bindingCount = @($bindingNames | Where-Object { $names -contains $_ }).Count $historicalBindingComplete = @($historicalBindingNames | Where-Object { $names -notcontains $_ }).Count -eq 0 -and @(@('excluded_path_patterns', 'excluded_path_patterns_sha256') | Where-Object { $names -contains $_ }).Count -eq 0 $currentBindingComplete = @($bindingNames | Where-Object { $names -notcontains $_ }).Count -eq 0 if ($bindingCount -ne 0 -and -not $historicalBindingComplete -and -not $currentBindingComplete) { Add-ReviewAuthorityError -Errors $errors -Message 'incomplete-group:recovery-target-bindings' } if ($names -contains 'verification_plan_present') { Test-ReviewAuthorityBooleanField -Object $InputObject -Name 'verification_plan_present' -Errors $errors } foreach ($name in @('verification_plan_sha256', 'machinery_paths_sha256', 'excluded_path_patterns_sha256')) { if ($names -contains $name) { Test-ReviewAuthorityStringField -Object $InputObject -Name $name -Errors $errors -MaxLength 128 } } if ($names -contains 'excluded_path_patterns') { $patterns = Get-ReviewAuthorityProperty -Object $InputObject -Name 'excluded_path_patterns' if (($patterns -is [string]) -or ($patterns -isnot [System.Collections.IEnumerable])) { Add-ReviewAuthorityError -Errors $errors -Message 'wrong-type:excluded_path_patterns:array' } else { $patternArray = @($patterns) if ($patternArray.Count -gt 256) { Add-ReviewAuthorityError -Errors $errors -Message 'too-many:excluded_path_patterns:256' } foreach ($pattern in $patternArray) { if ($pattern -isnot [string] -or [string]::IsNullOrWhiteSpace([string]$pattern) -or ([string]$pattern).Length -gt 1024) { Add-ReviewAuthorityError -Errors $errors -Message 'invalid-value:excluded_path_patterns:item' break } } } } if ($names -contains 'machinery_paths') { $paths = Get-ReviewAuthorityProperty -Object $InputObject -Name 'machinery_paths' if (($paths -is [string]) -or ($paths -isnot [System.Collections.IEnumerable])) { Add-ReviewAuthorityError -Errors $errors -Message 'wrong-type:machinery_paths:array' } else { $pathArray = @($paths) if ($pathArray.Count -gt 512) { Add-ReviewAuthorityError -Errors $errors -Message 'too-many:machinery_paths:512' } foreach ($path in $pathArray) { if ($path -isnot [string] -or [string]::IsNullOrWhiteSpace([string]$path) -or ([string]$path).Length -gt 512) { Add-ReviewAuthorityError -Errors $errors -Message 'invalid-value:machinery_paths:item' break } } } } } } if ($fields -contains 'findings') { $names = Get-ReviewAuthorityPropertyNames -Object $InputObject if ($names -notcontains 'findings') { Add-ReviewAuthorityError -Errors $errors -Message 'missing-required:findings' } else { $findings = Get-ReviewAuthorityProperty -Object $InputObject -Name 'findings' if (($findings -is [string]) -or ($findings -isnot [System.Collections.IEnumerable])) { Add-ReviewAuthorityError -Errors $errors -Message 'wrong-type:findings:array' } else { $array = @($findings) if ($array.Count -gt $candidateLimits.max_findings) { Add-ReviewAuthorityError -Errors $errors -Message ("too-many:findings:$($candidateLimits.max_findings)") } $candidateLocalIds = $null if ($ContractName -ceq 'ReviewerCandidate') { $candidateLocalIds = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) } for ($i = 0; $i -lt [Math]::Min($array.Count, $candidateLimits.max_findings); $i++) { Test-ReviewAuthorityFinding -Finding $array[$i] -Kind $(if ($ContractName -ceq 'ReviewerCandidate') { 'candidate' } else { 'terminal' }) -Errors $errors -Index $i if ($null -ne $candidateLocalIds) { $localId = Get-ReviewAuthorityProperty -Object $array[$i] -Name 'local_id' if ($localId -is [string] -and -not [string]::IsNullOrWhiteSpace($localId)) { $isNewLocalId = $candidateLocalIds.Add([string]$localId) if (-not $isNewLocalId) { Add-ReviewAuthorityError -Errors $errors -Message "duplicate-value:findings[$i].local_id" } } } } } } } # Cross-field state invariants: closed fields alone are not enough if their combination is illegal. if ($ContractName -ceq 'ReviewerCandidate') { $completion = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'completion') $verdict = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'verdict') $findingCount = @((Get-ReviewAuthorityProperty -Object $InputObject -Name 'findings')).Count if ($completion -ceq 'partial' -and $verdict -cne 'incomplete') { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:partial-requires-incomplete-verdict' } if ($completion -ceq 'complete' -and $verdict -ceq 'incomplete') { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:complete-cannot-be-incomplete' } if ($verdict -ceq 'pass' -and $findingCount -gt 0) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:pass-cannot-have-findings' } if ($verdict -ceq 'findings' -and $findingCount -eq 0) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:findings-verdict-requires-findings' } } elseif ($ContractName -ceq 'ReviewResult') { $completion = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'completion') $verdict = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'verdict') $runtime = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'runtime_outcome') $termination = Get-ReviewAuthorityProperty -Object $InputObject -Name 'termination_verified' $containment = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'containment') $currentness = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'currentness') $validation = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'validation') $approves = Get-ReviewAuthorityProperty -Object $InputObject -Name 'can_approve_current' if ($runtime -ceq 'timed-out' -and $termination -is [bool] -and -not [bool]$termination) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:timeout-requires-verified-termination' } if ($completion -ceq 'complete' -and ($runtime -cne 'completed' -or $validation -cne 'valid')) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:complete-requires-valid-completed-run' } if ($approves -is [bool] -and [bool]$approves -and ($completion -cne 'complete' -or $verdict -cne 'pass' -or $runtime -cne 'completed' -or -not [bool]$termination -or $containment -cne 'verified' -or $currentness -cne 'current' -or $validation -cne 'valid')) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:approval-prerequisites-not-proven' } } elseif ($ContractName -ceq 'ClaimFact') { $factType = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'fact_type') $disposition = [string](Get-ReviewAuthorityProperty -Object $InputObject -Name 'disposition') if (($factType -ceq 'claim-held' -and $disposition -cne 'held') -or ($factType -ceq 'claim-released' -and $disposition -cne 'released') -or ($factType -ceq 'claim-abandoned' -and $disposition -cne 'abandoned')) { Add-ReviewAuthorityError -Errors $errors -Message 'illegal-state:claim-type-disposition-mismatch' } } $digestIdentityField = if ($ContractName -ceq 'ReviewFinalizationFact') { 'reviewed_digest' } else { 'target_digest' } foreach ($join in @( @{ field = 'campaign_id'; expected = $ExpectedCampaignId }, @{ field = 'run_id'; expected = $ExpectedRunId }, @{ field = $digestIdentityField; expected = $ExpectedTargetDigest } )) { if (-not [string]::IsNullOrWhiteSpace([string]$join.expected) -and [string](Get-ReviewAuthorityProperty -Object $InputObject -Name $join.field) -cne [string]$join.expected) { Add-ReviewAuthorityError -Errors $errors -Message ('identity-mismatch:' + $join.field) } } $category = if ($errors.Count -eq 0) { 'valid' } elseif (@($errors | Where-Object { $_ -like 'identity-mismatch:*' }).Count -gt 0) { 'identity-mismatch' } elseif (@($errors | Where-Object { $_ -like 'unsupported-version:*' }).Count -gt 0) { 'unsupported-version' } elseif (@($errors | Where-Object { $_ -like 'unknown-field:*' -or $_ -like '*.unknown-field:*' }).Count -gt 0) { 'unknown-field' } else { 'schema-invalid' } return [pscustomobject]@{ valid = ($errors.Count -eq 0); category = $category; errors = @($errors) } } function Test-ReviewAuthorityContractJson { [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet( 'ReviewCampaign', 'ReviewRun', 'ReviewInvocation', 'ReviewerCandidate', 'ReviewResult', 'GrantFact', 'ReservationFact', 'SpendFact', 'ReleaseFact', 'ClaimFact', 'HumanDispositionFact', 'RecoveryFact', 'ReviewFinalizationFact' )][string]$ContractName, [Parameter(Mandatory)][AllowEmptyString()][string]$Json, [int]$MaxBytes = $script:ReviewAuthorityCandidateLimits.max_candidate_bytes, [string]$ExpectedCampaignId, [string]$ExpectedRunId, [string]$ExpectedTargetDigest ) if ([System.Text.Encoding]::UTF8.GetByteCount($Json) -gt $MaxBytes) { return [pscustomobject]@{ valid = $false; category = 'payload-too-large'; errors = @("payload-too-large:$MaxBytes") } } $trimmed = $Json.Trim() if (-not $trimmed.StartsWith('{') -or -not $trimmed.EndsWith('}')) { return [pscustomobject]@{ valid = $false; category = 'prose-wrapped-json'; errors = @('prose-wrapped-json') } } try { $document = [System.Text.Json.JsonDocument]::Parse($trimmed) try { if ($document.RootElement.ValueKind -ne [System.Text.Json.JsonValueKind]::Object) { return [pscustomobject]@{ valid = $false; category = 'schema-invalid'; errors = @('wrong-type:$:object') } } $duplicatePaths = [Collections.Generic.List[string]]::new() $pending = [Collections.Generic.Stack[object]]::new() $pending.Push([pscustomobject]@{ element = $document.RootElement; path = '$' }) while ($pending.Count -gt 0) { $node = $pending.Pop() $element = [System.Text.Json.JsonElement]$node.element $path = [string]$node.path if ($element.ValueKind -eq [System.Text.Json.JsonValueKind]::Object) { $seen = [Collections.Generic.HashSet[string]]::new([StringComparer]::Ordinal) foreach ($property in $element.EnumerateObject()) { $childPath = if ($path -ceq '$') { '$.' + $property.Name } else { $path + '.' + $property.Name } if (-not $seen.Add($property.Name)) { $duplicatePaths.Add($childPath) | Out-Null } $pending.Push([pscustomobject]@{ element = $property.Value; path = $childPath }) } } elseif ($element.ValueKind -eq [System.Text.Json.JsonValueKind]::Array) { $index = 0 foreach ($item in $element.EnumerateArray()) { $pending.Push([pscustomobject]@{ element = $item; path = "$path[$index]" }) $index++ } } } if ($duplicatePaths.Count -gt 0) { return [pscustomobject]@{ valid = $false; category = 'duplicate-field'; errors = @($duplicatePaths | ForEach-Object { 'duplicate-field:' + $_ }) } } } finally { $document.Dispose() } } catch [System.Text.Json.JsonException] { return [pscustomobject]@{ valid = $false; category = 'invalid-json'; errors = @('invalid-json') } } try { $object = $trimmed | ConvertFrom-Json -Depth 20 -ErrorAction Stop } catch { return [pscustomobject]@{ valid = $false; category = 'invalid-json'; errors = @('invalid-json') } } return Test-ReviewAuthorityContractObject -ContractName $ContractName -InputObject $object -ExpectedCampaignId $ExpectedCampaignId -ExpectedRunId $ExpectedRunId -ExpectedTargetDigest $ExpectedTargetDigest } # --- T043: campaign allowance, spend, rerun, and selection policy ------------------------------- function Get-ReviewCampaignAllowanceState { [CmdletBinding()] param( [Parameter(Mandatory)][string]$CampaignId, [object[]]$Grants = @(), [object[]]$Reservations = @(), [object[]]$Spends = @(), [object[]]$Releases = @() ) $errors = [System.Collections.Generic.List[string]]::new() $available = [System.Collections.Generic.List[object]]::new() $active = [System.Collections.Generic.List[object]]::new() $spent = [System.Collections.Generic.List[object]]::new() $seenSlots = @{} $releaseIds = @($Releases | ForEach-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') }) $spendIds = @($Spends | ForEach-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') }) foreach ($spend in @($Spends)) { $validation = Test-ReviewAuthorityContractObject -ContractName SpendFact -InputObject $spend -ExpectedCampaignId $CampaignId if (-not $validation.valid) { foreach ($error in $validation.errors) { Add-ReviewAuthorityError -Errors $errors -Message "spend:$error" } } } foreach ($release in @($Releases)) { $validation = Test-ReviewAuthorityContractObject -ContractName ReleaseFact -InputObject $release -ExpectedCampaignId $CampaignId if (-not $validation.valid) { foreach ($error in $validation.errors) { Add-ReviewAuthorityError -Errors $errors -Message "release:$error" } } } foreach ($reservationId in @($spendIds | Where-Object { $_ -cin $releaseIds })) { Add-ReviewAuthorityError -Errors $errors -Message "reservation-both-spent-and-released:$reservationId" } foreach ($grant in @($Grants)) { $validation = Test-ReviewAuthorityContractObject -ContractName GrantFact -InputObject $grant -ExpectedCampaignId $CampaignId if (-not $validation.valid) { foreach ($error in $validation.errors) { Add-ReviewAuthorityError -Errors $errors -Message "grant:$error" }; continue } $grantId = [string](Get-ReviewAuthorityProperty -Object $grant -Name 'grant_id') $slots = [int](Get-ReviewAuthorityProperty -Object $grant -Name 'slots') for ($slot = 1; $slot -le $slots; $slot++) { $matching = @($Reservations | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'grant_id') -ceq $grantId -and [int](Get-ReviewAuthorityProperty -Object $_ -Name 'slot') -eq $slot }) if ($matching.Count -eq 0) { $available.Add([pscustomobject]@{ grant_id = $grantId; slot = $slot }) | Out-Null; continue } $unreleased = [System.Collections.Generic.List[object]]::new() $spentForSlot = [System.Collections.Generic.List[object]]::new() foreach ($reservation in $matching) { $reservationValidation = Test-ReviewAuthorityContractObject -ContractName ReservationFact -InputObject $reservation -ExpectedCampaignId $CampaignId if (-not $reservationValidation.valid) { foreach ($error in $reservationValidation.errors) { Add-ReviewAuthorityError -Errors $errors -Message "reservation:$error" }; continue } $reservationId = [string](Get-ReviewAuthorityProperty -Object $reservation -Name 'reservation_id') if ($spendIds -ccontains $reservationId) { $spentForSlot.Add($reservation) | Out-Null } elseif ($releaseIds -cnotcontains $reservationId) { $unreleased.Add($reservation) | Out-Null } } if ($spentForSlot.Count -gt 1 -or $unreleased.Count -gt 1 -or ($spentForSlot.Count -gt 0 -and $unreleased.Count -gt 0)) { Add-ReviewAuthorityError -Errors $errors -Message ('overlapping-reservation-slot:{0}:{1}' -f $grantId, $slot) } elseif ($spentForSlot.Count -eq 1) { $spent.Add($spentForSlot[0]) | Out-Null } elseif ($unreleased.Count -eq 1) { $active.Add($unreleased[0]) | Out-Null } else { $available.Add([pscustomobject]@{ grant_id = $grantId; slot = $slot }) | Out-Null } $seenSlots["$grantId/$slot"] = $true } } foreach ($reservation in @($Reservations)) { $key = '{0}/{1}' -f [string](Get-ReviewAuthorityProperty -Object $reservation -Name 'grant_id'), [int](Get-ReviewAuthorityProperty -Object $reservation -Name 'slot') if (-not $seenSlots.ContainsKey($key)) { Add-ReviewAuthorityError -Errors $errors -Message "reservation-without-grant-slot:$key" } } return [pscustomobject]@{ valid = ($errors.Count -eq 0); errors = @($errors); available = @($available) active = @($active); spent = @($spent); granted_slots = (@($available).Count + @($active).Count + @($spent).Count) } } function Resolve-ReviewCampaignReservationDecision { [CmdletBinding()] param( [Parameter(Mandatory)][string]$CampaignId, [Parameter(Mandatory)][string]$RunId, [Parameter(Mandatory)][string]$ReservationId, [Parameter(Mandatory)][string]$ObservedAt, [object[]]$Grants = @(), [object[]]$Reservations = @(), [object[]]$Spends = @(), [object[]]$Releases = @() ) if (-not (Test-ReviewAuthorityIdentifier -Value $RunId -Kind run) -or -not (Test-ReviewAuthorityIdentifier -Value $ReservationId -Kind reservation)) { return [pscustomobject]@{ permitted = $false; reason = 'invalid-reservation-identity'; fact = $null } } if (@($Reservations | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'run_id') -ceq $RunId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'run-already-reserved'; fact = $null } } if (@($Reservations | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') -ceq $ReservationId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'reservation-id-already-used'; fact = $null } } $state = Get-ReviewCampaignAllowanceState -CampaignId $CampaignId -Grants $Grants -Reservations $Reservations -Spends $Spends -Releases $Releases if (-not $state.valid) { return [pscustomobject]@{ permitted = $false; reason = 'allowance-corrupt'; fact = $null; errors = $state.errors } } if ($state.available.Count -eq 0) { return [pscustomobject]@{ permitted = $false; reason = 'allowance-exhausted'; fact = $null } } $slot = @($state.available | Sort-Object grant_id, slot)[0] $fact = [pscustomobject][ordered]@{ schema_version = '1.0'; fact_type = 'reservation'; campaign_id = $CampaignId reservation_id = $ReservationId; grant_id = [string]$slot.grant_id; slot = [int]$slot.slot run_id = $RunId; observed_at = $ObservedAt } return [pscustomobject]@{ permitted = $true; reason = 'slot-available'; fact = $fact } } function Resolve-ReviewCampaignSpendDecision { [CmdletBinding()] param( [Parameter(Mandatory)]$Reservation, [Parameter(Mandatory)][string]$InvocationStartedAt, [hashtable]$Preflight, [object[]]$Spends = @(), [object[]]$Releases = @() ) $reservationValidation = Test-ReviewAuthorityContractObject -ContractName ReservationFact -InputObject $Reservation if (-not $reservationValidation.valid) { return [pscustomobject]@{ permitted = $false; reason = 'invalid-reservation'; fact = $null; errors = $reservationValidation.errors } } $reservationId = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'reservation_id') $failedChecks = @() foreach ($name in @('target', 'store', 'contract', 'containment', 'verification', 'harness', 'runtime')) { if ($null -eq $Preflight -or -not $Preflight.ContainsKey($name) -or -not [bool]$Preflight[$name]) { $failedChecks += $name } } if ($failedChecks.Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = ('preflight-failed:' + ($failedChecks -join ',')); fact = $null } } if (@($Releases | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') -ceq $reservationId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'reservation-released'; fact = $null } } if (@($Spends | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') -ceq $reservationId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'reservation-already-spent'; fact = $null } } $fact = [pscustomobject][ordered]@{ schema_version = '1.0'; fact_type = 'spend' campaign_id = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'campaign_id') reservation_id = $reservationId; run_id = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'run_id') invocation_started_at = $InvocationStartedAt } return [pscustomobject]@{ permitted = $true; reason = 'preflight-passed-invocation-spends-slot'; fact = $fact } } function Resolve-ReviewCampaignReleaseDecision { [CmdletBinding()] param( [Parameter(Mandatory)]$Reservation, [Parameter(Mandatory)][string]$Reason, [Parameter(Mandatory)][string]$ObservedAt, [object[]]$Spends = @(), [object[]]$Releases = @() ) $reservationValidation = Test-ReviewAuthorityContractObject -ContractName ReservationFact -InputObject $Reservation if (-not $reservationValidation.valid) { return [pscustomobject]@{ permitted = $false; reason = 'invalid-reservation'; fact = $null; errors = $reservationValidation.errors } } $reservationId = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'reservation_id') if (@($Spends | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') -ceq $reservationId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'invoked-slot-remains-spent'; fact = $null } } if (@($Releases | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'reservation_id') -ceq $reservationId }).Count -gt 0) { return [pscustomobject]@{ permitted = $false; reason = 'reservation-already-released'; fact = $null } } $fact = [pscustomobject][ordered]@{ schema_version = '1.0'; fact_type = 'release' campaign_id = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'campaign_id') reservation_id = $reservationId; run_id = [string](Get-ReviewAuthorityProperty -Object $Reservation -Name 'run_id') reason = ConvertTo-ReviewAuthorityBoundedText -Value $Reason -MaximumLength 512 observed_at = $ObservedAt } return [pscustomobject]@{ permitted = $true; reason = 'proven-pre-invocation-release'; fact = $fact } } function Resolve-ReviewRerunDecision { [CmdletBinding()] param( [Parameter(Mandatory)]$PriorResult, [Parameter(Mandatory)][string]$ProposedRunId, [string[]]$ExistingRunIds = @(), [Parameter(Mandatory)][bool]$HasAvailableSlot ) $complete = [string](Get-ReviewAuthorityProperty -Object $PriorResult -Name 'completion') -ceq 'complete' $current = [string](Get-ReviewAuthorityProperty -Object $PriorResult -Name 'currentness') -ceq 'current' $valid = [string](Get-ReviewAuthorityProperty -Object $PriorResult -Name 'validation') -ceq 'valid' if ($complete -and $current -and $valid) { return [pscustomobject]@{ required = $false; launch = $false; action = 'none'; reason = 'complete-current-result' } } $priorRunId = [string](Get-ReviewAuthorityProperty -Object $PriorResult -Name 'run_id') if (-not (Test-ReviewAuthorityIdentifier -Value $ProposedRunId -Kind run) -or $ProposedRunId -ceq $priorRunId -or $ProposedRunId -cin @($ExistingRunIds)) { return [pscustomobject]@{ required = $true; launch = $false; action = 'reject'; reason = 'rerun-requires-new-run-id' } } if ($HasAvailableSlot) { return [pscustomobject]@{ required = $true; launch = $true; action = 'launch-visible-rerun'; reason = 'authorized-slot-available' } } return [pscustomobject]@{ required = $true; launch = $false; action = 'request-human-grant'; reason = 'allowance-exhausted' } } function Resolve-ReviewCampaignSelectedResult { [CmdletBinding()] param( [Parameter(Mandatory)][string]$TargetDigest, [Parameter(Mandatory)][string[]]$OrderedRunIds, [object[]]$Results = @() ) $eligible = @{} foreach ($result in @($Results)) { $runId = [string](Get-ReviewAuthorityProperty -Object $result -Name 'run_id') if ([string](Get-ReviewAuthorityProperty -Object $result -Name 'target_digest') -ceq $TargetDigest -and [string](Get-ReviewAuthorityProperty -Object $result -Name 'completion') -ceq 'complete' -and [string](Get-ReviewAuthorityProperty -Object $result -Name 'currentness') -ceq 'current' -and [string](Get-ReviewAuthorityProperty -Object $result -Name 'validation') -ceq 'valid') { if ($eligible.ContainsKey($runId)) { return [pscustomobject]@{ valid = $false; selected_run_id = $null; reason = 'duplicate-terminal-result-for-run' } } $eligible[$runId] = $result } } $selected = $null foreach ($runId in @($OrderedRunIds)) { if ($eligible.ContainsKey($runId)) { $selected = $runId } } return [pscustomobject]@{ valid = $true; selected_run_id = $selected; reason = $(if ($null -eq $selected) { 'no-applicable-result' } else { 'latest-ordered-applicable-result' }) } } function Test-ReviewCampaignDuplicateCombination { param( [Parameter(Mandatory)][string]$TargetDigest, [Parameter(Mandatory)][string]$HarnessId, [Parameter(Mandatory)][string]$ContractVersion, [object[]]$Runs = @() ) $matches = @($Runs | Where-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'target_digest') -ceq $TargetDigest -and [string](Get-ReviewAuthorityProperty -Object $_ -Name 'harness_id') -ceq $HarnessId -and [string](Get-ReviewAuthorityProperty -Object $_ -Name 'schema_version') -ceq $ContractVersion }) return [pscustomobject]@{ duplicate = ($matches.Count -gt 0); prior_run_ids = @($matches | ForEach-Object { [string](Get-ReviewAuthorityProperty -Object $_ -Name 'run_id') }) } } # --- T044: one-invocation run, acceptance/currentness, and finding lineage ---------------------- function Resolve-ReviewRunTransition { [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet('requested', 'reserved', 'preflighted', 'claimed', 'invoked', 'validating', 'terminal')][string]$CurrentState, [Parameter(Mandatory)][ValidateSet('reserve', 'preflight-pass', 'claim', 'invoke', 'candidate-ready', 'close-pre-invocation', 'close-post-invocation')][string]$Event, [bool]$TerminalResultExists = $false ) if ($CurrentState -ceq 'terminal' -or $TerminalResultExists) { return [pscustomobject]@{ allowed = $false; next_state = $CurrentState; reason = 'terminal-is-immutable' } } $key = "$CurrentState/$Event" $next = switch ($key) { 'requested/reserve' { 'reserved' } 'reserved/preflight-pass' { 'preflighted' } 'preflighted/claim' { 'claimed' } 'claimed/invoke' { 'invoked' } 'invoked/candidate-ready' { 'validating' } 'reserved/close-pre-invocation' { 'terminal' } 'preflighted/close-pre-invocation' { 'terminal' } 'claimed/close-pre-invocation' { 'terminal' } 'invoked/close-post-invocation' { 'terminal' } 'validating/close-post-invocation' { 'terminal' } default { $null } } if ($null -eq $next) { return [pscustomobject]@{ allowed = $false; next_state = $CurrentState; reason = 'illegal-run-transition' } } return [pscustomobject]@{ allowed = $true; next_state = $next; reason = 'legal-run-transition' } } function Resolve-ReviewCurrentness { [CmdletBinding()] param( [Parameter(Mandatory)][AllowEmptyString()][string]$ReviewedDigest, [Parameter(Mandatory)][AllowEmptyString()][string]$CurrentDigest, [Parameter(Mandatory)][AllowEmptyString()][string]$OriginHeadBefore, [Parameter(Mandatory)][AllowEmptyString()][string]$OriginHeadAfter ) if ([string]::IsNullOrWhiteSpace($ReviewedDigest) -or [string]::IsNullOrWhiteSpace($CurrentDigest) -or [string]::IsNullOrWhiteSpace($OriginHeadBefore) -or [string]::IsNullOrWhiteSpace($OriginHeadAfter)) { return [pscustomobject]@{ classification = 'unknown'; exact = $false; reason = 'currentness-evidence-incomplete' } } if ($OriginHeadBefore -cne $OriginHeadAfter -or $ReviewedDigest -cne $CurrentDigest) { return [pscustomobject]@{ classification = 'snapshot-moved'; exact = $false; reason = 'origin-head-or-reviewed-digest-moved' } } return [pscustomobject]@{ classification = 'current'; exact = $true; reason = 'exact-head-and-digest-match' } } function Resolve-ReviewResultClassification { [CmdletBinding()] param( [Parameter(Mandatory)][ValidateSet('completed', 'preflight-failed', 'claim-contended', 'launch-failed', 'timed-out', 'terminated', 'invalid-output', 'identity-mismatch', 'containment-violated', 'abandoned')][string]$RuntimeOutcome, [Parameter(Mandatory)][bool]$Invoked, [Parameter(Mandatory)][bool]$TerminationVerified, [Parameter(Mandatory)][ValidateSet('verified', 'violated', 'unknown')][string]$Containment, [Parameter(Mandatory)][ValidateSet('current', 'snapshot-moved', 'unknown')][string]$Currentness, [AllowNull()]$Candidate, [Parameter(Mandatory)][bool]$CandidateValid ) if ($RuntimeOutcome -ceq 'timed-out' -and -not $TerminationVerified) { return [pscustomobject]@{ publish_permitted = $false; reason = 'timeout-requires-verified-tree-death'; completion = 'none'; verdict = 'failed'; findings_advisory = $true; can_approve_current = $false; require_complete_rerun = $true } } if ($Invoked -and -not $TerminationVerified) { return [pscustomobject]@{ publish_permitted = $false; reason = 'runtime-terminal-requires-verified-tree-death'; completion = 'none'; verdict = 'failed'; findings_advisory = $true; can_approve_current = $false; require_complete_rerun = $true } } $candidateFindings = @() if ($CandidateValid -and $null -ne $Candidate) { $candidateFindings = @((Get-ReviewAuthorityProperty -Object $Candidate -Name 'findings') | Where-Object { $null -ne $_ }) } $candidateCompletion = if ($CandidateValid -and $null -ne $Candidate) { [string](Get-ReviewAuthorityProperty -Object $Candidate -Name 'completion') } else { 'none' } $candidateVerdict = if ($CandidateValid -and $null -ne $Candidate) { [string](Get-ReviewAuthorityProperty -Object $Candidate -Name 'verdict') } else { 'failed' } $complete = $RuntimeOutcome -ceq 'completed' -and $CandidateValid -and $candidateCompletion -ceq 'complete' -and $Containment -ceq 'verified' -and $TerminationVerified $canApprove = $complete -and $Currentness -ceq 'current' -and $candidateVerdict -ceq 'pass' -and $TerminationVerified $advisory = (-not $complete) -or $Currentness -cne 'current' -or $Containment -cne 'verified' $completion = if ($complete) { 'complete' } elseif ($candidateFindings.Count -gt 0 -or $candidateCompletion -ceq 'partial') { 'partial' } else { 'none' } $verdict = if ($complete) { $candidateVerdict } elseif ($Invoked) { 'incomplete' } else { 'failed' } $reason = if (-not $Invoked) { $RuntimeOutcome } elseif (-not $CandidateValid) { 'candidate-invalid' } elseif ($Containment -ceq 'violated') { 'containment-violated' } elseif ($Currentness -ceq 'snapshot-moved') { 'snapshot-moved' } elseif (-not $complete) { $RuntimeOutcome } else { 'complete-result' } return [pscustomobject]@{ publish_permitted = $true; reason = $reason; completion = $completion; verdict = $verdict findings_advisory = $advisory; can_approve_current = $canApprove require_complete_rerun = (-not $complete -or $Currentness -cne 'current') findings = $candidateFindings } } function Get-ReviewFindingMatchKey { param([Parameter(Mandatory)]$Finding) $parts = foreach ($name in @('location', 'title', 'description')) { $text = [string](Get-ReviewAuthorityProperty -Object $Finding -Name $name) (($text.Trim().ToLowerInvariant()) -replace '\s+', ' ') } $material = $parts -join "`n" $bytes = [System.Text.Encoding]::UTF8.GetBytes($material) return [Convert]::ToHexString([System.Security.Cryptography.SHA256]::HashData($bytes)).ToLowerInvariant() } function Resolve-ReviewFindingLineage { [CmdletBinding()] param( [Parameter(Mandatory)][string]$RunId, [object[]]$CurrentFindings = @(), [object[]]$PriorFindings = @() ) $priorByKey = @{} foreach ($prior in @($PriorFindings)) { $key = Get-ReviewFindingMatchKey -Finding $prior if (-not $priorByKey.ContainsKey($key)) { $priorByKey[$key] = $prior } } $links = [System.Collections.Generic.List[object]]::new() $index = 0 foreach ($current in @($CurrentFindings | Where-Object { $null -ne $_ })) { $index++ $key = Get-ReviewFindingMatchKey -Finding $current $prior = if ($priorByKey.ContainsKey($key)) { $priorByKey[$key] } else { $null } $lineageId = if ($null -ne $prior -and -not [string]::IsNullOrWhiteSpace([string](Get-ReviewAuthorityProperty -Object $prior -Name 'lineage_id'))) { [string](Get-ReviewAuthorityProperty -Object $prior -Name 'lineage_id') } else { 'lin-' + $key.Substring(0, 16) } $findingMaterial = [Text.Encoding]::UTF8.GetBytes(('{0}/{1}/{2}' -f $RunId, $index, $key)) $findingHash = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($findingMaterial)).ToLowerInvariant() $links.Add([pscustomobject]@{ run_id = $RunId; current_local_id = [string](Get-ReviewAuthorityProperty -Object $current -Name 'local_id') finding_id = ('finding-' + $findingHash.Substring(0, 16)) lineage_id = $lineageId; matched_prior_finding_id = $(if ($null -ne $prior) { [string](Get-ReviewAuthorityProperty -Object $prior -Name 'finding_id') } else { $null }) severity = [string](Get-ReviewAuthorityProperty -Object $current -Name 'severity') prior_severity = $(if ($null -ne $prior) { [string](Get-ReviewAuthorityProperty -Object $prior -Name 'severity') } else { $null }) match_key = $key }) | Out-Null } return @($links) } |