188 lines
8.7 KiB
PowerShell
188 lines
8.7 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ReleaseRoot,
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
|
|
[string]$RunId,
|
|
[string]$SourcePackPath = "D:\NDC_MISSIONCORE\runtime\derived\e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b\lidar-pack.npz",
|
|
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m49-tgs-full-shadow"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$ProgressPreference = "SilentlyContinue"
|
|
$TravelImageTag = "ndc/mission-core-m49-t3-travel:20260826"
|
|
$TravelImageId = "sha256:7b412020f4d8392d1d1ed1b33beadc44140f0ea8f781e62dd69796042334300f"
|
|
$ParityImageTag = "ndc-mission-core-m48t-upstream-parity:1.9.4-cu130"
|
|
$ParityImageId = "sha256:ceb13548617e4bd3f619766bfdff00af3fa5160946b367828da6d2233dcdcba0"
|
|
|
|
function Assert-LastExitCode([string]$Operation) {
|
|
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
|
|
}
|
|
|
|
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
|
|
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
|
|
$null = New-Item -ItemType Directory -Path $Path
|
|
}
|
|
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
|
|
if (
|
|
-not $item.PSIsContainer -or
|
|
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
|
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
|
|
) { throw "$Label must be a real D: directory" }
|
|
return $item.FullName
|
|
}
|
|
|
|
function Resolve-DFile([string]$Path, [string]$Label) {
|
|
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
|
|
if (
|
|
$item.PSIsContainer -or
|
|
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
|
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
|
|
) { throw "$Label must be a real D: file" }
|
|
return $item.FullName
|
|
}
|
|
|
|
function Convert-ToDockerPath([string]$Path) { return ($Path -replace "\\", "/") }
|
|
|
|
function Get-Container([string]$Name) {
|
|
$rows = @(((& docker inspect $Name) | ConvertFrom-Json))
|
|
Assert-LastExitCode "Docker inspection for $Name"
|
|
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
|
|
return $rows[0]
|
|
}
|
|
|
|
function Assert-Image([string]$Tag, [string]$ExpectedId) {
|
|
$rows = @(((& docker image inspect $Tag) | ConvertFrom-Json))
|
|
Assert-LastExitCode "Docker image inspection for $Tag"
|
|
if ($rows.Count -ne 1 -or [string]$rows[0].Id -cne $ExpectedId) {
|
|
throw "Pinned image identity changed for $Tag"
|
|
}
|
|
}
|
|
|
|
function Remove-ExactContainer([string]$Name) {
|
|
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$Name$") {
|
|
& docker rm --force $Name *> $null
|
|
}
|
|
}
|
|
|
|
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") { throw "M49 TGS full shadow is pinned to Worker 006" }
|
|
$release = Resolve-DDirectory $ReleaseRoot "M49 TGS full-shadow release" $false
|
|
$payload = Resolve-DDirectory (Join-Path $release "payload") "M49 TGS full-shadow payload" $false
|
|
$sourcePack = Resolve-DFile $SourcePackPath "RAVNOVES00 source pack"
|
|
$output = Resolve-DDirectory $OutputRoot "M49 TGS full-shadow output root" $true
|
|
$runCandidate = Join-Path $output $RunId
|
|
if (Test-Path -LiteralPath $runCandidate) { throw "M49 TGS full-shadow output already exists" }
|
|
$null = New-Item -ItemType Directory -Path $runCandidate
|
|
$runOutput = Resolve-DDirectory $runCandidate "M49 TGS full-shadow run output" $false
|
|
|
|
$releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json
|
|
if (
|
|
$releaseDocument.schema_version -cne "missioncore.m49-tgs-full-shadow-worker-release/v1" -or
|
|
$releaseDocument.worker_id -cne "worker-006" -or
|
|
$releaseDocument.candidate_id -cne "travel-tgs-full-shadow"
|
|
) { throw "M49 TGS full-shadow release contract changed" }
|
|
foreach ($property in $releaseDocument.files.PSObject.Properties) {
|
|
$path = Join-Path $payload $property.Name
|
|
$actual = (Get-FileHash -Algorithm SHA256 -LiteralPath $path).Hash.ToLowerInvariant()
|
|
if ($actual -cne [string]$property.Value.sha256) {
|
|
throw "M49 TGS full-shadow payload digest changed: $($property.Name)"
|
|
}
|
|
}
|
|
$sourcePackSha = (Get-FileHash -Algorithm SHA256 -LiteralPath $sourcePack).Hash.ToLowerInvariant()
|
|
if ($sourcePackSha -cne "0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944") {
|
|
throw "RAVNOVES00 source pack digest changed"
|
|
}
|
|
$os = Get-CimInstance Win32_OperatingSystem
|
|
$freeMemoryGiB = [double]$os.FreePhysicalMemory / 1MB
|
|
if ($freeMemoryGiB -lt 24.0) {
|
|
throw ("M49 TGS full shadow requires 24 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB)
|
|
}
|
|
$tritonBefore = Get-Container "ndc-mission-core-triton"
|
|
if (-not $tritonBefore.State.Running -or $tritonBefore.State.Health.Status -cne "healthy") {
|
|
throw "Canonical Mission Core Triton must remain healthy during M49 TGS full shadow"
|
|
}
|
|
Assert-Image $TravelImageTag $TravelImageId
|
|
Assert-Image $ParityImageTag $ParityImageId
|
|
|
|
$prepareName = "ndc-mission-core-m49-tgs-full-prepare-$RunId"
|
|
$runName = "ndc-mission-core-m49-tgs-full-run-$RunId"
|
|
$analyzeName = "ndc-mission-core-m49-tgs-full-analyze-$RunId"
|
|
foreach ($name in @($prepareName, $runName, $analyzeName)) {
|
|
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
|
|
throw "M49 TGS full-shadow container name already exists: $name"
|
|
}
|
|
}
|
|
|
|
$started = [DateTimeOffset]::UtcNow
|
|
try {
|
|
& docker run --rm --name $prepareName --network none --cpus 8 --memory 16g `
|
|
--entrypoint python3 `
|
|
--volume ((Convert-ToDockerPath $sourcePack) + ":/source/lidar-pack.npz:ro") `
|
|
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
|
|
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
|
|
$ParityImageTag /release/prepare_tgs_full_shadow_inputs.py `
|
|
--source-pack /source/lidar-pack.npz `
|
|
--config /release/m49-tgs-full-shadow-v1.json `
|
|
--output-root /tgs/inputs
|
|
Assert-LastExitCode "M49 TGS full-shadow input preparation"
|
|
|
|
& docker run --rm --name $runName --network none --cpus 16 --memory 24g `
|
|
--entrypoint /bin/bash `
|
|
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
|
|
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
|
|
$TravelImageTag /release/run_tgs_full_shadow.sh
|
|
Assert-LastExitCode "M49 source-paced TGS full-shadow run"
|
|
|
|
& docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g `
|
|
--entrypoint python3 `
|
|
--volume ((Convert-ToDockerPath $payload) + ":/release:ro") `
|
|
--volume ((Convert-ToDockerPath $runOutput) + ":/tgs") `
|
|
$ParityImageTag /release/build_tgs_full_shadow_evidence.py `
|
|
--run-root /tgs `
|
|
--config /release/m49-tgs-full-shadow-v1.json `
|
|
--output-root /tgs/evidence
|
|
Assert-LastExitCode "M49 TGS full-shadow evidence analysis"
|
|
} finally {
|
|
foreach ($name in @($prepareName, $runName, $analyzeName)) { Remove-ExactContainer $name }
|
|
}
|
|
$completed = [DateTimeOffset]::UtcNow
|
|
$resultPath = Join-Path $runOutput "evidence\result.json"
|
|
if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw "M49 TGS full-shadow result is missing" }
|
|
$result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json
|
|
if (
|
|
$result.timeline.frame_count -ne 4489 -or
|
|
$result.timeline.available_lidar_frame_count -ne 3928 -or
|
|
$result.timeline.missing_lidar_frame_count -ne 561 -or
|
|
$result.point_accounting.unaccounted -ne 0
|
|
) { throw "M49 TGS full-shadow structural acceptance failed" }
|
|
$tritonAfter = Get-Container "ndc-mission-core-triton"
|
|
if (
|
|
-not $tritonAfter.State.Running -or
|
|
$tritonAfter.State.Health.Status -cne "healthy" -or
|
|
[string]$tritonAfter.Id -cne [string]$tritonBefore.Id
|
|
) { throw "Canonical Mission Core Triton changed during M49 TGS full shadow" }
|
|
$summary = [ordered]@{
|
|
schema_version = "missioncore.m49-tgs-full-shadow-worker-summary/v1"
|
|
worker_id = "worker-006"
|
|
run_id = $RunId
|
|
code_revision = [string]$releaseDocument.code_revision
|
|
source_pack_sha256 = $sourcePackSha
|
|
travel_image_id = $TravelImageId
|
|
parity_image_id = $ParityImageId
|
|
started_utc = $started.ToString("o")
|
|
wall_seconds = [math]::Round(($completed - $started).TotalSeconds, 6)
|
|
free_memory_gib_before = [math]::Round($freeMemoryGiB, 6)
|
|
canonical_triton_id = [string]$tritonAfter.Id
|
|
canonical_triton_health = [string]$tritonAfter.State.Health.Status
|
|
result_status = [string]$result.status
|
|
all_timeline_frames_accounted = $true
|
|
all_eligible_points_accounted = $true
|
|
aos_used = $false
|
|
gpu_requested = $false
|
|
integrated_graph_performance_accepted = $false
|
|
navigation_or_actuation_allowed = $false
|
|
}
|
|
$summary | ConvertTo-Json -Depth 3 | Set-Content -LiteralPath (Join-Path $runOutput "worker-summary.json") -Encoding utf8
|
|
$summary | ConvertTo-Json -Depth 3
|