Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 4m7s
Implemented deterministic Baseline Result Semantics (Spec 383), introducing CompareSubjectResult and CompareEvidenceResult. Replaced generic arrays with strict Data Transfer Objects for Baseline engine output.
136 lines
4.4 KiB
PHP
136 lines
4.4 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Support\Baselines\CompareSemantics;
|
|
|
|
use App\Support\OperationRunOutcome;
|
|
|
|
final class BaselineCompareRunSummaryClassifier
|
|
{
|
|
/**
|
|
* @param list<CompareSubjectOutcome|array<string, mixed>> $subjectOutcomes
|
|
* @param list<string> $uncoveredTypes
|
|
* @return array{
|
|
* run_outcome: string,
|
|
* operation_outcome: string,
|
|
* counts: array<string, array<string, int>>
|
|
* }
|
|
*/
|
|
public function summarize(
|
|
array $subjectOutcomes,
|
|
int $driftFindingsCount,
|
|
bool $warningsRecorded,
|
|
bool $resumeTokenPresent = false,
|
|
array $uncoveredTypes = [],
|
|
): array {
|
|
$counts = [
|
|
'by_reason' => [],
|
|
'by_category' => [],
|
|
'by_actionability' => [],
|
|
'by_readiness_impact' => [],
|
|
];
|
|
|
|
$verifiedSubjectCount = 0;
|
|
$blockingSubjectCount = 0;
|
|
$failedSubjectCount = 0;
|
|
|
|
foreach ($subjectOutcomes as $outcome) {
|
|
$outcome = $outcome instanceof CompareSubjectOutcome ? $outcome->toArray() : $outcome;
|
|
|
|
if (! is_array($outcome)) {
|
|
continue;
|
|
}
|
|
|
|
$reason = $this->nonEmptyString($outcome['reason'] ?? null);
|
|
$category = $this->nonEmptyString($outcome['category'] ?? null);
|
|
$actionability = $this->nonEmptyString($outcome['actionability'] ?? null);
|
|
$readinessImpact = $this->nonEmptyString($outcome['readiness_impact'] ?? null);
|
|
|
|
$this->increment($counts['by_reason'], $reason);
|
|
$this->increment($counts['by_category'], $category);
|
|
$this->increment($counts['by_actionability'], $actionability);
|
|
$this->increment($counts['by_readiness_impact'], $readinessImpact);
|
|
|
|
if (in_array($category, [CompareResultCategory::Verified->value, CompareResultCategory::DriftDetected->value], true)) {
|
|
$verifiedSubjectCount++;
|
|
}
|
|
|
|
if (in_array($readinessImpact, [CompareResultReadinessImpact::CustomerBlocker->value, CompareResultReadinessImpact::InternalBlocker->value], true)) {
|
|
$blockingSubjectCount++;
|
|
}
|
|
|
|
if ($category === CompareResultCategory::Failed->value) {
|
|
$failedSubjectCount++;
|
|
}
|
|
}
|
|
|
|
foreach ($counts as &$bucket) {
|
|
ksort($bucket);
|
|
}
|
|
unset($bucket);
|
|
|
|
$hasWarnings = $warningsRecorded || $resumeTokenPresent || $uncoveredTypes !== [];
|
|
$runOutcome = $this->runOutcome(
|
|
driftFindingsCount: $driftFindingsCount,
|
|
verifiedSubjectCount: $verifiedSubjectCount,
|
|
blockingSubjectCount: $blockingSubjectCount,
|
|
failedSubjectCount: $failedSubjectCount,
|
|
hasWarnings: $hasWarnings,
|
|
);
|
|
|
|
return [
|
|
'run_outcome' => $runOutcome->value,
|
|
'operation_outcome' => in_array($runOutcome, [CompareRunOutcome::Completed, CompareRunOutcome::CompletedWithDrift], true)
|
|
? OperationRunOutcome::Succeeded->value
|
|
: OperationRunOutcome::PartiallySucceeded->value,
|
|
'counts' => $counts,
|
|
];
|
|
}
|
|
|
|
private function runOutcome(
|
|
int $driftFindingsCount,
|
|
int $verifiedSubjectCount,
|
|
int $blockingSubjectCount,
|
|
int $failedSubjectCount,
|
|
bool $hasWarnings,
|
|
): CompareRunOutcome {
|
|
if ($failedSubjectCount > 0 && $verifiedSubjectCount === 0 && $driftFindingsCount === 0) {
|
|
return CompareRunOutcome::Failed;
|
|
}
|
|
|
|
if ($blockingSubjectCount > 0 && $verifiedSubjectCount === 0 && $driftFindingsCount === 0) {
|
|
return CompareRunOutcome::Blocked;
|
|
}
|
|
|
|
if ($hasWarnings || $blockingSubjectCount > 0 || $failedSubjectCount > 0) {
|
|
return CompareRunOutcome::Partial;
|
|
}
|
|
|
|
return $driftFindingsCount > 0
|
|
? CompareRunOutcome::CompletedWithDrift
|
|
: CompareRunOutcome::Completed;
|
|
}
|
|
|
|
/**
|
|
* @param array<string, int> $counts
|
|
*/
|
|
private function increment(array &$counts, ?string $key): void
|
|
{
|
|
if ($key === null) {
|
|
return;
|
|
}
|
|
|
|
$counts[$key] = ($counts[$key] ?? 0) + 1;
|
|
}
|
|
|
|
private function nonEmptyString(mixed $value): ?string
|
|
{
|
|
if (! is_string($value) || trim($value) === '') {
|
|
return null;
|
|
}
|
|
|
|
return trim($value);
|
|
}
|
|
}
|