# gate_preamble_ps51_min.txt # PS 5.1 minimal gate snippet (ASCII, UTF-8 no BOM). Paste at the top of any write script. # Enforced: devlock valid, bootpack pointer match, PS 5.1, no write if gate fails. $ErrorActionPreference = "Stop" if ($PSVersionTable.PSVersion.Major -ne 5) { Write-Host "[BLOCK] PS version must be 5.1"; exit 1 } $root = "\\DS-918\chatgpt\ChatGPT-Gouvernance-Projets\_registry" # Devlock $dev = Join-Path $root "dev\devlock_state.json" if (!(Test-Path -LiteralPath $dev)) { Write-Host "[BLOCK] devlock missing"; exit 2 } $encNoBom = New-Object System.Text.UTF8Encoding($false) $rawDev = [IO.File]::ReadAllText($dev, $encNoBom) $devObj = $null try { $devObj = $rawDev | ConvertFrom-Json } catch { $devObj = $null } if ($devObj -eq $null) { Write-Host "[BLOCK] devlock invalid json"; exit 3 } $exp = $null try { $exp = [DateTime]::Parse($devObj.expires_utc) } catch { $exp = $null } if ($exp -eq $null) { Write-Host "[BLOCK] devlock missing expires_utc"; exit 4 } if ([DateTime]::UtcNow -ge $exp) { Write-Host "[BLOCK] devlock expired"; exit 5 } # Bootpack pointer $boot = Join-Path $root "bootpack\bootpack.txt" if (!(Test-Path -LiteralPath $boot)) { Write-Host "[BLOCK] bootpack.txt missing"; exit 6 } $rawBoot = [IO.File]::ReadAllText($boot, $encNoBom) if ($rawBoot -notmatch "\[BUG_KB_JSON\]") { Write-Host "[BLOCK] BUG_KB_JSON missing"; exit 7 } $mx = [regex]::Match($rawBoot, "(?ms)^\[BUG_KB_JSON_POINTER\]\s*(.*?)(?=^\[|\Z)") if (-not $mx.Success) { Write-Host "[BLOCK] pointer block missing"; exit 8 } $blk = $mx.Groups[1].Value $kbPath = [regex]::Match($blk, "(?mi)^\s*Path\s*:\s*(.+)$").Groups[1].Value.Trim() $shaPtr = [regex]::Match($blk, "(?mi)^\s*SHA256\s*:\s*([0-9A-Fa-f]+)$").Groups[1].Value.Trim().ToLower() $entPtr = [regex]::Match($blk, "(?mi)^\s*Entries\s*:\s*([0-9]+)$").Groups[1].Value.Trim() if ([string]::IsNullOrWhiteSpace($kbPath) -or -not (Test-Path -LiteralPath $kbPath)) { Write-Host "[BLOCK] KB path invalid"; exit 9 } $shaReal = (Get-FileHash -LiteralPath $kbPath -Algorithm SHA256).Hash.ToLower() $bytes = [IO.File]::ReadAllBytes($kbPath) $txt = $encNoBom.GetString($bytes) if ($txt.Length -gt 0) { if ([int]$txt[0] -eq 65279) { $txt = $txt.Substring(1) } } $txt2 = $txt $i = $txt.LastIndexOf("]}") if ($i -gt 0) { $txt2 = $txt.Substring(0, $i + 2) } $entReal = -1 try { $entReal = (($txt2 | ConvertFrom-Json).entries).Count } catch { $entReal = ([regex]::Matches($txt2, '"id"\s*:')).Count } if ($shaPtr -ne "" -and $shaPtr -ne $shaReal) { Write-Host "[BLOCK] kb_sha_mismatch"; exit 10 } if ([string]$entPtr -ne "" -and [string]$entPtr -ne [string]$entReal) { Write-Host "[BLOCK] kb_entries_mismatch"; exit 11 } # From here, enforce SAFE-CREATE pattern only: # - Build content locally under C:\Temp_Gouvernance # - Copy to __tmp_* on NAS # - Move-Item -Force to final # - Write .bak_YYYYMMDD_HHMMSS before overwrite # - Verify SHA256 local/tmp/final # End of gate snippet.