map(fn (\SplFileInfo $file): string => str_replace($root.'/', '', $file->getPathname())) ->filter(fn (string $relativePath): bool => str_starts_with($relativePath, 'Support/Ai/') || $relativePath === 'Support/ProductKnowledge/ContextualHelpResolver.php' || $relativePath === 'Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php') ->values(); $patterns = [ 'outbound_http' => '/\bHttp::/', 'guzzle_client' => '/\bnew\s+Client\b/', 'curl_runtime' => '/\bcurl_/i', 'openai_vendor' => '/\bOpenAI\b/i', 'anthropic_vendor' => '/\bAnthropic\b/i', 'gemini_vendor' => '/\bGemini\b/i', 'openrouter_vendor' => '/\bOpenRouter\b/i', 'chat_completions_runtime' => '/\bChatCompletion\b/i', ]; $hits = []; foreach ($files as $relativePath) { $contents = file_get_contents($root.'/'.$relativePath); if (! is_string($contents) || $contents === '') { continue; } $lines = preg_split('/\R/', $contents) ?: []; foreach ($patterns as $label => $pattern) { foreach ($lines as $index => $line) { if (preg_match($pattern, $line) === 1) { $hits[] = $relativePath.':'.($index + 1).' ['.$label.'] '.trim($line); } } } } expect($hits)->toBeEmpty("AI governance surfaces must stay vendor-neutral and must not perform outbound provider runtime calls directly:\n".implode("\n", $hits)); });