<?php
declare(strict_types=1);
date_default_timezone_set('Asia/Shanghai');
$lane=(string)getenv('CATEGORY_LANE');$base=(string)getenv('CATEGORY_BASE');$runtime=(string)getenv('CATEGORY_RUNTIME');
if(!in_array($lane,['B','C'],true)||!preg_match('#^http://127\.0\.0\.1:1832[12]$#D',$base))exit(78);
$dm=getenv('POC_DIALECT')==='dm';
$connect=static function()use($dm):PDO{try{return new PDO($dm?'dm:host=127.0.0.1;port=5238;schema=AIZHCT_APP':
    'mysql:host='.getenv('POC_AI_HOST').';port='.getenv('POC_AI_PORT').';dbname='.getenv('POC_AI_SCHEMA').';charset=utf8',
    getenv('POC_AI_USER'),getenv('POC_AI_PASSWORD'),[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER]);
}catch(Throwable $error){throw new RuntimeException('Database preflight connection failed');}};
$db=$connect();$check=static function(bool $ok,string $reason):void{if(!$ok)throw new RuntimeException($reason);};
if($dm)$check($db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn()==='AIZHCT_APP','DM identity');
else $check(strpos((string)$db->query('SELECT VERSION()')->fetchColumn(),'5.6.')===0&&$db->query('SELECT DATABASE()')->fetchColumn()===getenv('POC_AI_SCHEMA'),'local MySQL identity');
$query=static function(string $sql,array $args=[])use($db):array{$s=$db->prepare($sql);$s->execute($args);$rows=$s->fetchAll(PDO::FETCH_ASSOC);
    foreach($rows as &$row)foreach($row as &$value)if(is_resource($value))$value=stream_get_contents($value);unset($row,$value);return $rows;};
$prefix='CDXART10AB31';$tenants=['store'=>2147000321,'terminal'=>2147000322];
$tables=['article_category'=>'category_id','article'=>'article_id','upload_file'=>'file_id'];
$counts=static function()use($query,$tables):array{$out=[];foreach($tables as $table=>$pk)$out[$table]=(int)$query('SELECT COUNT(*) n FROM yoshop_'.$table.' WHERE store_id IN (2147000321,2147000322)')[0]['n'];return $out;};
if(($argv[1]??'')==='audit'){$c=$counts();echo json_encode(['lane'=>$lane,'independent_owned_rows'=>$c]),"\n";exit(array_sum($c)?1:0);}
$check(array_sum($counts())===0,'Private tenant collision');
$required=[
    'article_category'=>['category_id','name','status','sort','store_id','create_time','update_time'],
    'article'=>['article_id','title','show_type','category_id','image_id','content','sort','status','virtual_views','actual_views','is_delete','store_id','create_time','update_time'],
    'upload_file'=>['file_id','group_id','channel','storage','domain','file_type','file_name','file_path','file_size','file_ext','cover','uploader_id','is_recycle','is_delete','store_id','create_time','update_time']];
$metadata=[];
foreach($required as $table=>$fields){
    $rows=$dm?$query('SELECT COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT,DATA_LENGTH FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID',[strtoupper('yoshop_'.$table)])
        :$query('SELECT COLUMN_NAME,DATA_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? ORDER BY ORDINAL_POSITION',['yoshop_'.$table]);
    $names=array_map(static function(array $r):string{return strtolower($r['column_name']);},$rows);sort($names);sort($fields);
    $check($names===$fields,'Formal table columns drift '.$table);
    foreach($rows as $row)$check(($dm?$row['nullable']:$row['is_nullable'])===($dm?'N':'NO'),'Required nullability drift '.$table);
    if($dm){$s=$db->prepare('CALL SP_TABLEDEF(?,?)');$s->execute(['AIZHCT_APP',strtoupper('yoshop_'.$table)]);$ddl=$s->fetchColumn();if(is_resource($ddl))$ddl=stream_get_contents($ddl);
        $check(preg_match('/"'.strtoupper($tables[$table]).'"\s+[A-Z0-9_]+\s+(?:AUTO_INCREMENT|IDENTITY)/i',(string)$ddl)===1,'Identity drift '.$table);
    }else{$found=false;foreach($rows as $row)if($row['column_name']===$tables[$table])$found=strpos($row['extra'],'auto_increment')!==false;$check($found,'Identity drift '.$table);}
    $metadata[$table]=['formal_columns_equal'=>true,'not_null_constraints_read'=>true,'defaults_and_types_read'=>true,'identity_verified'=>true];
}
$triggers=$dm?$query("SELECT COUNT(*) n FROM USER_TRIGGERS WHERE TABLE_NAME IN ('YOSHOP_ARTICLE_CATEGORY','YOSHOP_ARTICLE','YOSHOP_UPLOAD_FILE')")
    :$query("SELECT COUNT(*) n FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE IN ('yoshop_article_category','yoshop_article','yoshop_upload_file')");
