<?php
declare(strict_types=1);

// Reviewed-test candidate, DEFAULT DENY for all write/cleanup phases.
$phase = (string)($argv[1] ?? '');
$lane = (string)($argv[2] ?? '');
$marker = (string)($argv[3] ?? '');
if (!in_array($phase, ['--preflight', '--create', '--cleanup', '--audit', '--recover-probe'], true)
    || !in_array($lane, ['B', 'C'], true)
    || !preg_match('/^CDXSPEC[0-9A-F]{12}$/', $marker)) {
    fwrite(STDERR, "DENIED: exact phase/lane/marker required\n");
    exit(78);
}
if (in_array($phase, ['--create', '--cleanup', '--audit'], true)) {
    $reviewed = (string)getenv('CDX_SPEC_FIXTURE_REVIEWED_SHA256');
    if ((string)getenv('CDX_SPEC_FIXTURE_APPROVAL') !== 'OWNED_SPEC_FIXTURE_V1'
        || !preg_match('/^[0-9a-f]{64}$/', $reviewed)
        || ($lane === 'B' && hash_file('sha256', __FILE__) !== $reviewed)
        || ($lane === 'C' && (string)getenv('CDX_SPEC_FIXTURE_SOURCE_CHECKED') !== '1')) {
        fwrite(STDERR, "DENIED: exact reviewed SHA/write gate required\n");
        exit(78);
    }
}
$stage = 'start';
set_exception_handler(static function (Throwable $e) use (&$stage): void {
    fwrite(STDERR, 'SPEC_FIXTURE_STOP stage=' . $stage
        . ' class=' . get_class($e) . "\n");
    exit(3);
});
$root = $lane === 'B' ? '/private/tmp/zhct-http-ai-macos.x2CCZX'
    : '/tmp/zhct-ai-http-20260929.3XDj3w';
