Public/Set-Edge.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function Set-Edge { <# .SYNOPSIS Modifies properties of one or more edges in the taxonomy graph. .DESCRIPTION Updates edge properties in edges.json. Accepts edges by index or from the pipeline (output of Get-Edge). Supports changing status, confidence, strength, notes, and type. Uses ShouldProcess for safety. When piping from Get-Edge, each edge's Index property is used to locate the edge in edges.json. .PARAMETER Index Zero-based index of the edge to modify. Mutually exclusive with pipeline input. .PARAMETER InputObject Edge objects from Get-Edge (pipeline input). Each must have an Index property. .PARAMETER Status New approval status: proposed, approved, or rejected. .PARAMETER Confidence New confidence value (0.0-1.0). .PARAMETER Strength New strength value: strong, moderate, or weak. .PARAMETER Notes New notes text. Use empty string to clear. .PARAMETER Type New edge type (e.g., SUPPORTS, CONTRADICTS). .PARAMETER RepoRoot Path to the repository root. .PARAMETER PassThru Return the updated edge objects. .EXAMPLE Set-Edge -Index 42 -Status approved # Approve edge 42. .EXAMPLE Set-Edge -Index 10 -Notes 'Reviewed 2026-03-08 — strong relationship' # Add a note to edge 10. .EXAMPLE Get-Edge -Type CONTRADICTS -Status proposed | Set-Edge -Status approved # Approve all proposed contradictions. .EXAMPLE Get-Edge -NodeId 'acc-goals-001' -MinConfidence 0.9 | Set-Edge -Status approved # Approve high-confidence edges for a specific node. .EXAMPLE Get-Edge -Source 'saf-*' -Status proposed -MinConfidence 0.85 | Set-Edge -Status approved -PassThru # Bulk approve and see results. .EXAMPLE Set-Edge -Index 7 -Confidence 0.95 -Strength strong # Update confidence and strength on a single edge. #> [CmdletBinding(SupportsShouldProcess, DefaultParameterSetName = 'ByIndex')] param( [Parameter(Mandatory, ParameterSetName = 'ByIndex', Position = 0)] [int]$Index, [Parameter(Mandatory, ParameterSetName = 'ByPipeline', ValueFromPipeline)] [PSObject]$InputObject, [ValidateSet('proposed', 'approved', 'rejected')] [string]$Status, [ValidateRange(0.0, 1.0)] [double]$Confidence = -1, [ValidateSet('strong', 'moderate', 'weak', '')] [string]$Strength, [AllowEmptyString()] [string]$Notes, [string]$Type, [switch]$PassThru, [string]$RepoRoot = $script:RepoRoot ) begin { 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 $MaxIndex = $EdgesData.edges.Count - 1 # Track whether any modifications were made $Modified = $false $ModifiedCount = 0 $Indices = [System.Collections.Generic.List[int]]::new() # Validate that at least one property is being set $HasChange = $Status -or $Confidence -ge 0 -or $PSBoundParameters.ContainsKey('Strength') -or $PSBoundParameters.ContainsKey('Notes') -or $Type if (-not $HasChange) { Write-Fail 'Specify at least one property to change (-Status, -Confidence, -Strength, -Notes, -Type).' return } } process { # Determine edge index $EdgeIndex = if ($PSCmdlet.ParameterSetName -eq 'ByPipeline') { if (-not $InputObject.PSObject.Properties['Index']) { Write-Fail 'Pipeline object has no Index property. Pipe from Get-Edge.' return } $InputObject.Index } else { $Index } if ($EdgeIndex -lt 0 -or $EdgeIndex -gt $MaxIndex) { Write-Fail "Edge index $EdgeIndex out of range (0-$MaxIndex)." return } $E = $EdgesData.edges[$EdgeIndex] $Desc = "$($E.source) --[$($E.type)]--> $($E.target)" # Build change description for ShouldProcess $Changes = @() if ($Status) { $Changes += "status=$Status" } if ($Confidence -ge 0) { $Changes += "confidence=$Confidence" } if ($PSBoundParameters.ContainsKey('Strength')) { $Changes += "strength=$Strength" } if ($PSBoundParameters.ContainsKey('Notes')) { $Changes += "notes=$(if ($Notes) { $Notes.Substring(0, [Math]::Min(40, $Notes.Length)) } else { '(clear)' })" } if ($Type) { $Changes += "type=$Type" } $ChangeStr = $Changes -join ', ' if ($PSCmdlet.ShouldProcess("Edge $EdgeIndex ($Desc)", "Set $ChangeStr")) { if ($Status) { $EdgesData.edges[$EdgeIndex].status = $Status } if ($Confidence -ge 0) { $EdgesData.edges[$EdgeIndex].confidence = $Confidence } if ($PSBoundParameters.ContainsKey('Strength')) { if ($Strength) { if (-not $EdgesData.edges[$EdgeIndex].PSObject.Properties['strength']) { $EdgesData.edges[$EdgeIndex] | Add-Member -NotePropertyName 'strength' -NotePropertyValue $Strength } else { $EdgesData.edges[$EdgeIndex].strength = $Strength } } elseif ($EdgesData.edges[$EdgeIndex].PSObject.Properties['strength']) { $EdgesData.edges[$EdgeIndex].PSObject.Properties.Remove('strength') } } if ($PSBoundParameters.ContainsKey('Notes')) { if ($Notes) { if (-not $EdgesData.edges[$EdgeIndex].PSObject.Properties['notes']) { $EdgesData.edges[$EdgeIndex] | Add-Member -NotePropertyName 'notes' -NotePropertyValue $Notes } else { $EdgesData.edges[$EdgeIndex].notes = $Notes } } elseif ($EdgesData.edges[$EdgeIndex].PSObject.Properties['notes']) { $EdgesData.edges[$EdgeIndex].PSObject.Properties.Remove('notes') } } if ($Type) { $EdgesData.edges[$EdgeIndex].type = $Type } $Modified = $true $ModifiedCount++ $Indices.Add($EdgeIndex) } } end { if ($Modified) { $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 "Updated $ModifiedCount edge(s) in $EdgesPath" } catch { Write-Fail "Failed to write edges.json — $($_.Exception.Message)" Write-Info "Changes were made in memory but NOT saved to disk. Try again or check file permissions." throw } } if ($PassThru -and $Indices.Count -gt 0) { foreach ($Idx in $Indices) { $E = $EdgesData.edges[$Idx] [PSCustomObject]@{ PSTypeName = 'AITriad.Edge' Index = $Idx Source = $E.source Target = $E.target Type = $E.type Bidirectional = [bool]$E.bidirectional Confidence = $E.confidence Status = $E.status Strength = if ($E.PSObject.Properties['strength']) { $E.strength } else { $null } Rationale = $E.rationale Notes = if ($E.PSObject.Properties['notes']) { $E.notes } else { $null } DiscoveredAt = $E.discovered_at Model = $E.model } } } } } |