make(Kernel::class)->bootstrap(); try{ $db=DB::connection();$identity=$db->selectOne("select current_database() as db,current_user as role,inet_server_port() as port,current_setting('cluster_name') as cluster"); if(str_replace('\\','/',$app->basePath())!==$host||$identity->db!=='sgf_modules_dev_2576427d1939'||$identity->role!=='sgf_modules_qa_2576427d1939'||(int)$identity->port!==55439||$identity->cluster!=='sgf_modular_regression_2576427d1939'||config('app.url')!=='http://127.0.0.1:8039')throw new RuntimeException('Wrong persistent synthetic demonstration'); app(Permissions::class)->require(1,1,1,'access.manage'); $candidate=json_decode(file_get_contents($root.'/docs/proofs/sgf-sourcing-candidate.json'),true,32,JSON_THROW_ON_ERROR); if(!class_exists(Schema::class))require_once $candidate['directory'].'/integrations/sgf/sourcing/Schema.php';require_once $root.'/integrations/sgf/operational/CodeDeployment.php'; $hash=function(string $table)use($db){return hash('sha256',json_encode($db->table($table)->orderBy('id')->get(),JSON_THROW_ON_ERROR));}; $action=$argv[1]??''; if($action==='prepare'){ if(is_dir($recovery))throw new RuntimeException('Recovery exists: do not overwrite'); $proof=json_decode(file_get_contents($root.'/docs/proofs/sgf-sourcing-isolated.json'),true);if(!$proof['passed']||!$proof['persistent_demo_unchanged']||!($proof['recovery']['data_schema_restore_verified']??false)||$proof['setup']['deployment']['deployment_id']!==substr($candidate['archive']['manifest_sha256'],0,24))throw new RuntimeException('Current candidate needs accepted isolation/recovery proof'); if($db->table('users')->count()!==3)throw new RuntimeException('Unexpected accounts: reconcile before proceeding'); mkdir($recovery,0700,true);$cfg=$db->getConfig();(new Process(['C:/Program Files/PostgreSQL/17/bin/pg_dump.exe','--format=custom','--file='.$recovery.'/before.dump','--dbname='.$identity->db],null,['PGHOST'=>'127.0.0.1','PGPORT'=>'55439','PGUSER'=>$identity->role,'PGPASSWORD'=>$cfg['password']],null,90))->mustRun(); (new Process(['C:/Program Files/PostgreSQL/17/bin/pg_restore.exe','--list',$recovery.'/before.dump']))->mustRun(); $baseline=['candidate'=>$candidate,'identity'=>$identity,'backup_sha256'=>hash_file('sha256',$recovery.'/before.dump'),'users'=>$hash('users'),'requests'=>$hash('ir_requests'),'budget'=>$hash('cb_budget_entries'),'consumption'=>$hash('cb_budget_consumptions'),'grants'=>$db->table('permission_grants')->orderBy('id')->get()->all(),'pointer_sha256'=>hash_file('sha256',$host.'/packages/purchasing/current.json')]; file_put_contents($recovery.'/baseline.json',json_encode($baseline,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo json_encode(['prepared'=>true,'identity'=>$identity,'backup_sha256'=>$baseline['backup_sha256']]); }elseif($action==='apply'){ $baseline=json_decode(file_get_contents($recovery.'/baseline.json'),true,64,JSON_THROW_ON_ERROR); foreach(['users'=>'users','requests'=>'ir_requests','budget'=>'cb_budget_entries','consumption'=>'cb_budget_consumptions'] as $key=>$table)if($baseline[$key]!==$hash($table))throw new RuntimeException('Baseline changed: '.$table); if($baseline['candidate']['archive']['manifest_sha256']!==$candidate['archive']['manifest_sha256']||!hash_equals($baseline['backup_sha256'],hash_file('sha256',$recovery.'/before.dump')))throw new RuntimeException('Candidate/backup changed'); if(is_file($host.'/storage/framework/down'))throw new RuntimeException('Unexpected prior maintenance');file_put_contents($host.'/storage/framework/down','{}'); $old=json_decode(file_get_contents($host.'/packages/internal-requisitions/manifest.json'),true);$current=json_decode(file_get_contents($host.'/storage/app/private/module-deployments/current.json'),true);$next=json_decode(file_get_contents($candidate['directory'].'/manifest.json'),true); $migration=Schema::migrate($db,$baseline['backup_sha256'],'Owner-confirmed synthetic sourcing 8039'); $receipt=['host'=>$host,'from_manifest_sha256'=>$current['manifest_sha256'],'to_manifest_sha256'=>$candidate['archive']['manifest_sha256'],'schema_verified'=>Schema::compatible($db),'changes'=>[]]; foreach($next['support_files'] as $file=>$sha)if(($old['support_files'][$file]??null)!==$sha)$receipt['changes'][$file]=['before'=>$old['support_files'][$file]??null,'after'=>$sha]; file_put_contents($host.'/storage/app/private/module-deployments/schema-transition-'.$candidate['archive']['manifest_sha256'].'.json',json_encode($receipt)); $deploy=new CodeDeployment($host);$plan=$deploy->plan($candidate['directory'],$candidate['archive']['manifest_sha256']);$applied=$deploy->apply($candidate['directory'],$candidate['archive']['manifest_sha256'],$plan['target_sha256'],'Owner-confirmed synthetic sourcing 8039',true);file_put_contents($recovery.'/applied.json',json_encode($applied,JSON_PRETTY_PRINT)); $db->transaction(function()use($db){ foreach([1=>['purchasing_sourcing.prepare'],2=>['purchasing_sourcing.evaluate','purchasing_sourcing.propose']] as $id=>$rights)foreach($rights as $right){ $scope=['user_id'=>$id,'tenant_id'=>1,'company_id'=>1,'unit_id'=>1,'permission'=>$right];$inserted=$db->table('permission_grants')->insertOrIgnore($scope+['created_at'=>now(),'updated_at'=>now()]); if($inserted)Audit::record('access.granted',1,1,1,1,$id,details:['permission'=>$right,'reason'=>'Owner explicitly authorised exact sourcing map; no fourth persistent account.']); } foreach(['A','B','C'] as $letter)$db->table('ss_suppliers')->insertOrIgnore(['tenant_id'=>1,'company_id'=>1,'unit_id'=>1,'reference'=>'SOURCING-DEMO-'.$letter,'payload'=>json_encode(['name'=>'Fornecedor sintético '.$letter,'active'=>true,'blocked'=>false,'review_until'=>'2027-12-31','version'=>1,'synthetic_only'=>true]),'created_at'=>now()]); }); file_put_contents($host.'/config/purchasing-sourcing.php','true];');unlink($host.'/storage/framework/down');echo json_encode(['applied'=>true,'deployment'=>$applied,'schema'=>$migration,'persistent_accounts_created'=>false,'grants_added'=>3,'confirmation_blocked_without_independent_identity'=>true]); }elseif($action==='apply-code'){ if(!Schema::compatible($db))throw new RuntimeException('Code-only update requires the existing compatible sourcing schema'); $proof=json_decode(file_get_contents($root.'/docs/proofs/sgf-sourcing-isolated.json'),true);if(!$proof['passed']||$proof['setup']['deployment']['deployment_id']!==substr($candidate['archive']['manifest_sha256'],0,24))throw new RuntimeException('Candidate proof mismatch'); $before=[];foreach([...Schema::TABLES,'ir_requests','cb_budget_entries','cb_budget_consumptions','permission_grants','users'] as $table)$before[$table]=$hash($table); if(is_file($host.'/storage/framework/down'))throw new RuntimeException('Unexpected prior maintenance'); $deploy=new CodeDeployment($host);$plan=$deploy->plan($candidate['directory'],$candidate['archive']['manifest_sha256']); file_put_contents($recovery.'/before-final-code.json',json_encode(['candidate'=>$candidate,'data_hashes'=>$before,'plan'=>$plan],JSON_PRETTY_PRINT));file_put_contents($host.'/storage/framework/down','{}'); $applied=$deploy->apply($candidate['directory'],$candidate['archive']['manifest_sha256'],$plan['target_sha256'],'Authorised sourcing final code refinement',true); foreach($before as $table=>$expected)if($hash($table)!==$expected)throw new RuntimeException('Code update changed data: '.$table); file_put_contents($recovery.'/final-code.json',json_encode($applied,JSON_PRETTY_PRINT));unlink($host.'/storage/framework/down');echo json_encode(['applied'=>true,'code_only'=>true,'deployment'=>$applied,'data_preserved'=>true,'schema_changed'=>false,'rights_changed'=>false]); }elseif($action==='verify'){ if(hash_file('sha256',$host.'/packages/internal-requisitions/manifest.json')!==$candidate['archive']['manifest_sha256'])throw new RuntimeException('Installed candidate does not match the delivery report'); $baseline=json_decode(file_get_contents($recovery.'/baseline.json'),true,64,JSON_THROW_ON_ERROR);foreach(['users'=>'users','requests'=>'ir_requests','budget'=>'cb_budget_entries','consumption'=>'cb_budget_consumptions'] as $key=>$table)if($baseline[$key]!==$hash($table))throw new RuntimeException('Protected baseline changed: '.$table); $current=$db->table('permission_grants')->orderBy('id')->get()->keyBy('id');foreach($baseline['grants'] as $old)if((array)$current[$old['id']]!=$old)throw new RuntimeException('Previous grant changed'); $new=$current->except(array_column($baseline['grants'],'id'))->values()->all();if(count($new)!==3)throw new RuntimeException('Expected exactly three authorised new rights');foreach($new as $g)if([$g->tenant_id,$g->company_id,$g->unit_id]!==[1,1,1]||!in_array([$g->user_id,$g->permission],[[1,'purchasing_sourcing.prepare'],[2,'purchasing_sourcing.evaluate'],[2,'purchasing_sourcing.propose']],true))throw new RuntimeException('Unapproved grant'); if(hash_file('sha256',$host.'/packages/purchasing/current.json')!==$baseline['pointer_sha256'])throw new RuntimeException('Pointer changed'); $process=$db->table('ss_processes')->where(['tenant_id'=>1,'company_id'=>1,'unit_id'=>1,'request_id'=>1])->first();$state=json_decode($process->payload,true);if($state['confirmed']!==null||$state['proposals']!==[])throw new RuntimeException('Incomplete comparison must not be awarded'); $report=['passed'=>true,'identity'=>$identity,'scope'=>'1:1:1','candidate'=>$candidate,'protected_users_requisition_reservation_consumption_unchanged'=>true,'previous_grants_preserved'=>true,'new_grants'=>$new,'persistent_accounts_created'=>false,'proposals'=>0,'confirmed'=>false,'suppliers_selected'=>count($state['suppliers']),'quotations'=>count($state['quotes']),'pending'=>'Second eligible evaluator and independent confirmer absent; no account created by owner decision.','backup_sha256'=>$baseline['backup_sha256'],'recovery_directory'=>$recovery,'checked_at'=>gmdate('c')];file_put_contents($root.'/docs/proofs/sgf-sourcing-demo.json',json_encode($report,JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR));echo json_encode($report,JSON_PRETTY_PRINT); }else throw new RuntimeException('Use prepare/apply/verify'); }catch(Throwable $e){fwrite(STDERR,$e->getMessage()."\n");exit(1);}