d731800954
Co-authored-by: Claude Code <kjh2064@gmail.com> Co-committed-by: Claude Code <kjh2064@gmail.com>
233 lines
8.7 KiB
PowerShell
233 lines
8.7 KiB
PowerShell
#!/usr/bin/env pwsh
|
|
<#
|
|
.SYNOPSIS
|
|
Freeze an approved model/dataset VersionSet for Phase 1 shadow run.
|
|
|
|
.DESCRIPTION
|
|
Parameterized tool to INSERT approved model_id + dataset_id into:
|
|
- governance.model_version_registry (FROZEN status)
|
|
- evaluation.dataset_manifest (FROZEN status)
|
|
|
|
NO default values; all parameters REQUIRED. Fails immediately if any parameter is missing.
|
|
|
|
.PARAMETER ModelId
|
|
UUID of the approved model (e.g., "00000000-0000-0000-0000-000000000001")
|
|
Required. No default.
|
|
|
|
.PARAMETER DatasetId
|
|
UUID of the approved dataset (e.g., "00000000-0000-0000-0000-000000000002")
|
|
Required. No default.
|
|
|
|
.PARAMETER ApprovedBy
|
|
Email/ID of the approver (e.g., "kjh2064@gmail.com")
|
|
Required. No default.
|
|
|
|
.PARAMETER ConfigVersion
|
|
Configuration version string (e.g., "v1.0.0")
|
|
Required. No default.
|
|
|
|
.PARAMETER CodeSha
|
|
Git commit SHA (e.g., "acaa731b3f")
|
|
Required. No default.
|
|
|
|
.PARAMETER ConnectionString
|
|
PostgreSQL connection string.
|
|
Default: $env:KARTSELL_POSTGRES
|
|
|
|
.EXAMPLE
|
|
# Freeze a versionset (all parameters required)
|
|
.\freeze-versionset.ps1 `
|
|
-ModelId "00000000-0000-0000-0000-000000000001" `
|
|
-DatasetId "00000000-0000-0000-0000-000000000002" `
|
|
-ApprovedBy "kjh2064@gmail.com" `
|
|
-ConfigVersion "v1.0.0" `
|
|
-CodeSha "acaa731b3f"
|
|
|
|
.EXAMPLE
|
|
# Will fail: missing -ConfigVersion
|
|
.\freeze-versionset.ps1 `
|
|
-ModelId "00000000-0000-0000-0000-000000000001" `
|
|
-DatasetId "00000000-0000-0000-0000-000000000002" `
|
|
-ApprovedBy "kjh2064@gmail.com" `
|
|
-CodeSha "acaa731b3f"
|
|
# Error: Cannot bind argument to parameter 'ConfigVersion' because it is an empty string.
|
|
#>
|
|
|
|
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory, HelpMessage = "Model UUID (e.g., 00000000-0000-0000-0000-000000000001)")]
|
|
[ValidateScript({ $_ -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' })]
|
|
[string]$ModelId,
|
|
|
|
[Parameter(Mandatory, HelpMessage = "Dataset UUID")]
|
|
[ValidateScript({ $_ -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' })]
|
|
[string]$DatasetId,
|
|
|
|
[Parameter(Mandatory, HelpMessage = "Approver email/ID (e.g., kjh2064@gmail.com)")]
|
|
[ValidateScript({ $_ -match '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' })]
|
|
[string]$ApprovedBy,
|
|
|
|
[Parameter(Mandatory, HelpMessage = "Config version (e.g., v1.0.0)")]
|
|
[ValidateScript({ $_ -match '^v[0-9]+\.[0-9]+\.[0-9]+' })]
|
|
[string]$ConfigVersion,
|
|
|
|
[Parameter(Mandatory, HelpMessage = "Git commit SHA (at least 10 chars)")]
|
|
[ValidateScript({ $_.Length -ge 10 })]
|
|
[string]$CodeSha,
|
|
|
|
[string]$ConnectionString = $env:KARTSELL_POSTGRES
|
|
)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
|
|
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
|
Write-Host "Phase 1: Freeze VersionSet" -ForegroundColor Cyan
|
|
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
|
|
|
# Validate connection string
|
|
if (-not $ConnectionString) {
|
|
Write-Error "ConnectionString not provided and `$env:KARTSELL_POSTGRES not set. Aborting."
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "`n[1/3] PRE-FLIGHT CHECK"
|
|
Write-Host " Model ID: $ModelId"
|
|
Write-Host " Dataset ID: $DatasetId"
|
|
Write-Host " Approved By: $ApprovedBy"
|
|
Write-Host " Config Version: $ConfigVersion"
|
|
Write-Host " Code SHA: $CodeSha"
|
|
Write-Host " Connection: $(($ConnectionString -split 'Password=')[0])***"
|
|
|
|
# Verify 0032 migration is deployed
|
|
Write-Host "`n[2/3] VERIFY Migration 0032 deployed..."
|
|
try {
|
|
$conn = New-Object System.Data.NpgsqlClient.NpgsqlConnection($ConnectionString)
|
|
$conn.Open()
|
|
|
|
$cmd = $conn.CreateCommand()
|
|
$cmd.CommandText = @"
|
|
SELECT schema_version FROM schema_version_history
|
|
WHERE script_name = '0032_shadow_run_queued_status_contract.sql'
|
|
LIMIT 1
|
|
"@
|
|
$result = $cmd.ExecuteScalar()
|
|
|
|
if ($null -eq $result) {
|
|
throw "Migration 0032 NOT FOUND. Run DbMigrator first."
|
|
}
|
|
|
|
Write-Host " ✅ Migration 0032 deployed (schema_version: $result)"
|
|
$conn.Close()
|
|
}
|
|
catch {
|
|
Write-Error " ❌ Pre-flight failed: $_`n`nCorrective: Run DbMigrator to deploy 0032_*.sql before freezing."
|
|
exit 1
|
|
}
|
|
|
|
# Insert into governance.model_version_registry
|
|
Write-Host "`n[3/3] FREEZE VersionSet..."
|
|
|
|
try {
|
|
$conn = New-Object System.Data.NpgsqlClient.NpgsqlConnection($ConnectionString)
|
|
$conn.Open()
|
|
|
|
$correlationId = [System.Guid]::NewGuid()
|
|
$now = [System.DateTime]::UtcNow
|
|
|
|
$cmd = $conn.CreateCommand()
|
|
$cmd.CommandText = @"
|
|
INSERT INTO governance.model_version_registry (
|
|
id, model_id, dataset_id, status, approved_by, config_version, code_sha,
|
|
effective_at, published_at, revision, correlation_id
|
|
) VALUES (
|
|
@id, @model_id, @dataset_id, 'FROZEN', @approved_by, @config_version, @code_sha,
|
|
@effective_at, @published_at, 1, @correlation_id
|
|
)
|
|
ON CONFLICT (model_id, dataset_id) DO UPDATE SET
|
|
status = 'FROZEN',
|
|
approved_by = EXCLUDED.approved_by,
|
|
config_version = EXCLUDED.config_version,
|
|
code_sha = EXCLUDED.code_sha,
|
|
effective_at = EXCLUDED.effective_at,
|
|
revision = governance.model_version_registry.revision + 1,
|
|
published_at = EXCLUDED.published_at
|
|
RETURNING id, model_id, dataset_id, status, effective_at
|
|
"@
|
|
|
|
$cmd.Parameters.AddWithValue("@id", [System.Guid]::NewGuid()) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@model_id", [System.Guid]$ModelId) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@dataset_id", [System.Guid]$DatasetId) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@approved_by", $ApprovedBy) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@config_version", $ConfigVersion) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@code_sha", $CodeSha) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@effective_at", $now) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@published_at", $now) | Out-Null
|
|
$cmd.Parameters.AddWithValue("@correlation_id", $correlationId) | Out-Null
|
|
|
|
$reader = $cmd.ExecuteReader()
|
|
if ($reader.Read()) {
|
|
$insertedId = $reader['id']
|
|
$insertedModelId = $reader['model_id']
|
|
$insertedDatasetId = $reader['dataset_id']
|
|
$insertedStatus = $reader['status']
|
|
|
|
Write-Host " ✅ Inserted governance.model_version_registry:"
|
|
Write-Host " - ID: $insertedId"
|
|
Write-Host " - Model: $insertedModelId"
|
|
Write-Host " - Dataset: $insertedDatasetId"
|
|
Write-Host " - Status: $insertedStatus"
|
|
}
|
|
$reader.Close()
|
|
|
|
# Update evaluation.dataset_manifest
|
|
$cmd2 = $conn.CreateCommand()
|
|
$cmd2.CommandText = @"
|
|
INSERT INTO evaluation.dataset_manifest (
|
|
id, dataset_id, model_id, status, freeze_reason,
|
|
published_at, revision, correlation_id
|
|
) VALUES (
|
|
@id, @dataset_id, @model_id, 'FROZEN', 'Phase 1 VersionSet freeze',
|
|
@published_at, 1, @correlation_id
|
|
)
|
|
ON CONFLICT (dataset_id, model_id) DO UPDATE SET
|
|
status = 'FROZEN',
|
|
freeze_reason = 'Phase 1 VersionSet freeze',
|
|
revision = evaluation.dataset_manifest.revision + 1,
|
|
published_at = EXCLUDED.published_at
|
|
RETURNING id, dataset_id, model_id, status
|
|
"@
|
|
|
|
$cmd2.Parameters.AddWithValue("@id", [System.Guid]::NewGuid()) | Out-Null
|
|
$cmd2.Parameters.AddWithValue("@dataset_id", [System.Guid]$DatasetId) | Out-Null
|
|
$cmd2.Parameters.AddWithValue("@model_id", [System.Guid]$ModelId) | Out-Null
|
|
$cmd2.Parameters.AddWithValue("@published_at", $now) | Out-Null
|
|
$cmd2.Parameters.AddWithValue("@correlation_id", $correlationId) | Out-Null
|
|
|
|
$reader2 = $cmd2.ExecuteReader()
|
|
if ($reader2.Read()) {
|
|
$mId = $reader2['id']
|
|
$mDatasetId = $reader2['dataset_id']
|
|
$mModelId = $reader2['model_id']
|
|
$mStatus = $reader2['status']
|
|
|
|
Write-Host " ✅ Inserted evaluation.dataset_manifest:"
|
|
Write-Host " - ID: $mId"
|
|
Write-Host " - Dataset: $mDatasetId"
|
|
Write-Host " - Model: $mModelId"
|
|
Write-Host " - Status: $mStatus"
|
|
}
|
|
$reader2.Close()
|
|
|
|
$conn.Close()
|
|
|
|
Write-Host "`n✅ VersionSet FROZEN successfully"
|
|
Write-Host " Correlation ID: $correlationId"
|
|
Write-Host " Next: Run generate-shadow-run-identifiers.ps1 to create RunId/JobId"
|
|
}
|
|
catch {
|
|
Write-Error " ❌ Failed to freeze VersionSet: $_"
|
|
exit 1
|
|
}
|
|
|
|
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|