make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap(); if(!\App\Modules\Identity\Services\LocalDatabaseGuard::allows()) throw new RuntimeException('Own local development database required.'); $db=\Illuminate\Support\Facades\DB::class; $file=storage_path('app/service-fiscal-preservation-before.json'); $before=in_array('--before',$argv,true); $grants=in_array('--after-grants',$argv,true); $baseline=$before ? null : json_decode(file_get_contents($file),true,64,JSON_THROW_ON_ERROR); $tables=$before ? array_map(fn($r)=>$r->tablename,$db::select("SELECT tablename FROM pg_tables WHERE schemaname='public' AND (tablename ~ '^(a_|b_|ca_|doc_|document_|service_calculation_)' OR tablename IN ('users','tenants','companies','units','company_memberships','permission_grants','identity_audit')) ORDER BY tablename")) : array_keys($baseline['tables']); $summary=[]; foreach($tables as $table) { if(!preg_match('/^[a-z_]+$/D',$table)) throw new RuntimeException('Unexpected table.'); $where=''; if($grants && in_array($table,['permission_grants','identity_audit'],true)) $where=' WHERE id <= '.(int)$baseline['max'][$table]; $r=$db::selectOne('SELECT count(*) AS n, md5(COALESCE(string_agg(row_to_json(t)::text, E\'\\n\' ORDER BY row_to_json(t)::text),\'\')) AS fingerprint FROM (SELECT * FROM "'.$table.'"'.$where.') t'); $summary[$table]=['count'=>(int)$r->n,'hash'=>$r->fingerprint]; } if($before) { file_put_contents($file,json_encode(['tables'=>$summary,'max'=>['permission_grants'=>(int)$db::table('permission_grants')->max('id'),'identity_audit'=>(int)$db::table('identity_audit')->max('id')]],JSON_THROW_ON_ERROR)); echo "Existing SGF development records fingerprinted in database; no record contents disclosed.\n"; exit; } if($summary!==$baseline['tables']) throw new RuntimeException('Existing SGF record fingerprint changed; review before proceeding.'); foreach(['sf_proposals','sf_decisions','sf_activation_events'] as $table) if($db::table($table)->count()!==0) throw new RuntimeException('Unexpected real fiscal lifecycle record.'); if($grants) { $actual=$db::table('permission_grants')->where('permission','like','service_fiscal.%')->orderBy('user_id')->orderBy('permission')->get(['user_id','tenant_id','company_id','unit_id','permission'])->map(fn($r)=>(array)$r)->all(); $expected=[]; foreach([1=>['view','propose','calculate'],2=>['view','approve']] as $user=>$actions) foreach($actions as $a) $expected[]=['user_id'=>$user,'tenant_id'=>1,'company_id'=>1,'unit_id'=>$a==='calculate'?1:null,'permission'=>'service_fiscal.'.$a]; usort($expected,fn($a,$b)=>[$a['user_id'],$a['permission']]<=>[$b['user_id'],$b['permission']]); if($actual!==$expected) throw new RuntimeException('Fiscal grants differ from specifically confirmed matrix.'); $new=$db::table('identity_audit')->where('id','>',$baseline['max']['identity_audit'])->where('action','service_fiscal.initial_grant')->count(); if($new!==5) throw new RuntimeException('Expected five audited initial grants.'); } file_put_contents(base_path('docs/proofs/service-fiscal-dev-preservation'.($grants?'-grants':'').'.json'),json_encode(['database'=>'sgf_tempo_dev','preexisting_tables_checked'=>count($summary),'preexisting_records_unchanged'=>true,'new_fiscal_tables_empty'=>true,'real_profile_activated'=>false,'confirmed_grants_verified'=>$grants?5:0,'prior_grants_and_audit_unchanged'=>true],JSON_PRETTY_PRINT|JSON_THROW_ON_ERROR)); echo $grants ? "Exactly five confirmed fiscal grants and audit entries verified; prior records preserved; no real profile created or activated.\n" : "Migration preserved existing records and grants; all new fiscal tables empty.\n";