$pid = $lane === 'B' ? 13127 : 53772;
$port = $lane === 'B' ? 18101 : 18102;
$wanted = [
    'app/store/controller/goods/Spec.php' => '6138dfe6193274b871cff2fd20d350308f49b81346872d609ce06eff961e580c',
    'app/common/model/GoodsSpecRel.php' => '58c1ea08bb58963f43532b11dd4c0a008f1aa33f97bb553ba9361640c7b05f01',
    'app/common/model/Spec.php' => '6fb7e5f40c24b0c9ae7b76c89e88d8a1be6a180a3d0703efad717ad08ab23ba6',
    'app/common/model/SpecValue.php' => 'a173276dd9c7a7bd9d607658fda58e0b10e89e81870d3bf0100776c157af80d4',
];
foreach ($wanted as $file => $sha) {
    if (!is_file($root . '/' . $file) || hash_file('sha256', $root . '/' . $file) !== $sha) {
        throw new RuntimeException('Isolated source drift');
    }
}
if ($lane === 'B') {
    $cfg = parse_ini_file('/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local', true, INI_SCANNER_RAW)['DATABASE'] ?? null;
    $ps = (string)shell_exec('/bin/ps eww -p ' . $pid . ' -o command=');
    $owner = trim((string)shell_exec('/usr/sbin/lsof -nP -t -iTCP:' . $port . ' -sTCP:LISTEN 2>/dev/null'));
    if (!is_array($cfg) || strtolower((string)($cfg['TYPE'] ?? '')) !== 'mysql'
        || (string)($cfg['HOSTPORT'] ?? '') !== '3306' || $owner !== (string)$pid
        || strpos($ps, 'POC_DIALECT=mysql') === false
        || strpos($ps, 'POC_EXTERNAL_STUBS=1') === false) {
        throw new RuntimeException('B worker/config drift');
    }
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT', 'DATABASE' => 'SCHEMA',
              'USERNAME' => 'USER', 'PASSWORD' => 'PASSWORD'] as $from => $to) {
        $v = (string)($cfg[$from] ?? '');
        if ($v === '' || strpos($ps, 'POC_AI_' . $to . '=' . $v) === false) {
            throw new RuntimeException('B worker differs from code local config');
        }
    }
    $connect = static function () use ($cfg): PDO {
        return new PDO('mysql:host=' . $cfg['HOSTNAME'] . ';port=' . $cfg['HOSTPORT']
            . ';dbname=' . $cfg['DATABASE'] . ';charset=utf8', $cfg['USERNAME'], $cfg['PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    };
} else {
    $env = [];
    foreach (explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ')) as $item) {
        $pair = explode('=', $item, 2);
        if (count($pair) === 2) $env[$pair[0]] = $pair[1];
    }
    if (($env['POC_TARGET_ROOT'] ?? '') !== $root
        || ($env['POC_BASE_URL'] ?? '') !== 'http://127.0.0.1:' . $port
        || ($env['POC_DIALECT'] ?? '') !== 'dm'
        || ($env['POC_EXTERNAL_STUBS'] ?? '') !== '1'
        || ($env['POC_AI_HOST'] ?? '') !== '127.0.0.1'
        || ($env['POC_AI_PORT'] ?? '') !== '5238'
        || ($env['POC_AI_SCHEMA'] ?? '') !== 'AIZHCT_APP'
        || ($env['POC_AI_USER'] ?? '') !== 'AIZHCT_APP'
        || !in_array('dm', PDO::getAvailableDrivers(), true)) {
        throw new RuntimeException('C worker/config drift');
    }
    $connect = static function () use ($env): PDO {
        $db = new PDO('dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP',
            $env['POC_AI_USER'], $env['POC_AI_PASSWORD'],
            [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
        if ($db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn() !== 'AIZHCT_APP') {
            throw new RuntimeException('C schema drift');
        }
        return $db;
    };
}
$db = $connect();
$stage = 'metadata';
$allTables = ['goods' => 'GOODS_ID', 'goods_sku' => 'ID',
    'goods_image' => 'ID', 'goods_category_rel' => 'ID',
    'category' => 'CATEGORY_ID', 'upload_file' => 'FILE_ID',
    'spec' => 'SPEC_ID', 'spec_value' => 'SPEC_VALUE_ID',
    'goods_spec_rel' => 'ID'];
$t = static function (string $logical) use ($lane, $allTables): string {
    if (!isset($allTables[$logical])) throw new RuntimeException('Unexpected table');
    return 'yoshop_' . ($lane === 'B' ? $logical : strtoupper($logical));
};
$tableNames = array_map($t, array_keys($allTables));
$query = static function (PDO $d, string $sql, array $args = []): array {
    $s = $d->prepare($sql);
    $s->execute($args);
    return $s->fetchAll(PDO::FETCH_ASSOC);
};
$count = static function (PDO $d, string $sql, array $args = []): int {
    $s = $d->prepare($sql);
    $s->execute($args);
    return (int)$s->fetchColumn();
};
$expectedRefs = [
    'YOSHOP_CART.GOODS_ID', 'YOSHOP_COMMENT.GOODS_ID',
    'YOSHOP_GOODS.GOODS_ID', 'YOSHOP_GOODS_CATEGORY_REL.GOODS_ID',
    'YOSHOP_GOODS_IMAGE.GOODS_ID', 'YOSHOP_GOODS_SERVICE_REL.GOODS_ID',
    'YOSHOP_GOODS_SKU.GOODS_ID', 'YOSHOP_GOODS_SPEC_REL.GOODS_ID',
    'YOSHOP_GOODS_SPEC_REL.SPEC_ID', 'YOSHOP_GOODS_SPEC_REL.SPEC_VALUE_ID',
    'YOSHOP_ORDER_DELIVERY_GOODS.GOODS_ID', 'YOSHOP_ORDER_GOODS.GOODS_ID',
    'YOSHOP_SPEC.SPEC_ID', 'YOSHOP_SPEC_VALUE.SPEC_ID',
    'YOSHOP_SPEC_VALUE.SPEC_VALUE_ID',
];
sort($expectedRefs);
$refSql = $lane === 'B'
    ? "SELECT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME LIKE 'yoshop_%' AND UPPER(COLUMN_NAME) IN ('GOODS_ID','SPEC_ID','SPEC_VALUE_ID','GOODS_SPEC_REL_ID')"
    : "SELECT TABLE_NAME,COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME LIKE 'YOSHOP_%' AND COLUMN_NAME IN ('GOODS_ID','SPEC_ID','SPEC_VALUE_ID','GOODS_SPEC_REL_ID')";
$refs = $query($db, $refSql);
$seenRefs = [];
foreach ($refs as $ref) {
    $name = strtoupper((string)$ref['table_name']);
    $col = strtoupper((string)$ref['column_name']);
    if (!preg_match('/^[A-Z_][A-Z0-9_]*$/', $name)
        || !preg_match('/^[A-Z_][A-Z0-9_]*$/', $col)) {
        throw new RuntimeException('Reference metadata unsafe');
    }
    $seenRefs[] = $name . '.' . $col;
}
sort($seenRefs);
if ($seenRefs !== $expectedRefs) throw new RuntimeException('15-reference inventory drift');
$marks = implode(',', array_fill(0, count($tableNames), '?'));
if ($lane === 'B') {
    $triggers = $count($db, 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE IN (' . $marks . ')', $tableNames);
    $fks = $count($db, 'SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND (TABLE_NAME IN (' . $marks . ') OR REFERENCED_TABLE_NAME IN (' . $marks . '))', array_merge($tableNames, $tableNames));
} else {
    $quoted = "'" . implode("','", $tableNames) . "'";
    $triggers = $count($db, 'SELECT COUNT(*) FROM USER_TRIGGERS WHERE TABLE_NAME IN (' . $quoted . ')');
    $fks = $count($db, "SELECT COUNT(*) FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R' AND (TABLE_NAME IN (" . $quoted . ") OR R_CONSTRAINT_NAME IN (SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME IN (" . $quoted . ')))');
}
if ($triggers !== 0 || $fks !== 0) throw new RuntimeException('Trigger/FK drift');
$targetColumns = ['goods' => ['GOODS_ID', 'GOODS_NAME', 'GOODS_NO', 'SPEC_TYPE',
        'CONTENT', 'STOCK_TOTAL', 'STATUS', 'IS_DELETE', 'STORE_ID', 'CREATE_TIME', 'UPDATE_TIME'],
    'spec' => ['SPEC_ID', 'SPEC_NAME', 'STORE_ID', 'CREATE_TIME'],
    'spec_value' => ['SPEC_VALUE_ID', 'SPEC_VALUE', 'SPEC_ID', 'STORE_ID', 'CREATE_TIME'],
    'goods_spec_rel' => ['ID', 'GOODS_ID', 'SPEC_ID', 'SPEC_VALUE_ID', 'STORE_ID', 'CREATE_TIME']];
foreach ($targetColumns as $logical => $required) {
    $table = $t($logical);
    if ($lane === 'B') {
        $rows = $query($db, 'SELECT COLUMN_NAME,EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=?', [$table]);
        $auto = [];
        foreach ($rows as $r) {
            if (stripos((string)$r['extra'], 'auto_increment') !== false) {
                $auto[] = strtoupper((string)$r['column_name']);
            }
        }
    } else {
        $rows = $query($db, 'SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?', [$table]);
        $s = $db->prepare('CALL SP_TABLEDEF(?, ?)');
        $s->execute(['AIZHCT_APP', $table]);
        $ddl = $s->fetchColumn();
        if (is_resource($ddl)) $ddl = stream_get_contents($ddl);
        if (!is_string($ddl) || $ddl === '') throw new RuntimeException('DM DDL unavailable');
        preg_match_all('/"([A-Za-z_][A-Za-z0-9_]*)"\s+[A-Za-z0-9_]+(?:\([^)]*\))?\s+(?:AUTO_INCREMENT|IDENTITY\b)/i', $ddl, $matches);
        $auto = array_map('strtoupper', $matches[1]);
    }
    $actual = array_map(static function (array $r): string {
        return strtoupper((string)$r['column_name']);
    }, $rows);
    if (array_diff($required, $actual)
        || $auto !== [$required[0]]) {
        throw new RuntimeException('Target columns/identity drift');
    }
}
$natural = static function (PDO $d, array $excluded = []) use ($allTables, $t): array {
    $out = [];
    foreach ($allTables as $logical => $pk) {
        $s = $d->query('SELECT * FROM ' . $t($logical)
            . ' WHERE STORE_ID=10001 ORDER BY ' . $pk);
        $h = hash_init('sha256');
        $n = 0;
        $omit = array_map('intval', $excluded[$logical] ?? []);
        while (($row = $s->fetch(PDO::FETCH_ASSOC)) !== false) {
            if (in_array((int)($row[strtolower($pk)] ?? 0), $omit, true)) continue;
            hash_update($h, serialize(array_values($row)));
            if (++$n > 30000) throw new RuntimeException('Natural source cap exceeded');
        }
        $out[$logical] = ['count' => $n, 'sha256' => hash_final($h)];
    }
    return $out;
};
$naturalCleanupScope = static function (array $snapshot): array {
    // This test never touches upload_file; concurrent file metadata writes must not strand owned rows.
    unset($snapshot['upload_file']);
    return $snapshot;
};
$markerCounts = static function (PDO $d) use ($t, $count, $marker): array {
    return [
        'goods' => $count($d, 'SELECT COUNT(*) FROM ' . $t('goods')
            . ' WHERE GOODS_NAME=? AND STORE_ID=10001', [$marker]),
        'spec' => $count($d, 'SELECT COUNT(*) FROM ' . $t('spec')
            . ' WHERE SPEC_NAME=? AND STORE_ID=10001', [$marker . 'SPEC']),
        'value' => $count($d, 'SELECT COUNT(*) FROM ' . $t('spec_value')
            . ' WHERE SPEC_VALUE IN (?,?) AND STORE_ID=10001',
            [$marker . 'VAL1', $marker . 'VAL2']),
    ];
};
$journalDir = sys_get_temp_dir() . '/cdx-spec-fixture-'
    . strtolower($lane) . '-' . strtolower($marker);
$journalPath = $journalDir . '/journal.json';
$tokenDir = sys_get_temp_dir() . '/cdx-spec-token-'
    . strtolower($lane) . '-' . strtolower($marker);
$saveJournal = static function (array $j) use ($journalPath): void {
    $body = json_encode($j, JSON_UNESCAPED_SLASHES);
    $tmp = $journalPath . '.tmp';
    if (!is_string($body) || file_put_contents($tmp, $body, LOCK_EX) !== strlen($body)
        || !chmod($tmp, 0600) || !rename($tmp, $journalPath)) {
        throw new RuntimeException('Private journal write failed');
    }
};
$insert = [
    'goods' => 'INSERT INTO ' . $t('goods') . ' (GOODS_NAME,GOODS_NO,SPEC_TYPE,CONTENT,STOCK_TOTAL,STATUS,IS_DELETE,STORE_ID,CREATE_TIME,UPDATE_TIME) VALUES (?,?,20,?,0,20,0,10001,?,?)',
    'spec' => 'INSERT INTO ' . $t('spec') . ' (SPEC_NAME,STORE_ID,CREATE_TIME) VALUES (?,10001,?)',
    'value' => 'INSERT INTO ' . $t('spec_value') . ' (SPEC_VALUE,SPEC_ID,STORE_ID,CREATE_TIME) VALUES (?,?,10001,?)',
    'relation' => 'INSERT INTO ' . $t('goods_spec_rel') . ' (GOODS_ID,SPEC_ID,SPEC_VALUE_ID,STORE_ID,CREATE_TIME) VALUES (?,?,?,10001,?)',
];
foreach ($insert as $sql) $db->prepare($sql);
if ($phase === '--preflight') {
    $before = $natural($db);
    $marksNow = $markerCounts($db);
    $zero = $marksNow === ['goods' => 0, 'spec' => 0, 'value' => 0];
    $journalAbsent = !file_exists($journalDir) && !file_exists($tokenDir);
    echo json_encode(['lane' => $lane, 'phase' => $phase, 'marker' => $marker,
        'marker_zero' => $zero, 'journal_absent' => $journalAbsent,
        'source' => $before, 'source_files_sha256' => $wanted,
        'triggers' => 0, 'foreign_keys' => 0,
        'exact_reference_columns' => count($seenRefs), 'insert_prepare_ready' => true,
        'business_http' => 0, 'business_dml' => 0], JSON_UNESCAPED_SLASHES), "\n";
    exit($zero && $journalAbsent ? 0 : 3);
}
$journal = is_file($journalPath)
    ? json_decode((string)file_get_contents($journalPath), true) : null;
if ($phase === '--recover-probe') {
    echo json_encode(['lane' => $lane, 'phase' => $phase, 'marker' => $marker,
        'journal_phase' => is_array($journal) ? ($journal['phase'] ?? 'invalid') : 'absent',
        'marker_counts' => $markerCounts($db),
        'journal_ids' => is_array($journal) ? ($journal['ids'] ?? []) : [],
        'business_http' => 0, 'business_dml' => 0], JSON_UNESCAPED_SLASHES), "\n";
    exit(0);
}
$oneId = static function (PDO $d, string $logical, string $pk, string $field,
    string $value) use ($query, $t): int {
    $rows = $query($d, 'SELECT ' . $pk . ' FROM ' . $t($logical)
        . ' WHERE STORE_ID=10001 AND ' . $field . '=?', [$value]);
    if (count($rows) !== 1 || (int)$rows[0][strtolower($pk)] <= 0) {
        throw new RuntimeException('Owned identity unresolved');
    }
    return (int)$rows[0][strtolower($pk)];
};
$ownRows = static function (PDO $d, array $ids) use ($query, $t, $marker): array {
    $spec = [
        ['goods', 'GOODS_ID', $ids['goods'], 'GOODS_NAME', $marker],
        ['spec', 'SPEC_ID', $ids['spec'], 'SPEC_NAME', $marker . 'SPEC'],
        ['spec_value', 'SPEC_VALUE_ID', $ids['value1'], 'SPEC_VALUE', $marker . 'VAL1'],
        ['spec_value', 'SPEC_VALUE_ID', $ids['value2'], 'SPEC_VALUE', $marker . 'VAL2'],
        ['goods_spec_rel', 'ID', $ids['rel1'], 'GOODS_ID', $ids['goods']],
        ['goods_spec_rel', 'ID', $ids['rel2'], 'GOODS_ID', $ids['goods']],
    ];
    $out = [];
    foreach ($spec as $i => [$logical, $pk, $id, $field, $value]) {
        $rows = $query($d, 'SELECT * FROM ' . $t($logical)
            . ' WHERE ' . $pk . '=? AND STORE_ID=10001 AND ' . $field . '=?', [$id, $value]);
        if (count($rows) !== 1) throw new RuntimeException('Owned row missing/ambiguous');
        $out[$i] = $rows[0];
    }
    foreach ([4 => 'value1', 5 => 'value2'] as $i => $key) {
        if ((int)$out[$i]['spec_id'] !== (int)$ids['spec']
            || (int)$out[$i]['spec_value_id'] !== (int)$ids[$key]) {
            throw new RuntimeException('Owned relation drift');
        }
    }
    foreach ([2, 3] as $i) {
        if ((int)$out[$i]['spec_id'] !== (int)$ids['spec']) {
            throw new RuntimeException('Owned value link drift');
        }
    }
    if ((int)$out[0]['spec_type'] !== 20 || (int)$out[0]['status'] !== 20
        || (int)$out[0]['stock_total'] !== 0 || (int)$out[0]['is_delete'] !== 0) {
        throw new RuntimeException('Owned goods safety state drift');
    }
    return $out;
};
$excludeOwn = static function (array $ids): array {
    return ['goods' => [$ids['goods']], 'spec' => [$ids['spec']],
        'spec_value' => [$ids['value1'], $ids['value2']],
        'goods_spec_rel' => [$ids['rel1'], $ids['rel2']]];
};
$scanRefs = static function (PDO $d, array $ids, bool $expectOwn) use ($refs, $count, $t): bool {
    foreach ($refs as $ref) {
        $name = (string)$ref['table_name'];
        $column = strtoupper((string)$ref['column_name']);
        $targets = $column === 'GOODS_ID' ? [$ids['goods']]
            : ($column === 'SPEC_ID' ? [$ids['spec']]
                : ($column === 'SPEC_VALUE_ID'
                    ? [$ids['value1'], $ids['value2']] : []));
        foreach ($targets as $position => $target) {
            $expected = 0;
            if ($expectOwn) {
                if ($column === 'GOODS_ID' && strcasecmp($name, $t('goods')) === 0) $expected = 1;
                if ($column === 'GOODS_ID' && strcasecmp($name, $t('goods_spec_rel')) === 0) $expected = 2;
                if ($column === 'SPEC_ID' && strcasecmp($name, $t('spec')) === 0) $expected = 1;
                if ($column === 'SPEC_ID' && strcasecmp($name, $t('spec_value')) === 0) $expected = 2;
                if ($column === 'SPEC_ID' && strcasecmp($name, $t('goods_spec_rel')) === 0) $expected = 2;
                if ($column === 'SPEC_VALUE_ID' && strcasecmp($name, $t('spec_value')) === 0) $expected = 1;
                if ($column === 'SPEC_VALUE_ID' && strcasecmp($name, $t('goods_spec_rel')) === 0) $expected = 1;
            }
            if ($count($d, 'SELECT COUNT(*) FROM ' . $name . ' WHERE ' . $column . '=?', [$target]) !== $expected) {
                throw new RuntimeException('Unexpected external/owned reference');
            }
        }
    }
    return true;
};
if ($phase === '--create') {
    if ($journal !== null || file_exists($journalDir) || file_exists($tokenDir)
        || $markerCounts($db) !== ['goods' => 0, 'spec' => 0, 'value' => 0]) {
        throw new RuntimeException('Marker/journal not pristine');
    }
    $baseline = $natural($db);
    if (!mkdir($journalDir, 0700) || !chmod($journalDir, 0700)) {
        throw new RuntimeException('Private journal directory creation failed');
    }
    $journal = ['lane' => $lane, 'marker' => $marker, 'phase' => 'before_insert',
        'source_before' => $baseline, 'ids' => [], 'created_at' => time()];
    $saveJournal($journal);
    $stage = 'create-transaction';
    $now = time();
    $ids = [];
    $db->beginTransaction();
    try {
        $exec = static function (string $sql, array $args) use ($db): void {
            $s = $db->prepare($sql);
            $s->execute($args);
            if ($s->rowCount() !== 1) throw new RuntimeException('Insert affected-row mismatch');
        };
        $exec($insert['goods'], [$marker, $marker, '<p>' . $marker . '</p>', $now, $now]);
        $ids['goods'] = $oneId($db, 'goods', 'GOODS_ID', 'GOODS_NAME', $marker);
        $exec($insert['spec'], [$marker . 'SPEC', $now]);
        $ids['spec'] = $oneId($db, 'spec', 'SPEC_ID', 'SPEC_NAME', $marker . 'SPEC');
        foreach ([1, 2] as $i) {
            $value = $marker . 'VAL' . $i;
            $exec($insert['value'], [$value, $ids['spec'], $now]);
            $ids['value' . $i] = $oneId($db, 'spec_value', 'SPEC_VALUE_ID', 'SPEC_VALUE', $value);
            $exec($insert['relation'], [$ids['goods'], $ids['spec'], $ids['value' . $i], $now]);
            $matches = $query($db, 'SELECT ID FROM ' . $t('goods_spec_rel')
                . ' WHERE GOODS_ID=? AND SPEC_ID=? AND SPEC_VALUE_ID=? AND STORE_ID=10001',
                [$ids['goods'], $ids['spec'], $ids['value' . $i]]);
            if (count($matches) !== 1 || (int)$matches[0]['id'] <= 0) {
                throw new RuntimeException('Owned relation ID unresolved');
            }
            $ids['rel' . $i] = (int)$matches[0]['id'];
        }
        if (count($ids) !== 6 || min($ids) <= 0 || count($ownRows($db, $ids)) !== 6) {
            throw new RuntimeException('Owned six-row fixture incomplete');
        }
        $db->commit();
        $journal['phase'] = 'fixture_committed';
        $journal['ids'] = $ids;
        $journal['own_sha256'] = hash('sha256', serialize($ownRows($connect(), $ids)));
        $saveJournal($journal);
        echo json_encode(['lane' => $lane, 'phase' => $phase, 'marker' => $marker,
            'ids' => $ids, 'created_rows' => 6,
            'independent_audit_required' => true], JSON_UNESCAPED_SLASHES), "\n";
        exit(0);
    } catch (Throwable $e) {
        if ($db->inTransaction()) $db->rollBack();
        $journal['phase'] = 'needs_recovery_review';
        $journal['ids'] = $ids;
        $saveJournal($journal);
        throw $e;
    }
}
if (!is_array($journal) || ($journal['lane'] ?? '') !== $lane
    || ($journal['marker'] ?? '') !== $marker
    || !is_array($journal['source_before'] ?? null)
    || !is_array($journal['ids'] ?? null)) {
    throw new RuntimeException('Exact recovery journal absent/invalid');
}
$ids = $journal['ids'];
foreach (['goods', 'spec', 'value1', 'value2', 'rel1', 'rel2'] as $key) {
    if (!isset($ids[$key]) || (int)$ids[$key] <= 0) {
        throw new RuntimeException('Exact journal ID missing');
    }
    $ids[$key] = (int)$ids[$key];
}
if ($phase === '--audit') {
    $stage = 'independent-audit';
    if (file_exists($tokenDir)) throw new RuntimeException('Token audit not completed');
    $verify = $connect();
    $marksNow = $markerCounts($verify);
    $refsZero = $scanRefs($verify, $ids, false);
    $sourceSame = $naturalCleanupScope($natural($verify))
        === $naturalCleanupScope($journal['source_before']);
    $zero = $marksNow === ['goods' => 0, 'spec' => 0, 'value' => 0]
        && $count($verify, 'SELECT COUNT(*) FROM ' . $t('goods')
            . ' WHERE GOODS_ID=?', [$ids['goods']]) === 0
        && $count($verify, 'SELECT COUNT(*) FROM ' . $t('spec')
            . ' WHERE SPEC_ID=?', [$ids['spec']]) === 0
        && $count($verify, 'SELECT COUNT(*) FROM ' . $t('spec_value')
            . ' WHERE SPEC_VALUE_ID IN (?,?)', [$ids['value1'], $ids['value2']]) === 0
        && $count($verify, 'SELECT COUNT(*) FROM ' . $t('goods_spec_rel')
            . ' WHERE ID IN (?,?)', [$ids['rel1'], $ids['rel2']]) === 0;
    $phaseOkay = in_array($journal['phase'] ?? '',
        ['cleanup_committed', 'fixture_committed'], true);
    if (!$zero || !$refsZero || !$sourceSame || !$phaseOkay) {
        throw new RuntimeException('Independent source/owned-zero audit failed');
    }
    if (!unlink($journalPath) || !rmdir($journalDir)
        || file_exists($journalPath) || is_dir($journalDir)) {
        throw new RuntimeException('Verified journal removal failed');
    }
    echo json_encode(['lane' => $lane, 'phase' => $phase,
        'owned_marker_and_ids_zero' => true, 'references_zero' => true,
        'eight_relevant_table_natural_sha_unchanged' => true,
        'journal_absent' => true, 'business_http' => 0, 'business_dml' => 0],
        JSON_UNESCAPED_SLASHES), "\n";
    exit(0);
}
if ($phase !== '--cleanup' || ($journal['phase'] ?? '') !== 'fixture_committed') {
    throw new RuntimeException('Fixture not in exact cleanup state');
}
$stage = 'cleanup-preflight';
if (file_exists($tokenDir)) throw new RuntimeException('Token audit not completed');
$owned = $ownRows($db, $ids);
if (hash('sha256', serialize($owned)) !== (string)($journal['own_sha256'] ?? '')) {
    throw new RuntimeException('Owned fixture row drift; cleanup refused');
}
if (!$scanRefs($db, $ids, true)
    || $naturalCleanupScope($natural($db, $excludeOwn($ids)))
        !== $naturalCleanupScope($journal['source_before'])) {
    throw new RuntimeException('Natural source drift; cleanup refused');
}
$stage = 'exact-cleanup';
$db->beginTransaction();
try {
    foreach ([
        ['goods_spec_rel', 'ID', $ids['rel1'], 'SPEC_VALUE_ID', $ids['value1']],
        ['goods_spec_rel', 'ID', $ids['rel2'], 'SPEC_VALUE_ID', $ids['value2']],
        ['spec_value', 'SPEC_VALUE_ID', $ids['value1'], 'SPEC_VALUE', $marker . 'VAL1'],
        ['spec_value', 'SPEC_VALUE_ID', $ids['value2'], 'SPEC_VALUE', $marker . 'VAL2'],
        ['spec', 'SPEC_ID', $ids['spec'], 'SPEC_NAME', $marker . 'SPEC'],
        ['goods', 'GOODS_ID', $ids['goods'], 'GOODS_NAME', $marker],
    ] as [$logical, $pk, $id, $field, $value]) {
        $s = $db->prepare('DELETE FROM ' . $t($logical)
            . ' WHERE ' . $pk . '=? AND STORE_ID=10001 AND ' . $field . '=?');
        $s->execute([$id, $value]);
        if ($s->rowCount() !== 1) throw new RuntimeException('Exact delete affected-row mismatch');
    }
    $db->commit();
} catch (Throwable $e) {
    if ($db->inTransaction()) $db->rollBack();
    throw $e;
}
$journal['phase'] = 'cleanup_committed';
$saveJournal($journal);
echo json_encode(['lane' => $lane, 'phase' => $phase,
    'deleted_exact_rows' => 6, 'independent_audit_required' => true],
    JSON_UNESCAPED_SLASHES), "\n";
