find_binding($inventory); $meta = [ 'inventory_refs' => $this->inventory_refs($inventory), 'knowledge_gap_reasons' => [], 'freshness_errors' => 0, 'prerequisite_contract_errors' => 0, ]; if ($binding === null) { $meta['knowledge_gap_reasons'][] = 'core_loopback_evidence_missing'; return $this->unknown($meta, [], 'evaluation_error'); } $state = (string) ($binding['state'] ?? 'evaluation_error'); $refs = [(string) ($binding['id'] ?? '')]; if ($state === 'not_applicable') { return $this->not_applicable( $meta, $refs, trim((string) ($binding['provenance']['reason'] ?? 'core_loopback_not_applicable')) ?: 'core_loopback_not_applicable' ); } if (!in_array($state, ['evaluated', 'unobservable', 'evaluation_error'], true)) { $meta['knowledge_gap_reasons'][] = 'core_loopback_evidence_state_invalid'; return $this->unknown($meta, $refs, 'evaluation_error'); } if ($state !== 'evaluated') { $reason = trim((string) ($binding['provenance']['reason'] ?? '')); $meta['knowledge_gap_reasons'][] = $reason !== '' ? $reason : 'core_loopback_observation_unavailable'; return $this->unknown($meta, $refs, $state); } $valid_until = $binding['valid_until'] ?? null; if (!is_int($valid_until) || $valid_until < $review_observed_at) { $meta['freshness_errors']++; $meta['knowledge_gap_reasons'][] = 'core_loopback_observation_stale'; return $this->unknown($meta, $refs, 'unobservable'); } $value = is_array($binding['value'] ?? null) ? $binding['value'] : null; $validator = self::evidence_value_validators()[AIFQ_SR_Next_Core_Loopback_Evidence_Producer::KEY_RESULT] ?? null; if ($value === null || !is_callable($validator) || !$validator($value)) { $meta['knowledge_gap_reasons'][] = 'core_loopback_evidence_value_invalid'; return $this->unknown($meta, $refs, 'evaluation_error'); } $observed_at = $binding['observed_at'] ?? null; if (!is_int($observed_at) || $observed_at <= 0 || $observed_at > $review_observed_at) { $meta['knowledge_gap_reasons'][] = 'core_loopback_observed_at_invalid'; return $this->unknown($meta, $refs, 'evaluation_error'); } $status = (string) $value['core_status']; $base = [ 'evaluation' => 'evaluated', 'applicability' => 'applicable', 'test_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::TEST_ID, 'core_status' => $status, 'provider_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::PROVIDER_ID, 'observed_at' => $observed_at, 'currentness_deadline' => $valid_until, 'evidence_refs' => $refs, ]; if ($status === 'critical') { return $this->result($meta, array_merge($base, [ 'health_claim' => 'unhealthy', 'detection_ids' => [self::DETECTION_CRITICAL], 'dominant_reason' => self::REASON_CRITICAL, ])); } if ($status === 'recommended') { return $this->result($meta, array_merge($base, [ 'health_claim' => 'unhealthy', 'detection_ids' => [self::DETECTION_RECOMMENDED], 'dominant_reason' => self::REASON_RECOMMENDED, ])); } return $this->result($meta, array_merge($base, [ 'health_claim' => 'healthy', 'detection_ids' => [], 'dominant_reason' => null, ])); } private function not_applicable(array $meta, array $refs, string $reason): array { return $this->result($meta, [ 'evaluation' => 'evaluated', 'applicability' => 'not_applicable', 'health_claim' => 'not_applicable', 'detection_ids' => [], 'dominant_reason' => null, 'test_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::TEST_ID, 'core_status' => null, 'provider_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::PROVIDER_ID, 'observed_at' => null, 'currentness_deadline' => null, 'not_applicable_reason' => $reason, 'evidence_refs' => $refs, ]); } private function unknown(array $meta, array $refs, string $evaluation): array { return $this->result($meta, [ 'evaluation' => $evaluation, 'applicability' => 'applicable', 'health_claim' => 'unknown', 'detection_ids' => [], 'dominant_reason' => null, 'test_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::TEST_ID, 'core_status' => null, 'provider_id' => AIFQ_SR_Next_Core_Loopback_Evidence_Producer::PROVIDER_ID, 'observed_at' => null, 'currentness_deadline' => null, 'not_applicable_reason' => null, 'evidence_refs' => $refs, ]); } private function result(array $meta, array $result): array { $result['situation_id'] = self::SITUATION_ID; $result['definition_version'] = self::DEFINITION_VERSION; $result['evidence_inventory_refs'] = $meta['inventory_refs']; $result['knowledge_gap_reasons'] = array_values(array_unique($meta['knowledge_gap_reasons'])); $result['freshness_errors'] = (int) $meta['freshness_errors']; $result['prerequisite_contract_errors'] = (int) $meta['prerequisite_contract_errors']; if (!array_key_exists('not_applicable_reason', $result)) { $result['not_applicable_reason'] = null; } return $result; } private function find_binding(array $inventory): ?array { $match = null; foreach ($inventory as $evidence) { if ( !is_array($evidence) || ($evidence['evidence_type'] ?? 'observation') !== 'observation' || ($evidence['key'] ?? '') !== AIFQ_SR_Next_Core_Loopback_Evidence_Producer::KEY_RESULT || ($evidence['provider_id'] ?? '') !== AIFQ_SR_Next_Core_Loopback_Evidence_Producer::PROVIDER_ID || ($evidence['perspective'] ?? '') !== 'origin_network' || ($evidence['independent_external_vantage'] ?? null) !== false ) { continue; } if ($match !== null) { throw new RuntimeException('Duplicate Core loopback Evidence binding.'); } $match = $evidence; } return $match; } private function inventory_refs(array $inventory): array { $refs = []; foreach ($inventory as $evidence) { if (!is_array($evidence)) { continue; } $id = trim((string) ($evidence['id'] ?? '')); if ($id !== '' && !in_array($id, $refs, true)) { $refs[] = $id; } } return $refs; } }