# kb_bug_intake_gate_v1.0.ps1 # Principe: AUCUNE correction sans référence. Ce script: # 1) fabrique un AUTO_*.txt propre (entry JSON unique, ASCII safe) # 2) le dépose dans updates\inbox\kb\ # 3) déclenche la pipeline guard via wrapper NAS (ingest -> scrub -> emit -> acceptance -> quicklog) # PS 5.1-safe, UTF-8 BOM, TXT-only. Pas de ternaire. [CmdletBinding()] param( [switch]$Preview, [switch]$Execute, [string]$Root = "\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry", [string]$StageRoot = "C:\Temp_Gouvernance", # Données bug (min: -Title) [Parameter(Mandatory=$true)] [string]$Title, [switch]$Blocking, [string]$Workaround = "", [string]$Note = "", [string]$Fix = "", [string[]]$Tags = @(), [string[]]$SeenIn = @(), # Comportement [switch]$NoRunPipeline # si présent: n'exécute pas la pipeline après l'ingest ) function Ensure-Dir([string]$Path){ if(-not (Test-Path -LiteralPath $Path)){ New-Item -ItemType Directory -Force -Path $Path | Out-Null } } function Ensure-Parent([string]$Target){ $p=Split-Path -Parent $Target; if($p){ Ensure-Dir $p } } function NowIso(){ (Get-Date).ToString("yyyy-MM-ddTHH:mm:ssK") } function AsciiFold([string]$s){ if($null -eq $s){ return "" } $s = $s -replace "`u2018","'" -replace "`u2019","'" -replace "`u201A","'" -replace "`u201B","'" $s = $s -replace "`u201C",'"' -replace "`u201D",'"' -replace "`u201E",'"' $s = $s -replace "`u2013","-" -replace "`u2014","-" $s = $s -replace "`u00A0"," " $norm = $s.Normalize([Text.NormalizationForm]::FormD) $sb = New-Object Text.StringBuilder foreach($ch in $norm.ToCharArray()){ $cat = [Globalization.CharUnicodeInfo]::GetUnicodeCategory($ch) if($cat -ne [Globalization.UnicodeCategory]::NonSpacingMark){ [void]$sb.Append($ch) } } $s2 = $sb.ToString() $s2 = [regex]::Replace($s2,"[^\x09\x0A\x0D\x20-\x7E]"," ") $s2 = $s2 -replace "@\{","(" $s2 = [regex]::Replace($s2,"\s{2,}"," ") $s2.Trim() } function Trunc([string]$s,[int]$n){ if($null -eq $s){ return "" } if($s.Length -le $n){ return $s } return $s.Substring(0,$n) } function Slugify([string]$s){ $t = AsciiFold $s $t = $t.ToLower() $t = [regex]::Replace($t,"[^a-z0-9]+","-") $t = [regex]::Replace($t,"-+","-").Trim("-") if([string]::IsNullOrEmpty($t)){ $t = "bug" } if($t.Length -gt 60){ $t = $t.Substring(0,60).Trim("-") } $t } function Write-SafeText([string]$Target,[string]$Content,[string]$StageRoot){ Ensure-Dir $StageRoot; Ensure-Parent $Target $tmp=Join-Path $StageRoot ("write_" + [IO.Path]::GetRandomFileName()) $utf8=New-Object Text.UTF8Encoding($true) [IO.File]::WriteAllText($tmp,$Content,$utf8) $tmpR="$Target.tmp" Copy-Item -LiteralPath $tmp -Destination $tmpR -Force Move-Item -LiteralPath $tmpR -Destination $Target -Force Remove-Item -LiteralPath $tmp -Force } # Cibles $Inbox = Join-Path $Root "updates\inbox\kb" Ensure-Dir $Inbox # Normalisation des champs $title = Trunc (AsciiFold $Title) 200 $work = Trunc (AsciiFold $Workaround) 4000 $note = Trunc (AsciiFold $Note) 8000 $fix = Trunc (AsciiFold $Fix) 4000 $tags = @(); foreach($t in $Tags){ $tags += (Trunc (AsciiFold ([string]$t)) 100) } $seen = @(); foreach($s in $SeenIn){ $seen += (Trunc (AsciiFold ([string]$s)) 200) } # Entry (schéma KB) $slug = Slugify $title $tsId = (Get-Date -f yyyyMMddHHmmss) $id = ("bug-" + $slug + "-" + $tsId) $entry = [ordered]@{ id=$id title=$title blocking=[bool]$Blocking workaround=$work note=$note fix=$fix tags=@($tags) seen_in_threads=@($seen) last_seen=NowIso } $json = ($entry | ConvertTo-Json -Depth 5 -Compress) # Fichier cible $autoName = ("AUTO_{0}_{1}.txt" -f (Get-Date -f yyyyMMdd_HHmmss), $slug) $target = Join-Path $Inbox $autoName # PREVIEW if($Preview -or (-not $Execute)){ Write-Host "== PREVIEW :: BUG INTAKE GATE v1.0 ==" Write-Host ("Will write -> {0}" -f $target) Write-Host "Content(JSON entry):" Write-Host $json Write-Host "No write performed (Preview)." exit 0 } # EXECUTE: écrire l'AUTO et lancer la pipeline via wrapper NAS si disponible Write-SafeText -Target $target -Content $json -StageRoot $StageRoot Write-Host ("[OK] AUTO written -> {0}" -f $target) if(-not $NoRunPipeline){ $wrapNas = Join-Path (Join-Path $Root "scripts\wrappers") "gov_execute_nas.cmd" if(Test-Path -LiteralPath $wrapNas){ Write-Host ("[RUN] Pipeline via NAS wrapper: {0}" -f $wrapNas) & $wrapNas } else { # fallback local si NAS wrapper absent $wrapLoc = "C:\Temp_Gouvernance\gov_execute.cmd" if(Test-Path -LiteralPath $wrapLoc){ Write-Host ("[RUN] Pipeline via local wrapper: {0}" -f $wrapLoc) & $wrapLoc } else { Write-Host "[WARN] Aucun wrapper trouvé. AUTO déposé, mais pipeline non lancée." } } }