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.
Scanned 9/9/2026
Install to Claude Code
npx -y skills add yanacuti1121/Yana-AI --skill graph-dependency-resolution --agent claude-codeInstalls 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 automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/yanacuti1121-graph-dependency-resolution-yana-ai)More formats (shields.io, HTML) on the badges page.
---
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)
```
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!