<?php

declare(strict_types=1);

// Read-only GiftLog HTTP; writes only short tokens in a private File cache.
// The two synthetic gift rows must already exist in the private fixture journal.
$mode = (string)($argv[1] ?? '');
$lane = (string)getenv('CDX_GIFT_LANE');
if (!in_array($mode, ['--preflight', '--read', '--token-zero'], true)
    || !in_array($lane, ['A', 'B', 'C'], true)
    || ($mode !== '--preflight'
        && ((string)getenv('CDX_GIFT_HTTP_APPROVED') !== 'ROOT_APPROVED_GIFT_HTTP_V1'
            || (string)getenv('CDX_GIFT_HTTP_SHA') !== hash_file('sha256', __FILE__)))) {
    echo json_encode(['status' => 'default_deny', 'business_http' => 0,
        'db_dml' => 0, 'cache_token' => 0]), PHP_EOL;
    exit(78);
}
$identityPreflight = $lane === 'C'
    ? '/tmp/zhct-ai-read2-VgKO4CDY/read2_schema_preflight.php'
    : __DIR__ . '/../parallel-ai-next-read-safe4/read2_schema_preflight.php';
if (!is_file($identityPreflight) || hash_file('sha256', $identityPreflight)
    !== '8641ee015ad537d300de9c00f2c882e654b739d7c2194dd13ef4635ca9df1af0') {
    throw new RuntimeException('Gift HTTP identity preflight drift');
}
putenv('CDX_READ2_TARGET=' . $lane);
putenv('CDX_READ2_SCHEMA_PREFLIGHT=1');
require $identityPreflight;
$stage = 'after_identity';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'gift_http_error', 'stage' => $stage,
        'error_class' => get_class($error), 'sql_code' => (string)$error->getCode(),
        'reason_sha' => hash('sha256', $error->getMessage()),
        'db_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(2);
});
$ai->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$store->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$runtimeRoot = ['A' => '/private/tmp/zhct-http-ai-macos.zjaU8h',
    'B' => '/private/tmp/zhct-http-ai-macos.x2CCZX',
    'C' => '/tmp/zhct-ai-http-20260929.3XDj3w'][$lane];
