Public/Approve-Edge.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function Approve-Edge { <# .SYNOPSIS Approves or rejects proposed edges in the taxonomy graph. .DESCRIPTION Changes the status of edges in edges.json from 'proposed' to 'approved' or 'rejected'. Can operate on individual edges by index, or interactively review all proposed edges. .PARAMETER Index Zero-based index of the edge in edges.json to approve/reject. Use Get-GraphNode or review edges.json to find the index. .PARAMETER Approve Set the edge status to 'approved'. .PARAMETER Reject Set the edge status to 'rejected'. .PARAMETER Interactive Interactively review all proposed edges one by one. .PARAMETER BulkApprove Approve all proposed edges that meet the MinConfidence threshold. .PARAMETER MinConfidence Minimum confidence score for bulk approval (0.0-1.0). Default: 0.8. Only used with -BulkApprove. .PARAMETER RepoRoot Path to the repository root. .EXAMPLE Approve-Edge -Index 0 -Approve .EXAMPLE Approve-Edge -Index 5 -Reject .EXAMPLE Approve-Edge -Interactive .EXAMPLE Approve-Edge -BulkApprove -MinConfidence 0.8 .EXAMPLE Approve-Edge -BulkApprove -MinConfidence 0.9 #> [CmdletBinding(SupportsShouldProcess)] param( [int]$Index = -1, [switch]$Approve, [switch]$Reject, [switch]$Interactive, [switch]$BulkApprove, [ValidateRange(0.0, 1.0)] [double]$MinConfidence = 0.8, [string]$RepoRoot = $script:RepoRoot ) Set-StrictMode -Version Latest $TaxDir = Get-TaxonomyDir $EdgesPath = Join-Path $TaxDir 'edges.json' if (-not (Test-Path $EdgesPath)) { Write-Fail 'No edges.json found. Run Invoke-EdgeDiscovery first.' return } $EdgesData = Get-Content -Raw -Path $EdgesPath | ConvertFrom-Json if ($Interactive) { $Proposed = @() for ($i = 0; $i -lt $EdgesData.edges.Count; $i++) { if ($EdgesData.edges[$i].status -eq 'proposed') { $Proposed += [PSCustomObject]@{ Index = $i; Edge = $EdgesData.edges[$i] } } } if ($Proposed.Count -eq 0) { Write-OK 'No proposed edges to review.' return } Write-Host '' Write-Host "=== Interactive Edge Review: $($Proposed.Count) proposed edge(s) ===" -ForegroundColor Cyan Write-Host '' $ApprovedCount = 0 $RejectedCount = 0 $SkippedCount = 0 foreach ($Item in $Proposed) { $E = $Item.Edge Write-Host "[$($Item.Index)] " -NoNewline -ForegroundColor DarkGray Write-Host "$($E.source)" -NoNewline -ForegroundColor Green Write-Host " --[$($E.type)]--> " -NoNewline -ForegroundColor Yellow Write-Host "$($E.target)" -ForegroundColor Green Write-Host " Confidence: $($E.confidence) Strength: $(if ($E.PSObject.Properties['strength']) { $E.strength } else { 'n/a' })" -ForegroundColor DarkGray Write-Host " Rationale: $($E.rationale)" -ForegroundColor White if ($E.PSObject.Properties['notes'] -and $E.notes) { Write-Host " Notes: $($E.notes)" -ForegroundColor DarkGray } Write-Host '' $Choice = Read-Host ' (a)pprove / (r)eject / (s)kip / (q)uit' switch ($Choice.ToLower()) { 'a' { $EdgesData.edges[$Item.Index].status = 'approved' $ApprovedCount++ Write-OK 'Approved' } 'r' { $EdgesData.edges[$Item.Index].status = 'rejected' $RejectedCount++ Write-OK 'Rejected' } 'q' { Write-Info 'Quitting review.' break } default { $SkippedCount++ Write-Info 'Skipped' } } Write-Host '' if ($Choice.ToLower() -eq 'q') { break } } Write-Host '' Write-Host "Review complete: $ApprovedCount approved, $RejectedCount rejected, $SkippedCount skipped" -ForegroundColor Cyan } elseif ($BulkApprove) { $Candidates = @() for ($i = 0; $i -lt $EdgesData.edges.Count; $i++) { $E = $EdgesData.edges[$i] if ($E.status -eq 'proposed' -and $E.confidence -ge $MinConfidence) { $Candidates += [PSCustomObject]@{ Index = $i; Edge = $E } } } if ($Candidates.Count -eq 0) { Write-OK "No proposed edges with confidence >= $MinConfidence" return } if ($PSCmdlet.ShouldProcess("$($Candidates.Count) edges with confidence >= $MinConfidence", 'Bulk approve')) { foreach ($Item in $Candidates) { $EdgesData.edges[$Item.Index].status = 'approved' } Write-OK "Bulk approved $($Candidates.Count) edges (confidence >= $MinConfidence)" } } elseif ($Index -ge 0) { if ($Index -ge $EdgesData.edges.Count) { Write-Fail "Edge index $Index out of range (0-$($EdgesData.edges.Count - 1))" return } if ($Approve -and $Reject) { Write-Fail 'Specify either -Approve or -Reject, not both.' return } if (-not $Approve -and -not $Reject) { Write-Fail 'Specify either -Approve or -Reject.' return } $NewStatus = if ($Approve) { 'approved' } else { 'rejected' } $E = $EdgesData.edges[$Index] if ($PSCmdlet.ShouldProcess("Edge $Index ($($E.source) → $($E.target))", "Set status to '$NewStatus'")) { $EdgesData.edges[$Index].status = $NewStatus Write-OK "Edge $Index ($($E.source) --[$($E.type)]--> $($E.target)): $NewStatus" } } else { Write-Fail 'Specify -Index with -Approve/-Reject, -Interactive, or -BulkApprove.' return } # Save if ($PSCmdlet.ShouldProcess($EdgesPath, 'Write updated edges file')) { $EdgesData.last_modified = (Get-Date).ToString('yyyy-MM-dd') $Json = $EdgesData | ConvertTo-Json -Depth 20 try { Set-Content -Path $EdgesPath -Value $Json -Encoding UTF8 Write-OK "Saved $EdgesPath" } catch { Write-Fail "Failed to write edges.json — $($_.Exception.Message)" Write-Info "Approval changes were NOT saved. Check file permissions and try again." throw } } } |