param([Parameter(Mandatory)][string]$EvidenceDir,[Parameter(Mandatory)][string]$Fixture,[string]$ProgressionSource=(Join-Path $PSScriptRoot 'exercise-final-progression.ps1')) $ErrorActionPreference='Stop' New-Item -ItemType Directory -Path $EvidenceDir -Force|Out-Null $checks=[Collections.Generic.List[object]]::new();$commands=[Collections.Generic.List[object]]::new() function Record($Id,$Passed,$Actual){$checks.Add(@{id=$Id;passed=[bool]$Passed;actual=$Actual})} function Check($Id,$Passed,$Actual){if(!$Passed){throw "$Id : $Actual"}} function ReadFunction($Path,$Name){ $ast=[Management.Automation.Language.Parser]::ParseFile($Path,[ref]$null,[ref]$null) $f=$ast.Find({param($n)$n-is[Management.Automation.Language.FunctionDefinitionAst]-and$n.Name-eq$Name},$true) $expected=$f.Extent.Text.Replace("function $Name","function global:$Name") . ([scriptblock]::Create($expected)) $actual=(Get-Command $Name).ScriptBlock.Ast.Extent.Text Record "$Name-production-body-identity" ($actual-ceq$expected) @{source=$Path;sha256=[Convert]::ToHexString([Security.Cryptography.SHA256]::HashData([Text.Encoding]::UTF8.GetBytes($actual)))} } function Adb($Arguments){return 'Physical size: 1080x2400'} $common=Join-Path $PSScriptRoot 'final-common.ps1' foreach($name in @('Same','BallPoint','BallClear','WorldSafe','TapBall')){ReadFunction $common $name} foreach($name in @('Board','Orbit','CountRemaining')){ReadFunction $ProgressionSource $name} $captured=Get-Content -LiteralPath $Fixture -Raw|ConvertFrom-Json $stage=@($captured.puzzleStates|Where-Object {$_.balls.id-contains's16-b05'})[0].stageId if(!$stage-or(Board $captured $stage).countedBallIds.Count-ne5){throw 'Requires the exact captured Stage16 five-of-six blocked count state'} function Save($Name){if($Name-match'-count-\d+-state$'){$script:stateReads++};return $script:state|ConvertTo-Json -Depth 20|ConvertFrom-Json} function Step($Name,$Arguments=@(),$Delay=500){ $script:steps.Add(@{name=$Name;arguments=$Arguments}) if($Arguments[2]-eq'swipe'){ $b=Board $script:state $stage;$q=[Numerics.Quaternion]::new($b.camera.rotation.x,$b.camera.rotation.y,$b.camera.rotation.z,$b.camera.rotation.w) $yaw=([int]$Arguments[5]-[int]$Arguments[3])*.25*[Math]::PI/180 $pitch=([int]$Arguments[6]-[int]$Arguments[4])*.25*[Math]::PI/180 $q=[Numerics.Quaternion]::CreateFromAxisAngle([Numerics.Vector3]::UnitY,$yaw)*$q*[Numerics.Quaternion]::CreateFromAxisAngle([Numerics.Vector3]::UnitX,$pitch) if($script:mutation-ne'low-view-occluded'-or$pitch-ne0){$b.camera.rotation=[pscustomobject]@{x=$q.X;y=$q.Y;z=$q.Z;w=$q.W}} switch($script:mutation){ 'ball'{$b.balls[0].position.x+=.1} 'membership'{$b.groups[0].ballIds=@($b.groups[0].ballIds|Select-Object -Skip 1)} 'collection'{$b.collectedBallIds=@($b.collectedBallIds|Select-Object -Skip 1)} 'count'{$b.countedBallIds+= 's16-b05'} 'completed'{$script:state.completedStageIds+= $stage} 'other-puzzle'{$script:state.puzzleStates[0].balls[0].position.x+=.1} 'free-play'{$script:state.latestFreePlay.camera.distance+=.1} 'tutorial'{$script:state.seenTutorialIds+= 'unobserved'} } } if($Arguments[2]-eq'tap'){ $b=Board $script:state $stage;$point=BallPoint $b 's16-b05' $script:invalidTap=$script:invalidTap-or!(BallClear $b 's16-b05')-or[int]$Arguments[3]-ne$point[0]-or[int]$Arguments[4]-ne$point[1] } # A tap is observed, never translated into synthetic counting or completion. } try{ foreach($kind in @('captured-past-pole','near-upper-limit','low-view-occluded','ball','membership','collection','count','completed','other-puzzle','free-play','tutorial')){ $script:state=$captured|ConvertTo-Json -Depth 20|ConvertFrom-Json;$script:steps=[Collections.Generic.List[object]]::new();$script:mutation=$kind;$script:stateReads=0;$script:invalidTap=$false if($kind-eq'near-upper-limit'){ $q=[Numerics.Quaternion]::CreateFromAxisAngle([Numerics.Vector3]::UnitX,(89*[Math]::PI/180)) (Board $script:state $stage).camera.rotation=[pscustomobject]@{x=$q.X;y=$q.Y;z=$q.Z;w=$q.W} } if($kind-eq'low-view-occluded'){ # Counterfactual low camera and front sphere; yaw leaf remains stationary. # This tests signed pitch selection, never an actual reachable scene claim. $b=Board $script:state $stage;$b.camera.rotation=[pscustomobject]@{x=0;y=0;z=0;w=1} $p=@($b.balls|Where-Object id -eq 's16-b05')[0].position @($b.balls|Where-Object id -eq 's16-b04')[0].position=[pscustomobject]@{x=$p.x;y=$p.y;z=($p.z-1)} } $before=$script:state|ConvertTo-Json -Depth 20|ConvertFrom-Json $blocked=!(BallClear (Board $before $stage) 's16-b05') $errorText=$null;try{CountRemaining $stage|Out-Null}catch{$errorText=$_.Exception.Message} $taps=@($script:steps|Where-Object {$_.arguments[2]-eq'tap'}) $pitch=@($script:steps|Where-Object {$_.arguments[2]-eq'swipe'-and$_.arguments[3]-eq$_.arguments[5]}) if($kind-in@('captured-past-pole','near-upper-limit','low-view-occluded')){ $direction=if($kind-eq'near-upper-limit'){-120}else{120} $correctPitch=$pitch.Count-gt0-and$pitch[0].name-match'count-safe-view'-and([int]$pitch[0].arguments[6]-[int]$pitch[0].arguments[4])-eq$direction Record 'blocked-center-uses-lower-view-before-real-ID-tap-request' ($blocked-and$correctPitch-and$taps.Count-gt0-and!$script:invalidTap) @{kind=$kind;pitchCommands=$pitch;tapRequests=$taps.Count;scope='Offline projection and gesture requests only; scenery first hit and native count not evaluated'} $unchanged=$script:state|ConvertTo-Json -Depth 20|ConvertFrom-Json;(Board $unchanged $stage).camera=(Board $before $stage).camera Record 'forty-bound-no-fake-count-or-completion' ($errorText-match'Native counting bound reached'-and$script:stateReads-eq40-and(Same $before $unchanged)) @{kind=$kind;stateReads=$script:stateReads;gestureRequests=$script:steps.Count;error=$errorText} Record 'lower-pitch-gesture-keeps-ui-safe-endpoints' ($correctPitch-and(WorldSafe @([int]$pitch[0].arguments[3],[int]$pitch[0].arguments[4]))-and(WorldSafe @([int]$pitch[0].arguments[5],[int]$pitch[0].arguments[6]))) $kind }else{Record 'manual-view-search-rejects-save-mutation-before-tap' ($errorText-match'count-view-preserves-whole-save'-and$taps.Count-eq0) @{kind=$kind;error=$errorText}} } }catch{Record 'production-evaluation' $false $_.Exception.Message} $passed=$checks.Count-gt0-and@($checks|Where-Object passed -ne $true).Count-eq0 @{passed=$passed;scope='Captured state replay with production function body identity and leaf gesture mocks. No ADB, native first hit or completed-stage claim.';fixture=$Fixture;fixtureSha256=(Get-FileHash $Fixture).Hash;progressionSource=$ProgressionSource;progressionSha256=(Get-FileHash $ProgressionSource).Hash;commonSha256=(Get-FileHash $common).Hash;checks=$checks.ToArray()}|ConvertTo-Json -Depth 14|Set-Content (Join-Path $EvidenceDir 'count-view-result.json') if(!$passed){exit 1};exit 0