$ErrorActionPreference = 'Stop' function Assert-Contains { param([string]$Content, [string]$Expected, [string]$Message) if ($Content -notmatch [regex]::Escape($Expected)) { throw $Message } } function Assert-NoCssSurface { param([string]$Content, [string]$ClassName) foreach ($property in @('border', 'background', 'border-radius', 'box-shadow')) { if ($Content -match "(?s)\.$ClassName\s*\{[^}]*\b$property\s*:") { throw "T01 must not construct .$ClassName with CSS $property" } } } $root = Split-Path -Parent $PSScriptRoot $pagePath = Join-Path $root 'pages/tree/t01-tree-overview.vue' $t02Path = Join-Path $root 'pages/tree/t02-tree-states.vue' $page = Get-Content -LiteralPath $pagePath -Raw -Encoding utf8 $pages = Get-Content -LiteralPath (Join-Path $root 'pages.json') -Raw -Encoding utf8 $catalog = Get-Content -LiteralPath (Join-Path $root 'data/page-catalog.js') -Raw -Encoding utf8 if (Test-Path -LiteralPath $t02Path) { throw 'T02 duplicate route file must be removed' } if ($pages -match 'pages/tree/t02-tree-states') { throw 'T02 duplicate route must be removed from pages.json' } if ($catalog -match 't02:') { throw 'T02 duplicate catalog entry must be removed' } if ($page -match "@/utils/api\.js|\bappApi\b") { throw 'T01 design phase must not connect the API layer' } foreach ($required in @( 'const treeState = ref("loading")', 'tree-state--tree', 'tree-state--landscape', 'tree-state--empty', 'tree-state--error', 'tree-canvas--state', 'lineage-connector', 'const generationRows = [', 'v-for="row in generationRows"', ':style="generationRowStyle(row)"', 'class="tree-stage"', 'class="generation-rail"', 'tree-scroll--lineage', 'const generationRowStyle = (row)', 't01-member-node-standard.png', 't01-member-node-selected.png', 't01-state-panel.png', 't01-member-drawer.png', '/pages/tree/t03-member-profile', '/pages/tree/t04-add-relative', '/pages/tree/t06-edit-relationship', '/pages/tree/t07-member-directory', 'query.genealogyId' )) { Assert-Contains $page $required "Missing T01 contract: $required" } foreach ($forbidden in @( 'g03-create-flow-panel.png', 'g06-search-input-wide.png', 'application-status-card.png', 'generation-band--twelve', 'generation-band--thirteen', 'generation-band--fourteen' )) { if ($page -match [regex]::Escape($forbidden)) { throw "T01 must not keep opaque surface asset: $forbidden" } } foreach ($className in @('tree-canvas', 'member-node', 'member-sheet', 'tree-state-card')) { Assert-NoCssSurface $page $className } Write-Output 'T01-TREE-STATE-CONTRACT PASS'