Files
will.anderson b77f537dc6 fix cross-repo path deps; remove el compiler from neuron-lang/
- neuron-runtime, neuron-store, neuron-migrate: fix path deps
  (products/engram/ → foundation/engram/engrams/, products/el/ → foundation/el/engrams/)
- neuron-rs workspace: fix axon-events/axon-protocol paths
  (../../../platform/ → ../../platform/, paths were off by one level)
- neuron-lang/compiler/ removed (el self-hosting compiler now lives in foundation/el/el-compiler/)
2026-04-29 03:27:39 -05:00

118 lines
4.7 KiB
EmacsLisp

// graph.el Graph operations subsystem.
//
// Neuron's storage backend is Engram a graph database where every entity
// (Memory, Knowledge, BacklogItem, etc.) is a node and every relationship
// is a typed edge. This subsystem exposes the raw graph API for operations
// that span entity types or need structural graph information.
//
// WHY expose graph ops directly?
// High-level tools like search_memories or retrieve_knowledge hide the graph
// structure. But some questions are inherently graph-shaped:
// "What entities are connected to this decision?"
// "How deep is the dependency chain for this backlog item?"
// "Are these two memories causally related?"
//
// Graph ops answer these questions without the overhead of entity-typed
// queries. They're the escape hatch when you need to see the whole picture.
//
// link_entities is a @manager because it mutates the graph topology.
// All reads are @accessor.
from types import {
GraphNode,
SearchResult,
NeuronError,
}
// Public API
// inspect_graph get a node and its immediate connections.
//
// entity_type: "Memory" | "Knowledge" | "BacklogItem" | "Context" | "Artifact" | ...
// entity_id: the UUID or string ID of the entity.
//
// Returns the node and its edges formatted as "relation:target_id" strings.
// Use this to understand what a node is connected to before traversing deeper.
@accessor
fn inspect_graph(entity_type: String, entity_id: String) -> Result<GraphNode, NeuronError> {
let node = native_graph_inspect(entity_type, entity_id)?
Ok(node)
}
// traverse_graph walk the graph from a starting node up to depth hops.
//
// depth=1 returns immediate neighbors only (equivalent to inspect_graph).
// depth=2 returns neighbors of neighbors, etc.
// Default depth is 2 deep enough for most dependency chains.
//
// Returns all visited nodes, not just leaves. This gives the caller the
// full subgraph around the starting node.
@accessor
fn traverse_graph(entity_id: String, depth: Int) -> Result<[GraphNode], NeuronError> {
let effective_depth = if depth == 0 { 2 } else { depth }
let nodes = native_graph_traverse(entity_id, effective_depth)?
Ok(nodes)
}
// link_entities create a typed edge between two graph nodes.
//
// relation examples: "supersedes", "depends_on", "caused_by", "related_to",
// "implements", "references", "blocks"
//
// Edges are directional: source --relation--> target.
// Use causal links for reasoning chains, dependency links for work items,
// supersedes links for knowledge evolution.
@manager
fn link_entities(
source_id: String,
target_id: String,
relation: String,
) -> Result<GraphNode, NeuronError> {
native_graph_link(source_id, target_id, relation)?
native_emit("graph.linked", {"source": source_id, "target": target_id, "relation": relation})
// Return the source node with its updated edges.
let source_node = native_graph_inspect("", source_id)?
Ok(source_node)
}
// search_graph semantic search across all node types in the graph.
//
// Unlike type-specific search (search_memories, search_knowledge), this
// searches the entire graph regardless of entity type. Useful for
// cross-cutting queries: "what's related to authentication across all
// memories, knowledge, and backlog items?"
@accessor
fn search_graph(query: String) -> Result<[SearchResult], NeuronError> {
let results = native_semantic_search(query, "", 20)?
Ok(results)
}
// rebuild_graph trigger a full graph index rebuild.
//
// Used after bulk imports or when the semantic index drifts out of sync.
// This is an expensive operation avoid in normal sessions.
// It's sealed because it modifies the graph infrastructure, not just data.
@manager
fn rebuild_graph() -> Result<String, NeuronError> {
sealed {
native_emit("graph.rebuild_requested", {})
Ok("graph rebuild initiated")
}
}
// pin_node mark a graph node as pinned (exempt from compaction).
//
// Pinned nodes survive graph compaction even when their activation scores
// drop below the compaction threshold. Use for foundational knowledge nodes
// that must never be evicted.
@manager
fn pin_node(entity_id: String, entity_type: String) -> Result<GraphNode, NeuronError> {
sealed {
// Pinning is implemented as a special self-referential "pinned" edge.
native_graph_link(entity_id, entity_id, "pinned")?
native_emit("graph.node_pinned", {"id": entity_id, "type": entity_type})
let node = native_graph_inspect(entity_type, entity_id)?
Ok(node)
}
}