58 lines
3.7 KiB
PHP
58 lines
3.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
// Independent real-clock composition; synthetic control only, no app/bootstrap/network.
|
|
$root = dirname(__DIR__, 2);
|
|
require $root . '/server/app/adminapi/service/AssistantRequestControl.php';
|
|
require $root . '/app/artifacts/ai_concurrency/private_tcm_r26_wire_audit.php';
|
|
require $root . '/app/artifacts/ai_concurrency/private_tcm_r26_run_input_inspector.php';
|
|
$directory = sys_get_temp_dir() . '/zyt-r26-real-clock-' . bin2hex(random_bytes(8));
|
|
mkdir($directory, 0700);
|
|
$checks = 0; $failures = [];
|
|
$expect = static function (bool $value, string $label) use (&$checks, &$failures): void {
|
|
++$checks; if (!$value) $failures[] = $label;
|
|
};
|
|
try {
|
|
$control = new \app\adminapi\service\AssistantRequestControl(90001, 11, '33333333-3333-4333-8333-333333333333', $directory);
|
|
$expect($control->begin(), 'synthetic control starts');
|
|
$submission = $control->beginSubmission('qwen', 'synthetic-clock-user');
|
|
$control->bindTask('qwen', 'synthetic-clock-user', 'synthetic-clock-task', 'synthetic-clock-run', $submission);
|
|
$control->markTerminal('synthetic-clock-task', 'stopped', 'synthetic-clock-run');
|
|
$lookups = 0;
|
|
$result = $control->closeSubmittedGeneration($submission, 'synthetic-clock-task', 'unconfirmed',
|
|
static function (array $identity, int $milliseconds) use (&$lookups): array {
|
|
++$lookups;
|
|
return ['ok' => true, 'workflow_run_id' => $identity['workflow_run_id'], 'status' => 'stopped'];
|
|
});
|
|
$expect(($result['confirmed'] ?? false) === true && $lookups === 1, 'real control independently confirms one synthetic exact lookup');
|
|
$files = glob($directory . '/*.json');
|
|
$state = json_decode(file_get_contents($files[0]), true, 64, JSON_THROW_ON_ERROR);
|
|
$record = array_values($state['completed_generations'])[0];
|
|
$helper = (new ReflectionClass(PrivateTcmR26WireAudit::class))->newInstanceWithoutConstructor();
|
|
$snapshot = (new ReflectionMethod(PrivateTcmR26WireAudit::class, 'snapshot'))->invoke($helper, $record, true);
|
|
$expect(is_array($snapshot) && $snapshot['independently_terminal'] === true, 'actual helper captures real control terminal record');
|
|
$validate = new ReflectionMethod(\PrivateTcmR26\RunInputInspector::class, 'lifecycle');
|
|
$bound = ['closed' => true, 'snapshot' => null];
|
|
$expect($validate->invoke(null, $snapshot, $bound) === true,
|
|
'real monotonic verification clock and wall observation clock must not be directly compared');
|
|
// Positive witness isolates the cross-domain comparisons, not a production mutation.
|
|
$sameClock = $snapshot;
|
|
$sameClock['verification_started_at'] = $sameClock['transfer_closed_at'];
|
|
$expect($validate->invoke(null, $sameClock, $bound) === true, 'otherwise identical same-clock synthetic witness accepted');
|
|
echo json_encode(['checks' => $checks, 'failed' => count($failures), 'failures' => $failures,
|
|
'real_control_snapshot_accepted' => $validate->invoke(null, $snapshot, $bound),
|
|
'wall_and_monotonic_origins_differ' => abs($snapshot['transfer_closed_at'] - $snapshot['verification_started_at']) > 1000,
|
|
'actual_network_or_model_calls' => 0], JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR) . "\n";
|
|
} finally {
|
|
$base = realpath($directory);
|
|
if ($base === false || !str_starts_with(basename($base), 'zyt-r26-real-clock-')) throw new RuntimeException('Synthetic cleanup boundary unavailable');
|
|
foreach (glob($directory . '/*') as $file) {
|
|
if (dirname(realpath($file)) !== $base || is_dir($file)) throw new RuntimeException('Synthetic cleanup boundary mismatch');
|
|
unlink($file);
|
|
}
|
|
$lock = $directory . '/.gc.lock';
|
|
if (is_file($lock)) unlink($lock);
|
|
rmdir($directory);
|
|
}
|
|
exit($failures === [] ? 0 : 1);
|