Skip to content
Back to skills

Graph Dependency Resolution

ASecurity

Graph algorithms and dependency resolution for AI agent systems. DAG construction for action gate ordering, cycle detection in skill/rule graphs, BFS/DFS traversal, semver range resolution for version conflicts, topological sort for execution ordering, and PageRank-based rule importance scoring. Sources: dagrejs/graphlib, trekhleb/javascript-algorithms, npm/node-semver, sindresorhus/toposort, asv/pagerank.

  • 10 stars
  • 0 votes
  • 0 copies
  • 5 views
  • Added September 9, 2026
ai-agentsjavascripttypescriptgojavashellnode

Works with

  • terminal
  • cli

Security analysis

A100/100

Scanned September 9, 2026

npx -y skills add yanacuti1121/Yana-AI --skill graph-dependency-resolution --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Graph Dependency Resolution?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Graph Dependency Resolution
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/yanacuti1121-graph-dependency-resolution/badge)](https://www.skillsdirectory.com/skills/yanacuti1121-graph-dependency-resolution)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: graph-dependency-resolution
description: Graph algorithms and dependency resolution for AI agent systems. DAG construction for action gate ordering, cycle detection in skill/rule graphs, BFS/DFS traversal, semver range resolution for version conflicts, topological sort for execution ordering, and PageRank-based rule importance scoring. Sources: dagrejs/graphlib, trekhleb/javascript-algorithms, npm/node-semver, sindresorhus/toposort, asv/pagerank.
origin: yana-ai — synthesized from dagrejs/graphlib, trekhleb/javascript-algorithms, npm/node-semver, sindresorhus/toposort, asv/pagerank
license: Apache-2.0
version: 1.0.0
compatibility: yana-ai >= 1.3.46
---

# /graph-dependency-resolution

## When to Use

- "Which rule/skill depends on which — what's the safe load order?"
- Cycle detection: skill A depends on B depends on A → deadlock
- Semver range conflicts: two skills require different versions of the same package
- Rank rules by reference frequency to find the most critical ones
- Find shortest path between two gates (optimal middleware chain)

## Do NOT use for

- Linear ordered lists with no dependencies (just sort by priority number)
- Single-hop lookups (use a Map)

---

## DAG: Directed Acyclic Graph (graphlib)

```typescript
import { Graph, alg } from '@dagrejs/graphlib'

// Build the YAMTAM gate dependency graph
const gateGraph = new Graph({ directed: true, multigraph: false, compound: false })

// Nodes = gates/skills/rules
gateGraph.setNode('L0', { label: 'audit-log',         tier: 0 })
gateGraph.setNode('L1', { label: 'anti-evasion',       tier: 1 })
gateGraph.setNode('L2', { label: 'shell-sanitize',     tier: 2 })
gateGraph.setNode('L3', { label: 'network-egress',     tier: 3 })
gateGraph.setNode('tool-exec', { label: 'execute',     tier: 4 })

// Edges = "must run before"
gateGraph.setEdge('L0', 'L1')    // audit-log must be set up before anti-evasion
gateGraph.setEdge('L1', 'L2')
gateGraph.setEdge('L2', 'L3')
gateGraph.setEdge('L3', 'tool-exec')

// Cycle detection — returns [] if DAG is valid, lists cycle nodes if broken
const cycles = alg.findCycles(gateGraph)
if (cycles.length > 0) {
  throw new Error(`Dependency cycle detected: ${cycles.map(c => c.join('→')).join(', ')}`)
}

// Sources = nodes with no incoming edges (entry points)
const entryPoints = gateGraph.sources()   // ['L0']
// Sinks   = nodes with no outgoing edges (terminal nodes)
const terminals   = gateGraph.sinks()     // ['tool-exec']

// Reachability: all nodes reachable from L0
const reachable = alg.dijkstra(gateGraph, 'L0')
// reachable['tool-exec'].distance === 4

// Rule: always call alg.findCycles() before executing a gate graph — cycles = deadlock
// Rule: use gateGraph.sources() to find valid entry points, never hard-code 'L0'
```

---

## Cycle Detection + BFS/DFS (javascript-algorithms)

```typescript
// BFS — shortest path in unweighted graph (level by level)
function bfs(graph: Map<string, string[]>, start: string): Map<string, number> {
  const distances = new Map<string, number>([[start, 0]])
  const queue = [start]

  while (queue.length) {
    const node = queue.shift()!
    const neighbors = graph.get(node) ?? []

    for (const n of neighbors) {
      if (!distances.has(n)) {
        distances.set(n, distances.get(node)! + 1)
        queue.push(n)
      }
    }
  }
  return distances  // distances.get(target) = hop count from start
}

// DFS — cycle detection via visited + recursion stack
function hasCycle(graph: Map<string, string[]>): boolean {
  const visited     = new Set<string>()
  const inStack     = new Set<string>()

  function dfs(node: string): boolean {
    visited.add(node)
    inStack.add(node)

    for (const neighbor of graph.get(node) ?? []) {
      if (!visited.has(neighbor) && dfs(neighbor)) return true
      if (inStack.has(neighbor)) return true  // back edge = cycle
    }

    inStack.delete(node)
    return false
  }

  for (const node of graph.keys()) {
    if (!visited.has(node) && dfs(node)) return true
  }
  return false
}

// Dijkstra — weighted shortest path (e.g., skill load latency as weight)
function dijkstra(graph: Map<string, Array<{ to: string; weight: number }>>, start: string) {
  const dist = new Map<string, number>([[start, 0]])
  const pq: Array<{ node: string; d: number }> = [{ node: start, d: 0 }]

  while (pq.length) {
    pq.sort((a, b) => a.d - b.d)
    const { node, d } = pq.shift()!
    if (d > (dist.get(node) ?? Infinity)) continue

    for (const { to, weight } of graph.get(node) ?? []) {
      const newDist = d + weight
      if (newDist < (dist.get(to) ?? Infinity)) {
        dist.set(to, newDist)
        pq.push({ node: to, d: newDist })
      }
    }
  }
  return dist
}

// Rule: BFS for "minimum hops" questions, Dijkstra for weighted latency paths
// Rule: DFS inStack set must be reset on backtrack — missing this = false cycle reports
```

---

## Topological Sort: Execution Order (toposort)

```typescript
import toposort from 'toposort'

// Sort skills/scripts so dependencies run first
// Edge [A, B] means: A must run BEFORE B
const edges: [string, string][] = [
  ['validate-manifest',  'build-release'],
  ['run-tests',          'build-release'],
  ['lint',               'run-tests'],
  ['typecheck',          'run-tests'],
  ['install-deps',       'lint'],
  ['install-deps',       'typecheck'],
]

// toposort returns nodes in reverse dep order (last dep first)
const executionOrder = toposort(edges).reverse()
// ['install-deps', 'lint', 'typecheck', 'run-tests', 'validate-manifest', 'build-release']

// With explicit node list (for isolated nodes with no edges)
const allNodes = ['install-deps', 'lint', 'typecheck', 'run-tests', 'validate-manifest', 'build-release', 'notify']
const fullOrder = toposort.array(allNodes, edges).reverse()

// Rule: always .reverse() — toposort returns reverse topological order by default
// Rule: toposort throws 'Cyclic dependency' on cycle — catch it, report the chain
try {
  toposort(edges)
} catch (e) {
  if ((e as Error).message.includes('Cyclic')) {
    throw new Error(`Build order has circular dependency: ${(e as Error).message}`)
  }
}
```

---

## Semver Range Resolution (node-semver)

```typescript
import semver from 'semver'

// Check if a version satisfies a range
semver.satisfies('1.3.45', '>=1.3.0 <2.0.0')  // true
semver.satisfies('2.0.0',  '>=1.3.0 <2.0.0')  // false

// Find the best version from a list that satisfies a range
const versions = ['1.2.0', '1.3.0', '1.3.44', '2.0.0', '2.1.0']
semver.maxSatisfying(versions, '^1.3.0')   // '1.3.44'
semver.minSatisfying(versions, '^2.0.0')   // '2.0.0'

// Resolve conflicting ranges from multiple skills
function resolveConflicts(
  requirements: Array<{ skill: string; range: string }>
): string | null {
  // Find intersection: a version satisfying ALL ranges
  const intersection = requirements
    .map(r => semver.validRange(r.range))
    .filter(Boolean) as string[]

  // Try known versions against all constraints
  return semver.maxSatisfying(
    versions,
    intersection.join(' ')  // semver treats space as AND in ranges
  )
}

// Parse and compare
const v = semver.parse('1.3.45-beta.1')!
// v.major=1, v.minor=3, v.patch=45, v.prerelease=['beta', 1]

// Coerce messy version strings (e.g., from package.json non-standard values)
semver.coerce('v2.3.4-rc1')?.version  // '2.3.4'

// Rule: use semver.validRange() before storing user-supplied ranges — throws on invalid
// Rule: space-join = AND intersection; || = OR union — never mix accidentally
```

---

## PageRank: Rule Importance Scoring

```typescript
// Power iteration PageRank — find most-referenced rules/skills
// A rule with many inbound links from other rules = high importance

function pageRank(
  graph: Map<string, string[]>,   // node → outbound neighbors
  damping   = 0.85,
  iterations = 50,
  tolerance  = 1e-6
): Map<string, number> {
  const nodes = [...graph.keys()]
  const N     = nodes.length
  let ranks   = new Map(nodes.map(n => [n, 1 / N]))

  for (let i = 0; i < iterations; i++) {
    const next = new Map<string, number>()
    let delta = 0

    for (const node of nodes) {
      // Sum contributions from all nodes pointing to this one
      let sum = 0
      for (const [src, targets] of graph) {
        if (targets.includes(node)) {
          sum += (ranks.get(src) ?? 0) / targets.length
        }
      }
      const newRank = (1 - damping) / N + damping * sum
      next.set(node, newRank)
      delta += Math.abs(newRank - (ranks.get(node) ?? 0))
    }

    ranks = next
    if (delta < tolerance) break  // converged
  }

  return new Map([...ranks.entries()].sort((a, b) => b[1] - a[1]))
}

// Example: rank YAMTAM rules by cross-reference frequency
const ruleRefs = new Map([
  ['00-meta-rule-enforcer', ['agent-middleware-law', 'verification']],
  ['agent-middleware-law',  ['shell-sanitize-law', 'network-egress-law']],
  ['shell-sanitize-law',    ['00-meta-rule-enforcer']],
  ['network-egress-law',    ['00-meta-rule-enforcer', 'agent-middleware-law']],
  ['verification',          []],
])

const importance = pageRank(ruleRefs)
// '00-meta-rule-enforcer' ranks highest — most inbound links
// Use: prune rarely-referenced rules, highlight critical ones in docs

// Rule: damping=0.85 is Google's original value — do not change without reason
// Rule: convergence check (delta < tolerance) prevents over-iteration on small graphs
```

---

## Anti-Fake-Pass Checklist

```
❌ No cycle check before executing dependency graph (deadlock on first cycle)
❌ DFS without inStack set (can't distinguish visited from in-current-path)
❌ toposort result not .reverse()'d (execution order is backwards)
❌ semver.satisfies() on un-validated range string (throws on malformed range)
❌ Space-joined semver ranges confused with OR union (space = AND, || = OR)
❌ PageRank without convergence check (fixed iteration on large graphs = wrong)
❌ graphlib.alg.findCycles() skipped before graph traversal (silent infinite loop)
```

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.