309 lines
27 KiB
PHP
309 lines
27 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
|
||
// NEW independent r25b derivative review; retains the original independent
|
||
// boundary controls and adds serialized-input/source-equivalence controls.
|
||
// In-process GET/config/clock stubs and handcrafted synthetic audit fixtures.
|
||
// Do not load the probe, application, original credentials or runtime audits.
|
||
namespace PrivateTcmR25b {
|
||
function hrtime(bool $number = false): int { return $GLOBALS['iiClock']; }
|
||
function curl_init(): object {
|
||
++$GLOBALS['iiCreated'];
|
||
$scenario = array_shift($GLOBALS['iiScenarios']);
|
||
if (!is_array($scenario)) throw new \RuntimeException('Unexpected synthetic GET');
|
||
return (object) ['scenario' => $scenario, 'options' => [], 'errno' => 0];
|
||
}
|
||
function curl_setopt_array(object $handle, array $options): bool {
|
||
$handle->options = $options; $GLOBALS['iiOptions'][] = $options;
|
||
$GLOBALS['iiClock'] += $handle->scenario['setup_ns'] ?? 0;
|
||
return ($handle->scenario['set_ok'] ?? true) === true;
|
||
}
|
||
function curl_exec(object $handle): bool {
|
||
++$GLOBALS['iiExecuted'];
|
||
$GLOBALS['iiClock'] += $handle->scenario['advance_ns'] ?? 0;
|
||
if ($handle->scenario['throw'] ?? false) throw new \RuntimeException('private-independent-provider-error');
|
||
foreach ($handle->scenario['chunks'] ?? [$handle->scenario['body']] as $chunk) {
|
||
if (($handle->options[CURLOPT_WRITEFUNCTION])($handle, $chunk) !== strlen($chunk)) {
|
||
$handle->errno = CURLE_WRITE_ERROR; return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
function curl_errno(object $handle): int { return $handle->errno; }
|
||
function curl_getinfo(object $handle, int $option): int { return $handle->scenario['status'] ?? 200; }
|
||
function curl_close(object $handle): void {
|
||
++$GLOBALS['iiClosed'];
|
||
$GLOBALS['iiClock'] += $handle->scenario['close_ns'] ?? 0;
|
||
}
|
||
}
|
||
namespace {
|
||
require dirname(__DIR__, 2) . '/app/artifacts/ai_concurrency/private_tcm_r25b_run_input_inspector.php';
|
||
use PrivateTcmR25b\RunInputInspector as Inspector;
|
||
|
||
$checks = 0; $iiClock = 10000000000; $iiCreated = 0; $iiClosed = 0; $iiExecuted = 0; $iiScenarios = []; $iiOptions = [];
|
||
$expect = static function (bool $value, string $label) use (&$checks): void { ++$checks; if (!$value) throw new RuntimeException($label); };
|
||
$json = static fn (mixed $value): string => json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);
|
||
$base = sys_get_temp_dir() . '/tcm-r25b-inspector-independent-' . bin2hex(random_bytes(8));
|
||
$root = $base . '/server'; $auditDir = $root . '/runtime/tcm-r25-wire-audits'; $resultDir = $base . '/app/artifacts/ai_concurrency';
|
||
mkdir($auditDir, 0700, true); mkdir($resultDir, 0700, true);
|
||
$number = 0;
|
||
$query = "independent-private-query/中文\nsecond-line\r\n";
|
||
$config = ['enable' => true, 'base_url' => 'http://independent.synthetic.invalid:8088/v1/chat-messages',
|
||
'models' => ['qwen' => ['api_key' => 'private-independent-qwen-key']]];
|
||
$fixtureHash = 'b54c47ec11f6a99dc87bdda78629e18c8a6f37a64017e42a72db399c727cb1c6';
|
||
$questionHash = '28fbaee8585d4fbde3be3b1a6ee046c1a60f18a1fe67c0769211b08afdd5d306';
|
||
$make = static function (int $count = 1) use (&$number, $query, $fixtureHash, $questionHash): array {
|
||
$id = sprintf('44444444-4444-4444-8444-%012d', ++$number);
|
||
$records = [['event' => 'request', 'contract' => 'v43r25_private_run_wire', 'request_id' => $id,
|
||
'request_label' => 'independent_row_qwen', 'profile' => 'qwen']];
|
||
for ($i = 1; $i <= $count; ++$i) {
|
||
$body = json_encode(['inputs' => (object) [], 'query' => $query], JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR);
|
||
$records[] = ['event' => 'serialized_request', 'generation' => $i, 'report_stage' => $i === 1 ? 1 : 2,
|
||
'stage_attempt' => $i <= 2 ? 1 : $i - 1, 'protocol' => 'dify', 'body_sha256' => hash('sha256', $body), 'body_bytes' => strlen($body),
|
||
'query_sha256' => hash('sha256', $query), 'query_bytes' => strlen($query),
|
||
'inputs_sha256' => hash('sha256', '{}'), 'inputs_bytes' => 2, 'report_continuation_query' => $i > 1];
|
||
$records[] = ['event' => 'original_identity', 'generation' => $i, 'original_task_id' => 'private-independent-task-' . $i, 'original_workflow_run_id' => ''];
|
||
$records[] = ['event' => 'original_identity', 'generation' => $i, 'original_task_id' => 'private-independent-task-' . $i, 'original_workflow_run_id' => 'private-independent-run-' . $i];
|
||
}
|
||
$records[] = ['event' => 'after_control_cleanup', 'state' => ['transport_closed' => true, 'upstream_terminal_confirmed' => true,
|
||
'upstream_stop_confirmed' => false, 'cancel_requested' => false, 'timeout_reached' => false, 'upstream_pending_task_count' => 0]];
|
||
return ['records' => $records, 'summary' => ['persisted' => true, 'serialized_request_count' => $count,
|
||
'original_task_count' => $count, 'original_run_count' => $count], 'id' => $id, 'number' => $number,
|
||
'fixture' => $fixtureHash, 'question' => $questionHash];
|
||
};
|
||
$save = static function (array $fixture) use ($auditDir, $resultDir, $json): array {
|
||
$content = implode("\n", array_map($json, $fixture['records'])) . "\n";
|
||
$summary = $fixture['summary'] + ['audit_bytes' => strlen($content), 'audit_sha256' => hash('sha256', $content)];
|
||
$auditPath = $auditDir . '/' . hash('sha256', $fixture['id']) . '.private.jsonl';
|
||
file_put_contents($auditPath, $content);
|
||
$row = ['test_label' => 'independent_row', 'profile' => 'qwen', 'private_wire_audit' => $summary,
|
||
'harness_contract' => 'v43r25_private_run_wire', 'synthetic' => true, 'preparation_isolated' => true,
|
||
'preparation_isolation_verified' => true, 'production_acceptance_path' => true, 'diagnostic_only' => false, 'case_id' => 11,
|
||
'production_hashes' => ['fixture' => $fixture['fixture'], 'question' => $fixture['question']]];
|
||
$document = ['harness_contract' => 'v43r25_private_run_wire', 'synthetic_only' => true, 'preparation_isolated' => true,
|
||
'fixture_sha256' => $fixture['fixture'], 'canonical_question_sha256' => $fixture['question'], 'rows' => [$row]];
|
||
$resultFile = 'tcm_complete_independent_inspector_' . $fixture['number'] . '.safe.json';
|
||
$resultPath = $resultDir . '/' . $resultFile; file_put_contents($resultPath, $json($document));
|
||
return [['result_file' => $resultFile, 'test_label' => 'independent_row', 'profile' => 'qwen', 'audit_sha256' => $summary['audit_sha256']], $auditPath, $resultPath];
|
||
};
|
||
$response = static fn (int $i = 1): array => ['id' => 'private-independent-run-' . $i, 'status' => 'succeeded',
|
||
'inputs' => ['sys.query' => $query], 'outputs' => ['secret' => 'private-independent-output']];
|
||
$call = static function (array $selector, array $scenarios, ?array $override = null) use ($root, $config): array {
|
||
$GLOBALS['iiScenarios'] = $scenarios; $configCalls = 0;
|
||
$value = Inspector::inspectResultRow($root, $selector, static function () use (&$configCalls, $config, $override): array { ++$configCalls; return $override ?? $config; });
|
||
return [$value, $configCalls];
|
||
};
|
||
$privacy = static function (array $value) use ($expect, $json, $query): void {
|
||
$text = $json($value);
|
||
foreach (['private-independent-', 'independent.synthetic', 'independent_row', '44444444-', $query] as $secret)
|
||
$expect(!str_contains($text, $secret), 'safe reply excludes private identity/body/config/selector text');
|
||
$expect(($value['model_calls'] ?? null) === 0 && ($value['llm_expanded_prompt_observed'] ?? null) === false, 'query evidence never becomes model/node visibility');
|
||
};
|
||
try {
|
||
[$selected, , $resultPath] = $save($make(2));
|
||
[$ok, $configured] = $call($selected, [['body' => $json($response())], ['body' => $json($response(2))]]);
|
||
$expect($ok['ok'] && $configured === 1 && $ok['inspected_run_count'] === 2 && $ok['matched_run_count'] === 2, 'full saved-row bridge selects exactly two original runs');
|
||
$expect($ok['result_file_sha256'] === hash_file('sha256', $resultPath) && $ok['audit_sha256'] === $selected['audit_sha256'], 'whole result and audit digests retained');
|
||
$privacy($ok);
|
||
foreach ($iiOptions as $options) {
|
||
$expect($options[CURLOPT_HTTPGET] === true && !isset($options[CURLOPT_POSTFIELDS]) && !isset($options[CURLOPT_POST]), 'GET only');
|
||
$expect($options[CURLOPT_PROTOCOLS] === CURLPROTO_HTTP && $options[CURLOPT_REDIR_PROTOCOLS] === CURLPROTO_HTTP
|
||
&& str_starts_with($options[CURLOPT_URL], 'http://independent.synthetic.invalid:8088/v1/workflows/run/'), 'exact configured HTTP scheme/host/port/path only');
|
||
$expect($options[CURLOPT_TIMEOUT_MS] > 0 && $options[CURLOPT_TIMEOUT_MS] <= 2000 && $options[CURLOPT_CONNECTTIMEOUT_MS] <= 2000
|
||
&& !$options[CURLOPT_FOLLOWLOCATION] && $options[CURLOPT_MAXREDIRS] === 0, 'bounded deadline and redirects disabled');
|
||
}
|
||
foreach (['open_transport', 'no_terminal', 'pending', 'pending_string', 'extra_footer', 'header_upper_uuid', 'header_neighbor_label',
|
||
'identity_downgrade', 'identity_other_task', 'identity_before_serialization', 'wrong_query_hash_type', 'stage_backwards', 'non_dify',
|
||
'summary_bool_count', 'summary_byte_mismatch', 'summary_run_mismatch', 'too_many_runs'] as $kind) {
|
||
$fixture = $make(in_array($kind, ['stage_backwards', 'too_many_runs'], true) ? 3 : 1);
|
||
$last = count($fixture['records']) - 1;
|
||
if ($kind === 'open_transport') $fixture['records'][$last]['state']['transport_closed'] = false;
|
||
if ($kind === 'no_terminal') $fixture['records'][$last]['state']['upstream_terminal_confirmed'] = false;
|
||
if ($kind === 'pending') $fixture['records'][$last]['state']['upstream_pending_task_count'] = 1;
|
||
if ($kind === 'pending_string') $fixture['records'][$last]['state']['upstream_pending_task_count'] = '0';
|
||
if ($kind === 'extra_footer') $fixture['records'][$last]['state']['raw_body'] = 'private-independent-injection';
|
||
if ($kind === 'header_upper_uuid') $fixture['records'][0]['request_id'] = strtoupper(str_replace('44444444', 'abcdefab', $fixture['id']));
|
||
if ($kind === 'header_neighbor_label') $fixture['records'][0]['request_label'] = 'neighbor_qwen';
|
||
if ($kind === 'identity_downgrade') $fixture['records'][3]['original_workflow_run_id'] = '';
|
||
if ($kind === 'identity_other_task') $fixture['records'][3]['original_task_id'] = 'private-independent-other-task';
|
||
if ($kind === 'identity_before_serialization') [$fixture['records'][1], $fixture['records'][2]] = [$fixture['records'][2], $fixture['records'][1]];
|
||
if ($kind === 'wrong_query_hash_type') $fixture['records'][1]['query_sha256'] = ['private-independent-injection'];
|
||
if ($kind === 'stage_backwards') { $fixture['records'][7]['report_stage'] = 1; $fixture['records'][7]['report_continuation_query'] = false; }
|
||
if ($kind === 'non_dify') $fixture['records'][1]['protocol'] = 'openai';
|
||
if ($kind === 'summary_bool_count') $fixture['summary']['original_task_count'] = true;
|
||
if ($kind === 'summary_byte_mismatch') $fixture['summary']['audit_bytes'] = 1;
|
||
if ($kind === 'summary_run_mismatch') $fixture['summary']['original_run_count'] = 0;
|
||
[$selector] = $save($fixture); $before = $iiExecuted;
|
||
[$value, $configured] = $call($selector, []);
|
||
$expect(!$value['ok'] && $configured === 0 && $iiExecuted === $before, 'invalid exact-own bridge fails before config/GET: ' . $kind);
|
||
$privacy($value);
|
||
}
|
||
foreach (['object_sys', 'lookalike_dot', 'trailing_space', 'wrong_case', 'array_inputs', 'null_query', 'different_crlf',
|
||
'different_unicode', 'wrong_byte_count', 'optional_null_run', 'optional_null_task', 'output_only', 'both_same', 'running_input_only'] as $kind) {
|
||
$fixture = $make(); $value = $response();
|
||
if ($kind === 'object_sys') $value['inputs'] = ['sys' => ['query' => $query]];
|
||
if ($kind === 'lookalike_dot') $value['inputs'] = ['sys.query' => $query];
|
||
if ($kind === 'trailing_space') $value['inputs'] = ['sys.query ' => $query];
|
||
if ($kind === 'wrong_case') $value['inputs'] = ['SYS.QUERY' => $query];
|
||
if ($kind === 'array_inputs') $value['inputs'] = [['sys.query' => $query]];
|
||
if ($kind === 'null_query') $value['inputs']['query'] = null;
|
||
if ($kind === 'different_crlf') $value['inputs']['sys.query'] = str_replace("\r\n", "\n", $query);
|
||
if ($kind === 'different_unicode') $value['inputs']['sys.query'] .= "\u{0301}";
|
||
if ($kind === 'wrong_byte_count') ++$fixture['records'][1]['query_bytes'];
|
||
if ($kind === 'optional_null_run') $value['workflow_run_id'] = null;
|
||
if ($kind === 'optional_null_task') $value['task_id'] = null;
|
||
if ($kind === 'output_only') { $value['inputs'] = (object) []; $value['outputs']['query'] = $query; }
|
||
if ($kind === 'both_same') $value['inputs']['query'] = $query;
|
||
if ($kind === 'running_input_only') $value['status'] = 'running';
|
||
[$selector] = $save($fixture); $before = $iiExecuted;
|
||
[$answer] = $call($selector, [['body' => $json($value)]]);
|
||
$expect($answer['ok'] === in_array($kind, ['both_same', 'running_input_only'], true) && $iiExecuted === $before + 1,
|
||
'only exact whitelisted byte-identical query evidence: ' . $kind);
|
||
if ($kind === 'running_input_only') $expect($answer['runs'][0]['run_status'] === 'running', 'matching query does not assert a fresh terminal or cleanup proof');
|
||
$privacy($answer);
|
||
}
|
||
[$selector] = $save($make(2)); $before = $iiExecuted;
|
||
[$answer] = $call($selector, [['body' => $json($response()), 'advance_ns' => 2000000000], ['body' => $json($response(2))]]);
|
||
$expect(!$answer['ok'] && $iiExecuted === $before + 2 && $answer['matched_run_count'] === 1
|
||
&& $answer['runs'][0]['status'] === 'run_timeout' && $answer['runs'][1]['query_matches_wire'], 'timeout plus successor uses exactly two original GETs and cannot hide first failure');
|
||
$privacy($answer);
|
||
foreach (['setup_deadline', 'setopt_failure', 'chunk_overflow', 'throw'] as $kind) {
|
||
[$selector] = $save($make()); $scenario = ['body' => $json($response())];
|
||
if ($kind === 'setup_deadline') $scenario['setup_ns'] = 2000000000;
|
||
if ($kind === 'setopt_failure') $scenario['set_ok'] = false;
|
||
if ($kind === 'chunk_overflow') $scenario['chunks'] = [str_repeat('x', 1048576), str_repeat('x', 1048576), 'x'];
|
||
if ($kind === 'throw') $scenario['throw'] = true;
|
||
$before = $iiExecuted; [$answer] = $call($selector, [$scenario]);
|
||
$expect(!$answer['ok'] && $iiExecuted === $before + (in_array($kind, ['setup_deadline', 'setopt_failure'], true) ? 0 : 1), 'bounded setup/write/error closes without retry: ' . $kind);
|
||
$privacy($answer);
|
||
}
|
||
// NEW r25b controls: retain all old independent expectations above.
|
||
// Handcrafted body metadata is recomputed for these distinct strings;
|
||
// no private audit writer is used to derive expected query values.
|
||
$queryFixture = static function (array $fixture, array $queries) use ($json): array {
|
||
foreach ($fixture['records'] as &$record) {
|
||
if ($record['event'] !== 'serialized_request') continue;
|
||
$actual = $queries[$record['generation'] - 1];
|
||
$body = $json(['inputs' => (object) [], 'query' => $actual]);
|
||
$record['query_sha256'] = hash('sha256', $actual); $record['query_bytes'] = strlen($actual);
|
||
$record['body_sha256'] = hash('sha256', $body); $record['body_bytes'] = strlen($body);
|
||
}
|
||
unset($record);
|
||
return $fixture;
|
||
};
|
||
$exact = "literal\\u660e\\n/中文 \"quote\" <tag>& é e\u{0301} 😀\r\nNUL:\0\t ";
|
||
foreach ([0, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES,
|
||
JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT, JSON_PRETTY_PRINT] as $number => $flags) {
|
||
[$selector] = $save($queryFixture($make(), [$exact]));
|
||
$value = $response(); $value['status'] = 'running';
|
||
$value['inputs'] = json_encode(['sys.query' => $exact, 'query' => $exact], $flags | JSON_THROW_ON_ERROR);
|
||
$before = $iiExecuted; [$answer] = $call($selector, [['body' => json_encode($value, $flags | JSON_THROW_ON_ERROR)]]);
|
||
$row = $answer['runs'][0];
|
||
$expect($answer['ok'] && $iiExecuted === $before + 1 && $row['query_candidate_count'] === 2, 'two JSON transport layers recover identical exact aliases ' . $number);
|
||
$expect($row['observed_query_sha256'] === hash('sha256', $exact) && $row['observed_query_bytes'] === strlen($exact), 'transport escapes preserve original hash and bytes ' . $number);
|
||
$expect($row['inputs_encoding'] === 'json_string' && $row['inputs_availability'] === 'available'
|
||
&& $row['returned_run_id_matches'] && $row['run_status'] === 'running', 'encoding/status metadata does not become terminal ' . $number);
|
||
$privacy($answer);
|
||
$expect(!str_contains($json($answer), $exact), 'new escaped query text remains private ' . $number);
|
||
}
|
||
foreach (['escaped_key', 'double_inputs', 'nested_inputs', 'literal_json_query', 'decoded_query_different',
|
||
'different_alias', 'bad_bytes', 'bad_hash', 'foreign_id', 'foreign_alias', 'metadata_in_inputs'] as $kind) {
|
||
$fixture = $queryFixture($make(), [$exact]);
|
||
$value = $response(); $value['status'] = 'running';
|
||
$value['inputs'] = $json(['query' => $exact]);
|
||
$wanted = 'query_match';
|
||
if ($kind === 'escaped_key') $value['inputs'] = '{"sys\\u002equery":' . $json($exact) . '}';
|
||
if ($kind === 'double_inputs') { $value['inputs'] = $json($value['inputs']); $wanted = 'query_input_invalid'; }
|
||
if ($kind === 'nested_inputs') { $value['inputs'] = $json(['inputs' => $value['inputs']]); $wanted = 'query_input_unavailable'; }
|
||
if ($kind === 'literal_json_query' || $kind === 'decoded_query_different') {
|
||
$literal = $json(['query' => $exact]);
|
||
$fixture = $queryFixture($make(), [$literal]);
|
||
$value['inputs'] = $json(['query' => $kind === 'literal_json_query' ? $literal : $exact]);
|
||
if ($kind === 'decoded_query_different') $wanted = 'query_mismatch';
|
||
}
|
||
if ($kind === 'different_alias') { $value['inputs'] = $json(['sys.query' => $exact, 'query' => $json($exact)]); $wanted = 'query_input_ambiguous'; }
|
||
if ($kind === 'bad_bytes') { ++$fixture['records'][1]['query_bytes']; $wanted = 'query_mismatch'; }
|
||
if ($kind === 'bad_hash') { $fixture['records'][1]['query_sha256'] = hash('sha256', $exact . 'x'); $wanted = 'query_mismatch'; }
|
||
if ($kind === 'foreign_id') { $value['id'] = 'private-independent-foreign-run'; $wanted = 'returned_run_mismatch'; }
|
||
if ($kind === 'foreign_alias') { $value['task_id'] = 'private-independent-foreign-task'; $wanted = 'returned_run_mismatch'; }
|
||
if ($kind === 'metadata_in_inputs') $value['inputs'] = $json(['query' => $exact, 'run_status' => 'succeeded',
|
||
'returned_run_id_matches' => false, 'inputs_availability' => 'missing', 'status' => 'private-independent-status']);
|
||
[$selector] = $save($fixture); [$answer] = $call($selector, [['body' => $json($value)]]); $row = $answer['runs'][0];
|
||
$expect($row['status'] === $wanted && $answer['ok'] === ($wanted === 'query_match'), 'one decode and per-field exact comparison ' . $kind);
|
||
if (in_array($kind, ['foreign_id', 'foreign_alias'], true)) {
|
||
$expect($row['inputs_encoding'] === 'unobserved' && $row['inputs_availability'] === 'unobserved'
|
||
&& $row['returned_run_id_matches'] === ($kind === 'foreign_alias'), 'identity gate precedes any decoded input observation ' . $kind);
|
||
$expect(!array_key_exists('run_status', $row) && !array_key_exists('observed_query_sha256', $row), 'foreign identity cannot export query/status observations ' . $kind);
|
||
} else $expect($row['run_status'] === 'running', 'valid original running status independent of query decision ' . $kind);
|
||
if ($kind === 'metadata_in_inputs') $expect($row['returned_run_id_matches'] && $row['inputs_availability'] === 'available', 'input object cannot override safe metadata');
|
||
if (in_array($kind, ['double_inputs', 'nested_inputs', 'different_alias'], true))
|
||
$expect(!array_key_exists('observed_query_sha256', $row), 'unavailable or ambiguous query never invents a hash ' . $kind);
|
||
$privacy($answer);
|
||
}
|
||
$secondQuery = $exact . 'sole-continuation';
|
||
foreach (['own', 'swapped', 'one_wrong'] as $kind) {
|
||
[$selector] = $save($queryFixture($make(2), [$exact, $secondQuery]));
|
||
$first = $response(); $second = $response(2); $first['status'] = $second['status'] = 'running';
|
||
$first['inputs'] = $json(['sys.query' => $kind === 'swapped' ? $secondQuery : $exact]);
|
||
$second['inputs'] = $json(['query' => $kind === 'own' ? $secondQuery : $exact]);
|
||
$before = $iiExecuted; [$answer] = $call($selector, [['body' => $json($first)], ['body' => $json($second)]]);
|
||
$expect($iiExecuted === $before + 2 && $answer['inspected_run_count'] === 2, 'exactly two original generation GETs without recovery/fallback ' . $kind);
|
||
$expect($answer['matched_run_count'] === match ($kind) { 'own' => 2, 'swapped' => 0, 'one_wrong' => 1 }
|
||
&& $answer['ok'] === ($kind === 'own'), 'each serialized query bound to its own generation ' . $kind);
|
||
$expect(array_column($answer['runs'], 'run_status') === ['running', 'running'], 'matching or mismatching input never rewrites terminal state ' . $kind);
|
||
$privacy($answer);
|
||
}
|
||
[$selector] = $save($queryFixture($make(), [$exact]));
|
||
$value = $response(); $value['status'] = 'running'; $value['inputs'] = $json(['query' => $exact]);
|
||
[$answer] = $call($selector, [['body' => $json($value), 'close_ns' => 2000000000]]);
|
||
$row = $answer['runs'][0];
|
||
$expect(!$answer['ok'] && $row['status'] === 'run_timeout' && !$row['query_matches_wire'], 'outer total per-run budget invalidates a comparison that finished before slow close');
|
||
$expect($row['run_status'] === 'running' && $row['returned_run_id_matches']
|
||
&& $row['inputs_encoding'] === 'json_string' && $row['inputs_availability'] === 'available', 'outer timeout retains obtained safe observations without terminal override');
|
||
$privacy($answer);
|
||
|
||
// Exact source equivalence is stronger than middleware-name presence:
|
||
// the original saved-row/audit/config code remains mechanically equal.
|
||
$artifact = dirname(__DIR__, 2) . '/app/artifacts/ai_concurrency/';
|
||
require $artifact . 'private_tcm_r25_run_input_inspector.php';
|
||
$methodSource = static function (string $class, string $method): string {
|
||
$reflection = new ReflectionMethod($class, $method);
|
||
$lines = file($reflection->getFileName());
|
||
return str_replace("\r\n", "\n", implode('', array_slice($lines, $reflection->getStartLine() - 1,
|
||
$reflection->getEndLine() - $reflection->getStartLine() + 1)));
|
||
};
|
||
foreach (['inspectResultRow', 'readAudit', 'connection', 'keys', 'digest', 'id', 'samePath', 'error'] as $method) {
|
||
$expect($methodSource(Inspector::class, $method) === $methodSource('PrivateTcmR25\\RunInputInspector', $method), 'canonical old audit/result/connection method exact source equality ' . $method);
|
||
}
|
||
$oldProbe = str_replace("\r\n", "\n", file_get_contents($artifact . 'private_tcm_r25_run_input_probe.php'));
|
||
$newProbe = str_replace("\r\n", "\n", file_get_contents($artifact . 'private_tcm_r25b_run_input_probe.php'));
|
||
$expectedProbe = str_replace(['runInputFinish', 'private_tcm_r25_run_input_', '\\PrivateTcmR25\\'],
|
||
['runInputR25bFinish', 'private_tcm_r25b_run_input_', '\\PrivateTcmR25b\\'], $oldProbe);
|
||
$expectedProbe = str_replace('// POST: token,', "// New r25b derivative; the original r25 probe/helper remain immutable.\n// POST: token,", $expectedProbe);
|
||
$expect($newProbe === $expectedProbe, 'entire canonical private probe auth bridge unchanged except exact derivative naming/comment');
|
||
foreach (['private_tcm_r25_run_input_inspector.php' => '9f5f163e529ecdbb5e9b4d2b1ab1d04d8c4292b371c72c66034554b3b4860381',
|
||
'private_tcm_r25_run_input_probe.php' => '18ffca2a320c683c7e797ec6ac2e1fe606d0103fc59c701c95c818f49c8a4a0e',
|
||
'private_tcm_r25b_run_input_inspector.php' => 'f0f6ef018e391ea5f1f219b0d5fc931f59f8fa84fe7431d8d5ca29efab410a61',
|
||
'private_tcm_r25b_run_input_probe.php' => '6c19b575ae7fd88554dd68f12d8f258e15851e5e16246a52ae705e6840ad782a'] as $name => $sha) {
|
||
$expect(hash_file('sha256', $artifact . $name) === $sha, 'exact original/derivative source pin ' . $name);
|
||
}
|
||
[$selector, $auditPath] = $save($make());
|
||
$lock = fopen($auditPath, 'rb');
|
||
$expect(flock($lock, LOCK_EX | LOCK_NB), 'synthetic exclusive lock acquired');
|
||
$before = $iiExecuted; [$answer, $configured] = $call($selector, []);
|
||
$expect(!$answer['ok'] && $configured === 0 && $iiExecuted === $before, 'locked exact private audit never reaches config/GET');
|
||
flock($lock, LOCK_UN); fclose($lock);
|
||
$expect($iiCreated === $iiClosed, 'every fake GET handle closed');
|
||
$expect($iiScenarios === [], 'all and only declared fake scenarios consumed');
|
||
$expect(!class_exists('think\\App', false), 'application bootstrap never loaded');
|
||
echo "Independent r25b serialized exact-run input inspector: {$checks} checks passed (handcrafted private fixtures, in-memory GET/config/time; no auth/network/bootstrap).\n";
|
||
} finally {
|
||
$real = realpath($base); $temp = realpath(sys_get_temp_dir());
|
||
if ($real === false || $temp === false || !str_starts_with(str_replace('\\', '/', $real), rtrim(str_replace('\\', '/', $temp), '/') . '/tcm-r25b-inspector-independent-'))
|
||
throw new RuntimeException('Independent synthetic cleanup boundary');
|
||
$entries = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($real, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
|
||
foreach ($entries as $entry) { if ($entry->isLink()) throw new RuntimeException('Independent synthetic cleanup symlink'); if ($entry->isDir()) rmdir($entry->getPathname()); else unlink($entry->getPathname()); }
|
||
rmdir($real);
|
||
}
|
||
}
|