$countDescriptors */ public function build( ExplanationFamily $family, string $headline, string $executionOutcome, string $executionOutcomeLabel, string $evaluationResult, TrustworthinessLevel $trustworthinessLevel, string $reliabilityStatement, ?string $coverageStatement, ?string $dominantCauseCode, ?string $dominantCauseLabel, ?string $dominantCauseExplanation, string $nextActionCategory, string $nextActionText, array $countDescriptors = [], bool $diagnosticsAvailable = false, ?string $diagnosticsSummary = null, ): OperatorExplanationPattern { return new OperatorExplanationPattern( family: $family, headline: $headline, executionOutcome: $executionOutcome, executionOutcomeLabel: $executionOutcomeLabel, evaluationResult: $evaluationResult, trustworthinessLevel: $trustworthinessLevel, reliabilityStatement: $reliabilityStatement, coverageStatement: $coverageStatement, dominantCauseCode: $dominantCauseCode, dominantCauseLabel: $dominantCauseLabel, dominantCauseExplanation: $dominantCauseExplanation, nextActionCategory: $nextActionCategory, nextActionText: $nextActionText, countDescriptors: $countDescriptors, diagnosticsAvailable: $diagnosticsAvailable, diagnosticsSummary: $diagnosticsSummary, ); } /** * @param array $countDescriptors */ public function fromArtifactTruthEnvelope( ArtifactTruthEnvelope $truth, array $countDescriptors = [], ): OperatorExplanationPattern { $reason = $truth->reason?->toReasonResolutionEnvelope(); $family = $this->familyForTruth($truth, $reason); $trustworthiness = $this->trustworthinessForTruth($truth, $reason); $evaluationResult = $this->evaluationResultForTruth($truth, $family); $executionOutcome = $this->executionOutcomeKey($truth->executionOutcome); $executionOutcomeLabel = $this->executionOutcomeLabel($truth->executionOutcome); $dominantCauseCode = $reason?->internalCode; $dominantCauseLabel = $reason?->operatorLabel ?? $truth->primaryLabel; $dominantCauseExplanation = $reason?->shortExplanation ?? $truth->primaryExplanation; $headline = $this->headlineForTruth($truth, $family, $trustworthiness); $reliabilityStatement = $this->reliabilityStatementForTruth($truth, $trustworthiness); $coverageStatement = $this->coverageStatementForTruth($truth, $reason); $nextActionText = $truth->nextStepText(); $nextActionCategory = $this->nextActionCategory($truth->actionability, $reason); $diagnosticsAvailable = $truth->reason !== null || $truth->diagnosticLabel !== null || $countDescriptors !== []; return $this->build( family: $family, headline: $headline, executionOutcome: $executionOutcome, executionOutcomeLabel: $executionOutcomeLabel, evaluationResult: $evaluationResult, trustworthinessLevel: $trustworthiness, reliabilityStatement: $reliabilityStatement, coverageStatement: $coverageStatement, dominantCauseCode: $dominantCauseCode, dominantCauseLabel: $dominantCauseLabel, dominantCauseExplanation: $dominantCauseExplanation, nextActionCategory: $nextActionCategory, nextActionText: $nextActionText, countDescriptors: $countDescriptors, diagnosticsAvailable: $diagnosticsAvailable, diagnosticsSummary: $diagnosticsAvailable ? 'Technical truth detail remains available below the primary explanation.' : null, ); } private function familyForTruth( ArtifactTruthEnvelope $truth, ?ReasonResolutionEnvelope $reason, ): ExplanationFamily { return match (true) { $reason?->absencePattern === 'suppressed_output' => ExplanationFamily::SuppressedOutput, $reason?->absencePattern === 'blocked_prerequisite' => ExplanationFamily::BlockedPrerequisite, $truth->executionOutcome === 'pending' || $truth->artifactExistence === 'not_created' && $truth->actionability !== 'required' => ExplanationFamily::InProgress, $truth->executionOutcome === 'failed' || $truth->executionOutcome === 'blocked' => ExplanationFamily::BlockedPrerequisite, $truth->artifactExistence === 'created_but_not_usable' || $truth->contentState === 'missing_input' => ExplanationFamily::MissingInput, $truth->contentState === 'trusted' && $truth->freshnessState === 'current' && $truth->actionability === 'none' && $truth->primaryLabel === 'Trustworthy artifact' => ExplanationFamily::TrustworthyResult, $truth->contentState === 'trusted' && $truth->freshnessState === 'current' && $truth->actionability === 'none' => ExplanationFamily::NoIssuesDetected, $truth->artifactExistence === 'historical_only' => ExplanationFamily::Unavailable, default => ExplanationFamily::CompletedButLimited, }; } private function trustworthinessForTruth( ArtifactTruthEnvelope $truth, ?ReasonResolutionEnvelope $reason, ): TrustworthinessLevel { if ($reason?->trustImpact !== null) { return TrustworthinessLevel::tryFrom($reason->trustImpact) ?? TrustworthinessLevel::LimitedConfidence; } return match (true) { $truth->artifactExistence === 'created_but_not_usable', $truth->contentState === 'missing_input', $truth->executionOutcome === 'failed', $truth->executionOutcome === 'blocked' => TrustworthinessLevel::Unusable, $truth->supportState === 'limited_support', in_array($truth->contentState, ['reference_only', 'unsupported'], true) => TrustworthinessLevel::DiagnosticOnly, $truth->contentState === 'trusted' && $truth->freshnessState === 'current' && $truth->actionability === 'none' => TrustworthinessLevel::Trustworthy, default => TrustworthinessLevel::LimitedConfidence, }; } private function evaluationResultForTruth( ArtifactTruthEnvelope $truth, ExplanationFamily $family, ): string { return match ($family) { ExplanationFamily::TrustworthyResult => 'full_result', ExplanationFamily::NoIssuesDetected => 'no_result', ExplanationFamily::SuppressedOutput => 'suppressed_result', ExplanationFamily::MissingInput, ExplanationFamily::BlockedPrerequisite, ExplanationFamily::Unavailable => 'unavailable', ExplanationFamily::InProgress => 'unavailable', ExplanationFamily::CompletedButLimited => 'incomplete_result', }; } private function executionOutcomeKey(?string $executionOutcome): string { $normalized = BadgeCatalog::normalizeState($executionOutcome); return match ($normalized) { 'queued', 'running', 'pending' => 'in_progress', 'partially_succeeded' => 'completed_with_follow_up', 'blocked' => 'blocked', 'failed' => 'failed', default => 'completed', }; } private function executionOutcomeLabel(?string $executionOutcome): string { if (! is_string($executionOutcome) || trim($executionOutcome) === '') { return 'Completed'; } $spec = BadgeCatalog::spec(BadgeDomain::OperationRunOutcome, $executionOutcome); return $spec->label !== 'Unknown' ? $spec->label : ucfirst(str_replace('_', ' ', trim($executionOutcome))); } private function headlineForTruth( ArtifactTruthEnvelope $truth, ExplanationFamily $family, TrustworthinessLevel $trustworthiness, ): string { return match ($family) { ExplanationFamily::TrustworthyResult => 'The result is ready to use.', ExplanationFamily::NoIssuesDetected => 'No follow-up was detected from this result.', ExplanationFamily::SuppressedOutput => 'The run completed, but normal output was intentionally suppressed.', ExplanationFamily::MissingInput => 'The result exists, but missing inputs keep it from being decision-grade.', ExplanationFamily::BlockedPrerequisite => 'The workflow did not produce a usable result because a prerequisite blocked it.', ExplanationFamily::InProgress => 'The result is still being prepared.', ExplanationFamily::Unavailable => 'A result is not currently available for this surface.', ExplanationFamily::CompletedButLimited => match ($trustworthiness) { TrustworthinessLevel::DiagnosticOnly => 'The result is available for diagnostics, not for a final decision.', TrustworthinessLevel::LimitedConfidence => 'The result is available, but it should be read with caution.', TrustworthinessLevel::Unusable => 'The result is not reliable enough to use as-is.', default => 'The result completed with operator follow-up.', }, }; } private function reliabilityStatementForTruth( ArtifactTruthEnvelope $truth, TrustworthinessLevel $trustworthiness, ): string { return match ($trustworthiness) { TrustworthinessLevel::Trustworthy => 'Trustworthiness is high for the intended operator task.', TrustworthinessLevel::LimitedConfidence => $truth->primaryExplanation ?? 'Trustworthiness is limited because coverage, freshness, or publication readiness still need review.', TrustworthinessLevel::DiagnosticOnly => 'This output is suitable for diagnostics only and should not be treated as the final answer.', TrustworthinessLevel::Unusable => 'This output is not reliable enough to support the intended operator action yet.', }; } private function coverageStatementForTruth( ArtifactTruthEnvelope $truth, ?ReasonResolutionEnvelope $reason, ): ?string { return match (true) { $truth->contentState === 'trusted' && $truth->freshnessState === 'current' => 'Coverage and artifact quality are sufficient for the default reading path.', $truth->freshnessState === 'stale' => 'The artifact exists, but freshness limits how confidently it should be used.', $truth->contentState === 'partial' => 'Coverage is incomplete, so the visible output should be treated as partial.', $truth->contentState === 'missing_input' => $reason?->shortExplanation ?? 'Required inputs were missing or unusable when this result was assembled.', in_array($truth->contentState, ['reference_only', 'unsupported'], true) => 'Only reduced-fidelity support is available for this result.', $truth->publicationReadiness === 'blocked' => 'The artifact exists, but it is still blocked from the intended downstream use.', default => null, }; } private function nextActionCategory( string $actionability, ?ReasonResolutionEnvelope $reason, ): string { if ($reason?->actionability === 'retryable_transient') { return 'retry_later'; } return match ($actionability) { 'none' => 'none', 'optional' => 'review_evidence_gaps', default => $reason?->actionability === 'prerequisite_missing' ? 'fix_prerequisite' : 'manual_validate', }; } }