begin(), 'new synthetic request begins'); $task = 'synthetic-task-' . $number; $control->bindTask('qwen', 'synthetic-user', $task, $run); if ($closed) $control->finish('error'); return [$control, $directory . '/' . hash('sha256', '7:11:' . $uuid) . '.json', $task, $uuid]; }; $read = static fn (string $path): array => json_decode((string) file_get_contents($path), true, 512, JSON_THROW_ON_ERROR); $write = static function (string $path, array $state): void { file_put_contents($path, json_encode($state, JSON_THROW_ON_ERROR)); }; $blocked = static function (AssistantRequestControl $control, string $task, string $run = ''): void { try { $control->bindTask('qwen', 'synthetic-user', $task, $run); identityExpect(false, 'unexpected created task must remain blocked'); } catch (RuntimeException $e) { identityExpect($e->getMessage() === 'UPSTREAM_TASK_HANDOFF_BLOCKED', 'handoff remains blocked after recording identity'); } }; try { [$control, $path, $task] = $make('', false); $blocked($control, 'synthetic-handoff'); $control->signalCancel(); $generation = $read($path)['upstream']['generation']; $control->stopBoundTask(static function (array $snapshot) use ($control, $path, $task, $read, $generation): bool { $before = $read($path); $control->bindTask('qwen', 'synthetic-user', $task, 'synthetic-enriched-run'); $after = $read($path); identityExpect($after['upstream']['generation'] === $generation, 'run enrichment keeps the same generation'); foreach (['cancel_requested', 'stop_claim', 'stop_attempts', 'stop_retry_until', 'handoff_cleanup'] as $field) { identityExpect(($before[$field] ?? null) === ($after[$field] ?? null), 'run enrichment preserves ' . $field); } identityExpect($snapshot['task_id'] === $task, 'stop snapshot keeps original task'); return true; }, $task); $blocked($control, 'synthetic-handoff', 'synthetic-handoff-run'); $handoff = $read($path)['handoff_cleanup']['synthetic-handoff']; identityExpect($handoff['upstream']['workflow_run_id'] === 'synthetic-handoff-run' && $handoff['stop_attempts'] === 0 && $handoff['stop_requested'], 'existing handoff enriches without resetting cleanup intent'); $before = $read($path); $control->bindTask('qwen', 'synthetic-user', $task, 'synthetic-enriched-run'); $control->bindTask('qwen', 'synthetic-user', $task); identityExpect($read($path)['upstream'] === $before['upstream'], 'duplicate and task-only bind cannot erase the captured run'); identityExpect($read($path)['upstream_stop_confirmed'] && $read($path)['stop_attempts'] === 1 && $control->shouldAbort(), 'same-pair rebinding preserves accepted stop, attempts and cancellation'); foreach ([[$task, 'synthetic-conflict-run'], ['synthetic-handoff', 'synthetic-conflict-run']] as [$conflictTask, $conflictRun]) { try { $control->bindTask('qwen', 'synthetic-user', $conflictTask, $conflictRun); identityExpect(false, 'conflicting run must throw'); } catch (RuntimeException $e) { identityExpect($e->getMessage() === 'UPSTREAM_TASK_IDENTITY_CONFLICT', 'conflicting run fails closed'); } } identityExpect($read($path)['upstream'] === $before['upstream'] && $read($path)['handoff_cleanup'] === $before['handoff_cleanup'], 'conflicting run never overwrites either target'); foreach ([['openai', 'synthetic-user'], ['qwen', 'foreign-user']] as [$profile, $user]) { try { $control->bindTask($profile, $user, $task, 'synthetic-enriched-run'); identityExpect(false, 'same task under another authorization scope must throw'); } catch (RuntimeException $e) { identityExpect($e->getMessage() === 'UPSTREAM_TASK_IDENTITY_CONFLICT', 'task identity cannot migrate across profile/user scope'); } } $control->markTerminal($task, 'succeeded'); $control->markTerminal($task, 'succeeded', 'synthetic-wrong-run'); $control->markTerminal($task, 'running', 'synthetic-enriched-run'); $control->clearTask($task); identityExpect($control->publicState()['upstream_pending_task_count'] === 2, 'missing/wrong run and running status cannot release either target'); $control->markTerminal('synthetic-handoff', 'stopped', 'synthetic-handoff-run'); $control->clearTask('synthetic-handoff'); identityExpect($control->publicState()['upstream_pending_task_count'] === 1, 'handoff terminal never confirms primary'); $control->markTerminal($task, 'failed', 'synthetic-enriched-run'); $control->markTerminal($task, 'succeeded', 'synthetic-enriched-run'); identityExpect($control->publicState()['upstream_terminal_status'] === 'failed', 'first exact terminal status is immutable'); [$late, $latePath, $lateTask] = $make('', false); // A separate empty request models metadata discovered after timeout. $late->markTerminal($lateTask, 'stopped'); $late->clearTask($lateTask); $lateState = $read($latePath); unset($lateState['task_generation']); $write($latePath, $lateState); $late->signalTimeout(); $late->bindTask('qwen', 'synthetic-user', 'synthetic-late-first', 'synthetic-late-run'); identityExpect(($read($latePath)['upstream']['task_id'] ?? '') === 'synthetic-late-first' && $late->publicState()['timeout_reached'] && !$late->shouldAbort(), 'late first pair remains primary without user cancel'); foreach (['../invalid', 'https://invalid', str_repeat('x', 129)] as $invalid) { try { $late->bindTask('qwen', 'synthetic-user', 'synthetic-late-first', $invalid); identityExpect(false, 'invalid run must throw'); } catch (RuntimeException $e) { identityExpect($e->getMessage() === 'Invalid upstream task identity', 'bounded run identifier syntax rejects unsafe values'); } } [$strict, $strictPath, $strictTask] = $make('', false); $blocked($strict, 'synthetic-strict-handoff'); $strict->signalCancel(); $strict->stopBoundTask(static fn (): bool => true, $strictTask); $strictHash = hash_file('sha256', $strictPath); foreach (["synthetic-id\n", "synthetic-id\r\n", "\nsynthetic-id", "synthetic\nid", str_repeat('x', 128) . "\n"] as $malformed) { foreach (['task', 'primary_run', 'handoff_run'] as $field) { $rejected = false; try { $strict->bindTask('qwen', 'synthetic-user', $field === 'task' ? $malformed : ($field === 'primary_run' ? $strictTask : 'synthetic-strict-handoff'), $field === 'task' ? '' : $malformed); } catch (RuntimeException $e) { $rejected = $e->getMessage() === 'Invalid upstream task identity'; } identityExpect($rejected, $field . ': newline identifier fails strict whole-string validation'); identityExpect(hash_file('sha256', $strictPath) === $strictHash, $field . ': rejected identifier leaves primary/handoff, stop acceptance and cancellation byte-identical'); } } [$legacy, $legacyPath, $legacyTask] = $make(''); $legacyCalls = 0; $legacyResult = $legacy->reconcileTerminal($legacyTask, static function () use (&$legacyCalls): array { ++$legacyCalls; return ['workflow_run_id' => 'guessed', 'status' => 'stopped']; }); identityExpect($legacyCalls === 0 && $legacyResult['reason'] === 'identity_unavailable' && $legacy->publicState()['upstream_pending_task_count'] === 1, 'legacy missing-pair evidence cannot authorize independent lookup'); $legacyState = $read($legacyPath); $legacyState['expires_at'] = time() - 10; unset($legacyState['begun']); $write($legacyPath, $legacyState); touch($legacyPath, time() - 700); new AssistantRequestControl(7, 11, sprintf('19345678-1234-4234-8234-%012d', $number), $directory); identityExpect(is_file($legacyPath) && !$legacy->begin() && $legacy->publicState()['upstream_pending_task_count'] === 1, 'expired legacy missing-pair tombstone survives GC and blocks replay even without begun metadata'); $legacy->markTerminal($legacyTask, 'stopped', 'guessed'); identityExpect(!$legacy->publicState()['upstream_terminal_confirmed'], 'terminal cannot invent the legacy run binding'); $legacy->markTerminal($legacyTask, 'stopped'); identityExpect($legacy->publicState()['upstream_terminal_confirmed'], 'explicit legacy task-only live receipt stays compatible'); [$open, $openPath, $openTask] = $make('synthetic-open-run', false); $calls = 0; $openResult = $open->reconcileTerminal($openTask, static function () use (&$calls): array { ++$calls; return []; }); identityExpect($calls === 0 && $openResult['reason'] === 'transport_open', 'reconciliation cannot extend live-stream drain'); foreach (['succeeded', 'failed', 'stopped'] as $status) { [$exact, $exactPath, $exactTask] = $make('synthetic-exact-' . $status); $exact->signalTimeout(); $calls = 0; $result = $exact->reconcileTerminal($exactTask, static function (array $identity, int $timeoutMs) use ($exact, &$calls, $status): array { ++$calls; identityExpect($timeoutMs > 0 && $timeoutMs <= 1000 && count($identity) === 5, 'adapter receives a bounded exact private snapshot'); identityExpect($exact->publicState()['transport_closed'], 'adapter executes outside request and GC locks'); $nested = $exact->reconcileTerminal($identity['task_id'], static function (): array { identityExpect(false, 'active verifier cannot dispatch a duplicate lookup'); return []; }); identityExpect($nested['reason'] === 'verification_in_progress', 'distinct verifier lease prevents duplicate reads'); return $identity + ['status' => $status, 'response_body' => 'synthetic-secret-body']; }); $public = $exact->publicState(); identityExpect($calls === 1 && $result['confirmed'] && $public['upstream_pending_task_count'] === 0 && $public['status'] === 'error' && $public['timeout_reached'] && !$public['cancel_requested'], 'exact run terminal reconciles remote state without changing failed report intent'); identityExpect(!isset($read($exactPath)['upstream']) && $public['upstream_terminal_status'] === $status, 'authoritative exact terminal releases only the verified identity'); $exact->reconcileTerminal($exactTask, static function () use (&$calls): array { ++$calls; return []; }); identityExpect($calls === 1, 'released identity never triggers another read'); identityExpect(!str_contains((string) file_get_contents($exactPath), 'synthetic-secret-body'), 'adapter response body is never persisted'); } $badResponses = [ 'running' => ['status' => 'running'], 'unknown' => ['status' => 'complete'], 'cancelled' => ['status' => 'cancelled'], 'missing_status' => [], 'wrong_run' => ['workflow_run_id' => 'foreign-run', 'status' => 'stopped'], 'missing_run' => ['workflow_run_id' => null, 'status' => 'stopped'], 'wrong_task' => ['task_id' => 'foreign-task', 'status' => 'stopped'], 'wrong_user' => ['user' => 'foreign-user', 'status' => 'stopped'], 'wrong_profile' => ['profile' => 'openai', 'status' => 'stopped'], 'wrong_type' => ['profile' => [], 'status' => 'stopped'], 'explicit_failure' => ['ok' => false, 'status' => 'stopped'], 'malformed' => 'synthetic-untrusted-body', 'null' => null, ]; foreach ($badResponses as $label => $response) { [$bad, $badPath, $badTask] = $make('synthetic-bad-run'); $before = $read($badPath); $times = []; $result = $bad->reconcileTerminal($badTask, static function (array $identity) use ($response, &$times) { $times[] = hrtime(true) / 1e9; return is_array($response) ? $response + ['workflow_run_id' => $identity['workflow_run_id']] : $response; }); identityExpect(count($times) === 2 && $times[1] - $times[0] >= 0.249 && !$result['confirmed'] && $bad->publicState()['upstream_pending_task_count'] === 1, $label . ': invalid/nonterminal result stays pending under two spaced reads'); $beforeAttempts = $read($badPath)['verification_attempts']; $bad->reconcileTerminal($badTask, static function () use (&$times): array { $times[] = 0; return []; }); identityExpect(count($times) === 2 && $read($badPath)['verification_attempts'] === $beforeAttempts, $label . ': external invocation cannot reset read attempts'); if ($label === 'explicit_failure') { $after = $read($badPath); foreach (['upstream', 'status', 'transport_closed', 'upstream_stop_confirmed', 'upstream_terminal_confirmed'] as $field) { identityExpect(($after[$field] ?? null) === ($before[$field] ?? null), 'explicit adapter ok=false with an exact run and terminal status preserves ' . $field); } } } [$throwing, $throwingPath, $throwingTask] = $make('synthetic-throw-run'); $result = $throwing->reconcileTerminal($throwingTask, static function (): array { throw new RuntimeException('synthetic-secret-exception'); }); identityExpect(!$result['confirmed'] && $throwing->publicState()['upstream_pending_task_count'] === 1 && !str_contains((string) file_get_contents($throwingPath), 'synthetic-secret-exception'), 'callback exceptions fail closed without persisting error prose'); [$budget, $budgetPath, $budgetTask] = $make('synthetic-budget-run'); $timeouts = []; $result = $budget->reconcileTerminal($budgetTask, static function (array $identity, int $timeoutMs) use (&$timeouts): array { $timeouts[] = $timeoutMs; if (count($timeouts) === 1) { usleep(800000); return $identity + ['status' => 'running']; } return $identity + ['status' => 'succeeded']; }); identityExpect($result['confirmed'] && count($timeouts) === 2 && $timeouts[1] < 1000 && $timeouts[1] > 500, 'retry receives only remaining shared budget after first I/O and spacing'); [$slow, $slowPath, $slowTask] = $make('synthetic-slow-run'); $result = $slow->reconcileTerminal($slowTask, static function (array $identity): array { usleep(1020000); return $identity + ['status' => 'succeeded']; }); identityExpect(!$result['confirmed'] && $slow->publicState()['upstream_pending_task_count'] === 1, 'over-budget callback cannot apply an exact terminal'); $slowState = $read($slowPath); $slowState['verification_started_at'] = hrtime(true) / 1e9 - 3; $write($slowPath, $slowState); $calls = 0; $result = $slow->reconcileTerminal($slowTask, static function () use (&$calls): array { ++$calls; return []; }); identityExpect($calls === 0 && $result['reason'] === 'budget_exhausted', 'external invocation cannot renew an expired monotonic budget'); foreach (['generation', 'task_id', 'workflow_run_id', 'profile', 'user', 'token'] as $changed) { [$stale, $stalePath, $staleTask] = $make('synthetic-stale-run'); $result = $stale->reconcileTerminal($staleTask, static function (array $identity) use ($stalePath, $read, $write, $changed): array { $state = $read($stalePath); if ($changed === 'generation') ++$state['upstream']['generation']; elseif ($changed === 'token') $state['verification_claim']['token'] = 'replacement-token'; else $state['upstream'][$changed] = 'replacement'; $write($stalePath, $state); return $identity + ['status' => 'stopped']; }); identityExpect($result['reason'] === 'stale_result' && !$stale->publicState()['upstream_terminal_confirmed'], 'stale ' . $changed . ' callback cannot confirm a replacement identity/claim'); } [$abandoned, $abandonedPath, $abandonedTask] = $make('synthetic-abandoned-run'); $state = $read($abandonedPath); $now = hrtime(true) / 1e9; $state['verification_started_at'] = $now - 1.3; $state['verification_attempts'] = 1; $state['verification_claim'] = ['token' => 'abandoned-token', 'until' => $now - 0.05]; $write($abandonedPath, $state); $calls = 0; $result = $abandoned->reconcileTerminal($abandonedTask, static function (array $identity, int $timeoutMs) use (&$calls): array { ++$calls; identityExpect($timeoutMs <= 700 && $timeoutMs > 0, 'crashed verifier takeover inherits remaining budget'); return $identity + ['status' => 'stopped']; }); identityExpect($calls === 1 && $result['confirmed'] && $read($abandonedPath)['verification_attempts'] === 2, 'expired verification lease permits one remaining read without resetting attempts'); [$reboot, $rebootPath, $rebootTask] = $make('synthetic-clock-reset-run'); $state = $read($rebootPath); $state['verification_started_at'] = hrtime(true) / 1e9 + 100; $write($rebootPath, $state); $calls = 0; $result = $reboot->reconcileTerminal($rebootTask, static function () use (&$calls): array { ++$calls; return []; }); identityExpect($calls === 0 && $result['reason'] === 'budget_exhausted', 'monotonic clock reset fails closed without renewing budget'); [$handoffControl, $handoffPath, $primaryTask] = $make('synthetic-primary-run'); $blocked($handoffControl, 'synthetic-other-task', 'synthetic-other-run'); $state = $read($handoffPath); $state['stop_claim'] = ['token' => 'synthetic-stop-token', 'until' => microtime(true) + 1]; $state['stop_attempts'] = 1; $write($handoffPath, $state); $result = $handoffControl->reconcileTerminal('synthetic-other-task', static fn (array $identity): array => $identity + ['status' => 'stopped']); $state = $read($handoffPath); identityExpect($result['confirmed'] && $handoffControl->publicState()['upstream_pending_task_count'] === 1 && $state['upstream']['task_id'] === $primaryTask && $state['stop_claim']['token'] === 'synthetic-stop-token' && $state['stop_attempts'] === 1, 'handoff verification is isolated from primary identity and stop lease'); // Expired unresolved primary and handoff evidence must survive read/reset/GC/replay. foreach (['primary', 'handoff'] as $kind) { [$retained, $retainedPath, $retainedTask, $uuid] = $make('synthetic-retained-run'); if ($kind === 'handoff') { $blocked($retained, 'synthetic-retained-handoff', 'synthetic-retained-handoff-run'); $retained->markTerminal($retainedTask, 'stopped', 'synthetic-retained-run'); $retained->clearTask($retainedTask); } $state = $read($retainedPath); $state['expires_at'] = time() - 100; $write($retainedPath, $state); touch($retainedPath, time() - 700); $hash = hash_file('sha256', $retainedPath); $reloaded = new AssistantRequestControl(7, 11, $uuid, $directory); identityExpect(is_file($retainedPath) && $reloaded->publicState()['upstream_pending_task_count'] === 1 && hash_file('sha256', $retainedPath) === $hash, $kind . ': GC and read-only status retain expired evidence byte-for-byte'); identityExpect(!$reloaded->begin() && !$reloaded->canStartAttempt(), $kind . ': expired unresolved request cannot replay'); } foreach (['empty', 'confirmed'] as $kind) { [$expired, $expiredPath, $expiredTask, $uuid] = $make('synthetic-expired-run'); if ($kind === 'confirmed') $expired->markTerminal($expiredTask, 'stopped', 'synthetic-expired-run'); else { $state = $read($expiredPath); unset($state['upstream']); $write($expiredPath, $state); } $state = $read($expiredPath); $state['expires_at'] = time() - 100; $write($expiredPath, $state); touch($expiredPath, time() - 700); new AssistantRequestControl(7, 11, $uuid, $directory); identityExpect(!is_file($expiredPath), $kind . ': normal expired resolved/empty controls remain GC eligible'); } [$reset, $resetPath] = $make('synthetic-reset-run'); $reset->markTerminal('not-own-task', 'stopped', 'synthetic-reset-run'); $state = $read($resetPath); $state['upstream_terminal_confirmed'] = true; $state['expires_at'] = time() - 1; $write($resetPath, $state); identityExpect($reset->publicState()['status'] === 'pending' && $reset->begin(), 'normal expired resolved control still resets without waiting for GC'); $serialized = json_encode($handoffControl->publicState()) . json_encode($result); foreach (['synthetic-primary-run', 'synthetic-other-run', 'synthetic-user', $primaryTask, 'response_body', 'verification_claim'] as $private) { identityExpect(!str_contains($serialized, $private), 'public state/result excludes private identity or response: ' . $private); } $controller = (string) file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/tcm/DiagnosisController.php'); identityExpect(str_contains($controller, '$control->bindTask($profile, $upstreamUser, $taskId, $workflowRunId)') && str_contains($controller, '$control->markTerminal($taskId, $status, $workflowRunId)'), 'authorized controller forwards captured run on both callbacks'); echo 'Assistant request exact identity / finite reconciliation / retention: OK (' . $assertions . " assertions)\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); }