'e7a43edad2c4c64ba968d52b991b7f0b3dba12de0165a01062cf9be2bcd3ee7d', 'server/app/common/service/DifyChatService.php' => 'af60ea942e57e02e712ae2ed8e1ff4724a2c4aa52d253a81f0a4351179ded028', 'server/app/adminapi/controller/tcm/DiagnosisController.php' => '83d47bf0ffb47c0f06a0cd69db910d28c0e790ccbfb5e5d3e5c931bf84f72568', 'server/app/common/service/DifyWorkflowRunStatus.php' => 'eeb1a690d308f4c9ed38000ba76077167a7aeac83356d9d89bd137c5600375ec', ]; foreach ($pins as $path => $sha) $expect(hash_file('sha256', $root . '/' . $path) === $sha, 'exact reviewed source pin before ' . $path); $directory = sys_get_temp_dir() . '/r26-independent-lifecycle-' . bin2hex(random_bytes(8)); $read = static fn (string $path): array => json_decode(file_get_contents($path), true, 64, JSON_THROW_ON_ERROR); $write = static fn (string $path, array $value): int|false => file_put_contents($path, json_encode($value, JSON_THROW_ON_ERROR)); $make = static function (string $run = 'private-independent-run') use (&$number, $directory, $expect): array { $GLOBALS['r26iNs'] += 10000000000; $uuid = sprintf('f3456789-1234-4234-8234-%012d', ++$number); $task = 'private-independent-task-' . $number; $c = new Control(17, 11, $uuid, $directory); $expect($c->begin(), 'fresh isolated request begins'); $c->bindTask('qwen', 'private-independent-user', $task, $run); return [$c, $task, $directory . '/' . hash('sha256', '17:11:' . $uuid) . '.json', $uuid]; }; $reply = static fn (array $id, string $status): array => ['ok' => true, 'workflow_run_id' => $id['workflow_run_id'], 'status' => $status]; $privacy = static function (Control $c) use ($expect): void { $p = $c->publicState(); $encoded = json_encode($p, JSON_THROW_ON_ERROR); foreach (['private-independent-', 'f3456789-', 'raw-provider-secret'] as $sentinel) $expect(!str_contains($encoded, $sentinel), 'safe state excludes private identity/body/exception sentinel'); foreach (['producer_terminal_observed_at', 'persisted_run_observed_at', 'stop_accepted_at', 'generation_transfer_closed_at'] as $key) $expect($p[$key] === null || ((is_float($p[$key]) || is_int($p[$key])) && is_finite((float) $p[$key]) && $p[$key] >= 0), 'safe observation time is finite or null ' . $key); }; $unlocked = static function (string $path) use ($directory, $expect): void { foreach ([$directory . '/.gc.lock', $path] as $file) { $lock = fopen($file, 'c+'); $held = flock($lock, LOCK_EX | LOCK_NB); $expect($held, 'neither GC nor request file lock held across injected I/O'); if ($held) flock($lock, LOCK_UN); fclose($lock); } }; try { foreach (['stopped', 'succeeded', 'failed'] as $producer) foreach (['stopped', 'succeeded', 'failed'] as $persisted) { [$c, $task, $path] = $make(); $c->markTerminal($task, $producer, 'private-independent-run'); $expect(!$c->canStartAttempt() && !$c->publicState()['upstream_terminal_confirmed'], 'producer enum alone never releases gate'); $calls = 0; $r = $c->closeGeneration($task, static function (array $id, int $ms) use (&$calls, $reply, $persisted, $unlocked, $path, $expect): array { ++$calls; $unlocked($path); $expect($ms > 0 && $ms <= 1000, 'per-generation I/O timeout remains bounded'); return $reply($id, $persisted); }); $s = $read($path); $a = array_values($s['completed_generations'] ?? [])[0] ?? []; $expect($r['confirmed'] && $calls === 1 && $c->canStartAttempt(), 'exact independently terminal enum permits eligible handoff'); $expect(($a['producer_terminal_status'] ?? null) === $producer && ($a['persisted_run_status'] ?? null) === $persisted, 'archive keeps both terminal sources even when enums differ'); $expect(($a['run_status_observations'][0]['status'] ?? null) === $persisted && ($a['run_status_observations'][0]['within_budget'] ?? false) === true, 'private archive retains exact within-budget status history'); $expect($c->publicState()['lifecycle_conflict'] === ($producer !== $persisted), 'terminal disagreement reported without terminal-state override'); $expect(($a['upstream']['task_id'] ?? null) === $task && ($a['upstream']['workflow_run_id'] ?? null) === 'private-independent-run' && !isset($s['upstream']), 'completed pair retained privately while active binding is released'); $privacy($c); } // Strict boolean true is intentional. All other adapter shapes remain // negative, even with an exact run and terminal enum. foreach (['missing', null, false, 0, 1, '', 'true', [], (object) ['value' => true]] as $numberOk => $ok) { [$c, $task, $path] = $make(); $c->markTerminal($task, 'stopped', 'private-independent-run'); $calls = 0; $r = $c->closeGeneration($task, static function (array $id) use ($ok, &$calls, $reply): array { ++$calls; $value = $reply($id, 'succeeded'); if ($ok === 'missing') unset($value['ok']); else $value['ok'] = $ok; return $value; }); $s = $read($path); $expect(!$r['confirmed'] && $r['pending'] && $calls === 2 && !$c->canStartAttempt(), 'non-true or missing ok fails closed ' . $numberOk); $expect(!isset($s['persisted_run_status']) && !isset($s['completed_generations']) && isset($s['upstream']), 'invalid ok creates no trusted observation/archive ' . $numberOk); $privacy($c); } [$c, $task, $path, $uuid] = $make(); $c->signalValidationFailure(); $stopCalls = 0; $c->stopBoundTask(static function (array $id) use ($path, $unlocked, &$stopCalls): bool { ++$stopCalls; $unlocked($path); return true; }); $c->markTerminal($task, 'stopped', 'private-independent-run'); $calls = 0; $running = static function (array $id) use (&$calls, $path, $unlocked, $c, $uuid, $directory, $expect, $reply): array { ++$calls; $unlocked($path); $peer = new Control(17, 11, $uuid, $directory); $duplicate = 0; $r = $peer->reconcileTerminal($id['task_id'], static function () use (&$duplicate): array { ++$duplicate; return []; }); $expect($duplicate === 0 && in_array($r['reason'], ['verification_in_progress', 'attempts_exhausted'], true), 'shared claim prevents cross-worker duplicate I/O'); $expect(!$c->canStartAttempt(), 'handoff remains blocked during status I/O'); return $reply($id, 'running'); }; $c->closeGeneration($task, $running); $s = $read($path); $started = $s['verification_started_at']; $expect($calls === 2 && $stopCalls === 1 && $c->publicState()['upstream_pending_task_count'] === 1 && $c->publicState()['persisted_run_status'] === 'running' && $c->publicState()['lifecycle_conflict'], 'actual stopped/running shape is pending and blocks successor'); $c->closeGeneration($task, $running); $c->finish('error'); $c->reconcileClosedTasks($running); $expect($calls === 2 && $read($path)['verification_started_at'] === $started && count($read($path)['run_status_observations']) === 2, 'EOF and final recovery share original attempts/start with bounded history'); $s = $read($path); $s['expires_at'] = time() - 1200; $write($path, $s); touch($path, time() - 1200); $peer = new Control(17, 11, $uuid, $directory); $expect(is_file($path) && $peer->publicState()['upstream_pending_task_count'] === 1 && !$peer->begin(), 'TTL GC does not erase unresolved producer receipt or restart UUID'); $privacy($peer); // Previous running is retained after a later differing terminal. A new // generation cannot reuse a completed task or run to renew its budget. [$c, $task, $path] = $make(); $c->markTerminal($task, 'stopped', 'private-independent-run'); $calls = 0; $c->closeGeneration($task, static function (array $id) use (&$calls, $reply): array { return $reply($id, ++$calls === 1 ? 'running' : 'failed'); }); $a = array_values($read($path)['completed_generations'])[0]; $expect(array_column($a['run_status_observations'], 'status') === ['running', 'failed'] && $a['producer_terminal_status'] === 'stopped', 'running and differing terminal histories both survive archive'); foreach ([[$task, 'private-independent-new-run'], ['private-independent-new-task', 'private-independent-run']] as [$replayTask, $replayRun]) { $thrown = false; try { $c->bindTask('qwen', 'private-independent-user', $replayTask, $replayRun); } catch (RuntimeException $e) { $thrown = $e->getMessage() === 'UPSTREAM_TASK_IDENTITY_CONFLICT'; } $expect($thrown && count($read($path)['completed_generations']) === 1, 'completed task or run cannot be replayed for a fresh budget'); } $c->bindTask('qwen', 'private-independent-user', 'private-independent-next', 'private-independent-next-run'); $c->markTerminal($task, 'succeeded', 'private-independent-run'); $c->markStop(true, $task); $staleCalls = 0; $c->closeGeneration($task, static function () use (&$staleCalls): array { ++$staleCalls; return []; }); $s = $read($path); $expect($staleCalls === 0 && $s['upstream']['task_id'] === 'private-independent-next' && !isset($s['producer_terminal_status']) && empty($s['transfer_closed']) && !$c->canStartAttempt(), 'late archived callbacks cannot close or poison active successor'); $expect($c->publicState()['lifecycle_conflict'] && count($s['completed_generations']) === 1, 'archive conflict remains public-safe across later generation'); $privacy($c); // A stale exact response is not applied after claim/pair replacement. foreach (['claim', 'generation', 'profile', 'user', 'run'] as $changed) { [$c, $task, $path] = $make(); $r = $c->closeGeneration($task, static function (array $id) use ($changed, $path, $read, $write, $reply): array { $s = $read($path); if ($changed === 'claim') $s['verification_claim']['token'] = 'new-private-claim'; elseif ($changed === 'generation') ++$s['upstream']['generation']; else $s['upstream'][$changed === 'run' ? 'workflow_run_id' : $changed] = 'private-independent-replacement'; $write($path, $s); return $reply($id, 'succeeded'); }); $expect($r['reason'] === 'stale_result' && isset($read($path)['upstream']) && !isset($read($path)['persisted_run_status']), 'stale exact reply cannot apply after ownership replacement ' . $changed); } [$c, $task, $path] = $make(); $calls = 0; $r = $c->closeGeneration($task, static function (array $id) use ($reply, &$calls): array { ++$calls; $GLOBALS['r26iNs'] += 1000000001; return $reply($id, 'succeeded'); }); $s = $read($path); $expect(!$r['confirmed'] && $r['reason'] === 'budget_exhausted' && $calls === 1 && !$c->canStartAttempt(), 'late terminal observation cannot confirm or start generation'); $expect($s['persisted_run_status'] === 'succeeded' && $s['run_status_observations'][0]['within_budget'] === false && empty($s['persisted_terminal_confirmed']), 'late safe observation retained explicitly outside proof budget'); // Global final envelope narrows the later target's otherwise fresh // generation budget. It is not renewed by another final call. [$c, $task, $path] = $make(); try { $c->bindTask('qwen', 'private-independent-user', 'private-independent-handoff', 'private-independent-handoff-run'); } catch (RuntimeException $e) {} $c->finish('error'); $calls = 0; $timeouts = []; $r = $c->reconcileClosedTasks(static function (array $id, int $ms) use (&$calls, &$timeouts, $reply): array { ++$calls; $timeouts[] = $ms; $GLOBALS['r26iNs'] += ($calls <= 2 ? 600 : $ms) * 1000000; return $reply($id, $calls === 2 ? 'running' : 'succeeded'); }); $s = $read($path); $globalStart = $s['closed_verification_started_at']; $expect($calls === 3 && $timeouts === [1000, 1000, 550] && $r['elapsed_ms'] === 2000 && $r['budget_exhausted'], 'one global two-second recovery budget bounds all original pairs'); $expect($r['upstream_pending_task_count'] === 1 && count($s['completed_generations']) === 1, 'late second target stays pending while confirmed first remains archived'); $GLOBALS['r26iNs'] += 3000000000; $c->reconcileClosedTasks(static function () use (&$calls): array { ++$calls; return []; }); $expect($calls === 3 && $read($path)['closed_verification_started_at'] === $globalStart, 'whole recovery deadline and per-target attempts are never renewed'); foreach (['task_only', 'old_sse_flag'] as $kind) { [$c, $task, $path, $uuid] = $make($kind === 'task_only' ? '' : 'private-independent-run'); if ($kind === 'old_sse_flag') { $s = $read($path); $s['upstream_terminal_confirmed'] = true; $s['upstream_terminal_status'] = 'stopped'; $write($path, $s); } $c->markTerminal($task, 'stopped', $kind === 'task_only' ? '' : 'private-independent-run'); $c->clearTask($task); $s = $read($path); $s['expires_at'] = time() - 1200; $write($path, $s); touch($path, time() - 1200); $peer = new Control(17, 11, $uuid, $directory); $expect(isset($read($path)['upstream']) && !$peer->publicState()['upstream_terminal_confirmed'] && !$peer->begin(), 'legacy producer-only state survives expiry without independent proof ' . $kind); if ($kind === 'task_only') { $calls = 0; $r = $peer->closeGeneration($task, static function () use (&$calls): array { ++$calls; return []; }); $expect($calls === 0 && $r['reason'] === 'identity_unavailable', 'missing original run never guessed or queried'); } $privacy($peer); } foreach ($pins as $path => $sha) $expect(hash_file('sha256', $root . '/' . $path) === $sha, 'exact reviewed source pin after ' . $path); $expect(!class_exists('think\\App', false) && !class_exists('app\\common\\service\\DifyChatService', false), 'Control test never loads framework/provider/config'); echo json_encode(['scope' => 'independent r26 private lifecycle state adversarial review', 'assertions' => $checks, 'failures' => $failures, 'network_calls' => 0, 'model_calls' => 0], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR), "\n"; } finally { $real = realpath($directory); $temp = realpath(sys_get_temp_dir()); if ($real !== false) { if ($temp === false || !str_starts_with(str_replace('\\', '/', $real), rtrim(str_replace('\\', '/', $temp), '/') . '/r26-independent-lifecycle-')) throw new RuntimeException('Synthetic cleanup boundary'); foreach (new DirectoryIterator($real) as $entry) { if ($entry->isDot()) continue; if ($entry->isLink() || !$entry->isFile()) throw new RuntimeException('Unexpected synthetic cleanup entry'); unlink($entry->getPathname()); } rmdir($real); } } exit($failures === [] ? 0 : 1); }