[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-t3-travel-ravnoves" ) $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 T3 RAVNOVES probe is pinned to Worker 006" } $release = Resolve-DDirectory $ReleaseRoot "M49 T3 probe release" $false $payload = Resolve-DDirectory (Join-Path $release "payload") "M49 T3 probe payload" $false $sourcePack = Resolve-DFile $SourcePackPath "RAVNOVES00 source pack" $output = Resolve-DDirectory $OutputRoot "M49 T3 probe output root" $true $runOutputCandidate = Join-Path $output $RunId if (Test-Path -LiteralPath $runOutputCandidate) { throw "M49 T3 probe output already exists" } $null = New-Item -ItemType Directory -Path $runOutputCandidate $runOutput = Resolve-DDirectory $runOutputCandidate "M49 T3 probe run output" $false $releaseDocument = Get-Content -LiteralPath (Join-Path $payload "release.json") -Raw | ConvertFrom-Json if ( $releaseDocument.schema_version -cne "missioncore.m49-t3-ravnoves-worker-release/v1" -or $releaseDocument.worker_id -cne "worker-006" -or $releaseDocument.candidate_id -cne "travel" ) { throw "M49 T3 probe 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 T3 probe 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 16.0) { throw ("M49 T3 probe requires 16 GiB free memory; observed {0:N2} GiB" -f $freeMemoryGiB) } $canonicalTritonBefore = Get-Container "ndc-mission-core-triton" if ( -not $canonicalTritonBefore.State.Running -or $canonicalTritonBefore.State.Health.Status -cne "healthy" ) { throw "Canonical Mission Core Triton must remain healthy during M49 T3 probe" } Assert-Image $TravelImageTag $TravelImageId Assert-Image $ParityImageTag $ParityImageId $prepareName = "ndc-mission-core-m49-t3-prepare-$RunId" $travelName = "ndc-mission-core-m49-t3-probe-$RunId" $analyzeName = "ndc-mission-core-m49-t3-analyze-$RunId" foreach ($name in @($prepareName, $travelName, $analyzeName)) { if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") { throw "M49 T3 probe 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) + ":/probe") ` $ParityImageTag /release/prepare_ravnoves_probe.py ` --source-pack /source/lidar-pack.npz ` --config /release/m49-travel-ravnoves-compatibility-v1.json ` --output-root /probe/inputs Assert-LastExitCode "M49 T3 RAVNOVES input preparation" & docker run --rm --name $travelName --network none --cpus 16 --memory 24g ` --entrypoint /bin/bash ` --volume ((Convert-ToDockerPath $payload) + ":/release:ro") ` --volume ((Convert-ToDockerPath $runOutput) + ":/probe") ` $TravelImageTag /release/run_ravnoves_probe.sh Assert-LastExitCode "M49 T3 RAVNOVES TRAVEL probe" & docker run --rm --name $analyzeName --network none --cpus 8 --memory 16g ` --entrypoint python3 ` --volume ((Convert-ToDockerPath $payload) + ":/release:ro") ` --volume ((Convert-ToDockerPath $runOutput) + ":/probe") ` $ParityImageTag /release/analyze_ravnoves_probe.py ` --probe-root /probe ` --config /release/m49-travel-ravnoves-compatibility-v1.json Assert-LastExitCode "M49 T3 RAVNOVES result analysis" } finally { foreach ($name in @($prepareName, $travelName, $analyzeName)) { Remove-ExactContainer $name } } $completed = [DateTimeOffset]::UtcNow $resultPath = Join-Path $runOutput "result.json" if (-not (Test-Path -LiteralPath $resultPath -PathType Leaf)) { throw "M49 T3 RAVNOVES result.json is missing" } $result = Get-Content -LiteralPath $resultPath -Raw | ConvertFrom-Json if ($result.status -cne "passed") { throw "M49 T3 RAVNOVES probe did not complete" } $canonicalTritonAfter = Get-Container "ndc-mission-core-triton" if ( -not $canonicalTritonAfter.State.Running -or $canonicalTritonAfter.State.Health.Status -cne "healthy" -or [string]$canonicalTritonAfter.Id -cne [string]$canonicalTritonBefore.Id ) { throw "Canonical Mission Core Triton changed during M49 T3 probe" } $summary = [ordered]@{ schema_version = "missioncore.m49-t3-ravnoves-worker-summary/v1" worker_id = "worker-006" run_id = $RunId code_revision = [string]$releaseDocument.code_revision travel_image_id = $TravelImageId parity_image_id = $ParityImageId source_pack_sha256 = $sourcePackSha 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]$canonicalTritonAfter.Id canonical_triton_health = [string]$canonicalTritonAfter.State.Health.Status candidate_run_count = [int]$result.candidate_run_count direct_upstream_profile_compatible = [bool]$result.summary.direct_upstream_profile_compatible ravnoves00_visual_quality_accepted = $false realtime_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