[CmdletBinding()] param( [ValidateSet("Build", "Probe", "Validate", "Ravnoves", "Status")] [string]$Mode = "Status", [ValidateSet("Ddrnet", "Ppliteseg")] [string]$Candidate = "Ddrnet", [string]$AssetRoot = "D:\NDC_MISSIONCORE\datasets\vegetation-v1\observed-2026-08-27", [string]$ToolRoot = "D:\NDC_MISSIONCORE\datasets\tooling\lab-v1-vegetation-goose", [string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\experiments\lab-v1-vegetation", [string]$RavnovesVideo = "D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4" ) Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $datasetPrefix = "D:\NDC_MISSIONCORE\datasets\" $runtimePrefix = "D:\NDC_MISSIONCORE\runtime\experiments\" if (-not $AssetRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) { throw "AssetRoot must stay under $datasetPrefix" } if (-not $ToolRoot.StartsWith($datasetPrefix, [StringComparison]::OrdinalIgnoreCase)) { throw "ToolRoot must stay under $datasetPrefix" } if (-not $OutputRoot.StartsWith($runtimePrefix, [StringComparison]::OrdinalIgnoreCase)) { throw "OutputRoot must stay under $runtimePrefix" } $image = "ndc/mission-core-lab-v1-goose:sg3.2.0-cu117-v1" $canonicalContainer = "ndc-mission-core-triton" $candidateKey = $Candidate.ToLowerInvariant() $contextRoot = Join-Path $ToolRoot "context" $configRoot = Join-Path $ToolRoot "config" $benchmarkConfig = Join-Path $configRoot "lab-v1-goose-vegetation-benchmark-v1.json" $policyConfig = Join-Path $configRoot "lab-v1-vegetation-mission-policy-v1.json" $providerMapConfig = Join-Path $configRoot "lab-v1-vegetation-provider-label-map-v1.json" $datasetRoot = Join-Path $AssetRoot "goose-2d\validation" $checkpointRelative = if ($candidateKey -eq "ddrnet") { "models\goose\ddrnet_class_512.pth" } else { "models\goose\ppliteseg_class_512.pth" } $checkpoint = Join-Path $AssetRoot $checkpointRelative $expectedCheckpointSha256 = if ($candidateKey -eq "ddrnet") { "b99c2838051bcd7b092fd3970aa62a77d5c0bbb809c9b9afb2ff4b0ebdaa4ee6" } else { "6dd412c0c99115e359896c4cab43a8e6bce9e09b843e7fa885fe597b0a6121cd" } $expectedCheckpointBytes = if ($candidateKey -eq "ddrnet") { 259419077 } else { 98208249 } $ravnovesSha256 = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8" $frameIndices = @(0, 253, 512, 768, 1024, 1536, 2048, 2560, 3072, 3584, 4096, 4488) $dockerConfig = "D:\NDC_MISSIONCORE\datasets\state\lab-v1-vegetation\docker-config" function Get-CanonicalTritonIdentity { $identity = & docker inspect $canonicalContainer --format "{{.Id}}|{{.Config.Image}}|{{.State.Status}}|{{if .State.Health}}{{.State.Health.Status}}{{end}}" if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($identity)) { throw "Canonical Triton is unavailable" } $parts = $identity.Split("|") if ($parts.Count -ne 4 -or $parts[2] -ne "running" -or $parts[3] -ne "healthy") { throw "Canonical Triton is not running and healthy: $identity" } return $identity } function Assert-FileIdentity { param([string]$Path, [long]$ExpectedBytes, [string]$ExpectedSha256) $file = Get-Item -LiteralPath $Path -ErrorAction SilentlyContinue if ($null -eq $file -or $file.Length -ne $ExpectedBytes) { throw "File identity changed: $Path" } $actualSha256 = (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant() if ($actualSha256 -ne $ExpectedSha256) { throw "File digest changed: $Path" } } function Assert-RunnerInputs { foreach ($path in @($benchmarkConfig, $policyConfig, $providerMapConfig)) { if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { throw "Runner config is unavailable: $path" } } if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "Dockerfile") -PathType Leaf)) { throw "Runner Dockerfile is unavailable" } if (-not (Test-Path -LiteralPath (Join-Path $contextRoot "run_goose_vegetation_benchmark.py") -PathType Leaf)) { throw "Runner source is unavailable" } Assert-FileIdentity -Path $checkpoint -ExpectedBytes $expectedCheckpointBytes -ExpectedSha256 $expectedCheckpointSha256 } function New-RunRoot { param([string]$Kind) $stamp = [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssfffZ") $path = Join-Path $OutputRoot ("{0}-{1}-{2}" -f $Kind, $candidateKey, $stamp) New-Item -ItemType Directory -Path $path | Out-Null return $path } function Invoke-IsolatedRun { param( [ValidateSet("goose", "ravnoves")][string]$RunMode, [string]$RunRoot, [int]$Limit, [string]$FramesRoot = "" ) $containerName = "ndc-lab-v1-goose-$candidateKey-$([Guid]::NewGuid().ToString('N').Substring(0, 10))" $arguments = @( "run", "--rm", "--name", $containerName, "--gpus", "all", "--network", "none", "--read-only", "--cap-drop", "ALL", "--security-opt", "no-new-privileges", "--memory", "10g", "--cpus", "8", "--pids-limit", "512", "--tmpfs", "/tmp:rw,noexec,nosuid,size=2g", "--env", "HOME=/tmp", "--mount", "type=bind,src=$datasetRoot,dst=/data/goose,readonly", "--mount", "type=bind,src=$checkpoint,dst=/models/candidate.pth,readonly", "--mount", "type=bind,src=$configRoot,dst=/config,readonly", "--mount", "type=bind,src=$RunRoot,dst=/output", $image, "--mode", $RunMode, "--candidate", $candidateKey, "--config", "/config/lab-v1-goose-vegetation-benchmark-v1.json", "--policy", "/config/lab-v1-vegetation-mission-policy-v1.json", "--provider-map", "/config/lab-v1-vegetation-provider-label-map-v1.json", "--checkpoint", "/models/candidate.pth", "--dataset-root", "/data/goose", "--output", "/output/result", "--limit", $Limit.ToString(), "--visual-count", "12" ) if ($RunMode -eq "ravnoves") { $arguments = @($arguments[0..($arguments.Count - 1)]) $arguments += @("--frames-root", "/input") $mountIndex = [Array]::IndexOf($arguments, $image) $head = @($arguments[0..($mountIndex - 1)]) $tail = @($arguments[$mountIndex..($arguments.Count - 1)]) $arguments = $head + @("--mount", "type=bind,src=$FramesRoot,dst=/input,readonly") + $tail } & docker @arguments if ($LASTEXITCODE -ne 0) { throw "LAB V1 container failed with exit code $LASTEXITCODE" } } function Export-RavnovesFrames { param([string]$Destination) Assert-FileIdentity -Path $RavnovesVideo -ExpectedBytes (Get-Item -LiteralPath $RavnovesVideo).Length -ExpectedSha256 $ravnovesSha256 New-Item -ItemType Directory -Path $Destination | Out-Null $expression = ($frameIndices | ForEach-Object { "eq(n\,$_ )" }) -join "+" $temporaryPattern = Join-Path $Destination "selected-%03d.png" & ffmpeg -hide_banner -loglevel error -i $RavnovesVideo -vf "select='$expression'" -fps_mode vfr $temporaryPattern if ($LASTEXITCODE -ne 0) { throw "RAVNOVES exact frame extraction failed" } $selected = @(Get-ChildItem -LiteralPath $Destination -Filter "selected-*.png" | Sort-Object Name) if ($selected.Count -ne $frameIndices.Count) { throw "RAVNOVES frame island changed: expected $($frameIndices.Count), got $($selected.Count)" } for ($index = 0; $index -lt $selected.Count; $index++) { $target = Join-Path $Destination ("frame-{0:D6}.png" -f $frameIndices[$index]) Move-Item -LiteralPath $selected[$index].FullName -Destination $target } } if ($Mode -eq "Status") { $imageIdentity = & docker image inspect $image --format "{{.Id}}" 2>$null [ordered]@{ schema_version = "missioncore.lab-v1-goose-runner-status/v1" observed_at_utc = [DateTime]::UtcNow.ToString("o") worker_id = "worker-006" image = $image image_id = if ($LASTEXITCODE -eq 0) { $imageIdentity } else { $null } canonical_triton = Get-CanonicalTritonIdentity asset_root = $AssetRoot output_root = $OutputRoot candidate = $candidateKey } | ConvertTo-Json -Depth 6 exit 0 } Assert-RunnerInputs $canonicalBefore = Get-CanonicalTritonIdentity try { if ($Mode -eq "Build") { if (-not (Test-Path -LiteralPath (Join-Path $dockerConfig "config.json") -PathType Leaf)) { throw "Isolated Docker client configuration is unavailable" } $previousDockerConfig = $env:DOCKER_CONFIG try { $env:DOCKER_CONFIG = $dockerConfig & docker build --pull=false --label "com.nodedc.component=mission-core-lab-v1-goose" --label "com.nodedc.authority=shadow-only" --tag $image $contextRoot if ($LASTEXITCODE -ne 0) { throw "LAB V1 image build failed with exit code $LASTEXITCODE" } } finally { $env:DOCKER_CONFIG = $previousDockerConfig } } elseif ($Mode -eq "Probe") { $runRoot = New-RunRoot -Kind "probe" Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 8 } elseif ($Mode -eq "Validate") { $runRoot = New-RunRoot -Kind "validation" Invoke-IsolatedRun -RunMode "goose" -RunRoot $runRoot -Limit 0 } elseif ($Mode -eq "Ravnoves") { $runRoot = New-RunRoot -Kind "ravnoves" $framesRoot = Join-Path $runRoot "input-frames" Export-RavnovesFrames -Destination $framesRoot Invoke-IsolatedRun -RunMode "ravnoves" -RunRoot $runRoot -Limit 0 -FramesRoot $framesRoot } } finally { $canonicalAfter = Get-CanonicalTritonIdentity if ($canonicalAfter -ne $canonicalBefore) { throw "Canonical Triton identity changed during LAB V1 work" } }