scripts/internal/continuous-co-review/inline-review-gate-evaluator.ps1
|
$ErrorActionPreference = 'Stop' Set-StrictMode -Version Latest function ConvertTo-ContinuousCoReviewGateIsoTimestamp { param( [datetime] $Timestamp = [datetime]::UtcNow ) return $Timestamp.ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ssZ', [System.Globalization.CultureInfo]::InvariantCulture) } function Get-ContinuousCoReviewGateProperty { param( [Parameter(Mandatory)] [AllowNull()] $Object, [Parameter(Mandatory)] [string] $Name ) if ($null -eq $Object) { return $null } if (Test-ReviewerContractPropertyExists -Object $Object -Name $Name) { return Get-ReviewerContractPropertyValue -Object $Object -Name $Name } return $null } function New-ContinuousCoReviewGateVerdict { param( [Parameter(Mandatory)] [string] $RunId, [Parameter(Mandatory)] [string] $CheckpointId, [Parameter(Mandatory)] [string] $State, [string[]] $BlockingFindingIds = @(), [string[]] $UnsafeReasons = @(), [int] $RoundCount = 1, [AllowNull()] [string] $EscalationRef = $null, [datetime] $CreatedAt = [datetime]::UtcNow ) $distinctBlockingIds = @($BlockingFindingIds | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique) $distinctUnsafeReasons = @($UnsafeReasons | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -Unique) return [pscustomobject][ordered]@{ schema_version = '1.0' verdict_id = "verdict-$RunId" run_id = $RunId checkpoint_id = $CheckpointId state = $State unresolved_blocking_count = @($distinctBlockingIds).Count blocking_finding_ids = @($distinctBlockingIds) unsafe_reasons = @($distinctUnsafeReasons) round_count = $RoundCount escalation_ref = if ([string]::IsNullOrWhiteSpace([string] $EscalationRef)) { $null } else { $EscalationRef } created_at = ConvertTo-ContinuousCoReviewGateIsoTimestamp -Timestamp $CreatedAt } } function Get-ContinuousCoReviewLatestDispositionByFindingId { param( [AllowNull()] $ReviewThread ) $latest = @{} if ($null -eq $ReviewThread) { return $latest } foreach ($disposition in @($ReviewThread.dispositions)) { $findingId = Get-ContinuousCoReviewGateProperty -Object $disposition -Name 'finding_id' if ([string]::IsNullOrWhiteSpace([string] $findingId)) { continue } $round = Get-ContinuousCoReviewGateProperty -Object $disposition -Name 'review_round' if ($null -eq $round) { $round = 0 } if (-not $latest.ContainsKey($findingId)) { $latest[$findingId] = $disposition continue } $currentRound = Get-ContinuousCoReviewGateProperty -Object $latest[$findingId] -Name 'review_round' if ($null -eq $currentRound) { $currentRound = 0 } if ([int] $round -ge [int] $currentRound) { $latest[$findingId] = $disposition } } return $latest } function Get-ContinuousCoReviewRoundCount { param( [AllowNull()] $ReviewThread ) if ($null -eq $ReviewThread) { return 1 } $maxRound = 0 foreach ($disposition in @($ReviewThread.dispositions)) { $round = Get-ContinuousCoReviewGateProperty -Object $disposition -Name 'review_round' if ($null -ne $round -and [int] $round -gt $maxRound) { $maxRound = [int] $round } } return ($maxRound + 1) } function Get-ContinuousCoReviewFindingEffectiveState { param( [Parameter(Mandatory)] $Finding, [AllowNull()] $LatestDisposition ) $resolution = Get-ContinuousCoReviewGateProperty -Object $Finding -Name 'resolution' $dispositionState = Get-ContinuousCoReviewGateProperty -Object $Finding -Name 'disposition' $resolutionState = Get-ContinuousCoReviewGateProperty -Object $resolution -Name 'state' $rationale = Get-ContinuousCoReviewGateProperty -Object $resolution -Name 'rationale' $fixEvidenceRef = Get-ContinuousCoReviewGateProperty -Object $resolution -Name 'fix_evidence_ref' if ($null -ne $LatestDisposition) { $latestState = Get-ContinuousCoReviewGateProperty -Object $LatestDisposition -Name 'state' if (-not [string]::IsNullOrWhiteSpace([string] $latestState)) { $dispositionState = $latestState } $latestRationale = Get-ContinuousCoReviewGateProperty -Object $LatestDisposition -Name 'rationale' if (-not [string]::IsNullOrWhiteSpace([string] $latestRationale)) { $rationale = $latestRationale } $latestFixEvidence = Get-ContinuousCoReviewGateProperty -Object $LatestDisposition -Name 'fix_evidence_ref' if (-not [string]::IsNullOrWhiteSpace([string] $latestFixEvidence)) { $fixEvidenceRef = $latestFixEvidence } } return [pscustomobject][ordered]@{ disposition_state = $dispositionState resolution_state = $resolutionState rationale = $rationale fix_evidence_ref = $fixEvidenceRef } } function Get-ContinuousCoReviewUnresolvedBlockingFindingInfo { param( [AllowNull()] $FindingsResult, [AllowNull()] $ReviewThread, [System.Collections.Generic.List[string]] $UnsafeReasons ) $latestDispositions = Get-ContinuousCoReviewLatestDispositionByFindingId -ReviewThread $ReviewThread $unresolved = [System.Collections.Generic.List[object]]::new() if ($null -eq $FindingsResult) { $UnsafeReasons.Add('missing-findings-result') return @($unresolved) } foreach ($finding in @($FindingsResult.findings)) { $findingId = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'finding_id' $severity = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'severity' if ($severity -ne 'blocking') { continue } $latestDisposition = $null if ($latestDispositions.ContainsKey($findingId)) { $latestDisposition = $latestDispositions[$findingId] } $effectiveState = Get-ContinuousCoReviewFindingEffectiveState -Finding $finding -LatestDisposition $latestDisposition $originalDispositionState = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'disposition' if (-not (Test-ReviewerFindingDispositionValue -Disposition ([string] $originalDispositionState))) { $UnsafeReasons.Add('unknown-blocking-disposition') continue } if (-not (Test-ReviewerFindingDispositionValue -Disposition ([string] $effectiveState.disposition_state))) { $UnsafeReasons.Add('unknown-blocking-disposition') continue } if (-not (Test-ReviewerFindingResolutionStateValue -State ([string] $effectiveState.resolution_state))) { $UnsafeReasons.Add('unknown-blocking-resolution-state') continue } if ($effectiveState.disposition_state -eq 'resolved') { if ($effectiveState.resolution_state -ne 'resolved') { $UnsafeReasons.Add('malformed-durable-state') continue } if ([string]::IsNullOrWhiteSpace([string] $effectiveState.fix_evidence_ref) -and [string]::IsNullOrWhiteSpace([string] $effectiveState.rationale)) { $UnsafeReasons.Add('missing-blocking-resolution-evidence') } continue } if ($effectiveState.disposition_state -eq 'rejected_with_rationale') { if ([string]::IsNullOrWhiteSpace([string] $effectiveState.rationale)) { $UnsafeReasons.Add('missing-rejection-rationale') } if ($effectiveState.resolution_state -eq 'unresolved') { $unresolved.Add([pscustomobject][ordered]@{ finding_id = $findingId fingerprint = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'fingerprint' source_run_id = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'source_run_id' }) } continue } if ($effectiveState.disposition_state -eq 'escalated_to_human' -or $effectiveState.resolution_state -eq 'escalated') { continue } if (($effectiveState.disposition_state -eq 'open' -or $effectiveState.disposition_state -eq 'accepted_fix_pending') -and $effectiveState.resolution_state -eq 'unresolved') { $unresolved.Add([pscustomobject][ordered]@{ finding_id = $findingId fingerprint = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'fingerprint' source_run_id = Get-ContinuousCoReviewGateProperty -Object $finding -Name 'source_run_id' }) } } return @($unresolved) } function Test-ContinuousCoReviewSameBlockingFindingPreviouslyUnresolved { param( [Parameter(Mandatory)] $CurrentBlockingInfo, [AllowNull()] $PriorFindingsResult ) if ($null -eq $PriorFindingsResult) { return $false } foreach ($priorFinding in @($PriorFindingsResult.findings)) { if ((Get-ContinuousCoReviewGateProperty -Object $priorFinding -Name 'severity') -ne 'blocking') { continue } $priorResolution = Get-ContinuousCoReviewGateProperty -Object $priorFinding -Name 'resolution' $priorResolutionState = Get-ContinuousCoReviewGateProperty -Object $priorResolution -Name 'state' if ($priorResolutionState -ne 'unresolved') { continue } $priorFindingId = Get-ContinuousCoReviewGateProperty -Object $priorFinding -Name 'finding_id' $priorFingerprint = Get-ContinuousCoReviewGateProperty -Object $priorFinding -Name 'fingerprint' $priorRunId = Get-ContinuousCoReviewGateProperty -Object $priorFinding -Name 'source_run_id' if ($CurrentBlockingInfo.finding_id -eq $priorFindingId) { return $true } if (-not [string]::IsNullOrWhiteSpace([string] $CurrentBlockingInfo.fingerprint) -and $CurrentBlockingInfo.fingerprint -eq $priorFingerprint) { return $true } if (-not [string]::IsNullOrWhiteSpace([string] $CurrentBlockingInfo.source_run_id) -and $CurrentBlockingInfo.source_run_id -eq $PriorFindingsResult.run_id) { return $true } if (-not [string]::IsNullOrWhiteSpace([string] $CurrentBlockingInfo.source_run_id) -and $CurrentBlockingInfo.source_run_id -eq $priorRunId) { return $true } } return $false } function Invoke-ContinuousCoReviewInlineGateEvaluator { param( [Parameter(Mandatory)] [string] $RunId, [Parameter(Mandatory)] [string] $CheckpointId, [AllowNull()] $FindingsResult, [AllowNull()] $ReviewThread, [AllowNull()] $SkippedRun, [AllowNull()] $PriorFindingsResult, [int] $MaxReviewRounds = 2, [string] $SchemaRoot, [datetime] $CreatedAt = [datetime]::UtcNow ) if ($null -ne $SkippedRun) { $skipUnsafeReasons = [System.Collections.Generic.List[string]]::new() if ((Get-ContinuousCoReviewGateProperty -Object $SkippedRun -Name 'run_id') -ne $RunId) { $skipUnsafeReasons.Add('malformed-durable-state') } if ((Get-ContinuousCoReviewGateProperty -Object $SkippedRun -Name 'checkpoint_id') -ne $CheckpointId) { $skipUnsafeReasons.Add('malformed-durable-state') } if ($skipUnsafeReasons.Count -gt 0) { return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'unsafe' -UnsafeReasons @($skipUnsafeReasons) -RoundCount 0 -CreatedAt $CreatedAt } return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'skipped' -RoundCount 0 -CreatedAt $CreatedAt } $unsafeReasons = [System.Collections.Generic.List[string]]::new() if ($null -eq $FindingsResult) { $unsafeReasons.Add('missing-findings-result') } elseif ($SchemaRoot) { $findingsValidation = Test-ReviewerContractObject -ContractName 'FindingsResult' -SchemaRoot $SchemaRoot -InputObject $FindingsResult if (-not $findingsValidation.Valid) { $unsafeReasons.Add('invalid-findings-schema') } } if ($null -eq $ReviewThread) { $unsafeReasons.Add('missing-review-thread') } elseif ($SchemaRoot) { $threadValidation = Test-ReviewerContractObject -ContractName 'ReviewThread' -SchemaRoot $SchemaRoot -InputObject $ReviewThread if (-not $threadValidation.Valid) { $unsafeReasons.Add('malformed-durable-state') } } if ($null -ne $FindingsResult -and (Get-ContinuousCoReviewGateProperty -Object $FindingsResult -Name 'run_id') -ne $RunId) { $unsafeReasons.Add('malformed-durable-state') } if ($null -ne $ReviewThread -and (Get-ContinuousCoReviewGateProperty -Object $ReviewThread -Name 'run_id') -ne $RunId) { $unsafeReasons.Add('malformed-durable-state') } if ($null -ne $ReviewThread -and (Get-ContinuousCoReviewGateProperty -Object $ReviewThread -Name 'checkpoint_id') -ne $CheckpointId) { $unsafeReasons.Add('malformed-durable-state') } $roundCount = Get-ContinuousCoReviewRoundCount -ReviewThread $ReviewThread $unresolvedBlocking = Get-ContinuousCoReviewUnresolvedBlockingFindingInfo -FindingsResult $FindingsResult -ReviewThread $ReviewThread -UnsafeReasons $unsafeReasons $blockingIds = @($unresolvedBlocking | ForEach-Object { $_.finding_id }) if ($unsafeReasons.Count -gt 0) { return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'unsafe' -BlockingFindingIds $blockingIds -UnsafeReasons @($unsafeReasons) -RoundCount $roundCount -CreatedAt $CreatedAt } if (@($unresolvedBlocking).Count -eq 0) { return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'pass' -RoundCount $roundCount -CreatedAt $CreatedAt } $samePriorBlocking = @( foreach ($blockingInfo in @($unresolvedBlocking)) { if (Test-ContinuousCoReviewSameBlockingFindingPreviouslyUnresolved -CurrentBlockingInfo $blockingInfo -PriorFindingsResult $PriorFindingsResult) { $blockingInfo.finding_id } } ) if (($roundCount -ge $MaxReviewRounds) -and (@($samePriorBlocking).Count -gt 0)) { $escalationRef = "human-escalation:${RunId}:$($samePriorBlocking -join ',')" return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'escalated' -BlockingFindingIds $blockingIds -RoundCount $roundCount -EscalationRef $escalationRef -CreatedAt $CreatedAt } return New-ContinuousCoReviewGateVerdict -RunId $RunId -CheckpointId $CheckpointId -State 'blocked' -BlockingFindingIds $blockingIds -RoundCount $roundCount -CreatedAt $CreatedAt } |