$ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSScriptRoot $initArtifactsScript = Join-Path $root "scripts/wireframe/init-artifacts.ps1" $extractScript = Join-Path $root "scripts/wireframe/extract-documents.ps1" $validateScript = Join-Path $root "scripts/wireframe/validate-artifacts.ps1" $initWorkspaceScript = Join-Path $root "scripts/system/init-workspace.ps1" $exportUpdateScript = Join-Path $root "scripts/system/export-update-package.ps1" $applyUpdateScript = Join-Path $root "scripts/system/apply-hot-update.ps1" $newBugReportScript = Join-Path $root "scripts/system/new-bug-report.ps1" $fixtureDocs = Join-Path $root "tests/fixtures/docs" $fixtureArtifacts = Join-Path $root "tests/fixtures/artifacts" $tempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("wireframe-gen-test-" + [guid]::NewGuid().ToString()) function Copy-FixtureArtifacts { param([string]$Destination) New-Item -ItemType Directory -Force -Path $Destination | Out-Null Get-ChildItem -LiteralPath $fixtureArtifacts -Force | ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force } } function Write-Blueprints { param( [object]$Blueprints, [string]$ArtifactDir ) $path = Join-Path $ArtifactDir "screen_blueprints.json" $Blueprints | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8 } function Write-NormalizedProject { param( [object]$Project, [string]$ArtifactDir ) $path = Join-Path $ArtifactDir "normalized_project.json" $Project | ConvertTo-Json -Depth 20 | Set-Content -LiteralPath $path -Encoding UTF8 } function Expect-ValidationFailure { param( [string]$Name, [scriptblock]$Mutate, [string]$Stage = "schema" ) $caseDir = Join-Path $tempDir $Name Copy-FixtureArtifacts -Destination $caseDir & $Mutate $caseDir $oldErrorActionPreference = $ErrorActionPreference $ErrorActionPreference = "Continue" try { powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage *> $null $exitCode = $LASTEXITCODE } finally { $ErrorActionPreference = $oldErrorActionPreference } if ($exitCode -eq 0) { throw "Expected validation failure for $Name" } } function Expect-ValidationSuccess { param( [string]$Name, [scriptblock]$Mutate, [string]$Stage = "schema" ) $caseDir = Join-Path $tempDir $Name Copy-FixtureArtifacts -Destination $caseDir & $Mutate $caseDir powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $caseDir -Strict -Stage $Stage | Out-Null } function Assert-PathExists { param([string]$Path) if (-not (Test-Path -LiteralPath $Path)) { throw "Expected path to exist: $Path" } } function Assert-PathMissing { param([string]$Path) if (Test-Path -LiteralPath $Path) { throw "Expected path to be absent: $Path" } } function Assert-Equal { param( [object]$Actual, [object]$Expected, [string]$Message ) if ($Actual -ne $Expected) { throw "$Message Expected '$Expected', got '$Actual'" } } function Copy-DirectoryContents { param( [string]$Source, [string]$Destination ) New-Item -ItemType Directory -Force -Path $Destination | Out-Null Get-ChildItem -LiteralPath $Source -Force | ForEach-Object { Copy-Item -LiteralPath $_.FullName -Destination $Destination -Recurse -Force } } function Assert-PackageHasNoPreservedFiles { param([object]$PackageManifest) foreach ($file in @($PackageManifest.files)) { $path = ([string]$file.path) -replace "\\", "/" foreach ($blocked in @("workspace", "artifacts", "maintenance", "dist")) { if ($path -eq $blocked -or $path.StartsWith("$blocked/")) { throw "Package should not include preserved path: $path" } } } } try { $defaultCase = Join-Path $tempDir "default-paths" New-Item -ItemType Directory -Force -Path $defaultCase | Out-Null Push-Location $defaultCase try { powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript | Out-Null Assert-PathExists (Join-Path $defaultCase "workspace/artifacts/wireframe-gen/source_inventory.json") powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -Strict | Out-Null } finally { Pop-Location } $legacyArtifactDir = Join-Path $tempDir "legacy-artifact-dir" powershell -NoProfile -ExecutionPolicy Bypass -File $initArtifactsScript -ArtifactDir $legacyArtifactDir | Out-Null powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $legacyArtifactDir -Strict | Out-Null $legacySource = Join-Path $tempDir "legacy-source/artifacts/wireframe-gen" Copy-FixtureArtifacts -Destination $legacySource $workspaceRoot = Join-Path $tempDir "client-workspace" $workspaceArtifacts = Join-Path $workspaceRoot "artifacts/wireframe-gen" powershell -NoProfile -ExecutionPolicy Bypass -File $initWorkspaceScript -WorkspaceDir $workspaceRoot -ArtifactDir $workspaceArtifacts -LegacyArtifactDir $legacySource | Out-Null Assert-PathExists (Join-Path $workspaceArtifacts "source_inventory.json") Assert-PathExists (Join-Path $workspaceRoot "system-feedback/bug-reports") Assert-PathExists (Join-Path $workspaceRoot ".hot-update/backups") Assert-PathExists (Join-Path $legacySource "source_inventory.json") powershell -NoProfile -ExecutionPolicy Bypass -File $extractScript -InputPath $fixtureDocs -OutputDir $tempDir | Out-Null $inventoryPath = Join-Path $tempDir "source_inventory.json" if (-not (Test-Path -LiteralPath $inventoryPath)) { throw "source_inventory.json was not created" } $inventory = Get-Content -LiteralPath $inventoryPath -Raw -Encoding UTF8 | ConvertFrom-Json if (@($inventory.sources).Count -lt 2) { throw "Expected at least 2 extracted fixture sources" } powershell -NoProfile -ExecutionPolicy Bypass -File $validateScript -ArtifactDir $fixtureArtifacts -Strict | Out-Null Expect-ValidationFailure "legacy-open-question-blocks-pre-figma" { param($caseDir) } -Stage "pre-figma" Expect-ValidationSuccess "resolved-open-question-passes-pre-ux" { param($caseDir) } -Stage "pre-ux" Expect-ValidationFailure "unresolved-open-question-blocks-pre-ux" { param($caseDir) $project = Get-Content -LiteralPath (Join-Path $caseDir "normalized_project.json") -Raw -Encoding UTF8 | ConvertFrom-Json $project.open_questions += [pscustomobject]@{ id = "Q-003" question = "Which navigation model should be used?" status = "unresolved" blocks = @("ux-construction") default_assumption = "" answer = "" answered_at = "" source_refs = @() } Write-NormalizedProject -Project $project -ArtifactDir $caseDir } -Stage "pre-ux" Expect-ValidationFailure "bad-screen-width" { param($caseDir) $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json $blueprints[0].viewport.width = 1436 Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir } Expect-ValidationFailure "bad-screen-height" { param($caseDir) $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json $blueprints[0].viewport.height = 796 Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir } Expect-ValidationFailure "missing-element-parent" { param($caseDir) $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json $blueprints[1].PSObject.Properties.Remove("parent_screen_id") Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir } Expect-ValidationFailure "fractional-spacing" { param($caseDir) $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json $blueprints[0].sections[0].padding = 32.5 Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir } Expect-ValidationFailure "bad-element-size" { param($caseDir) $blueprints = Get-Content -LiteralPath (Join-Path $caseDir "screen_blueprints.json") -Raw -Encoding UTF8 | ConvertFrom-Json $blueprints[1].bounds.width = 13 Write-Blueprints -Blueprints $blueprints -ArtifactDir $caseDir } $packageOutput = Join-Path $tempDir "packages" $exportOutput = powershell -NoProfile -ExecutionPolicy Bypass -File $exportUpdateScript -OutputDir $packageOutput $packagePath = @($exportOutput)[-1] Assert-PathExists $packagePath $packageManifestPath = Join-Path $packagePath "package-manifest.json" Assert-PathExists $packageManifestPath $packageManifest = Get-Content -LiteralPath $packageManifestPath -Raw -Encoding UTF8 | ConvertFrom-Json Assert-PackageHasNoPreservedFiles -PackageManifest $packageManifest $clientRoot = Join-Path $tempDir "client-copy" Copy-DirectoryContents -Source $packagePath -Destination $clientRoot $clientAgents = Join-Path $clientRoot "AGENTS.md" Set-Content -LiteralPath $clientAgents -Value "old system instructions" -Encoding UTF8 -NoNewline New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "workspace/artifacts/wireframe-gen") | Out-Null Set-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Value "do not touch" -Encoding UTF8 -NoNewline New-Item -ItemType Directory -Force -Path (Join-Path $clientRoot "maintenance") | Out-Null Set-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Value "do not touch" -Encoding UTF8 -NoNewline powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot -DryRun | Out-Null Assert-Equal (Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8) "old system instructions" "Dry-run must not mutate system files." Assert-PathMissing (Join-Path $clientRoot "workspace/.hot-update/last-apply.json") powershell -NoProfile -ExecutionPolicy Bypass -File $applyUpdateScript -PackagePath $packagePath -TargetRoot $clientRoot | Out-Null $sourceAgents = Get-Content -LiteralPath (Join-Path $packagePath "AGENTS.md") -Raw -Encoding UTF8 $updatedAgents = Get-Content -LiteralPath $clientAgents -Raw -Encoding UTF8 Assert-Equal $updatedAgents $sourceAgents "Hot Update must replace allowlisted system files." Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "workspace/artifacts/wireframe-gen/client-data.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve workspace files." Assert-Equal (Get-Content -LiteralPath (Join-Path $clientRoot "maintenance/origin-only.txt") -Raw -Encoding UTF8) "do not touch" "Hot Update must preserve maintenance files." Assert-PathExists (Join-Path $clientRoot "workspace/.hot-update/last-apply.json") $backupAgents = Get-ChildItem -LiteralPath (Join-Path $clientRoot "workspace/.hot-update/backups") -Recurse -File -Filter "AGENTS.md" | Select-Object -First 1 if ($null -eq $backupAgents) { throw "Expected Hot Update to create a backup for overwritten AGENTS.md" } Assert-Equal (Get-Content -LiteralPath $backupAgents.FullName -Raw -Encoding UTF8) "old system instructions" "Backup should contain the previous file content." $bugWorkspace = Join-Path $tempDir "bug-workspace" powershell -NoProfile -ExecutionPolicy Bypass -File $newBugReportScript ` -WorkspaceDir $bugWorkspace ` -Title "Validation does not block bad screen" ` -Area "validation" ` -Expected "Validation fails when a screen is underspecified." ` -Actual "Validation passed." ` -TriggerContext "During sanitized fixture checks." ` -ReproSteps "Run validation; observe success" ` -Impact "Incorrect downstream wireframes" | Out-Null $reportJsonPath = Get-ChildItem -LiteralPath (Join-Path $bugWorkspace "system-feedback/bug-reports") -Recurse -File -Filter "report.json" | Select-Object -First 1 if ($null -eq $reportJsonPath) { throw "Expected bug report JSON to be created." } $report = Get-Content -LiteralPath $reportJsonPath.FullName -Raw -Encoding UTF8 | ConvertFrom-Json foreach ($field in @("id", "created_at", "system_version", "area", "severity", "trigger_context", "actual_behavior", "expected_behavior", "repro_steps", "impact", "workaround", "depersonalization_notes", "privacy_confirmed")) { if (-not ($report.PSObject.Properties.Name -contains $field)) { throw "Bug report missing required field: $field" } } Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "report.md") Assert-PathExists (Join-Path $reportJsonPath.Directory.FullName "sanitized-snippets") Write-Output "Wireframe checks passed" } finally { if (Test-Path -LiteralPath $tempDir) { Remove-Item -LiteralPath $tempDir -Recurse -Force } }