$fks=$dm?$query("SELECT COUNT(*) n FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R' AND (TABLE_NAME IN ('YOSHOP_ARTICLE_CATEGORY','YOSHOP_ARTICLE','YOSHOP_UPLOAD_FILE') OR R_CONSTRAINT_NAME IN (SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME IN ('YOSHOP_ARTICLE_CATEGORY','YOSHOP_ARTICLE','YOSHOP_UPLOAD_FILE')))")
    :$query("SELECT COUNT(*) n FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND (TABLE_NAME IN ('yoshop_article_category','yoshop_article','yoshop_upload_file') OR REFERENCED_TABLE_NAME IN ('yoshop_article_category','yoshop_article','yoshop_upload_file'))");
$check((int)$triggers[0]['n']===0&&(int)$fks[0]['n']===0,'Trigger/FK gate');
$snapshot=static function()use($query,$tables):array{$out=[];foreach($tables as $table=>$pk){$rows=$query('SELECT * FROM yoshop_'.$table.' ORDER BY '.$pk);$out[$table]=['rows'=>count($rows),'sha'=>hash('sha256',serialize($rows))];}return $out;};
$before=$snapshot();$boundary=json_decode((string)file_get_contents($base.'/__poc_boundary'),true);
$check(!empty($boundary['external_stubs'])&&!empty($boundary['private_cache'])&&$boundary['allowlist']==='article-owned-only','Boundary');
$root=(string)getenv('CATEGORY_ROOT');define('TP_ENV','poc');define('BUSINESS','aizhct');define('ENTERPRISE_NO','10001');require $root.'/vendor/autoload.php';
$app=new \think\App($root);$app->setEnvName('poc');$app->setRuntimePath($runtime.'/fixture-runtime/');$app->initialize();
$cache=new \think\cache\driver\File($app,['path'=>$runtime.'/cache/','prefix'=>'','expire'=>0]);$tokens=[];
foreach($tenants as $module=>$tenant){$tokens[$module]=bin2hex(random_bytes(24));$cache->set($tokens[$module],['user'=>[
    'store_user_id'=>$tenant,'store_id'=>$tenant,'real_name'=>$prefix,'user_name'=>$prefix,'is_super'=>1],'store_id'=>$tenant,'is_login'=>true],1800);}
$shape=static function($value)use(&$shape){if(is_object($value)){$out=[];foreach(get_object_vars($value) as $k=>$v)$out[$k]=$shape($v);ksort($out);return ['object'=>$out];}
    if(is_array($value))return ['array'=>array_map($shape,$value)];if(is_int($value)||is_float($value))return 'number';return gettype($value);};
