<?php
// Root-only candidate. This file's --gate/test modes never connect to a database.
const FD2_PLAN_SHA='de97e490c867ef7bbbaacce80470a225b124be62771e2c52e3f4468a516d3391';
const FD2_RUNTIME='/tmp/cdx-food-date-root-20261011';
function fd2_need($v,$m){if(!$v)throw new RuntimeException($m);}
function fd2_json($v){$j=json_encode($v,JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRESERVE_ZERO_FRACTION);fd2_need($j!==false,'JSON encoding');return $j;}
function fd2_canonical($v){if(!is_array($v))return $v;foreach($v as &$x)$x=fd2_canonical($x);unset($x);if($v!==[]&&array_keys($v)!==range(0,count($v)-1))ksort($v,SORT_STRING);return $v;}
function fd2_equal($a,$b,$m){fd2_need(fd2_canonical($a)===fd2_canonical($b),$m);}
function fd2_tokens($s){preg_match_all('~\x27(?:\x27\x27|[^\x27])*\x27|"(?:""|[^"])*"|--[^\r\n]*|[A-Za-z_][A-Za-z0-9_$]*|(?:<>|:=|<=|>=)|[^\s]~u',$s,$m);$out=[];foreach($m[0]as $t)if(substr($t,0,2)!=='--')$out[]=($t[0]===chr(39)||$t[0]===chr(34))?$t:strtoupper($t);return $out;}
function fd2_trigger($actual,$approved){$headers=[];$bodies=[];foreach([$actual,$approved]as $s){fd2_need(preg_match('/FOR\s+EACH\s+ROW\b/i',$s,$m,PREG_OFFSET_CAPTURE)===1,'Full owned trigger header');$pos=$m[0][1]+strlen($m[0][0]);$bodies[]=fd2_tokens(substr($s,$pos));$header=[];foreach(fd2_tokens(substr($s,0,$pos))as $t)if(!in_array($t,['OR','REPLACE'],true))$header[]=$t[0]===chr(34)?strtoupper(substr($t,1,-1)):$t;$headers[]=$header;}fd2_equal($headers[0],$headers[1],'Exact owned trigger header');fd2_equal($bodies[0],$bodies[1],'Complete literal-preserving61-field body');}
function fd2_build_trigger($cols){$clauses=[];$groups=['numeric'=>[],'varchar'=>[],'datetime'=>[]];foreach($cols as $c){$n=$c['column_name'];if($n==='UPDATE_TIME')continue;fd2_need(preg_match('/^[A-Z_][A-Z_0-9]*$/D',$n),'Real column name');$type=$c['data_type'];if($type==='VARCHAR'){$groups['varchar'][]=$n;$clauses[]="    (NVL(LENGTHB(:NEW.$n),-1) <> NVL(LENGTHB(:OLD.$n),-1) OR NVL(RAWTOHEX(CHARTOBIN(:NEW.$n)),'#') <> NVL(RAWTOHEX(CHARTOBIN(:OLD.$n)),'#'))";}else{fd2_need(in_array($type,['DECIMAL','BIGINT','SMALLINT','TINYINT','DATETIME'],true),'Actual scalar types, no LOB route');$groups[$type==='DATETIME'?'datetime':'numeric'][]=$n;$clauses[]="    ((:NEW.$n IS NULL AND :OLD.$n IS NOT NULL) OR (:NEW.$n IS NOT NULL AND :OLD.$n IS NULL) OR (:NEW.$n IS NOT NULL AND :OLD.$n IS NOT NULL AND :NEW.$n <> :OLD.$n))";}}
 fd2_need(count($groups['numeric'])===54&&count($groups['varchar'])===6&&$groups['datetime']===['CREATE_TIME']&&in_array('CONDIMENT_STATE',$groups['numeric'],true),'54numeric+6varchar+CREATE_TIME incl CONDIMENT_STATE');
 $sql="CREATE TRIGGER ZHCT_APP.TRG_YDY_FOODS_COMPOSITION_UT\nBEFORE UPDATE ON ZHCT_APP.YDY_FOODS_COMPOSITION\nFOR EACH ROW\nBEGIN\n IF NOT UPDATING('UPDATE_TIME') THEN\n  IF\n".implode("\n OR\n",$clauses)."\n  THEN\n   :NEW.UPDATE_TIME := TO_TIMESTAMP(TO_CHAR(CURRENT_TIMESTAMP,'YYYY-MM-DD HH24:MI:SS'),'YYYY-MM-DD HH24:MI:SS');\n  END IF;\n END IF;\nEND;";
 return ['SQL'=>$sql,'groups'=>$groups];}
