0]; } function curl_setopt_array(\stdClass $handle, array $options): bool { $handle->options = $options; return true; } function curl_exec(\stdClass $handle): string|bool { $url = $handle->options[CURLOPT_URL]; if (str_ends_with($url, '/stop')) { ++$GLOBALS['readinessStops']; return '{"result":"success"}'; } ++$GLOBALS['readinessSends']; $GLOBALS['readinessReady'] = false; if ($GLOBALS['readinessMode'] === 'blocking') { $handle->httpCode = 404; return '{"code":"not_found","message":"Not Found"}'; } $header = $handle->options[CURLOPT_HEADERFUNCTION]; $write = $handle->options[CURLOPT_WRITEFUNCTION]; $header($handle, "HTTP/1.1 200 OK\r\n"); $write($handle, "data: {\"event\":\"workflow_started\",\"task_id\":\"synthetic-unconfirmed\"}\n\n"); return true; // EOF without message_end/workflow terminal; never a real network call. } function curl_errno(\stdClass $handle): int { return $handle->errno; } function curl_getinfo(\stdClass $handle, int $option): int { return $handle->httpCode ?? 200; } function curl_close(\stdClass $handle): void {} } namespace { require dirname(__DIR__) . '/vendor/autoload.php'; use app\common\service\DifyChatService; use app\adminapi\service\AssistantRequestControl; $assertions = 0; function readinessCheck(bool $ok, string $message): void { ++$GLOBALS['assertions']; if (!$ok) throw new RuntimeException($message); } $readinessConfig = ['enable' => true, 'base_url' => 'https://synthetic.invalid/v1', 'timeout' => 5, 'models' => ['qwen' => ['name' => 'synthetic-qwen', 'api_key' => 'offline-only'], 'openai' => ['name' => 'synthetic-openai', 'api_key' => 'offline-only']]]; $directory = sys_get_temp_dir() . '/zyt-readiness-' . bin2hex(random_bytes(8)); try { foreach (['qwen', 'openai'] as $profile) { foreach (['stream', 'blocking'] as $readinessMode) { $readinessSends = 0; $readinessReady = true; $readinessStops = 0; $control = new AssistantRequestControl(7, 11, '12345678-1234-4234-8234-' . sprintf('%012d', $assertions), $directory); readinessCheck($control->begin(), 'isolated control begins'); $options = ['knowledge_mode' => 'evidence', 'can_start_attempt' => static fn (): bool => $GLOBALS['readinessReady'] && $control->canStartAttempt(), 'on_task' => static function (string $task) use ($control, $profile): void { $control->bindTask($profile, 'synthetic-user', $task); }, 'on_transfer_end' => static function (string $task) use ($control): void { $control->clearTask($task); }]; $result = $readinessMode === 'stream' ? DifyChatService::streamChat($profile, [], 'synthetic', 'synthetic-user', static fn ($text): bool => true, null, [], $options) : DifyChatService::chat($profile, [], 'synthetic', 'synthetic-user', [], $options); readinessCheck(($result['error_code'] ?? '') === 'UPSTREAM_CLEANUP_PENDING' && $readinessSends === 1, 'An internal protocol retry cannot start another generation while cleanup is pending.'); readinessCheck(DifyChatService::stopTask($profile, 'synthetic-unconfirmed', 'synthetic-user') && $readinessStops === 1, 'The generation gate never blocks the stop endpoint.'); if ($readinessMode === 'stream') readinessCheck(!$control->canStartAttempt(), 'EOF never clears an unconfirmed remote identity.'); } $control = new AssistantRequestControl(7, 11, '22345678-1234-4234-8234-' . sprintf('%012d', $assertions), $directory); readinessCheck($control->begin(), 'receipt control begins'); $control->bindTask($profile, 'synthetic-user', 'old-task'); $control->signalValidationFailure(); $control->markStop(true, 'old-task'); try { $control->bindTask($profile, 'synthetic-user', 'race-task'); } catch (RuntimeException $expected) {} $control->markTerminal('old-task', 'stopped'); $control->clearTask('old-task'); $state = $control->publicState(); readinessCheck(!$state['upstream_terminal_confirmed'] && !$state['upstream_stop_confirmed'] && $state['upstream_pending_task_count'] === 1 && !$control->canStartAttempt(), 'A pending quarantined task cannot be hidden by a previous task terminal.'); readinessCheck(!str_contains(json_encode($state), 'race-task'), 'Public receipts never expose task identities.'); } echo "Dify per-attempt readiness / cleanup receipt: {$assertions} assertions passed (pure cURL stubs).\n"; } finally { foreach (glob($directory . '/*') ?: [] as $path) unlink($path); if (is_file($directory . '/.gc.lock')) unlink($directory . '/.gc.lock'); if (is_dir($directory)) rmdir($directory); } }