$deep=static function($actual,$expected,string $path='$')use(&$deep,$check):void{
    if(is_array($expected)){$check(is_array($actual),'Expected array/object '.$path);$ak=array_keys($actual);$ek=array_keys($expected);sort($ak);sort($ek);$check($ak===$ek,'Contract keys '.$path);
        foreach($expected as $key=>$value)$deep($actual[$key],$value,$path.'.'.$key);
    }else $check($actual===$expected,'Contract type/value '.$path);
};
$cases=[];$idMaps=['article_id'=>[],'category_id'=>[],'image_id'=>[],'file_id'=>[]];$fixtures=[];$error=null;$privateResponses=[];$counterChecks=[];$diagnostics=[];
file_put_contents($runtime.'/queries.jsonl','');chmod($runtime.'/queries.jsonl',0600);
$request=static function(string $module,string $scenario,string $action,array $args=[],array $form=[],string $encoding='json')use($base,$tokens,$shape,&$privateResponses):array{
    $method=in_array($action,['list','detail'],true)?'GET':'POST';$scenario='PILOT_AR_'.strtoupper($module).'_'.$scenario;
    $url=$base.'/'.$module.'/content.Article/'.$action.($args?'?'.http_build_query($args):'');
    $headers='Host: '.$module.".ar-test.invalid\r\nAccess-Token: ".$tokens[$module]."\r\nPlatform: POC\r\nX-Pilot-Scenario: ".$scenario."\r\nContent-Type: ".($encoding==='json'?'application/json':'application/x-www-form-urlencoded')."\r\n";
    $content=$encoding==='json'?json_encode(['form'=>$form],JSON_UNESCAPED_UNICODE):http_build_query(['form'=>$form]);
    $raw=file_get_contents($url,false,stream_context_create(['http'=>['method'=>$method,'header'=>$headers,'content'=>$content,'timeout'=>30,'ignore_errors'=>true]]));
    if(!is_string($raw)||strlen($raw)>1048576)throw new RuntimeException('Response body cap '.$scenario);
    preg_match('#^HTTP/\S+\s+(\d+)#',$http_response_header[0]??'',$m);$body=json_decode($raw,true);if(!is_array($body))throw new RuntimeException('Non JSON '.$scenario);
    $privateResponses[$scenario]=['http_status'=>(int)($m[1]??0),'body'=>$body];
    return ['scenario'=>$scenario,'method'=>$method,'path'=>'/'.$module.'/content.Article/'.$action,'encoding'=>$encoding,
        'http_status'=>(int)($m[1]??0),'body'=>$body,'raw_json_shape'=>$shape(json_decode($raw))];
};
$expect=static function(array $case,int $status=200,string $message='')use($check):void{$check($case['http_status']===200&&$case['body']['status']===$status,'Business failure '.$case['scenario']);if($message!=='')$check($case['body']['message']===$message,'Business message '.$case['scenario']);};
$convert=static function(array $row):array{foreach($row as $key=>&$value){if(in_array($key,['create_time','update_time'],true))$value=date('Y-m-d H:i:s',(int)$value);
    elseif(in_array($key,['article_id','show_type','category_id','image_id','sort','status','virtual_views','actual_views','is_delete','store_id','file_id','group_id','channel','file_type','file_size','uploader_id','is_recycle'],true))$value=(int)$value;}unset($value);return $row;};
$project=static function(string $module,array $row,bool $list)use($query,$convert,$check,$tenants):array{
    $check((int)$row['store_id']===$tenants[$module],'Original owner');$image=$query('SELECT * FROM yoshop_upload_file WHERE file_id=? AND store_id=?',[$row['image_id'],$tenants[$module]]);
    $check(count($image)===1&&$image[0]['storage']==='local','Original owned local file metadata');
    $url='http://'.$module.'.ar-test.invalid/uploads/'.$image[0]['file_path'];$file=$convert($image[0]);$file['preview_url']=$url;$file['external_url']=$url;
    $expected=$convert($row);$expected['show_views']=(int)$row['virtual_views']+(int)$row['actual_views'];$expected['image_url']=$url;$expected['image']=$file;
    if($list){unset($expected['content']);$category=$query('SELECT * FROM yoshop_article_category WHERE category_id=? AND store_id=?',[$row['category_id'],$tenants[$module]]);$check(count($category)===1,'Original owned category');$expected['category']=$convert($category[0]);}
    else $expected['content']=htmlspecialchars_decode((string)$row['content']);return $expected;
};
$normaliseTimes=static function($value)use(&$normaliseTimes){if(!is_array($value))return $value;
    $ownedArticle=isset($value['article_id'])&&is_int($value['article_id']);
    foreach($value as $key=>&$part){if($ownedArticle&&$key==='update_time'&&is_string($part))$part='SOURCE_VERIFIED_AUTO_UPDATE_TIME';else $part=$normaliseTimes($part);}unset($part);return $value;};
