| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- # Edge builder & validator for cockpit-point-locate-and-device-link
- # Written by AI agent. Runs in PowerShell (Windows sandbox safe).
- #
- # 本脚本是本 effort「关联到 map 的每个 ticket」的**原生边唯一来源**:
- # 1. 按下面的声明把 `Blocked by:` 字段行写进每张子票 —— 本仓库(本地 Markdown 后端)
- # 的原生依赖边就是这一行(面板按它渲染地图视图的依赖分层);
- # 2. 校验子票数量 == 声明数量 == map 的 `## 任务清单` 条目数;
- # 3. 校验 map 的任务清单**逐条引用**了每张子票(关联关系的落点);
- # 4. 校验每条 `Blocked by:` 引用都解析得到真实子票。
- # 任一校验对不上就非零退出,并把 expected / actual 都打印出来 —— 对不上就是失败,不要当成成功。
- param(
- [string]$EffortDir = ""
- )
- if (-not $EffortDir) {
- $EffortDir = Split-Path $PSScriptRoot -Parent
- }
- $TicketsDir = Join-Path $EffortDir "issues"
- $MapPath = Join-Path $EffortDir "map.md"
- Write-Host "Building native `Blocked by:` edges and validating ticket count..."
- Write-Host " effort: $EffortDir"
- # Declare the expected tickets and their blocking edges (map 任务清单 的 01–14).
- $ExpectedTickets = [ordered]@{
- "01-research-device-position-data-and-rendering-primitives.md" = "None"
- "02-research-device-status-effect-mechanisms.md" = "None"
- "03-locate-camera-view-contract.md" = "None"
- "04-panel-anchor-following-form.md" = "None"
- "05-device-point-layer-trigger-and-lifecycle.md" = "None"
- "06-panel-to-scene-device-correspondence.md" = "05"
- "07-device-status-effect-visual-language.md" = "02, 05"
- "08-prototype-anchor-panel-and-device-effects.md" = "04, 05, 07"
- "09-task-backend-device-coordinate-contract.md" = "01"
- "10-device-model-asset-definition.md" = "01, 05"
- "11-overlapping-device-points.md" = "05"
- "12-panel-anchor-following-spec.md" = "04"
- "13-mode-layer-visibility-matrix.md" = "05"
- "14-associated-monitoring-device-layer-spec.md" = "05"
- }
- $ExpectedTicketCount = $ExpectedTickets.Count
- $ExpectedTaskListCount = 14
- $Success = $true
- # ---- 1. build edges -------------------------------------------------------------------
- function Fix-Edge {
- param([string]$TicketPath)
- $TicketName = Split-Path $TicketPath -Leaf
- if (-not $ExpectedTickets.Contains($TicketName)) {
- Write-Host " Unknown ticket (not declared): $TicketName"
- return $false
- }
- $EdgeLine = "Blocked by: " + $ExpectedTickets[$TicketName]
- $Content = Get-Content $TicketPath -Raw
- if ($Content -match "(?m)^Blocked by: .*$") {
- $Content = $Content -replace "(?m)^Blocked by: .*$", $EdgeLine
- } else {
- $Content = $EdgeLine + "`n`n" + $Content
- }
- $Content | Set-Content $TicketPath -Encoding UTF8 -NoNewline
- Write-Host " Fixed $TicketName : $EdgeLine"
- return $true
- }
- foreach ($Ticket in Get-ChildItem $TicketsDir -Filter "*.md") {
- if (-not (Fix-Edge $Ticket.FullName)) { $Success = $false }
- }
- # ---- 2. expected vs actual ticket count -----------------------------------------------
- $ActualTicketCount = (Get-ChildItem $TicketsDir -Filter "*.md").Count
- Write-Host "expected tickets: $ExpectedTicketCount / actual: $ActualTicketCount"
- if ($ActualTicketCount -ne $ExpectedTicketCount) {
- Write-Host " Ticket count mismatch."
- $Success = $false
- }
- # ---- 3. map task list must reference every ticket, one line each -----------------------
- if (-not (Test-Path $MapPath)) {
- Write-Host " Missing map.md at $MapPath"
- $Success = $false
- } else {
- $MapContent = Get-Content $MapPath -Raw
- $TaskSection = ""
- if ($MapContent -match "(?ms)^## 任务清单\s*(.*?)^## ") {
- $TaskSection = $Matches[1]
- } else {
- Write-Host " map.md has no `## 任务清单` section"
- $Success = $false
- }
- $ListedRefs = @()
- if ($TaskSection) {
- foreach ($Line in ($TaskSection -split "`n")) {
- if ($Line -match '^\s*-\s*\[[ x]\]\s*\[\d{2}\s*:\s*[^\]]*\]\(issues/([^)]+)\)') {
- $ListedRefs += $Matches[1].Trim()
- }
- }
- }
- Write-Host "expected map task-list items: $ExpectedTaskListCount / actual: $($ListedRefs.Count)"
- if ($ListedRefs.Count -ne $ExpectedTaskListCount) {
- Write-Host " Task list item count mismatch."
- $Success = $false
- }
- foreach ($TicketName in $ExpectedTickets.Keys) {
- if ($ListedRefs -notcontains $TicketName) {
- Write-Host " Ticket not referenced from map task list: $TicketName"
- $Success = $false
- }
- }
- foreach ($Ref in $ListedRefs) {
- if (-not $ExpectedTickets.Contains($Ref)) {
- Write-Host " Map task list references an undeclared ticket: $Ref"
- $Success = $false
- }
- }
- }
- # ---- 4. every `Blocked by:` reference must resolve ------------------------------------
- foreach ($Ticket in Get-ChildItem $TicketsDir -Filter "*.md") {
- $Content = Get-Content $Ticket.FullName -Raw
- if ($Content -match "(?m)^Blocked by: (.*?)\s*$") {
- $Edge = $Matches[1]
- foreach ($Ref in ($Edge -split "," | ForEach-Object { $_.Trim() })) {
- if ($Ref -eq "None" -or $Ref -eq "") { continue }
- if (-not (Get-ChildItem $TicketsDir -Filter "$Ref-*.md")) {
- Write-Host " Invalid reference in $($Ticket.Name): $Ref"
- $Success = $false
- }
- }
- } else {
- Write-Host " Missing `Blocked by:` in $($Ticket.Name)"
- $Success = $false
- }
- }
- if ($Success) {
- Write-Host "All edges built and validated successfully. Total tickets: $ActualTicketCount (expected $ExpectedTicketCount); map task-list items: $($ExpectedTaskListCount)."
- exit 0
- } else {
- Write-Host "Validation failed. Please fix above issues."
- exit 1
- }
|