<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');$lane=getenv('CITY_LANE');$parent=getenv('CITY_PARENT');$stage='identity';
set_exception_handler(static function(Throwable $e)use($lane,&$stage){echo json_encode(['lane'=>$lane,'status'=>'fail','stage'=>$stage,'error_class'=>get_class($e),'error_line'=>$e->getLine(),'sqlstate'=>$e->getCode(),'driver_code'=>$e instanceof PDOException?($e->errorInfo[1]??null):null,'message_sha256'=>hash('sha256',$e->getMessage())]),"\n";exit(2);});
$cfg=require __DIR__.'/config.php';$dm=$lane==='C';
if(!$dm){$local=require '/workspace/zhct/zhctproject/store/application/config/local/database.php';foreach(['hostname','database','username','password'] as $k)if((string)$cfg[$k]!== (string)$local[$k])throw new RuntimeException('Not code local');if((int)($cfg['hostport']?:3306)!==(int)($local['hostport']??3306))throw new RuntimeException('Not code local port');}
elseif($cfg['hostname']!=='127.0.0.1'||(int)$cfg['hostport']!==5237||$cfg['database']!=='ZHCT_APP')throw new RuntimeException('Not approved DM');
$options=[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER];if(!$dm)$options[PDO::ATTR_EMULATE_PREPARES]=false;
$dsn=$dm?'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP':'mysql:host='.$cfg['hostname'].';port='.($cfg['hostport']?:3306).';dbname='.$cfg['database'].';charset='.$cfg['charset'];
$db=new PDO($dsn,$cfg['username'],$cfg['password'],$options);
$q=static function(string $sql,array $args=[])use($db):array{$s=$db->prepare($sql);$s->execute($args);$r=$s->fetchAll(PDO::FETCH_ASSOC);foreach($r as &$row)foreach($row as &$v)if(is_resource($v))$v=stream_get_contents($v);unset($row,$v);return $r;};
$meta=$dm?$q("SELECT COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS WHERE TABLE_NAME='YDY_CITY' ORDER BY COLUMN_ID"):$q('SHOW COLUMNS FROM ydy_city');
if(count($meta)!==4)throw new RuntimeException('Unexpected city schema');
$before=$q('SELECT * FROM ydy_city ORDER BY id');if(count($before)>20000)throw new RuntimeException('Bounded source required');
$sourceSha=hash('sha256',serialize($before));
if(($argv[1]??'')==='preflight'){echo json_encode(['lane'=>$lane,'status'=>'ready','rows'=>count($before),'columns'=>$meta,'business_dml'=>0,'http_sent'=>false]),"\n";exit;}
$stage='source';$manifest=json_decode((string)file_get_contents(__DIR__.'/source-manifest.json'),true);
foreach($manifest as $file=>$hash)if(hash_file('sha256',__DIR__.'/source/'.$file)!==$hash)throw new RuntimeException('Source SHA drift');
require $parent.'/env.php';define('APP_PATH',__DIR__.'/source/application/');define('RUNTIME_PATH',__DIR__.'/cli-runtime/');require $parent.'/external_stubs.php';require $parent.'/thinkphp/base.php';\think\App::initCommon();
require_once __DIR__.'/source/application/base/Security.php';
// Existing normal token decoder; synthetic cache context, not a login test.
$token=\app\base\Security::encrypt(implode("\t",['2147001838','CDXCITY10_SYNTHETIC','CDXCITY10_SYNTHETIC','0','','','CDXCITY10_SYNTHETIC']));
$sign=md5((string)\think\Config::get('api_sk'));
$expectedTree=static function(array $rows,int $parentId=0)use(&$expectedTree):array{
    $out=[];foreach($rows as $row){if($row['pid']!==$parentId)continue;$children=$expectedTree($rows,$row['id']);if($children)$row['children']=$children;$row['chhildShow']=false;$out[]=$row;}return $out;
};
$expected=['code'=>0,'message'=>'','data'=>$expectedTree($before)];if(!$expected['data'])throw new RuntimeException('Independent source tree empty');
$results=[];$base='http://127.0.0.1:'.($dm?18382:18381);$private=[];
$eventsBefore=count(is_file(__DIR__.'/events.jsonl')?(file(__DIR__.'/events.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[]):[]);
for($i=0;$i<2;$i++){
    $stage='http_'.$i;$ctx=stream_context_create(['http'=>['method'=>'POST','timeout'=>20,'ignore_errors'=>true,'follow_location'=>0,'header'=>"Content-Type: application/x-www-form-urlencoded\r\ntoken: ".$token."\r\n",'content'=>http_build_query(['sign'=>$sign])]]);
    $raw=(string)file_get_contents($base.'/p/city/lists',false,$ctx);preg_match('/HTTP\/\S+\s+(\d+)/',$http_response_header[0]??'',$m);$body=json_decode($raw,true);
    if((int)($m[1]??0)!==200||!is_array($body)||($body['code']??-1)!==0)throw new RuntimeException('Formal HTTP not positive');
    if(isset($body['trace_id'])){if(!is_string($body['trace_id'])||!preg_match('/^[a-f0-9]{13}$/D',$body['trace_id']))throw new RuntimeException('Trace invalid');unset($body['trace_id']);}
    if($body!==$expected)throw new RuntimeException('Original table full tree/type/order mismatch');
    $private[]=['body'=>$body,'raw'=>$raw];$results[]=['method'=>'POST','path'=>'/p/city/lists','http'=>200,'business_code'=>0,'full_original_tree_type_value_order_equal'=>true,'body_sha256'=>hash('sha256',json_encode($body,JSON_UNESCAPED_UNICODE))];
}
$stage='after';$after=$q('SELECT * FROM ydy_city ORDER BY id');if(hash('sha256',serialize($after))!==$sourceSha)throw new RuntimeException('Natural city changed');
file_put_contents(__DIR__.'/city-private.json',json_encode(['source'=>$before,'responses'=>$private],JSON_UNESCAPED_UNICODE|JSON_PRESERVE_ZERO_FRACTION));chmod(__DIR__.'/city-private.json',0600);
$events=array_map(static function($v){return json_decode($v,true);},file(__DIR__.'/events.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[]);
$out=['lane'=>$lane,'status'=>'pass','cases'=>$results,'source_rows'=>count($before),'source_sha256'=>$sourceSha,'natural_unchanged'=>true,'business_dml'=>0,'ddl'=>0,'actor_database_writes'=>0,'source_files'=>$manifest,'events'=>array_slice($events,$eventsBefore),'prior_tool_attempt_event_count'=>$eventsBefore,'ordinary_auth_used'=>true,'real_login_tested'=>false,'unordered_source_limitation'=>true];
file_put_contents(__DIR__.'/result.json',json_encode($out,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE));echo json_encode(['lane'=>$lane,'status'=>'pass','http_cases'=>2,'source_rows'=>count($before),'business_dml'=>0]),"\n";
