Payload/scripts/kaden/harness/quality-gate/RepositoryQualityGate.psm1
|
#Requires -Version 5.1 Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $script:EvidenceFreshnessLimit = [timespan]::FromHours(24) $script:ProtectedDestination = 'main' $script:ProtectedIntegrationKinds = @('direct-update', 'merge') $script:EmergencyMaintainerRole = 'repository-maintainer' $script:EmergencyScope = 'local-advisory' $script:RepositoryCheckCatalog = @( @{ Id = 'comprehensive-pester' RulePrefix = 'pester-runner-v1' FailureReason = 'full-check-failed' OutcomeKind = 'counter' CounterName = 'FailedCount' MissingCounterInput = 'failed-count' } @{ Id = 'pester-test-quality' RulePrefix = 'pester-test-quality-v1' FailureReason = 'test-quality-semgrep-failed' OutcomeKind = 'counter' CounterName = 'BlockingFindingCount' MissingCounterInput = 'finding-count' FindingCountName = 'BlockingFindingCount' } @{ Id = 'powershell-security' RulePrefix = 'powershell-security.semgrep.yml' FailureReason = 'security-semgrep-failed' OutcomeKind = 'counter' CounterName = 'ErrorFindingCount' MissingCounterInput = 'error-finding-count' } @{ Id = 'trivy-secrets' RulePrefix = 'trivy-secrets-v1' FailureReason = 'secret-check-failed' OutcomeKind = 'counter' CounterName = 'SecretFindingCount' MissingCounterInput = 'secret-finding-count' } @{ Id = 'powershell-quality' RulePrefix = 'PSScriptAnalyzerSettings.psd1' FailureReason = 'PowerShell-quality-failed' OutcomeKind = 'quality' } @{ Id = 'agent-skill-behavior' RulePrefix = 'deterministic-eval-runner-v1' FailureReason = 'behavioral-coverage-gap' OutcomeKind = 'behavioral' ApplicableWhenTargetNonEmpty = $true } @{ Id = 'ai-surface-sync' RulePrefix = 'ai-surface-pair-v1' FailureReason = 'AI-path-unclassified' OutcomeKind = 'surface' ApplicableWhenTargetNonEmpty = $true } ) function Get-QualityEvidenceEntry { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) if ($null -eq $Evidence) { return [pscustomobject]@{ Present = $false; Value = $null } } if (-not ($Evidence -is [System.Collections.IDictionary])) { return [pscustomobject]@{ Present = $false; Value = $null } } if (-not $Evidence.Contains($Name)) { return [pscustomobject]@{ Present = $false; Value = $null } } return [pscustomobject]@{ Present = $true; Value = $Evidence[$Name] } } function Get-RepositoryCheckDefinition { param( [Parameter(Mandatory = $true)] [string]$Check ) foreach ($row in @($script:RepositoryCheckCatalog)) { if ([string]::Equals([string]$row.Id, $Check, [System.StringComparison]::Ordinal)) { return $row } } return $null } function Get-QualityCount { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name if (-not $entry.Present) { return $null } return [int]$entry.Value } function Get-QualityFlag { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name if (-not $entry.Present) { return $false } return [bool]$entry.Value } function Test-SuccessfulComprehensiveResult { param($Evidence) $count = Get-QualityCount -Evidence $Evidence -Name 'FailedCount' if ($null -eq $count) { return $false } return ($count -eq 0) } function Get-QualityText { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name if (-not $entry.Present -or $null -eq $entry.Value) { return $null } return [string]$entry.Value } function Get-QualityTime { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name if (-not $entry.Present) { return $null } $value = $entry.Value if ($null -eq $value) { return $null } if ($value -is [datetime]) { return [datetime]$value } try { return [datetime]$value } catch { return $null } } function Get-QualityList { param( $Evidence, [Parameter(Mandatory = $true)] [string]$Name ) $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name $Name if (-not $entry.Present -or $null -eq $entry.Value) { return $null } return @($entry.Value) } function ConvertTo-GatePath { param([string]$Path) if ([string]::IsNullOrWhiteSpace($Path)) { return '' } $text = $Path.Replace('\', '/') while ($text.StartsWith('./')) { $text = $text.Substring(2) } return $text } function Test-SameGatePath { param( [string]$Left, [string]$Right ) $leftPath = ConvertTo-GatePath $Left $rightPath = ConvertTo-GatePath $Right return [string]::Equals($leftPath, $rightPath, [System.StringComparison]::OrdinalIgnoreCase) } function Test-SameTargetSet { param($Left, $Right) if ($null -eq $Left -or $null -eq $Right) { return $false } $leftItems = @($Left | ForEach-Object { ConvertTo-GatePath ([string]$_) }) $rightItems = @($Right | ForEach-Object { ConvertTo-GatePath ([string]$_) }) if ($leftItems.Count -ne $rightItems.Count) { return $false } $remaining = New-Object System.Collections.Generic.List[string] foreach ($item in $rightItems) { [void]$remaining.Add($item) } foreach ($item in $leftItems) { $found = -1 for ($index = 0; $index -lt $remaining.Count; $index++) { if (Test-SameGatePath -Left $remaining[$index] -Right $item) { $found = $index break } } if ($found -lt 0) { return $false } $remaining.RemoveAt($found) } return ($remaining.Count -eq 0) } function Test-PrefixedGatePath { param( [string]$Path, [string[]]$Prefixes ) $text = ConvertTo-GatePath $Path foreach ($prefix in $Prefixes) { $root = $prefix.TrimEnd('/') if (Test-SameGatePath -Left $text -Right $root) { return $true } if ($text.StartsWith($prefix, [System.StringComparison]::OrdinalIgnoreCase)) { return $true } } return $false } function Test-PowerShellScriptPath { param([string]$Path) $extension = [System.IO.Path]::GetExtension((ConvertTo-GatePath $Path)) if ([string]::IsNullOrWhiteSpace($extension)) { return $false } return @('.ps1', '.psm1', '.psd1') -contains $extension.ToLowerInvariant() } function Test-PathInTargetSet { param( $TargetSet, [string]$Path ) $needle = ConvertTo-GatePath $Path foreach ($target in @($TargetSet)) { $candidate = ConvertTo-GatePath ([string]$target) if (Test-SameGatePath -Left $candidate -Right $needle) { return $true } } return $false } function Test-PesterInvalidatingChange { param( $ChangedPaths, $CurrentTargetSet ) $contractPrefixes = @( 'tests/harness/', 'scripts/', 'templates/', '.github/', '.cursor/', '.husky/', '.devcontainer/', 'docs/kaden/' ) $documentationPrefixes = @( 'docs/brand/', 'docs/retros/', 'docs/standards/' ) foreach ($path in @($ChangedPaths)) { $text = [string]$path if ([string]::IsNullOrWhiteSpace($text)) { continue } $scriptChange = Test-PowerShellScriptPath $text $contractChange = Test-PrefixedGatePath -Path $text -Prefixes $contractPrefixes $documentationChange = Test-PrefixedGatePath -Path $text -Prefixes $documentationPrefixes $insideTargetSet = Test-PathInTargetSet -TargetSet $CurrentTargetSet -Path $text if ($scriptChange -or $contractChange) { return $true } if ($documentationChange -and $insideTargetSet) { return $true } } return $false } function Test-DeclaredRuleIdentity { param( [string]$Check, [string]$RuleIdentity ) $definition = Get-RepositoryCheckDefinition -Check $Check if ($null -eq $definition) { return $false } if ([string]::IsNullOrWhiteSpace($RuleIdentity)) { return $false } return $RuleIdentity.StartsWith([string]$definition.RulePrefix, [System.StringComparison]::Ordinal) } function Get-CheckFailureReason { param([string]$Check) $definition = Get-RepositoryCheckDefinition -Check $Check if ($null -eq $definition) { return 'required-check-missing' } return [string]$definition.FailureReason } function Get-EvidenceFreshness { param( [datetime]$CompletedAt, [datetime]$RequestedAt ) $age = $RequestedAt - $CompletedAt $limit = $script:EvidenceFreshnessLimit if ($age -lt [timespan]::Zero) { return 'unknown' } if ($age -gt $limit) { return 'stale' } return 'fresh' } function Get-RejectionValue { param( [hashtable]$Rejection, [string]$Name ) if ($null -eq $Rejection -or -not $Rejection.Contains($Name)) { return $null } return $Rejection[$Name] } function Get-RejectedCheckResult { param( [Parameter(Mandatory = $true)] [string]$Check, [hashtable]$Rejection ) $reasons = @() $reasonValue = Get-RejectionValue -Rejection $Rejection -Name 'Reasons' if ($null -ne $reasonValue) { $reasons = @($reasonValue) } return [pscustomobject]@{ Check = $Check CheckAccepted = $false EvidenceClass = 'failed-evidence' Freshness = Get-RejectionValue -Rejection $Rejection -Name 'Freshness' ProvenanceRevision = $null MissingInput = Get-RejectionValue -Rejection $Rejection -Name 'MissingInput' RequiredRun = Get-RejectionValue -Rejection $Rejection -Name 'RequiredRun' FailedCheck = $Check AffectedTarget = Get-RejectionValue -Rejection $Rejection -Name 'AffectedTarget' Condition = Get-RejectionValue -Rejection $Rejection -Name 'Condition' Reasons = $reasons TargetCount = $null FindingCount = $null ErrorFindingCount = $null SecretFindingCount = $null FixtureContents = $null CapturedOutput = $null DeterministicScore = $null CompletedAt = $null BehaviorClassification = $null BehaviorTargetSet = $null } } function Get-AcceptedCheckResult { param( [Parameter(Mandatory = $true)] [string]$Check, [Parameter(Mandatory = $true)] [string]$EvidenceClass, [Parameter(Mandatory = $true)] [string]$ProvenanceRevision, $Evidence ) $findingCount = $null $definition = Get-RepositoryCheckDefinition -Check $Check if (($null -ne $definition) -and $definition.Contains('FindingCountName')) { $findingCount = Get-QualityCount -Evidence $Evidence -Name ([string]$definition.FindingCountName) } $behaviorTargetSet = $null $fixtureContents = $null $capturedOutput = $null $deterministicScore = $null $completedAt = $null $behaviorClassification = $null if ($null -ne $definition) { $kind = [string]$definition.OutcomeKind if (($kind -eq 'behavioral') -or ($kind -eq 'surface')) { $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' if ($null -eq $targets) { $behaviorTargetSet = @() } else { $behaviorTargetSet = @($targets) } $behaviorClassification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification' $completedAt = Get-QualityTime -Evidence $Evidence -Name 'CompletedAt' } if ($kind -eq 'behavioral') { $fixtures = Get-QualityList -Evidence $Evidence -Name 'FixtureContents' if ($null -eq $fixtures) { $fixtureContents = @() } else { $fixtureContents = @($fixtures) } $capturedOutput = Get-QualityText -Evidence $Evidence -Name 'CapturedOutput' $deterministicScore = Get-QualityText -Evidence $Evidence -Name 'DeterministicScore' } } return [pscustomobject]@{ Check = $Check CheckAccepted = $true EvidenceClass = $EvidenceClass Freshness = 'fresh' ProvenanceRevision = $ProvenanceRevision MissingInput = $null RequiredRun = $null FailedCheck = $null AffectedTarget = $null Condition = $null Reasons = @() TargetCount = Get-QualityCount -Evidence $Evidence -Name 'TargetCount' FindingCount = $findingCount ErrorFindingCount = Get-QualityCount -Evidence $Evidence -Name 'ErrorFindingCount' SecretFindingCount = Get-QualityCount -Evidence $Evidence -Name 'SecretFindingCount' FixtureContents = $fixtureContents CapturedOutput = $capturedOutput DeterministicScore = $deterministicScore CompletedAt = $completedAt BehaviorClassification = $behaviorClassification BehaviorTargetSet = $behaviorTargetSet } } function Copy-CheckOntoDecision { param( $Decision, $Result ) $Decision.CheckAccepted = $Result.CheckAccepted $Decision.EvidenceClass = $Result.EvidenceClass $Decision.Freshness = $Result.Freshness $Decision.ProvenanceRevision = $Result.ProvenanceRevision $Decision.MissingInput = $Result.MissingInput $Decision.RequiredRun = $Result.RequiredRun $Decision.FailedCheck = $Result.FailedCheck $Decision.AffectedTarget = $Result.AffectedTarget $Decision.Condition = $Result.Condition $Decision.Reasons = @($Result.Reasons) $Decision.TargetCount = $Result.TargetCount $Decision.FindingCount = $Result.FindingCount $Decision.ErrorFindingCount = $Result.ErrorFindingCount $Decision.SecretFindingCount = $Result.SecretFindingCount $Decision.FixtureContents = $Result.FixtureContents $Decision.CapturedOutput = $Result.CapturedOutput $Decision.DeterministicScore = $Result.DeterministicScore $Decision.CompletedAt = $Result.CompletedAt $Decision.BehaviorClassification = $Result.BehaviorClassification $Decision.BehaviorTargetSet = $Result.BehaviorTargetSet } function Get-CheckEvidenceRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) if ($null -eq $Evidence) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'missing' Reasons = @('required-check-missing') } } if (-not (Test-AiEvidenceContract -Check $Check -Evidence $Evidence)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'trusted-adapter-evidence' } } if ((Get-ExpectedAiEvidenceSource -Check $Check) -and (Test-ChangedPathsIncludeAiSurface -Evidence $Evidence)) { $aiTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' if (($null -eq $aiTargets) -or (@($aiTargets).Count -eq 0)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @(Get-CheckFailureReason -Check $Check) AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } } $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' if ($null -eq $currentTargets) { $declaredCount = Get-QualityCount -Evidence $Evidence -Name 'TargetCount' if (($null -ne $declaredCount) -and ($declaredCount -le 0)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @(Get-CheckFailureReason -Check $Check) } } return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'target-set' } } $currentRule = Get-QualityText -Evidence $Evidence -Name 'CurrentRuleIdentity' $ruleKnown = -not [string]::IsNullOrWhiteSpace($currentRule) if ($ruleKnown) { $ruleKnown = Test-DeclaredRuleIdentity -Check $Check -RuleIdentity $currentRule } if (-not $ruleKnown) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'rule-identity' } } if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'CompletedAt')) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'completion-time' } } if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'RequestedAt')) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'requested-at' } } if ([string]::IsNullOrWhiteSpace((Get-QualityText -Evidence $Evidence -Name 'ProtectedRevision'))) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'protected-revision' } } return $null } function Get-CheckFreshnessRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $completedAt = Get-QualityTime -Evidence $Evidence -Name 'CompletedAt' $requestedAt = Get-QualityTime -Evidence $Evidence -Name 'RequestedAt' $freshness = Get-EvidenceFreshness -CompletedAt $completedAt -RequestedAt $requestedAt if ($freshness -eq 'unknown') { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'completion-time' } } if ($freshness -eq 'stale') { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'stale' Reasons = @('evidence-stale') Freshness = 'stale' RequiredRun = $Check } } return $null } function Get-CheckIdentityRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' $recordedTargets = Get-QualityList -Evidence $Evidence -Name 'TargetSet' $sameTargets = Test-SameTargetSet -Left $recordedTargets -Right $currentTargets $sameRule = [string]::Equals( (Get-QualityText -Evidence $Evidence -Name 'RuleIdentity'), (Get-QualityText -Evidence $Evidence -Name 'CurrentRuleIdentity'), [System.StringComparison]::Ordinal ) $presentedAgain = Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain' $changedPaths = Get-QualityList -Evidence $Evidence -Name 'ChangedPaths' if ($null -eq $changedPaths) { $changedPaths = @() } if (($Check -eq 'comprehensive-pester') -and $presentedAgain) { if (Test-PesterInvalidatingChange -ChangedPaths $changedPaths -CurrentTargetSet $currentTargets) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'stale' Reasons = @('evidence-stale') Freshness = 'stale' RequiredRun = 'comprehensive-pester' } } } if ((-not $sameTargets) -or (-not $sameRule)) { if ($presentedAgain) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'stale' Reasons = @('evidence-stale') Freshness = 'stale' RequiredRun = $Check } } return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @(Get-CheckFailureReason -Check $Check) } } return $null } function Get-CheckCoverageRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $currentTargets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' $scannedTargets = Get-QualityList -Evidence $Evidence -Name 'ScannedTargetSet' $scannedComplete = $false if ($null -ne $scannedTargets) { $scannedComplete = Test-SameTargetSet -Left $scannedTargets -Right $currentTargets } if (-not $scannedComplete) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @(Get-CheckFailureReason -Check $Check) } } $targetCount = Get-QualityCount -Evidence $Evidence -Name 'TargetCount' if ($null -eq $targetCount) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'target-count' } } if ($targetCount -le 0) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @(Get-CheckFailureReason -Check $Check) } } return $null } function Get-FirstGateTarget { param($Evidence) $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' if ($null -eq $targets) { return $null } foreach ($target in @($targets)) { $text = [string]$target if (-not [string]::IsNullOrWhiteSpace($text)) { return (ConvertTo-GatePath $text) } } return $null } function Test-BehaviorClassificationAllowed { param([string]$Classification) if ([string]::IsNullOrWhiteSpace($Classification)) { return $false } foreach ($allowed in @('behavior-bearing', 'non-behavior-bearing')) { if ([string]::Equals($Classification, $allowed, [System.StringComparison]::Ordinal)) { return $true } } return $false } function Get-ExpectedAiEvidenceSource { param([string]$Check) switch ($Check) { 'agent-skill-behavior' { return 'deterministic-eval-runner' } 'ai-surface-sync' { return 'canonical-generated-pair' } default { return $null } } } function Test-AiEvidenceContract { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $expectedSource = Get-ExpectedAiEvidenceSource -Check $Check if ($null -eq $expectedSource) { return $true } $adapterAttested = Get-QualityFlag -Evidence $Evidence -Name 'AdapterAttested' $applicabilitySource = Get-QualityText -Evidence $Evidence -Name 'ApplicabilitySource' $evidenceSource = Get-QualityText -Evidence $Evidence -Name 'EvidenceSource' return ( $adapterAttested -and [string]::Equals($applicabilitySource, 'changed-path-policy', [System.StringComparison]::Ordinal) -and [string]::Equals($evidenceSource, $expectedSource, [System.StringComparison]::Ordinal) ) } function Test-ChangedPathsIncludeAiSurface { param($Evidence) $changed = Get-QualityList -Evidence $Evidence -Name 'ChangedPaths' foreach ($path in @($changed)) { if (Test-PrefixedGatePath -Path ([string]$path) -Prefixes @('.github/', '.cursor/')) { return $true } } return $false } function Test-OptionalCheckWithoutTarget { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $definition = Get-RepositoryCheckDefinition -Check $Check if ($null -eq $definition) { return $false } if (-not (Test-AiEvidenceContract -Check $Check -Evidence $Evidence)) { return $false } if (Test-ChangedPathsIncludeAiSurface -Evidence $Evidence) { return $false } $optional = $false if ($definition.Contains('ApplicableWhenTargetNonEmpty')) { $optional = [bool]$definition.ApplicableWhenTargetNonEmpty } if (-not $optional) { return $false } $targets = Get-QualityList -Evidence $Evidence -Name 'CurrentTargetSet' if ($null -ne $targets) { return (@($targets).Count -eq 0) } $count = Get-QualityCount -Evidence $Evidence -Name 'TargetCount' return (($null -ne $count) -and ($count -le 0)) } function Get-NotApplicableCheckResult { param( [Parameter(Mandatory = $true)] [string]$Check ) return [pscustomobject]@{ Check = $Check CheckAccepted = $true EvidenceClass = $null Freshness = $null ProvenanceRevision = $null MissingInput = $null RequiredRun = $null FailedCheck = $null AffectedTarget = $null Condition = 'not-applicable' Reasons = @() TargetCount = 0 FindingCount = $null ErrorFindingCount = $null SecretFindingCount = $null FixtureContents = $null CapturedOutput = $null DeterministicScore = $null CompletedAt = $null BehaviorClassification = $null BehaviorTargetSet = @() } } function Get-BehavioralCoverageRejection { param( [Parameter(Mandatory = $true)] [string]$Check, [Parameter(Mandatory = $true)] [string]$Condition, $Evidence ) return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = $Condition Reasons = @(Get-CheckFailureReason -Check $Check) AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } function Get-BehavioralOutcomeRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) if (Get-QualityFlag -Evidence $Evidence -Name 'StaticContentCheckOnly') { return Get-BehavioralCoverageRejection -Check $Check -Condition 'static-content-check' -Evidence $Evidence } $fixtures = Get-QualityList -Evidence $Evidence -Name 'FixtureContents' $fixtureCount = 0 if ($null -ne $fixtures) { $fixtureCount = @($fixtures).Count } if ($fixtureCount -le 0) { return Get-BehavioralCoverageRejection -Check $Check -Condition 'coverage-gap' -Evidence $Evidence } $captured = Get-QualityText -Evidence $Evidence -Name 'CapturedOutput' if ([string]::IsNullOrWhiteSpace($captured)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'captured-output' AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } $score = Get-QualityText -Evidence $Evidence -Name 'DeterministicScore' if ([string]::IsNullOrWhiteSpace($score)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'deterministic-score' AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } if (-not [string]::Equals($score, 'pass', [System.StringComparison]::Ordinal)) { return Get-BehavioralCoverageRejection -Check $Check -Condition 'score-failed' -Evidence $Evidence } $presentedAgain = Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain' $classification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification' $behaviorBearing = [string]::Equals($classification, 'behavior-bearing', [System.StringComparison]::Ordinal) if ($presentedAgain -and $behaviorBearing) { return Get-BehavioralCoverageRejection -Check $Check -Condition 'capture-not-new' -Evidence $Evidence } if ((-not $presentedAgain) -and (-not (Get-QualityFlag -Evidence $Evidence -Name 'NewlyCaptured'))) { return Get-BehavioralCoverageRejection -Check $Check -Condition 'capture-not-new' -Evidence $Evidence } return $null } function Get-SurfaceOutcomeRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $classification = Get-QualityText -Evidence $Evidence -Name 'BehaviorClassification' if (-not (Test-BehaviorClassificationAllowed -Classification $classification)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'unclassified' Reasons = @(Get-CheckFailureReason -Check $Check) AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } if (-not (Get-QualityFlag -Evidence $Evidence -Name 'SurfacesSynchronized')) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'unsynchronized' Reasons = @('AI-surface-unsynchronized') AffectedTarget = Get-FirstGateTarget -Evidence $Evidence } } return $null } function Get-CheckOutcomeRejection { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) $definition = Get-RepositoryCheckDefinition -Check $Check if ($null -eq $definition) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'missing' Reasons = @('required-check-missing') } } $kind = [string]$definition.OutcomeKind if ($kind -eq 'behavioral') { return Get-BehavioralOutcomeRejection -Check $Check -Evidence $Evidence } if ($kind -eq 'surface') { return Get-SurfaceOutcomeRejection -Check $Check -Evidence $Evidence } if ($kind -eq 'counter') { $count = Get-QualityCount -Evidence $Evidence -Name ([string]$definition.CounterName) if ($null -eq $count) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = [string]$definition.MissingCounterInput } } if ($count -gt 0) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'failed' Reasons = @([string]$definition.FailureReason) } } return $null } $analyzerCount = Get-QualityCount -Evidence $Evidence -Name 'AnalyzerFindingCount' $encodingCount = Get-QualityCount -Evidence $Evidence -Name 'EncodingFaultCount' if (($null -eq $analyzerCount) -or ($null -eq $encodingCount)) { return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = 'incomplete' Reasons = @('evidence-input-unknown') MissingInput = 'quality-finding-count' } } if (($analyzerCount -gt 0) -or ($encodingCount -gt 0)) { $condition = Get-QualityText -Evidence $Evidence -Name 'Condition' if ([string]::IsNullOrWhiteSpace($condition)) { if ($analyzerCount -gt 0) { $condition = 'analysis-finding' } else { $condition = 'encoding-fault' } } return Get-RejectedCheckResult -Check $Check -Rejection @{ Condition = $condition Reasons = @([string]$definition.FailureReason) AffectedTarget = Get-QualityText -Evidence $Evidence -Name 'AffectedTarget' } } return $null } function Get-RepositoryCheckResult { param( [Parameter(Mandatory = $true)] [string]$Check, $Evidence ) if (Test-OptionalCheckWithoutTarget -Check $Check -Evidence $Evidence) { return Get-NotApplicableCheckResult -Check $Check } $incomplete = Get-CheckEvidenceRejection -Check $Check -Evidence $Evidence if ($null -ne $incomplete) { return $incomplete } $stale = Get-CheckFreshnessRejection -Check $Check -Evidence $Evidence if ($null -ne $stale) { return $stale } $identity = Get-CheckIdentityRejection -Check $Check -Evidence $Evidence if ($null -ne $identity) { return $identity } $coverage = Get-CheckCoverageRejection -Check $Check -Evidence $Evidence if ($null -ne $coverage) { return $coverage } $outcome = Get-CheckOutcomeRejection -Check $Check -Evidence $Evidence if ($null -ne $outcome) { return $outcome } $evidenceClass = 'new-full-evidence' if (Get-QualityFlag -Evidence $Evidence -Name 'PresentedAgain') { $evidenceClass = 'reused-full-evidence' } return Get-AcceptedCheckResult -Check $Check -EvidenceClass $evidenceClass -ProvenanceRevision (Get-QualityText -Evidence $Evidence -Name 'ProtectedRevision') -Evidence $Evidence } function Get-ManifestCheckResult { param([hashtable]$ManifestResults) foreach ($row in @($script:RepositoryCheckCatalog)) { $name = [string]$row.Id if (-not $ManifestResults.Contains($name)) { continue } Write-Output (Get-RepositoryCheckResult -Check $name -Evidence $ManifestResults[$name]) } } function Test-ProtectedBoundary { param( [string]$Destination, [string]$IntegrationKind ) $onMain = [string]::Equals($Destination, $script:ProtectedDestination, [System.StringComparison]::Ordinal) foreach ($kind in @($script:ProtectedIntegrationKinds)) { $kindMatches = [string]::Equals($IntegrationKind, [string]$kind, [System.StringComparison]::Ordinal) if ($onMain -and $kindMatches) { return $true } } return $false } function Get-RepositoryQualityCheckCatalog { foreach ($row in @($script:RepositoryCheckCatalog)) { Write-Output ([pscustomobject]@{ Id = [string]$row.Id RulePrefix = [string]$row.RulePrefix }) } } function Get-FirstMissingCatalogCheck { param([hashtable]$ManifestResults) foreach ($row in @($script:RepositoryCheckCatalog)) { $name = [string]$row.Id $present = $false if ($null -ne $ManifestResults) { $present = $ManifestResults.Contains($name) } if (-not $present) { return Get-RejectedCheckResult -Check $name -Rejection @{ Condition = 'missing' Reasons = @('required-check-missing') } } } return $null } function Get-EmergencyPartyGap { param( $Evidence, [Parameter(Mandatory = $true)] [string]$NameKey, [Parameter(Mandatory = $true)] [string]$RoleKey, [Parameter(Mandatory = $true)] [string]$MissingNameToken, [Parameter(Mandatory = $true)] [string]$MissingRoleToken ) $name = Get-QualityText -Evidence $Evidence -Name $NameKey if ([string]::IsNullOrWhiteSpace($name)) { return $MissingNameToken } $role = Get-QualityText -Evidence $Evidence -Name $RoleKey $roleKnown = [string]::Equals($role, $script:EmergencyMaintainerRole, [System.StringComparison]::Ordinal) if (-not $roleKnown) { return $MissingRoleToken } return $null } function Get-EmergencyRecordGap { param($Evidence) $revision = Get-QualityText -Evidence $Evidence -Name 'Revision' if ([string]::IsNullOrWhiteSpace($revision)) { return 'revision' } $reason = Get-QualityText -Evidence $Evidence -Name 'Reason' if ([string]::IsNullOrWhiteSpace($reason)) { return 'reason' } $requesterGap = Get-EmergencyPartyGap -Evidence $Evidence -NameKey 'Requester' -RoleKey 'RequesterRole' -MissingNameToken 'requester' -MissingRoleToken 'requester' if ($null -ne $requesterGap) { return $requesterGap } $approverGap = Get-EmergencyPartyGap -Evidence $Evidence -NameKey 'Approver' -RoleKey 'ApproverRole' -MissingNameToken 'approver' -MissingRoleToken 'approver-role' if ($null -ne $approverGap) { return $approverGap } $requester = Get-QualityText -Evidence $Evidence -Name 'Requester' $approver = Get-QualityText -Evidence $Evidence -Name 'Approver' $samePerson = [string]::Equals($requester, $approver, [System.StringComparison]::OrdinalIgnoreCase) if ($samePerson) { return 'approver-same-as-requester' } if ($null -eq (Get-QualityTime -Evidence $Evidence -Name 'Timestamp')) { return 'timestamp' } $scope = Get-QualityText -Evidence $Evidence -Name 'Scope' if (-not [string]::Equals($scope, $script:EmergencyScope, [System.StringComparison]::Ordinal)) { return 'scope' } $followUp = Get-QualityText -Evidence $Evidence -Name 'FollowUpValidation' if ([string]::IsNullOrWhiteSpace($followUp)) { return 'follow-up-validation' } return $null } function Get-VisibleEmergencyRecord { param($Evidence) $quality = $null $entry = Get-QualityEvidenceEntry -Evidence $Evidence -Name 'QualityResults' if ($entry.Present) { $quality = $entry.Value } return [pscustomobject]@{ Visible = $true Revision = Get-QualityText -Evidence $Evidence -Name 'Revision' Reason = Get-QualityText -Evidence $Evidence -Name 'Reason' Requester = Get-QualityText -Evidence $Evidence -Name 'Requester' Approver = Get-QualityText -Evidence $Evidence -Name 'Approver' Timestamp = Get-QualityTime -Evidence $Evidence -Name 'Timestamp' Scope = $script:EmergencyScope FollowUpValidation = Get-QualityText -Evidence $Evidence -Name 'FollowUpValidation' QualityResults = $quality } } function Get-EmergencyAssessment { param($Evidence) if ($null -eq $Evidence) { return [pscustomobject]@{ Accepted = $false MissingField = 'revision' Record = $null } } $gap = Get-EmergencyRecordGap -Evidence $Evidence if ($null -ne $gap) { return [pscustomobject]@{ Accepted = $false MissingField = $gap Record = $null } } return [pscustomobject]@{ Accepted = $true MissingField = $null Record = (Get-VisibleEmergencyRecord -Evidence $Evidence) } } function Copy-ProtectedRequestContext { param( $Decision, [string]$Destination, [string]$IntegrationKind, $EmergencyRecord, [bool]$LocalAdvisoryAvoided ) if ($LocalAdvisoryAvoided) { $Decision.LocalAdvisory = 'avoided' } $Decision.RequestedDestination = $Destination $Decision.IntegrationKind = $IntegrationKind $emergency = Get-EmergencyAssessment -Evidence $EmergencyRecord if ($emergency.Accepted) { $Decision.EmergencyRecord = $emergency.Record } } function Get-ProtectedBaselineState { param($ComprehensiveSuite) $baselineCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount' if ($null -eq $baselineCount) { return 'unknown' } if ($baselineCount -gt 0) { return 'red' } return 'clear' } function Copy-ProtectedBaselineRejection { param( $Decision, [string]$State ) if ($State -eq 'unknown') { $Decision.ProtectedIntegration = 'rejected' $Decision.EvidenceClass = 'failed-evidence' $Decision.Reasons = @('evidence-input-unknown') return $true } if ($State -eq 'red') { $Decision.ProtectedIntegration = 'rejected' $Decision.EvidenceClass = 'failed-evidence' $Decision.Recovery = 'baseline-recovery' $Decision.Reasons = @('baseline-red') return $true } return $false } function Get-ProtectedFullEvidenceClass { param( $FullEvidence, $PriorFullEvidence ) if (Test-SuccessfulComprehensiveResult -Evidence $FullEvidence) { return 'new-full-evidence' } if (Test-SuccessfulComprehensiveResult -Evidence $PriorFullEvidence) { return 'reused-full-evidence' } return $null } function Copy-ProtectedMissingFullEvidence { param( $Decision, $FastFeedback ) $Decision.ProtectedIntegration = 'rejected' $Decision.MissingEvidence = 'full-evidence' $Decision.Reasons = @('required-check-missing') if (Get-QualityFlag -Evidence $FastFeedback -Name 'Succeeded') { $Decision.EvidenceClass = 'fast-advisory-evidence' } else { $Decision.EvidenceClass = 'failed-evidence' } } function Copy-ProtectedCompositionOutcome { param( $Decision, [hashtable]$ManifestResults, [string]$ProtectedRevision ) $evaluated = @() if ($null -ne $ManifestResults) { $evaluated = @(Get-ManifestCheckResult -ManifestResults $ManifestResults) } $Decision.CheckResults = $evaluated $failure = $null foreach ($item in $evaluated) { if (-not $item.CheckAccepted) { $failure = $item break } } if ($null -eq $failure) { $failure = Get-FirstMissingCatalogCheck -ManifestResults $ManifestResults if ($null -ne $failure) { $Decision.CheckResults = @($evaluated + $failure) } } if ($null -ne $failure) { Copy-CheckOntoDecision -Decision $Decision -Result $failure $Decision.ProtectedIntegration = 'rejected' $Decision.MissingEvidence = $failure.FailedCheck $Decision.FullEvidenceAccepted = $false return } $Decision.ProtectedIntegration = 'allowed' $Decision.MissingEvidence = $null $Decision.Reasons = @() $Decision.FullEvidenceAccepted = $true if (-not [string]::IsNullOrWhiteSpace($ProtectedRevision)) { $Decision.ProvenanceRevision = $ProtectedRevision } elseif ($evaluated.Count -gt 0) { $Decision.ProvenanceRevision = $evaluated[0].ProvenanceRevision } } function Get-ProtectedIntegrationDecision { param( $Decision, [string]$Destination, [string]$IntegrationKind, $ComprehensiveSuite, $FullEvidence, $PriorFullEvidence, $FastFeedback, [hashtable]$ManifestResults, [string]$ProtectedRevision, $EmergencyRecord, [bool]$LocalAdvisoryAvoided ) Copy-ProtectedRequestContext -Decision $Decision -Destination $Destination -IntegrationKind $IntegrationKind -EmergencyRecord $EmergencyRecord -LocalAdvisoryAvoided $LocalAdvisoryAvoided $insideBoundary = Test-ProtectedBoundary -Destination $Destination -IntegrationKind $IntegrationKind if (-not $insideBoundary) { $Decision.ProtectedIntegration = 'rejected' $Decision.EvidenceClass = 'failed-evidence' $Decision.Reasons = @('protected-boundary-rejected') return $Decision } $baselineState = Get-ProtectedBaselineState -ComprehensiveSuite $ComprehensiveSuite if (Copy-ProtectedBaselineRejection -Decision $Decision -State $baselineState) { return $Decision } $evidenceClass = Get-ProtectedFullEvidenceClass -FullEvidence $FullEvidence -PriorFullEvidence $PriorFullEvidence if ($null -eq $evidenceClass) { Copy-ProtectedMissingFullEvidence -Decision $Decision -FastFeedback $FastFeedback return $Decision } $Decision.EnforcementReadiness = 'evaluable' $Decision.EvidenceClass = $evidenceClass Copy-ProtectedCompositionOutcome -Decision $Decision -ManifestResults $ManifestResults -ProtectedRevision $ProtectedRevision return $Decision } function Invoke-RepositoryQualityDecision { <# .SYNOPSIS Classify repository quality evidence and decide baseline readiness, protected integration, and emergency exception records. .DESCRIPTION Driving port for baseline recovery, fast-versus-full evidence, per-check identity, protected composition, and emergency records. A red comprehensive baseline stays enforcement-disabled. A recorded green baseline may be evaluated. Fast advisory evidence never authorizes protected integration. Each manifest check is reused only when its target set and rule identity match and the result is at most 24 hours old. A direct update or merge into main is allowed only when every catalog row is successful and fresh. A behavior-bearing AI change needs newly captured output scored by the deterministic eval runner. A missing fixture is a behavioral coverage gap. Cosmetic prose reuses that evidence only when it is classified non-behavior-bearing and the paired AI surfaces are synchronized. An empty behavioral or AI surface target set is not applicable. An emergency record is visible and never waives those checks. The rollout flag stays disabled; ProtectedIntegration is the server decision. .PARAMETER Request prepare-protected-enforcement, evaluate-enforcement-readiness, record-fast-feedback, request-protected-integration, record-full-evidence, evaluate-check, or record-emergency-exception. .PARAMETER Check comprehensive-pester, pester-test-quality, powershell-security, trivy-secrets, powershell-quality, agent-skill-behavior, or ai-surface-sync. Used by evaluate-check. .PARAMETER ComprehensiveSuite Recorded comprehensive suite. FailedCount is required for baseline requests. RecordedAsBaseline marks a green run as the baseline. Evaluate requests also carry PriorFailedCount and PriorRecordedAsBaseline. .PARAMETER FastFeedback Staged local result. Succeeded is true when the fast gate passed. .PARAMETER FullEvidence A comprehensive result recorded from a new run. FailedCount is the failure total. .PARAMETER PriorFullEvidence A previously recorded comprehensive result presented again with no new run. .PARAMETER CheckEvidence One check result. Callers supply CurrentTargetSet, TargetSet, ScannedTargetSet, CurrentRuleIdentity, RuleIdentity, CompletedAt, RequestedAt, ProtectedRevision, TargetCount, PresentedAgain, ChangedPaths, FixtureContents, CapturedOutput, DeterministicScore, NewlyCaptured, StaticContentCheckOnly, BehaviorClassification, SurfacesSynchronized, and the check-specific finding counters FailedCount, BlockingFindingCount, ErrorFindingCount, SecretFindingCount, AnalyzerFindingCount, and EncodingFaultCount. BehaviorClassification, SurfacesSynchronized, DeterministicScore, CapturedOutput, and FixtureContents are caller-asserted. AI checks additionally require AdapterAttested = true, ApplicabilitySource = changed-path-policy, and EvidenceSource = deterministic-eval-runner for agent-skill-behavior or canonical-generated-pair for ai-surface-sync. This port does not cryptographically attest those claims. A trusted adapter derives CurrentTargetSet from the real changed-path set, derives SurfacesSynchronized from a real canonical and generated pair, BehaviorClassification from an explicit path policy, and the score, capture, and fixtures from the deterministic eval runner. It rejects evidence that omits the adapter contract, invents a passing score, or invents a synchronized pair. Adapters must not write full CapturedOutput to console or CI logs. .PARAMETER ManifestResults Named check results for a protected integration request. A null row is a missing required result. A catalog row omitted from the hashtable is the missing companion when every presented row passes. .PARAMETER Destination Protected integration target. Only main is inside the protected boundary. .PARAMETER IntegrationKind direct-update or merge. Any other kind is outside the protected boundary. .PARAMETER ProtectedRevision Provenance recorded on an allowed composed result. It is not a check identity. A difference from check-row revisions does not reject the decision. Callers pass the git SHA that identifies the composed integration. .PARAMETER EmergencyRecord Exception record with Revision, Reason, Requester, Approver, RequesterRole, ApproverRole, Timestamp, Scope, FollowUpValidation, and optional QualityResults. Both roles must be repository-maintainer. The requester and approver are different people. Scope must be local-advisory. This port does not accept IdentityProof. Requester, Approver, and roles are caller-asserted strings; an adapter binds those names from an authenticated identity and rejects records that invent role strings. A complete record is visible and never waives protected checks, and it is never successful full evidence. EvidenceClass stays emergency-bypass-record. .PARAMETER LocalAdvisoryAvoided Observational metadata. The maintainer disabled or skipped a local advisory check. The switch does not authorize integration. The server still requires the manifest rows. .OUTPUTS A decision object. EvidenceClass is fast-advisory-evidence, new-full-evidence, reused-full-evidence, failed-evidence, or emergency-bypass-record. ProtectedEnforcement stays disabled. EnforcementReadiness is disabled, evaluable, or paused. ProtectedIntegration is not-requested, rejected, or allowed. Reasons include baseline-red, protected-boundary-rejected, full-check-failed, required-check-missing, evidence-input-unknown, evidence-stale, test-quality-semgrep-failed, security-semgrep-failed, secret-check-failed, PowerShell-quality-failed, behavioral-coverage-gap, AI-surface-unsynchronized, AI-path-unclassified, or emergency-record-incomplete. MissingEvidence is full-evidence when no full Pester evidence exists, or the failed check name when a manifest row is rejected or absent. Recovery is baseline-recovery or required when recovery work remains. LocalFeedback is available or unavailable. LocalAdvisory is used or avoided. MissingField is revision, reason, requester, approver, approver-role, approver-same-as-requester, timestamp, scope, or follow-up-validation. MissingInput may be target-set, rule-identity, completion-time, requested-at, protected-revision, or trusted-adapter-evidence when an AI check lacks the adapter contract. EmergencyRecord is the visible exception when the record is complete. evaluate-check also sets CheckAccepted, Freshness (fresh, stale, or null), ProvenanceRevision, MissingInput, RequiredRun, FailedCheck, AffectedTarget, Condition, CheckResults, TargetCount, FindingCount, ErrorFindingCount, SecretFindingCount, FixtureContents, CapturedOutput, DeterministicScore, CompletedAt, BehaviorClassification, and BehaviorTargetSet. .EXAMPLE Invoke-RepositoryQualityDecision -Request prepare-protected-enforcement -ComprehensiveSuite @{ FailedCount = 8; RecordedAsBaseline = $true } #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidateSet( 'prepare-protected-enforcement', 'evaluate-enforcement-readiness', 'record-fast-feedback', 'request-protected-integration', 'record-full-evidence', 'evaluate-check', 'record-emergency-exception' )] [string]$Request, [ValidateScript({ $null -ne (Get-RepositoryCheckDefinition -Check $_) })] [string]$Check, [hashtable]$ComprehensiveSuite, [hashtable]$FastFeedback, [hashtable]$FullEvidence, [hashtable]$PriorFullEvidence, [hashtable]$CheckEvidence, [hashtable]$ManifestResults, [string]$Destination = $script:ProtectedDestination, [string]$IntegrationKind = $(@($script:ProtectedIntegrationKinds)[0]), [string]$ProtectedRevision, [hashtable]$EmergencyRecord, [switch]$LocalAdvisoryAvoided ) $decision = [pscustomobject]@{ Request = $Request EvidenceClass = $null ProtectedEnforcement = 'disabled' EnforcementReadiness = 'disabled' ProtectedIntegration = 'not-requested' LocalFeedback = 'unavailable' ComprehensiveSuiteRequiredForCommit = $false FullEvidenceAccepted = $false Recovery = $null MissingEvidence = $null Reasons = @() CheckAccepted = $false Freshness = $null ProvenanceRevision = $null MissingInput = $null RequiredRun = $null FailedCheck = $null AffectedTarget = $null Condition = $null CheckResults = @() TargetCount = $null FindingCount = $null ErrorFindingCount = $null SecretFindingCount = $null LocalAdvisory = 'used' RequestedDestination = $null IntegrationKind = $null MissingField = $null EmergencyRecord = $null FixtureContents = $null CapturedOutput = $null DeterministicScore = $null CompletedAt = $null BehaviorClassification = $null BehaviorTargetSet = $null } switch ($Request) { 'prepare-protected-enforcement' { $count = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount' if ($null -eq $count) { $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('evidence-input-unknown') return $decision } if ($count -gt 0) { $decision.EvidenceClass = 'failed-evidence' $decision.Recovery = 'baseline-recovery' $decision.Reasons = @('baseline-red') return $decision } if (-not (Get-QualityFlag -Evidence $ComprehensiveSuite -Name 'RecordedAsBaseline')) { return $decision } $decision.EvidenceClass = 'new-full-evidence' $decision.EnforcementReadiness = 'evaluable' $decision.FullEvidenceAccepted = $true return $decision } 'evaluate-enforcement-readiness' { $laterCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'FailedCount' $priorCount = Get-QualityCount -Evidence $ComprehensiveSuite -Name 'PriorFailedCount' $priorRecorded = Get-QualityFlag -Evidence $ComprehensiveSuite -Name 'PriorRecordedAsBaseline' if (($null -eq $laterCount) -or ($null -eq $priorCount) -or (-not $priorRecorded)) { $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('evidence-input-unknown') return $decision } if ($priorCount -gt 0) { $decision.EvidenceClass = 'failed-evidence' $decision.Recovery = 'baseline-recovery' $decision.Reasons = @('baseline-red') return $decision } if ($laterCount -gt 0) { $decision.EvidenceClass = 'failed-evidence' $decision.EnforcementReadiness = 'paused' $decision.Recovery = 'required' $decision.Reasons = @('full-check-failed') return $decision } $decision.EvidenceClass = 'new-full-evidence' $decision.EnforcementReadiness = 'evaluable' $decision.FullEvidenceAccepted = $true return $decision } 'record-fast-feedback' { $decision.ComprehensiveSuiteRequiredForCommit = $false if (Get-QualityFlag -Evidence $FastFeedback -Name 'Succeeded') { $decision.EvidenceClass = 'fast-advisory-evidence' $decision.LocalFeedback = 'available' return $decision } $decision.EvidenceClass = 'failed-evidence' $decision.LocalFeedback = 'unavailable' return $decision } 'request-protected-integration' { return Get-ProtectedIntegrationDecision -Decision $decision -Destination $Destination -IntegrationKind $IntegrationKind -ComprehensiveSuite $ComprehensiveSuite -FullEvidence $FullEvidence -PriorFullEvidence $PriorFullEvidence -FastFeedback $FastFeedback -ManifestResults $ManifestResults -ProtectedRevision $ProtectedRevision -EmergencyRecord $EmergencyRecord -LocalAdvisoryAvoided:$LocalAdvisoryAvoided } 'record-emergency-exception' { $emergency = Get-EmergencyAssessment -Evidence $EmergencyRecord if (-not $emergency.Accepted) { $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('emergency-record-incomplete') $decision.MissingField = $emergency.MissingField $decision.FullEvidenceAccepted = $false return $decision } $decision.EvidenceClass = 'emergency-bypass-record' $decision.EmergencyRecord = $emergency.Record $decision.FullEvidenceAccepted = $false return $decision } 'evaluate-check' { if ([string]::IsNullOrWhiteSpace($Check)) { $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('evidence-input-unknown') $decision.MissingInput = 'check' return $decision } $result = Get-RepositoryCheckResult -Check $Check -Evidence $CheckEvidence Copy-CheckOntoDecision -Decision $decision -Result $result $decision.CheckResults = @($result) return $decision } 'record-full-evidence' { $newCount = Get-QualityCount -Evidence $FullEvidence -Name 'FailedCount' if ($null -ne $newCount) { if ($newCount -eq 0) { $decision.EvidenceClass = 'new-full-evidence' $decision.FullEvidenceAccepted = $true return $decision } $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('full-check-failed') return $decision } $priorCount = Get-QualityCount -Evidence $PriorFullEvidence -Name 'FailedCount' if (($null -ne $priorCount) -and ($priorCount -eq 0)) { $decision.EvidenceClass = 'reused-full-evidence' $decision.FullEvidenceAccepted = $true return $decision } $decision.EvidenceClass = 'failed-evidence' $decision.Reasons = @('evidence-input-unknown') return $decision } } return $decision } Export-ModuleMember -Function Invoke-RepositoryQualityDecision, Get-RepositoryQualityCheckCatalog |