, * sections: list> * } */ public function compose(EvidenceSnapshot $snapshot, ?TenantReview $review = null): array { $tenant = $snapshot->tenant; if ($tenant === null) { throw new \RuntimeException('Evidence snapshot tenant is required for review composition.'); } $sections = $this->sectionFactory->make($snapshot); $blockers = $this->readinessGate->blockersForSections($sections); $sectionStateCounts = $this->readinessGate->sectionStateCounts($sections); $completeness = $this->readinessGate->completenessForSections($sections); $status = $this->readinessGate->statusForSections($sections); $controlInterpretationSection = collect($sections) ->firstWhere('section_key', 'control_interpretation'); $operationsSection = collect($sections) ->firstWhere('section_key', 'operations_health'); if ($review instanceof TenantReview && $review->isPublished()) { $status = TenantReviewStatus::Published; } return [ 'fingerprint' => $this->fingerprint->forSnapshot($tenant, $snapshot), 'completeness_state' => $completeness->value, 'status' => $status->value, 'summary' => [ 'evidence_basis' => [ 'snapshot_id' => (int) $snapshot->getKey(), 'snapshot_fingerprint' => (string) $snapshot->fingerprint, 'snapshot_completeness_state' => (string) $snapshot->completeness_state, 'snapshot_generated_at' => $snapshot->generated_at?->toIso8601String(), ], 'section_count' => count($sections), 'section_state_counts' => $sectionStateCounts, 'publish_blockers' => $blockers, 'has_ready_export' => false, 'finding_count' => (int) data_get($sections, '0.summary_payload.finding_count', 0), 'finding_outcomes' => is_array(data_get($sections, '0.summary_payload.finding_outcomes')) ? data_get($sections, '0.summary_payload.finding_outcomes') : [], 'finding_report_buckets' => is_array(data_get($sections, '0.summary_payload.finding_report_buckets')) ? data_get($sections, '0.summary_payload.finding_report_buckets') : [], 'canonical_controls' => is_array(data_get($sections, '0.summary_payload.canonical_controls')) ? data_get($sections, '0.summary_payload.canonical_controls') : [], 'control_interpretation' => is_array(data_get($controlInterpretationSection, 'summary_payload')) ? array_merge( data_get($controlInterpretationSection, 'summary_payload'), [ 'controls' => is_array(data_get($controlInterpretationSection, 'render_payload.entries')) ? data_get($controlInterpretationSection, 'render_payload.entries') : [], ], ) : [], 'report_count' => 2, 'operation_count' => (int) data_get($operationsSection, 'summary_payload.operation_count', 0), 'highlights' => data_get($sections, '0.render_payload.highlights', []), 'recommended_next_actions' => data_get($sections, '0.render_payload.next_actions', []), 'last_composed_at' => now()->toIso8601String(), ], 'sections' => $sections, ]; } }