$sourceRoot = ['A' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/baseline-a-ai-api-V4.0.40',
    'B' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api'][$lane];
$port = ['A' => 18105, 'B' => 18101, 'C' => 18102][$lane];
$modelSha = $lane === 'A'
    ? 'cbe789c4d5d3f15ce99b3ac8a2ace58af9935d132edf70129f83cb7eb6ba2782'
    : '0e55edc5a3ea0a98eae2dee78500d3d2ba177a6ea059bdee077908d687862035';
$sources = [
    'app/api/controller/gift/Log.php' => '9d70fbc0555f77697065ddb6568cf85cdc9cfdd7b6c69589e4fb617cdf592c4d',
    'app/api/service/gift/Log.php' => '03c3af93dc4a08c0932ee573112dfe5ac9ad7e225c902ca18f0cb9de31df4681',
    'app/api/model/user/GiftLog.php' => $modelSha,
];
foreach ($sources as $relative => $sha) {
    $file = $sourceRoot . '/' . $relative;
    if (!is_file($file) || is_link($file) || hash_file('sha256', $file) !== $sha) {
        throw new RuntimeException('Actual Composer GiftLog source drift');
    }
}
if ($lane !== 'A') {
    $helper = $sourceRoot . '/app/common/service/FixedScaleDecimalResponse.php';
    if (!is_file($helper) || hash_file('sha256', $helper)
        !== 'c3bce04654a313e2918667a8a1019eb7b4c45b98edf9a5218d3e8c40b8d333cc') {
        throw new RuntimeException('Fixed decimal helper drift');
    }
}
$cacheRoot = realpath($runtimeRoot . '/runtime/cache');
if (realpath($runtimeRoot) !== $runtimeRoot
    || $cacheRoot !== $runtimeRoot . '/runtime/cache'
    || (((int)fileperms($runtimeRoot) & 0077) !== 0)
    || !hash_equals('ccf1c56eb4178f3eaad8775e26a27742b2cacf662b46fb058cbb91d23783b8c6',
        (string)hash_file('sha256', $runtimeRoot . '/config/cache.php'))
    || is_file($runtimeRoot . '/.env') || is_file($runtimeRoot . '/.env.poc')
    || getenv('CACHE_DRIVER') !== false || getenv('cache.driver') !== false) {
    throw new RuntimeException('Gift HTTP private File cache boundary drift');
}
foreach ($config as $name => $value) {
    if (strpos($name, 'POC_') === 0) putenv($name . '=' . $value);
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $runtimeRoot . '/vendor/autoload.php';
if ((new ReflectionClass(\app\api\model\user\GiftLog::class))->getFileName()
    !== $sourceRoot . '/app/api/model/user/GiftLog.php') {
    throw new RuntimeException('Composer resolved unexpected GiftLog source');
}
(new \think\App($runtimeRoot))->setEnvName('poc')->initialize();
if (strtolower((string)\think\facade\Config::get('cache.default')) !== 'file'
    || realpath((string)\think\facade\Config::get('cache.stores.file.path')) !== $cacheRoot
    || (string)\think\facade\Config::get('cache.stores.file.prefix') !== '') {
    throw new RuntimeException('Effective Gift File cache differs from isolated root');
}
if ($mode === '--preflight' && (string)getenv('CDX_GIFT_JOURNAL') === '') {
    echo json_encode(['status' => 'gift_http_runtime_preflight_pass',
        'lane' => $lane, 'actual_composer_model_sha' => $modelSha,
        'isolated_cache' => true, 'fixture_not_created' => true,
        'business_http' => 0, 'db_dml' => 0, 'cache_token' => 0],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$journalPath = (string)getenv('CDX_GIFT_JOURNAL');
$privateDir = dirname($journalPath);
if (basename($journalPath) !== 'fixture.json'
    || strpos(basename($privateDir), 'cdx-giftlog-CDXGL5') !== 0
    || !is_file($journalPath) || is_link($journalPath)
    || !is_dir($privateDir) || is_link($privateDir)
    || (((int)fileperms($privateDir) & 0077) !== 0)
    || (((int)fileperms($journalPath) & 0077) !== 0)) {
    throw new RuntimeException('Gift fixture private journal absent');
}
$journal = json_decode((string)file_get_contents($journalPath), true);
if (!is_array($journal) || !preg_match('/^CDXGL5[0-9A-F]{12}$/',
        (string)($journal['marker'] ?? ''))
    || ($journal['lane_group'] ?? '') !== ($lane === 'C' ? 'C' : 'AB')
    || count($journal['log_ids'] ?? []) !== 2
    || ($journal['period'] ?? null)
        !== ['2026-09-01 00:00:00', '2026-10-01 00:00:00']) {
    throw new RuntimeException('Gift fixture journal ownership invalid');
}
$marker = (string)$journal['marker'];
$giftTable = $dialect === 'dm' ? 'YOSHOP_USER_GIFT_LOG' : 'yoshop_user_gift_log';
$userTable = $dialect === 'dm' ? 'YOSHOP_USER' : 'yoshop_user';
$storeUser = $dialect === 'dm' ? 'YDY_USER' : 'ydy_user';
$strictIdentity = static function (PDO $pdo, string $side)
    use ($dialect, $config): bool {
    $expectedSchema = $side === 'ai' ? 'AIZHCT_APP' : 'ZHCT_APP';
    if ($dialect === 'dm') {
        return strtoupper((string)$pdo->query('SELECT USER FROM DUAL')->fetchColumn())
                === $expectedSchema
            && strtoupper((string)$pdo->query(
                "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn())
                === $expectedSchema;
    }
    $identity = (string)$pdo->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
        ->fetchColumn();
    $sha = $side === 'ai'
        ? '210f925e093f71e26a8c8923f0f626e3be0a15ccff3d386bba21feef695c8a5b'
        : '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
    return hash_equals($sha, hash('sha256', $identity))
        && (string)$pdo->query('SELECT DATABASE()')->fetchColumn()
            === (string)$config[$side === 'ai' ? 'POC_AI_SCHEMA' : 'POC_SCHEMA'];
};
if (!$strictIdentity($ai, 'ai') || !$strictIdentity($store, 'store')) {
    throw new RuntimeException('Gift initial customer DB identity drift');
}
$controlled = $lane === 'C'
    ? ['owner' => 'CDXPARMOB9411C2CB', 'foreign' => 'CDXPAY661BEE6B']
    : ['owner' => 'CDXPARMOB426CA4B0', 'foreign' => 'CDXPAY112FC189'];
$one = static function (PDO $pdo, string $sql, array $args = []): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    if (count($rows) !== 1) throw new RuntimeException('Expected one controlled Gift row');
    $out = [];
    foreach (array_change_key_case($rows[0], CASE_LOWER) as $name => $value) {
        $out[$name] = is_resource($value) ? stream_get_contents($value) : $value;
    }
    return $out;
};
$digest = static function (array $row): string {
    ksort($row, SORT_STRING);
    return hash('sha256', serialize($row));
};
$sourceRows = [];
foreach ($controlled as $role => $nick) {
    $id = (int)$journal['owner_ids'][$role];
    $user = $one($ai, 'SELECT * FROM ' . $userTable
        . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
        [$id, $nick]);
    $mapping = $one($store, 'SELECT * FROM ' . $storeUser
        . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$id]);
    $log = $one($ai, 'SELECT * FROM ' . $giftTable
        . ' WHERE LOG_ID=? AND USER_ID=? AND IDEMPOTENCY_KEY=? AND STORE_ID=10001',
        [(int)$journal['log_ids'][$role], $id,
            $marker . ':' . ($role === 'owner' ? 'O' : 'F')]);
    if ($digest($user) !== (string)$journal['user_hashes'][$role]
        || $digest($mapping) !== (string)$journal['store_hashes'][$role]
        || $digest($log) !== (string)$journal['log_hashes'][$role]) {
        throw new RuntimeException('Gift user/mapping/log source changed');
    }
    $sourceRows[$role] = ['user' => $user, 'mapping' => $mapping, 'log' => $log];
}
$approvedSource = ['model_sha' => $modelSha,
    'owned_users' => count($sourceRows), 'owned_logs' => count($sourceRows),
    'period' => '2026-09', 'cache_private' => true];
if ($mode === '--preflight') {
    echo json_encode(['status' => 'gift_http_preflight_pass', 'lane' => $lane,
        'source' => $approvedSource, 'business_http' => 0, 'db_dml' => 0,
        'cache_token' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
$writeJournal = static function () use (&$journal, $privateDir, $journalPath): void {
    $temp = tempnam($privateDir, 'http-');
    if (!is_string($temp) || !chmod($temp, 0600)) {
        throw new RuntimeException('Gift HTTP journal temp unavailable');
    }
    $json = json_encode($journal, JSON_UNESCAPED_SLASHES);
    if (!is_string($json) || file_put_contents($temp, $json, LOCK_EX) !== strlen($json)
        || !rename($temp, $journalPath)) {
        throw new RuntimeException('Gift HTTP journal atomic write failed');
    }
};
if ($mode === '--token-zero') {
    $tokens = $journal['tokens'][$lane] ?? null;
    if (!is_array($tokens) || count($tokens) !== 2) {
        throw new RuntimeException('No two issued tokens for independent zero');
    }
    foreach ($tokens as $token) {
        if (!is_string($token) || strlen($token) < 20
            || \think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift short token still present or invalid');
        }
    }
    unset($journal['tokens'][$lane]);
    $journal['token_states'][$lane] = 'zero';
    $writeJournal();
    echo json_encode(['status' => 'gift_token_independent_zero',
        'lane' => $lane, 'token_count' => 2,
        'db_dml' => 0, 'business_http' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(0);
}
if ($lane === 'B'
    && (($journal['token_states']['A'] ?? '') !== 'zero'
        || empty($journal['contracts']['A']))) {
    throw new RuntimeException('A read and independent token-zero required before B');
}
if (isset($journal['tokens'][$lane]) || isset($journal['token_states'][$lane])) {
    throw new RuntimeException('Gift lane already attempted, no blind replay');
}
$aBaseline = null;
if ($lane === 'B') $aBaseline = $journal['contracts']['A'];
if ($lane === 'C') {
    $baselinePath = (string)getenv('CDX_GIFT_A_CONTRACT_FILE');
    $baselineSha = (string)getenv('CDX_GIFT_A_CONTRACT_SHA');
    if (!is_file($baselinePath) || is_link($baselinePath)
        || !is_dir(dirname($baselinePath)) || is_link(dirname($baselinePath))
        || (((int)fileperms($baselinePath) & 0077) !== 0)
        || (((int)fileperms(dirname($baselinePath)) & 0077) !== 0)
        || strlen($baselineSha) !== 64
        || !hash_equals($baselineSha, (string)hash_file('sha256', $baselinePath))) {
        throw new RuntimeException('A type contract file/SHA required for C');
    }
    $aBaseline = json_decode((string)file_get_contents($baselinePath), true);
    if (!is_array($aBaseline) || ($aBaseline['lane'] ?? '') !== 'A') {
        throw new RuntimeException('A type contract invalid for C');
    }
}
$shapeHelper = __DIR__ . '/gift_contract_shape.php';
if (!is_file($shapeHelper) || is_link($shapeHelper)
    || hash_file('sha256', $shapeHelper)
        !== '03eefd38885b735d129b2835323adf4e275ba04c8777c23a775e24f72b3915a8') {
    throw new RuntimeException('Gift recursive type helper drift');
}
require_once $shapeHelper;
$normalize = static function (array $json): array {
    unset($json['trace_id']);
    return $json;
};
$rowAssertion = static function (array $actual, array $raw, bool $detail): void {
    if ((int)($actual['log_id'] ?? 0) !== (int)$raw['log_id']
        || (int)($actual['user_id'] ?? 0) !== (int)$raw['user_id']
        || (int)($actual['scene'] ?? 0) !== (int)$raw['scene']) {
        throw new RuntimeException('Gift JSON identity/scene differs from own raw row');
    }
    foreach (['money', 'balance_before', 'balance_after'] as $field) {
        $value = $actual[$field] ?? null;
        if (!is_string($value) || !preg_match('/^-?\d+\.\d{2}$/', $value)
            || !function_exists('bccomp')
            || bccomp($value, (string)$raw[$field], 2) !== 0) {
            throw new RuntimeException('Gift JSON decimal must be exact two-place string');
        }
    }
    foreach (['biz_type', 'biz_order_no', 'describe', 'remark',
        'operator_id', 'operator_name', 'create_time'] as $field) {
        if ((string)($actual[$field] ?? '') !== (string)($raw[$field] ?? '')) {
            throw new RuntimeException('Gift JSON source field differs');
        }
    }
    foreach (['store_id', 'idempotency_key', 'is_delete',
        'create_by', 'update_time'] as $hidden) {
        if (array_key_exists($hidden, $actual)) {
            throw new RuntimeException('Gift internal field leaked to client');
        }
    }
    if ($detail && ((string)($actual['order_no'] ?? '')
        !== (string)$raw['biz_order_no']
        || (string)($actual['pay_method'] ?? '__MISSING__') !== '')) {
        throw new RuntimeException('Gift detail derived fields mismatch');
    }
};
$requests = [];
$request = static function (string $path, array $query, string $token)
    use ($port, $lane, &$journal, $writeJournal, &$requests): array {
    if (!in_array($path, ['/api/gift.Log/list', '/api/gift.Log/detail'], true)) {
        throw new RuntimeException('Gift HTTP path outside read-only whitelist');
    }
    $query = array_merge(['storeId' => 10001], $query);
    $headers = "platform: MP-WEIXIN\r\nversion: 2.0.51\r\nenv-version: develop\r\n";
    if ($token !== '') $headers .= 'Access-Token: ' . $token . "\r\n";
    $context = stream_context_create(['http' => [
        'method' => 'GET', 'timeout' => 15, 'ignore_errors' => true,
        'follow_location' => 0, 'max_redirects' => 0,
        'header' => $headers]]);
    $journal['http_states'][$lane] = 'inflight';
    $writeJournal();
    $stream = @fopen('http://127.0.0.1:' . $port . $path . '?'
        . http_build_query($query, '', '&', PHP_QUERY_RFC3986),
        'rb', false, $context);
    $body = is_resource($stream) ? stream_get_contents($stream, 262145) : false;
    $meta = is_resource($stream) ? stream_get_meta_data($stream) : [];
    if (is_resource($stream)) fclose($stream);
    if (!is_string($body) || !empty($meta['timed_out']) || strlen($body) > 262144) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal();
        throw new RuntimeException('Gift HTTP transport outcome unknown/oversized');
    }
    $responseHeaders = $http_response_header ?? [];
    $httpCode = preg_match('/HTTP\/\S+\s+(\d+)/',
        (string)($responseHeaders[0] ?? ''), $m) ? (int)$m[1] : 0;
    $json = json_decode($body, true);
    if (!is_array($json) || !array_key_exists('status', $json)
        || $httpCode === 0) {
        $journal['http_states'][$lane] = 'unknown';
        $writeJournal();
        throw new RuntimeException('Gift HTTP response not confirmed JSON');
    }
    $journal['http_states'][$lane] = 'confirmed';
    $writeJournal();
    $requests[] = ['path' => $path, 'http' => $httpCode,
        'business_status' => (int)$json['status']];
    return [$json, $httpCode, $body];
};
$tokens = [];
$contracts = [];
$failure = '';
$cacheDeleted = false;
try {
    $stage = 'issue_two_short_tokens';
    foreach (['owner', 'foreign'] as $role) {
        $id = (int)$journal['owner_ids'][$role];
        $token = 'CDXGL5T' . strtoupper(bin2hex(random_bytes(16)));
        $tokens[$role] = $token;
        $journal['tokens'][$lane][$role] = $token;
    }
    $journal['token_states'][$lane] = 'issued';
    $writeJournal(); // Persist recovery pointer BEFORE touching File cache.
    foreach ($tokens as $role => $token) {
        $id = (int)$journal['owner_ids'][$role];
        $model = \app\api\model\User::detail($id);
        if (empty($model) || (int)$model['user_id'] !== $id) {
            throw new RuntimeException('Gift synthetic auth model missing');
        }
        $version = (int)$sourceRows[$role]['mapping']['password_version'];
        if (!\think\facade\Cache::set($token, [
            'user' => $model, 'store_id' => 10001, 'is_login' => true,
            'password_version' => $version, 'login_type' => 'sms'], 300)
            || !\think\facade\Cache::has($token)) {
            throw new RuntimeException('Gift isolated short token unavailable');
        }
    }
    $stage = 'business_http';
    $cases = [
        ['owner', 'list', ['year' => 2026, 'month' => 9,
            'scene' => 0, 'page' => 1, 'per_page' => 10]],
        ['owner', 'list_scene', ['year' => 2026, 'month' => 9,
            'scene' => 20, 'page' => 1, 'per_page' => 10]],
        ['owner', 'detail', ['logId' => (int)$journal['log_ids']['owner'],
            'log_id' => (int)$journal['log_ids']['owner']]],
        ['foreign', 'list', ['year' => 2026, 'month' => 9,
            'scene' => 0, 'page' => 1, 'per_page' => 10]],
        ['foreign', 'list_scene', ['year' => 2026, 'month' => 9,
            'scene' => 10, 'page' => 1, 'per_page' => 10]],
        ['foreign', 'detail', ['logId' => (int)$journal['log_ids']['foreign'],
            'log_id' => (int)$journal['log_ids']['foreign']]],
    ];
    foreach ($cases as $case) {
        [$role, $kind, $query] = $case;
        $path = $kind === 'detail' ? '/api/gift.Log/detail' : '/api/gift.Log/list';
        [$json, $httpCode, $rawBody] = $request($path, $query, $tokens[$role]);
        if ($httpCode !== 200 || (int)$json['status'] !== 200) {
            throw new RuntimeException('Gift positive HTTP/business status mismatch');
        }
        $business = $json['data'] ?? null;
        if (!is_array($business)) throw new RuntimeException('Gift success data envelope mismatch');
        if ($kind === 'detail') {
            $item = $business['data'] ?? null;
            if (!is_array($item)) throw new RuntimeException('Gift detail object absent');
        } else {
            $list = $business['list'] ?? null;
            $rows = is_array($list) ? ($list['data'] ?? null) : null;
            if (!is_array($rows) || count($rows) !== 1
                || (int)($list['total'] ?? -1) !== 1) {
                throw new RuntimeException('Gift one-row month/scene list mismatch');
            }
            $item = $rows[0];
        }
        $rowAssertion($item, $sourceRows[$role]['log'], $kind === 'detail');
        $normal = $normalize($json);
        $key = $role . '_' . $kind;
        $contracts[$key] = [
            'business_sha' => hash('sha256', json_encode($normal,
                JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)),
            'type_map' => giftLogJsonShape($rawBody)];
        if ($lane === 'B' && $contracts[$key] !== ($aBaseline['routes'][$key] ?? null)) {
            throw new RuntimeException('A/B normalized full business response differs');
        }
        if ($lane === 'C'
            && $contracts[$key]['type_map']
                !== ($aBaseline['routes'][$key]['type_map'] ?? null)) {
            throw new RuntimeException('C JSON field/type contract differs from A');
        }
    }
    foreach (['owner' => 'foreign', 'foreign' => 'owner'] as $viewer => $other) {
        $foreignId = (int)$journal['log_ids'][$other];
        [$json] = $request('/api/gift.Log/detail',
            ['logId' => $foreignId, 'log_id' => $foreignId], $tokens[$viewer]);
        if ((int)$json['status'] === 200
            || (int)($json['data']['data']['log_id'] ?? 0) === $foreignId) {
            throw new RuntimeException('Gift cross-user detail was not rejected');
        }
    }
    foreach (['/api/gift.Log/list', '/api/gift.Log/detail'] as $path) {
        $query = $path === '/api/gift.Log/list'
            ? ['year' => 2026, 'month' => 9, 'scene' => 0]
            : ['logId' => (int)$journal['log_ids']['owner']];
        [$json] = $request($path, $query, '');
        if ((int)$json['status'] === 200) {
            throw new RuntimeException('Gift no-auth route accepted');
        }
    }
    $stage = 'source_after_http';
    $newConnection = static function (string $prefix) use ($config, $dialect): PDO {
        $dsn = $dialect === 'dm'
            ? 'dm:host=' . $config[$prefix . 'HOST'] . ';port='
                . $config[$prefix . 'PORT'] . ';schema=' . $config[$prefix . 'SCHEMA']
            : 'mysql:host=' . $config[$prefix . 'HOST'] . ';port='
                . $config[$prefix . 'PORT'] . ';dbname='
                . $config[$prefix . 'SCHEMA'] . ';charset=utf8';
        return new PDO($dsn, $config[$prefix . 'USER'], $config[$prefix . 'PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
    };
    $aiAfter = $newConnection('POC_AI_');
    $storeAfter = $newConnection('POC_');
    if (!$strictIdentity($aiAfter, 'ai')
        || !$strictIdentity($storeAfter, 'store')) {
        throw new RuntimeException('Gift independent post-HTTP database identity drift');
    }
    foreach (['owner', 'foreign'] as $role) {
        $id = (int)$journal['owner_ids'][$role];
        $user = $one($aiAfter, 'SELECT * FROM ' . $userTable
            . ' WHERE USER_ID=? AND NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0',
            [$id, $controlled[$role]]);
        $mapping = $one($storeAfter, 'SELECT * FROM ' . $storeUser
            . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$id]);
        $log = $one($aiAfter, 'SELECT * FROM ' . $giftTable
            . ' WHERE LOG_ID=? AND USER_ID=? AND IDEMPOTENCY_KEY=? AND STORE_ID=10001',
            [(int)$journal['log_ids'][$role], $id,
                $marker . ':' . ($role === 'owner' ? 'O' : 'F')]);
        if ($digest($user) !== (string)$journal['user_hashes'][$role]
            || $digest($mapping) !== (string)$journal['store_hashes'][$role]
            || $digest($log) !== (string)$journal['log_hashes'][$role]) {
            throw new RuntimeException('Gift source row changed after GET');
        }
    }
    $journal['http_states'][$lane] = 'passed';
    if ($lane === 'A') {
        $journal['contracts']['A'] = ['lane' => 'A', 'routes' => $contracts];
    }
    $writeJournal();
} catch (Throwable $error) {
    $failure = get_class($error) . ':' . hash('sha256', $error->getMessage());
    if (($journal['http_states'][$lane] ?? '') !== 'unknown'
        && ($journal['http_states'][$lane] ?? '') !== 'inflight') {
        $journal['http_states'][$lane] = 'confirmed_failure';
    }
    try { $writeJournal(); } catch (Throwable $ignored) { /* Preserve earlier journal. */ }
} finally {
    $stage = 'short_token_cleanup';
    try {
        foreach ($tokens as $token) \think\facade\Cache::delete($token);
        $cacheDeleted = true;
        $journal['token_states'][$lane] = 'deleted_needs_independent_zero';
        $writeJournal();
    } catch (Throwable $error) {
        $failure .= ($failure === '' ? '' : ';') . 'token_cleanup:'
            . get_class($error) . ':' . hash('sha256', $error->getMessage());
    }
}
$result = ['status' => $failure === '' ? 'gift_http_confirmed' : 'gift_http_failed',
    'lane' => $lane, 'routes' => $contracts, 'requests' => $requests,
    'token_deleted_first_process' => $cacheDeleted,
    'token_independent_zero_pending' => true,
    'source_unchanged' => $failure === '', 'journal' => $journalPath,
    'failure' => $failure, 'db_dml' => 0];
echo json_encode($result, JSON_UNESCAPED_SLASHES), PHP_EOL;
if ($failure !== '' || count($contracts) !== 6 || !$cacheDeleted) exit(1);
