[CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$Candidate, [Parameter(Mandatory = $true)] [string]$ExpectedPredecessorSha256, [Parameter(Mandatory = $true)] [string]$ExpectedCandidateSha256, [string]$RunnerRoot = "D:\NDC_MISSIONCORE\runtime\derived\e23-runner-20260724-002" ) $ErrorActionPreference = "Stop" $target = Join-Path $RunnerRoot "Invoke-E15PersistentShadowRun.ps1" $expectedPredecessor = $ExpectedPredecessorSha256.ToLowerInvariant() $expectedCandidate = $ExpectedCandidateSha256.ToLowerInvariant() $predecessor = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() if ($predecessor -ne $expectedPredecessor) { throw "Persistent launcher predecessor digest changed" } if ( (Get-FileHash -Algorithm SHA256 -LiteralPath $Candidate).Hash.ToLowerInvariant() ` -ne $expectedCandidate ) { throw "Persistent launcher candidate digest changed" } $backup = Join-Path $RunnerRoot ( "Invoke-E15PersistentShadowRun.ps1.rollback-" + [DateTime]::UtcNow.ToString("yyyyMMddTHHmmssZ") + "-" + $predecessor.Substring(0, 12) ) Copy-Item -LiteralPath $target -Destination $backup try { Copy-Item -LiteralPath $Candidate -Destination $target -Force $installed = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() if ($installed -ne $expectedCandidate) { throw "Persistent launcher replacement digest changed" } $tokens = $null $parseErrors = $null [Management.Automation.Language.Parser]::ParseFile( $target, [ref]$tokens, [ref]$parseErrors ) | Out-Null if (@($parseErrors).Count -ne 0) { throw "Persistent launcher candidate has PowerShell parse errors" } } catch { Copy-Item -LiteralPath $backup -Destination $target -Force throw } [ordered]@{ SchemaVersion = "missioncore.worker-persistent-launcher-deploy-result/v1" PredecessorSha256 = $predecessor CandidateSha256 = ( Get-FileHash -Algorithm SHA256 -LiteralPath $target ).Hash.ToLowerInvariant() Backup = $backup } | ConvertTo-Json -Compress