<?php
declare(strict_types=1);

// DRAFT: only the eight existing-frontend POST read routes. No calls without
// root review of this exact SHA and the explicit approval environment.
const CAT8_APPROVAL = 'CDX_CAT8_HTTP_APPROVED_20261002_V1';
$stage = 'default_deny';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'redacted_error', 'stage' => $stage,
        'class' => get_class($error), 'business_pass' => false],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(2);
});
set_error_handler(static function (int $severity): bool {
    if (!(error_reporting() & $severity)) return false;
    throw new ErrorException('redacted_warning', 0, $severity);
});

if (!hash_equals(CAT8_APPROVAL, (string)getenv('CDX_CAT8_APPROVAL'))) {
    echo json_encode(['status' => 'default_deny', 'business_http_sent' => false,
        'database_connected' => false, 'dml_sent' => false],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(78);
}
$stage = 'exact_script_review';
$actualSha = hash_file('sha256', __FILE__);
if (!is_string($actualSha)
    || !hash_equals($actualSha, (string)getenv('CDX_CAT8_SCRIPT_SHA256'))) {
    throw new RuntimeException('Exact reviewed draft SHA required');
}
$target = (string)getenv('CDX_CAT8_TARGET');
$targets = [
    'A' => ['/private/tmp/zhct-http-ai-macos.zjaU8h', 18105],
    'B' => ['/private/tmp/zhct-http-ai-macos.x2CCZX', 18101],
    'C' => ['/tmp/zhct-ai-http-20260929.3XDj3w', 18102],
];
if (!isset($targets[$target])) throw new RuntimeException('Exact target required');
[$runtimeRoot, $port] = $targets[$target];
if ((fileperms($runtimeRoot) & 0077) !== 0
    || hash_file('sha256', $runtimeRoot . '/config/cache.php')
        !== 'ccf1c56eb4178f3eaad8775e26a27742b2cacf662b46fb058cbb91d23783b8c6'
    || getenv('CACHE_DRIVER') !== false || getenv('cache.driver') !== false) {
    throw new RuntimeException('Isolated file-cache source mismatch');
}
$stage = 'private_synthetic_operator_manifest';
$manifestPath = (string)getenv('CDX_CAT8_FIXTURE_MANIFEST');
$resolvedManifest = realpath($manifestPath);
if (!is_string($resolvedManifest)
    || !preg_match('#^/(?:private/)?tmp/cdx-cat8-[a-z0-9-]+/manifest\.json$#',
        $resolvedManifest)
    || (fileperms($resolvedManifest) & 0077) !== 0
    || (fileperms(dirname($resolvedManifest)) & 0077) !== 0) {
    throw new RuntimeException('Private synthetic operator manifest required');
}
$manifest = json_decode((string)file_get_contents($resolvedManifest), true);
if (!is_array($manifest)
    || ($manifest['status'] ?? null) !== 'ready'
    || ($manifest['fixture_mode'] ?? null) !== 'synthetic_role_catalog_read'
    || ($manifest['target'] ?? null) !== $target
    || !is_string($manifest['schema'] ?? null)
    || (int)($manifest['store_id'] ?? 0) !== 10001
    || (int)($manifest['store_user_id'] ?? 0) <= 0
    || (int)($manifest['role_id'] ?? 0) <= 0
    || (int)($manifest['menu_id'] ?? 0) <= 0
    || (int)($manifest['ttl_seconds'] ?? 0) !== 300
    || ($manifest['ttl_proof'] ?? null) !== 'runtime_cache_ttl_verified'
    || (int)($manifest['expires_at'] ?? 0) <= time() + 150
    || (int)($manifest['expires_at'] ?? 0) > time() + 300
    || !preg_match('/^CDXCAT8U[A-Z0-9]{12,48}$/',
        (string)($manifest['user_name'] ?? ''))) {
    throw new RuntimeException('Synthetic operator identity or TTL mismatch');
}
$token = (string)($manifest['token'] ?? '');
if (!preg_match('/^CDXCAT8T[A-Z0-9]{16,64}$/', $token)) {
    throw new RuntimeException('Synthetic operator cache token unavailable');
}

$stage = 'isolated_runtime_and_database_identity';
$config = [];
if ($target === 'C') {
    $pid = 53772;
    $command = (string)file_get_contents('/proc/' . $pid . '/cmdline');
    $parts = explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ'));
    foreach ($parts as $part) {
        if (strpos($part, 'CACHE_DRIVER=') === 0
            || strpos($part, 'cache.driver=') === 0) {
            throw new RuntimeException('C cache driver override denied');
        }
        if (strpos($part, 'POC_') !== 0 || strpos($part, '=') === false) continue;
        [$key, $value] = explode('=', $part, 2);
        $config[$key] = $value;
    }
    $expected = ['POC_TARGET_ROOT' => $runtimeRoot,
        'POC_DIALECT' => 'dm', 'POC_EXTERNAL_STUBS' => '1',
        'POC_AI_HOST' => '127.0.0.1', 'POC_AI_PORT' => '5238',
        'POC_AI_SCHEMA' => 'AIZHCT_APP', 'POC_AI_USER' => 'AIZHCT_APP',
        'POC_HOST' => '127.0.0.1', 'POC_PORT' => '5237',
        'POC_SCHEMA' => 'ZHCT_APP'];
    if (strpos($command, '127.0.0.1:' . $port) === false
        || strpos($command, $runtimeRoot . '/public') === false) {
        throw new RuntimeException('C isolated process mismatch');
    }
    foreach ($expected as $key => $value) {
        if (($config[$key] ?? null) !== $value) {
            throw new RuntimeException('C DM boundary mismatch');
        }
    }
    if (empty($config['POC_AI_PASSWORD'])) {
        throw new RuntimeException('C private AI secret unavailable');
    }
    $pdo = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
        $config['POC_AI_USER'], $config['POC_AI_PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER]);
    if ((string)$pdo->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
        ->fetchColumn() !== 'AIZHCT_APP') {
        throw new RuntimeException('C actual AI schema mismatch');
    }
} else {
    $local = '/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local';
    if (realpath($local) !== $local
        || !hash_equals((string)getenv('CDX_CAT8_DEV_CONFIG_SHA256'),
            (string)hash_file('sha256', $local))) {
        throw new RuntimeException('Local development config unavailable');
    }
    $ini = parse_ini_file($local, true, INI_SCANNER_RAW);
    if (!is_array($ini) || !isset($ini['DATABASE'], $ini['DATABASE_ZHCT'])
        || ($ini['DATABASE']['TYPE'] ?? null) !== 'mysql'
        || ($ini['DATABASE_ZHCT']['TYPE'] ?? null) !== 'mysql') {
        throw new RuntimeException('Both local development sections must be MySQL');
    }
    foreach (['DATABASE' => 'POC_AI_', 'DATABASE_ZHCT' => 'POC_'] as $section => $prefix) {
        foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT',
            'DATABASE' => 'SCHEMA', 'USERNAME' => 'USER',
            'PASSWORD' => 'PASSWORD'] as $field => $suffix) {
            $config[$prefix . $suffix] = (string)($ini[$section][$field] ?? '');
        }
    }
    $pidText = trim((string)shell_exec('/usr/sbin/lsof -t -nP -iTCP:' . $port
        . ' -sTCP:LISTEN 2>/dev/null'));
    if (!ctype_digit($pidText)) {
        throw new RuntimeException('One A/B listener required');
    }
    $command = (string)shell_exec('/bin/ps eww -p ' . $pidText . ' -o command=');
    $displayRoot = str_replace('/private/tmp/', '/tmp/', $runtimeRoot);
    if (strpos($command, '-S 127.0.0.1:' . $port) === false
        || (strpos($command, $runtimeRoot . '/public') === false
            && strpos($command, $displayRoot . '/public') === false)
        || strpos($command, 'CACHE_DRIVER=') !== false
        || strpos($command, 'cache.driver=') !== false) {
        throw new RuntimeException('A/B isolated process mismatch');
    }
    foreach ($config as $key => $value) {
        if ($value === '' || preg_match('/(?:^|\s)' . preg_quote($key . '=' . $value, '/')
            . '(?=\s|$)/', $command) !== 1) {
            throw new RuntimeException('A/B process not using code local configuration');
        }
    }
    $pdo = new PDO('mysql:host=' . $config['POC_AI_HOST']
        . ';port=' . $config['POC_AI_PORT']
        . ';dbname=' . $config['POC_AI_SCHEMA'] . ';charset=utf8',
        $config['POC_AI_USER'], $config['POC_AI_PASSWORD'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
    if ((string)$pdo->query('SELECT DATABASE()')->fetchColumn()
        !== $config['POC_AI_SCHEMA']) {
        throw new RuntimeException('A/B actual AI schema mismatch');
    }
}

$stage = 'external_stub_boundary';
$boundary = json_decode((string)file_get_contents('http://127.0.0.1:' . $port
    . '/__poc_boundary'), true);
if (!is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true) {
    throw new RuntimeException('Isolated external stubs unavailable');
}

$stage = 'synthetic_operator_db_and_cache_readback';
if ($manifest['schema'] !== $config['POC_AI_SCHEMA']) {
    throw new RuntimeException('Synthetic operator schema differs from active AI connection');
}
$operatorStmt = $pdo->prepare('SELECT STORE_USER_ID,USER_NAME,REAL_NAME,'
    . ' STORE_ID,IS_SUPER,IS_DELETE FROM yoshop_store_user'
    . ' WHERE STORE_USER_ID=? AND USER_NAME=? AND STORE_ID=10001');
$operatorStmt->execute([(int)$manifest['store_user_id'], $manifest['user_name']]);
$operator = $operatorStmt->fetch(PDO::FETCH_ASSOC);
if (!is_array($operator) || (int)$operator['store_user_id']
    !== (int)$manifest['store_user_id']
    || (string)$operator['real_name'] !== (string)$manifest['user_name']
    || (int)$operator['is_super'] !== 0 || (int)$operator['is_delete'] !== 0
    || $operatorStmt->fetch(PDO::FETCH_ASSOC) !== false) {
    throw new RuntimeException('Exact synthetic merchant row not proven');
}
$operatorSha = hash('sha256', json_encode($operator,
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES));
$grantedStmt = $pdo->prepare('SELECT DISTINCT a.URL FROM yoshop_store_user_role ur'
    . ' INNER JOIN yoshop_store_role_menu rm ON rm.ROLE_ID=ur.ROLE_ID'
    . ' INNER JOIN yoshop_store_menu_api ma ON ma.MENU_ID=rm.MENU_ID'
    . ' INNER JOIN yoshop_store_api a ON a.API_ID=ma.API_ID'
    . ' WHERE ur.STORE_USER_ID=? AND ur.ROLE_ID=? AND ur.STORE_ID=10001'
    . ' AND rm.MENU_ID=? AND rm.STORE_ID=10001 ORDER BY a.URL');
$grantedStmt->execute([(int)$manifest['store_user_id'],
    (int)$manifest['role_id'], (int)$manifest['menu_id']]);
$grantedUrls = array_map(static function (array $row): string {
    return (string)$row['url'];
}, $grantedStmt->fetchAll(PDO::FETCH_ASSOC));
$expectedUrls = ['/dishes.dishes/list', '/dishes.dishes/detail',
    '/dishes.dishes/cate', '/dishes.dishes/dishLibrary',
    '/dishes.foods/list', '/dishes.foods/detail',
    '/dishes.foods/all', '/dishes.foods/cate'];
sort($expectedUrls);
if ($grantedUrls !== $expectedUrls) {
    throw new RuntimeException('Synthetic role does not grant exact catalog eight');
}
foreach ($config as $key => $value) {
    if (strpos($key, 'POC_') === 0) putenv($key . '=' . $value);
}
putenv('POC_TARGET_ROOT=' . $runtimeRoot);
putenv('POC_EXTERNAL_STUBS=1');
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $runtimeRoot . '/vendor/autoload.php';
(new \think\App($runtimeRoot))->setEnvName('poc')->initialize();
$effectiveCache = config('cache');
if (($effectiveCache['default'] ?? null) !== 'file'
    || ($effectiveCache['stores']['file']['prefix'] ?? null) !== ''
    || realpath((string)($effectiveCache['stores']['file']['path'] ?? ''))
        !== realpath($runtimeRoot . '/runtime/cache')) {
    throw new RuntimeException('Effective isolated File cache mismatch');
}
$session = \think\facade\Cache::get($token);
if (!is_array($session) || empty($session['is_login'])
    || (int)($session['store_id'] ?? 0) !== 10001
    || !is_array($session['user'] ?? null)
    || (int)($session['user']['store_user_id'] ?? 0)
        !== (int)$manifest['store_user_id']
    || (int)($session['user']['store_id'] ?? 0) !== 10001
    || (int)($session['user']['is_super'] ?? -1) !== 0) {
    throw new RuntimeException('Exact short-lived merchant cache session not proven');
}
$cacheDriver = \think\facade\Cache::store('file');
$cacheFile = $cacheDriver->getCacheKey($token);
if (!is_file($cacheFile)) throw new RuntimeException('Synthetic file-cache entry absent');
$cacheHeader = (string)file_get_contents($cacheFile, false, null, 0, 32);
clearstatcache(true, $cacheFile);
$cacheMtime = filemtime($cacheFile);
if (preg_match('/^<\?php\n\/\/(\d{12})\n/', $cacheHeader, $ttlMatch) !== 1
    || (int)$ttlMatch[1] !== 300 || $cacheMtime === false
    || $cacheMtime + 300 !== (int)$manifest['expires_at']) {
    throw new RuntimeException('Synthetic file-cache TTL proof mismatch');
}

$stage = 'read_only_schema_and_natural_fixture';
$tables = [
    'yoshop_dishes' => ['id', 'id,name,category,file_id,weight,energy,store_id,is_delete,user_delete'],
    'yoshop_dishes_cate' => ['id', 'id,name,pid'],
    'yoshop_dishes_foods' => ['id', 'id,dishes_id,foods_id,foods_weight,foods_ratio,store_id,is_delete'],
    'yoshop_foods' => ['id', 'id,name,category,energy,store_id,is_delete'],
    'yoshop_foods_cate' => ['id', 'id,name,pid'],
    'yoshop_dishes_unsuitable' => ['id', 'id,dishes_id,meal_times,store_id,is_delete'],
    'yoshop_dishes_tag' => ['id', 'id,dishes_id,store_id,is_delete'],
    'yoshop_upload_file' => ['file_id', 'file_id,store_id,domain,file_path'],
    'yoshop_config_enum' => ['id', 'id,enum_type,enum_key,enum_name,is_delete'],
];
foreach ($tables as $table => [$pk, $columns]) {
    $pdo->query('SELECT ' . $columns . ' FROM ' . $table . ' WHERE 1=0');
}
$candidate = $pdo->query('SELECT d.ID AS dish_id,d.NAME AS dish_name,'
    . ' d.CATEGORY AS dish_category,d.FILE_ID AS file_id,'
    . ' df.FOODS_ID AS food_id,f.NAME AS food_name,'
    . ' f.CATEGORY AS food_category'
    . ' FROM yoshop_dishes d INNER JOIN yoshop_dishes_foods df'
    . ' ON df.DISHES_ID=d.ID INNER JOIN yoshop_foods f ON f.ID=df.FOODS_ID'
    . ' WHERE d.STORE_ID=10001 AND d.IS_DELETE=0 AND d.USER_DELETE=0'
    . ' AND d.CATEGORY>0 AND f.CATEGORY>0'
    . ' AND df.STORE_ID=10001 AND df.IS_DELETE=0'
    . ' AND f.STORE_ID=10001 AND f.IS_DELETE=0 ORDER BY d.ID,df.ID')
    ->fetch(PDO::FETCH_ASSOC);
if (!is_array($candidate) || (int)$candidate['dish_id'] <= 0
    || (int)$candidate['food_id'] <= 0) {
    throw new RuntimeException('Nonempty natural dish-food relation unavailable');
}
$dishId = (int)$candidate['dish_id'];
$foodId = (int)$candidate['food_id'];
$fingerprint = static function (PDO $db, array $scope) use ($tables): array {
    $out = [];
    foreach ($scope as $table => [$where, $bind]) {
        if (!isset($tables[$table])) throw new RuntimeException('Fingerprint table not whitelisted');
        $stmt = $db->prepare('SELECT * FROM ' . $table . ' WHERE ' . $where
            . ' ORDER BY ' . $tables[$table][0]);
        $stmt->execute($bind);
        $hash = hash_init('sha256');
        $count = 0;
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            ksort($row);
            $line = json_encode($row, JSON_UNESCAPED_UNICODE
                | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE);
            if (!is_string($line)) throw new RuntimeException('Unhashable source row');
            hash_update($hash, $line . "\n");
            $count++;
        }
        $out[$table] = ['rows' => $count, 'sha256' => hash_final($hash)];
    }
    return $out;
};
$scope = [
    'yoshop_dishes' => ['ID=? AND STORE_ID=10001', [$dishId]],
    'yoshop_foods' => ['ID=? AND STORE_ID=10001', [$foodId]],
    'yoshop_dishes_foods' => ['DISHES_ID=? AND STORE_ID=10001', [$dishId]],
    'yoshop_dishes_cate' => ['1=1', []],
    'yoshop_foods_cate' => ['1=1', []],
    'yoshop_dishes_unsuitable' => ['DISHES_ID=? AND STORE_ID=10001', [$dishId]],
    'yoshop_dishes_tag' => ['DISHES_ID=? AND STORE_ID=10001', [$dishId]],
    'yoshop_config_enum' => ['1=1', []],
];
if ((int)$candidate['file_id'] > 0) {
    $scope['yoshop_upload_file'] = ['FILE_ID=? AND STORE_ID=10001',
        [(int)$candidate['file_id']]];
}
$before = $fingerprint($pdo, $scope);
if ($before['yoshop_dishes']['rows'] !== 1
    || $before['yoshop_foods']['rows'] !== 1
    || $before['yoshop_dishes_foods']['rows'] < 1
    || $before['yoshop_dishes_cate']['rows'] < 1
    || $before['yoshop_foods_cate']['rows'] < 1) {
    throw new RuntimeException('Natural source fixture is incomplete');
}

// Exact whitelist. All eight methods come from ai_store's actual axios POST
// callers; Dishes/all is intentionally excluded pending separate reachability.
$cases = [
    ['dish_list', '/store/dishes.dishes/list',
        ['search' => $candidate['dish_name']], ['list', 'data'], $dishId],
    ['dish_detail', '/store/dishes.dishes/detail',
        ['id' => $dishId], ['detail'], $dishId],
    ['dish_cate', '/store/dishes.dishes/cate',
        [], ['list'], (int)$candidate['dish_category']],
    ['dish_library', '/store/dishes.dishes/dishLibrary',
        ['name' => $candidate['dish_name']], ['list'], $dishId],
    ['food_list', '/store/dishes.foods/list',
        ['search' => $candidate['food_name']], ['list', 'data'], $foodId],
    ['food_detail', '/store/dishes.foods/detail',
        ['id' => $foodId], ['detail'], $foodId],
    ['food_all', '/store/dishes.foods/all',
        ['search' => $candidate['food_name']], ['list'], $foodId],
    ['food_cate', '/store/dishes.foods/cate',
        [], ['list'], (int)$candidate['food_category']],
];
$typeShape = null;
$typeShape = static function ($value, int $depth = 0) use (&$typeShape) {
    if (!is_array($value)) {
        if (is_string($value) && preg_match('/^-?\d+\.\d+$/', $value)) {
            return 'decimal_string_' . strlen(substr(strrchr($value, '.'), 1)) . 'dp';
        }
        return gettype($value);
    }
    if ($depth >= 5) return ['array_count' => count($value)];
    if ($value === []) return ['empty_array' => true];
    if (array_keys($value) === range(0, count($value) - 1)) {
        return ['list_count' => count($value),
            'first_item' => $typeShape($value[0], $depth + 1)];
    }
    $keys = array_slice(array_keys($value), 0, 40);
    $shape = [];
    foreach ($keys as $key) {
        $shape[hash('sha256', (string)$key)] = $typeShape($value[$key], $depth + 1);
    }
    ksort($shape);
    return $shape;
};
$containsId = null;
$containsId = static function ($value, int $expected, int $depth = 0)
    use (&$containsId): bool {
    if (!is_array($value) || $depth > 8) return false;
    foreach ($value as $key => $item) {
        if (is_string($key) && strcasecmp($key, 'id') === 0
            && is_scalar($item) && (int)$item === $expected) return true;
        if (is_array($item) && $containsId($item, $expected, $depth + 1)) return true;
    }
    return false;
};
$results = [];
$failure = null;
$stage = 'business_http';
foreach ($cases as [$label, $path, $form, $payloadPath, $expectedId]) {
    if ((int)$manifest['expires_at'] <= time() + 20) {
        $failure = 'synthetic_session_ttl_too_short';
        break;
    }
    $url = 'http://127.0.0.1:' . $port . $path . '?storeId=10001';
    $context = stream_context_create(['http' => [
        'method' => 'POST', 'timeout' => 15, 'ignore_errors' => true,
        'header' => 'Access-Token: ' . $token . "\r\n"
            . "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
    ]]);
    $start = microtime(true);
    try {
        $raw = file_get_contents($url, false, $context);
    } catch (Throwable $transportError) {
        $raw = false;
    }
    $elapsed = (int)round((microtime(true) - $start) * 1000);
    $httpLine = $http_response_header[0] ?? '';
    $http = preg_match('/HTTP\/\S+\s+(\d+)/', $httpLine, $matches)
        ? (int)$matches[1] : 0;
    $body = is_string($raw) ? json_decode($raw, true) : null;
    $data = is_array($body) ? ($body['data'] ?? null) : null;
    $payload = $data;
    foreach ($payloadPath as $part) {
        $payload = is_array($payload) ? ($payload[$part] ?? null) : null;
    }
    $hasExpectedId = $expectedId > 0 && $containsId($payload, $expectedId);
    $results[$label] = ['http' => $http,
        'business_status' => is_array($body) ? ($body['status'] ?? null) : null,
        'elapsed_ms' => $elapsed,
        'response_sha256' => is_string($raw) ? hash('sha256', $raw) : null,
        'data_type' => gettype($data),
        'top_level_data_keyset_sha256' => is_array($data)
            ? hash('sha256', implode('|', array_map('strval', array_keys($data))))
            : null,
        'data_type_shape' => $typeShape($data),
        'contains_original_row_id' => $hasExpectedId,
    ];
    if ($http !== 200 || !is_array($body) || (int)($body['status'] ?? 0) !== 200
        || !is_array($data) || !$hasExpectedId || $elapsed >= 15000) {
        $failure = $label;
        break;
    }
}

$stage = 'source_readback';
$after = $fingerprint($pdo, $scope);
$operatorStmt->execute([(int)$manifest['store_user_id'], $manifest['user_name']]);
$operatorAfter = $operatorStmt->fetch(PDO::FETCH_ASSOC);
$operatorUnchanged = is_array($operatorAfter)
    && hash_equals($operatorSha, hash('sha256', json_encode($operatorAfter,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)))
    && $operatorStmt->fetch(PDO::FETCH_ASSOC) === false;
$sourceUnchanged = $before === $after && $operatorUnchanged;
echo json_encode(['status' => $failure === null && $sourceUnchanged
        ? 'http_batch_completed_not_full_contract' : 'batch_red_or_blocked',
    'target' => $target, 'method' => 'POST',
    'natural_relation_present' => true,
    'natural_relation_identity_sha256' => hash('sha256', $dishId . ':' . $foodId),
    'source_rows_unchanged' => $sourceUnchanged,
    'synthetic_operator_unchanged' => $operatorUnchanged,
    'source_scope_row_counts' => array_map(static function (array $item): int {
        return $item['rows'];
    }, $after),
    'cases' => $results, 'first_failure' => $failure,
    'business_http_count' => count($results), 'dml_sent' => false,
    'full_contract_pass' => false], JSON_UNESCAPED_SLASHES), PHP_EOL;
if ($failure !== null || !$sourceUnchanged || count($results) !== 8) exit(3);
