Files
ARCADIA-Studio/harness/experiments/scenario-workspace-gates-2c9cd2b-20260913/original/run.ps1
T

112 lines
11 KiB
PowerShell

#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-workspace-ytr83r-20260913'
$taskTarget='C:\Users\User\Documents\Git\ARCADIA-Studio\target'
$taskPin='2c9cd2b3e661ef85f11590c7128dcb2f502d0c26'
$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.'}
$clearancePath=Join-Path $PSScriptRoot 'package-clearance.json'
$clearance=Get-Content -Raw -LiteralPath $clearancePath|ConvertFrom-Json
if($clearance.base -cne $taskPin -or $clearance.cargoExitCode -ne 0 -or [IO.Path]::GetFullPath($clearance.target) -ine $taskTarget -or -not $clearance.dependenciesRetained){throw 'Parent-owned successful current-pin package clearance is required.'}
$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_count -ne 224 -or $expected.Count -ne 224 -or @($selection.tests).Count -ne 224){throw 'Wrong exact focused membership.'}
$expectedCount=224
$commonFiles=@('run.ps1','capture.ps1','expected-tests.json','expected-inputs.json','enumeration.json','external-inputs.json','inputs-complete-before.json','package-clearance.json','server-provenance.json')
$boundScripts=@{}
foreach($file in $commonFiles){$boundScripts[$file]=Hash-Input (Join-Path $PSScriptRoot $file)}
$previous=@{focused='list';all='focused';clippy='all';build='clippy'}
$priorBinding=$null
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){throw 'Exact prior sequential stage did not pass.'}
foreach($file in $commonFiles){if($prior.boundScriptAndInputHashes.$file -cne $boundScripts[$file]){throw ('Prior recipe/input binding changed: '+$file)}}
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.'}
$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'
if($Stage -eq 'list'){
if(Test-Path -LiteralPath $versionPath){throw 'Preserve prior toolchain version evidence.'}
$cargoVersion=@(& cargo --version --verbose 2>&1);$cargoVersionExit=$LASTEXITCODE
$rustcVersion=@(& rustc --version --verbose 2>&1);$rustcVersionExit=$LASTEXITCODE
$clippyVersion=@(& cargo clippy --version 2>&1);$clippyVersionExit=$LASTEXITCODE
$clangVersion=@(& clang --version 2>&1);$clangVersionExit=$LASTEXITCODE
[ordered]@{base=$taskPin;capturedUtc=[DateTime]::UtcNow.ToString('o');externalEnvironmentPath=$toolchainEnv;externalEnvironmentSha256=(Hash-Input $toolchainEnv);cargo=@($cargoVersion|ForEach-Object {$_.ToString()});cargoExitCode=$cargoVersionExit;rustc=@($rustcVersion|ForEach-Object {$_.ToString()});rustcExitCode=$rustcVersionExit;clippy=@($clippyVersion|ForEach-Object {$_.ToString()});clippyExitCode=$clippyVersionExit;clang=@($clangVersion|ForEach-Object {$_.ToString()});clangExitCode=$clangVersionExit;cargoPath=(Get-Command cargo).Source;rustcPath=(Get-Command rustc).Source;clangPath=(Get-Command clang).Source;cargoHome=$env:CARGO_HOME;rustupHome=$env:RUSTUP_HOME;rustupToolchain=$env:RUSTUP_TOOLCHAIN;buildJobs=$env:CARGO_BUILD_JOBS;incremental=$env:CARGO_INCREMENTAL}|ConvertTo-Json -Depth 8|Set-Content -LiteralPath $versionPath -Encoding utf8
if($cargoVersionExit -ne 0 -or $rustcVersionExit -ne 0 -or $clippyVersionExit -ne 0 -or $clangVersionExit -ne 0){throw 'Toolchain version query failed.'}
}
$versionsHash=Hash-Input $versionPath
if($priorBinding -and $prior.toolchainVersionsSha256 -cne $versionsHash){throw 'Toolchain version receipt changed.'}
$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','--all-targets','--locked','--offline','--','-D','warnings')}
'build' {@('build','-p','oa-desktop','--bin','arcadia-studio','--locked','--offline')}
}
$taskStart=[DateTime]::UtcNow.ToString('o')
& 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 $executed.Count -eq $expectedCount -and $passedCount -eq $expectedCount -and $ignored.Count -eq 0 -and $summaries.Count -eq 1 -and $summaries[0].filtered -eq (@($prior.listedTests).Count - $expectedCount)}
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}
$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;command=('cargo '+($taskArgs -join ' '));arguments=$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;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}