# gov_profile_launcher_v1.7.ps1 - PS 5.1-safe param() $UseAssertsOnly = $true # sécurité maximale $EnforceKBGate = $true function Ensure-Dir([string]$p){ if($p -and !(Test-Path $p)){ New-Item -ItemType Directory -Force -Path $p | Out-Null } } function Ask-YesNo([string]$q){ while($true){ $a=Read-Host ($q+' (O/N)'); if($a -in @('O','o','Y','y')){return $true}; if($a -in @('N','n')){return $false} } } function FileSha([string]$p){ if(Test-Path $p){ (Get-FileHash -Algorithm SHA256 -LiteralPath $p).Hash } else { '' } } function Discover-Profiles(){ $root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $dir = Join-Path $root 'scripts' if(!(Test-Path $dir)){ return @() } $files = Get-ChildItem -LiteralPath $dir -Filter 'rebuild_bootpack_full_*.ps1' -File $list = @() foreach($f in $files){ $n = $f.BaseName $name = $n -replace '^rebuild_bootpack_full_','' $name = ($name -split '_v')[0] if([string]::IsNullOrWhiteSpace($name)){ continue } $label = $name.ToUpper() $obj = New-Object psobject -Property @{ Label=$label; Script=$f.FullName } $list += $obj } $list | Sort-Object Label } function Choose-Profile(){ $profiles = Discover-Profiles if($profiles.Count -eq 0){ Write-Host "[ERR] Aucun profil trouvé."; return $null } Write-Host "=== Profils détectés ===" for($i=0; $i -lt $profiles.Count; $i++){ ($i+1).ToString().PadLeft(2,' ') + ") " + $profiles[$i].Label | Write-Host } while($true){ $c = Read-Host "Numéro de profil" $ok=[int]::TryParse($c,[ref]([int]$null)) if($ok){ $idx=[int]$c-1; if($idx -ge 0 -and $idx -lt $profiles.Count){ return $profiles[$idx] } } Write-Host "Sélection invalide." } } function Export-IngestPack([string]$reason){ $root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $exp = Join-Path $root 'scripts\export_ingest_pack_v1.0.ps1' if(!(Test-Path $exp)){ Write-Host "[WARN] export_ingest_pack_v1.0.ps1 manquant."; return } & 'powershell.exe' -NoProfile -ExecutionPolicy Bypass -File $exp -Reason $reason 2>&1 } function Run-Selected([string]$mode){ $sel = Choose-Profile if($null -eq $sel){ return } $root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $logs=Join-Path $root 'logs'; Ensure-Dir $logs $ts=Get-Date -Format 'yyyyMMdd_HHmmss' $log=Join-Path $logs ('run_'+$sel.Label+'_'+$mode+'_'+$ts+'.txt') if(!(Test-Path $sel.Script)){ Write-Host "[ERR] Script introuvable: "+$sel.Script; return } $bp = Join-Path $root 'bootpack\bootpack.txt' $kb = Join-Path $root 'bootpack\bootpack_paste.txt' $preBP = FileSha $bp $preKB = FileSha $kb if($mode -eq 'Write'){ if(-not (Ask-YesNo ("Confirmer l'écriture réelle pour "+$sel.Label))){ Write-Host "Annulé."; return } if($EnforceKBGate){ $validator=Join-Path $root 'scripts\kb_guard_validator_v1.2.ps1' if(!(Test-Path $validator)){ Write-Host "[BLOCKING] kb_guard_validator_v1.2.ps1 manquant."; return } $tmpLog = Join-Path $logs ('run_kb_guard_'+$ts+'.txt') $args=@(); $args+='-Profile'; $args+=$sel.Label if($UseAssertsOnly){ $args+='-AssertsOnly' } & 'powershell.exe' -NoProfile -ExecutionPolicy Bypass -File $validator @args 2>&1 | Tee-Object -FilePath $tmpLog | Out-Null Get-Content -LiteralPath $tmpLog -Tail 60 if($LASTEXITCODE -ne 0){ Write-Host "[GATE BLOCKED] KB blockers non couverts pour profil "+$sel.Label+"." Write-Host "Ajoute les lignes dans [KB_GUARD_ASSERTS] puis relance." return } } } $arg=@('-NoProfile','-ExecutionPolicy','Bypass','-File',$sel.Script); if($mode -eq 'Preview'){$arg+='-Preview'}else{$arg+='-Write'} & 'powershell.exe' @arg 2>&1 | Tee-Object -FilePath $log | Out-Null if(Test-Path $log){ Get-Content -LiteralPath $log -Tail 60; 'Log -> '+$log | Write-Host } else { Write-Host "[WARN] Log non trouvé: $log" } if($mode -eq 'Write'){ $postBP = FileSha $bp $postKB = FileSha $kb if(($preBP -ne $postBP) -or ($preKB -ne $postKB)){ Write-Host "[INFO] Changement détecté (BootPack/KB). Export du pack d'ingestion." Export-IngestPack -reason "post-write change" } } } function Recalc-ManifestShaObject([string]$raw){ $m = [regex]::Match($raw,'(?ms)^\[SYNC_MANIFEST\]\s*(.*?)(?=^\[|\Z)') if(-not $m.Success){ return @{'ok'=$false;'msg'='Section [SYNC_MANIFEST] absente.'} } $blk = $m.Groups[1].Value $infile = [regex]::Match($blk,'(?mi)^\s*MANIFEST_SHA256\s*:\s*([0-9a-f]+)\s*$').Groups[1].Value.ToLower() $coreLines=@(); foreach($ln in ($blk -split "`r?`n")){ if($ln -match '^\s*MANIFEST_SHA256\s*:'){ continue }; $coreLines += $ln.TrimEnd() } $core=[string]::Join("`r`n",$coreLines) $sha=[BitConverter]::ToString([Security.Cryptography.SHA256]::Create().ComputeHash([Text.Encoding]::UTF8.GetBytes($core))).Replace('-','').ToLower() return @{'ok'=$true;'infile'=$infile;'recalc'=$sha} } function Audit-Manifest(){ $bp="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry\bootpack\bootpack.txt" if(!(Test-Path $bp)){ Write-Host "[ERR] BootPack introuvable: $bp"; return } $raw = Get-Content -LiteralPath $bp -Raw -Encoding UTF8 $res = Recalc-ManifestShaObject $raw if(-not $res.ok){ Write-Host "[ERR] "+$res.msg; return } 'InFile = '+$res.infile | Write-Host 'Recalc = '+$res.recalc | Write-Host 'STATUS = '+($(if($res.infile -eq $res.recalc){'MATCH'}else{'DIFF'})) | Write-Host } function KB-Guards-ValidateMenu([switch]$Export,[switch]$Suggest){ $root="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" $validator=Join-Path $root 'scripts\kb_guard_validator_v1.2.ps1' if(!(Test-Path $validator)){ Write-Host "[ERR] kb_guard_validator_v1.2.ps1 introuvable."; return } $logs=Join-Path $root 'logs'; Ensure-Dir $logs $ts=Get-Date -Format 'yyyyMMdd_HHmmss'; $log=Join-Path $logs ('run_kb_guard_'+$ts+'.txt') $args=@() if($Export){ $args+='-Export' } if($Suggest){ $args+='-SuggestAsserts' } if($UseAssertsOnly){ $args+='-AssertsOnly' } & 'powershell.exe' -NoProfile -ExecutionPolicy Bypass -File $validator @args 2>&1 | Tee-Object -FilePath $log | Out-Null Get-Content -LiteralPath $log -Tail 100 'Log -> '+$log | Write-Host } function Show-Pending(){ $outbox="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry\ingest_outbox" $ptr=Join-Path $outbox 'PENDING_LAST.txt' if(Test-Path $ptr){ $path=(Get-Content -LiteralPath $ptr -Raw -Encoding UTF8).Trim() if($path -and (Test-Path $path)){ if(Test-Path (Join-Path $path 'INGEST_PENDING.flag')){ Write-Host "[PENDING] Un pack d'ingestion attend envoi -> $path" } } } } function Mark-Ingested(){ $outbox="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry\ingest_outbox" $ptr=Join-Path $outbox 'PENDING_LAST.txt' if(!(Test-Path $ptr)){ Write-Host "[INFO] Aucun pack en attente."; return } $path=(Get-Content -LiteralPath $ptr -Raw -Encoding UTF8).Trim() $flag=Join-Path $path 'INGEST_PENDING.flag' if(Test-Path $flag){ Rename-Item -LiteralPath $flag -NewName 'INGEST_DONE.flag' -Force Write-Host "Marqué comme ingéré -> "+$path } else { Write-Host "[INFO] Pas de flag PENDING ici." } } function Effective-Policy(){ $bp="\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry\bootpack\bootpack.txt" if(!(Test-Path $bp)){ Write-Host "[ERR] BootPack introuvable."; return } $raw = Get-Content -LiteralPath $bp -Raw -Encoding UTF8 $proj = [regex]::Match($raw,'(?mi)^\s*PROJECT:\s*(.+)$').Groups[1].Value.Trim() $rootp= [regex]::Match($raw,'(?mi)^\s*ROOT:\s*(.+)$').Groups[1].Value.Trim() $policy= [regex]::Match($raw,'(?ms)^\[POLICY\]\s*(.*?)(?=^\[|\Z)').Groups[1].Value.Trim() $profRef=[regex]::Match($raw,'(?ms)^\[PROJECT_PROFILE_REF\]\s*(.*?)(?=^\[|\Z)').Groups[1].Value.Trim() Write-Host "=== EFFECTIVE POLICY ===" "PROJECT : $proj" | Write-Host "ROOT : $rootp" | Write-Host "POLICY (L0) :" | Write-Host foreach($ln in ($policy -split "`r?`n")){ " - "+$ln.Trim() | Write-Host } "PROFILE REF :" | Write-Host foreach($ln in ($profRef -split "`r?`n")){ " "+$ln | Write-Host } } function Show-Menu(){ Clear-Host Show-Pending Write-Host "=== GOV PROFILE LAUNCHER v1.7 (asserts-only) ===" Write-Host "1) Lancer profil - Preview" Write-Host "2) Lancer profil - Write (commit) [GATE KB (asserts-only) + export ingest]" Write-Host "3) Audit MANIFEST (recalc vs fichier)" Write-Host "4) Exporter INGEST PACK (manuel)" Write-Host "5) Marquer dernier pack comme INGESTÉ" Write-Host "6) AFFICHER EFFECTIVE POLICY (L0+profil)" Write-Host "7) KB GUARDS - VALIDATE COVERAGE" Write-Host "8) KB GUARDS - EXPORT REPORT" Write-Host "9) KB GUARDS - SUGGEST ASSERTS" Write-Host "0) Quitter" Write-Host "" } while($true){ Show-Menu $c=Read-Host "Choix" switch($c){ '1' { Run-Selected -mode 'Preview' ; Pause } '2' { Run-Selected -mode 'Write' ; Pause } '3' { Audit-Manifest ; Pause } '4' { Export-IngestPack -reason "manual"; Pause } '5' { Mark-Ingested ; Pause } '6' { Effective-Policy ; Pause } '7' { KB-Guards-ValidateMenu ; Pause } '8' { KB-Guards-ValidateMenu -Export ; Pause } '9' { KB-Guards-ValidateMenu -Suggest ; Pause } '0' { return } default { Write-Host "Sélection invalide." ; Start-Sleep -Seconds 1 } } }