$ErrorActionPreference = 'Stop' $root = Split-Path -Parent $PSScriptRoot $tree = Get-Content -LiteralPath (Join-Path $root 'pages/tree/t01-tree-overview.vue') -Raw -Encoding UTF8 $pedigree = Get-Content -LiteralPath (Join-Path $root 'pages/tree/t02-pedigree-overview.vue') -Raw -Encoding UTF8 $routes = Get-Content -LiteralPath (Join-Path $root 'utils/navigation-routes.js') -Raw -Encoding UTF8 foreach ($required in @( 'class="member-node__surface"', 'const memberActionPanelVisible = ref(false)', 'const memberActions = Object.freeze([', 'const openMemberAction = (action) =>', 'class="member-action-panel-layer"', 'class="member-action-panel"', 'closeMemberActionPanel', 'member-action-grid', 'unavailableActionVisible', 'BIND_INVITE' )) { if (-not $tree.Contains($required)) { throw "T01 tree member action panel missing: $required" } } foreach ($actionKey in @( 'VIEW_PROFILE', 'ADD_FATHER', 'ADD_MOTHER', 'ADD_SPOUSE', 'ADD_SIBLING', 'ADJUST_RANK', 'ADD_SON', 'ADD_DAUGHTER', 'BIND_INVITE', 'EDIT_PROFILE' )) { if (-not $tree.Contains(('key: "' + $actionKey + '"'))) { throw "T01 tree member action missing: $actionKey" } } foreach ($asset in @( 'static/assets/modules/tree/transparent/t01-action-panel-paper-v3.png', 'static/assets/modules/tree/transparent/t01-action-arrow-v2.png', 'static/assets/modules/tree/transparent/t01-action-close-v2.png', 'static/assets/modules/tree/transparent/t01-management-marker-v2.png', 'static/assets/modules/tree/transparent/t01-member-medallion-v2.png', 'static/assets/modules/tree/transparent/t01-relation-marker-v2.png', 'static/assets/modules/tree/transparent/t01-relation-button-frame-v4.png', 'static/assets/modules/tree/transparent/t01-relation-map-v5.png' )) { if (-not $tree.Contains(('/' + $asset))) { throw "T01 人物操作面板缺少资产引用:$asset" } if (-not (Test-Path -LiteralPath (Join-Path $root $asset))) { throw "T01 人物操作面板资产不存在:$asset" } } foreach ($required in @( 'const openMemberProfile = (member) =>', 'const openMemberDetail = async (member) =>', 'appApi.getPerson(genealogyId.value, member.id', 'detailVisible', 'class="member-node__name"', 'class="member-node__copy"' )) { if (-not $pedigree.Contains($required)) { throw "T02 pedigree member interaction missing: $required" } } foreach ($required in @( 'optionalParams: ["personId", "mode", "relationType"]', 'allowedSources: ["T01", "T02", "T03"]', 'optionalParams: ["mode"]' )) { if (-not $routes.Contains($required)) { throw "Tree route contract missing: $required" } } Write-Output 'T01-PERSON-ACTION-PANEL-CONTRACT PASS'