$list=static function(string $module,string $scenario,array $filters=[])use($request,$expect,$query,$tenants,$project,$deep,$normaliseTimes,&$cases):void{
    $case=$request($module,$scenario,'list',$filters);$expect($case);$sql='SELECT * FROM yoshop_article WHERE store_id=? AND is_delete=0';$args=[$tenants[$module]];
    if(isset($filters['title'])&&$filters['title']!==''){$sql.=' AND title LIKE ?';$args[]='%'.$filters['title'].'%';}
    if(isset($filters['categoryId'])){$sql.=' AND category_id=?';$args[]=$filters['categoryId'];}
    if(isset($filters['status'])){$sql.=' AND status=?';$args[]=$filters['status'];}
    $rows=$query($sql.' ORDER BY sort ASC,create_time DESC',$args);$items=[];foreach($rows as $row)$items[]=$project($module,$row,true);
    $expected=['total'=>count($rows),'per_page'=>15,'current_page'=>1,'last_page'=>(int)ceil(count($rows)/15),'data'=>$items];
    $deep($case['body']['data'],['list'=>$expected]);$case['independent_rows']=count($rows);$case['full_source_projection_equal']=true;
    $case['body']=$normaliseTimes($case['body']);$cases[]=$case;
};
$detail=static function(string $module,string $scenario,int $id)use($request,$expect,$query,$tenants,$project,$deep,$check,$normaliseTimes,&$cases,&$counterChecks):void{
    $before=$query('SELECT * FROM yoshop_article WHERE article_id=? AND store_id=?',[$id,$tenants[$module]]);$check(count($before)===1,'Detail original row');
    $case=$request($module,$scenario,'detail',['articleId'=>$id]);$expect($case);
    $after=$query('SELECT * FROM yoshop_article WHERE article_id=? AND store_id=?',[$id,$tenants[$module]]);$check($before===$after,'Detail unexpectedly changed original row/counter');
    $deep($case['body']['data'],['detail'=>$project($module,$after[0],false)]);
    $counterChecks[]=['scenario'=>$case['scenario'],'before_actual_views'=>(int)$before[0]['actual_views'],'after_actual_views'=>(int)$after[0]['actual_views'],
        'virtual_views'=>(int)$after[0]['virtual_views'],'http_show_views'=>$case['body']['data']['detail']['show_views'],'counter_delta'=>0];
    $case['original_row_unchanged']=true;$case['full_source_projection_equal']=true;$case['body']=$normaliseTimes($case['body']);$cases[]=$case;
};
try{
foreach($tenants as $module=>$tenant){
    $categoryName=$prefix.'_'.$module.'_CATEGORY';$category=$db->prepare('INSERT INTO yoshop_article_category (name,status,sort,store_id,create_time,update_time) VALUES (?,?,?,?,?,?)');
    $category->execute([$categoryName,1,7,$tenant,1609459200,1609459200]);$cat=$query('SELECT * FROM yoshop_article_category WHERE name=? AND store_id=?',[$categoryName,$tenant]);$check(count($cat)===1,'Category fixture');$catId=(int)$cat[0]['category_id'];$idMaps['category_id'][$catId]=1;
    $fileName=$prefix.'_'.$module.'_cover.png';$path=$prefix.'/'.$module.'/cover.png';
    $fileColumns=['group_id','channel','storage','domain','file_type','file_name','file_path','file_size','file_ext','cover','uploader_id','is_recycle','is_delete','store_id','create_time','update_time'];
    $quotedColumns=array_map(static function(string $column)use($dm):string{return $dm?'"'.strtoupper($column).'"':'`'.$column.'`';},$fileColumns);
    $file=$db->prepare('INSERT INTO yoshop_upload_file ('.implode(',',$quotedColumns).') VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)');
    $file->execute([0,40,'local','',10,$fileName,$path,0,'png','',0,0,0,$tenant,1609459200,1609459200]);
    $f=$query('SELECT * FROM yoshop_upload_file WHERE file_name=? AND store_id=?',[$fileName,$tenant]);$check(count($f)===1,'Local file metadata fixture');$fileId=(int)$f[0]['file_id'];$idMaps['image_id'][$fileId]=1;$idMaps['file_id'][$fileId]=1;
    $fixtures[$module]=['tenant'=>$tenant,'category_id'=>$catId,'file_id'=>$fileId];
    $list($module,'LIST_EMPTY');
    $long='<p title="db">'.str_repeat('智慧餐厅\'\\',2600).'</p>';
    $form=['title'=>$prefix.'_'.$module.'_LONG_中文\'\\','show_type'=>10,'category_id'=>$catId,'image_id'=>$fileId,'content'=>$long,
        'sort'=>21,'status'=>0,'virtual_views'=>17,'actual_views'=>3,'is_delete'=>0,'create_time'=>1609459200,'update_time'=>1609459200];
    foreach([['ADD_IMAGE_ZERO_REJECT','image_id',0,'请上传封面图'],['ADD_CONTENT_EMPTY_REJECT','content','','请输入文章内容']] as $negative){
        $bad=$form;$bad[$negative[1]]=$negative[2];$case=$request($module,$negative[0],'add',[],$bad);$expect($case,500,$negative[3]);
        $check(!$query('SELECT article_id FROM yoshop_article WHERE store_id=?',[$tenant]),'Rejected add wrote row');$case['owned_article_rows']=0;$cases[]=$case;
    }
    $case=$request($module,'ADD_LONG_JSON','add',[],$form);$expect($case);
    $rows=$query('SELECT * FROM yoshop_article WHERE title=? AND store_id=?',[$form['title'],$tenant]);$check(count($rows)===1,'Long add original row');$first=(int)$rows[0]['article_id'];$idMaps['article_id'][$first]=1;
    $diagnostics[$module.'_long_content']=['source_bytes'=>strlen($rows[0]['content']),'source_sha'=>hash('sha256',$rows[0]['content']),
        'expected_bytes'=>strlen(htmlspecialchars($long,ENT_COMPAT)),'expected_sha'=>hash('sha256',htmlspecialchars($long,ENT_COMPAT))];
    $check($rows[0]['content']===htmlspecialchars($long,ENT_COMPAT)&&strlen($rows[0]['content'])>32768,'Long encoded content original row');
    $case['independent_state']=['rows'=>1,'content_encoded_equal'=>true,'content_over_32k'=>true,'content_bytes'=>strlen($rows[0]['content']),
        'content_sha'=>hash('sha256',$rows[0]['content']),'owner_equal'=>true,'image_category_equal'=>(int)$rows[0]['image_id']===$fileId&&(int)$rows[0]['category_id']===$catId];$cases[]=$case;
    $detail($module,'DETAIL_LONG',$first);$detail($module,'DETAIL_LONG_REPEAT',$first);
    $short=$form;$short['title']=$prefix.'_'.$module.'_SECOND';$short['content']='<p>第二篇中文</p>';$short['sort']=20;$short['status']=1;$short['virtual_views']=5;$short['actual_views']=2;
    $case=$request($module,'ADD_SECOND_FORM','add',[],$short,'form');$expect($case);$rows=$query('SELECT * FROM yoshop_article WHERE title=? AND store_id=?',[$short['title'],$tenant]);$check(count($rows)===1&&$rows[0]['content']===htmlspecialchars($short['content'],ENT_COMPAT),'Short original row');$second=(int)$rows[0]['article_id'];$idMaps['article_id'][$second]=2;$case['independent_state']=['rows'=>1,'content_encoded_equal'=>true];$cases[]=$case;
    $list($module,'LIST_TWO');$list($module,'LIST_STATUS_ZERO',['status'=>0]);$list($module,'LIST_STATUS_ONE',['status'=>1]);$list($module,'LIST_CATEGORY',['categoryId'=>$catId]);$list($module,'LIST_TITLE_LONG',['title'=>$prefix.'_'.$module.'_LONG']);
    foreach([['EDIT_IMAGE_ZERO_REJECT','image_id',0,'请上传封面图'],['EDIT_CONTENT_EMPTY_REJECT','content','','请输入文章内容']] as $negative){
        $original=$query('SELECT * FROM yoshop_article WHERE article_id=? AND store_id=?',[$first,$tenant]);$bad=$form;$bad[$negative[1]]=$negative[2];
        $case=$request($module,$negative[0],'edit',['articleId'=>$first],$bad,'form');$expect($case,500,$negative[3]);
        $check($original===$query('SELECT * FROM yoshop_article WHERE article_id=? AND store_id=?',[$first,$tenant]),'Rejected edit changed original row');$case['original_row_unchanged']=true;$cases[]=$case;
    }
    $edit=$form;$edit['title']=$prefix.'_'.$module.'_EDIT_中文\'\\';$edit['content']='<p title="修改">编辑\'中文\\</p>';$edit['sort']=19;$edit['status']=1;$edit['virtual_views']=23;
    $case=$request($module,'EDIT_FIRST_FORM','edit',['articleId'=>$first],$edit,'form');$expect($case);
    $rows=$query('SELECT * FROM yoshop_article WHERE article_id=? AND store_id=?',[$first,$tenant]);$check(count($rows)===1&&$rows[0]['title']===$edit['title']&&$rows[0]['content']===htmlspecialchars($edit['content'],ENT_COMPAT)
        &&(int)$rows[0]['sort']===19&&(int)$rows[0]['status']===1&&(int)$rows[0]['virtual_views']===23&&(int)$rows[0]['actual_views']===3,'Edit original state');
    $case['independent_state']=['content_encoded_equal'=>true,'title_equal'=>true,'sort'=>19,'status'=>1,'virtual_views'=>23,'actual_views'=>3];$cases[]=$case;
    $detail($module,'DETAIL_EDITED',$first);$list($module,'LIST_REORDERED');
    $case=$request($module,'DELETE_FIRST','delete',['articleId'=>$first]);$expect($case);$rows=$query('SELECT is_delete FROM yoshop_article WHERE article_id=? AND store_id=?',[$first,$tenant]);$check(count($rows)===1&&(int)$rows[0]['is_delete']===1,'First soft delete');$case['independent_state']=['physical_rows'=>1,'is_delete'=>1];$cases[]=$case;
    $detail($module,'DETAIL_SOFT_STATE',$first);$list($module,'LIST_ONE');
    $case=$request($module,'REPEAT_DELETE_FIRST','delete',['articleId'=>$first]);$expect($case);$rows=$query('SELECT is_delete FROM yoshop_article WHERE article_id=? AND store_id=?',[$first,$tenant]);$check(count($rows)===1&&(int)$rows[0]['is_delete']===1,'Repeat soft delete');$case['independent_state']=['physical_rows'=>1,'is_delete'=>1];$cases[]=$case;
    $case=$request($module,'DELETE_SECOND','delete',['articleId'=>$second]);$expect($case);$rows=$query('SELECT is_delete FROM yoshop_article WHERE article_id=? AND store_id=?',[$second,$tenant]);$check(count($rows)===1&&(int)$rows[0]['is_delete']===1,'Second soft delete');$case['independent_state']=['physical_rows'=>1,'is_delete'=>1];$cases[]=$case;
    $list($module,'LIST_EMPTY_AFTER');
}
}catch(Throwable $caught){$error=['class'=>get_class($caught),'message'=>$caught->getMessage(),'line'=>$caught->getLine()];}
// Never invoke UploadFile::setDelete or StorageDriver; physical cleanup is SQL only.
$unexpectedRefs=0;$referenceColumns=0;
foreach($fixtures as $module=>$fixture){
    $refs=$dm?$query("SELECT TABLE_NAME,COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME LIKE 'YOSHOP_%' AND COLUMN_NAME IN ('IMAGE_ID','FILE_ID')")
        :$query("SELECT TABLE_NAME,COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME LIKE 'yoshop_%' AND COLUMN_NAME IN ('image_id','file_id')");
    foreach($refs as $ref){$table=$ref['table_name'];$column=$ref['column_name'];$check(preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/D',$table)===1&&preg_match('/^[A-Za-z_][A-Za-z0-9_]*$/D',$column)===1,'Reference metadata safety');++$referenceColumns;
        if(strtolower($table)==='yoshop_upload_file'&&strtolower($column)==='file_id')continue;
        $sql='SELECT COUNT(*) n FROM '.$table.' WHERE '.$column.'=?';$args=[$fixture['file_id']];
        if(strtolower($table)==='yoshop_article'){$sql.=' AND NOT (store_id=? AND title LIKE ?)';$args[]=$fixture['tenant'];$args[]=$prefix.'%';}
        $unexpectedRefs+=(int)$query($sql,$args)[0]['n'];
    }
    $unexpectedRefs+=(int)$query('SELECT COUNT(*) n FROM yoshop_article WHERE category_id=? AND NOT (store_id=? AND title LIKE ?)',[$fixture['category_id'],$fixture['tenant'],$prefix.'%'])[0]['n'];
}
if($unexpectedRefs===0){
    foreach($tables as $table=>$pk){if($table!=='article')continue;$rows=$query('SELECT article_id,title,store_id FROM yoshop_article WHERE store_id IN (2147000321,2147000322) AND title LIKE ?',[$prefix.'%']);
        foreach($rows as $row){$s=$db->prepare('DELETE FROM yoshop_article WHERE article_id=? AND title=? AND store_id=?');$s->execute([$row['article_id'],$row['title'],$row['store_id']]);}}
    foreach([['article_category','category_id','name'],['upload_file','file_id','file_name']] as $spec){$rows=$query('SELECT '.$spec[1].','.$spec[2].',store_id FROM yoshop_'.$spec[0].' WHERE store_id IN (2147000321,2147000322) AND '.$spec[2].' LIKE ?',[$prefix.'%']);
        foreach($rows as $row){$s=$db->prepare('DELETE FROM yoshop_'.$spec[0].' WHERE '.$spec[1].'=? AND '.$spec[2].'=? AND store_id=?');$s->execute([$row[$spec[1]],$row[$spec[2]],$row['store_id']]);}}
}
foreach($tokens as $token)$cache->delete($token);
$fresh=$connect();$remaining=[];foreach($tables as $table=>$pk)$remaining[$table]=(int)$fresh->query('SELECT COUNT(*) FROM yoshop_'.$table.' WHERE store_id IN (2147000321,2147000322)')->fetchColumn();
$tokensAbsent=true;foreach($tokens as $token)$tokensAbsent=$tokensAbsent&&$cache->get($token)===null;
$cleanup=['fresh_pdo_owned_rows'=>$remaining,'tokens_absent'=>$tokensAbsent,'natural_tables_unchanged'=>$snapshot()===$before,'unexpected_references'=>$unexpectedRefs,'reference_columns_examined'=>$referenceColumns];
$normalise=static function($value)use(&$normalise,$idMaps){if(!is_array($value))return $value;foreach($value as $key=>&$part){if(isset($idMaps[$key])&&is_int($part)&&isset($idMaps[$key][$part]))$part=$idMaps[$key][$part];else $part=$normalise($part);}unset($part);return $value;};
foreach($cases as &$case){$trace=$case['body']['trace_id']??null;$case['trace_format_valid']=is_string($trace)&&preg_match('/^[0-9a-f]{13}$/D',$trace)===1;if($case['trace_format_valid'])$case['body']['trace_id']='VALID_DYNAMIC_TRACE';$case=$normalise($case);}unset($case);
$events=[];foreach(file($runtime.'/queries.jsonl',FILE_IGNORE_NEW_LINES|FILE_SKIP_EMPTY_LINES)?:[] as $line)$events[]=json_decode($line,true);
$report=['lane'=>$lane,'boundary_verified'=>true,'metadata'=>$metadata,'triggers'=>0,'foreign_keys'=>0,'case_count'=>count($cases),'cases'=>$cases,
    'counter_checks'=>$counterChecks,'diagnostics'=>$diagnostics,'cleanup'=>$cleanup,'source_before'=>$before,'error'=>$error,'events'=>$events,
    'normalization'=>'validated trace; original-row-verified owned ID fields; update_time only after full original projection equality; store_id and counters unchanged'];
file_put_contents($runtime.'/report.json',json_encode($report,JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE));chmod($runtime.'/report.json',0600);
file_put_contents($runtime.'/private-responses.json',json_encode($privateResponses,JSON_UNESCAPED_UNICODE));chmod($runtime.'/private-responses.json',0600);
echo json_encode(['lane'=>$lane,'cases'=>count($cases),'cleanup'=>$cleanup,'error'=>$error,'report'=>$runtime.'/report.json']),"\n";
exit($error!==null||array_sum($remaining)!==0||!$tokensAbsent||!$cleanup['natural_tables_unchanged']||$unexpectedRefs!==0?1:0);
