$ErrorActionPreference = 'Stop' function ConvertFrom-Utf8Base64 { param([string]$Value) return [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($Value)) } $root = Split-Path -Parent $PSScriptRoot $pagesPath = Join-Path $root 'pages.json' $pageHeader = ConvertFrom-Utf8Base64 '6aG16Z2i57yW5Y+377ya' $expectedPages = @( 'pages/auth/a01-entry', 'pages/auth/a02-login', 'pages/auth/a04-register', 'pages/auth/a05-reset-password', 'pages/auth/a06-auth-status', 'pages/genealogy/g01-my-genealogies', 'pages/genealogy/g03-create-genealogy', 'pages/genealogy/g05-genealogy-overview', 'pages/genealogy/g06-search-genealogies', 'pages/genealogy/g08-join-application', 'pages/genealogy/g09-my-applications', 'pages/genealogy/g10-application-review', 'pages/genealogy/g11-genealogy-settings', 'pages/genealogy/g12-generation-poems', 'pages/tree/t01-tree-overview', 'pages/tree/t02-tree-states', 'pages/tree/t03-member-profile', 'pages/tree/t04-add-relative', 'pages/tree/t05-edit-member', 'pages/tree/t06-edit-relationship', 'pages/tree/t07-member-directory', 'pages/tree/t08-member-states', 'pages/family/f01-family-feed', 'pages/family/f02-publish-feed', 'pages/family/f03-feed-detail', 'pages/family/f04-article-list', 'pages/family/f05-article-detail', 'pages/family/f06-article-editor', 'pages/family/f07-album-list', 'pages/family/f08-album-detail', 'pages/family/f09-media-upload', 'pages/family/f10-video-list', 'pages/records/r01-people-list', 'pages/records/r02-person-detail', 'pages/records/r03-gift-list', 'pages/records/r04-gift-editor', 'pages/records/r05-ritual-list', 'pages/records/r06-ritual-detail', 'pages/records/r07-ritual-editor', 'pages/records/r08-growth-journal', 'pages/records/r09-life-events', 'pages/records/r10-memo-list', 'pages/records/r11-merit-records', 'pages/notification/n01-message-center', 'pages/notification/n02-message-detail', 'pages/profile/m01-profile-home', 'pages/profile/m02-edit-profile', 'pages/profile/m03-security-settings', 'pages/profile/m04-change-password', 'pages/profile/m05-change-phone', 'pages/profile/m06-help-center', 'pages/profile/m07-feedback', 'pages/profile/m08-promotion', 'pages/profile/m09-vip-orders', 'pages/profile/m10-about-settings' ) $pages = Get-Content -LiteralPath $pagesPath -Raw -Encoding utf8 | ConvertFrom-Json $actualPaths = @($pages.pages.path) if ($actualPaths.Count -ne 55) { throw "Expected 55 visual routes after the approved G02-to-G01, G04-to-G03 and G07-to-G06 state merges, found $($actualPaths.Count)." } if (Compare-Object -ReferenceObject $expectedPages -DifferenceObject $actualPaths) { throw 'pages.json routes do not match the 55-route visual delivery contract.' } foreach ($page in $expectedPages) { $pageFile = Join-Path $root ($page + '.vue') if (-not (Test-Path -LiteralPath $pageFile)) { throw "Missing planned page file: $page.vue" } $content = Get-Content -LiteralPath $pageFile -Raw -Encoding utf8 if ($content -notmatch [regex]::Escape($pageHeader)) { throw "Missing Chinese page header: $page.vue" } } Write-Output 'FULL-PAGE-VISUAL-CONTRACT PASS'