[CmdletBinding(SupportsShouldProcess)] param([Parameter(Mandatory)][ValidateSet('mock','real')][string]$Binding) $ErrorActionPreference='Stop' $repo='' $base=Join-Path $repo 'docs/evidence/release10/entry-shrink-extension-20261031T124626' $baseline=Get-Content -LiteralPath (Join-Path $base 'before.json') -Raw|ConvertFrom-Json -Depth 30 $directory=Join-Path $base $Binding if(Test-Path -LiteralPath $directory){throw 'Retain existing gate; no overwrite or rerun'} $testPaths=@('tests/application/capacity-policy.test.ts','tests/application/legacy-match-capacity.test.ts','tests/application/capacity-public.test.ts','tests/web/capacity-ui.test.ts') $sourcePaths=@($testPaths+@('tests/application/capacity-contract-fixture.ts','tests/web/capacity-ui-fixture.ts')) $currentProduct=@(Get-ChildItem -LiteralPath (Join-Path $repo 'src') -File -Recurse|Sort-Object FullName|ForEach-Object {[ordered]@{path=[IO.Path]::GetRelativePath($repo,$_.FullName);sha256=(Get-FileHash -LiteralPath $_.FullName).Hash}}) if(($currentProduct|ConvertTo-Json -Depth 8 -Compress) -cne ($baseline.productPins|ConvertTo-Json -Depth 8 -Compress)){throw 'Current product source changed since post-implementation baseline; stop and report exact chronology'} if((Get-Content -LiteralPath (Join-Path $repo 'src/domain/rooms.ts') -Raw) -notmatch 'export const MAX_ROOM_PLAYERS = 10;'){throw 'This is current MAX10 post-implementation extension, never original MAX15 gate'} $pins=@(foreach($path in $sourcePaths){[ordered]@{path=$path;sha256=(Get-FileHash -LiteralPath (Join-Path $repo $path)).Hash}}) if($Binding -eq 'real'){ $mock=Get-Content -LiteralPath (Join-Path $base 'mock/receipt.json') -Raw|ConvertFrom-Json -Depth 30 if($mock.exitCode -ne 0 -or $mock.cases -ne 42 -or $mock.failed -ne 0 -or $mock.skipped -ne 0 -or $mock.negativeControls -ne 14 -or ($mock.testPins|ConvertTo-Json -Depth 8 -Compress) -cne ($pins|ConvertTo-Json -Depth 8 -Compress)){throw 'Real gate requires same42-case source after passing post-implementation mock42'} } if(-not $PSCmdlet.ShouldProcess($directory,'Run42 same assertions with14 expected negative controls against post-implementation mock/real provider, retain unchanged CURRENT MAX10 product hashes and native JUnit')){return} [void][IO.Directory]::CreateDirectory($directory) $prior=$env:WORDPAIRS_CAPACITY_FIXTURE if($Binding -eq 'mock'){$env:WORDPAIRS_CAPACITY_FIXTURE='correct'}else{Remove-Item -LiteralPath Env:WORDPAIRS_CAPACITY_FIXTURE -ErrorAction SilentlyContinue} $xml=Join-Path $directory 'unit.xml';$log=Join-Path $directory 'unit.log' $arguments=@('exec','vitest','--','run')+$testPaths+@('--maxWorkers=1','--reporter=default','--reporter=junit',('--outputFile.junit='+$xml)) $started=Get-Date -Format s Push-Location $repo try{& npm @arguments *> $log;$exit=$LASTEXITCODE}finally{Pop-Location;if($null -eq $prior){Remove-Item Env:WORDPAIRS_CAPACITY_FIXTURE -ErrorAction SilentlyContinue}else{$env:WORDPAIRS_CAPACITY_FIXTURE=$prior}} [xml]$native=Get-Content -LiteralPath $xml -Raw $cases=@($native.SelectNodes('//testcase'));$failed=@($cases|Where-Object {$_.SelectSingleNode('failure|error')}).Count;$skipped=@($cases|Where-Object {$_.SelectSingleNode('skipped')}).Count;$negative=@($cases|Where-Object name -Like '*rejects wrong*').Count $names=@($cases|ForEach-Object {"$($_.classname)|$($_.name)"}|Sort-Object) foreach($pin in $pins){$source=Join-Path $repo $pin.path;if((Get-FileHash -LiteralPath $source).Hash -cne $pin.sha256){throw 'Capacity test/fixture bytes changed during gate'};$copy=Join-Path $directory (Join-Path 'source' $pin.path);[void][IO.Directory]::CreateDirectory((Split-Path $copy));[IO.File]::Copy($source,$copy,$false);if((Get-FileHash -LiteralPath $copy).Hash -cne $pin.sha256){throw 'Retained capacity source copy mismatch'}} foreach($pin in $baseline.productPins){if((Get-FileHash -LiteralPath (Join-Path $repo $pin.path)).Hash -cne $pin.sha256){throw 'Product bytes changed during focused post-implementation gate'}} if($Binding -eq 'real' -and ($names|ConvertTo-Json -Compress) -cne ($mock.caseNames|ConvertTo-Json -Compress)){throw 'Mock/real case name parity mismatch'} [IO.File]::Copy($PSCommandPath,(Join-Path $directory 'EntryRunCap10ShrinkGate.ps1'),$false) $receipt=[ordered]@{startedAt=$started;finishedAt=(Get-Date -Format s);sessionId=$baseline.sessionId;requestId=$baseline.requestId;parentSessionId=$baseline.parentSessionId;parentRequestId=$baseline.parentRequestId;binding=$Binding;phase='POST-implementation test extension at current MAX10; not original preimplementation mock-first evidence';revision=(& git -C $repo rev-parse HEAD).Trim();dirtySource=$true;executable='npm';arguments=$arguments;exitCode=$exit;cases=$cases.Count;failed=$failed;skipped=$skipped;negativeControls=$negative;caseNames=$names;testPins=$pins;unchangedCurrentProductPins=$baseline.productPins;productFileCount=$baseline.productFileCount;baselineSha256=(Get-FileHash -LiteralPath (Join-Path $base 'before.json')).Hash;junitSha256=(Get-FileHash -LiteralPath $xml).Hash;producerSha256=(Get-FileHash -LiteralPath $PSCommandPath).Hash;qualification='Original40-case mock-first and real history retained. This new42-case mock then identical real binding is a post-implementation test extension. No product edit, full gate, browser gameplay, hosted result, independent HV or done-state claim.'} $receipt|ConvertTo-Json -Depth 30|Set-Content -LiteralPath (Join-Path $directory 'receipt.json') -Encoding utf8NoBOM [ordered]@{recordedAt=(Get-Date -Format s);binding=$Binding;exitCode=$exit;cases=$cases.Count;failed=$failed;skipped=$skipped;negativeControls=$negative;receiptSha256=(Get-FileHash -LiteralPath (Join-Path $directory 'receipt.json')).Hash;junitSha256=$receipt.junitSha256;productFiles=$baseline.productFileCount}|ConvertTo-Json if($exit -or $cases.Count -ne 42 -or $failed -or $skipped -or $negative -ne 14){Get-Content -LiteralPath $log;throw 'Focused42-case post-implementation gate failed; preserve native evidence'}