loadMissing(['baselineProfile', 'items']); $summary = is_array($snapshot->summary_jsonb) ? $snapshot->summary_jsonb : []; $items = $snapshot->items instanceof EloquentCollection ? $snapshot->items->sortBy([ ['policy_type', 'asc'], ['id', 'asc'], ])->values() : collect(); $groups = $items ->groupBy(static fn (BaselineSnapshotItem $item): string => (string) $item->policy_type) ->map(fn (Collection $groupItems, string $policyType): RenderedSnapshotGroup => $this->presentGroup($policyType, $groupItems)) ->sortBy(static fn (RenderedSnapshotGroup $group): string => mb_strtolower($group->label)) ->values() ->all(); $summaryRows = array_map( static fn (RenderedSnapshotGroup $group): array => [ 'policyType' => $group->policyType, 'label' => $group->label, 'governedSubjectLabel' => data_get($group->subjectDescriptor, 'display_label', $group->label), 'subjectDescriptor' => $group->subjectDescriptor, 'itemCount' => $group->itemCount, 'fidelity' => $group->fidelity->value, 'gapCount' => $group->gapSummary->count, 'capturedAt' => $group->capturedAt, 'coverageHint' => $group->coverageHint, ], $groups, ); $overallGapSummary = $this->summaryGapSummary($summary); $overallGapCount = $overallGapSummary->count; $overallFidelity = FidelityState::fromSummary($summary, $items->isNotEmpty()); return new RenderedSnapshot( snapshotId: (int) $snapshot->getKey(), baselineProfileName: $snapshot->baselineProfile?->name, capturedAt: $snapshot->captured_at?->toIso8601String(), snapshotIdentityHash: is_string($snapshot->snapshot_identity_hash) && trim($snapshot->snapshot_identity_hash) !== '' ? trim($snapshot->snapshot_identity_hash) : null, stateLabel: $this->gapStatusSpec($overallGapCount)->label, fidelitySummary: $this->fidelitySummary($summary), overallFidelity: $overallFidelity, overallGapCount: $overallGapCount, summaryRows: $summaryRows, groups: $groups, technicalDetail: [ 'defaultCollapsed' => true, 'summaryPayload' => $summary, 'groupPayloads' => array_map( static fn (RenderedSnapshotGroup $group): array => [ 'label' => $group->label, 'renderingError' => $group->renderingError, 'payload' => $group->technicalPayload, ], $groups, ), ], hasItems: $items->isNotEmpty(), ); } /** * @param list> $relatedContext */ public function presentEnterpriseDetail(BaselineSnapshot $snapshot, array $relatedContext = []): EnterpriseDetailPageData { $rendered = $this->present($snapshot); $factory = new EnterpriseDetailSectionFactory; $truthPresenter = app(ArtifactTruthPresenter::class); $truth = $truthPresenter->forBaselineSnapshot($snapshot); $compressedOutcome = $truthPresenter->compressedOutcomeFor($snapshot, SurfaceCompressionContext::baselineSnapshot()); $truthBadge = $factory->statusBadge( $truth->primaryBadgeSpec()->label, $truth->primaryBadgeSpec()->color, $truth->primaryBadgeSpec()->icon, $truth->primaryBadgeSpec()->iconColor, ); $lifecycleSpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotLifecycle, $snapshot->lifecycleState()->value); $lifecycleBadge = $factory->statusBadge( $lifecycleSpec->label, $lifecycleSpec->color, $lifecycleSpec->icon, $lifecycleSpec->iconColor, ); $fidelitySpec = BadgeRenderer::spec(BadgeDomain::BaselineSnapshotFidelity, $rendered->overallFidelity->value); $fidelityBadge = $factory->statusBadge( $fidelitySpec->label, $fidelitySpec->color, $fidelitySpec->icon, $fidelitySpec->iconColor, ); $capturedItemCount = array_sum(array_map( static fn (array $row): int => (int) ($row['itemCount'] ?? 0), $rendered->summaryRows, )); $currentTruth = $this->currentTruthPresentation($truth); $currentTruthBadge = $factory->statusBadge( $currentTruth['label'], $currentTruth['color'], $currentTruth['icon'], $currentTruth['iconColor'], ); $operatorExplanation = $truth->operatorExplanation; $primaryOutcomeLabel = $compressedOutcome?->primaryLabel ?? $truth->primaryLabel; $primaryOutcomeReason = $compressedOutcome?->primaryReason; $nextActionText = $compressedOutcome?->nextActionText ?? $truth->nextStepText(); $capturedItemLabel = sprintf( '%d captured item%s', $capturedItemCount, $capturedItemCount === 1 ? '' : 's', ); $gapSummaryLine = match (true) { $capturedItemCount === 0 => 'No governed subjects were captured for this baseline snapshot yet.', $rendered->overallGapCount > 0 => sprintf( '%s with %d evidence gap%s still needing review.', $capturedItemLabel, $rendered->overallGapCount, $rendered->overallGapCount === 1 ? '' : 's', ), default => $capturedItemLabel.' with no recorded evidence gaps.', }; return EnterpriseDetailBuilder::make('baseline_snapshot', 'workspace') ->header(new SummaryHeaderData( title: $rendered->baselineProfileName ?? 'Baseline snapshot', subtitle: 'Snapshot #'.$rendered->snapshotId, statusBadges: [$truthBadge, $lifecycleBadge, $fidelityBadge], keyFacts: [ $factory->keyFact('Current truth', $currentTruth['label'], badge: $currentTruthBadge), $factory->keyFact('Next step', $nextActionText), $factory->keyFact('Captured', $this->formatTimestamp($rendered->capturedAt)), $factory->keyFact('Captured items', $capturedItemCount), ], descriptionHint: 'Outcome, short reason, and next step stay ahead of lifecycle proof and technical payload detail.', )) ->decisionZone($factory->decisionZone( facts: array_values(array_filter([ $factory->keyFact('Outcome', $primaryOutcomeLabel, badge: $truthBadge), $primaryOutcomeReason !== null ? $factory->keyFact('Why this matters', $primaryOutcomeReason) : null, $factory->keyFact('Current truth', $currentTruth['label'], badge: $currentTruthBadge), $operatorExplanation !== null ? $factory->keyFact('Result meaning', $operatorExplanation->evaluationResultLabel()) : null, $operatorExplanation !== null ? $factory->keyFact('Result trust', $operatorExplanation->trustworthinessLabel()) : null, $factory->keyFact('Lifecycle', $lifecycleSpec->label, badge: $lifecycleBadge), $operatorExplanation !== null && $operatorExplanation->coverageStatement !== null ? $factory->keyFact('Coverage', $operatorExplanation->coverageStatement) : null, ])), primaryNextStep: $factory->primaryNextStep( $nextActionText, 'Baseline snapshot', ), description: 'Start here to judge whether this baseline snapshot is usable for downstream compare or review work before reading lifecycle proof and technical payload detail.', compactCounts: $factory->countPresentation( summaryLine: $gapSummaryLine, primaryFacts: [ $factory->keyFact('Captured items', $capturedItemCount), $factory->keyFact('Evidence gaps', $rendered->overallGapCount), ], ), title: 'Outcome summary', )) ->addSection( $factory->viewSection( id: 'artifact_truth', kind: 'current_status', title: 'Outcome details', view: 'filament.infolists.entries.governance-artifact-truth', viewData: [ 'artifactTruthState' => $truth->toArray($compressedOutcome), 'surface' => 'expanded', ], description: 'Detailed outcome diagnostics stay secondary to the primary operator summary above.', ), $factory->viewSection( id: 'coverage_summary', kind: 'current_status', title: 'Coverage summary', view: 'filament.infolists.entries.baseline-snapshot-summary-table', viewData: ['rows' => $rendered->summaryRows], description: $rendered->fidelitySummary, emptyState: $factory->emptyState('No captured governed subjects are available in this snapshot.'), ), $factory->viewSection( id: 'related_context', kind: 'related_context', title: 'Related context', view: 'filament.infolists.entries.related-context', viewData: ['entries' => $relatedContext], emptyState: $factory->emptyState('No related context is available for this record.'), ), $factory->viewSection( id: 'captured_policy_types', kind: 'domain_detail', title: 'Captured governed subjects', view: 'filament.infolists.entries.baseline-snapshot-groups', viewData: ['groups' => array_map( static fn (RenderedSnapshotGroup $group): array => $group->toArray(), $rendered->groups, )], emptyState: $factory->emptyState('No snapshot items were captured for this baseline snapshot.'), ), ) ->addSupportingCard( $factory->supportingFactsCard( kind: 'status', title: 'Snapshot status', items: [ $factory->keyFact('Outcome', $primaryOutcomeLabel, badge: $truthBadge), $factory->keyFact('Current truth', $currentTruth['label'], badge: $currentTruthBadge), $factory->keyFact('Lifecycle', $lifecycleSpec->label, badge: $lifecycleBadge), ], ), $factory->supportingFactsCard( kind: 'coverage', title: 'Coverage', items: [ $factory->keyFact('Overall fidelity', $fidelitySpec->label, badge: $fidelityBadge), $factory->keyFact('Fidelity mix', $rendered->fidelitySummary), $factory->keyFact('Evidence gaps', $rendered->overallGapCount), $factory->keyFact('Captured items', $capturedItemCount), ], ), $factory->supportingFactsCard( kind: 'timestamps', title: 'Capture timing', items: [ $factory->keyFact('Captured', $this->formatTimestamp($rendered->capturedAt)), $factory->keyFact('Completed', $this->formatTimestamp($snapshot->completed_at?->toIso8601String())), $factory->keyFact('Failed', $this->formatTimestamp($snapshot->failed_at?->toIso8601String())), $factory->keyFact('Identity hash', $rendered->snapshotIdentityHash), ], ), ) ->addTechnicalSection( $factory->technicalDetail( title: 'Technical detail', entries: [ $factory->keyFact('Identity hash', $rendered->snapshotIdentityHash), ], description: 'Technical payloads are secondary on purpose. Use them for debugging capture fidelity and renderer fallbacks.', view: 'filament.infolists.entries.baseline-snapshot-technical-detail', viewData: ['technical' => $rendered->technicalDetail], ), ) ->build(); } /** * @param Collection $items */ private function presentGroup(string $policyType, Collection $items): RenderedSnapshotGroup { $renderer = $this->registry->rendererFor($policyType); $fallbackRenderer = $this->registry->fallbackRenderer(); $renderingError = null; $subjectDescriptor = $this->subjectDescriptor($policyType); $technicalPayload = $this->technicalPayload($items) + ['subject_descriptor' => $subjectDescriptor]; try { $renderedItems = $items ->map(fn (BaselineSnapshotItem $item): RenderedSnapshotItem => $renderer->render($item)) ->all(); } catch (Throwable) { $renderedItems = $items ->map(fn (BaselineSnapshotItem $item): RenderedSnapshotItem => $fallbackRenderer->render($item)) ->all(); $renderingError = 'Structured rendering failed for this governed subject family. Fallback metadata is shown instead.'; } /** @var array $renderedItems */ $groupFidelity = FidelityState::aggregate(array_map( static fn (RenderedSnapshotItem $item): FidelityState => $item->fidelity, $renderedItems, )); $gapSummary = GapSummary::merge(array_map( static fn (RenderedSnapshotItem $item): GapSummary => $item->gapSummary, $renderedItems, )); $capturedAt = collect($renderedItems) ->pluck('observedAt') ->filter(static fn (mixed $value): bool => is_string($value) && trim($value) !== '') ->sortDesc() ->first(); $coverageHint = $groupFidelity->coverageHint(); if ($coverageHint === null && $gapSummary->messages !== []) { $coverageHint = $gapSummary->messages[0]; } return new RenderedSnapshotGroup( policyType: $policyType, label: $this->typeLabel($policyType), itemCount: $items->count(), fidelity: $groupFidelity, gapSummary: $gapSummary, initiallyCollapsed: true, items: $renderedItems, renderingError: $renderingError, coverageHint: $coverageHint, capturedAt: is_string($capturedAt) ? $capturedAt : null, technicalPayload: $technicalPayload, subjectDescriptor: $subjectDescriptor, ); } /** * @param Collection $items * @return array */ private function technicalPayload(Collection $items): array { return [ 'items' => $items ->map(static fn (BaselineSnapshotItem $item): array => [ 'snapshot_item_id' => (int) $item->getKey(), 'policy_type' => (string) $item->policy_type, 'meta_jsonb' => is_array($item->meta_jsonb) ? $item->meta_jsonb : [], ]) ->all(), ]; } /** * @param array $summary */ private function summaryGapCount(array $summary): int { return $this->summaryGapSummary($summary)->count; } /** * @param array $summary */ private function fidelitySummary(array $summary): string { $counts = is_array($summary['fidelity_counts'] ?? null) ? $summary['fidelity_counts'] : []; $content = is_numeric($counts['content'] ?? null) ? (int) $counts['content'] : 0; $meta = is_numeric($counts['meta'] ?? null) ? (int) $counts['meta'] : 0; return sprintf( '%s %d, %s %d', BadgeCatalog::spec(BadgeDomain::BaselineSnapshotFidelity, FidelityState::Full->value)->label, $content, BadgeCatalog::spec(BadgeDomain::BaselineSnapshotFidelity, FidelityState::ReferenceOnly->value)->label, $meta, ); } /** * @param array $summary */ private function summaryGapSummary(array $summary): GapSummary { $gaps = is_array($summary['gaps'] ?? null) ? $summary['gaps'] : []; $byReason = is_array($gaps['by_reason'] ?? null) ? $gaps['by_reason'] : []; $gapSummary = GapSummary::fromReasonMap($byReason); if ($byReason !== [] || ! is_numeric($gaps['count'] ?? null) || (int) $gaps['count'] <= 0) { return $gapSummary; } return new GapSummary( count: (int) $gaps['count'], messages: ['Coverage gaps need review.'], ); } private function gapStatusSpec(int $gapCount): \App\Support\Badges\BadgeSpec { return BadgeRenderer::spec( BadgeDomain::BaselineSnapshotGapStatus, $gapCount > 0 ? 'gaps_present' : 'clear', ); } /** * @return array{label: string, color: string, icon: string, iconColor: string} */ private function currentTruthPresentation(ArtifactTruthEnvelope $truth): array { return match ($truth->artifactExistence) { 'historical_only' => [ 'label' => 'Historical trace', 'color' => 'gray', 'icon' => 'heroicon-m-clock', 'iconColor' => 'gray', ], 'created_but_not_usable' => [ 'label' => 'Not compare input', 'color' => 'warning', 'icon' => 'heroicon-m-exclamation-triangle', 'iconColor' => 'warning', ], default => [ 'label' => 'Current baseline', 'color' => 'success', 'icon' => 'heroicon-m-check-badge', 'iconColor' => 'success', ], }; } private function typeLabel(string $policyType): string { return (string) (data_get($this->subjectDescriptor($policyType), 'display_label') ?? InventoryPolicyTypeMeta::baselineCompareLabel($policyType) ?? InventoryPolicyTypeMeta::label($policyType) ?? Str::headline($policyType)); } /** * @return array */ private function subjectDescriptor(string $policyType): array { static $cache = []; if (array_key_exists($policyType, $cache)) { return $cache[$policyType]; } $result = app(PlatformSubjectDescriptorNormalizer::class)->fromArray([ 'policy_type' => $policyType, ], 'baseline_snapshot'); return $cache[$policyType] = $result->descriptor->toArray(); } private function formatTimestamp(?string $value): string { if ($value === null || trim($value) === '') { return '—'; } try { return Carbon::parse($value)->toDayDateTimeString(); } catch (Throwable) { return $value; } } }