#Requires -Version 7.0 param([Parameter(Mandatory=$true)][ValidateSet('list','focused','all','clippy','build')][string]$Stage) Set-StrictMode -Version Latest $ErrorActionPreference='Stop' $PSNativeCommandUseErrorActionPreference=$false $taskWorkspace='C:\Users\User\Documents\Git\ARCADIA-Studio\.local\scenario-spacing-ytr83r-20260913' $taskTarget='C:\Users\User\Documents\Git\ARCADIA-Studio\target' $taskPin='b10b28cefd1d0794073ba241a0c8de9a83e85f68' if($taskPin -notmatch '^[0-9a-f]{40}$'){throw 'Final reviewed source pin and captures are not prepared; do not run this template.'} $toolchainEnv='C:\Users\User\Documents\Git\ARCADIA-Studio\.local\toolchain\env.ps1' function Hash-Input([string]$Path){(Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()} function Same-Names($Left,$Right){$a=@($Left|Sort-Object);$b=@($Right|Sort-Object);return $a.Count -eq $b.Count -and @(Compare-Object $a $b).Count -eq 0} $taskLog=Join-Path $PSScriptRoot ($Stage+'.log') $taskReceipt=Join-Path $PSScriptRoot ($Stage+'-receipt.json') if((Test-Path -LiteralPath $taskLog)-or(Test-Path -LiteralPath $taskReceipt)){throw 'Do not replace evidence; prepare a new gate directory for retries.'} $cache=Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot 'cache-policy.json')|ConvertFrom-Json if($cache.base -cne $taskPin -or -not $cache.retainExistingTarget -or $cache.cleanRequested){throw 'Wrong reviewed cache policy.'} . (Join-Path $PSScriptRoot 'binary-inputs.ps1') . (Join-Path $PSScriptRoot 'batch-helpers.ps1') $batchPlan=Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot 'batches.json')|ConvertFrom-Json $selection=Get-Content -Raw -LiteralPath (Join-Path $PSScriptRoot 'expected-tests.json')|ConvertFrom-Json $expected=@($selection.tests|Sort-Object -Unique) if($selection.source_commit -cne $taskPin -or $selection.expected_desktop_count -ne 1095 -or @($selection.desktop_tests).Count -ne 1095 -or @($selection.desktop_tests|Sort-Object -Unique).Count -ne 1095 -or $selection.expected_count -ne 301 -or $expected.Count -ne 301 -or @($selection.tests).Count -ne 301){throw 'Wrong exact focused membership.'} $expectedCount=301 $commonFiles=@('run.ps1','capture.ps1','binary-inputs.ps1','batch-helpers.ps1','batches.json','expected-tests.json','expected-inputs.json','enumeration.json','external-inputs.json','inputs-complete-before.json','cache-policy.json','source-provenance.json','README.md','verify-preparation.ps1','preparation-verification.json','strict-preflight.ps1','strict-preflight-receipt.json','strict-preflight.log','inputs-complete-strict-preflight-before.json','inputs-complete-strict-preflight-after.json','toolchain-versions.json') $boundScripts=@{} foreach($file in $commonFiles){$boundScripts[$file]=Hash-Input (Join-Path $PSScriptRoot $file)} $previous=@{focused='list';all='focused';clippy='all';build='clippy'} $preflightPath=Join-Path $PSScriptRoot 'strict-preflight-receipt.json' $preflight=Get-Content -Raw -LiteralPath $preflightPath|ConvertFrom-Json if($preflight.stage -cne 'strict-preflight' -or $preflight.base -cne $taskPin -or $preflight.workspace -ine $taskWorkspace -or $preflight.target -ine $taskTarget -or $preflight.cargoExitCode -ne 0 -or -not $preflight.passed -or -not $preflight.inputsUnchanged -or $null -ne $preflight.postflightFailure){throw 'Strict preflight has not passed for this exact source'} foreach($property in $preflight.boundScriptAndInputHashes.PSObject.Properties){if((Hash-Input (Join-Path $PSScriptRoot $property.Name)) -cne $property.Value){throw ('Preflight transitive binding changed: '+$property.Name)}} if((Hash-Input (Join-Path $PSScriptRoot 'strict-preflight.log')) -cne $preflight.rawLogSha256 -or (Hash-Input (Join-Path $PSScriptRoot 'inputs-complete-strict-preflight-before.json')) -cne $preflight.beforeCaptureSha256 -or (Hash-Input (Join-Path $PSScriptRoot 'inputs-complete-strict-preflight-after.json')) -cne $preflight.afterCaptureSha256 -or (Hash-Input (Join-Path $PSScriptRoot 'toolchain-versions.json')) -cne $preflight.toolchainVersionsSha256){throw 'Preflight output/capture binding changed'} $priorBinding=if($Stage -eq 'list'){[ordered]@{stage='strict-preflight';receiptSha256=(Hash-Input $preflightPath);rawLogSha256=$preflight.rawLogSha256}}else{$null} $prior=$preflight if($previous.ContainsKey($Stage)){ $priorName=$previous[$Stage] $priorPath=Join-Path $PSScriptRoot ($priorName+'-receipt.json') $prior=Get-Content -Raw -LiteralPath $priorPath|ConvertFrom-Json if($prior.stage -cne $priorName -or $prior.base -cne $taskPin -or $prior.workspace -ine $taskWorkspace -or $prior.target -ine $taskTarget -or $prior.cargoExitCode -ne 0 -or -not $prior.selectionValid -or -not $prior.inputsUnchanged -or -not $prior.testBinaryBindingValid -or $null -ne $prior.postflightFailure){throw 'Exact prior sequential stage did not pass.'} foreach($property in $prior.boundScriptAndInputHashes.PSObject.Properties){if((Hash-Input (Join-Path $PSScriptRoot $property.Name)) -cne $property.Value){throw ('Prior transitive binding changed: '+$property.Name)};$boundScripts[$property.Name]=$property.Value} if((Hash-Input (Join-Path $PSScriptRoot ($priorName+'.log'))) -cne $prior.rawLogSha256){throw 'Prior raw output changed.'} foreach($side in @('before','after')){ $captureName='inputs-complete-'+$priorName+'-'+$side+'.json' $captureHash=Hash-Input (Join-Path $PSScriptRoot $captureName) $expectedHash=if($side -eq 'before'){$prior.beforeCaptureSha256}else{$prior.afterCaptureSha256} if($captureHash -cne $expectedHash){throw 'Prior source capture changed'} $boundScripts[$captureName]=$captureHash } $boundScripts[$priorName+'-receipt.json']=Hash-Input $priorPath $boundScripts[$priorName+'.log']=$prior.rawLogSha256 $priorBinding=[ordered]@{stage=$priorName;receiptSha256=(Hash-Input $priorPath);rawLogSha256=$prior.rawLogSha256} } Push-Location -LiteralPath $taskWorkspace try { & (Join-Path $PSScriptRoot 'capture.ps1') -Stage ($Stage+'-before') . $toolchainEnv $env:CARGO_TARGET_DIR=$taskTarget $env:CARGO_TERM_COLOR='never' $PSNativeCommandUseErrorActionPreference=$false $versionPath=Join-Path $PSScriptRoot 'toolchain-versions.json' $versionsHash=Hash-Input $versionPath if($priorBinding -and $prior.toolchainVersionsSha256 -cne $versionsHash){throw 'Toolchain version receipt changed.'} Assert-BatchPlan $batchPlan $expected (Get-Command cargo).Source $batchReceipts=@();$batchesValid=$true $taskArgs=switch($Stage){ 'list' {@('test','-p','oa-desktop','--all-targets','--locked','--offline','--','--list')} 'focused' {@('test','-p','oa-desktop','--bin','arcadia-studio','--locked','--offline','--','--exact')+$expected+@('--test-threads=1')} 'all' {@('test','-p','oa-desktop','--all-targets','--locked','--offline','--','--test-threads=1')} 'clippy' {@('clippy','-p','oa-desktop','-p','oa-server','--all-targets','--locked','--offline','--','-D','warnings')} 'build' {@('build','-p','oa-desktop','--bin','arcadia-studio','--locked','--offline')} } $taskStart=[DateTime]::UtcNow.ToString('o') $testExecutablesBefore=@() if($Stage -ne 'list'){ $testExecutablesBefore=@($prior.testExecutables|ForEach-Object {Get-GateBinary $_.path $taskTarget}) if(-not (Same-GateBinaries @($prior.testExecutables) $testExecutablesBefore)){throw 'Previously bound test executable changed before this stage.'} } if($Stage -eq 'focused'){ $taskExit=0;$lastBatch=$null foreach($batch in $batchPlan.batches){ $batchId=$batch.id;$batchLog=Join-Path $PSScriptRoot ($batchId+'.log');$batchReceipt=Join-Path $PSScriptRoot ($batchId+'-receipt.json') if((Test-Path -LiteralPath $batchLog)-or(Test-Path -LiteralPath $batchReceipt)){throw 'Preserve existing batch evidence'} & (Join-Path $PSScriptRoot 'capture.ps1') -Stage ($batchId+'-before') $batchBefore=@($testExecutablesBefore|ForEach-Object {Get-GateBinary $_.path $taskTarget}) if(-not (Same-GateBinaries $testExecutablesBefore $batchBefore)){throw 'Listed executable changed before batch'} $batchArgs=@($batchPlan.base_arguments)+@($batch.tests)+@($batchPlan.tail_arguments) $batchStart=[DateTime]::UtcNow.ToString('o');$batchFailure=$null;$batchExit=-1 try{& cargo @batchArgs 2>&1|Tee-Object -FilePath $batchLog;$batchExit=$LASTEXITCODE}catch{$batchFailure=$_.Exception.Message;if(-not(Test-Path -LiteralPath $batchLog)){[IO.File]::WriteAllText($batchLog,$batchFailure)}} $batchEnd=[DateTime]::UtcNow.ToString('o');$batchText=Get-Content -Raw -LiteralPath $batchLog $batchResult=Read-BatchResults $batchText;$batchValid=$false;$batchInputs=$false;$batchAfter=@();$batchBinaryValid=$false try{ & (Join-Path $PSScriptRoot 'capture.ps1') -Stage ($batchId+'-after') foreach($file in $boundScripts.Keys){if((Hash-Input (Join-Path $PSScriptRoot $file)) -cne $boundScripts[$file]){throw 'Recipe/input changed during batch'}} if((Hash-Input $versionPath) -cne $versionsHash){throw 'Toolchain record changed during batch'} $batchInputs=$true $batchAfter=@($batchBefore|ForEach-Object {Get-GateBinary $_.path $taskTarget}) $observed=@(Get-GateTestBinaries $batchText $taskTarget) if(-not(Same-GateBinaries $batchBefore $batchAfter)-or-not(Same-GateBinaries $batchAfter $observed)){throw 'Batch executed another or changed test binary'} $batchBinaryValid=$true Assert-ExactBatch $batch $batchResult 1095 if($batchExit -ne 0 -or $null -ne $batchFailure){throw 'Batch process failed to finish successfully'} $batchValid=$true }catch{$batchFailure=$_.Exception.Message} $record=[ordered]@{schema='scenario_details_exact_batch_v1';stage=$batchId;base=$taskPin;workspace=$taskWorkspace;target=$taskTarget;command=('cargo '+($batchArgs -join ' '));arguments=$batchArgs;commandCharacters=$batch.command_characters;startUtc=$batchStart;endUtc=$batchEnd;cargoExitCode=$batchExit;selectionValid=$batchValid;inputsUnchanged=$batchInputs;postflightFailure=$batchFailure;boundScriptAndInputHashes=$boundScripts;listReceiptSha256=(Hash-Input (Join-Path $PSScriptRoot 'list-receipt.json'));batchPlanSha256=(Hash-Input (Join-Path $PSScriptRoot 'batches.json'));priorBatch=$lastBatch;rawLogSha256=(Hash-Input $batchLog);beforeCaptureSha256=(Hash-Input (Join-Path $PSScriptRoot ('inputs-complete-'+$batchId+'-before.json')));afterCaptureSha256=if(Test-Path (Join-Path $PSScriptRoot ('inputs-complete-'+$batchId+'-after.json'))){Hash-Input (Join-Path $PSScriptRoot ('inputs-complete-'+$batchId+'-after.json'))}else{$null};executedTests=$batchResult.executedTests;testResultSummaries=$batchResult.testResultSummaries;testExecutablesBefore=$batchBefore;testExecutables=$batchAfter;testBinaryBindingValid=$batchBinaryValid;toolchainVersionsSha256=$versionsHash} $record|ConvertTo-Json -Depth 14|Set-Content -LiteralPath $batchReceipt -Encoding utf8 $lastBatch=[ordered]@{stage=$batchId;receiptSha256=(Hash-Input $batchReceipt);rawLogSha256=(Hash-Input $batchLog)} $batchReceipts+=$lastBatch foreach($artifact in @(($batchId+'-receipt.json'),($batchId+'.log'),('inputs-complete-'+$batchId+'-before.json'),('inputs-complete-'+$batchId+'-after.json'))){if(Test-Path -LiteralPath (Join-Path $PSScriptRoot $artifact)){$boundScripts[$artifact]=Hash-Input (Join-Path $PSScriptRoot $artifact)}} # Aggregate raw output is the byte-exact concatenation, never a fabricated # one-process summary. Individual logs/receipts remain the execution authority. $stream=[IO.File]::Open($taskLog,[IO.FileMode]::Append,[IO.FileAccess]::Write,[IO.FileShare]::Read) try{$bytes=[IO.File]::ReadAllBytes($batchLog);$stream.Write($bytes,0,$bytes.Length)}finally{$stream.Dispose()} if(-not $batchValid){$batchesValid=$false;$taskExit=if($batchExit -ne 0){$batchExit}else{1};break} } }else{ & cargo @taskArgs 2>&1|Tee-Object -FilePath $taskLog $taskExit=$LASTEXITCODE } $taskEnd=[DateTime]::UtcNow.ToString('o') $taskText=Get-Content -Raw -LiteralPath $taskLog $taskValid=$true;$taskFailure=$null $listed=@();$executed=@();$matched=@();$ignored=@();$summaries=@();$listSummaries=@() if($Stage -eq 'list'){ $listed=@([regex]::Matches($taskText,'(?m)^([^\r\n]+): test\r?$')|ForEach-Object {$_.Groups[1].Value}) $matched=@($listed|Where-Object {$_ -cin $expected}|Sort-Object) $listSummaries=@([regex]::Matches($taskText,'(?m)^(\d+) tests, (\d+) benchmarks\r?$')|ForEach-Object {[ordered]@{tests=[int]$_.Groups[1].Value;benchmarks=[int]$_.Groups[2].Value}}) $taskValid=$listed.Count -ge $expectedCount -and @($listed|Sort-Object -Unique).Count -eq $listed.Count -and (Same-Names $expected $matched) -and (Same-Names @($selection.desktop_tests) $listed) -and $listSummaries.Count -gt 0 -and ($listSummaries|ForEach-Object {$_.tests}|Measure-Object -Sum).Sum -eq $listed.Count -and ($listSummaries|ForEach-Object {$_.benchmarks}|Measure-Object -Sum).Sum -eq 0 } if($Stage -in @('focused','all')){ $executed=@([regex]::Matches($taskText,'(?m)^test ([^\r\n]+?) \.\.\. (ok|FAILED|ignored)[^\r\n]*\r?$')|ForEach-Object {[ordered]@{name=$_.Groups[1].Value;result=$_.Groups[2].Value}}) $summaries=@([regex]::Matches($taskText,'test result: (ok|FAILED)\. (\d+) passed; (\d+) failed; (\d+) ignored; (\d+) measured; (\d+) filtered out;')|ForEach-Object {[ordered]@{status=$_.Groups[1].Value;passed=[int]$_.Groups[2].Value;failed=[int]$_.Groups[3].Value;ignored=[int]$_.Groups[4].Value;measured=[int]$_.Groups[5].Value;filtered=[int]$_.Groups[6].Value}}) $ignored=@($executed|Where-Object {$_.result -eq 'ignored'}|ForEach-Object {$_.name}) $passedCount=@($executed|Where-Object {$_.result -eq 'ok'}).Count $failedCount=@($executed|Where-Object {$_.result -eq 'FAILED'}).Count $taskValid=$summaries.Count -gt 0 -and $failedCount -eq 0 -and @($summaries|Where-Object {$_.status -ne 'ok' -or $_.failed -ne 0 -or $_.measured -ne 0}).Count -eq 0 -and ($summaries|ForEach-Object {$_. passed}|Measure-Object -Sum).Sum -eq $passedCount -and ($summaries|ForEach-Object {$_. ignored}|Measure-Object -Sum).Sum -eq $ignored.Count if($Stage -in @('focused','all')){ $matched=@($executed|Where-Object {$_.name -cin $expected -and $_.result -eq 'ok'}|ForEach-Object {$_.name}|Sort-Object) $taskValid=$taskValid -and (Same-Names $expected $matched) } if($Stage -eq 'focused'){$taskValid=$taskValid -and $batchesValid -and $batchReceipts.Count -eq @($batchPlan.batches).Count -and $executed.Count -eq $expectedCount -and @($executed.name|Sort-Object -Unique).Count -eq $expectedCount -and $passedCount -eq $expectedCount -and $ignored.Count -eq 0 -and $summaries.Count -eq $batchReceipts.Count} if($Stage -eq 'all'){ $listPath=Join-Path $PSScriptRoot 'list-receipt.json' $listHash=Hash-Input $listPath $listReceipt=Get-Content -Raw -LiteralPath $listPath|ConvertFrom-Json if($prior.priorStage.stage -cne 'list' -or $prior.priorStage.receiptSha256 -cne $listHash){throw 'Desktop listing changed after focused execution.'} $boundScripts['list-receipt.json']=$listHash $taskValid=$taskValid -and (Same-Names @($listReceipt.listedTests) @($executed|ForEach-Object {$_.name})) -and ($summaries|ForEach-Object {$_. filtered}|Measure-Object -Sum).Sum -eq 0 -and (Same-Names @($selection.ignored_tests) $ignored) } } $inputsUnchanged=$false try { & (Join-Path $PSScriptRoot 'capture.ps1') -Stage ($Stage+'-after') foreach($file in $boundScripts.Keys){if((Hash-Input (Join-Path $PSScriptRoot $file)) -cne $boundScripts[$file]){throw ('Gate recipe/input changed: '+$file)}} if((Hash-Input $versionPath) -cne $versionsHash){throw 'Toolchain version receipt changed.'} if($priorBinding -and (Hash-Input (Join-Path $PSScriptRoot ($priorBinding.stage+'-receipt.json'))) -cne $priorBinding.receiptSha256){throw 'Prior receipt changed during gate.'} $inputsUnchanged=$true }catch{$taskFailure=$_.Exception.Message} $testExecutables=@();$testBinaryBindingValid=$false try { if($Stage -eq 'list'){ $testExecutables=@(Get-GateTestBinaries $taskText $taskTarget) if($testExecutables.Count -ne 1){throw 'Expected exactly one actual desktop test executable from Cargo listing.'} } else { $testExecutables=@($testExecutablesBefore|ForEach-Object {Get-GateBinary $_.path $taskTarget}) if(-not (Same-GateBinaries $testExecutablesBefore $testExecutables)){throw 'Bound test executable changed during this stage.'} if($Stage -in @('focused','all')){ $observed=@(Get-GateTestBinaries $taskText $taskTarget) if(-not (Same-GateBinaries $testExecutables $observed)){throw 'Executed test path differs from the exact listed executable.'} } } $testBinaryBindingValid=$true } catch {$taskFailure=$_.Exception.Message} $taskValid=$taskValid -and $testBinaryBindingValid $binary=$null if($Stage -eq 'build' -and $taskExit -eq 0){$path=Join-Path $taskTarget 'debug/arcadia-studio.exe';$item=Get-Item -LiteralPath $path;$binary=[ordered]@{path=$item.FullName;bytes=$item.Length;sha256=(Hash-Input $path);mtimeUtc=$item.LastWriteTimeUtc.ToString('o')}} [ordered]@{stage=$Stage;executionMode=if($Stage -eq 'focused'){'bounded-exact-batches-v1'}else{'cargo-single-stage'};batchReceipts=$batchReceipts;batchPlanSha256=(Hash-Input (Join-Path $PSScriptRoot 'batches.json'));command=if($Stage -eq 'focused'){'cargo test in bounded exact-name batches; see batchReceipts'}else{'cargo '+($taskArgs -join ' ')};arguments=if($Stage -eq 'focused'){@()}else{$taskArgs};base=$taskPin;workspace=$taskWorkspace;target=$taskTarget;startUtc=$taskStart;endUtc=$taskEnd;cargoExitCode=$taskExit;selectionValid=$taskValid;inputsUnchanged=$inputsUnchanged;postflightFailure=$taskFailure;boundScriptAndInputHashes=$boundScripts;priorStage=$priorBinding;externalEnvironmentPath=$toolchainEnv;externalEnvironmentSha256=(Hash-Input $toolchainEnv);toolchainVersionsSha256=$versionsHash;rawLogSha256=(Hash-Input $taskLog);beforeCaptureSha256=(Hash-Input (Join-Path $PSScriptRoot ('inputs-complete-'+$Stage+'-before.json')));afterCaptureSha256=if(Test-Path -LiteralPath (Join-Path $PSScriptRoot ('inputs-complete-'+$Stage+'-after.json'))){Hash-Input (Join-Path $PSScriptRoot ('inputs-complete-'+$Stage+'-after.json'))}else{$null};listedTests=$listed;executedTests=$executed;testResultSummaries=$summaries;matchedRegressionTests=$matched;ignoredTests=$ignored;listSummaries=$listSummaries;testExecutablesBefore=$testExecutablesBefore;testExecutables=$testExecutables;testBinaryBindingValid=$testBinaryBindingValid;binary=$binary}|ConvertTo-Json -Depth 14|Set-Content -LiteralPath $taskReceipt -Encoding utf8 if($taskExit -ne 0){exit $taskExit} if(-not $taskValid -or -not $inputsUnchanged){throw 'Gate membership or input binding failed; inspect receipt and raw output.'} } finally {Pop-Location}