Public/Get-Edge.ps1
|
# Copyright (c) 2026 Jeffrey Snover. All rights reserved. # Licensed under the MIT License. See LICENSE file in the project root. function Get-Edge { <# .SYNOPSIS Lists and filters edges in the taxonomy graph. .DESCRIPTION Reads edges.json and returns edges matching the specified criteria. All string filters support wildcards. Multiple filters are AND-combined. With no parameters, returns all edges sorted by confidence descending. .PARAMETER Source Wildcard pattern matched against the source node ID. .PARAMETER Target Wildcard pattern matched against the target node ID. .PARAMETER NodeId Wildcard pattern matched against either source or target node ID. Useful for finding all edges connected to a node regardless of direction. .PARAMETER Type Wildcard pattern matched against the edge type (e.g., SUPPORTS, 'TENS*'). .PARAMETER Status Edge approval status: proposed, approved, or rejected. .PARAMETER MinConfidence Minimum confidence threshold (0.0-1.0). Default: 0.0. .PARAMETER MaxConfidence Maximum confidence threshold (0.0-1.0). Default: 1.0. .PARAMETER Bidirectional When specified, returns only bidirectional ($true) or directional ($false) edges. .PARAMETER CrossPov When specified, returns only cross-POV ($true) or same-POV ($false) edges. .PARAMETER Strength Wildcard pattern matched against the edge strength (strong, moderate, weak). .PARAMETER Model Wildcard pattern matched against the model that discovered the edge. .PARAMETER Rationale Wildcard pattern matched against the edge rationale text. .PARAMETER DiscoveredAfter Returns only edges discovered on or after this date (yyyy-MM-dd). .PARAMETER DiscoveredBefore Returns only edges discovered on or before this date (yyyy-MM-dd). .PARAMETER SourcePov Filter to edges whose source node belongs to this POV. .PARAMETER TargetPov Filter to edges whose target node belongs to this POV. .PARAMETER Index Return a specific edge by its zero-based index in edges.json. .PARAMETER First Return only the first N matching edges. .PARAMETER RepoRoot Path to the repository root. .EXAMPLE Get-Edge # Returns all edges. .EXAMPLE Get-Edge -Source 'acc-goals-*' # All edges from accelerationist goal nodes. .EXAMPLE Get-Edge -NodeId 'saf-goals-001' # All edges connected to saf-goals-001 (source or target). .EXAMPLE Get-Edge -Type CONTRADICTS -Status approved # Approved contradictions. .EXAMPLE Get-Edge -CrossPov -MinConfidence 0.9 # High-confidence cross-POV edges. .EXAMPLE Get-Edge -Rationale '*existential*' -Type 'TENS*' # Tension edges mentioning existential risk. .EXAMPLE Get-Edge -SourcePov safetyist -TargetPov accelerationist -Status approved # Approved edges from safetyist to accelerationist nodes. .EXAMPLE Get-Edge -Index 42 # Return edge at index 42. .EXAMPLE Get-Edge -Type SUPPORTS -First 10 # First 10 SUPPORTS edges by confidence. #> [CmdletBinding()] param( [Parameter(Position = 0)] [string]$Source, [string]$Target, [string]$NodeId, [string]$Type, [ValidateSet('proposed', 'approved', 'rejected', '')] [string]$Status, [ValidateRange(0.0, 1.0)] [double]$MinConfidence = 0.0, [ValidateRange(0.0, 1.0)] [double]$MaxConfidence = 1.0, [Nullable[bool]]$Bidirectional, [Nullable[bool]]$CrossPov, [string]$Strength, [string]$Model, [string]$Rationale, [string]$DiscoveredAfter, [string]$DiscoveredBefore, [ValidateSet('accelerationist', 'safetyist', 'skeptic', 'cross-cutting', '')] [string]$SourcePov, [ValidateSet('accelerationist', 'safetyist', 'skeptic', 'cross-cutting', '')] [string]$TargetPov, [int]$Index = -1, [int]$First = 0, [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 # ------------------------------------------------------------------ # Index mode — fast return of a single edge # ------------------------------------------------------------------ if ($Index -ge 0) { if ($Index -ge $EdgesData.edges.Count) { Write-Fail "Edge index $Index out of range (0-$($EdgesData.edges.Count - 1))." return } $E = $EdgesData.edges[$Index] return [PSCustomObject]@{ PSTypeName = 'AITriad.Edge' Index = $Index 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 } } # ------------------------------------------------------------------ # Build node→POV map (only when POV-based filters are active) # ------------------------------------------------------------------ $NodePovMap = $null if ($SourcePov -or $TargetPov -or $CrossPov -ne $null) { $NodePovMap = @{} foreach ($PovKey in @('accelerationist', 'safetyist', 'skeptic', 'cross-cutting')) { $FilePath = Join-Path $TaxDir "$PovKey.json" if (-not (Test-Path $FilePath)) { continue } $FileData = Get-Content -Raw -Path $FilePath | ConvertFrom-Json foreach ($Node in $FileData.nodes) { $NodePovMap[$Node.id] = $PovKey } } } # ------------------------------------------------------------------ # Filter edges # ------------------------------------------------------------------ $Results = [System.Collections.Generic.List[PSObject]]::new() $EdgeCount = $EdgesData.edges.Count for ($i = 0; $i -lt $EdgeCount; $i++) { $E = $EdgesData.edges[$i] # Source/Target/NodeId wildcard filters if ($Source -and $E.source -notlike $Source) { continue } if ($Target -and $E.target -notlike $Target) { continue } if ($NodeId -and ($E.source -notlike $NodeId) -and ($E.target -notlike $NodeId)) { continue } # Type wildcard if ($Type -and $E.type -notlike $Type) { continue } # Status exact if ($Status -and $E.status -ne $Status) { continue } # Confidence range if ($E.confidence -lt $MinConfidence) { continue } if ($E.confidence -gt $MaxConfidence) { continue } # Bidirectional filter if ($null -ne $Bidirectional) { $IsBidir = [bool]$E.bidirectional if ($IsBidir -ne $Bidirectional) { continue } } # Strength wildcard if ($Strength) { $EStrength = if ($E.PSObject.Properties['strength']) { $E.strength } else { '' } if ($EStrength -notlike $Strength) { continue } } # Model wildcard if ($Model -and $E.model -notlike $Model) { continue } # Rationale wildcard if ($Rationale -and $E.rationale -notlike $Rationale) { continue } # Date range filters if ($DiscoveredAfter -and $E.discovered_at -lt $DiscoveredAfter) { continue } if ($DiscoveredBefore -and $E.discovered_at -gt $DiscoveredBefore) { continue } # POV-based filters if ($NodePovMap) { $SPov = if ($NodePovMap.ContainsKey($E.source)) { $NodePovMap[$E.source] } else { 'unknown' } $TPov = if ($NodePovMap.ContainsKey($E.target)) { $NodePovMap[$E.target] } else { 'unknown' } if ($SourcePov -and $SPov -ne $SourcePov) { continue } if ($TargetPov -and $TPov -ne $TargetPov) { continue } if ($null -ne $CrossPov) { $IsCross = $SPov -ne $TPov if ($IsCross -ne $CrossPov) { continue } } } $Results.Add([PSCustomObject]@{ PSTypeName = 'AITriad.Edge' Index = $i 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 }) if ($First -gt 0 -and $Results.Count -ge $First) { break } } if ($Results.Count -eq 0) { Write-Warning 'No edges matched the specified filters.' return } $Results | Sort-Object Confidence -Descending } |