param( [ValidatePattern('^P1-trace-[0-9]{3}$')][string]$Name='P1-trace-001', [string]$BuildDirectory='.starlet_diag_p1', [string]$RuntimeDirectory='.starlet_msvc2', [Parameter(Mandatory=$true)][string]$PythonExecutable ) # Create an independent, verified pre-update NAND and frozen diagnostic build. # Never write to the user's normal NAND, the seed, or an existing experiment. $ErrorActionPreference = 'Stop' $p1Repo = [IO.Path]::GetFullPath((Join-Path $PSScriptRoot '../..')) $p1Root = Join-Path $p1Repo '.starlet_update_ab_20260913' $p1Profile = Join-Path $p1Root $Name $p1Build = Join-Path $p1Root "build-$Name" $p1SourceBuild = [IO.Path]::GetFullPath((Join-Path $p1Repo $BuildDirectory)) $p1RuntimeBuild = [IO.Path]::GetFullPath((Join-Path $p1Repo $RuntimeDirectory)) $p1SourceExe = Join-Path $p1SourceBuild 'Binaries/DolphinNoGUI.exe' $p1SourcePDB = Join-Path $p1SourceBuild 'pdb/DolphinNoGUI.pdb' $p1Seed = Join-Path $p1Root 'pre-update.nand-journal' $p1JournalRelative = 'Starlet\0238188DE97894AE3AF93A92C4F238C0572731A7.nand-journal' $p1Manifest = Get-Content -LiteralPath (Join-Path $p1Root 'manifest.json') -Raw | ConvertFrom-Json $p1Expected = Get-Content -LiteralPath (Join-Path $p1Root 'initial-files-B.json') -Raw | ConvertFrom-Json $p1Python = (Resolve-Path -LiteralPath $PythonExecutable).Path if (Get-Process DolphinNoGUI,Dolphin -ErrorAction SilentlyContinue) { throw 'Close the existing Dolphin instance before preparing a native-update run.' } foreach ($p1Target in @($p1Profile, $p1Build)) { if (Test-Path -LiteralPath $p1Target) { throw "Refusing to overwrite $p1Target" } } foreach ($p1Required in @($p1Seed, $p1SourceExe, $p1SourcePDB, (Join-Path $p1RuntimeBuild 'Binaries/Sys'), (Join-Path $p1SourceBuild 'Binaries/Network'))) { if (-not (Test-Path -LiteralPath $p1Required)) { throw "Missing input: $p1Required" } } if ((Get-FileHash -LiteralPath $p1Seed -Algorithm SHA256).Hash -ne $p1Manifest.SeedSHA256) { throw 'The preserved pre-update NAND journal differs from its manifest.' } $p1InputBytes = ($p1Expected | Measure-Object -Property Bytes -Sum).Sum $p1InputBytes += (Get-Item -LiteralPath $p1SourceExe).Length + (Get-Item -LiteralPath $p1SourcePDB).Length foreach ($p1Folder in @('Sys','Network')) { $p1FolderSource = if ($p1Folder -eq 'Sys') { $p1RuntimeBuild } else { $p1SourceBuild } $p1InputBytes += (Get-ChildItem -LiteralPath (Join-Path $p1FolderSource "Binaries/$p1Folder") ` -File -Recurse | Measure-Object -Property Length -Sum).Sum } if ((Get-PSDrive C).Free -lt ($p1InputBytes + 5GB)) { throw 'Insufficient free space for copies, update growth, and a 5 GiB reserve.' } $p1Copies = foreach ($p1File in $p1Expected) { $p1Target = [IO.Path]::GetFullPath((Join-Path $p1Profile $p1File.Relative)) if (-not $p1Target.StartsWith($p1Profile + '\', [StringComparison]::OrdinalIgnoreCase)) { throw 'Invalid relative path in the initial-file manifest.' } $p1Sources = if ($p1File.Relative -eq $p1JournalRelative) { @($p1Seed) } else { @((Join-Path (Join-Path $p1Root 'B') $p1File.Relative), (Join-Path (Join-Path $p1Repo '.starlet_formatter_test_20260913') $p1File.Relative)) } $p1Selected = $null foreach ($p1Source in $p1Sources) { if ((Test-Path -LiteralPath $p1Source -PathType Leaf) -and (Get-FileHash -LiteralPath $p1Source -Algorithm SHA256).Hash -eq $p1File.SHA256) { $p1Selected = $p1Source break } } if (-not $p1Selected) { throw "No byte-identical initial source for $($p1File.Relative)" } [pscustomobject]@{ Relative=$p1File.Relative; Source=$p1Selected; Target=$p1Target; SHA256=$p1File.SHA256 } } New-Item -ItemType Directory -Path $p1Profile,(Join-Path $p1Build 'Binaries'), (Join-Path $p1Build 'pdb') | Out-Null foreach ($p1Copy in $p1Copies) { New-Item -ItemType Directory -Force -Path (Split-Path $p1Copy.Target -Parent) | Out-Null Copy-Item -LiteralPath $p1Copy.Source -Destination $p1Copy.Target if ((Get-FileHash -LiteralPath $p1Copy.Target -Algorithm SHA256).Hash -ne $p1Copy.SHA256) { throw "Copied input verification failed: $($p1Copy.Relative)" } } foreach ($p1Folder in @('Sys','Network')) { $p1FolderSource = if ($p1Folder -eq 'Sys') { $p1RuntimeBuild } else { $p1SourceBuild } Copy-Item -LiteralPath (Join-Path $p1FolderSource "Binaries/$p1Folder") ` -Destination (Join-Path $p1Build 'Binaries') -Recurse } Copy-Item -LiteralPath $p1SourceExe -Destination (Join-Path $p1Build 'Binaries/DolphinNoGUI.exe') Copy-Item -LiteralPath $p1SourcePDB -Destination (Join-Path $p1Build 'pdb/DolphinNoGUI.pdb') foreach ($p1Folder in @('Logs','Perf','Capture')) { New-Item -ItemType Directory -Force -Path (Join-Path $p1Profile $p1Folder) | Out-Null } $p1ConfigPath = Join-Path $p1Profile 'Config/Dolphin.ini' $p1OriginalConfig = [IO.File]::ReadAllText($p1ConfigPath) $p1OldPrefix = (Join-Path $p1Root 'B').Replace('\','/') + '/' $p1NewPrefix = $p1Profile.Replace('\','/') + '/' if (-not $p1OriginalConfig.Contains($p1OldPrefix)) { throw 'The source configuration has no expected baseline profile path.' } $p1NewConfig = $p1OriginalConfig.Replace($p1OldPrefix, $p1NewPrefix) if ($p1NewConfig.Replace($p1NewPrefix, $p1OldPrefix) -cne $p1OriginalConfig) { throw 'The isolated configuration differs beyond its profile paths.' } [IO.File]::WriteAllText($p1ConfigPath, $p1NewConfig, [Text.UTF8Encoding]::new($false)) & $p1Python -B (Join-Path $p1Root '../.starlet-profile/VerifyJournalCheckpoint.py') ` (Join-Path $p1Profile $p1JournalRelative) $p1Manifest.PrefixBytes ` > (Join-Path $p1Profile 'Capture/initial-journal-verification.txt') 2>&1 if ($LASTEXITCODE) { throw 'Preserved NAND checkpoint verification failed; no emulator was launched.' } [pscustomobject]@{ CreatedUTC=[datetime]::UtcNow.ToString('o'); Profile=$p1Profile; Build=$p1Build InitialFiles=$p1Copies.Count; SeedSHA256=$p1Manifest.SeedSHA256 ExeSHA256=(Get-FileHash -LiteralPath (Join-Path $p1Build 'Binaries/DolphinNoGUI.exe')).Hash PdbSHA256=(Get-FileHash -LiteralPath (Join-Path $p1Build 'pdb/DolphinNoGUI.pdb')).Hash SourceBuild=$p1SourceBuild; RuntimeBuild=$p1RuntimeBuild PerformanceTraceRequestedAtLaunch=$false } | ConvertTo-Json -Depth 4 | Out-File -LiteralPath (Join-Path $p1Profile 'Capture/prepare-manifest.json') -Encoding utf8 Write-Output "Prepared isolated profile $p1Profile and frozen build $p1Build. No Dolphin instance was launched."