$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', 'border-radius')) { if ($Content -match "(?s)\\.$ClassName\\s*\\{[^}]*\\b$property\\s*:") { throw "Application flow must not construct .$ClassName with CSS $property" } } } $root = Split-Path -Parent $PSScriptRoot $g08 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g08-join-application.vue') -Raw -Encoding utf8 $g09 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g09-my-applications.vue') -Raw -Encoding utf8 $g10 = Get-Content -LiteralPath (Join-Path $root 'pages/genealogy/g10-application-review.vue') -Raw -Encoding utf8 $catalog = Get-Content -LiteralPath (Join-Path $root 'data/page-catalog.js') -Raw -Encoding utf8 foreach ($page in @($g08, $g09, $g10)) { if ($page -match '' 'G08-G10 must use the accepted shared genealogy background' if ($page -match "@/utils/api\.js|\bappApi\b") { throw 'G08-G10 design phase must not connect the API layer' } } foreach ($required in @( 'const joinState = ref("form")', 'join-state--form', 'join-state--success', 'join-state--error', 'g01-empty-panel-frame.png', 'g-form-field-frame.png', 'a01-scroll-primary-v3.png', 'const genealogyPreview', 'query.genealogyId', 'const source = ref("search")', 'const sourceContract = computed(() =>', 'const fieldErrors = reactive({', '/pages/genealogy/g09-my-applications', '/pages/genealogy/g01-my-genealogies?genealogyId=' )) { Assert-Contains $g08 $required "Missing G08 join contract: $required" } foreach ($nativeUi in @('uni.showToast', 'uni.showModal', 'uni.showLoading', 'uni.showActionSheet')) { if ($g08 -match [regex]::Escape($nativeUi)) { throw "G08 must not use native UniApp UI: $nativeUi" } } if ($g08 -notmatch '(?s)\.join-field-error\s*\{[^}]*font-size:\s*24rpx;[^}]*line-height:\s*34rpx;') { throw 'G08 inline validation must remain readable over the illustrated panel' } foreach ($required in @( 'const applicationState = ref("loading")', 'application-state--list', 'application-state--empty', 'application-state--error', 'list-slip-frame.png', '"PENDING"', '"APPROVED"', '"REJECTED"', '"WITHDRAWN"', 'const applicationSamples', 'const actionFor = (item) =>', 'const withdrawTarget = ref(null)', '