function fd2_gate($p){fd2_need(hash_file('sha256',__DIR__.'/plan.json')===FD2_PLAN_SHA,'Exact frozen external plan');fd2_equal($p,json_decode(file_get_contents(__DIR__.'/plan.json'),true),'Literal plan');$counts=['read'=>0,'DDL'=>0];foreach($p['steps']as $i=>$s){fd2_need($s['ordinal']===$i+1&&isset($counts[$s['kind']]),'Literal next ordinal/kind');$counts[$s['kind']]++;fd2_need(count($s['binds'])===count($s['PDO_types'])&&substr_count($s['SQL'],'?')===count($s['binds']),'Bind arity');foreach($s['binds']as $j=>$v)fd2_need(is_string($v)&&$s['PDO_types'][$j]===2,'Literal string/PDO2 bind');}fd2_equal($counts,['read'=>46,'DDL'=>2],'46read2DDL only');fd2_equal($p['budget'],['read'=>46,'DDL'=>2,'DML'=>0,'HTTP'=>0,'Auth'=>0],'No other effect budget');foreach($p['DDL_files']as $label=>$f){fd2_need(hash_file('sha256',__DIR__.'/'.$f['path'])===$f['SHA256'],'DDL bytes pinned');$ordinal=$label==='alter'?16:32;fd2_need(trim(file_get_contents(__DIR__.'/'.$f['path']))===$p['steps'][$ordinal-1]['SQL'],'Literal DDL tied to exact ordinal');}return $counts;}
function fd2_expected_columns($cols,$calibrated){if(!$calibrated)return $cols;foreach($cols as &$c)if(in_array($c['column_name'],['CREATE_TIME','UPDATE_TIME'],true)){$c['data_scale']=is_float($c['data_scale'])?0.0:0;$c['data_default']='CURRENT_TIMESTAMP(0)';$n=strlen($c['data_default']);$c['default_length']=is_float($c['default_length'])?(float)$n:$n;}unset($c);return $cols;}
function fd2_expected_ddl($sql,$calibrated){if(!$calibrated)return $sql;foreach(['CREATE_TIME','UPDATE_TIME']as $n){$old='"'.$n.'" DATETIME(6) DEFAULT CURRENT_TIMESTAMP NOT NULL';$new='"'.$n.'" DATETIME(0) DEFAULT CURRENT_TIMESTAMP(0) NOT NULL';fd2_need(substr_count($sql,$old)===1,'Exactly two original time declarations');$sql=str_replace($old,$new,$sql);}return $sql;}
function fd2_original($h){$line=fgets($h);if($line===false)return null;$r=json_decode($line,true);fd2_need(is_array($r),'Original JSONL object');return $r;}
function fd2_types($row){$types=[];foreach($row as $k=>$v){fd2_need(!is_resource($v),'Actual Food has no LOB stream');$types[$k]=gettype($v);}return $types;}
function fd2_row($record,$base,$map,$calibrated){$r=$record['row'];$old=$base['row'];fd2_need(count($r)===62&&array_keys($r)===array_keys($old),'Exact62 native column order');fd2_equal($record['native_types'],$base['native_types'],'Exact62 original PDO value types');$want=$old;foreach(['create_time','update_time']as $k){$v=$map['map'][$k.'_zero'];fd2_need(preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.000000$/D',$v)===1&&$map['map'][$k.'_old']===$v,'Only source-proven valid unchanged CAST0');if($calibrated)$want[$k]=substr($v,0,19);}fd2_equal($r,$want,'Whole62 exact old values or approved date0 output');$other=$r;unset($other['create_time'],$other['update_time']);fd2_need(count($other)===60&&hash('sha256',serialize($other))===$base['other60_typed_sha256'],'All other60 native values/hash immutable');fd2_need($r['id']===$map['map']['id'],'Original PK/date map binding');}
function fd2_baseline($dir,$p){fd2_need(is_dir($dir)&&!is_link($dir)&&(fileperms($dir)&0777)===0700,'Private baseline dir700');foreach($p['baseline']['files']as $name=>$sha){$f=$dir.'/'.$name;fd2_need(is_file($f)&&!is_link($f)&&(fileperms($f)&0777)===0600&&hash_file('sha256',$f)===$sha,'Exact immutable raw baseline '.$name);}$b=json_decode(file_get_contents($dir.'/date-fresh-private.json'),true);fd2_need($b['summary']['rows']===2338&&$b['summary']['actual_columns']===62&&$b['summary']['actual_LOB_fields']===[]&&$b['summary']['whole_before_after_equal']===true,'Original complete62/2338 stable noLOB');fd2_equal($b['summary']['actual_CAST0_changed'],['create_time'=>0,'update_time'=>0],'Actual old maps changed0');$made=fd2_build_trigger($b['metadata']['columns']);fd2_trigger($p['steps'][31]['SQL'],$made['SQL']);fd2_equal($p['trigger_fields']['numeric'],$made['groups']['numeric'],'All54 numeric names');fd2_equal($p['trigger_fields']['varchar'],$made['groups']['varchar'],'All6 varchar names');
 $h=fopen($dir.'/date-before62.jsonl','r');$m=fopen($dir.'/date-map0.jsonl','r');$all=hash_init('sha256');$other=hash_init('sha256');$ids=hash_init('sha256');$n=0;while(($base=fd2_original($h))!==null){$map=fd2_original($m);fd2_need($map!==null,'Complete original map');fd2_row($base,$base,$map,false);hash_update($all,serialize($base['row']));hash_update($other,$base['other60_typed_sha256']);hash_update($ids,serialize($base['row']['id']));$n++;}fd2_need($n===2338&&fd2_original($m)===null,'Exact original complete row/map set');fclose($h);fclose($m);fd2_need(hash_final($all)===$b['before']['whole_native_typed_sha256']&&hash_final($other)===$b['before']['other60_typed_sha256']&&hash_final($ids)===$b['before']['row_ids_typed_sha256'],'Independent streamed original whole/other60/ID typed hashes');return $b;}
function fd2_append($h,$e){$j=fd2_json($e)."\n";fd2_need(fwrite($h,$j)===strlen($j)&&fflush($h),'Private append-before/raw result flush');}
function fd2_materialize($rows){$types=[];$keys=[];$bits=[];foreach($rows as $i=>&$r){$keys[$i]=array_keys($r);foreach($r as $k=>$v){fd2_need(!is_resource($v),'Scalar metadata only');$types[$i][strtolower($k)]=gettype($v);if(is_float($v))$bits[$i][strtolower($k)]=bin2hex(pack('d',$v));}$r=array_change_key_case($r,CASE_LOWER);}unset($r);return ['rows'=>$rows,'native_types'=>$types,'native_keys'=>$keys,'native_float_bits'=>$bits];}
function fd2_stream($stmt,$s,$dir,$calibrated,$journal){$base=fopen($dir.'/date-before62.jsonl','r');$maps=fopen($dir.'/date-map0.jsonl','r');$whole=hash_init('sha256');$other=hash_init('sha256');$typehash=hash_init('sha256');$n=0;while(($r=$stmt->fetch(PDO::FETCH_ASSOC))!==false){$r=array_change_key_case($r,CASE_LOWER);$types=fd2_types($r);fd2_append($journal,['event'=>'original-stream-row','ordinal'=>$s['ordinal'],'row'=>$r,'native_types'=>$types]);$old=fd2_original($base);$map=fd2_original($maps);fd2_need($old!==null&&$map!==null&&++$n<=2338,'No extra/missing original row or map');if($s['label']==='map0'){fd2_equal(['map'=>$r,'native_types'=>$types],$map,'Actual full old-to0 mapping same original by exact ID/type');}else{fd2_row(['row'=>$r,'native_types'=>$types],$old,$map,$calibrated);$o=$r;unset($o['create_time'],$o['update_time']);hash_update($other,hash('sha256',serialize($o)));}hash_update($whole,serialize($r));hash_update($typehash,serialize($types));}fd2_need($n===2338&&fd2_original($base)===null&&fd2_original($maps)===null,'Exact all2338 rows and mappings');fclose($base);fclose($maps);return ['rows'=>$n,'whole_typed_sha256'=>hash_final($whole),'other60_typed_sha256'=>hash_final($other),'types_sha256'=>hash_final($typehash)];}
function fd2_phase($stage,$b,$p,$phase){fd2_need(count($stage)===15,'All15 phase reads complete');$calibrated=$phase!=='pre';$created=$phase==='after_trigger';$m=$b['metadata'];fd2_equal($stage['identity']['rows'],[['db'=>'ZHCT_APP']],'Exact actual connection schema');foreach(['version','indexes','index_columns','constraints','constraint_columns']as $k)fd2_equal($stage[$k]['rows'],$m[$k],'Exact unchanged typed metadata '.$k);fd2_equal($stage['columns']['rows'],fd2_expected_columns($m['columns'],$calibrated),'Only two date scale/default typed metadata changes');fd2_equal($stage['table_ddl']['rows'][0]['ddl'],fd2_expected_ddl($m['table_ddl'][0]['ddl'],$calibrated),'Only two date GET_DDL changes');fd2_equal($stage['rows_before'],$stage['rows_after'],'Whole62 read-window stable');fd2_need($stage['rows_before']['other60_typed_sha256']===$b['before']['other60_typed_sha256'],'Immutable other60 whole hash');fd2_need($stage['map0']['rows']===2338,'All mapping rows');$clock=$stage['clock']['rows'];fd2_need(count($clock)===1&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{6} \+08:00$/D',$clock[0]['full_clock'])&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} \+08:00$/D',$clock[0]['zero_clock']),'Actual original same-build precision clock');
 if(!$created){foreach(['triggers','objects','dependencies']as $k)fd2_equal($stage[$k]['rows'],$m[$k],'Original trigger0/collision0/deps0 '.$k);return;}
 $triggers=$stage['triggers']['rows'];fd2_need(count($triggers)===1,'Only one owned trigger');$t=$triggers[0];fd2_need($t['trigger_name']===$p['trigger']&&$t['table_name']==='YDY_FOODS_COMPOSITION'&&$t['table_owner']==='ZHCT_APP'&&$t['status']==='Y'&&$t['triggering_type']==='BEFORE ROW'&&$t['triggering_event']==='UPDATE','Y BEFORE ROW UPDATE own trigger');fd2_trigger($t['trigger_body'],$p['steps'][31]['SQL']);$objects=$m['objects'];$objects[]=['object_name'=>$p['trigger'],'object_type'=>'TRIGGER','status'=>'VALID'];usort($objects,static function($a,$b){return strcmp($a['object_name'],$b['object_name']);});fd2_equal($stage['objects']['rows'],$objects,'Only expected TABLE+TRIGGER VALID');fd2_equal($stage['dependencies']['rows'],$p['baseline']['expected_owned_dependencies'],'Only own dependency, actual Prepay template names mapped explicitly');}
function fd2_connect(){ $file='/tmp/zhct-dm-poc-20260928.fcpiiN/store/application/database.php';fd2_need(realpath($file)===$file&&!is_link($file),'Original code config');$cfg=(static function($f){return require $f;})($file);fd2_need($cfg['hostname']==='127.0.0.1'&&(string)$cfg['hostport']==='5237'&&$cfg['database']==='ZHCT_APP'&&getenv('POC_HOST')==='127.0.0.1'&&getenv('POC_PORT')==='5237'&&getenv('POC_SCHEMA')==='ZHCT_APP'&&getenv('POC_EXTERNAL_STUBS')==='1'&&getenv('POC_REAL_QUEUE')==='0','Exact loopback nonproduction target only');$pdo=new PDO('dm:host=127.0.0.1;port=5237;schema=ZHCT_APP',$cfg['username'],$cfg['password'],[PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_CASE=>PDO::CASE_LOWER,PDO::ATTR_STRINGIFY_FETCHES=>false,PDO::ATTR_EMULATE_PREPARES=>false]);unset($cfg);return $pdo;}
function fd2_execute($p,$approval){fd2_need(getenv('FD2_ACTOR')==='ROOT'&&getenv('FD2_RELEASE')==='FOOD_DATE_ROOT_RELEASED'&&isset($approval['baseline_dir'],$approval['runner_sha256']),'Root explicit release before config or PDO');fd2_gate($p);fd2_need(getenv('FD2_ROOT_PLAN_SHA')===FD2_PLAN_SHA&&getenv('FD2_ROOT_RUNNER_SHA')===$approval['runner_sha256']&&hash_file('sha256',__FILE__)===$approval['runner_sha256'],'Exact approved Root plan/runner SHA');$b=fd2_baseline($approval['baseline_dir'],$p);fd2_need(!file_exists(FD2_RUNTIME)&&!is_link(FD2_RUNTIME)&&mkdir(FD2_RUNTIME,0700),'Exclusive once runtime, never replay successful DDL');$h=fopen(FD2_RUNTIME.'/events.jsonl','x');fd2_need(is_resource($h)&&chmod(FD2_RUNTIME.'/events.jsonl',0600),'Private original journal600');$state=['cursor'=>0,'read'=>0,'DDL'=>0,'pre'=>false,'after_alter'=>false,'after_trigger'=>false];$stage=[];$stmt=null;
 try{fd2_append($h,['event'=>'start','plan_sha256'=>FD2_PLAN_SHA,'runner_sha256'=>$approval['runner_sha256'],'baseline_files'=>$p['baseline']['files']]);$db=fd2_connect();foreach($p['steps']as $s){fd2_need($s===$p['steps'][$state['cursor']]&&$state[$s['kind']]<$p['budget'][$s['kind']],'Exact next ordinal/budget no retries');if($s['kind']==='DDL')fd2_need($s['ordinal']===16?($state['pre']&&$state['DDL']===0):($s['ordinal']===32&&$state['after_alter']&&$state['DDL']===1),'Full phase proof before each DDL');fd2_append($h,['event'=>'append-before','statement'=>$s,'state'=>$state]);$state[$s['kind']]++;$stmt=$db->prepare($s['SQL']);foreach($s['binds']as $i=>$v)fd2_need($stmt->bindValue($i+1,$v,$s['PDO_types'][$i]),'Original typed native bind');fd2_need($stmt->execute(),'Native statement result');
  if($s['kind']==='DDL')fd2_append($h,['event'=>'original-DDL-result','statement'=>$s,'rowCount'=>$stmt->rowCount(),'errorInfo'=>$stmt->errorInfo()]);
  else{if(in_array($s['label'],['rows_before','rows_after','map0'],true))$rec=fd2_stream($stmt,$s,$approval['baseline_dir'],$s['phase']!=='pre',$h);else $rec=fd2_materialize($stmt->fetchAll(PDO::FETCH_ASSOC));fd2_append($h,['event'=>'original-read-result','statement'=>$s,'record'=>$rec,'rowCount'=>$stmt->rowCount(),'errorInfo'=>$stmt->errorInfo()]);if($s['label']==='identity')fd2_equal($rec['rows'],[['db'=>'ZHCT_APP']],'Immediate connection identity before next SQL');if($s['label']==='version')fd2_equal($rec['rows'],$b['metadata']['version'],'Immediate exact build');if($s['phase']==='final_definition'){fd2_need($state['after_trigger']&&count($rec['rows'])===1,'Actual final full trigger DDL');fd2_trigger($rec['rows'][0]['ddl'],$p['steps'][31]['SQL']);}else{$stage[$s['phase']][$s['label']]=$rec;if($s['label']==='clock'){fd2_phase($stage[$s['phase']],$b,$p,$s['phase']);$state[$s['phase']]=true;fd2_append($h,['event'=>'protected-phase','phase'=>$s['phase'],'state'=>$state]);}}}$state['cursor']++;}
  fd2_need($state['read']===46&&$state['DDL']===2&&$state['after_trigger'],'Complete bounded46read2DDL ledger');fd2_append($h,['event'=>'complete','state'=>$state,'DML'=>0,'HTTP'=>0,'Auth'=>0]);fclose($h);echo fd2_json(['status'=>'ROOT_FOOD_DATE_REPAIR_COMPLETE','state'=>$state,'journal_sha256'=>hash_file('sha256',FD2_RUNTIME.'/events.jsonl')])."\n";return 0;
 }catch(Throwable $e){fd2_append($h,['event'=>'STOPPED_NO_RETRY_NO_DROP_NO_REVERSE_MUTATION','class'=>get_class($e),'code'=>$e->getCode(),'message'=>$e->getMessage(),'errorInfo'=>$stmt?$stmt->errorInfo():null,'state'=>$state]);fclose($h);fwrite(STDERR,fd2_json(['status'=>'STOPPED_ORIGINAL_STATE_RETAINED','state'=>$state])."\n");return 1;}}
if(realpath($_SERVER['SCRIPT_FILENAME'])===__FILE__){try{$p=json_decode(file_get_contents(__DIR__.'/plan.json'),true);$mode=$argv[1]??'--gate';$counts=fd2_gate($p);if($mode==='--gate'){$b=fd2_baseline($argv[2]??__DIR__.'/private',$p);echo fd2_json(['status'=>'PURE_GATE_GREEN','counts'=>$counts,'plan_sha256'=>FD2_PLAN_SHA,'runner_sha256'=>hash_file('sha256',__FILE__),'DB_connections'=>0,'SQL'=>0])."\n";exit(0);}fd2_need($mode==='--execute'&&isset($argv[2]),'Root explicit execute/baseline directory');exit(fd2_execute($p,['baseline_dir'=>$argv[2],'runner_sha256'=>getenv('FD2_ROOT_RUNNER_SHA')]));}catch(Throwable $e){fwrite(STDERR,$e->getMessage()."\n");exit(1);}}
