Introduction
m1nd is a local MCP runtime for coding agents. It ingests a repository into a graph-backed operational model so an agent can ask for structure, impact, connected context, continuity, and likely risk instead of reconstructing the repo from raw files every time.
The current public shape of the product is not just “graph search.” It is a guided runtime with:
- graph-grounded retrieval and impact analysis
proof_stateon the main structural flowsnext_suggested_tool,next_suggested_target, andnext_step_hint- actionable continuity through
trail_resume - observable multi-file writes through
apply_batch - recovery loops that teach the next valid move when a tool is used badly
m1nd ships as an MCP server, runs locally, and works with any MCP-compatible client over stdio. The exported schema exposes the live MCP tool surface for your current build; use tools/list for the exact count.
The Problem
Most agent loops still waste time in the same place: navigation.
An LLM can reason about a file once it has the file. The expensive part is getting the right file, the right neighbors, and enough proof to act without reopening half the repo.
Without a structural layer, the loop usually looks like this:
- grep for a symbol or phrase
- open a file
- grep for callers, callees, or related paths
- open more files
- repeat until the subsystem shape becomes clear
That cost shows up as:
- more file reads than necessary
- more token burn on repo reconstruction
- weaker stopping rules during triage
- more false starts before editing
- more friction resuming prior investigations
What m1nd Changes
m1nd keeps the graph local and lets an agent ask for structure directly:
tracemaps stacktraces to likely suspectsimpactinspects blast radius before editsseekandactivatefind intent and connected structuredocument_resolve,document_bindings, anddocument_driftconnect docs/specs to likely code targets and surface stale linksdocument_provider_healthandauto_ingest_*expose the local-first document runtimevalidate_planandsurgical_context_v2prepare safer multi-file changestrail_resumerestores investigations with next-focus and next-tool hintsapply_batchexposes progress, phases, and final handoff signals
The result is less context churn and better decision quality per step.
Documents And Knowledge Artifacts
The public shape of m1nd is no longer just code plus optional markdown memory.
The merged universal lane can ingest and operationalize:
- markdown notes
- HTML/wiki pages
- office documents
- scholarly PDFs
- structured standards and citation corpora
When a document enters through the universal lane, m1nd can preserve canonical local artifacts, bind that document to likely code, and surface document/code drift when the implementation moves faster than the docs.
Current benchmark truth from the recorded warm-graph corpus:
10518 -> 5182aggregate token proxy50.73%aggregate reduction14 -> 0false starts39guided follow-throughs12successful recovery loops
Not every scenario is a token win. Some wins are continuity, recovery, or execution clarity. That is part of the product truth too.
Core Runtime Ideas
Graph-grounded retrieval
The graph is still the foundation. Activation, semantic retrieval, path search, temporal history, and blast-radius analysis all sit on top of a shared structural model rather than a stateless grep loop.
Guided handoff
Several high-value tools now return more than raw results. They can expose:
proof_statenext_suggested_toolnext_suggested_targetnext_step_hint
That turns the server from a catalog of answers into a layer that helps the agent decide what to do next.
Continuity
trail_resume is no longer just bookmark restore. It can return compact resume hints, reactivated nodes, the next focus node, the next open question, and the likely next tool. This is one of the main reasons the benchmark corpus now records fewer false starts.
Observable execution
apply_batch is now an observable write surface:
status_messageproof_state- lifecycle phases such as
validate,write,reingest,verify, anddone - coarse progress fields like
progress_pct - structured
progress_events - live SSE progress in serve mode
Recovery loops
Common failures no longer have to be dead ends. Many invalid calls now return hints, examples, and a suggested next step so the agent can repair the call instead of rediscovering the workflow from scratch.
Who This Is For
- agent builders who want a local structural layer for navigation and edit prep
- MCP client users who want better triage, continuity, and connected context
- multi-agent systems that need shared graph truth without shipping code to an API
- teams that want safer workflow around stacktrace triage, blast radius, and multi-file changes
m1nd is not a compiler, debugger, or test runner replacement. It is best when the real bottleneck is structural understanding and repo navigation.
How To Read This Wiki
Architecture explains how the core crates and auxiliary bridge surfaces fit together and how the MCP server turns graph truth into agent-facing runtime behavior.
Concepts covers the underlying graph ideas such as activation, plasticity, and structural holes.
API Reference documents the current MCP surface, including underscore-based canonical tool names, guided outputs, and transport behavior.
Tutorials walks through the main workflows from first ingest to connected edit prep.
The Benchmarks page is the current product-truth layer for token proxy, false starts, guided follow-through, and recovery loops. The Changelog tracks the release history from v0.6.x through v0.8.0 and onward.
System Architecture Overview
m1nd is a local MCP runtime for coding agents. Internally it is still built on a graph-and-analysis system, but the public runtime is organized around an operational contract:
- graph-grounded retrieval and ranking
- proof-aware structural flows
- next-step handoff on high-value tools
- continuity through trails and perspectives
- observable multi-file writes through
apply_batch - repair-friendly errors when a tool call goes wrong
The workspace exposes the live MCP tool surface over JSON-RPC stdio, with an HTTP/UI surface in the default build. Use tools/list for the exact count in your current build.
Four-Crate Workspace
The system is organized as a Cargo workspace with three core crates plus one auxiliary bridge crate:
[workspace]
members = ["m1nd-core", "m1nd-ingest", "m1nd-mcp", "m1nd-openclaw"]
resolver = "2"
[workspace.dependencies]
thiserror = "2"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
smallvec = { version = "1.13", features = ["serde"] }
parking_lot = "0.12"
rayon = "1.10"
static_assertions = "1"
| Crate | Role | Key Dependency |
|---|---|---|
| m1nd-core | Graph engine, activation, plasticity, XLR, resonance, temporal, semantic | parking_lot, smallvec, static_assertions |
| m1nd-ingest | File walking, language-specific extraction, reference resolution, diff | rayon, walkdir, regex |
| m1nd-mcp | JSON-RPC transport, tool dispatch, session management, persistence | tokio, serde_json |
| m1nd-openclaw | Auxiliary OpenClaw bridge and transport integration | m1nd-core, m1nd-mcp |
Dependencies flow strictly downward: m1nd-mcp depends on both m1nd-core and m1nd-ingest; m1nd-ingest depends on m1nd-core; m1nd-core has no internal crate dependencies.
Data Flow
graph TD
subgraph "m1nd-mcp (Transport)"
STDIO["JSON-RPC stdio<br/>dual: framed + line"]
DISPATCH["Tool Dispatch<br/>live tool surface"]
SESSION["SessionState<br/>SharedGraph + Engines"]
PERSIST["Auto-Persist<br/>every 50 queries"]
end
subgraph "m1nd-ingest (Extraction)"
WALK["DirectoryWalker<br/>walkdir + binary detect"]
EXTRACT["Parallel Extraction<br/>rayon threadpool"]
RESOLVE["ReferenceResolver<br/>proximity disambiguation"]
DIFF["GraphDiff<br/>incremental updates"]
MEMORY["MemoryAdapter<br/>markdown → graph"]
UNIVERSAL["Universal + Document Adapters<br/>canonical docs → graph"]
end
subgraph "m1nd-core (Engine)"
GRAPH["CSR Graph<br/>forward + reverse"]
NODES["NodeStorage (SoA)<br/>hot|warm|cold paths"]
ACT["Activation Engine<br/>hybrid heap/wavefront"]
SEM["Semantic Engine<br/>trigram TF-IDF + PPMI"]
TEMP["Temporal Engine<br/>co-change + decay"]
PLAST["Plasticity Engine<br/>Hebbian LTP/LTD"]
XLR["XLR Engine<br/>spectral noise cancel"]
RES["Resonance Engine<br/>standing waves"]
SNAP["Snapshot<br/>atomic JSON persist"]
end
STDIO --> DISPATCH
DISPATCH --> SESSION
SESSION --> PERSIST
PERSIST --> SNAP
SESSION -->|ingest| WALK
WALK --> EXTRACT
EXTRACT --> RESOLVE
RESOLVE --> GRAPH
UNIVERSAL --> GRAPH
DIFF -->|incremental| GRAPH
SESSION -->|activate| ACT
SESSION -->|impact/predict| TEMP
SESSION -->|learn| PLAST
SESSION -->|activate+xlr| XLR
SESSION -->|resonate| RES
ACT --> GRAPH
ACT --> SEM
ACT --> NODES
SEM --> GRAPH
TEMP --> GRAPH
PLAST --> GRAPH
XLR --> GRAPH
RES --> GRAPH
GRAPH --> NODES
Request Lifecycle
A typical structural workflow now has two layers: graph execution and agent handoff.
Example: activate
- Transport: JSON-RPC message arrives on stdin (either Content-Length framed or raw line JSON).
- Dispatch:
McpServer.serve()parses the JSON-RPC request, matches the tool name, extracts parameters. - Session: The tool handler acquires a read lock on
SharedGraph(Arc<parking_lot::RwLock<Graph>>). - Seed Finding:
SeedFinderlocates matching nodes via a 5-level cascade: exact label, prefix, substring, tag, fuzzy trigram. - Activation:
HybridEngineauto-selects heap or wavefront strategy based on seed ratio and average degree. - Dimensions: Four dimensions run: Structural (BFS/heap propagation), Semantic (trigram TF-IDF + co-occurrence PPMI), Temporal (decay + velocity), Causal (forward/backward with discount).
- Merge:
merge_dimensions()combines results with adaptive weights[0.35, 0.25, 0.15, 0.25]and resonance bonus (4-dim: 1.5x, 3-dim: 1.3x). - XLR: If enabled,
AdaptiveXlrEngineruns spectral noise cancellation with dual hot/cold pulses and sigmoid gating. - Plasticity:
PlasticityEngine.update()runs the 5-step Hebbian cycle on co-activated edges. - Response: Results serialized to JSON-RPC response, written to stdout in the same transport mode as the request.
Example: guided structural flow
A more agent-native flow such as trace -> view -> surgical_context_v2 -> validate_plan -> apply_batch adds two extra kinds of behavior on top of the graph work:
- Cognitive state: tools can expose
proof_statesuch astriaging,proving, orready_to_edit - Next-step guidance: tools can return
next_suggested_tool,next_suggested_target, andnext_step_hint
This is one of the biggest architectural changes in v0.6.0: the MCP layer is no longer just a transport for graph answers. It is a transport for graph answers plus workflow guidance.
Example: code plus document workflow
The current architecture also supports a second operating loop:
- ingest code
- ingest or auto-ingest document roots through the universal lane
- resolve canonical document artifacts
- bind documents to likely code
- inspect document/code drift after implementation moves
This is what turns m1nd from a code-only graph into a local code + knowledge runtime.
Key Design Decisions
Compressed Sparse Row (CSR) Graph
The graph uses CSR format rather than adjacency lists or adjacency matrices. CSR provides O(1) neighbor iteration start, cache-friendly sequential access, and compact memory layout. Edge weights are stored as AtomicU32 (bit-reinterpreted f32) for lock-free plasticity updates via CAS with a 64-retry limit.
Forward and reverse CSR arrays are maintained in parallel, enabling both outgoing and incoming edge traversal without full graph scans.
Struct-of-Arrays Node Storage
NodeStorage uses SoA layout with explicit hot/warm/cold path separation:
- Hot path (every query):
activation: Vec<[FiniteF32; 4]>,pagerank: Vec<FiniteF32> - Warm path (plasticity):
plasticity: Vec<PlasticityNode> - Cold path (seed finding, export):
label,node_type,tags,last_modified,change_frequency,provenance
This layout ensures that activation queries touch only hot-path arrays, maximizing L1/L2 cache utilization.
FiniteF32 Type Safety
All floating-point values in the graph are wrapped in FiniteF32, a #[repr(transparent)] newtype that makes NaN and Infinity impossible by construction:
#![allow(unused)]
fn main() {
#[derive(Clone, Copy, Default, PartialEq)]
#[repr(transparent)]
pub struct FiniteF32(f32);
impl FiniteF32 {
#[inline]
pub fn new(v: f32) -> Self {
debug_assert!(v.is_finite(), "FiniteF32::new received non-finite: {v}");
Self(if v.is_finite() { v } else { 0.0 })
}
}
}
Because NaN is excluded, FiniteF32 can safely implement Ord, Eq, and Hash – operations that are unsound on raw f32. Related newtypes PosF32 (strictly positive), LearningRate (0, 1]), and DecayFactor (0, 1]) provide compile-time invariant enforcement for their respective domains.
String Interning
All node labels, tags, and relation names are interned via StringInterner. Once interned, string comparisons become u32 == u32 (zero-allocation, single CPU cycle). The interner maps strings to InternedStr(u32) handles and provides O(1) bidirectional lookup.
Parallel Extraction, Sequential Building
Ingestion uses rayon for parallel file reading and language-specific extraction across all CPU cores, but graph construction is single-threaded. This avoids the complexity of concurrent graph mutation while still saturating I/O bandwidth during the most expensive phase (parsing hundreds of files).
Atomic Persistence
Graph and plasticity state are saved via atomic write: serialize to a temporary file, then rename() over the target. This prevents corruption on crash (FM-PL-008). The NaN firewall at the export boundary rejects any non-finite values that might have leaked through.
Performance Characteristics
There are two truths worth separating:
- engine/runtime timings
- workflow-level benchmark truth
Engine timings still matter, especially for ingest and structural queries. Workflow benchmarks matter when the goal is to reduce context churn, false starts, and repair loops.
Representative measured engine timings:
| Operation | Time | Notes |
|---|---|---|
| Full ingest | ~910ms | Walk + parallel extract + resolve + finalize (CSR + PageRank) |
| Activate query | ~31ms | 4-dimension with XLR, top-20 results |
| Impact analysis | ~5ms | BFS blast radius, 3-hop default |
| Trace analysis | ~3.5ms | Stacktrace to ranked suspects |
| Trail resume | ~0.2ms | Restore continuity and next-step hints |
| Apply batch | ~165ms | Atomic multi-file write before deeper verification |
Workflow benchmark truth lives on the Benchmarks page. The current recorded warm-graph corpus shows:
10518 -> 5182aggregate token proxy50.73%aggregate reduction14 -> 0false starts39guided follow-throughs12successful recovery loops
Memory footprint scales linearly with graph size. A 10K-node graph with 25K edges uses approximately 15MB of heap (graph + all engine indexes). The CSR representation is 3-5x more compact than equivalent adjacency list representations.
Scaling Bounds
- Node limit:
NodeId(u32)supports up to ~4 billion nodes. Practical limit is themax_nodesconfig (default: 500K). - Edge weights:
AtomicU32CAS with 64-retry limit. Under high contention (>32 concurrent plasticity updates on the same edge), CAS may exhaust retries and returnCasRetryExhausted. - Co-occurrence index: Disabled above 50K nodes (
COOCCURRENCE_MAX_NODES) to avoid O(N * walks * length) random walk cost. - Co-change matrix: Hard budget of 500K entries with per-row cap of 100 entries.
- Resonance pulse budget: 50K pulses per standing wave analysis to prevent combinatorial explosion in dense subgraphs.
Multi-Agent Model
m1nd runs as a single process serving multiple agents through the same JSON-RPC stdio channel. All agents share one graph and one set of engines. Writes (plasticity updates, ingestion) are immediately visible to all readers through SharedGraph = Arc<parking_lot::RwLock<Graph>>.
Each agent gets its own AgentSession tracking first/last seen timestamps and query count. The perspective system, trail system, and shared runtime state provide isolation where needed without duplicating the underlying graph.
The important modern detail is that agent-facing state is not limited to ownership and transport. Several flows now expose cognitive runtime state directly, including:
proof_state- next-step guidance fields
- trail continuation hints
- batch progress and handoff metadata
parking_lot::RwLock is used instead of std::sync::RwLock to prevent writer starvation – a critical property when plasticity updates (writes) must interleave with activation queries (reads).
Graph Engine (m1nd-core)
m1nd-core is the computational core of the graph runtime. It owns the graph data structure, the activation and analysis engines, the plasticity system, and the type-safe numeric primitives that prevent NaN/Inf corruption system-wide.
Source: m1nd-core/src/
Type System
Numeric Primitives
Every floating-point value in m1nd flows through one of four newtype wrappers defined in types.rs:
| Type | Invariant | Use |
|---|---|---|
FiniteF32 | Never NaN or Inf | All activation scores, edge weights, scores |
PosF32 | Strictly positive, finite | Wavelength, frequency, half-life, decay rate, threshold |
LearningRate | (0.0, 1.0] | Plasticity learning rate |
DecayFactor | (0.0, 1.0] | Signal decay per hop |
FiniteF32 is the foundation. In debug builds, constructing one from a non-finite value panics. In release builds, it clamps to 0.0. Because NaN is excluded by construction, FiniteF32 implements Ord, Eq, and Hash – all unsound on raw f32:
#![allow(unused)]
fn main() {
impl Ord for FiniteF32 {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.total_cmp(&other.0)
}
}
}
Index Types
Thin #[repr(transparent)] wrappers over u32 provide type-safe indexing:
| Type | Wraps | Purpose |
|---|---|---|
NodeId(u32) | Node index | Index into NodeStorage parallel arrays |
EdgeIdx(u32) | Edge index | Index into CSR parallel arrays |
InternedStr(u32) | String handle | Opaque index into StringInterner |
CommunityId(u32) | Community | Louvain community membership |
Generation(u64) | Mutation counter | Plasticity engine desync detection |
Node and Edge Classification
#![allow(unused)]
fn main() {
#[repr(u8)]
pub enum NodeType {
File = 0, Directory = 1, Function = 2, Class = 3,
Struct = 4, Enum = 5, Type = 6, Module = 7,
Reference = 8, Concept = 9, Material = 10, Process = 11,
Product = 12, Supplier = 13, Regulatory = 14, System = 15,
Cost = 16, Custom(u8),
}
#[repr(u8)]
pub enum EdgeDirection {
Forward = 0,
Bidirectional = 1,
}
}
Variants 0-8 are code-domain types. Variants 9-16 support non-code domains (supply chain, manufacturing). Custom(u8) is the extension point for future domains.
Dimension System
Activation operates across four dimensions with fixed weights:
#![allow(unused)]
fn main() {
pub enum Dimension {
Structural = 0, // Graph topology (BFS/heap propagation)
Semantic = 1, // Text similarity (trigram TF-IDF + co-occurrence)
Temporal = 2, // Time-based (decay + velocity)
Causal = 3, // Causal chains (forward/backward with discount)
}
pub const DIMENSION_WEIGHTS: [f32; 4] = [0.35, 0.25, 0.15, 0.25];
}
When fewer than 4 dimensions contribute to a result, weights are adaptively redistributed. Results that fire across multiple dimensions receive a resonance bonus: 1.5x for all 4, 1.3x for 3.
Property Graph Model
String Interner
All strings pass through StringInterner before entering the graph. The interner maps strings to InternedStr(u32) handles via a HashMap<String, InternedStr> and resolves handles back via a Vec<String> indexed by the u32 value.
Once interned, all string comparisons become integer comparisons – zero-allocation, single CPU cycle.
#![allow(unused)]
fn main() {
pub struct StringInterner {
strings: Vec<String>,
index: HashMap<String, InternedStr>,
}
}
CSR Graph
The graph uses Compressed Sparse Row (CSR) format with both forward and reverse adjacency. For node i, outgoing edges span offsets[i]..offsets[i+1] into parallel arrays for targets, weights, inhibitory flags, relations, directions, and causal strengths.
#![allow(unused)]
fn main() {
pub struct CsrGraph {
// Forward CSR
pub offsets: Vec<u64>, // num_nodes + 1
pub targets: Vec<NodeId>, // total_edges
pub weights: Vec<AtomicU32>, // bit-reinterpreted f32 for lock-free CAS
pub inhibitory: Vec<bool>, // edge polarity
pub relations: Vec<InternedStr>, // edge type (interned)
pub directions: Vec<EdgeDirection>, // forward or bidirectional
pub causal_strengths: Vec<FiniteF32>,
// Reverse CSR (built at finalize)
pub rev_offsets: Vec<u64>,
pub rev_sources: Vec<NodeId>,
pub rev_edge_idx: Vec<EdgeIdx>, // maps back to forward arrays
// Pre-finalize staging
pub pending_edges: Vec<PendingEdge>,
}
}
The CSR is immutable after finalization. Edges are added to pending_edges during graph building, then sorted by source and compacted into the CSR arrays by Graph.finalize(). Bidirectional edges are expanded into two forward entries during finalization.
Atomic Weight Updates
Edge weights are stored as AtomicU32 rather than f32. The plasticity engine updates weights concurrently with read queries using Compare-And-Swap:
#![allow(unused)]
fn main() {
pub fn atomic_max_weight(
&self, edge: EdgeIdx, new_val: FiniteF32, max_retries: u32,
) -> M1ndResult<()> {
let slot = &self.weights[edge.as_usize()];
let new_bits = new_val.get().to_bits();
for _ in 0..max_retries {
let old_bits = slot.load(Ordering::Relaxed);
let old_val = f32::from_bits(old_bits);
if old_val >= new_val.get() { return Ok(()); }
if slot.compare_exchange_weak(
old_bits, new_bits, Ordering::Release, Ordering::Relaxed
).is_ok() { return Ok(()); }
}
Err(M1ndError::CasRetryExhausted { edge, limit: max_retries })
}
}
Two CAS operations are provided: atomic_max_weight (only increases, for activation scatter-max) and atomic_write_weight (unconditional, for plasticity). Both retry up to 64 times (constant CAS_RETRY_LIMIT).
Node Storage (SoA)
All per-node data lives in NodeStorage, organized as Struct-of-Arrays with explicit cache-path separation:
#![allow(unused)]
fn main() {
pub struct NodeStorage {
pub count: u32,
// Hot path: activation engine reads every query
pub activation: Vec<[FiniteF32; 4]>, // [structural, semantic, temporal, causal]
pub pagerank: Vec<FiniteF32>,
// Warm path: plasticity reads per query
pub plasticity: Vec<PlasticityNode>,
// Cold path: seed finding, display, export
pub label: Vec<InternedStr>,
pub node_type: Vec<NodeType>,
pub tags: Vec<SmallVec<[InternedStr; 6]>>,
pub last_modified: Vec<f64>,
pub change_frequency: Vec<FiniteF32>,
pub provenance: Vec<NodeProvenance>,
}
}
The SmallVec<[InternedStr; 6]> for tags avoids heap allocation for nodes with 6 or fewer tags (the common case), while still supporting arbitrary tag counts.
Node Provenance
Each node carries source metadata for tracing back to the original code:
#![allow(unused)]
fn main() {
pub struct NodeProvenance {
pub source_path: Option<InternedStr>,
pub line_start: u32,
pub line_end: u32,
pub excerpt: Option<InternedStr>,
pub namespace: Option<InternedStr>,
pub canonical: bool,
}
}
Generation Counter
Generation(u64) tracks graph mutations. The plasticity engine stores the generation at initialization. Every plasticity operation asserts that the current graph generation matches; a mismatch (from concurrent ingestion) causes a controlled rebuild rather than operating on stale state.
Activation Propagation
Engine Selection (HybridEngine)
The HybridEngine auto-selects between two propagation strategies based on graph characteristics:
flowchart TD
START["HybridEngine.propagate()"]
CHECK{"seed_ratio < 0.001<br/>AND avg_degree < 8?"}
HEAP["HeapEngine<br/>(priority queue)"]
WAVE["WavefrontEngine<br/>(BFS depth-parallel)"]
MERGE["Collect sparse results"]
START --> CHECK
CHECK -->|yes| HEAP
CHECK -->|no| WAVE
HEAP --> MERGE
WAVE --> MERGE
- HeapEngine: Max-heap priority queue. Processes strongest signal first. Early-terminates when the heap top drops below threshold. Uses a double-hashing
BloomFilterfor O(1) amortized visited checks. Best for sparse queries with few seeds in low-degree graphs. - WavefrontEngine: BFS depth-parallel. All active nodes at current depth fire simultaneously. Signal accumulated via scatter-max into next depth’s buffer. Best for dense queries or high-degree graphs.
Structural Propagation (D1)
The wavefront engine is the reference implementation. Signal propagates depth by depth:
- Seed nodes initialized with their scores (capped at
saturation_cap). - For each depth (up to
max_depth=5, hard cap 20):- Each frontier node
srcwith activation abovethreshold=0.04fires. - For each outgoing edge:
signal = src_activation * weight * decay(0.55). - Inhibitory edges:
signal = -signal * inhibitory_factor(0.5), subtracted from target (floored at 0). - Excitatory edges: scatter-max into target (keep strongest arrival).
- Each frontier node
- Collect all nodes with non-zero activation, sorted descending.
Semantic Dimension (D2)
Two indexes power semantic matching:
CharNgramIndex: FNV-1a 24-bit trigram hashing over node labels with TF-IDF weighting. An inverted index maps trigram hashes to node lists, enabling O(K) query time (K = number of matching trigrams) instead of O(N) full scan.
CoOccurrenceIndex: DeepWalk-lite random walks (20 walks/node, length 10, window 4) generate co-occurrence counts, normalized to Positive Pointwise Mutual Information (PPMI). Sorted vectors enable O(D) merge-intersection for similarity queries. Disabled above 50K nodes to bound walk cost.
The semantic engine also includes a SynonymExpander with 15 default groups covering code terminology and Portuguese domain terms.
Query pipeline: Phase 1 ngram candidates (3x top_k) -> Phase 2 multi-seed co-occurrence re-rank.
Temporal Dimension (D3)
Three scorers combine:
- TemporalDecayScorer: Per-NodeType half-lives (File=7 days, Function=14 days, Module/Directory=30 days). Formula:
exp(-ln(2) * age_hours / half_life). Dormant nodes (>35 days) get a resurrection bonus with an additive floor. - VelocityScorer: Z-score based change velocity. Nodes changing faster than average receive higher scores.
- CoChangeMatrix: Sparse matrix (budget: 500K entries, 100 per row) bootstrapped from BFS depth 3, refined with git co-change observations.
Causal Dimension (D4)
CausalChainDetector: Budget-limited priority-queue DFS along causal_strength edges. Forward propagation follows contains/imports/calls edges; backward propagation reverses direction. Discount factor 0.7 per hop. Chain depth capped at 6 by default.
Dimension Merging
merge_dimensions() combines all four dimension results:
- For each activated node, compute weighted sum:
score = sum(dim_score * DIMENSION_WEIGHTS[dim]). - If a dimension produced no results, redistribute its weight proportionally to active dimensions.
- Apply resonance bonus: 1.5x if all 4 dimensions contributed, 1.3x if 3 contributed.
- Sort by final score, truncate to top_k.
Seed Finding
SeedFinder resolves query strings to graph nodes via a 5-level matching cascade:
- Exact label match (highest priority)
- Prefix match (e.g., “chat_” matches “chat_handler”)
- Substring match (e.g., “handler” matches “chat_handler”)
- Tag match (e.g., “#api” tag)
- Fuzzy trigram (cosine similarity of trigram vectors, lowest priority)
A semantic re-ranking phase (0.6 basic score / 0.4 semantic blend) refines results when multiple candidates match.
Weight Systems
Hebbian Plasticity
The PlasticityEngine implements biological Hebbian learning with 5 phases executed after every activation query:
flowchart LR
A["1. Hebbian Strengthen<br/>delta_w = lr * act_src * act_tgt"]
B["2. Synaptic Decay<br/>w *= (1 - decay_rate) for inactive"]
C["3. LTP/LTD<br/>permanent bonus/penalty<br/>after N consecutive"]
D["4. Homeostatic Normalize<br/>scale if total > ceiling"]
E["5. Record Query<br/>ring buffer + bigrams"]
A --> B --> C --> D --> E
Constants (from plasticity.rs):
| Parameter | Value | Purpose |
|---|---|---|
DEFAULT_LEARNING_RATE | 0.08 | Hebbian weight change rate |
DEFAULT_DECAY_RATE | 0.005 | Inactive synapse decay per query |
LTP_THRESHOLD | 5 | Consecutive strengthens before permanent bonus |
LTD_THRESHOLD | 5 | Consecutive weakens before permanent penalty |
LTP_BONUS | 0.15 | Permanent weight increase |
LTD_PENALTY | 0.15 | Permanent weight decrease |
HOMEOSTATIC_CEILING | 5.0 | Max sum of incoming weights per node |
WEIGHT_FLOOR | 0.05 | Minimum edge weight (prevents extinction) |
WEIGHT_CAP | 3.0 | Maximum edge weight |
Hebbian update: For each edge where both source and target were activated: delta_w = learning_rate * activation_source * activation_target. Applied via atomic CAS (atomic_write_weight).
Homeostatic normalization: If the sum of incoming weights for any node exceeds HOMEOSTATIC_CEILING, all incoming weights are scaled proportionally to bring the total back under the ceiling. This prevents runaway positive feedback.
Query Memory: A ring buffer of 1000 entries tracks recent queries. Each record stores the query text, seed nodes, activated nodes, and timestamp. The memory tracks:
- Node frequency: how often each node appears across recent queries.
- Seed bigrams: co-occurring seed pairs, used for priming signals.
When the buffer wraps, evicted records have their frequency and bigram counts decremented – maintaining accurate sliding-window statistics.
Persistence: Plasticity state (per-edge SynapticState) is exported as JSON using triple-based identity matching (source_label, target_label, relation). This allows plasticity to survive graph rebuilds as long as the semantic structure remains similar.
XLR Differential Processing
XLR (eXcitatory-Lateral-inhibitory Response) is a spectral noise cancellation system that separates signal from noise in activation results.
Constants:
| Parameter | Value | Purpose |
|---|---|---|
F_HOT | 1.0 | Hot signal frequency |
F_COLD | 3.7 | Cold signal frequency |
SPECTRAL_BANDWIDTH | 0.8 | Gaussian kernel bandwidth |
IMMUNITY_HOPS | 2 | BFS immunity radius |
SIGMOID_STEEPNESS | 6.0 | Gating function steepness |
6-step pipeline:
- Anti-seed selection: Pick nodes dissimilar to seeds (Jaccard similarity < 0.2, degree ratio filter).
- Immunity computation: BFS 2 hops from seeds. Immune nodes cannot be suppressed.
- Hot propagation: Spread
SpectralPulsewith frequencyF_HOT=1.0from seed nodes. Pulses carry amplitude, phase, frequency, and a bounded recent path ([NodeId; 3], not unbounded Vec – FM-RES-007). - Cold propagation: Spread from anti-seeds with frequency
F_COLD=3.7. - Spectral overlap + density modulation: Compute overlap between hot and cold spectra using Gaussian kernel (
bw=0.8). Dense neighborhoods get modulated (clamped to[0.3, 2.0]). - Sigmoid gating: Apply
1 / (1 + exp(-steepness * (hot - cold)))to produce final scores.
Over-cancellation fallback (FM-XLR-010): If all results score zero after gating (cold dominates everywhere), fall back to hot-only results. This prevents total signal erasure in adversarial topologies.
Standing Wave Resonance
The resonance engine discovers structural harmonics by propagating wave pulses through the graph:
#![allow(unused)]
fn main() {
pub struct WavePulse {
pub node: NodeId,
pub amplitude: FiniteF32, // can be negative for destructive interference
pub phase: FiniteF32, // [0, 2*pi)
pub frequency: PosF32, // MUST be positive (FM-RES-002)
pub wavelength: PosF32, // MUST be positive (FM-RES-001)
pub hops: u8,
pub prev_node: NodeId,
}
}
Wave interference: Pulses accumulate at each node as complex numbers (real + imaginary). Constructive interference occurs when pulses arrive in phase; destructive when out of phase. The WaveAccumulator tracks sum(amplitude * cos(phase)) and sum(amplitude * sin(phase)), with resultant amplitude sqrt(real^2 + imag^2).
Reflection rules:
- Dead-end reflection: At leaf nodes (degree 1), pulse reflects with a pi phase shift (
REFLECTION_PHASE_SHIFT). - Hub partial reflection: At hub nodes (degree > 4x average), 30% of amplitude reflects (
HUB_REFLECTION_COEFF).
Budget: DEFAULT_PULSE_BUDGET = 50,000 prevents combinatorial explosion in dense subgraphs. Pulses are processed in BFS order via a VecDeque.
Harmonic analysis: HarmonicAnalyzer sweeps across frequencies, groups nodes by harmonic response, and identifies resonant frequencies (local maxima in the sweep). SympatheticResonanceDetector finds nodes that resonate across regions (outside 2-hop seed neighborhood) – indicating structural coupling without direct edges.
PageRank
Computed once at Graph.finalize() via power iteration:
- Damping factor: 0.85
- Max iterations: 50
- Convergence threshold: 1e-6
PageRank scores are stored in NodeStorage.pagerank and used as a static importance signal in seed finding and result ranking.
Persistence
Graph Snapshot
snapshot.rs serializes the graph to JSON format (version 3):
#![allow(unused)]
fn main() {
struct GraphSnapshot {
version: u32, // SNAPSHOT_VERSION = 3
nodes: Vec<NodeSnapshot>,
edges: Vec<EdgeSnapshot>,
}
}
Each NodeSnapshot includes external_id, label, node_type, tags, timestamps, and optional provenance. Each EdgeSnapshot includes source/target IDs, relation, weight, direction, inhibitory flag, and causal strength.
Atomic write (FM-PL-008): Write to a temporary file, then rename() over the target. The rename is atomic on POSIX filesystems, so a crash mid-write leaves the previous snapshot intact.
NaN firewall: All values are checked for finiteness at the export boundary. Non-finite values are rejected before writing.
Plasticity State
Per-edge SynapticState (original weight, current weight, strengthen/weaken counts, LTP/LTD flags) is serialized to a separate JSON file. Import uses triple-based matching (source_label, target_label, relation) to reconnect state to edges after graph rebuilds.
Auto-Persist
The MCP server triggers persistence every auto_persist_interval queries (default: 50). Ordering: graph first (source of truth), then plasticity. If graph save fails, plasticity save is skipped to prevent inconsistent state.
Ingestion Pipeline (m1nd-ingest)
m1nd-ingest transforms codebases and structured documents into the property graph consumed by m1nd-core. It handles file discovery, language-specific code extraction, cross-file reference resolution, incremental diff computation, and memory/markdown ingestion.
Source: m1nd-ingest/src/
Module Map
| Module | Purpose |
|---|---|
lib.rs | Ingestor pipeline, IngestAdapter trait, config, stats |
walker.rs | DirectoryWalker, binary detection, git enrichment |
extract/mod.rs | Extractor trait, comment stripping, CommentSyntax |
extract/python.rs | Python: classes, functions, decorators, imports |
extract/typescript.rs | TypeScript/JS: classes, functions, interfaces, imports |
extract/rust_lang.rs | Rust: structs, enums, impls, traits, functions, mods |
extract/go.rs | Go: structs, interfaces, functions, packages |
extract/java.rs | Java: classes, interfaces, methods, packages |
extract/generic.rs | Fallback: file-level node with tag extraction |
resolve.rs | ReferenceResolver, proximity disambiguation |
cargo_workspace.rs | Cargo workspace/crate/dependency enrichment for Rust repos |
cross_file.rs | Python-weighted cross-file enrichment (imports, tests, registers) |
diff.rs | GraphDiff for incremental updates |
json_adapter.rs | Generic JSON-to-graph adapter |
memory_adapter.rs | Markdown/memory document adapter |
canonical.rs | Canonical document substrate used by the universal lane |
merge.rs | Graph merge utilities |
patent_adapter.rs | USPTO/EPO patent XML ingestion |
jats_adapter.rs | PubMed/JATS scientific article XML ingestion |
bibtex_adapter.rs | BibTeX bibliography file ingestion |
rfc_adapter.rs | IETF RFC XML v3 ingestion |
crossref_adapter.rs | CrossRef API JSON (DOI metadata) ingestion |
document_router.rs | Auto-detect document format and route to correct adapter |
universal_adapter.rs | Best-effort document canonicalization, provider routing, and graphification |
cross_domain.rs | Cross-domain edge resolution (DOI, ORCID, keyword bridges) |
Pipeline Overview
flowchart TD
subgraph "Phase 1: Walk"
DIR["Directory Root"]
WALK["DirectoryWalker<br/>walkdir + skip rules"]
BIN["Binary Detection<br/>NUL in first 8KB"]
GIT["Git Enrichment<br/>commit counts + timestamps"]
FILES["DiscoveredFile[]"]
end
subgraph "Phase 2: Extract (parallel)"
RAYON["rayon::par_iter"]
PY["PythonExtractor"]
TS["TypeScriptExtractor"]
RS["RustExtractor"]
GO["GoExtractor"]
JAVA["JavaExtractor"]
GEN["GenericExtractor"]
RESULTS["ExtractionResult[]"]
end
subgraph "Phase 3: Build Graph"
NODES["Add all nodes<br/>with provenance + timestamps"]
EDGES["Add resolved edges<br/>with causal strengths"]
CAUSAL["Causal strength assignment<br/>contains=0.8, imports=0.6, etc."]
end
subgraph "Phase 4: Resolve + Enrich"
REFS["ReferenceResolver<br/>ref:: → actual nodes"]
PROX["Proximity Disambiguation<br/>same file > same dir > same project"]
HINTS["Import Hints<br/>module path → target preference"]
CARGO["Cargo Workspace<br/>workspace/crate/dependency graph"]
XFILE["Cross-File Enrichment<br/>currently strongest for Python"]
end
subgraph "Phase 5: Finalize"
CSR["Build CSR<br/>sort edges, compact arrays"]
BIDIR["Expand Bidirectional<br/>contains, implements"]
REV["Build Reverse CSR"]
PR["PageRank<br/>power iteration"]
end
DIR --> WALK --> BIN --> GIT --> FILES
FILES --> RAYON
RAYON --> PY & TS & RS & GO & JAVA & GEN
PY & TS & RS & GO & JAVA & GEN --> RESULTS
RESULTS --> NODES --> EDGES --> CAUSAL
CAUSAL --> REFS --> PROX
PROX --> HINTS --> CARGO --> XFILE
XFILE --> CSR --> BIDIR --> REV --> PR
Phase 1: Directory Walking
DirectoryWalker uses the walkdir crate to traverse the filesystem. It applies skip rules, detects binary files, and enriches results with git metadata.
Skip Rules
Default skip directories (configured via IngestConfig):
#![allow(unused)]
fn main() {
skip_dirs: vec![
".git", "node_modules", "__pycache__", ".venv",
"target", "dist", "build", ".next", "vendor",
],
skip_files: vec![
"package-lock.json", "yarn.lock", "Cargo.lock", "poetry.lock",
],
}
Hidden directories (starting with .) are skipped unless they are the root. Symlinks are not followed.
Binary Detection (FM-ING-004)
After discovering a file, the walker reads the first 8KB and checks for NUL bytes (0x00). Any file containing NUL is classified as binary and skipped. This prevents feeding compiled binaries, images, or other non-text files into the language extractors.
Git Enrichment
If the root is inside a git repository, the walker runs git log --format=%at --name-only to extract:
- Commit count per file: Used to compute
change_frequency(1 commit = 0.1, 50+ = 1.0, capped). - Most recent commit timestamp: Used for temporal decay scoring.
- Commit groups: Sets of files that changed together in the same commit. Fed into the
CoChangeMatrixafter graph finalization.
The result is a WalkResult containing Vec<DiscoveredFile> and Vec<Vec<String>> commit groups.
#![allow(unused)]
fn main() {
pub struct DiscoveredFile {
pub path: PathBuf,
pub relative_path: String,
pub extension: Option<String>,
pub size_bytes: u64,
pub last_modified: f64,
pub commit_count: u32,
pub last_commit_time: f64,
}
}
Phase 2: Parallel Extraction
Files are distributed across rayon’s thread pool for concurrent extraction. Each file is assigned a language-specific extractor based on its extension:
| Extension | Extractor | Extracted Entities |
|---|---|---|
.py, .pyi | PythonExtractor | Classes, functions, decorators, imports, global assignments |
.ts, .tsx, .js, .jsx, .mjs, .cjs | TypeScriptExtractor | Classes, functions, interfaces, type aliases, imports, exports |
.rs | RustExtractor | Structs, enums, traits, impls, functions, modules, macros |
.go | GoExtractor | Structs, interfaces, functions, methods, packages |
.java | JavaExtractor | Classes, interfaces, methods, fields, packages |
| everything else | GenericExtractor | File-level node with tag extraction from content |
This stack is hybrid:
- native/manual extractors for Python, TypeScript/JavaScript, Rust, Go, and Java
- tree-sitter-backed tiers for additional languages
- generic fallback for unsupported files
Extractor Interface
All extractors implement the Extractor trait:
#![allow(unused)]
fn main() {
pub trait Extractor: Send + Sync {
fn extract(&self, content: &[u8], file_id: &str) -> M1ndResult<ExtractionResult>;
fn extensions(&self) -> &[&str];
}
}
An ExtractionResult contains:
#![allow(unused)]
fn main() {
pub struct ExtractionResult {
pub nodes: Vec<ExtractedNode>,
pub edges: Vec<ExtractedEdge>,
pub unresolved_refs: Vec<String>,
}
}
Each extracted node carries:
#![allow(unused)]
fn main() {
pub struct ExtractedNode {
pub id: String, // e.g. "file::backend/chat_handler.py::ChatHandler"
pub label: String, // e.g. "ChatHandler"
pub node_type: NodeType,
pub tags: Vec<String>,
pub line: u32,
pub end_line: u32,
}
}
Comment and String Stripping
Before extraction, each file’s content passes through strip_comments_and_strings() which removes comments and string literals to prevent false-positive matches from regex extractors. The function preserves import line string content (so from "react" still resolves) but strips string bodies elsewhere.
Comment syntax is per-language:
#![allow(unused)]
fn main() {
pub struct CommentSyntax {
pub line: &'static str, // e.g. "//" or "#"
pub block_open: &'static str, // e.g. "/*"
pub block_close: &'static str, // e.g. "*/"
}
}
Supported: Rust (//, /* */), Python (#, """ """), C-style (//, /* */), Go (//, /* */), Generic (#, none).
Node ID Format
Extracted nodes use a hierarchical ID scheme: file::{relative_path}::{entity_name}. For example:
file::backend/chat_handler.py(file node)file::backend/chat_handler.py::ChatHandler(class node)file::backend/chat_handler.py::ChatHandler::handle_message(method node)
Unresolved references use the prefix ref::: ref::Config, ref::react. These are resolved to actual nodes in Phase 4.
Phase 3: Graph Building
After parallel extraction completes, results are collected and processed sequentially (graph mutation is single-threaded).
Node Creation
For each ExtractedNode:
- Look up the file timestamp from git enrichment (or filesystem mtime).
- Compute
change_frequencyfrom git commit count:(commits / 50).clamp(0.1, 1.0). Default 0.3 for non-git repos. - Call
graph.add_node()with the external ID, label, node type, tags, timestamp, and change frequency. - Set provenance:
source_path,line_start,line_end,namespace="code". - On
DuplicateNodeerror, increment collision counter and continue.
Edge Creation
For each ExtractedEdge (skipping ref:: targets, which are deferred):
- Resolve source and target IDs to
NodeId. - Assign causal strength by relation type:
| Relation | Causal Strength | Direction |
|---|---|---|
contains | 0.8 | Bidirectional |
implements | 0.7 | Bidirectional |
imports | 0.6 | Forward |
calls | 0.5 | Forward |
references | 0.3 | Forward |
| other | 0.4 | Forward |
contains and implements edges are bidirectional so that both parent-to-child and child-to-parent navigation work.
Safety Guards (FM-ING-002)
Two budget checks run between sequential file processing:
- Timeout: If
start.elapsed() > config.timeout(default 300s), stop processing. - Node budget: If
nodes >= config.max_nodes(default 500K), stop processing.
Both log a warning and break from the build loop, producing a partial but consistent graph from whatever was processed.
Phase 4: Reference Resolution And Enrichment
ReferenceResolver
Unresolved references (ref::Config, ref::FastAPI, etc.) are resolved to actual graph nodes using the ReferenceResolver.
Multi-value label index (FM-ING-008): The resolver builds a HashMap from labels to lists of matching NodeIds. When multiple nodes share a label (e.g., multiple files define a Config class), proximity disambiguation selects the best match:
| Proximity | Score | Condition |
|---|---|---|
| Same file | 100 | Source and target share the same file:: prefix |
| Same directory | 50 | Source and target share the same directory |
| Same project | 10 | Default (both exist in the graph) |
Import hint disambiguation: When the extractor sees from foo.bar import Baz, it records an import hint mapping (source_file, "ref::Baz") to the module path foo.bar. The resolver uses this hint to prefer the Baz node under foo/bar/ over a same-named node elsewhere.
Resolution outcome per reference:
- Resolved: Exactly one match (or best proximity match). Edge created with resolved
NodeId. - Ambiguous: Multiple matches with equal proximity. Best guess selected, counted in stats.
- Unresolved: No match found. Counted in stats, no edge created.
Cargo Workspace Enrichment
For Rust repos, cargo_workspace.rs adds a workspace-aware layer before finalization:
- workspace nodes
- crate nodes
- crate -> file
containsedges - crate -> crate
depends_onedges for internal workspace dependencies - external dependency nodes for non-workspace dependencies
This means Rust repos are no longer represented only as file graphs.
Cross-File Enrichment
After reference resolution and Cargo enrichment, cross_file.rs adds a narrower set of shipped cross-file edges.
Today this pass is strongest for Python and focuses on:
importstestsregisters
It should not be described as a language-uniform cross-file engine yet.
Phase 5: Finalization
Graph.finalize() transforms the mutable graph into its read-optimized CSR form:
- Sort edges by source: All pending edges are sorted by
source.0(node index). - Build forward CSR: Compute offsets array, pack targets/weights/relations/etc into parallel arrays.
- Expand bidirectional edges: For each bidirectional edge
(A, B), ensure bothA->BandB->Aexist in the CSR. - Build reverse CSR: Sort edges by target, build
rev_offsets,rev_sources,rev_edge_idx(mapping back to forward array indices). - Rebuild plasticity arrays: Allocate
PlasticityNodefor each node with default ceiling. - Compute PageRank: Power iteration with damping 0.85, max 50 iterations, convergence 1e-6.
After finalization, the graph is immutable (except for atomic weight updates by plasticity).
Incremental Ingestion (GraphDiff)
diff.rs enables incremental updates without full re-ingestion.
#![allow(unused)]
fn main() {
pub enum DiffAction {
AddNode(ExtractedNode),
RemoveNode(String),
ModifyNode { external_id, new_label, new_tags, new_last_modified },
AddEdge(ExtractedEdge),
RemoveEdge { source_id, target_id, relation },
ModifyEdgeWeight { source_id, target_id, relation, new_weight },
}
}
GraphDiff::compute() compares old and new extraction results by indexing both into HashMaps by external ID, then classifying each node/edge as added, removed, or modified.
GraphDiff::apply() executes the diff against a live graph. Note: CSR does not support true node/edge removal. “Removed” nodes are tombstoned (zero weight, empty label) rather than physically deleted. A full re-ingest is needed to reclaim space.
When to use incremental vs full:
| Scenario | Strategy |
|---|---|
| Single file changed | Incremental diff (fast, ~10ms) |
| Many files changed (>20%) | Full re-ingest (cleaner CSR, correct PageRank) |
| New codebase | Full ingest |
| Plasticity state important | Full ingest + plasticity reimport (triple matching) |
Memory Adapter
MemoryIngestAdapter converts markdown documents into graph nodes. It implements the IngestAdapter trait with domain "memory".
Supported Formats
Files with extensions .md, .markdown, or .txt are accepted. The adapter walks a directory of memory files and parses each one into:
- Section nodes (
NodeType::Concept): Created from markdown headings (#,##, etc.). - Entry nodes (
NodeType::Process): Created from list items under sections. - File reference nodes (
NodeType::Reference): Created from file paths mentioned in content.
Entry Classification
List items are classified by content patterns:
| Classification | Pattern | Example |
|---|---|---|
| Task | Contains “TODO”, “FIXME”, “pending”, “implement” | “- TODO: add tests” |
| Decision | Contains “decision:”, “decided:”, “chose” | “- Decision: use CSR format” |
| State | Contains “status:”, “state:”, “current:” | “- Status: in progress” |
| Event | Contains date pattern (YYYY-MM-DD) | “- 2026-03-12: deployed” |
| Note | Default | “- Config lives in settings.py” |
Edges
The adapter creates:
containsedges from section to child entries.referencesedges from entries to file reference nodes.followsedges between sequential entries in the same section.
This allows the activation engine to traverse from a concept (“plasticity”) through memory entries to referenced code files, bridging the semantic gap between human notes and source code.
IngestAdapter Trait
The IngestAdapter trait enables domain-specific ingestion beyond code:
#![allow(unused)]
fn main() {
pub trait IngestAdapter: Send + Sync {
fn domain(&self) -> &str;
fn ingest(&self, root: &Path) -> M1ndResult<(Graph, IngestStats)>;
}
}
Implemented adapters:
| Adapter | Domain | Input | MCP adapter= |
|---|---|---|---|
Ingestor | "code" | Source code directories | code |
MemoryIngestAdapter | "memory" | Markdown/text documents | memory |
JsonIngestAdapter | "generic" | Arbitrary JSON with nodes[] and edges[] | json |
PatentIngestAdapter | "patent" | USPTO/EPO patent XML | patent |
JatsArticleAdapter | "article" | PubMed NLM / JATS Z39.96 XML | article |
BibTexAdapter | "bibtex" | BibTeX bibliography files | bibtex, bib |
RfcAdapter | "rfc" | IETF RFC XML v3 | rfc |
CrossRefAdapter | "crossref" | CrossRef API JSON (DOI metadata) | crossref, doi |
L1ghtIngestAdapter | "light" | L1GHT protocol Markdown | light |
The JSON adapter is the escape hatch for importing graphs from external tools. It expects a JSON document with nodes (array of {id, label, type, tags}) and edges (array of {source, target, relation, weight}).
Universal Document Lane
The universal lane is the best-effort document path for sources that are not authored in L1GHT and are not already handled by a stronger native structured adapter.
Its flow is:
- detect document family
- normalize into a
CanonicalDocument - graphify sections, blocks, tables, citations, entities, and claims
Optional providers can enrich the lane when available:
Trafilaturafor HTML/wiki/article extractionDoclingfor office and broad document canonicalizationMarkItDownas a lightweight fallback laneGROBIDfor scholarly PDFs
This provider stack is intentionally optional. The default green path does not require these providers; richer extraction appears only when the environment supports it.
Document Router (Auto-Detection)
DocumentRouter inspects file content and extension to auto-detect the correct adapter:
#![allow(unused)]
fn main() {
let (format, adapter) = DocumentRouter::detect(path);
let (format, adapter) = DocumentRouter::detect_directory(root); // samples ≤20 files
}
| Detection Method | Format | Heuristic |
|---|---|---|
Extension .bib / .bibtex | BibTeX | Extension only |
Extension .md + Protocol: L1GHT | L1GHT | Content check |
Extension .md without L1GHT, .txt, .rst, .adoc, .html, .pdf, .docx, .pptx, .xlsx | Universal | Extension + universal lane |
Extension .xml / .nxml | Patent, JATS, or RFC | Root element inspection |
Extension .json | CrossRef | Checks for DOI + publisher + type keys |
| Fallback | Code | Default pipeline |
Used via MCP: ingest(adapter="auto"), adapter="document", or adapter="universal" when you want best-effort document normalization directly.
For directory detection, the router samples up to 20 files and returns the dominant format.
Cross-Domain Resolution
CrossDomainResolver merges multiple adapter outputs and discovers cross-domain connections automatically.
Bridge Strategies
| Bridge | Weight | Source | Description |
|---|---|---|---|
same_as | 1.0 | DOI/PMID | Same identifier in different domains → identity edge |
cross_cites | 0.95 | Citation edges | Citation target exists as a full node in another domain |
same_orcid | 0.95 | ORCID tags | Same researcher ORCID across different domains |
same_author | 0.7 | Author name | Same author name across different namespaces |
shared_keyword | 0.6 | Keyword tags | Shared keyword:, article:keyword:, or subject: tags |
citation_chain | 0.5 | Citation adjacency | Transitive A→B→C bridging with decayed weight |
Safety Guards
- Keyword cap: Keywords shared by >20 nodes are ignored to prevent hub explosion.
- Cross-domain only: All bridges require nodes from ≥2 different namespaces. Same-domain matches are skipped.
- Self-loop prevention: Citation chains A→B→A do not generate self-loop edges.
- Deduplication: Nodes with identical external IDs are deduplicated (first wins).
Resolution Statistics
#![allow(unused)]
fn main() {
pub struct ResolutionStats {
pub graphs_merged: usize,
pub total_nodes: u32,
pub total_edges: usize,
pub cross_edges_created: usize,
pub identity_matches: usize,
pub author_bridges: usize,
pub keyword_bridges: usize,
pub orcid_bridges: usize,
pub citation_chains: usize,
}
}
Configuration Reference
#![allow(unused)]
fn main() {
pub struct IngestConfig {
pub root: PathBuf,
pub timeout: Duration, // default: 300s
pub max_nodes: u64, // default: 500_000
pub skip_dirs: Vec<String>, // default: [".git", "node_modules", ...]
pub skip_files: Vec<String>, // default: ["package-lock.json", ...]
pub parallelism: usize, // default: 8 (rayon threads)
}
}
Statistics
Every ingest run produces IngestStats:
#![allow(unused)]
fn main() {
pub struct IngestStats {
pub files_scanned: u64,
pub files_parsed: u64,
pub files_skipped_binary: u64,
pub files_skipped_encoding: u64,
pub nodes_created: u64,
pub edges_created: u64,
pub references_resolved: u64,
pub references_unresolved: u64,
pub label_collisions: u64,
pub elapsed_ms: f64,
pub commit_groups: Vec<Vec<String>>,
}
}
commit_groups is passed to the CoChangeMatrix in m1nd-core after graph finalization, seeding the temporal co-change model with real git history.
MCP Server (m1nd-mcp)
m1nd-mcp is the transport and session layer. It exposes m1nd-core and m1nd-ingest through the live MCP tool surface over JSON-RPC stdio, manages the shared graph lifecycle, handles multi-agent sessions, and turns graph results into a more agent-operational runtime with proof-state, next-step guidance, recovery-oriented errors, observable batch execution, and a local-first document runtime. Use tools/list for the exact count in your current build.
Source: m1nd-mcp/src/
Module Map
| Module | Purpose |
|---|---|
main.rs | Binary entry point, config loading, tokio runtime, SIGINT handling |
server.rs | McpServer, JSON-RPC transport (framed + line), tool schema registry |
session.rs | SessionState, engine lifecycle, auto-persist, perspective/lock management |
tools.rs | Tool handler implementations for the exported MCP surface |
auto_ingest.rs | Document watcher runtime, persisted manifest, queue/tick orchestration |
universal_docs.rs | Canonical document artifacts, provider health, resolve/bindings/drift surfaces |
engine_ops.rs | Read-only engine wrappers for perspective synthesis |
protocol/auto_ingest.rs | Request/response types for document and auto-ingest tools |
perspective/ | Perspective branching, lock state, watcher events |
layer_handlers.rs | Layer-based tool dispatch |
Transport Layer
Dual Transport Mode
m1nd-mcp accepts two JSON-RPC transport formats on stdin, auto-detected per message:
Framed mode (HTTP-style headers):
Content-Length: 142\r\n
\r\n
{"jsonrpc":"2.0","method":"tools/call","params":{"name":"activate","arguments":{"query":"chat","agent_id":"jimi"}},"id":1}
Line mode (raw JSON):
{"jsonrpc":"2.0","method":"tools/call","params":{"name":"activate","arguments":{"query":"chat","agent_id":"jimi"}},"id":1}
Detection is based on the first non-whitespace byte: if it is { or [, the message is treated as line-mode JSON. Otherwise, it is parsed as framed with Content-Length headers. Responses are written in the same mode as the incoming request.
#![allow(unused)]
fn main() {
fn read_request_payload<R: BufRead>(
reader: &mut R,
) -> std::io::Result<Option<(String, TransportMode)>> {
loop {
let buffer = reader.fill_buf()?;
if buffer.is_empty() { return Ok(None); }
let first_non_ws = buffer.iter().copied()
.find(|byte| !byte.is_ascii_whitespace());
let starts_framed = matches!(first_non_ws, Some(byte) if byte != b'{' && byte != b'[');
// ...
}
}
}
This dual-mode support allows m1nd to work with both Claude Code (which uses framed headers) and other MCP clients that send raw line JSON.
Request/Response Format
Requests follow the JSON-RPC 2.0 specification with MCP conventions:
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"query": "chat handler",
"agent_id": "jimi",
"top_k": 20,
"dimensions": ["structural", "semantic", "temporal", "causal"],
"xlr": true
}
},
"id": 1
}
Responses:
{
"jsonrpc": "2.0",
"result": {
"content": [{
"type": "text",
"text": "..."
}]
},
"id": 1
}
The tools/list method returns the live tool schemas with full inputSchema per MCP spec, enabling auto-discovery by any MCP client. Use it as the source of truth for exact names and count in your current build.
Server Lifecycle
sequenceDiagram
participant Main as main.rs
participant Server as McpServer
participant Session as SessionState
participant Tokio as tokio runtime
Main->>Main: load_config()
Main->>Server: McpServer::new(config)
Server->>Session: SessionState::initialize(graph, config, domain)
Session->>Session: Build all engines (orchestrator, temporal, counterfactual, topology, resonance, plasticity)
Main->>Server: server.start()
Main->>Tokio: spawn_blocking(server.serve())
loop Until EOF or SIGINT
Tokio->>Server: read JSON-RPC request
Server->>Server: dispatch to tool handler
Server->>Session: execute tool logic
Session->>Session: auto-persist check
Server->>Tokio: write JSON-RPC response
end
Tokio->>Server: server.shutdown()
Server->>Session: final persist
Configuration
Configuration is resolved in priority order: CLI argument (JSON file path) > environment variables > defaults.
#![allow(unused)]
fn main() {
pub struct McpConfig {
pub graph_source: PathBuf, // default: "./graph_snapshot.json"
pub plasticity_state: PathBuf, // default: "./plasticity_state.json"
pub auto_persist_interval: u32, // default: 50 (queries between persists)
pub learning_rate: f32, // default: 0.08
pub decay_rate: f32, // default: 0.005
pub xlr_enabled: bool, // default: true
pub max_concurrent_reads: usize, // default: 32
pub write_queue_size: usize, // default: 64
pub domain: Option<String>, // default: None ("code")
}
}
Environment variables: M1ND_GRAPH_SOURCE, M1ND_PLASTICITY_STATE, M1ND_XLR_ENABLED.
Startup Sequence
load_config(): Resolve config from CLI args, env vars, or defaults.McpServer::new(config): Load graph snapshot from disk (or create empty graph). Load plasticity state. InitializeSessionStatewith all engines.server.start(): Prepare the server for serving (no-op currently, reserved for future setup).tokio::task::spawn_blocking(server.serve()): The serve loop does synchronous stdio I/O in a blocking task.tokio::select!waits for either SIGINT (ctrl_c()) or serve loop completion.
Shutdown
On SIGINT or stdin EOF:
server.shutdown(): Final persist of graph and plasticity state.- Process exits.
The atomic write pattern (temp file + rename) ensures that even if shutdown is interrupted, the previous snapshot remains intact.
Tool Registration and Dispatch
Schema Registry
tool_schemas() returns a JSON array of the live tool definitions with full inputSchema objects. Each tool specifies:
name: Canonical live registry name (e.g.,activate)description: Human-readable purposeinputSchema: JSON Schema withproperties,required,type, defaults
Some clients may display transport-prefixed compatibility aliases, but the live registry returned by tools/list uses the bare tool names.
The current surface now includes a document runtime in addition to the code graph runtime:
document_resolvedocument_provider_healthdocument_bindingsdocument_driftauto_ingest_startauto_ingest_stopauto_ingest_statusauto_ingest_tick
Example schema entry:
{
"name": "activate",
"description": "Spreading activation query across the graph",
"inputSchema": {
"type": "object",
"properties": {
"query": { "type": "string" },
"agent_id": { "type": "string" },
"top_k": { "type": "integer", "default": 20 },
"dimensions": {
"type": "array",
"items": { "type": "string", "enum": ["structural", "semantic", "temporal", "causal"] },
"default": ["structural", "semantic", "temporal", "causal"]
},
"xlr": { "type": "boolean", "default": true }
},
"required": ["query", "agent_id"]
}
}
Tool Categories
The live tool surface is organized into functional groups. Use tools/list for the exhaustive registry in your current build.
Core Query Tools:
| Tool | Purpose | Key Parameters |
|---|---|---|
activate | Spreading activation query | query, top_k, dimensions, xlr |
impact | Blast radius analysis | node_id, direction (forward/reverse/both) |
missing | Structural hole detection | query, min_sibling_activation |
why | Path explanation between nodes | source, target, max_hops |
warmup | Task-based priming | task_description, boost_strength |
counterfactual | Node removal simulation | node_ids, include_cascade |
predict | Co-change prediction | changed_node, top_k, include_velocity |
fingerprint | Equivalence detection | target_node |
drift | Weight changes since baseline | since |
learn | Hebbian feedback | feedback (correct/wrong) |
resonate | Standing wave analysis | query, frequencies, num_harmonics |
seek | Seed-level node lookup | query |
scan | Full graph summary | (none) |
Graph Mutation Tools:
| Tool | Purpose |
|---|---|
ingest | Ingest codebase into graph |
health | Server diagnostics |
timeline | Temporal event timeline |
Perspective Tools:
| Tool | Purpose |
|---|---|
perspective_start | Open a named perspective branch |
perspective_close | Close a perspective |
perspective_list | List open perspectives for an agent |
perspective_inspect | View perspective state and cached results |
perspective_compare | Diff two perspectives |
perspective_branch | Fork a perspective |
perspective_suggest | Generate suggestions from perspective context |
perspective_back | Undo last perspective operation |
perspective_peek | Read source file content from within perspective |
perspective_follow | Follow links from perspective results |
perspective_routes | View cached activation routes |
perspective_affinity | Cross-perspective affinity analysis |
Lock Tools:
| Tool | Purpose |
|---|---|
lock_create | Create a baseline snapshot for change tracking |
lock_diff | Diff current state against lock baseline |
lock_rebase | Update lock baseline to current state |
lock_release | Release a lock |
lock_watch | Watch for changes against lock baseline |
Trail Tools:
| Tool | Purpose |
|---|---|
trail_save | Save current exploration trail |
trail_list | List saved trails |
trail_resume | Resume a saved trail |
trail_merge | Merge trails |
Topology Tools:
| Tool | Purpose |
|---|---|
federate | Cross-graph federation |
diverge | Divergence analysis between graph regions |
differential | Differential activation (compare two queries) |
hypothesize | Generate hypotheses from graph structure |
validate_plan | Validate an implementation plan against graph |
Dispatch
All tools require an agent_id parameter. The serve loop matches the tool name from the JSON-RPC request and dispatches to the corresponding handler in tools.rs. The handler extracts parameters from the arguments JSON object, acquires the appropriate lock on SessionState, executes the operation, and returns the result as a JSON-RPC response.
Session Management
SessionState
SessionState is the central state object. It owns the graph, all engines, and all session metadata:
#![allow(unused)]
fn main() {
pub struct SessionState {
pub graph: SharedGraph, // Arc<RwLock<Graph>>
pub domain: DomainConfig,
pub orchestrator: QueryOrchestrator, // activation + XLR + semantic
pub temporal: TemporalEngine,
pub counterfactual: CounterfactualEngine,
pub topology: TopologyAnalyzer,
pub resonance: ResonanceEngine,
pub plasticity: PlasticityEngine,
pub queries_processed: u64,
pub auto_persist_interval: u32, // default: 50
pub start_time: Instant,
pub last_persist_time: Option<Instant>,
pub graph_path: PathBuf,
pub plasticity_path: PathBuf,
pub sessions: HashMap<String, AgentSession>,
// Perspective/lock state
pub graph_generation: u64,
pub plasticity_generation: u64,
pub cache_generation: u64,
pub perspectives: HashMap<(String, String), PerspectiveState>,
pub locks: HashMap<String, LockState>,
pub perspective_counter: HashMap<String, u64>,
pub lock_counter: HashMap<String, u64>,
pub pending_watcher_events: Vec<WatcherEvent>,
pub perspective_limits: PerspectiveLimits,
pub peek_security: PeekSecurityConfig,
pub ingest_roots: Vec<String>,
}
}
SharedGraph
SharedGraph = Arc<parking_lot::RwLock<Graph>> provides concurrent access:
- Reads (activation, impact, predict, etc.): Acquire read lock. Multiple concurrent reads allowed.
- Writes (ingest, learn): Acquire write lock. Exclusive access. Blocks all readers.
parking_lot::RwLock is used instead of std::sync::RwLock for two reasons:
- Writer starvation prevention: parking_lot uses a fair queue, so plasticity writes do not starve behind continuous read queries.
- Performance: parking_lot’s implementation is faster for the read-heavy, write-rare access pattern of m1nd.
Engine Rebuild
After ingestion replaces the graph, all engines must be rebuilt because they hold indexes derived from the old graph:
#![allow(unused)]
fn main() {
pub fn rebuild_engines(&mut self) -> M1ndResult<()> {
{
let graph = self.graph.read();
self.orchestrator = QueryOrchestrator::build(&graph)?;
self.temporal = TemporalEngine::build(&graph)?;
self.plasticity = PlasticityEngine::new(
&graph, PlasticityConfig::default(),
);
}
self.invalidate_all_perspectives();
self.mark_all_lock_baselines_stale();
self.graph_generation += 1;
self.cache_generation = self.cache_generation.max(self.graph_generation);
Ok(())
}
}
The rebuild also invalidates all perspective and lock state, bumping generation counters so that stale caches are detected.
Auto-Persist
Every auto_persist_interval queries (default 50), the session persists state to disk:
- Graph first:
save_graph()writes the CSR graph to JSON via atomic temp-file-then-rename. - Plasticity second:
export_state()extracts per-edgeSynapticState, thensave_plasticity_state()writes to JSON. - If graph save fails, plasticity save is skipped (prevents inconsistent state).
- If plasticity save fails after graph succeeds, a warning is logged but the server continues.
#![allow(unused)]
fn main() {
pub fn persist(&mut self) -> M1ndResult<()> {
let graph = self.graph.read();
m1nd_core::snapshot::save_graph(&graph, &self.graph_path)?;
match self.plasticity.export_state(&graph) {
Ok(states) => {
if let Err(e) = save_plasticity_state(&states, &self.plasticity_path) {
eprintln!("[m1nd] WARNING: plasticity persist failed: {}", e);
}
}
Err(e) => eprintln!("[m1nd] WARNING: plasticity export failed: {}", e),
}
self.last_persist_time = Some(Instant::now());
Ok(())
}
}
Multi-Agent Support
Agent Sessions
Each unique agent_id gets an AgentSession:
#![allow(unused)]
fn main() {
pub struct AgentSession {
pub agent_id: String,
pub first_seen: Instant,
pub last_seen: Instant,
pub query_count: u64,
}
}
Sessions are created on first query and updated on each subsequent query. The last_seen timestamp enables timeout-based cleanup.
Agent Isolation
All agents share one graph (writes are immediately visible), but isolation is provided through:
- Perspectives: Per-agent branching views with independent route caches. A perspective opened by Agent A is invisible to Agent B.
- Locks: Per-agent change tracking baselines. Each agent can create independent locks to track changes from their perspective.
- Query Memory: The plasticity engine’s ring buffer is global (shared learning), but perspective-level route caches are per-agent.
Generation Counters
Three generation counters detect stale state:
| Counter | Bumped By | Purpose |
|---|---|---|
graph_generation | Ingest, rebuild_engines | Detects stale engine indexes |
plasticity_generation | Learn | Detects stale plasticity state |
cache_generation | max(graph_gen, plasticity_gen) | Unified staleness for perspective caches |
When a perspective’s cached results were computed at a different generation than the current cache_generation, the perspective is marked stale and results are recomputed on next access.
Perspective System
Perspectives are per-agent named branches that cache activation results and enable comparative analysis.
Lifecycle
stateDiagram-v2
[*] --> Open: perspective_start
Open --> Active: first query (activate/impact/etc)
Active --> Active: additional queries
Active --> Branched: perspective_branch
Active --> Stale: graph mutation (ingest/learn)
Stale --> Active: re-query (auto-refresh)
Active --> [*]: perspective_close
Open --> [*]: perspective_close
Perspective IDs
Generated as persp_{agent_prefix}_{counter:03}. Each agent has an independent monotonic counter. Example: Agent “jimi” creates perspectives persp_jimi_001, persp_jimi_002, etc.
Resource Limits
PerspectiveLimits caps resource usage:
- Maximum open perspectives per agent
- Maximum total perspectives across all agents
- Maximum locks per agent
Peek Security
perspective_peek allows reading source file content from within a perspective context. Security restrictions:
- Files must be within an
ingest_rootsallow-list (populated during ingest). - Path traversal (
..) is blocked. - Symlinks outside the allow-list are rejected.
Lock System
Locks capture a baseline snapshot of graph state for change tracking.
Lock Lifecycle
stateDiagram-v2
[*] --> Created: lock_create
Created --> Active: baseline captured
Active --> Diffed: lock_diff (non-destructive)
Diffed --> Active: continue tracking
Active --> Rebased: lock_rebase (new baseline)
Rebased --> Active: continue tracking
Active --> Stale: graph mutation
Stale --> Rebased: lock_rebase
Active --> [*]: lock_release
Operations
- lock_create: Captures current graph state (generation counter + weight snapshot for tracked edges).
- lock_diff: Compares current state against baseline. Reports weight changes, new/removed edges. Non-destructive.
- lock_rebase: Updates baseline to current state. Clears staleness flag.
- lock_release: Frees the lock.
- lock_watch: Returns pending watcher events (changes that occurred since last check).
When a graph mutation occurs (ingest, learn), all locks are marked baseline_stale = true. lock_diff reports this staleness and suggests lock_rebase.
Engine Operations (Perspective Synthesis)
engine_ops.rs provides read-only wrappers around engine operations for use within perspective synthesis. These wrappers operate under a SynthesisBudget:
- Max calls: 8 engine calls per synthesis operation.
- Wall-clock timeout: 500ms total.
This prevents perspective synthesis from monopolizing the server. Each wrapper (activate_readonly, impact_readonly, etc.) checks budget before executing and returns a budget-exhausted error if limits are exceeded.
Concurrency Model
flowchart TD
STDIN["stdin (JSON-RPC)"]
TOKIO["tokio::task::spawn_blocking"]
SERVE["McpServer.serve()<br/>(synchronous loop)"]
RW["SharedGraph<br/>Arc<parking_lot::RwLock<Graph>>"]
READ["Read Lock<br/>(activate, impact, predict, ...)"]
WRITE["Write Lock<br/>(ingest, graph swap)"]
PERSIST["Auto-Persist<br/>(write lock on graph)"]
STDIN --> TOKIO
TOKIO --> SERVE
SERVE --> READ
SERVE --> WRITE
SERVE --> PERSIST
READ --> RW
WRITE --> RW
PERSIST --> RW
The serve loop is single-threaded (synchronous stdio I/O), but graph access is concurrent-safe through SharedGraph. Within a single request:
- Read operations acquire a read lock (shared, non-blocking with other reads).
- Write operations acquire a write lock (exclusive, blocks all other access).
- Plasticity weight updates use atomic CAS (no lock needed for individual weight writes).
The tokio runtime is used solely for the select! between SIGINT and serve loop completion. All actual tool processing happens synchronously within spawn_blocking.
Spreading Activation
Spreading activation is the core query mechanism in m1nd. Instead of searching for text matches, m1nd fires a signal into a weighted graph and observes where the energy propagates. The resulting activation pattern tells you what is structurally, semantically, temporally, and causally related to your query.
The neuroscience origin
The concept comes from cognitive psychology. In 1975, Allan Collins and Elizabeth Loftus proposed that human semantic memory is organized as a network of interconnected concepts. When you think of “doctor,” activation spreads along weighted connections to “nurse,” “hospital,” “stethoscope” – but not to “bicycle.” The strength of the connection determines how much activation passes through. Distant or weak connections receive less energy. The pattern of activated nodes is the meaning.
m1nd applies this model to code. Files, functions, classes, and modules become nodes. Import relationships, call chains, type references, and co-change history become weighted edges. Querying “authentication” does not find the string “authentication” – it fires signal into nodes that match and watches energy flow outward through the graph’s structure.
Four dimensions of activation
Most spreading activation systems operate on a single graph. m1nd runs four independent activation passes and fuses the results. Each dimension captures a different kind of relationship.
D1: Structural
Graph distance, edge types, and edge weights. This is classical spreading activation – signal decays as it hops along edges. A file that imports another file is one hop away. A function that calls through two intermediate modules is three hops away. The signal strength at each node tells you how structurally close it is to the query.
The structural dimension uses m1nd’s CSR (Compressed Sparse Row) adjacency matrix with forward and reverse indices. PageRank scores computed at ingest time provide a static importance baseline.
D2: Semantic
Token overlap and naming patterns. This dimension scores nodes based on how their identifiers (file names, function names, class names) relate to the query text. m1nd uses a TF-IDF weighted trigram index built at ingest time. The trigrams for “authentication” overlap with “auth_handler,” “authenticate_user,” and “auth_middleware” – without requiring exact string matching.
Co-occurrence embeddings from short random walks on the graph add a second signal: nodes that tend to appear near each other in the graph structure score higher, even if their names share no characters.
D3: Temporal
Recency and change frequency. Files modified recently score higher than files untouched for months. Files that change often (high velocity) score higher than stable ones. The temporal dimension uses exponential decay with a 7-day half-life:
recency = exp(-0.693 * age_seconds / (168 * 3600))
score = recency * 0.6 + frequency * 0.4
This ensures that recently active parts of the codebase surface in activation results, even if they are structurally distant.
D4: Causal
Forward and backward causal chain traversal. Edges with causal_strength > 0 (derived from call chains, error propagation paths, and stacktrace mappings) form a causal subgraph. Signal propagates forward along causal edges with full strength, and backward with a 0.7 discount factor. This surfaces nodes that cause or are caused by the query target.
The activation wavefront algorithm
m1nd implements two propagation strategies and a hybrid selector that chooses between them at runtime.
WavefrontEngine (BFS)
Breadth-first, depth-parallel propagation. All active nodes at the current depth fire simultaneously. Signal accumulates into the next depth’s buffer via a scatter-max operation: when multiple sources send signal to the same target, only the strongest arrival survives.
for each depth 0..max_depth:
for each node in current frontier:
if activation[node] < threshold: skip
for each outgoing edge (node -> target):
signal = activation[node] * edge_weight * decay
if inhibitory: signal = -signal * inhibitory_factor
if signal > activation[target]:
activation[target] = signal // scatter-max
add target to next frontier
frontier = next_frontier
This processes all nodes at the same distance in one pass. Optimal when many seeds fire simultaneously or the graph has high average degree.
HeapEngine (priority queue)
Max-heap propagation. The strongest signal fires first. Early-terminates when the heap top drops below threshold. Uses a Bloom filter for fast visited checks (probabilistic, with FPR ~ 1% at typical graph sizes).
Optimal for sparse queries: few seeds, low graph density. The seed-to-node ratio and average degree determine the crossover point.
HybridEngine (auto-select)
The HybridEngine chooses at runtime:
#![allow(unused)]
fn main() {
fn prefer_heap(graph: &Graph, seed_count: usize) -> bool {
let seed_ratio = seed_count as f64 / graph.num_nodes().max(1) as f64;
seed_ratio < 0.001 && graph.avg_degree() < 8.0
}
}
If fewer than 0.1% of nodes are seeds and the average degree is below 8, the HeapEngine wins. Otherwise, the WavefrontEngine is faster because it avoids heap overhead for dense propagation.
Seed node selection
Before activation can begin, the query text must be converted into seed nodes. The SeedFinder performs a multi-strategy match against all node labels:
| Strategy | Relevance score | Example |
|---|---|---|
| Exact label match | 1.0 | “auth” matches node “auth” |
| Prefix match | 0.9 | “auth” matches “auth_handler” |
| Substring match | 0.8 | “auth” matches “pre_auth_check” |
| Tag match | 0.85 | “auth” matches node tagged “authentication” |
| Fuzzy trigram | 0.7 * sim | “authenticaton” (typo) matches “authentication” |
Trigram similarity uses cosine distance between character n-gram frequency vectors. A match threshold of 0.3 catches most typos without generating noise.
For enhanced matching, find_seeds_semantic runs a two-phase process: basic seed finding produces candidates, then the semantic engine re-ranks them with a 0.6/0.4 blend of basic and semantic scores. Maximum 200 seeds are returned (capped by MAX_SEEDS).
Decay and damping
Signal attenuates at every hop:
signal_at_target = signal_at_source * edge_weight * decay_factor
The default decay factor is 0.55 per hop (from DecayFactor::DEFAULT). After 3 hops, the signal has decayed to 0.55^3 = 16.6% of its original strength. After 5 hops (the default max_depth), it is at 5%. The max depth is hard-capped at 20 to prevent runaway propagation.
Additional controls:
- Threshold (default 0.04): signal below this value is discarded. This prunes the wavefront and prevents ghost activations.
- Saturation cap (default 1.0): seeds cannot inject more than this amount of activation. Prevents a single high-PageRank node from dominating.
- Inhibitory factor (default 0.5): inhibitory edges (negative relationships like “replaces,” “deprecates”) apply capped proportional suppression rather than simple negation.
How dimensions are combined
After all four dimensions produce their scored node lists, the merge_dimensions function fuses them into a single ranking.
Weighted sum
Each dimension has a base weight:
#![allow(unused)]
fn main() {
pub const DIMENSION_WEIGHTS: [f32; 4] = [0.35, 0.25, 0.15, 0.25];
// D1 D2 D3 D4
// Struct Seman Temp Causal
}
If a dimension produces no results (e.g., no temporal data available), its weight is redistributed proportionally to the active dimensions. This adaptive redistribution prevents zero-result dimensions from diluting the signal.
Resonance bonus
Nodes that appear in multiple dimensions receive a multiplicative bonus:
#![allow(unused)]
fn main() {
pub const RESONANCE_BONUS_3DIM: f32 = 1.3; // 3 dimensions agree
pub const RESONANCE_BONUS_4DIM: f32 = 1.5; // all 4 dimensions agree
}
A node that scores above 0.01 in all four dimensions receives a 1.5x boost. This is a powerful signal: structural proximity, naming similarity, recent change activity, and causal relationship all point to the same node. These are the nodes the agent should examine first.
The check order matters. m1nd checks dim_count >= 4 before dim_count >= 3. The original Python implementation had a dead elif branch that never triggered the 4-dimension bonus – this was fixed during the Rust port (FM-ACT-001).
PageRank boost
After dimension fusion, each node receives a small PageRank boost:
#![allow(unused)]
fn main() {
const PAGERANK_BOOST: f32 = 0.1;
node.activation += node.pagerank * PAGERANK_BOOST;
}
This ensures that structurally important nodes (high PageRank) surface slightly higher in results, all else being equal. The factor is intentionally small (10% of PageRank) to avoid overwhelming the activation signal.
Visual example
Consider a simplified graph of a backend application:
graph LR
main["main.py<br/>PR: 0.15"]
auth["auth.py<br/>PR: 0.12"]
session["session.py<br/>PR: 0.08"]
middleware["middleware.py<br/>PR: 0.10"]
user["user_model.py<br/>PR: 0.06"]
db["database.py<br/>PR: 0.09"]
routes["routes.py<br/>PR: 0.11"]
main -->|imports| auth
main -->|imports| routes
routes -->|imports| auth
routes -->|imports| middleware
auth -->|imports| session
auth -->|imports| user
auth -->|imports| db
session -->|imports| db
middleware -->|imports| session
user -->|imports| db
style auth fill:#ff6b6b,stroke:#333,color:#fff
style session fill:#ffa07a,stroke:#333
style middleware fill:#ffa07a,stroke:#333
style user fill:#ffd700,stroke:#333
style db fill:#ffd700,stroke:#333
style routes fill:#ffa07a,stroke:#333
style main fill:#ffd700,stroke:#333
Query: "authentication"
Step 1: Seed selection. auth.py matches with relevance 0.9 (prefix match). session.py matches with relevance 0.85 (tag “auth” in its tags). These become seeds with activation 0.9 and 0.85 respectively.
Step 2: Structural propagation (D1). From auth.py (0.9), signal flows to:
session.py: 0.9 * 1.0 * 0.55 = 0.495user_model.py: 0.9 * 1.0 * 0.55 = 0.495database.py: 0.9 * 1.0 * 0.55 = 0.495
From session.py (already at 0.85 from seed, plus 0.495 from structural – scatter-max keeps the higher):
database.py: 0.85 * 1.0 * 0.55 = 0.467 (less than existing 0.495, discarded)
Reverse propagation (via reverse CSR):
routes.pyimportsauth.py, soroutes.pyreceives: 0.9 * 1.0 * 0.55 = 0.495main.pyimportsauth.py, somain.pyreceives: 0.9 * 1.0 * 0.55 = 0.495middleware.pyimportssession.py, somiddleware.pyreceives: 0.85 * 1.0 * 0.55 = 0.467
Step 3: Merge. After running all four dimensions, the final ranking (with resonance bonuses for multi-dimension hits) produces:
| Node | D1 | D2 | D3 | D4 | Dims | Bonus | Final |
|---|---|---|---|---|---|---|---|
| auth.py | 0.90 | 0.88 | 0.70 | 0.65 | 4 | 1.5x | 0.88 |
| session.py | 0.85 | 0.40 | 0.50 | 0.45 | 4 | 1.5x | 0.64 |
| user_model.py | 0.50 | 0.30 | 0.10 | 0.20 | 4 | 1.5x | 0.40 |
| middleware.py | 0.47 | 0.15 | 0.60 | 0.30 | 4 | 1.5x | 0.39 |
| database.py | 0.50 | 0.05 | 0.20 | 0.10 | 3 | 1.3x | 0.28 |
The result tells the agent: start with auth.py, then examine session.py and user_model.py. The four-dimension resonance bonus pushes middleware.py ahead of database.py even though their structural scores are similar – because middleware was recently modified (temporal) and sits on a causal chain (causal).
What this means in practice
An agent calling activate("authentication") gets back a ranked list of the 20 most relevant nodes in 31ms. It does not need to know file names, directory structure, or grep patterns. The activation pattern encodes structural proximity, naming affinity, temporal relevance, and causal relationships – fused into a single score.
Over time, as the agent provides feedback via learn, the edge weights shift through Hebbian plasticity (see Hebbian Plasticity). The paths that led to useful results strengthen. The paths that led to noise weaken. The same query tomorrow will produce better results.
Hebbian Plasticity
m1nd’s graph learns. Every query changes the edge weights. Paths that lead to useful results get stronger. Paths that lead to noise get weaker. Over time, the graph evolves to match how your team thinks about your codebase. No other code intelligence tool does this.
The neuroscience principle
In 1949, Donald Hebb proposed a theory of synaptic learning: “When an axon of cell A is near enough to excite cell B and repeatedly or persistently takes part in firing it, some growth process or metabolic change takes place in one or both cells such that A’s efficiency, as one of the cells firing B, is increased.”
The popular summary: neurons that fire together wire together.
The converse is equally important: neurons that fire independently weaken their connections. This bidirectional learning – strengthening co-active pathways, weakening inactive ones – is the foundation of how biological neural networks adapt to experience.
m1nd applies this principle to code graphs. Nodes are modules. Edges are relationships. “Firing” means being activated by a spreading activation query. When an agent confirms that a result was useful, the edges connecting those activated nodes strengthen. When the agent marks results as wrong, those paths weaken. The graph remembers what worked.
The five-step learning cycle
Every query that passes through m1nd’s PlasticityEngine triggers a five-step update cycle. This runs automatically – no explicit training phase required.
Step 1: Hebbian strengthening
For every edge where both the source and target were activated in the query results, the weight increases:
delta_w = learning_rate * activation_source * activation_target
new_weight = min(current_weight + delta_w, weight_cap)
The default learning rate is 0.08 (from LearningRate::DEFAULT). The weight cap is 3.0 – no edge can grow stronger than 3x its original weight. This prevents runaway positive feedback.
From the source code:
#![allow(unused)]
fn main() {
// Hebbian: delta_w = lr * act_src * act_tgt
let delta = lr * src_act.get() * tgt_act;
let new_weight = (current + delta).min(cap);
}
The product activation_source * activation_target means that strongly co-activated pairs get the largest boost. A pair where both nodes scored 0.8 gets 0.08 * 0.8 * 0.8 = 0.051 added to their edge weight. A pair where one scored 0.2 gets only 0.08 * 0.8 * 0.2 = 0.013. This is faithful to Hebb’s rule: the strength of the update is proportional to the correlation of the firing.
Step 2: Synaptic decay
Edges whose source nodes were not activated in this query decay slightly:
new_weight = max(current_weight * (1 - decay_rate), weight_floor)
The default decay rate is 0.005 per query (0.5%). The weight floor is 0.05 – edges never decay below 5% of their original weight. This ensures that even unused paths retain some connectivity, preventing the graph from fragmenting.
#![allow(unused)]
fn main() {
let decay_factor = 1.0 - self.config.decay_rate.get(); // 0.995
let new_weight = (current * decay_factor).max(floor);
}
The asymmetry is intentional. Strengthening applies a fixed delta (additive). Decay applies a multiplicative factor. This means strong edges resist decay (a weight-3.0 edge loses 0.015 per query) while weak edges decay faster in relative terms (a weight-0.1 edge loses 0.0005 per query). Frequently activated paths grow monotonically. Rarely activated paths slowly fade but never disappear.
Step 3: Long-Term Potentiation / Long-Term Depression
After an edge has been strengthened 5 consecutive times (the LTP threshold), it receives a one-time +0.15 bonus:
#![allow(unused)]
fn main() {
if !graph.edge_plasticity.ltp_applied[j]
&& graph.edge_plasticity.strengthen_count[j] >= self.config.ltp_threshold
{
let new_weight = (current + self.config.ltp_bonus.get()).min(cap);
graph.edge_plasticity.ltp_applied[j] = true;
}
}
Conversely, after an edge has been weakened 5 consecutive times (the LTD threshold), it receives a one-time -0.15 penalty:
#![allow(unused)]
fn main() {
if !graph.edge_plasticity.ltd_applied[j]
&& graph.edge_plasticity.weaken_count[j] >= self.config.ltd_threshold
{
let new_weight = (current - self.config.ltd_penalty.get()).max(floor);
graph.edge_plasticity.ltd_applied[j] = true;
}
}
These thresholds model biological LTP/LTD – the transition from short-term to long-term memory. Five consecutive activations is a signal of sustained relevance, not a fluke. The one-time bonus/penalty is permanent: it does not reset, and it does not apply again for the same edge. This prevents unbounded weight inflation from repeated queries.
Step 4: Homeostatic normalization
After strengthening and LTP/LTD, the total incoming weight for each node is checked against a ceiling of 5.0:
#![allow(unused)]
fn main() {
if total_incoming > ceiling {
let scale = ceiling / total_incoming;
for each incoming edge:
new_weight = current * scale;
}
}
This is homeostatic plasticity – a biological mechanism that prevents individual neurons from becoming over-stimulated. In m1nd, it prevents hub nodes (like config.py or main.py) from accumulating so much incoming weight that they dominate every activation query regardless of the actual query content.
The normalization is proportional: all incoming edges are scaled by the same factor. This preserves relative strengths while enforcing an absolute ceiling. A node with 10 incoming edges at weight 1.0 each (total 10.0) would have all edges scaled to 0.5, bringing the total to 5.0.
Step 5: Query memory recording
The query, its seeds, and its activated nodes are recorded in a bounded ring buffer (capacity: 1000 queries). This memory serves two purposes:
-
Priming signal: future queries that share seeds with past queries get a boost from nodes that frequently appeared in those past results. This implements a form of associative memory – “things I looked at near authentication tend to be relevant when I look at authentication again.”
-
Seed bigrams: pairs of seeds that co-occur across multiple queries are tracked. This supports the
warmuptool, which uses query memory to pre-activate frequently queried paths.
How learn works
The automatic plasticity cycle runs on every activate call. But agents can also provide explicit feedback via learn:
Positive feedback: learn(feedback="correct", node_ids=[...])
When an agent confirms that specific nodes were useful:
- The edges connecting those nodes are strengthened with an amplified Hebbian update (the activation values are set to 1.0 for the confirmed nodes, producing maximum
delta_w). - The strengthen counters increment, moving edges closer to the LTP threshold.
- Query memory records the confirmed nodes with high weight, boosting them in future priming signals.
Negative feedback: learn(feedback="wrong", node_ids=[...])
When an agent marks results as irrelevant:
- The edges connecting those nodes receive decay as if they were inactive, even though they were activated.
- The weaken counters increment, moving edges closer to the LTD threshold.
- Query memory records the rejection, reducing the priming signal for those nodes.
This feedback loop is what makes m1nd adaptive. A team that mostly works on the payment system will gradually strengthen all paths around payment-related modules. An agent investigating authentication will produce different results than an agent investigating billing – even on the same codebase – because their feedback histories have shaped different edge weight landscapes.
Plasticity state persistence
The learned weights are valuable. Losing them means losing the graph’s adaptation to your workflow. m1nd persists plasticity state in two ways:
Per-edge state (SynapticState)
Each edge’s plasticity state is captured as a serializable record:
#![allow(unused)]
fn main() {
pub struct SynapticState {
pub source_label: String,
pub target_label: String,
pub relation: String,
pub original_weight: f32,
pub current_weight: f32,
pub strengthen_count: u16,
pub weaken_count: u16,
pub ltp_applied: bool,
pub ltd_applied: bool,
}
}
This is exported via PlasticityEngine::export_state() and persisted to M1ND_PLASTICITY_STATE (a JSON file). The export includes a NaN firewall (FM-PL-001): any non-finite weight falls back to the original weight. The write is atomic (temp file + rename, FM-PL-008) to prevent corruption on crash.
Importing state
When m1nd restarts, import_state restores learned weights. Edge identity matching uses (source_label, target_label, relation) triples – not numeric indices – because re-ingesting the codebase may produce different node numbering. This means plasticity survives codebase re-ingestion: if auth.py -> session.py was strengthened, that strengthening persists even if auth.py gets a different NodeId after re-ingest.
Weights are clamped to [weight_floor, weight_cap] on import. Invalid JSON triggers a schema validation error (FM-PL-007) rather than corrupting the graph.
Persistence frequency
The graph auto-persists every 50 queries and on server shutdown. This is a balance between durability (don’t lose too much learning) and disk I/O (don’t write on every query).
The drift tool
After persistence, the natural question is: how much has the graph changed? The drift tool answers this.
drift compares the current edge weights against their original (ingest-time) baselines and reports:
- Total edges changed: how many edges have weights different from their original values.
- Average weight change: the mean absolute delta across all modified edges.
- Top strengthened edges: the edges that have grown the most relative to their baseline.
- Top weakened edges: the edges that have decayed the most.
- LTP/LTD counts: how many edges have crossed the long-term potentiation or depression thresholds.
This is designed for session recovery. When an agent starts a new session, drift tells it what has changed since the graph was last loaded. The agent can see that “paths around the payment module strengthened significantly since yesterday” and adjust its investigation accordingly.
Session 1:
ingest -> activate("auth") -> agent uses results -> learn(correct)
→ 740 edges strengthened, 12,340 edges decayed slightly
Session 2:
drift(since=session_1) -> auth paths now 15% stronger on average
activate("auth") -> better results, faster convergence to useful nodes
Session N:
the graph has adapted to how your team thinks about auth
How this makes the graph adapt
The combination of automatic Hebbian updates, explicit feedback, LTP/LTD thresholds, and homeostatic normalization creates a self-tuning system:
-
Short-term adaptation (within a session): edges on frequently queried paths strengthen immediately. The next query about the same topic converges faster.
-
Long-term memory (across sessions): edges that cross the LTP threshold receive a permanent bonus. Persistent investigation patterns are encoded in the graph structure.
-
Forgetting (controlled decay): paths that are never queried slowly fade toward the weight floor. This prevents the graph from becoming saturated with historical patterns that no longer reflect the codebase’s structure.
-
Stability (homeostatic normalization): no node can accumulate unbounded incoming weight. Hub nodes stay important but do not become black holes that absorb all activation energy.
The result is a graph that starts generic (all edges at their ingest-time weights, reflecting code structure) and gradually becomes specific (edges weighted by how you use the codebase). Two teams working on the same repository will develop different plasticity landscapes. This is a feature: the graph models the team’s mental model, not just the code’s static structure.
Constants reference
| Parameter | Default | Purpose |
|---|---|---|
DEFAULT_LEARNING_RATE | 0.08 | Hebbian delta_w scaling |
DEFAULT_DECAY_RATE | 0.005 | Per-query inactive edge decay |
LTP_THRESHOLD | 5 | Consecutive strengthens for long-term bonus |
LTD_THRESHOLD | 5 | Consecutive weakens for long-term penalty |
LTP_BONUS | 0.15 | One-time weight bonus at LTP threshold |
LTD_PENALTY | 0.15 | One-time weight penalty at LTD threshold |
HOMEOSTATIC_CEILING | 5.0 | Max total incoming weight per node |
WEIGHT_FLOOR | 0.05 | Minimum edge weight (never decays below) |
WEIGHT_CAP | 3.0 | Maximum edge weight (never strengthens above) |
DEFAULT_MEMORY_CAPACITY | 1000 | Ring buffer size for query memory |
CAS_RETRY_LIMIT | 64 | Atomic weight update retries |
XLR Noise Cancellation
Every code graph is noisy. Utility modules, shared constants, logging wrappers – they connect to everything, and they show up in every activation query whether they are relevant or not. m1nd solves this with a technique borrowed from professional audio engineering: differential signal processing with inverted channels. The same principle that makes balanced XLR cables immune to electromagnetic interference makes m1nd’s activation queries immune to structural noise.
The origin: balanced audio cables
In a recording studio, audio signals travel over long cables. The cable acts as an antenna, picking up electromagnetic interference (EMI) from power supplies, lighting, and radio transmissions. An unbalanced cable delivers a noisy signal: the audio you want plus the interference you do not.
A balanced XLR cable solves this by carrying two copies of the audio signal: one normal (hot, pin 2) and one inverted (cold, pin 3). Both signals travel the same physical path, so they pick up the same interference. At the receiving end, the cold signal is flipped back and added to the hot signal. The audio doubles in strength. The interference – identical on both channels – cancels out.
Hot channel: signal + noise
Cold channel: -signal + noise
At receiver: hot - cold = (signal + noise) - (-signal + noise) = 2 * signal
The noise vanishes. The signal survives.
The problem in code graphs
When you ask m1nd “what is related to the payment system?”, structural spreading activation fires from payment-related seed nodes and propagates outward. The result includes genuinely relevant modules (billing, invoicing, refunds) but also noisy modules that connect to everything:
config.py– imported by 40+ fileslogger.py– called from every moduledatabase.py– the persistence layer for everythingutils.py– helper functions used everywhere__init__.py– package marker files
These modules score high in structural activation not because they are relevant to payments, but because they are structurally central. They are the electromagnetic interference of code graphs: noise that correlates with structure, not with the query.
Text search (grep, ripgrep) cannot solve this. It finds files that contain the word “payment,” which misses files that handle payments but never use that string. Static analysis cannot solve it either – the call graph from payment modules genuinely includes config.py and logger.py.
The problem is not that these edges are wrong. They are structurally correct. The problem is that they carry noise mixed in with signal, and the noise is stronger.
How m1nd implements differential processing
m1nd’s AdaptiveXlrEngine implements a six-stage pipeline that mirrors the physics of balanced XLR cables.
Stage 1: Select anti-seeds
The cold channel needs its own starting points – nodes that represent “not the query.” m1nd calls these anti-seeds. Good anti-seeds are structurally similar to the seeds (similar degree, similar graph position) but semantically distant (low neighborhood overlap).
The selection criteria:
#![allow(unused)]
fn main() {
// Degree ratio filter: anti-seed must have similar connectivity
if avg_seed_degree > 0.0 {
let ratio = degree / avg_seed_degree;
if ratio < 0.3 { continue; } // Too peripheral
}
// Jaccard similarity: anti-seed must have different neighbors
let jaccard = intersection as f32 / union_size as f32;
if jaccard > 0.2 { continue; } // Too similar to seeds
}
A node with similar degree but different neighbors is the ideal anti-seed. It will activate the same noisy hub nodes (because it has similar connectivity) but different domain-specific nodes (because it lives in a different part of the graph).
By default, 3 anti-seeds are selected.
Stage 2: Compute seed immunity
Nodes close to the seeds – within 2 BFS hops – are marked as immune to cold-channel cancellation. This prevents the anti-seed signal from cancelling legitimate results near the query target.
#![allow(unused)]
fn main() {
// BFS from seeds, mark everything within immunity_hops as immune
while let Some((node, d)) = queue.pop_front() {
if d >= self.params.immunity_hops { continue; }
for each neighbor:
immune[neighbor] = true;
queue.push_back((neighbor, d + 1));
}
}
Immunity is the key insight that prevents over-cancellation. Without it, the cold channel could erase results that are genuinely close to the query. With it, only distant noise gets cancelled.
Stage 3: Propagate hot signal
Spectral pulses propagate from the seed nodes at frequency F_HOT = 1.0. Each pulse carries an amplitude, a phase, and a frequency. The signal decays along edges and attenuates at inhibitory edges (by INHIBITORY_COLD_ATTENUATION = 0.5).
#![allow(unused)]
fn main() {
let pulse = SpectralPulse {
node: seed,
amplitude: seed_activation,
phase: 0.0,
frequency: F_HOT, // 1.0
hops: 0,
prev_node: seed,
};
}
Propagation is budget-limited (default 50,000 total pulses across hot and cold channels) to prevent runaway computation on dense graphs.
Stage 4: Propagate cold signal
The same propagation runs from the anti-seed nodes at frequency F_COLD = 3.7. The cold frequency is intentionally different from the hot frequency. This spectral separation allows m1nd to distinguish which contributions came from which channel.
#![allow(unused)]
fn main() {
let cold_freq = PosF32::new(F_COLD).unwrap(); // 3.7
let anti_seed_pairs: Vec<(NodeId, FiniteF32)> = anti_seeds
.iter()
.map(|&n| (n, FiniteF32::ONE))
.collect();
let cold_pulses = self.propagate_spectral(
graph, &anti_seed_pairs, cold_freq, config, half_budget
)?;
}
Stage 5: Adaptive differential
At each node, the hot and cold amplitudes are combined. Immune nodes receive the full hot signal with no cold cancellation. Non-immune nodes get the differential:
#![allow(unused)]
fn main() {
let effective_cold = if immune { 0.0 } else { cold_amp[i] };
let raw = hot - effective_cold;
}
The raw differential is then modulated by local graph density. Nodes with degree near the graph average get density 1.0. High-degree nodes (hubs) get density > 1.0, amplifying the differential effect. Low-degree nodes (leaves) get density < 1.0, attenuating it. This is density-adaptive strength – a hub with both hot and cold signal has its differential amplified because hubs are the primary source of noise.
#![allow(unused)]
fn main() {
let density = if avg_deg > 0.0 {
(out_deg / avg_deg).max(DENSITY_FLOOR).min(DENSITY_CAP)
} else {
1.0
};
}
Density is clamped to [0.3, 2.0] to prevent extreme values from distorting results.
Stage 6: Sigmoid gating
The density-modulated differential passes through a sigmoid gate:
#![allow(unused)]
fn main() {
pub fn sigmoid_gate(net_signal: FiniteF32) -> FiniteF32 {
let x = net_signal.get() * SIGMOID_STEEPNESS; // * 6.0
let clamped = x.max(-20.0).min(20.0);
let result = 1.0 / (1.0 + (-clamped).exp());
FiniteF32::new(result)
}
}
The sigmoid converts the raw differential (which can be any value) into a smooth activation in [0, 1]. The steepness factor of 6.0 creates a sharp transition: small positive differentials map close to 0.5, strong positive differentials map close to 1.0, and negative differentials (more cold than hot) map close to 0.0 and are effectively suppressed.
graph LR
subgraph "XLR Pipeline"
S["Seeds<br/>F=1.0 (hot)"] --> HP["Hot<br/>propagation"]
AS["Anti-seeds<br/>F=3.7 (cold)"] --> CP["Cold<br/>propagation"]
HP --> DIFF["Differential<br/>hot - cold"]
CP --> DIFF
DIFF --> DEN["Density<br/>modulation"]
DEN --> SIG["Sigmoid<br/>gating"]
SIG --> OUT["Clean<br/>activation"]
end
subgraph "Immunity"
IMM["2-hop BFS<br/>from seeds"] -.->|"immune = true"| DIFF
end
style S fill:#ff6b6b,stroke:#333,color:#fff
style AS fill:#4dabf7,stroke:#333,color:#fff
style OUT fill:#51cf66,stroke:#333,color:#fff
style IMM fill:#ffd43b,stroke:#333
Over-cancellation fallback
If the cold channel is too aggressive – cancelling all hot signal and leaving zero results – m1nd triggers a fallback (FM-XLR-010):
#![allow(unused)]
fn main() {
let fallback = all_zero && !hot_pulses.is_empty();
if fallback {
// Return hot-only (no cancellation)
activations.clear();
for i in 0..n {
if hot_amp[i] > 0.01 {
activations.push((NodeId::new(i as u32), FiniteF32::new(hot_amp[i])));
}
}
}
}
This ensures that XLR noise cancellation never makes results worse than plain activation. It can either improve them (by removing noise) or fall back to the unfiltered result. The xlr_fallback_used flag in the activation result tells the caller that this happened.
Before and after
Without XLR
Query: “payment processing”
| Rank | Node | Activation | Relevant? |
|---|---|---|---|
| 1 | config.py | 0.82 | No – imports everywhere |
| 2 | payment_handler.py | 0.78 | Yes |
| 3 | database.py | 0.75 | No – generic persistence |
| 4 | logger.py | 0.72 | No – called from everything |
| 5 | billing.py | 0.70 | Yes |
| 6 | utils.py | 0.68 | No – shared utilities |
| 7 | invoice.py | 0.65 | Yes |
| 8 | middleware.py | 0.63 | No – request pipeline |
| 9 | refund.py | 0.60 | Yes |
| 10 | routes.py | 0.58 | No – URL dispatch |
Signal-to-noise ratio: 4 relevant out of 10.
With XLR
The anti-seeds (e.g., nodes from the user management subsystem) produce cold signal that reaches the same hub nodes – config.py, database.py, logger.py, utils.py, middleware.py, routes.py. These hubs receive both hot and cold signal. The differential cancels them.
Payment-specific nodes (payment_handler.py, billing.py, invoice.py, refund.py) receive hot signal but little cold signal, because the anti-seeds live in a different part of the graph. Their differential stays high.
| Rank | Node | Hot | Cold | Differential | Relevant? |
|---|---|---|---|---|---|
| 1 | payment_handler.py | 0.78 | 0.05 | 0.73 | Yes |
| 2 | billing.py | 0.70 | 0.08 | 0.62 | Yes |
| 3 | invoice.py | 0.65 | 0.06 | 0.59 | Yes |
| 4 | refund.py | 0.60 | 0.04 | 0.56 | Yes |
| 5 | payment_models.py | 0.55 | 0.03 | 0.52 | Yes |
| 6 | stripe_adapter.py | 0.50 | 0.02 | 0.48 | Yes |
| 7 | config.py | 0.82 | 0.79 | 0.03 | No (cancelled) |
| 8 | database.py | 0.75 | 0.71 | 0.04 | No (cancelled) |
Signal-to-noise ratio: 6 relevant out of 8.
The hub nodes did not disappear entirely – they still have small positive differentials. But they dropped from ranks 1, 3, 4, 6 to ranks 7, 8. The payment-specific nodes rose to the top.
Why the spectral model matters
The use of different frequencies for hot (1.0) and cold (3.7) channels enables a richer cancellation model than simple subtraction. The spectral_overlap function measures how much the hot and cold frequency distributions overlap at each node:
#![allow(unused)]
fn main() {
// Overlap = sum(min(hot_buckets, cold_buckets)) / sum(hot_buckets)
}
Nodes where hot and cold signals arrive at similar frequencies have high spectral overlap – these are the noise nodes that respond to any query. Nodes where the hot signal dominates a different frequency band than the cold signal have low spectral overlap – these are the signal nodes that respond specifically to this query.
This is directly analogous to common-mode rejection in electrical engineering: the interference is the component that appears identically on both channels, and the signal is the component that differs.
Constants reference
| Parameter | Value | Purpose |
|---|---|---|
F_HOT | 1.0 | Hot channel frequency |
F_COLD | 3.7 | Cold channel frequency |
SPECTRAL_BANDWIDTH | 0.8 | Gaussian kernel width for overlap |
IMMUNITY_HOPS | 2 | BFS depth for seed immunity |
SIGMOID_STEEPNESS | 6.0 | Sharpness of activation gate |
SPECTRAL_BUCKETS | 20 | Resolution of frequency overlap |
DENSITY_FLOOR | 0.3 | Minimum density modulation |
DENSITY_CAP | 2.0 | Maximum density modulation |
INHIBITORY_COLD_ATTENUATION | 0.5 | Cold signal reduction at inhibitory edges |
| Default anti-seeds | 3 | Number of cold-channel origins |
| Default pulse budget | 50,000 | Total pulse limit (hot + cold) |
Structural Holes
Most developer tools find things that exist. m1nd finds things that should exist but do not. A missing error handler. A test file that was never written. A validation function that every sibling module has except one. These are structural holes – gaps in the code graph that reveal omissions, risks, and design inconsistencies.
The network theory origin
The concept of structural holes comes from sociologist Ronald Burt’s 1992 book Structural Holes: The Social Structure of Competition. Burt observed that in social networks, the most valuable positions are not the most connected ones – they are the ones that bridge otherwise disconnected groups. The gap between two groups is a structural hole. The person who spans it controls information flow.
Burt was studying people and organizations, but the insight transfers directly to code. In a codebase graph, a structural hole is a place where a connection should exist based on the surrounding structure but does not. The module that imports five out of six modules in a cluster but skips the sixth. The service that handles create, read, update, but not delete. The directory where every file has a test except one.
These gaps are invisible to text search. You cannot grep for something that is absent. You cannot find a missing test by scanning the test directory – you have to know what should be there and check whether it is there. This requires structural reasoning over the graph, not pattern matching over text.
How m1nd detects structural holes
m1nd detects structural holes through a combination of activation context and neighborhood analysis. The detection is embedded in the query pipeline and can also be invoked directly via the missing tool.
The algorithm
The core insight is: a node surrounded by activated neighbors but not activated itself is probably missing something.
After a spreading activation query produces its ranked results, the structural hole detector scans every node in the graph:
#![allow(unused)]
fn main() {
pub fn detect_structural_holes(
&self,
graph: &Graph,
activation: &ActivationResult,
min_sibling_activation: FiniteF32,
) -> M1ndResult<Vec<StructuralHole>> {
// Build activation lookup
let mut act_map = vec![0.0f32; n];
for a in &activation.activated {
act_map[a.node.as_usize()] = a.activation.get();
}
for each node i:
// Skip if already activated (it is in the results)
if act_map[i] > 0.01: continue
// Count activated neighbors
for each neighbor of i:
if act_map[neighbor] > min_sibling_activation:
activated_neighbors += 1
neighbor_act_sum += act_map[neighbor]
// If 2+ neighbors are activated but this node is not: structural hole
if activated_neighbors >= 2:
holes.push(StructuralHole {
node: i,
sibling_avg_activation: neighbor_act_sum / activated_neighbors,
reason: format!("{} activated neighbors (avg={:.2}) but node inactive",
activated_neighbors, avg),
})
}
The threshold of 2 activated neighbors prevents false positives from nodes that happen to touch one activated node. The min_sibling_activation parameter (default 0.3) filters out weakly activated neighbors. Results are sorted by the average activation of the surrounding neighbors – the higher the average, the more conspicuous the absence.
Why this works
Consider a query about “authentication.” The activation pattern covers auth.py, session.py, middleware.py, user_model.py, and their neighbors. Now suppose auth_test.py does not exist. The test files for every other module in the auth cluster exist and are activated (because they have edges to the modules they test). auth_test.py is absent from the graph entirely. But if it did exist, it would be surrounded by activated nodes. Its absence is detectable by the gap it leaves.
In the more subtle case, auth_test.py exists but lacks tests for the password reset flow. The test file node exists and is activated, but password_reset_handler.py is activated while auth_test.py’s test coverage edges do not include it. The neighborhood analysis detects this: password_reset_handler.py’s siblings all have test edges, but this one does not.
The missing tool
The missing tool wraps structural hole detection in a purpose-built interface. Instead of requiring the caller to run an activation query first, missing accepts a natural-language query, runs internal activation, and returns only the holes.
The tool exposes the detection through the MCP protocol. Behind the scenes, it:
- Finds seed nodes matching the query text.
- Runs full four-dimension spreading activation (structural, semantic, temporal, causal).
- Analyzes the activation pattern for structural holes using the neighborhood algorithm.
- Returns the top 10 holes sorted by surrounding activation strength.
Each hole includes:
- node_id: the node at the center of the gap.
- label: human-readable identifier.
- node_type: file, function, class, etc.
- reason: a description of why this is a hole (e.g., “4 activated neighbors (avg=0.72) but node inactive”).
- sibling_avg_activation: quantifies how strongly the surrounding nodes were activated.
Real examples
Missing error handler
Query: “error handling in the payment flow”
Activation lights up: payment_handler.py, billing.py, refund.py, payment_errors.py, retry_logic.py.
Structural hole detected: webhook_handler.py has 3 activated neighbors (payment_handler.py, billing.py, payment_errors.py) but is itself not activated. Investigation reveals: the webhook handler processes Stripe callbacks but has no error handling – raw exceptions propagate to the caller. Every other module in the payment cluster catches and wraps errors via payment_errors.py. The webhook handler does not.
StructuralHole {
node: "file::webhook_handler.py",
sibling_avg_activation: 0.71,
reason: "3 activated neighbors (avg=0.71) but node inactive"
}
Missing test file
Query: “authentication testing”
Activation lights up: test_auth.py, test_session.py, test_middleware.py, auth.py, session.py.
Structural hole detected: password_reset.py has 4 activated neighbors but zero activation itself. The module handles password reset logic. It imports from auth.py and session.py (both activated). It is imported by routes.py (activated). But there is no test_password_reset.py in the graph, and password_reset.py has no test-relationship edges. Every sibling module in the auth cluster has a corresponding test file. This one does not.
StructuralHole {
node: "file::password_reset.py",
sibling_avg_activation: 0.68,
reason: "4 activated neighbors (avg=0.68) but node inactive"
}
Missing validation
Query: “input validation for API endpoints”
Activation lights up: validate_user_input.py, validate_payment_input.py, validate_search_params.py, schema_validator.py.
Structural hole detected: admin_routes.py has 3 activated neighbors (the validation modules it shares with other route handlers) but is not activated itself. Investigation reveals: user routes, payment routes, and search routes all import and call validation functions. Admin routes do not. The admin API accepts raw input without validation – a security risk hidden in a structural gap.
StructuralHole {
node: "file::admin_routes.py",
sibling_avg_activation: 0.65,
reason: "3 activated neighbors (avg=0.65) but node inactive"
}
Why no other tool can do this
Text search (grep, ripgrep)
Text search finds strings. It cannot find the absence of a string. You cannot grep for “the test file that should exist but does not.” You would need to know in advance what you are looking for, which defeats the purpose.
Static analysis (AST, linters)
Linters can enforce specific rules (“every function must have a docstring”) but cannot detect structural patterns across the graph (“every module in this cluster has a test file except this one”). They operate on individual files, not on the relationships between files.
RAG / embedding search
Embedding search finds documents similar to a query. It cannot detect the gap between documents. If test_password_reset.py does not exist, there is no document to embed. The absence is invisible.
Code coverage tools
Coverage tools tell you which lines of code are executed during tests. They can identify untested code within a file. They cannot identify missing files – a test file that was never created has zero coverage, indistinguishable from a file that exists but is not run.
m1nd’s advantage
m1nd operates on the graph structure, not on file contents. It reasons about the topology of relationships: which nodes connect to which other nodes, and where those connections are unexpectedly absent. The spreading activation query establishes a context (“what is related to authentication?”), and the structural hole detector finds gaps within that context.
This is a fundamentally different capability. It is not better text search, or better static analysis, or better coverage. It is structural reasoning about the shape of the code graph – something that requires a graph in the first place, and an activation pattern to establish context.
Connection to other concepts
Structural hole detection works best when combined with other m1nd capabilities:
-
Spreading activation (see Spreading Activation) provides the activation context that defines “which neighbors count.” Without activation, every node would be checked against every other node – too noisy to be useful.
-
Hebbian plasticity (see Hebbian Plasticity) sharpens the detection over time. As edges on well-tested paths strengthen and edges on untested paths weaken, the contrast between activated and inactive nodes increases. Holes become more conspicuous.
-
XLR noise cancellation (see XLR Noise Cancellation) removes hub-node noise from the activation pattern before hole detection runs. Without XLR,
config.pybeing activated might cause false-positive holes in unrelated modules that happen to import config. With XLR, the activation pattern is cleaner, and the holes that surface are more meaningful.
Interpreting results
A structural hole is not necessarily a bug. It is a signal that something deviates from the surrounding pattern. Possible interpretations:
| Hole type | What it means | Action |
|---|---|---|
| Missing test file | A module has no tests while its siblings do | Write tests or mark as intentionally untested |
| Missing error handling | A module does not use the error patterns its siblings use | Add error handling or document why it is unnecessary |
| Missing validation | An endpoint lacks input validation that peer endpoints have | Add validation – likely a security gap |
| Missing import | A module does not import a shared utility that all siblings import | Check if the module implements the functionality differently |
| Missing documentation | A module has no doc-edges while siblings do | Write documentation or accept the gap |
| Intentional isolation | A module is deliberately decoupled from a cluster | No action – but the hole confirms the isolation is real |
The sibling_avg_activation score helps prioritize. A hole where surrounding nodes have an average activation of 0.85 is more suspicious than one at 0.35. The former means the node is deeply embedded in a strongly activated cluster and conspicuously absent. The latter means the surrounding nodes were only weakly related to the query – the “hole” may just be a node in a different domain.
API Reference Overview
m1nd exposes the live MCP tool surface over JSON-RPC 2.0 via stdio. Every tool requires agent_id as a parameter. The exported MCP schema uses underscore-based canonical names such as trail_resume, perspective_start, and apply_batch. Use tools/list for the exact count in your current build.
The grouped pages below still keep historical prefixed anchors for stable links, but executable tools/call examples use the canonical bare names returned by tools/list.
Several tools now do more than return raw results. On the main structural flows you should expect some combination of:
proof_statenext_suggested_toolnext_suggested_targetnext_step_hint
That matters for how you integrate m1nd into an agent loop: treat many responses as workflow guidance, not just data blobs.
Tool Index
Core Activation
| Tool | Description |
|---|---|
activate | Spreading activation query across the graph |
warmup | Task-based warmup and priming |
resonate | Resonance analysis: harmonics, sympathetic pairs, and resonant frequencies |
Analysis
| Tool | Description |
|---|---|
impact | Impact radius / blast analysis for a node |
predict | Co-change prediction for a modified node |
counterfactual | What-if node removal simulation |
fingerprint | Activation fingerprint and equivalence detection |
hypothesize | Graph-based hypothesis testing against structural claims |
differential | Focused structural diff between two graph snapshots |
diverge | Structural drift between a baseline and current graph state |
Memory, Trails, and Learning
| Tool | Description |
|---|---|
learn | Explicit feedback-based edge adjustment |
drift | Weight and structural drift analysis |
why | Path explanation between two nodes |
trail_save | Persist current investigation state |
trail_resume | Restore a saved investigation with next-step guidance |
trail_list | List saved investigation trails |
trail_merge | Combine two or more investigation trails |
Exploration
| Tool | Description |
|---|---|
seek | Intent-aware semantic code search |
scan | Pattern-aware structural code analysis |
missing | Detect structural holes and missing connections |
trace | Map runtime errors to structural root causes |
timeline | Git-based temporal history for a node |
federate | Multi-repository federated graph ingestion |
federate_auto | Discover repo candidates from external path evidence and optionally federate them |
Perspectives
| Tool | Description |
|---|---|
perspective_start | Enter a perspective: navigable route surface from a query |
perspective_routes | Browse the current route set with pagination |
perspective_inspect | Expand a route with metrics, provenance, and affinity |
perspective_peek | Extract a code/doc slice from a route target |
perspective_follow | Follow a route: move focus to target, synthesize new routes |
perspective_suggest | Get the next best move suggestion |
perspective_affinity | Discover probable connections a route target might have |
perspective_branch | Fork navigation state into a new branch |
perspective_back | Navigate back to previous focus |
perspective_compare | Compare two perspectives on shared/unique nodes |
perspective_list | List all perspectives for an agent |
perspective_close | Close a perspective and release associated locks |
Lifecycle, Search, and Surgical
| Tool | Description |
|---|---|
ingest | Ingest or re-ingest a codebase, descriptor, or memory corpus |
document_resolve | Resolve canonical local artifacts for a universal document |
document_provider_health | Report optional document provider availability and install hints |
document_bindings | Show deterministic document-to-code bindings |
document_drift | Detect stale, missing, or ambiguous document/code links |
auto_ingest_start | Start local-first document auto-ingest watchers |
auto_ingest_status | Inspect the document auto-ingest runtime and counters |
auto_ingest_tick | Drain queued document changes immediately |
auto_ingest_stop | Stop document watchers and persist manifest state |
health | Server health and statistics |
search | Literal, regex, or semantic-graph-aware content search |
glob | Graph-aware file globbing |
view | Fast line-numbered file inspection |
validate_plan | Validate a modification plan against the code graph |
surgical_context_v2 | Pull connected edit context with proof-oriented options |
apply_batch | Atomic multi-file write with progress, verification, and handoff |
The grouped reference pages below still organize the surface by area, but the current best operational map is in help, the README tool table, and the live tools/list response.
Common Parameters
Every tool requires the agent_id parameter:
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Identifier for the calling agent. Used for session tracking, perspective ownership, lock ownership, and multi-agent coordination. |
Agent IDs are free-form strings. Convention: use the agent’s name or role identifier (e.g. "jimi", "hacker-auth", "forge-build").
JSON-RPC Request Format
m1nd uses the MCP protocol over JSON-RPC 2.0 via stdio. All tool calls use the tools/call method.
Request structure
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"agent_id": "jimi",
"query": "session management"
}
}
}
Successful response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{ ... pretty-printed JSON result ... }"
}
]
}
}
The text field contains the tool-specific output as a pretty-printed JSON string. Parse it to get the structured result.
Error response (tool execution error)
Tool execution errors are returned as MCP isError content, not as JSON-RPC errors. Many current errors are recovery-oriented and may include fields such as hint, workflow_hint, example, or suggested_next_step.
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "{ \"error\": \"...\", \"hint\": \"...\", \"suggested_next_step\": \"...\" }"
}
],
"isError": true
}
}
Error response (protocol error)
JSON-RPC protocol errors use standard error codes:
{
"jsonrpc": "2.0",
"id": 1,
"error": {
"code": -32601,
"message": "Method not found: bad_method"
}
}
Error Codes
| Code | Meaning |
|---|---|
-32700 | Parse error – invalid JSON |
-32601 | Method not found – unknown JSON-RPC method |
-32603 | Internal error – server-level failure |
Tool-specific errors (node not found, perspective not found, lock ownership violation, etc.) are returned via isError: true in the content, not as JSON-RPC errors.
Transport
m1nd supports two stdio transport modes, auto-detected per message:
- Framed:
Content-Length: N\r\n\r\n{json}– standard MCP/LSP framing. Used by Claude Code, Cursor, and most MCP clients. - Line:
{json}\n– one JSON object per line. Used by simple scripts and testing.
The server auto-detects which mode each incoming message uses and responds in the same mode.
Tool Name Normalization
The exported MCP schema uses underscore-based canonical names, but the server still accepts legacy transport aliases when possible:
activateis canonical- transport-prefixed aliases are accepted where normalization applies
If you are generating tool calls from an MCP client, prefer the canonical schema names returned by tools/list.
Protocol Handshake
Before calling tools, MCP clients perform a handshake:
{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}
Response:
{
"jsonrpc": "2.0",
"id": 0,
"result": {
"protocolVersion": "2024-11-05",
"serverInfo": { "name": "m1nd-mcp", "version": "0.8.0" },
"capabilities": { "tools": {} }
}
}
Then list available tools:
{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}
This returns the full schema for the live tool surface with inputSchema for each entry. Treat tools/list as the source of truth for the exact count in your current build.
Activation Tools
Three tools for querying the graph through spreading activation, task-based priming, and resonance analysis.
activate
Spreading activation query across the graph. The primary structural search tool – propagates signal from seed nodes through the graph across four dimensions (structural, semantic, temporal, causal), with XLR noise cancellation and optional ghost edge / structural hole detection.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | – | Search query for spreading activation. Matched against node labels, tags, and provenance to find seed nodes. |
agent_id | string | Yes | – | Calling agent identifier. |
top_k | integer | No | 20 | Number of top activated nodes to return. |
dimensions | string[] | No | ["structural", "semantic", "temporal", "causal"] | Activation dimensions to include. Each dimension contributes independently to the final activation score. Values: "structural", "semantic", "temporal", "causal". |
xlr | boolean | No | true | Enable XLR noise cancellation. Filters low-confidence activations to reduce false positives. |
include_ghost_edges | boolean | No | true | Include ghost edge detection. Ghost edges are probable but unconfirmed connections inferred from activation patterns. |
include_structural_holes | boolean | No | false | Include structural hole detection. Identifies nodes that should be connected but are not. |
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"agent_id": "jimi",
"query": "session pool management",
"top_k": 5,
"include_ghost_edges": true
}
}
}
Example Response
{
"query": "session pool management",
"seeds": [
{ "node_id": "file::session_pool.py", "label": "session_pool.py", "relevance": 0.95 }
],
"activated": [
{
"node_id": "file::session_pool.py",
"label": "session_pool.py",
"type": "file",
"activation": 0.89,
"dimensions": { "structural": 0.92, "semantic": 0.95, "temporal": 0.78, "causal": 0.71 },
"pagerank": 0.635,
"tags": ["session", "pool"],
"provenance": {
"source_path": "backend/session_pool.py",
"line_start": 1,
"line_end": 245,
"canonical": true
}
},
{
"node_id": "file::session_pool.py::class::SessionPool",
"label": "SessionPool",
"type": "class",
"activation": 0.84,
"dimensions": { "structural": 0.88, "semantic": 0.91, "temporal": 0.72, "causal": 0.65 },
"pagerank": 0.412,
"tags": ["pool", "session"],
"provenance": {
"source_path": "backend/session_pool.py",
"line_start": 15,
"line_end": 180,
"canonical": true
}
}
],
"ghost_edges": [
{
"source": "session_pool.py",
"target": "healing_manager.py",
"shared_dimensions": ["semantic", "causal"],
"strength": 0.34
}
],
"structural_holes": [],
"plasticity": {
"edges_strengthened": 12,
"edges_decayed": 3,
"ltp_events": 1,
"priming_nodes": 5
},
"elapsed_ms": 31.2
}
When to Use
- Primary search – the default way to ask “what in the codebase relates to X?”
- Exploration – when you know a topic but not the specific files
- Context building – before working on a feature, activate its topic to find all related code
- Gap detection – enable
include_structural_holesto find missing connections
Side Effects
Activate has plasticity side effects: it strengthens edges between activated nodes and decays inactive edges. This makes the graph learn from usage patterns over time.
Related Tools
warmup– activate + prime for a specific taskseek– intent-aware search (finds code by purpose, not just keywords)perspective_start– wraps activate into a navigable perspectivelearn– explicitly provide feedback on activation results
warmup
Task-based warmup and priming. Activates the graph around a task description and applies a temporary boost to relevant nodes, preparing the graph for focused work. The boost decays naturally over time.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
task_description | string | Yes | – | Description of the task to warm up for. Natural language. |
agent_id | string | Yes | – | Calling agent identifier. |
boost_strength | number | No | 0.15 | Priming boost strength applied to relevant nodes. Range: 0.0 to 1.0. Higher values make the primed nodes more dominant in subsequent queries. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "warmup",
"arguments": {
"agent_id": "jimi",
"task_description": "Refactor the WhatsApp message routing to support group chats",
"boost_strength": 0.2
}
}
}
Example Response
{
"task": "Refactor the WhatsApp message routing to support group chats",
"primed_nodes": 23,
"top_primed": [
{ "node_id": "file::whatsapp_routes.py", "label": "whatsapp_routes.py", "boost": 0.2 },
{ "node_id": "file::whatsapp_manager.py", "label": "whatsapp_manager.py", "boost": 0.18 },
{ "node_id": "file::whatsapp_models.py", "label": "whatsapp_models.py", "boost": 0.15 },
{ "node_id": "file::chat_handler.py", "label": "chat_handler.py", "boost": 0.12 }
],
"elapsed_ms": 18.5
}
When to Use
- Session start – warm up before a focused work session to bias the graph toward relevant code
- Context switch – when changing tasks, warm up the new topic
- Before complex queries – warmup biases subsequent
activate,impact, andwhyqueries toward the warmed-up region
Side Effects
Applies temporary priming boosts to node activations. These boosts decay naturally and are NOT persisted across server restarts.
Related Tools
activate– raw activation query without the priming boosttrail_resume– restores a full investigation context including activation boosts
resonate
Resonance analysis: standing waves, harmonics, sympathetic pairs, and resonant frequencies in the graph. Identifies nodes that form natural clusters of mutual reinforcement.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | No | – | Search query to find seed nodes for resonance analysis. Provide either query or node_id (or neither for global resonance). |
node_id | string | No | – | Specific node identifier to use as seed. Alternative to query. |
agent_id | string | Yes | – | Calling agent identifier. |
top_k | integer | No | 20 | Number of top resonance results to return. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "resonate",
"arguments": {
"agent_id": "jimi",
"query": "authentication flow",
"top_k": 10
}
}
}
Example Response
{
"seed": "authentication flow",
"harmonics": [
{
"node_id": "file::auth_discovery.py",
"label": "auth_discovery.py",
"amplitude": 0.92,
"harmonic_order": 1
},
{
"node_id": "file::middleware.py",
"label": "middleware.py",
"amplitude": 0.71,
"harmonic_order": 2
},
{
"node_id": "file::principal_registry.py",
"label": "principal_registry.py",
"amplitude": 0.68,
"harmonic_order": 2
}
],
"sympathetic_pairs": [
{ "a": "auth_discovery.py", "b": "principal_registry.py", "coupling": 0.84 }
],
"elapsed_ms": 45.0
}
When to Use
- Deep structural analysis – find natural clusters of mutually reinforcing code
- Pattern discovery – identify which modules form coherent subsystems
- Architecture review – see which modules resonate together (and which do not)
- Refactoring – resonance groups suggest natural module boundaries
Side Effects
Read-only. No plasticity side effects.
Related Tools
activate– simpler spreading activation without harmonic analysisfingerprint– finds structurally equivalent nodesmissing– finds gaps in the resonance structure
Analysis Tools
Seven tools for impact analysis, prediction, counterfactual simulation, fingerprinting, hypothesis testing, structural diffing, and drift detection.
impact
Impact radius / blast analysis for a node. Propagates signal outward from a source node to determine which other nodes would be affected by a change. Supports forward (downstream), reverse (upstream), and bidirectional analysis.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
node_id | string | Yes | – | Target node identifier. Can be an external_id (file::backend/config.py) or a node label. |
agent_id | string | Yes | – | Calling agent identifier. |
direction | string | No | "forward" | Propagation direction. Values: "forward" (what does this affect?), "reverse" (what affects this?), "both". |
include_causal_chains | boolean | No | true | Include causal chain detection. Shows the specific paths through which impact propagates. |
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "impact",
"arguments": {
"agent_id": "jimi",
"node_id": "file::chat_handler.py",
"direction": "forward",
"include_causal_chains": true
}
}
}
Example Response
{
"source": "file::chat_handler.py",
"source_label": "chat_handler.py",
"direction": "forward",
"blast_radius": [
{ "node_id": "file::chat_routes.py", "label": "chat_routes.py", "type": "file", "signal_strength": 0.91, "hop_distance": 1 },
{ "node_id": "file::ws_relay.py", "label": "ws_relay.py", "type": "file", "signal_strength": 0.78, "hop_distance": 1 },
{ "node_id": "file::stream_parser.py", "label": "stream_parser.py", "type": "file", "signal_strength": 0.65, "hop_distance": 2 }
],
"total_energy": 4271.0,
"max_hops_reached": 3,
"causal_chains": [
{
"path": ["chat_handler.py", "chat_routes.py", "main.py"],
"relations": ["imported_by", "registered_in"],
"cumulative_strength": 0.82
}
]
}
When to Use
- Before modifying code – understand the blast radius before touching a file
- Risk assessment – high total_energy = high-risk change
- Scope validation – verify that a planned change does not leak beyond expected boundaries
- Reverse analysis – find all upstream dependencies that could cause a bug in a given module
Related Tools
predict– predicts which files will co-change (more actionable than blast radius)counterfactual– simulates deletion rather than changevalidate_plan– validates an entire modification plan
predict
Co-change prediction for a modified node. Given a node that was just changed, predicts which other nodes are most likely to need changes too, ranked by confidence. Uses historical co-change patterns, structural proximity, and velocity scoring.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
changed_node | string | Yes | – | Node identifier that was changed. |
agent_id | string | Yes | – | Calling agent identifier. |
top_k | integer | No | 10 | Number of top predictions to return. |
include_velocity | boolean | No | true | Include velocity scoring. Velocity considers how recently and frequently nodes co-changed. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "predict",
"arguments": {
"agent_id": "jimi",
"changed_node": "file::session_pool.py",
"top_k": 5
}
}
}
Example Response
{
"changed_node": "file::session_pool.py",
"predictions": [
{ "node_id": "file::worker_pool.py", "label": "worker_pool.py", "confidence": 0.89, "velocity": 0.72, "reason": "high co-change frequency + structural coupling" },
{ "node_id": "file::process_manager.py", "label": "process_manager.py", "confidence": 0.76, "velocity": 0.65, "reason": "imports session_pool" },
{ "node_id": "file::tests/test_session_pool.py", "label": "test_session_pool.py", "confidence": 0.71, "velocity": 0.80, "reason": "test file" },
{ "node_id": "file::spawner.py", "label": "spawner.py", "confidence": 0.54, "velocity": 0.41, "reason": "2-hop dependency" },
{ "node_id": "file::config.py", "label": "config.py", "confidence": 0.32, "velocity": 0.28, "reason": "shared configuration" }
],
"elapsed_ms": 8.3
}
When to Use
- After modifying a module – find what else needs updating
- Before committing – verify you have not missed a co-change partner
- Code review – check if a PR is missing changes to coupled modules
Related Tools
impact– blast radius (broader, less actionable)timeline– detailed co-change historyvalidate_plan– validates an entire plan against co-change predictions
counterfactual
What-if node removal simulation. Simulates removing one or more nodes from the graph and reports the cascade: orphaned nodes, lost activation energy, and the resulting blast radius. Non-destructive – the graph is not modified.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
node_ids | string[] | Yes | – | Node identifiers to simulate removal of. |
agent_id | string | Yes | – | Calling agent identifier. |
include_cascade | boolean | No | true | Include cascade analysis. Shows multi-hop propagation of the removal. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "counterfactual",
"arguments": {
"agent_id": "jimi",
"node_ids": ["file::spawner.py"],
"include_cascade": true
}
}
}
Example Response
{
"removed_nodes": ["file::spawner.py"],
"cascade": [
{ "depth": 1, "affected": 23 },
{ "depth": 2, "affected": 456 },
{ "depth": 3, "affected": 3710 }
],
"total_affected": 4189,
"orphaned_count": 0,
"pct_activation_lost": 0.41,
"elapsed_ms": 3.1
}
When to Use
- Before deleting/rewriting modules – understand the full cascade before removing code
- Dependency audit – find modules whose removal would be catastrophic
- Architecture planning – evaluate the cost of removing a subsystem
Related Tools
impact– change impact (less extreme than removal)hypothesize– test a structural claim about dependencies
fingerprint
Activation fingerprint and equivalence detection. Computes a structural fingerprint for a target node (or the entire graph) and finds functionally equivalent or duplicate nodes. Uses probe queries to generate activation patterns and compares them via cosine similarity.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
target_node | string | No | – | Target node to find equivalents for. If omitted, performs global fingerprinting. |
agent_id | string | Yes | – | Calling agent identifier. |
similarity_threshold | number | No | 0.85 | Cosine similarity threshold for equivalence. Range: 0.0 to 1.0. Lower values find more (but weaker) matches. |
probe_queries | string[] | No | – | Optional probe queries for fingerprinting. Auto-generated from the node’s neighborhood if omitted. |
Example Request
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "fingerprint",
"arguments": {
"agent_id": "jimi",
"target_node": "file::session_pool.py",
"similarity_threshold": 0.7
}
}
}
Example Response
{
"target": "file::session_pool.py",
"fingerprint": [0.92, 0.45, 0.78, 0.33, 0.67],
"equivalents": [
{ "node_id": "file::worker_pool.py", "label": "worker_pool.py", "similarity": 0.88, "reason": "similar pool lifecycle pattern" },
{ "node_id": "file::fast_pool.py", "label": "fast_pool.py", "similarity": 0.74, "reason": "shared structural role" }
],
"elapsed_ms": 55.2
}
When to Use
- Duplicate detection – find code doing the same thing in different places
- Consolidation audit – identify candidates for unification
- Post-build review – verify new code does not duplicate existing functionality
Related Tools
resonate– finds harmonically coupled nodes (complementary, not duplicate)activate– simpler search without equivalence scoring
hypothesize
Graph-based hypothesis testing. Takes a natural language claim about the codebase, parses it into a structural query pattern, and returns evidence for and against the claim with a Bayesian confidence score.
Supported claim patterns (auto-detected from natural language): NEVER_CALLS, ALWAYS_BEFORE, DEPENDS_ON, NO_DEPENDENCY, COUPLING, ISOLATED, GATEWAY, CIRCULAR.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
claim | string | Yes | – | Natural language claim about the codebase. Examples: "chat_handler never validates session tokens", "all external calls go through smart_router", "critic is independent of whatsapp". |
agent_id | string | Yes | – | Calling agent identifier. |
max_hops | integer | No | 5 | Maximum BFS hops for evidence search. |
include_ghost_edges | boolean | No | true | Include ghost edges as weak evidence. Ghost edges count as lower-weight supporting evidence. |
include_partial_flow | boolean | No | true | Include partial flow when full path not found. Shows how far the search reached. |
path_budget | integer | No | 1000 | Budget cap for all-paths enumeration. Limits computation on dense graphs. |
Example Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "hypothesize",
"arguments": {
"agent_id": "jimi",
"claim": "worker_pool depends on whatsapp_manager at runtime"
}
}
}
Example Response
{
"claim": "worker_pool depends on whatsapp_manager at runtime",
"claim_type": "depends_on",
"subject_nodes": ["file::worker_pool.py"],
"object_nodes": ["file::whatsapp_manager.py"],
"verdict": "likely_true",
"confidence": 0.72,
"supporting_evidence": [
{
"type": "path_found",
"description": "2-hop path via process_manager.cancel",
"likelihood_factor": 3.5,
"nodes": ["file::worker_pool.py", "file::process_manager.py", "file::whatsapp_manager.py"],
"relations": ["calls", "imports"],
"path_weight": 0.68
}
],
"contradicting_evidence": [],
"paths_explored": 25015,
"elapsed_ms": 58.0
}
Verdict Values
| Verdict | Confidence Range | Meaning |
|---|---|---|
"likely_true" | > 0.8 | Strong structural evidence supports the claim |
"likely_false" | < 0.2 | Strong structural evidence contradicts the claim |
"inconclusive" | 0.2 – 0.8 | Evidence exists both for and against |
When to Use
- Architecture validation – test claims about module boundaries and dependencies
- Bug investigation – test whether a suspected dependency exists
- Code review – verify architectural invariants are maintained
- Security audit – test isolation claims (e.g. “auth module is isolated from user input”)
Related Tools
why– finds the path between two specific nodesimpact– measures downstream impact rather than testing a claimscan– structural analysis with predefined patterns
differential
Focused structural diff between two graph snapshots. Compares edges, weights, nodes, and coupling between snapshot A and snapshot B, optionally narrowed by a focus question.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
snapshot_a | string | Yes | – | Path to snapshot A, or "current" for the in-memory graph. |
snapshot_b | string | Yes | – | Path to snapshot B, or "current" for the in-memory graph. |
question | string | No | – | Focus filter question. Narrows the diff output. Examples: "what new coupling was introduced?", "what modules became isolated?". |
focus_nodes | string[] | No | [] | Limit diff to neighborhood of specific nodes. |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "differential",
"arguments": {
"agent_id": "jimi",
"snapshot_a": "/path/to/before.json",
"snapshot_b": "current",
"question": "what new coupling was introduced?"
}
}
}
Example Response
{
"snapshot_a": "/path/to/before.json",
"snapshot_b": "current",
"new_edges": [
{ "source": "whatsapp_routes.py", "target": "chat_handler.py", "relation": "calls", "weight": 0.45 }
],
"removed_edges": [],
"weight_changes": [
{ "source": "session_pool.py", "target": "worker_pool.py", "relation": "imports", "old_weight": 0.5, "new_weight": 0.78, "delta": 0.28 }
],
"new_nodes": ["file::whatsapp_chat_bridge.py"],
"removed_nodes": [],
"coupling_deltas": [
{ "community_a": "whatsapp", "community_b": "chat", "old_coupling": 0.2, "new_coupling": 0.65, "delta": 0.45 }
],
"summary": "1 new edge, 1 new node, 1 weight change, 1 coupling increase",
"elapsed_ms": 120.5
}
When to Use
- Pre/post refactor comparison – snapshot before, refactor, then diff against current
- PR review – compare graph before and after a branch’s changes
- Architecture drift monitoring – periodic snapshot comparison
Related Tools
diverge– higher-level drift analysis with anomaly detectionlock_diff– diff within a locked region (no snapshot file needed)
diverge
Structural drift detection between a baseline and the current graph state. Higher-level than differential – includes anomaly detection (test deficits, velocity spikes, new coupling), coupling matrix changes, and a Jaccard-based structural drift score.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
baseline | string | Yes | – | Baseline reference. Values: ISO date ("2026-03-01"), git ref (SHA or tag), or "last_session" to use the saved GraphFingerprint. |
scope | string | No | – | File path glob to limit scope. Example: "backend/stormender*". None = all nodes. |
include_coupling_changes | boolean | No | true | Include coupling matrix delta between communities. |
include_anomalies | boolean | No | true | Detect anomalies: test deficits, velocity spikes, new coupling, isolation. |
Example Request
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "diverge",
"arguments": {
"agent_id": "jimi",
"baseline": "2026-03-01",
"scope": "backend/stormender*"
}
}
}
Example Response
{
"baseline": "2026-03-01",
"baseline_commit": "a1b2c3d",
"scope": "backend/stormender*",
"structural_drift": 0.23,
"new_nodes": ["file::stormender_v2_runtime_guard.py"],
"removed_nodes": [],
"modified_nodes": [
{ "file": "stormender_v2.py", "delta": "+450/-30", "growth_ratio": 1.85 }
],
"coupling_changes": [
{ "pair": ["stormender_v2.py", "stormender_v2_routes.py"], "was": 0.4, "now": 0.72, "direction": "strengthened" }
],
"anomalies": [
{ "type": "test_deficit", "file": "stormender_v2_runtime_guard.py", "detail": "New file with 0 test files", "severity": "warning" },
{ "type": "velocity_spike", "file": "stormender_v2.py", "detail": "450 lines added in 12 days", "severity": "info" }
],
"summary": "23% structural drift. 1 new file (untested). Stormender coupling strengthened.",
"elapsed_ms": 85.0
}
Anomaly Types
| Type | Description |
|---|---|
test_deficit | New or modified file with no corresponding test file |
velocity_spike | Unusually high churn rate |
new_coupling | Previously independent modules are now coupled |
isolation | Module that was connected became isolated |
When to Use
- Session start –
driftshows weight-level changes;divergeshows structural-level changes - Sprint retrospective – how much did the architecture change this sprint?
- Quality gate – flag files with test deficits before merging
Related Tools
drift– weight-level drift (lighter, faster)differential– lower-level snapshot difftimeline– single-node temporal history
ghost_edges
Parse git history and surface temporal co-change ghost edges: files that move together without an explicit static dependency.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Optional path prefix. |
depth | string | No | Git history window such as 7d, 30d, 90d, or all. |
top_k | integer | No | Maximum ghost edges to return. |
When to Use
- To find hidden temporal coupling
- Before refactors in churn-heavy areas
Related Tools
taint_trace
Inject taint at entry points and trace propagation through the graph to expose missed validation, auth, or sanitization boundaries.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
entry_nodes | string[] | Yes | Entry points to taint. |
taint_type | string | No | user_input, sensitive_data, or custom. |
max_depth | integer | No | Maximum propagation depth. |
When to Use
- Security reviews on trust boundaries
- Input validation and auth flow audits
Related Tools
twins
Find structurally similar or identical nodes by topology signature.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Optional path prefix. |
node_types | string[] | No | Restrict to node families. |
similarity_threshold | number | No | Minimum similarity threshold. |
When to Use
- Duplicate logic detection
- Consolidation/refactor candidate discovery
Related Tools
refactor_plan
Graph-native refactoring proposals: community detection and extraction candidates for a scoped region.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Optional path prefix. |
max_communities | integer | No | Upper bound on candidate communities. |
min_community_size | integer | No | Smallest extractable group to report. |
When to Use
- Before modularization/extraction work
- To identify communities with high internal cohesion
Related Tools
runtime_overlay
Overlay OpenTelemetry span activity onto the graph to paint runtime heat, latency, and error signals onto nodes.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
spans | object[] | Yes | OTel-like spans to ingest. |
service_name | string | No | Optional service scope. |
mapping_strategy | string | No | Mapping mode such as label_match, code_attribute, exact_id. |
When to Use
- Correlating runtime hotspots with structural risk
- Prioritizing where to inspect first after a production incident
Related Tools
metrics
Return structural metrics per file, function, class, struct, or module.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Optional path prefix. |
node_types | string[] | No | Restrict output to certain node types. |
sort | string | No | Sort order such as loc_desc, complexity_desc, name_asc. |
When to Use
- To rank hot modules by size/degree/centrality
- To anchor audits with hard structural numbers
Related Tools
type_trace
Trace where a type, struct, or enum is used across the graph.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
target | string | Yes | Type name or external_id to trace. |
direction | string | No | forward, reverse, or both. |
group_by_file | boolean | No | Group usage sites by file. |
When to Use
- Following type spread before edits
- Understanding where a central data model is consumed
Related Tools
diagram
Generate Mermaid or DOT graph diagrams from a query or node-centered slice.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
center | string | No | Seed query or node_id to center the diagram. |
format | string | No | mermaid or dot. |
direction | string | No | TD or LR. |
max_nodes | integer | No | Maximum nodes to include. |
When to Use
- Human-readable architecture explanations
- Sharing graph context with another agent or reviewer
Related Tools
Memory & Learning Tools
Seven tools for feedback-based learning, drift analysis, path explanation, and investigation trail management.
learn
Explicit feedback-based edge adjustment. After using activate or other query tools, call learn to tell m1nd whether the results were correct, wrong, or partial. This applies Hebbian learning to strengthen or weaken edges between the query seeds and the reported nodes.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | – | The original query this feedback relates to. Must match the query used in the activation. |
agent_id | string | Yes | – | Calling agent identifier. |
feedback | string | Yes | – | Feedback type. Values: "correct" (strengthen edges), "wrong" (weaken edges), "partial" (strengthen confirmed nodes only). |
node_ids | string[] | Yes | – | Node identifiers to apply feedback to. For "correct", these are the relevant results. For "wrong", these are the irrelevant ones. |
strength | number | No | 0.2 | Feedback strength for edge adjustment. Range: 0.0 to 1.0. Higher = stronger plasticity effect. |
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "learn",
"arguments": {
"agent_id": "jimi",
"query": "session pool management",
"feedback": "correct",
"node_ids": ["file::session_pool.py", "file::worker_pool.py"],
"strength": 0.3
}
}
}
Example Response
{
"query": "session pool management",
"feedback": "correct",
"edges_adjusted": 8,
"nodes_affected": 2,
"plasticity_delta": 0.3,
"elapsed_ms": 2.1
}
When to Use
- After every activate where results were used – always provide feedback to improve future results
- After investigation – report which activated nodes were actually relevant
- Continuous improvement – the graph learns from your feedback over time
Side Effects
Modifies edge weights in the graph. Changes are persisted on the next auto-persist cycle (every 50 queries) and on shutdown.
Related Tools
activate– the query tool whose results you are providing feedback ondrift– see cumulative weight changes from learning
drift
Weight and structural drift analysis. Compares the current graph state against a baseline (typically "last_session") to show what changed – new edges, removed edges, and weight drift. Useful for context recovery at session start.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
since | string | No | "last_session" | Baseline reference point. Values: "last_session" (saved state from previous session), or a timestamp. |
include_weight_drift | boolean | No | true | Include edge weight drift analysis. Shows which edges strengthened or weakened. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "drift",
"arguments": {
"agent_id": "jimi",
"since": "last_session",
"include_weight_drift": true
}
}
}
Example Response
{
"since": "last_session",
"node_count_delta": 15,
"edge_count_delta": 42,
"new_nodes": ["file::forem_publisher.py", "file::forem_routes.py"],
"removed_nodes": [],
"top_weight_drifts": [
{ "edge": "session_pool.py -> worker_pool.py", "old": 0.45, "new": 0.72, "delta": 0.27 },
{ "edge": "chat_handler.py -> stream_parser.py", "old": 0.60, "new": 0.48, "delta": -0.12 }
],
"elapsed_ms": 12.0
}
When to Use
- Session start – first tool to call after
healthto recover context - After ingest – see what the new ingest changed
- After extended learning – track cumulative drift from feedback
Related Tools
diverge– higher-level structural drift with anomaly detectionhealth– basic server health (call before drift)
why
Path explanation between two nodes. Finds and explains the relationship paths connecting a source node to a target node. Returns all paths up to max_hops, ranked by cumulative edge strength.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
source | string | Yes | – | Source node identifier. |
target | string | Yes | – | Target node identifier. |
agent_id | string | Yes | – | Calling agent identifier. |
max_hops | integer | No | 6 | Maximum hops in path search. Higher values find more indirect paths but take longer. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "why",
"arguments": {
"agent_id": "jimi",
"source": "file::worker_pool.py",
"target": "file::whatsapp_manager.py",
"max_hops": 4
}
}
}
Example Response
{
"source": "file::worker_pool.py",
"target": "file::whatsapp_manager.py",
"paths": [
{
"nodes": ["worker_pool.py", "process_manager.py", "whatsapp_manager.py"],
"relations": ["calls::cancel", "imports"],
"cumulative_strength": 0.68,
"hops": 2
},
{
"nodes": ["worker_pool.py", "spawner.py", "chat_handler.py", "whatsapp_manager.py"],
"relations": ["imported_by", "calls", "imports"],
"cumulative_strength": 0.31,
"hops": 3
}
],
"total_paths_found": 2,
"elapsed_ms": 15.0
}
When to Use
- Understanding dependencies – “why are these two modules connected?”
- Tracing influence – find the relationship chain between distant modules
- Bug investigation – understand how a change in A could affect B
Related Tools
hypothesize– tests a claim about the relationship (more powerful)impact– finds all affected nodes (broader scope)
trail_save
Persist the current investigation state – nodes visited, hypotheses formed, conclusions reached, and open questions. Captures activation boosts for later restoration.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
label | string | Yes | – | Human-readable label for this investigation. |
hypotheses | object[] | No | [] | Hypotheses formed during investigation. Each object has: statement (string, required), confidence (number, default 0.5), supporting_nodes (string[]), contradicting_nodes (string[]). |
conclusions | object[] | No | [] | Conclusions reached. Each object has: statement (string, required), confidence (number, default 0.5), from_hypotheses (string[]), supporting_nodes (string[]). |
open_questions | string[] | No | [] | Open questions remaining for future investigation. |
tags | string[] | No | [] | Tags for organization and search. |
summary | string | No | – | Optional summary. Auto-generated if omitted. |
visited_nodes | object[] | No | [] | Explicitly list visited nodes with annotations. Each object has: node_external_id (string, required), annotation (string, optional), relevance (number, default 0.5). If omitted, captured from active perspective state. |
activation_boosts | object | No | {} | Map of node_external_id to boost weight [0.0, 1.0]. Re-injected on resume. |
Example Request
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "trail_save",
"arguments": {
"agent_id": "jimi",
"label": "auth-leak-investigation",
"hypotheses": [
{
"statement": "Auth tokens leak through session pool",
"confidence": 0.7,
"supporting_nodes": ["file::session_pool.py", "file::auth_discovery.py"]
},
{
"statement": "Rate limiter missing from auth chain",
"confidence": 0.9,
"supporting_nodes": ["file::middleware.py"]
}
],
"open_questions": ["Does the rate limiter apply to WebSocket connections?"],
"tags": ["security", "auth", "session"],
"activation_boosts": {
"file::session_pool.py": 0.8,
"file::auth_discovery.py": 0.6
}
}
}
}
Example Response
{
"trail_id": "trail_jimi_001_a1b2c3",
"label": "auth-leak-investigation",
"agent_id": "jimi",
"nodes_saved": 47,
"hypotheses_saved": 2,
"conclusions_saved": 0,
"open_questions_saved": 1,
"graph_generation_at_creation": 42,
"created_at_ms": 1710300000000
}
When to Use
- End of investigation session – save your work before ending a session
- Before context compaction – checkpoint your investigation state
- Cross-session continuity – resume exactly where you left off
Related Tools
trail_resume– restore a saved trailtrail_list– find saved trailstrail_merge– combine trails from parallel investigations
trail_resume
Restore a saved investigation. Re-injects activation boosts into the graph, validates that saved nodes still exist, detects staleness, and optionally downgrades hypotheses whose supporting nodes are missing.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
trail_id | string | Yes | – | Trail ID to resume (from trail_save or trail_list). |
force | boolean | No | false | Resume even if trail is stale (>50% missing nodes). Default behavior: refuse to resume stale trails. |
Example Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "trail_resume",
"arguments": {
"agent_id": "jimi",
"trail_id": "trail_jimi_001_a1b2c3"
}
}
}
Example Response
{
"trail_id": "trail_jimi_001_a1b2c3",
"label": "auth-leak-investigation",
"stale": false,
"generations_behind": 3,
"missing_nodes": [],
"nodes_reactivated": 47,
"hypotheses_downgraded": [],
"trail": {
"trail_id": "trail_jimi_001_a1b2c3",
"agent_id": "jimi",
"label": "auth-leak-investigation",
"status": "active",
"created_at_ms": 1710300000000,
"last_modified_ms": 1710300000000,
"node_count": 47,
"hypothesis_count": 2,
"conclusion_count": 0,
"open_question_count": 1,
"tags": ["security", "auth", "session"],
"summary": "Investigating auth token leaks through session pool"
},
"elapsed_ms": 22.5
}
When to Use
- Session start – restore a previous investigation
- Cross-agent handoff – agent B resumes agent A’s trail
- After re-ingest – check if investigation nodes survived the graph update
Related Tools
trail_save– save a trail to resume laterwarmup– simpler priming without full trail restoration
trail_list
List saved investigation trails with optional filters. Returns compact summaries suitable for selecting a trail to resume.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
filter_agent_id | string | No | – | Filter to a specific agent’s trails. None = all agents. |
filter_status | string | No | – | Filter by status: "active", "saved", "archived", "stale", "merged". |
filter_tags | string[] | No | [] | Filter by tags (any match). |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "trail_list",
"arguments": {
"agent_id": "jimi",
"filter_status": "saved",
"filter_tags": ["security"]
}
}
}
Example Response
{
"trails": [
{
"trail_id": "trail_jimi_001_a1b2c3",
"agent_id": "jimi",
"label": "auth-leak-investigation",
"status": "saved",
"created_at_ms": 1710300000000,
"last_modified_ms": 1710300000000,
"node_count": 47,
"hypothesis_count": 2,
"conclusion_count": 0,
"open_question_count": 1,
"tags": ["security", "auth", "session"]
}
],
"total_count": 1
}
When to Use
- Session start – see what investigations are available to resume
- Multi-agent coordination – see trails from other agents
- Cleanup – find stale or merged trails
Related Tools
trail_resume– resume a trail from this listtrail_merge– combine related trails
trail_merge
Combine two or more investigation trails. Merges visited nodes, hypotheses, and conclusions. Uses confidence+recency scoring for conflict resolution. Discovers cross-connections between independently explored areas.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
trail_ids | string[] | Yes | – | Two or more trail IDs to merge. |
label | string | No | – | Label for the merged trail. Auto-generated if omitted. |
Example Request
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "trail_merge",
"arguments": {
"agent_id": "jimi",
"trail_ids": ["trail_jimi_001_a1b2c3", "trail_analyst_002_d4e5f6"],
"label": "combined-auth-investigation"
}
}
}
Example Response
{
"merged_trail_id": "trail_jimi_003_g7h8i9",
"label": "combined-auth-investigation",
"source_trails": ["trail_jimi_001_a1b2c3", "trail_analyst_002_d4e5f6"],
"nodes_merged": 83,
"hypotheses_merged": 5,
"conflicts": [
{
"hypothesis_a": "Session pool leaks tokens",
"hypothesis_b": "Session pool tokens are properly scoped",
"resolution": "resolved",
"winner": "Session pool leaks tokens",
"score_delta": 0.35
}
],
"connections_discovered": [
{
"type": "bridge_edge",
"detail": "auth_discovery.py connects the auth trail to the session trail",
"from_node": "file::auth_discovery.py",
"to_node": "file::session_pool.py",
"weight": 0.72
}
],
"elapsed_ms": 45.0
}
Conflict Resolution
When merging hypotheses that contradict each other:
- confidence+recency scoring determines the winner
- If the score delta is too small, the conflict is marked
"unresolved"for human review - Source trails are set to
"merged"status after a successful merge
When to Use
- Multi-agent investigation – combine findings from parallel agents
- Investigation continuation – merge an old investigation with new findings
- Consolidation – clean up related but separate investigation threads
Related Tools
trail_save– save individual trailstrail_list– find trails to merge
Exploration Tools
Six tools for intent-aware search, pattern-based scanning, structural hole detection, stacktrace analysis, temporal history, and multi-repository federation.
seek
Intent-aware semantic code search. Finds code by purpose, not text pattern. Combines keyword matching, graph activation (PageRank), and trigram similarity for ranking.
Unlike activate (which propagates signal through edges), seek scores every node independently against the query, making it better for finding specific code when you know what it does but not where it lives.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | – | Natural language description of what the agent is looking for. Example: "code that validates user credentials". |
agent_id | string | Yes | – | Calling agent identifier. |
top_k | integer | No | 20 | Maximum results to return. |
scope | string | No | – | File path prefix to limit search scope. Example: "backend/". None = entire graph. |
node_types | string[] | No | [] | Filter by node type: "function", "class", "struct", "module", "file". Empty = all types. |
min_score | number | No | 0.1 | Minimum combined score threshold. Range: 0.0 to 1.0. |
graph_rerank | boolean | No | true | Whether to run graph re-ranking (PageRank weighting) on candidates. Disable for pure text matching. |
Scoring Formula (V1)
combined = keyword_match * 0.6 + graph_activation * 0.3 + trigram * 0.1
V2 upgrade path will replace keyword matching with real embeddings (fastembed-rs + jina-embeddings-v2-base-code).
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "seek",
"arguments": {
"agent_id": "jimi",
"query": "code that validates user credentials",
"top_k": 5,
"node_types": ["function", "class"]
}
}
}
Example Response
{
"query": "code that validates user credentials",
"results": [
{
"node_id": "file::auth_discovery.py::fn::validate_credentials",
"label": "validate_credentials",
"type": "function",
"score": 0.87,
"score_breakdown": {
"embedding_similarity": 0.92,
"graph_activation": 0.78,
"temporal_recency": 0.65
},
"intent_summary": "Validates user credentials against provider store",
"file_path": "backend/auth_discovery.py",
"line_start": 45,
"line_end": 82,
"connections": [
{ "node_id": "file::principal_registry.py", "label": "principal_registry.py", "relation": "calls" }
]
}
],
"total_candidates_scanned": 9767,
"embeddings_used": false,
"elapsed_ms": 25.0
}
When to Use
- “Find the code that does X” – when you know the purpose, not the filename
- Codebase onboarding – exploring unfamiliar code by intent
- Pre-modification search – find all code related to a feature before changing it
Related Tools
activate– graph-propagation search (better for exploring neighborhoods)scan– pattern-based structural analysis (finds anti-patterns, not features)
scan
Pattern-aware structural code analysis with graph-validated findings. Detects structural issues using predefined patterns, then validates each finding against the graph to filter false positives. Works across file boundaries.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
pattern | string | Yes | – | Pattern ID or custom pattern string. Built-in patterns: "error_handling", "resource_cleanup", "api_surface", "state_mutation", "concurrency", "auth_boundary", "test_coverage", "dependency_injection". |
agent_id | string | Yes | – | Calling agent identifier. |
scope | string | No | – | File path prefix to limit scan scope. |
severity_min | number | No | 0.3 | Minimum severity threshold. Range: 0.0 to 1.0. |
graph_validate | boolean | No | true | Validate findings against graph edges (cross-file analysis). Disable for raw pattern matching only. |
limit | integer | No | 50 | Maximum findings to return. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "scan",
"arguments": {
"agent_id": "jimi",
"pattern": "error_handling",
"scope": "backend/",
"severity_min": 0.5
}
}
}
Example Response
{
"pattern": "error_handling",
"findings": [
{
"pattern": "error_handling",
"status": "confirmed",
"severity": 0.78,
"node_id": "file::spawner.py::fn::spawn_agent",
"label": "spawn_agent",
"file_path": "backend/spawner.py",
"line": 89,
"message": "Bare except clause catches all exceptions including KeyboardInterrupt",
"graph_context": [
{ "node_id": "file::process_manager.py", "label": "process_manager.py", "relation": "calls" }
]
}
],
"files_scanned": 77,
"total_matches_raw": 23,
"total_matches_validated": 8,
"elapsed_ms": 150.0
}
Finding Status Values
| Status | Meaning |
|---|---|
"confirmed" | Graph validation confirms the issue is real |
"mitigated" | The issue exists but is handled by a related module |
"false_positive" | Graph context shows the pattern match is not actually an issue |
When to Use
- Code quality audit – scan for anti-patterns across the codebase
- Security review – use
"auth_boundary"to find auth bypass paths - Pre-deploy check – scan for
"error_handling"and"resource_cleanup"issues - Test gaps – use
"test_coverage"to find untested code
Related Tools
hypothesize– test a specific structural claimtrace– analyze a specific error (not patterns)
missing
Detect structural holes and missing connections. Given a topic query, finds areas where the graph suggests something should exist but does not. Identifies absent abstractions, missing connections, and incomplete patterns.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
query | string | Yes | – | Search query to find structural holes around. |
agent_id | string | Yes | – | Calling agent identifier. |
min_sibling_activation | number | No | 0.3 | Minimum sibling activation threshold. Siblings with activation below this are not considered for hole detection. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "missing",
"arguments": {
"agent_id": "jimi",
"query": "database connection pooling",
"min_sibling_activation": 0.3
}
}
}
Example Response
{
"query": "database connection pooling",
"holes": [
{
"node_id": "structural_hole_1",
"label": "connection lifecycle",
"type": "structural_hole",
"reason": "No dedicated connection pool abstraction -- 4 adjacent modules manage connections independently"
},
{
"node_id": "structural_hole_2",
"label": "pool metrics",
"type": "structural_hole",
"reason": "No pool health monitoring -- 3 adjacent modules expose metrics but pool does not"
}
],
"total_holes": 9,
"elapsed_ms": 67.0
}
When to Use
- Gap analysis – “what am I missing?” before implementing a feature
- Pre-spec – identify areas that need design before building
- Architecture review – find missing abstractions or connections
- Feature completeness – after building, check for structural holes around the feature
Related Tools
activate– activate withinclude_structural_holes: truefor inline hole detectionhypothesize– test a specific claim about missing structure
trace
Map runtime errors to structural root causes via stacktrace analysis. Parses the stacktrace, maps frames to graph nodes, and scores each node’s suspiciousness using trace depth, modification recency, and centrality. Also finds co-change suspects (files modified around the same time as the top suspect).
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
error_text | string | Yes | – | Full error output (stacktrace + error message). |
agent_id | string | Yes | – | Calling agent identifier. |
language | string | No | – | Language hint: "python", "rust", "typescript", "javascript", "go". Auto-detected if omitted. |
window_hours | number | No | 24.0 | Temporal window (hours) for co-change suspect scan. |
top_k | integer | No | 10 | Max suspects to return. |
Example Request
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "trace",
"arguments": {
"agent_id": "jimi",
"error_text": "Traceback (most recent call last):\n File \"backend/chat_handler.py\", line 234, in handle_message\n File \"backend/session_pool.py\", line 89, in acquire\n File \"backend/worker_pool.py\", line 156, in submit\nTimeoutError: pool exhausted",
"language": "python",
"top_k": 5
}
}
}
Example Response
{
"language_detected": "python",
"error_type": "TimeoutError",
"error_message": "pool exhausted",
"frames_parsed": 3,
"frames_mapped": 3,
"suspects": [
{
"node_id": "file::worker_pool.py::fn::submit",
"label": "submit",
"type": "function",
"suspiciousness": 0.91,
"signals": { "trace_depth_score": 1.0, "recency_score": 0.85, "centrality_score": 0.88 },
"file_path": "backend/worker_pool.py",
"line_start": 150,
"line_end": 175,
"related_callers": ["session_pool.py::acquire"]
},
{
"node_id": "file::session_pool.py::fn::acquire",
"label": "acquire",
"type": "function",
"suspiciousness": 0.78,
"signals": { "trace_depth_score": 0.67, "recency_score": 0.72, "centrality_score": 0.65 },
"file_path": "backend/session_pool.py",
"line_start": 80,
"line_end": 110,
"related_callers": ["chat_handler.py::handle_message"]
}
],
"co_change_suspects": [
{ "node_id": "file::config.py", "label": "config.py", "modified_at": 1710295000.0, "reason": "Modified within 2h of top suspect" }
],
"causal_chain": ["worker_pool.py::submit", "session_pool.py::acquire", "chat_handler.py::handle_message"],
"fix_scope": {
"files_to_inspect": ["backend/worker_pool.py", "backend/session_pool.py", "backend/config.py"],
"estimated_blast_radius": 23,
"risk_level": "medium"
},
"unmapped_frames": [],
"elapsed_ms": 3.5
}
Suspiciousness Scoring
| Signal | Weight | Description |
|---|---|---|
trace_depth_score | High | 1.0 = deepest frame (most specific); decays linearly |
recency_score | Medium | Exponential decay from last modification time |
centrality_score | Medium | Normalized PageRank centrality |
When to Use
- Bug investigation – paste an error and get ranked suspects
- Root cause analysis – the causal chain shows the error propagation path
- Fix scoping –
fix_scopetells you which files to inspect and the risk level
Related Tools
hypothesize– test a hypothesis about the root causeimpact– assess the blast radius of a fixvalidate_plan– validate your fix plan before implementing
timeline
Git-based temporal history for a node. Returns the change history, co-change partners, velocity, stability score, and churn data for a specific file or module.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
node | string | Yes | – | Node external_id. Example: "file::backend/chat_handler.py". |
agent_id | string | Yes | – | Calling agent identifier. |
depth | string | No | "30d" | Time depth. Values: "7d", "30d", "90d", "all". |
include_co_changes | boolean | No | true | Include co-changed files with coupling scores. |
include_churn | boolean | No | true | Include lines added/deleted churn data. |
top_k | integer | No | 10 | Max co-change partners to return. |
Example Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "timeline",
"arguments": {
"agent_id": "jimi",
"node": "file::backend/chat_handler.py",
"depth": "30d",
"top_k": 5
}
}
}
Example Response
{
"node": "file::backend/chat_handler.py",
"depth": "30d",
"changes": [
{
"date": "2026-03-10",
"commit": "a1b2c3d",
"author": "cosmophonix",
"delta": "+45/-12",
"co_changed": ["stream_parser.py", "chat_routes.py"]
},
{
"date": "2026-03-05",
"commit": "e4f5g6h",
"author": "cosmophonix",
"delta": "+120/-30",
"co_changed": ["session_pool.py", "worker_pool.py"]
}
],
"co_changed_with": [
{ "file": "stream_parser.py", "times": 8, "coupling_degree": 0.72 },
{ "file": "chat_routes.py", "times": 6, "coupling_degree": 0.55 },
{ "file": "session_pool.py", "times": 4, "coupling_degree": 0.38 }
],
"velocity": "accelerating",
"stability_score": 0.35,
"pattern": "churning",
"total_churn": { "lines_added": 580, "lines_deleted": 120 },
"commit_count_in_window": 12,
"elapsed_ms": 45.0
}
Velocity Values
| Value | Meaning |
|---|---|
"accelerating" | Change frequency is increasing |
"decelerating" | Change frequency is decreasing |
"stable" | Consistent change rate |
Pattern Values
| Value | Meaning |
|---|---|
"expanding" | Growing (net positive churn) |
"shrinking" | Reducing (net negative churn) |
"churning" | High add+delete with little net growth |
"dormant" | Few or no changes in the window |
"stable" | Small, consistent changes |
When to Use
- Hotspot detection – find files that change too frequently (stability_score < 0.3)
- Co-change discovery – find files that always change together (coupling_degree > 0.6)
- Refactoring signals – churning files may need redesign
Related Tools
diverge– structural drift across the whole codebasepredict– predict co-changes for a given modification
federate
Ingest multiple repositories into a unified federated graph with automatic cross-repo edge detection. After federation, all existing query tools (activate, impact, why, etc.) traverse cross-repo edges automatically.
Node IDs in the federated graph use {repo_name}::file::path format.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
repos | object[] | Yes | – | List of repositories to federate. Each object has: name (string, required – namespace prefix), path (string, required – absolute path), adapter (string, default "code"). |
detect_cross_repo_edges | boolean | No | true | Auto-detect cross-repo edges: config, API, import, type, deployment, MCP contract. |
incremental | boolean | No | false | Only re-ingest repos that changed since last federation. |
Cross-Repo Edge Types
| Edge Type | Description |
|---|---|
shared_config | Two repos reference the same configuration key |
api_contract | One repo’s API client matches another’s API server |
package_dep | Direct package dependency |
shared_type | Same type/interface definition used across repos |
deployment_dep | Deployment configuration dependency |
mcp_contract | MCP tool consumer/provider relationship |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "federate",
"arguments": {
"agent_id": "jimi",
"repos": [
{ "name": "backend", "path": "/project/backend" },
{ "name": "frontend", "path": "/project/frontend" },
{ "name": "mcp-server", "path": "/project/mcp-server", "adapter": "code" }
],
"detect_cross_repo_edges": true,
"incremental": false
}
}
}
Example Response
{
"repos_ingested": [
{ "name": "backend", "path": "/project/backend", "node_count": 9767, "edge_count": 26557, "from_cache": false, "ingest_ms": 910.0 },
{ "name": "frontend", "path": "/project/frontend", "node_count": 1200, "edge_count": 3500, "from_cache": false, "ingest_ms": 320.0 },
{ "name": "mcp-server", "path": "/project/mcp-server", "node_count": 250, "edge_count": 680, "from_cache": false, "ingest_ms": 85.0 }
],
"total_nodes": 11217,
"total_edges": 30737,
"cross_repo_edges": [
{
"source_repo": "frontend",
"target_repo": "backend",
"source_node": "frontend::file::src/lib/apiConfig.ts",
"target_node": "backend::file::main.py",
"edge_type": "api_contract",
"relation": "calls_api",
"weight": 0.85,
"causal_strength": 0.72
},
{
"source_repo": "mcp-server",
"target_repo": "backend",
"source_node": "mcp-server::file::src/mcp-server.js",
"target_node": "backend::file::main.py",
"edge_type": "mcp_contract",
"relation": "calls_api",
"weight": 0.78,
"causal_strength": 0.65
}
],
"cross_repo_edge_count": 18203,
"incremental": false,
"skipped_repos": [],
"elapsed_ms": 1315.0
}
When to Use
- Multi-repo projects – analyze dependencies across frontend, backend, and infrastructure repos
- Monorepo decomposition – understand how packages depend on each other
- API impact analysis – find which repos are affected by an API change
Related Tools
ingest– single-repo ingestionimpact– blast radius analysis (works across federated repos)why– path explanation (traverses cross-repo edges)
federate_auto
Turn explicit external path evidence, local manifest/workspace hints, import/package-name evidence, shared API-route signals, or contract artifacts such as .proto, MCP tools, and OpenAPI into repo candidates, namespace suggestions, and an optional one-shot federate call.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
scope | string | No | – | File path prefix to limit discovery sources. |
current_repo_name | string | No | auto | Optional namespace override for the current workspace. |
max_repos | integer | No | 8 | Maximum discovered external repos to include. |
detect_cross_repo_edges | boolean | No | true | Whether execute=true should auto-detect cross-repo edges. |
execute | boolean | No | false | If true, immediately run federate with the current repo plus discovered candidates. |
Example Request
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "federate_auto",
"arguments": {
"agent_id": "jimi",
"scope": "docs",
"execute": false
}
}
}
Example Response
{
"current_repo": { "namespace": "m1nd", "repo_root": "/repo/m1nd" },
"discovered_repos": [
{
"namespace": "runtime",
"repo_root": "/repo/runtime",
"marker": ".git",
"confidence": "high",
"evidence_types": ["markdown_link"],
"source_nodes": ["file::docs/architecture.md"],
"source_files": ["/repo/m1nd/docs/architecture.md"],
"sampled_paths": ["/repo/runtime/docs/ARCH.md"],
"suggested_action": "run federate_auto with execute=true or pass suggested_repos into federate"
}
],
"suggested_repos": [
{ "name": "runtime", "path": "/repo/runtime", "adapter": "code" }
],
"skipped_paths": [],
"executed": false,
"federate_result": null,
"elapsed_ms": 42.0
}
When to Use
- Cross-repo audits – when
auditorexternal_referencesalready surfaced sibling repo paths - Manifest-driven workspaces – when
Cargo.toml,package.json,pnpm-workspace.yaml,pyproject.toml, orgo.workalready point at sibling repos - Import-driven discovery – when code already imports a sibling package/crate name but the current repo has no explicit path hint
- Contract-driven discovery – when nearby repos expose
.protoor MCP tool contracts that the current workspace already references by name - OpenAPI-driven discovery – when the current workspace references
operationIds or routes defined in a nearby OpenAPI/Swagger spec - Schema-driven discovery – when the current workspace references message/component names defined in
.protoor OpenAPI schemas - Planning/doc hubs – when docs point to runtime repos and you want a namespace plan without manual copy-paste
- Worktree-heavy setups – when the current workspace path is a worktree but you still want repo-shaped namespace suggestions
Related Tools
external_references– raw external path evidencefederate– explicit multi-repo federationaudit– broader audit that can surface the evidence before auto-federation
batch_view
Read multiple files or glob-resolved files in one call with stable delimiters, optional summaries, and optional auto-ingest.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
files | string[] | Yes | File paths and/or glob patterns to expand. |
auto_ingest | boolean | No | Auto-ingest discovered files before reading. |
summary_mode | boolean | No | Add inline per-file summaries. |
When to Use
- Audit proof packets
- Compact multi-file reading after search/glob
- Agent handoff packages
Related Tools
scan_all
Run all structural scan patterns in one call and return grouped findings by pattern.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Scope path prefix. |
patterns | string[] | No | Subset of scan patterns to run. |
limit_per_pattern | integer | No | Maximum findings per pattern. |
When to Use
- Broad first-pass quality/security audit
- Pre-hardening sweep before prioritizing fixes
Related Tools
cross_verify
Compare graph state against filesystem truth: existence, LOC drift, and optional hash mismatches.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Path prefix to limit verification. |
check | string[] | No | Checks to run such as existence, loc, hash. |
include_dotfiles | boolean | No | Include selected dotfiles in verification. |
When to Use
- To verify graph and disk still agree
- After daemon drift alerts
- Before trusting an audit result on a changing repo
Related Tools
coverage_session
Report what the current agent session has already read or touched.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
When to Use
- To avoid re-reading files during long sessions
- To build a compact unread-next list after search/audit sweeps
Related Tools
external_references
Find explicit path references that point outside current ingest roots.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
scope | string | No | Optional scope prefix. |
When to Use
- Before federation when docs/config already point outside the repo
- To surface repo-boundary evidence in audits
Related Tools
glob
Graph-aware file globbing over already indexed files.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
pattern | string | Yes | Glob pattern. |
scope | string | No | Optional path prefix. |
sort | string | No | Sort order such as path or activation. |
When to Use
- When you need filenames by pattern, not content
- Before
batch_viewon a file family
Related Tools
view
Fast line-numbered file inspection with optional auto-ingest if the file is not yet in the graph.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
file_path | string | Yes | Absolute or workspace-relative file path. |
auto_ingest | boolean | No | Auto-ingest file into the graph if not present. |
offset | integer | No | Start line offset. |
limit | integer | No | Max lines to return. |
When to Use
- Quick inspection when you already know the file
- Narrow, bounded reads before a surgical context call
Related Tools
Perspective Tools
Twelve tools for stateful, navigable exploration of the code graph. Perspectives are m1nd’s primary interface for guided codebase exploration – they synthesize routes (weighted navigation suggestions) from a query, then let you follow, inspect, branch, and compare routes.
Core Concepts
Perspective: A stateful exploration session. Created from a query, it synthesizes a set of routes from the current graph state. The agent navigates by following routes, which moves the focus and generates new routes.
Route: A suggested direction of exploration. Each route points to a target node and carries a score, a family classification, and a path preview.
Focus: The current node the perspective is centered on. Following a route moves the focus. back restores the previous focus.
Mode: Either "anchored" (routes stay related to an anchor node) or "local" (routes synthesized purely from the current focus). Anchored mode degrades to local if navigation moves more than 8 hops from the anchor.
Route Set Version: A version counter for the current route set. Clients must pass this in subsequent calls for staleness detection. If the graph changes between calls, the version changes and stale requests are rejected.
Lens: Optional filtering configuration for perspectives (dimension weights, node type filters, etc.).
perspective_start
Enter a perspective: creates a navigable route surface from a query. Returns the initial set of routes.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
query | string | Yes | – | Seed query for route synthesis. |
anchor_node | string | No | – | Anchor to a specific node. If provided, activates anchored mode where all routes maintain relevance to this node. |
lens | object | No | – | Starting lens configuration. Controls dimension weights, node type filters, etc. |
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "perspective_start",
"arguments": {
"agent_id": "jimi",
"query": "session management and connection pooling",
"anchor_node": "file::session_pool.py"
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"mode": "anchored",
"anchor_node": "file::session_pool.py",
"focus_node": "file::session_pool.py",
"routes": [
{
"route_id": "R_a1b2c3",
"index": 1,
"target_node": "file::worker_pool.py",
"target_label": "worker_pool.py",
"family": "structural_neighbor",
"score": 0.89,
"path_preview": ["session_pool.py", "worker_pool.py"],
"reason": "High structural coupling with anchor"
},
{
"route_id": "R_d4e5f6",
"index": 2,
"target_node": "file::process_manager.py",
"target_label": "process_manager.py",
"family": "causal_downstream",
"score": 0.76,
"path_preview": ["session_pool.py", "process_manager.py"],
"reason": "Causal dependency via imports"
}
],
"total_routes": 12,
"page": 1,
"total_pages": 2,
"route_set_version": 100,
"cache_generation": 42,
"suggested": "inspect R_a1b2c3 for structural details"
}
When to Use
- Guided exploration – when you want to explore a topic interactively
- Codebase navigation – start from a known file and discover related code
- Investigation – anchor to a suspicious file and follow routes to find related issues
Related Tools
activate– one-shot query without navigation stateperspective_routes– paginate through routesperspective_follow– follow a route to navigate
perspective_routes
Browse the current route set with pagination.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to browse. |
page | integer | No | 1 | Page number (1-based). |
page_size | integer | No | 6 | Routes per page. Clamped to [1, 10]. |
route_set_version | integer | No | – | Version from previous response for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "perspective_routes",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"page": 2,
"page_size": 6,
"route_set_version": 100
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"mode": "anchored",
"mode_effective": "anchored",
"anchor": "file::session_pool.py",
"focus": "file::session_pool.py",
"lens_summary": "all dimensions, no type filter",
"page": 2,
"total_pages": 2,
"total_routes": 12,
"route_set_version": 100,
"cache_generation": 42,
"routes": [
{
"route_id": "R_g7h8i9",
"index": 7,
"target_node": "file::config.py",
"target_label": "config.py",
"family": "configuration",
"score": 0.42,
"path_preview": ["session_pool.py", "config.py"],
"reason": "Shared configuration key"
}
],
"suggested": null,
"page_size_clamped": false
}
When to Use
- Pagination – browse all available routes when the first page was not enough
- Full route survey – see the complete landscape before deciding which route to follow
Related Tools
perspective_inspect– expand a specific route for detailperspective_follow– follow a route to navigate
perspective_inspect
Expand a route with fuller path, metrics, provenance, affinity candidates, and score breakdown. Does not change the perspective state – purely informational.
Specify the route by either route_id (stable, content-addressed) or route_index (1-based position on the current page). Exactly one must be provided.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective containing the route. |
route_id | string | No | – | Stable content-addressed route ID. |
route_index | integer | No | – | 1-based page-local position. |
route_set_version | integer | Yes | – | Route set version for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "perspective_inspect",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"route_id": "R_a1b2c3",
"route_set_version": 100
}
}
}
Example Response
{
"route_id": "R_a1b2c3",
"route_index": 1,
"family": "structural_neighbor",
"target_node": "file::worker_pool.py",
"target_label": "worker_pool.py",
"target_type": "file",
"path_preview": ["session_pool.py", "spawner.py", "worker_pool.py"],
"family_explanation": "Direct structural neighbor via import chain",
"score": 0.89,
"score_breakdown": {
"local_activation": 0.92,
"path_coherence": 0.88,
"novelty": 0.75,
"anchor_relevance": 0.95,
"continuity": 0.82
},
"provenance": {
"source_path": "backend/worker_pool.py",
"line_start": 1,
"line_end": 312,
"namespace": null,
"provenance_stale": false
},
"peek_available": true,
"affinity_candidates": [
{ "node_id": "file::fast_pool.py", "label": "fast_pool.py", "strength": 0.72, "reason": "Similar pool pattern" }
],
"response_chars": 450
}
When to Use
- Before following – understand what a route leads to before committing
- Score analysis – see why a route was ranked high or low
- Provenance check – verify the source file exists and is not stale
Related Tools
perspective_peek– extract actual code contentperspective_follow– navigate to the route targetperspective_affinity– deeper affinity analysis
perspective_peek
Extract a small relevant code or documentation slice from a route’s target node. Reads the actual source file and returns the relevant excerpt. Security-checked: only reads files within the ingested graph scope.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective containing the route. |
route_id | string | No | – | Stable content-addressed route ID. |
route_index | integer | No | – | 1-based page-local position. |
route_set_version | integer | Yes | – | Route set version for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "perspective_peek",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"route_index": 1,
"route_set_version": 100
}
}
}
Example Response
{
"route_id": "R_a1b2c3",
"route_index": 1,
"target_node": "file::worker_pool.py",
"content": {
"excerpt": "class WorkerPool:\n \"\"\"Manages a pool of CLI subprocess workers.\"\"\"\n def __init__(self, max_workers=8):\n self.pool = {}\n self.max_workers = max_workers\n ...",
"file_path": "backend/worker_pool.py",
"line_start": 15,
"line_end": 45,
"truncated": true
}
}
When to Use
- Quick preview – see the actual code at a route target without leaving m1nd
- Decision making – peek at code to decide whether to follow a route
- Investigation – read suspicious code without opening a file
Related Tools
perspective_inspect– structural information about the route (no code content)perspective_follow– navigate to the route target
perspective_follow
Follow a route: move focus to the target node and synthesize new routes from the new position. This is the primary navigation action in a perspective. Updates the navigation history for back.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to navigate within. |
route_id | string | No | – | Stable content-addressed route ID. |
route_index | integer | No | – | 1-based page-local position. |
route_set_version | integer | Yes | – | Route set version for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "perspective_follow",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"route_id": "R_a1b2c3",
"route_set_version": 100
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"previous_focus": "file::session_pool.py",
"new_focus": "file::worker_pool.py",
"mode": "anchored",
"mode_effective": "anchored",
"routes": [
{
"route_id": "R_j1k2l3",
"index": 1,
"target_node": "file::process_manager.py",
"target_label": "process_manager.py",
"family": "causal_downstream",
"score": 0.85,
"path_preview": ["worker_pool.py", "process_manager.py"],
"reason": "Direct caller of worker pool submit"
},
{
"route_id": "R_m4n5o6",
"index": 2,
"target_node": "file::spawner.py",
"target_label": "spawner.py",
"family": "structural_neighbor",
"score": 0.71,
"path_preview": ["worker_pool.py", "spawner.py"],
"reason": "Co-manages subprocess lifecycle"
}
],
"total_routes": 8,
"page": 1,
"total_pages": 2,
"route_set_version": 101,
"cache_generation": 42,
"suggested": "inspect R_j1k2l3 -- high causal relevance"
}
When to Use
- Navigation – this is the primary way to move through the graph
- Guided exploration – follow high-scoring routes to discover related code
- Investigation – follow causal chains to trace a bug
Related Tools
perspective_back– undo a followperspective_branch– fork before following to explore alternatives
perspective_suggest
Get the next best move suggestion based on navigation history. Analyzes the routes, the current focus, and the history of followed routes to recommend what to do next.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to get a suggestion for. |
route_set_version | integer | Yes | – | Route set version for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "perspective_suggest",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"route_set_version": 101
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"suggestion": {
"action": "follow",
"route_id": "R_j1k2l3",
"reason": "High-scoring causal route not yet explored. process_manager.py is a hub node connecting to 47 downstream modules.",
"confidence": 0.82
}
}
When to Use
- Undecided – when you are not sure which route to follow
- Systematic exploration – let m1nd guide you through the most informative path
- Investigation – follow the suggestion trail to efficient root cause discovery
Related Tools
perspective_follow– follow the suggested routeperspective_inspect– inspect the suggested route first
perspective_affinity
Discover probable connections a route target might have. Returns affinity candidates – nodes that are not directly connected to the target but show structural affinity (similar neighborhoods, shared patterns).
Epistemic notice: these are probable connections, not verified graph edges.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective containing the route. |
route_id | string | No | – | Stable content-addressed route ID. |
route_index | integer | No | – | 1-based page-local position. |
route_set_version | integer | Yes | – | Route set version for staleness check. |
Example Request
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "perspective_affinity",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"route_id": "R_j1k2l3",
"route_set_version": 101
}
}
}
Example Response
{
"route_id": "R_j1k2l3",
"target_node": "file::process_manager.py",
"notice": "Probable connections, not verified edges.",
"candidates": [
{ "node_id": "file::healing_manager.py", "label": "healing_manager.py", "strength": 0.68, "reason": "Similar lifecycle management pattern" },
{ "node_id": "file::autonomous_daemon.py", "label": "autonomous_daemon.py", "strength": 0.52, "reason": "Shared subprocess management trait" }
]
}
When to Use
- Discovery – find non-obvious connections that the graph does not yet have
- Architecture analysis – identify modules that should be connected but are not
- Ghost edge validation – affinity candidates often become confirmed edges after investigation
Related Tools
missing– finds structural holes (broader scope)perspective_inspect– affinity candidates are also included in inspect output
perspective_branch
Fork the current navigation state into a new perspective branch. The branch starts at the same focus with the same route set, but future navigation in the branch is independent. Useful for exploring alternatives without losing your place.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to branch from. |
branch_name | string | No | – | Optional branch name. Auto-generated if not provided. |
Example Request
{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "perspective_branch",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001",
"branch_name": "auth-path"
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"branch_perspective_id": "persp_jimi_002",
"branch_name": "auth-path",
"branched_from_focus": "file::worker_pool.py"
}
When to Use
- Alternative exploration – explore two different paths from the same point
- Comparative analysis – branch, follow different routes, then compare
- Safe investigation – branch before following a risky route
Related Tools
perspective_compare– compare the branch with the originalperspective_follow– navigate within the branch
perspective_back
Navigate back to the previous focus, restoring the checkpoint state. Like browser back navigation. Pops the latest navigation event from the history stack.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to navigate back in. |
Example Request
{
"jsonrpc": "2.0",
"id": 9,
"method": "tools/call",
"params": {
"name": "perspective_back",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_001"
}
}
}
Example Response
{
"perspective_id": "persp_jimi_001",
"restored_focus": "file::session_pool.py",
"restored_mode": "anchored",
"routes": [
{
"route_id": "R_a1b2c3",
"index": 1,
"target_node": "file::worker_pool.py",
"target_label": "worker_pool.py",
"family": "structural_neighbor",
"score": 0.89,
"path_preview": ["session_pool.py", "worker_pool.py"],
"reason": "High structural coupling with anchor"
}
],
"total_routes": 12,
"page": 1,
"total_pages": 2,
"route_set_version": 102,
"cache_generation": 42
}
When to Use
- Undo navigation – go back after following a wrong route
- Breadth-first exploration – follow a route, come back, follow another
Related Tools
perspective_follow– the forward navigation thatbackundoesperspective_branch– alternative: branch instead of back+follow
perspective_compare
Compare two perspectives on shared/unique nodes and dimension deltas. Both perspectives must belong to the same agent (V1 restriction).
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id_a | string | Yes | – | First perspective ID. |
perspective_id_b | string | Yes | – | Second perspective ID. |
dimensions | string[] | No | [] | Dimensions to compare on. Empty = all dimensions. |
Example Request
{
"jsonrpc": "2.0",
"id": 10,
"method": "tools/call",
"params": {
"name": "perspective_compare",
"arguments": {
"agent_id": "jimi",
"perspective_id_a": "persp_jimi_001",
"perspective_id_b": "persp_jimi_002"
}
}
}
Example Response
{
"perspective_id_a": "persp_jimi_001",
"perspective_id_b": "persp_jimi_002",
"shared_nodes": ["worker_pool.py", "process_manager.py", "session_pool.py"],
"unique_to_a": ["spawner.py", "config.py"],
"unique_to_b": ["auth_discovery.py", "middleware.py"],
"dimension_deltas": [
{ "dimension": "structural", "score_a": 0.82, "score_b": 0.75, "delta": 0.07 },
{ "dimension": "semantic", "score_a": 0.65, "score_b": 0.88, "delta": -0.23 },
{ "dimension": "causal", "score_a": 0.71, "score_b": 0.45, "delta": 0.26 }
],
"response_chars": 380
}
When to Use
- Branch comparison – compare a branch perspective with the original
- Multi-approach analysis – see how two different starting queries reach different parts of the graph
- Investigation correlation – find shared nodes between two independent investigations
Related Tools
perspective_branch– create branches to comparetrail_merge– merge investigation trails (deeper than compare)
perspective_list
List all perspectives for an agent. Returns compact summaries with status, focus, route count, and memory usage.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
Example Request
{
"jsonrpc": "2.0",
"id": 11,
"method": "tools/call",
"params": {
"name": "perspective_list",
"arguments": {
"agent_id": "jimi"
}
}
}
Example Response
{
"agent_id": "jimi",
"perspectives": [
{
"perspective_id": "persp_jimi_001",
"mode": "anchored",
"focus_node": "file::worker_pool.py",
"route_count": 12,
"nav_event_count": 3,
"stale": false,
"created_at_ms": 1710300000000,
"last_accessed_ms": 1710300500000
},
{
"perspective_id": "persp_jimi_002",
"mode": "anchored",
"focus_node": "file::auth_discovery.py",
"route_count": 8,
"nav_event_count": 1,
"stale": false,
"created_at_ms": 1710300200000,
"last_accessed_ms": 1710300400000
}
],
"total_memory_bytes": 24576
}
When to Use
- Session overview – see all active perspectives
- Memory management – check total memory usage and close stale perspectives
- Multi-perspective work – switch between perspectives
Related Tools
perspective_close– close perspectives you no longer need
perspective_close
Close a perspective and release associated locks. Frees memory and stops route caching for this perspective. Cascade-releases any locks that were associated with this perspective.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
perspective_id | string | Yes | – | Perspective to close. |
Example Request
{
"jsonrpc": "2.0",
"id": 12,
"method": "tools/call",
"params": {
"name": "perspective_close",
"arguments": {
"agent_id": "jimi",
"perspective_id": "persp_jimi_002"
}
}
}
Example Response
{
"perspective_id": "persp_jimi_002",
"closed": true,
"locks_released": ["lock_jimi_003"]
}
When to Use
- Cleanup – close perspectives after investigation is complete
- Memory pressure – close unused perspectives to free memory
- Session end – close all perspectives before ending a session
Related Tools
perspective_list– find perspectives to close
Full Workflow Example
A complete perspective exploration session, from start to close:
1. START a perspective from a query
perspective_start(query="authentication and session security")
-> persp_jimi_001, 12 routes, anchored to auth_discovery.py
2. BROWSE routes (page 1 already returned by start)
Routes: R01 auth_discovery.py, R02 middleware.py, R03 principal_registry.py, ...
3. INSPECT a high-scoring route before following
perspective_inspect(route_id="R01", route_set_version=100)
-> score_breakdown, provenance, affinity_candidates
4. FOLLOW the route to navigate
perspective_follow(route_id="R01", route_set_version=100)
-> new focus: auth_discovery.py, 8 new routes, version=101
5. ASK for a suggestion on what to do next
perspective_suggest(route_set_version=101)
-> "follow R_x: principal_registry.py, high causal relevance"
6. BRANCH before exploring a risky path
perspective_branch(branch_name="session-path")
-> new persp_jimi_002, same focus
7. FOLLOW different routes in each branch
Branch 1: follow toward principal_registry.py
Branch 2: follow toward session_pool.py
8. COMPARE the two branches
perspective_compare(perspective_id_a="persp_jimi_001", perspective_id_b="persp_jimi_002")
-> shared: [auth_discovery.py], unique_to_a: [principal_registry.py], unique_to_b: [session_pool.py]
9. BACK to undo the last follow in branch 1
perspective_back()
-> restored focus: auth_discovery.py
10. CLOSE both perspectives when done
perspective_close(perspective_id="persp_jimi_001")
perspective_close(perspective_id="persp_jimi_002")
This workflow demonstrates the full exploration cycle: start with a query, navigate through the graph by following routes, branch to explore alternatives, compare branches, and close when done.
Lifecycle, Document & Lock Tools
This page covers graph ingestion, document runtime operations, health monitoring, plan validation, persistence, and subgraph locking with change detection.
ingest
Ingest or re-ingest a codebase, descriptor, or memory/document corpus into the graph. This is the primary way to load data into m1nd. It now supports code-first, structured-document, and universal document adapters.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
path | string | Yes | – | Filesystem path to the source root or memory corpus. |
agent_id | string | Yes | – | Calling agent identifier. |
incremental | boolean | No | false | Incremental ingest (code adapter only). Only re-processes files that changed since the last ingest. |
adapter | string | No | "code" | Adapter to use for parsing. Values include "code", "json", "memory", "light", "patent", "article", "bibtex", "rfc", "crossref", "universal", and "auto" / "document" for format detection. |
mode | string | No | "replace" | How to handle the existing graph. Values: "replace" (clear and rebuild), "merge" (add new nodes/edges into existing graph). |
namespace | string | No | – | Optional namespace tag for non-code nodes. Used by memory and json adapters to prefix node external_ids. |
Example Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "tools/call",
"params": {
"name": "ingest",
"arguments": {
"agent_id": "jimi",
"path": "/Users/cosmophonix/clawd/roomanizer-os/backend",
"adapter": "code",
"mode": "replace",
"incremental": false
}
}
}
Example Response
{
"files_processed": 335,
"nodes_created": 9767,
"edges_created": 26557,
"languages": { "python": 335 },
"elapsed_ms": 910.0
}
Adapters
| Adapter | Input | Node Types | Edge Types |
|---|---|---|---|
code | Source code directory | file, class, function, struct, module | imports, calls, registers, configures, tests, inherits |
json | Graph snapshot JSON | (preserved from snapshot) | (preserved from snapshot) |
memory | Markdown files | document, concept, entity | references, relates_to |
light | L1GHT protocol markdown | document, section, entity, typed semantic nodes | explicit semantic edges from frontmatter and markers |
patent / article / bibtex / rfc / crossref | Structured document formats | document, section, citation, entity | citation and cross-domain edges |
universal | Best-effort document canonicalization | document, section, block, table, citation, entity, claim | document containment, references, bindings, supports |
auto / document | Format detection wrapper | routes to the strongest detected adapter | adapter-specific |
Mode Behavior
| Mode | Behavior |
|---|---|
replace | Clears the existing graph, ingests fresh, finalizes (PageRank + CSR). All perspectives and locks are invalidated. |
merge | Adds new nodes and edges into the existing graph. Existing nodes are updated if they share the same external_id. Graph is re-finalized after merge. |
When to Use
- Session start – ingest the codebase if the graph is empty or stale
- After code changes – re-ingest incrementally to update the graph
- Multi-source – merge a memory corpus into a code graph for cross-domain queries
- Federation preparation – use
federateinstead for multi-repo ingestion
Side Effects
- replace mode: clears all graph state, invalidates all perspectives and locks, marks lock baselines as stale
- merge mode: adds to graph, increments graph generation, triggers watcher events on affected locks
Related Tools
health– check graph status before deciding to ingestdrift– see what changed since last sessionfederate– multi-repo ingestiondocument_resolve– resolve canonical artifacts for a universal documentauto_ingest_start– keep document roots synchronized after ingest
document_resolve
Resolve the canonical local artifact set for a universally ingested document by source path or universal node id.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
path | string | No | Original source path or canonical markdown path. |
node_id | string | No | Universal graph node id for the document. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "document_resolve",
"arguments": {
"agent_id": "jimi",
"path": "docs/specs/auth.md"
}
}
}
Example Response
{
"source_path": "docs/specs/auth.md",
"canonical_markdown_path": "/tmp/m1nd-runtime/l1ght-cache/sources/abcd/canonical.md",
"canonical_json_path": "/tmp/m1nd-runtime/l1ght-cache/sources/abcd/canonical.json",
"claims_path": "/tmp/m1nd-runtime/l1ght-cache/sources/abcd/claims.json",
"producer": "universal:internal",
"section_count": 4,
"claim_count": 3,
"binding_count": 2
}
When to Use
- when an agent needs the durable local artifact path
- when a doc has already been ingested and you want its canonical projection
- before opening
canonical.mdorclaims.jsondirectly
Related Tools
document_provider_health
Report availability, mode, detail, and install hints for optional universal-document providers.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
Example Response
{
"python": "python3",
"providers": [
{ "name": "docling", "available": true, "mode": "broad-spectrum canonicalizer" },
{ "name": "grobid", "available": false, "mode": "scholarly pdf lane", "install_hint": "Set M1ND_GROBID_URL to a reachable GROBID service." }
]
}
When to Use
- before assuming richer HTML/PDF/office extraction exists
- during environment setup
- when a provider-backed lane seems to be falling back unexpectedly
Related Tools
document_bindings
Resolve deterministic document-to-code bindings for a universal document.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
path | string | No | Original source path or canonical markdown path. |
node_id | string | No | Universal graph node id for the document. |
top_k | integer | No | Maximum bindings to return. |
Example Response
{
"source_path": "docs/specs/auth.md",
"bindings": [
{
"target_node_id": "file::src/auth/session.rs",
"target_label": "SessionPool",
"relation": "mentions_symbol",
"score": 0.92,
"confidence": "parsed",
"reason": "exact label match"
}
]
}
When to Use
- when the question is “which code implements this doc?”
- when preparing an implementation map from a spec, paper, or note
- before editing code to match a document
Related Tools
document_drift
Analyze stale, missing, or ambiguous document/code bindings for a universal document.
Example Response
{
"source_path": "docs/specs/auth.md",
"summary": {
"total_findings": 1,
"stale_bindings": 1,
"missing_targets": 0,
"ambiguous_targets": 0,
"unbacked_claims": 0,
"code_change_unreflected": 1
}
}
When to Use
- after refactors or repo moves
- when document claims may no longer be backed by current code
- when a spec feels “probably stale” and you want a grounded first pass
Related Tools
auto_ingest_start
Start local-first document watchers for one or more roots and supported document families.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
roots | string[] | Yes | Filesystem roots to watch recursively. |
formats | string[] | No | Supported document formats to auto-ingest. |
debounce_ms | integer | No | Minimum quiet period before a change is eligible for ingestion. |
namespace | string | No | Optional namespace for non-code document nodes. |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "auto_ingest_start",
"arguments": {
"agent_id": "jimi",
"roots": ["/project/docs", "/project/wiki"],
"formats": ["universal", "light"],
"debounce_ms": 200
}
}
}
auto_ingest_status
Inspect the current auto-ingest runtime, queue depth, semantic counts, provider status, and provider route/fallback counts.
Example Response
{
"running": true,
"queue_depth": 0,
"semantic_document_count": 12,
"semantic_claim_count": 34,
"drift_document_count": 1,
"provider_status": { "docling": true, "trafilatura": true, "grobid": false }
}
auto_ingest_tick
Drain queued document changes immediately and apply them to the active graph.
Example Response
{
"ingested_paths": ["/project/docs/specs/auth.md"],
"removed_paths": [],
"skipped_paths": [],
"errored_paths": []
}
auto_ingest_stop
Stop active document watchers and persist the manifest state.
Related Tools
health
Server health and statistics. Returns node/edge counts, query count, uptime, memory usage, plasticity state, and active sessions.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
Example Request
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "health",
"arguments": {
"agent_id": "jimi"
}
}
}
Example Response
{
"status": "healthy",
"node_count": 9767,
"edge_count": 26557,
"queries_processed": 142,
"uptime_seconds": 3600.5,
"memory_usage_bytes": 52428800,
"plasticity_state": "active",
"last_persist_time": "2026-03-13T10:30:00Z",
"active_sessions": [
{ "agent_id": "jimi", "last_active": "2026-03-13T11:25:00Z" }
]
}
When to Use
- Session start – first tool to call; verify the server is alive and the graph is loaded
- Monitoring – periodic health checks in long sessions
- Debugging – check memory usage and query counts
Related Tools
validate_plan
Validate a proposed modification plan against the code graph. Detects gaps (affected files missing from the plan), risk level, test coverage, and suggested additions. Designed to be called before implementing a plan.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
actions | object[] | Yes | – | Ordered list of planned actions. Each object has: action_type (string, required – "modify", "create", "delete", "rename", "test"), file_path (string, required – relative path), description (string, optional), depends_on (string[], optional – other file_paths this action depends on). |
include_test_impact | boolean | No | true | Analyze test coverage for modified files. |
include_risk_score | boolean | No | true | Compute composite risk score. |
Example Request
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "validate_plan",
"arguments": {
"agent_id": "jimi",
"actions": [
{ "action_type": "modify", "file_path": "backend/session_pool.py", "description": "Add connection timeout" },
{ "action_type": "modify", "file_path": "backend/worker_pool.py", "description": "Update pool acquire to use timeout" },
{ "action_type": "test", "file_path": "backend/tests/test_session_pool.py" }
]
}
}
}
Example Response
{
"actions_analyzed": 3,
"actions_resolved": 3,
"actions_unresolved": 0,
"gaps": [
{
"file_path": "backend/config.py",
"node_id": "file::config.py",
"reason": "imported by modified file session_pool.py -- timeout config likely needed here",
"severity": "warning",
"signal_strength": 0.72
},
{
"file_path": "backend/process_manager.py",
"node_id": "file::process_manager.py",
"reason": "in blast radius of worker_pool.py -- calls worker_pool.submit",
"severity": "info",
"signal_strength": 0.45
}
],
"risk_score": 0.52,
"risk_level": "medium",
"test_coverage": {
"modified_files": 2,
"tested_files": 1,
"untested_files": ["backend/worker_pool.py"],
"coverage_ratio": 0.5
},
"suggested_additions": [
{ "action_type": "modify", "file_path": "backend/config.py", "reason": "Timeout configuration likely needed" },
{ "action_type": "test", "file_path": "backend/tests/test_worker_pool.py", "reason": "Modified file has no test action in plan" }
],
"blast_radius_total": 47,
"elapsed_ms": 35.0
}
Risk Levels
| Level | Score Range | Meaning |
|---|---|---|
"low" | < 0.3 | Small, well-tested change |
"medium" | 0.3 – 0.6 | Moderate scope, some gaps |
"high" | 0.6 – 0.8 | Large scope or missing tests |
"critical" | >= 0.8 | Very high risk – review carefully |
When to Use
- Before implementing – validate your plan catches all affected files
- PR review – validate that a PR’s changes are complete
- Planning – estimate risk and scope before committing to a plan
- Quality gate – reject plans with risk_score > threshold
Related Tools
impact– blast radius for a single nodepredict– co-change prediction for a single nodetrace– validate a fix plan for a specific error
lock_create
Pin a subgraph region and capture a baseline snapshot for change monitoring. Locks are used to track what changes in a region of the graph while you work. The baseline is compared against the current state when you call lock.diff.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. Lock is owned by this agent. |
scope | string | Yes | – | Scope type. Values: "node" (single nodes only), "subgraph" (BFS expansion from roots), "query_neighborhood" (nodes matching a query), "path" (ordered node list). |
root_nodes | string[] | Yes | – | Root nodes for the lock scope. Non-empty. Matched by external_id (exact), then label, then substring. |
radius | integer | No | – | BFS radius for subgraph scope. Range: 1 to 4. Required for subgraph scope. |
query | string | No | – | Query string for query_neighborhood scope. |
path_nodes | string[] | No | – | Ordered node list for path scope. |
Scope Types
| Scope | Description |
|---|---|
node | Lock only the specified root nodes |
subgraph | BFS expansion from root nodes up to radius hops |
query_neighborhood | Nodes matching a query activation |
path | An ordered list of nodes forming a path |
Example Request
{
"jsonrpc": "2.0",
"id": 4,
"method": "tools/call",
"params": {
"name": "lock_create",
"arguments": {
"agent_id": "jimi",
"scope": "subgraph",
"root_nodes": ["file::chat_handler.py"],
"radius": 2
}
}
}
Example Response
{
"lock_id": "lock_jimi_001",
"scope": "subgraph",
"baseline_nodes": 1639,
"baseline_edges": 707,
"graph_generation": 42,
"created_at_ms": 1710300000000
}
Limits
- Max locks per agent: configurable (default: 10)
- Max baseline nodes: configurable (default: 5000)
- Max baseline edges: configurable (default: 10000)
- Total memory budget shared with perspectives
When to Use
- Change monitoring – lock a region before making changes, then diff to see what changed
- Multi-agent coordination – lock regions to detect when other agents’ changes affect your work
- Regression detection – lock a stable region and watch for unexpected changes
Related Tools
lock_watch– set automatic change detectionlock_diff– compute what changed since baselinelock_release– release the lock when done
lock_watch
Set a watcher strategy on a lock. Watchers determine when the lock automatically detects changes. Without a watcher, you must manually call lock.diff.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. Must own the lock. |
lock_id | string | Yes | – | Lock to set the watcher on. |
strategy | string | Yes | – | Watcher strategy. Values: "manual" (no automatic detection), "on_ingest" (detect after every ingest), "on_learn" (detect after every learn call). Note: "periodic" is not supported in V1. |
Example Request
{
"jsonrpc": "2.0",
"id": 5,
"method": "tools/call",
"params": {
"name": "lock_watch",
"arguments": {
"agent_id": "jimi",
"lock_id": "lock_jimi_001",
"strategy": "on_ingest"
}
}
}
Example Response
{
"lock_id": "lock_jimi_001",
"strategy": "on_ingest",
"previous_strategy": null
}
When to Use
- Automatic monitoring – set
on_ingestto detect changes after every code re-ingest - Learning feedback – set
on_learnto detect when learning shifts edge weights in your region - Manual control – set
manualto disable automatic detection
Related Tools
lock_diff– manually trigger a diff (always available regardless of strategy)lock_create– create the lock first
lock_diff
Compute what changed in a locked region since the baseline was captured. Returns new/removed nodes, new/removed edges, weight changes, and watcher event counts. Fast-path: if the graph generation has not changed, returns immediately with no_changes: true.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. Must own the lock. |
lock_id | string | Yes | – | Lock to diff. |
Example Request
{
"jsonrpc": "2.0",
"id": 6,
"method": "tools/call",
"params": {
"name": "lock_diff",
"arguments": {
"agent_id": "jimi",
"lock_id": "lock_jimi_001"
}
}
}
Example Response (changes detected)
{
"diff": {
"lock_id": "lock_jimi_001",
"no_changes": false,
"new_nodes": ["file::chat_handler.py::fn::new_method"],
"removed_nodes": [],
"new_edges": ["chat_handler.py|new_method->stream_parser.py|calls"],
"removed_edges": [],
"boundary_edges_added": [],
"boundary_edges_removed": [],
"weight_changes": [
{ "edge_key": "chat_handler.py|stream_parser.py|imports", "old_weight": 0.5, "new_weight": 0.72 }
],
"baseline_stale": false,
"elapsed_ms": 0.08
},
"watcher_events_drained": 2,
"rebase_suggested": null
}
Example Response (no changes)
{
"diff": {
"lock_id": "lock_jimi_001",
"no_changes": true,
"new_nodes": [],
"removed_nodes": [],
"new_edges": [],
"removed_edges": [],
"boundary_edges_added": [],
"boundary_edges_removed": [],
"weight_changes": [],
"baseline_stale": false,
"elapsed_ms": 0.001
},
"watcher_events_drained": 0,
"rebase_suggested": null
}
When to Use
- After ingest – check if your locked region was affected
- After learning – check if feedback shifted weights in your region
- Periodic check – poll for changes during long sessions
- Before committing – verify no unexpected changes in the region
Related Tools
lock_rebase– re-capture baseline after acknowledging changeslock_watch– set automatic change detection
lock_rebase
Re-capture the lock baseline from the current graph without releasing the lock. Use this after calling lock.diff and acknowledging the changes – the new baseline becomes the reference for future diffs.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. Must own the lock. |
lock_id | string | Yes | – | Lock to rebase. |
Example Request
{
"jsonrpc": "2.0",
"id": 7,
"method": "tools/call",
"params": {
"name": "lock_rebase",
"arguments": {
"agent_id": "jimi",
"lock_id": "lock_jimi_001"
}
}
}
Example Response
{
"lock_id": "lock_jimi_001",
"previous_generation": 42,
"new_generation": 45,
"baseline_nodes": 1645,
"baseline_edges": 712,
"watcher_preserved": true
}
When to Use
- After acknowledging changes – rebase after reviewing a diff to reset the baseline
- After stale warning – when
lock.diffreturnsbaseline_stale: true, rebase to fix it - Periodic refresh – rebase periodically in long sessions to keep baselines current
Related Tools
lock_diff– the diff that triggers a rebaselock_create– creating a new lock is an alternative to rebasing
lock_release
Release a lock and free its resources. Removes the lock state, cleans up pending watcher events, and frees memory. Irreversible.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. Must own the lock. |
lock_id | string | Yes | – | Lock to release. |
Example Request
{
"jsonrpc": "2.0",
"id": 8,
"method": "tools/call",
"params": {
"name": "lock_release",
"arguments": {
"agent_id": "jimi",
"lock_id": "lock_jimi_001"
}
}
}
Example Response
{
"lock_id": "lock_jimi_001",
"released": true
}
When to Use
- Done monitoring – release when you no longer need change detection
- Memory pressure – locks consume memory proportional to baseline size
- Session end – release all locks before ending a session
- Automatic: locks are also cascade-released when their associated perspective is closed via
perspective.close
Related Tools
lock_create– create a new lockperspective_close– cascade-releases associated locks
daemon_start
Start the persisted daemon control plane. Stores watched roots, initializes daemon counters, and begins the long-lived structural monitoring lane.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
watch_paths | string[] | No | current ingest roots | Paths the daemon should monitor. |
poll_interval_ms | integer | No | 500 | Poll interval fallback in milliseconds. |
When to Use
- Start of a long-lived agent session
- Before relying on daemon alerts or
daemon_tick - Before background/idle reconciliation should run
Related Tools
daemon_stop
Stop the daemon control plane without deleting persisted alert history.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
When to Use
- End of a daemon-backed session
- Before shutting down a host that should not keep reconciling
Related Tools
daemon_status
Inspect daemon liveness and runtime counters. Returns watched roots, tracked files, recent tick metrics, and alert counts.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
Typical Output Fields
activewatch_pathspoll_interval_mstracked_filestick_countlast_tick_duration_mslast_tick_changed_fileslast_tick_deleted_fileslast_tick_alerts_emittedalert_count
When to Use
- To verify daemon startup worked
- To inspect whether reconciliation is actually happening
- To debug daemon slowness or alert silence
Related Tools
daemon_tick
Run one explicit daemon reconciliation pass. Polls watched roots, re-ingests changed files, detects deletions, and emits drift alerts.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
max_files | integer | No | 32 | Maximum changed files to process in one tick. |
Typical Output Fields
changed_files_detecteddeleted_files_detectedfiles_reingestedingested_files[]alerts_emittedalert_ids[]tick_at_ms
When to Use
- To force one reconciliation before reading daemon status
- To debug watched-root drift deterministically
- To reproduce daemon ingest issues outside background ticking
Related Tools
alerts_list
List persisted daemon and proactive alerts.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
agent_id | string | Yes | – | Calling agent identifier. |
include_acked | boolean | No | false | Include already acknowledged alerts. |
limit | integer | No | 50 | Maximum alerts to return. |
When to Use
- Reviewing daemon findings after a session
- Building an alert inbox for an agent or UI
Related Tools
alerts_ack
Acknowledge one or more persisted daemon/proactive alerts so they stop resurfacing in the unread queue.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
alert_ids | string[] | Yes | Alert IDs to acknowledge. |
When to Use
- After reviewing or actioning daemon findings
- To keep the alert queue focused on new drift
Related Tools
edit_preview
Preview a full-file write without touching disk. Returns a diff, freshness snapshot, and validation report so the caller can inspect before committing.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
file_path | string | Yes | Absolute or workspace-relative file path. |
new_content | string | Yes | Candidate replacement content. |
description | string | No | Human-readable summary of the edit. |
When to Use
- Before risky writes
- When you want a two-phase edit protocol
- When a human or another agent should inspect the diff first
Related Tools
edit_commit
Commit a previously previewed edit after freshness re-check and explicit confirmation.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
preview_id | string | Yes | Preview handle returned by edit_preview. |
confirm | boolean | Yes | Must be true to commit the preview. |
reingest | boolean | No | Re-ingest the modified file after commit. |
When to Use
- After a human/agent approves an
edit_preview - When stale-source protection matters more than speed
Related Tools
persist
Force graph and sidecar persistence immediately.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
action | string | Yes | Persistence action, such as save/load semantics supported by the current implementation. |
When to Use
- Before shutdown
- Before risky host lifecycle transitions
- When you want an explicit persistence checkpoint
Related Tools
boot_memory
Persist small canonical hot-state values next to the graph without polluting larger investigation trails.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
action | string | Yes | Memory action (set, get, list, delete, etc.). |
key | string | No | Canonical key to address. |
value | json | No | JSON value to store. |
When to Use
- Short doctrine/state values that should stay hot
- Session bootstrapping facts an agent should retrieve quickly
Related Tools
heuristics_surface
Explain why a node or file is currently ranked as risky or important. Surfaces trust/tremor/antibody/blast-style heuristic factors in one payload.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
node_id | string | No | Graph node to inspect. |
file_path | string | No | File path to inspect. |
When to Use
- After
predict,validate_plan, or surgical flows rank something unexpectedly high - When an agent needs explainability before editing or escalating
Related Tools
audit
Profile-aware one-call audit over topology, scans, verification, filesystem truth, and git state.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
agent_id | string | Yes | Calling agent identifier. |
path | string | Yes | Root path to audit. |
profile | string | No | Audit profile such as auto, quick, coordination, or production. |
depth | string | No | Audit depth. |
cross_verify | boolean | No | Include graph-vs-disk verification. |
external_refs | boolean | No | Include explicit external reference discovery. |
When to Use
- First pass on an unfamiliar repo
- Long-running session orientation
- Pre-handoff or pre-merge structural review
Related Tools
Quick Start
Five minutes from zero to your first useful graph query.
Prerequisites
- Rust toolchain: current stable recommended if you build from source
- A codebase: Python, Rust, TypeScript/JavaScript, Go, and Java have the strongest handling; additional languages are available through tree-sitter and fallback extraction
- An MCP client: Claude Code, Codex, Cursor, Windsurf, Zed, Cline, Continue, or any client that can connect to an MCP server over stdio
Install
Choose one path:
1. Build from source
git clone https://github.com/maxkle1nz/m1nd.git
cd m1nd
cargo build --release
The binary will be at:
./target/release/m1nd-mcp
If you want it on your PATH:
cp ./target/release/m1nd-mcp /usr/local/bin/
2. Install from crates.io
cargo install m1nd-mcp
3. Download a release binary
The current release workflow publishes these artifact names:
m1nd-mcp-linux-x86_64m1nd-mcp-macos-x86_64m1nd-mcp-macos-aarch64
If you use release binaries, download them from the latest GitHub release page instead of relying on hardcoded tarball names.
Verify the binary
m1nd-mcp --help
If the binary is healthy, you should see the CLI help for the MCP server.
Configure your MCP client
m1nd is an MCP server over stdio. Your client starts the binary and calls tools through MCP.
Claude Code
Add this to your Claude Code MCP config.
The exact file path depends on how you run Claude Code, but the current repo README uses this shape:
{
"mcpServers": {
"m1nd": {
"command": "/path/to/m1nd-mcp",
"env": {
"M1ND_GRAPH_SOURCE": "/tmp/m1nd-graph.json",
"M1ND_PLASTICITY_STATE": "/tmp/m1nd-plasticity.json"
}
}
}
}
Codex
Codex reads MCP servers from ~/.codex/config.toml.
Use:
[mcp_servers.m1nd]
command = "/path/to/m1nd-mcp"
args = ["--stdio", "--no-gui"]
[mcp_servers.m1nd.env]
M1ND_GRAPH_SOURCE = "/tmp/m1nd-graph.json"
M1ND_PLASTICITY_STATE = "/tmp/m1nd-plasticity.json"
Verify with:
codex mcp list
If the entry is enabled, restart Codex if needed and make the first call through the MCP tool surface.
Cursor
In MCP Servers, use the same binary + env setup:
{
"m1nd": {
"command": "/path/to/m1nd-mcp",
"env": {
"M1ND_GRAPH_SOURCE": "/tmp/m1nd-graph.json",
"M1ND_PLASTICITY_STATE": "/tmp/m1nd-plasticity.json"
}
}
}
Antigravity
Antigravity supports a workspace-local mcp_config.json shape.
Use:
{
"mcpServers": {
"m1nd": {
"command": "python3",
"args": ["/path/to/m1nd-antigravity-proxy.py"],
"cwd": "/path/to/workspace",
"env": {
"M1ND_OPENCLAW_SOCKET": "/tmp/m1nd-openclaw.sock",
"M1ND_GRAPH_SOURCE": "/tmp/m1nd-graph.json",
"M1ND_PLASTICITY_STATE": "/tmp/m1nd-plasticity.json"
}
}
}
}
This keeps the host-facing contract stdio-compatible while routing into the hot native daemon.
Other MCP clients
The pattern is the same:
- point the client at
m1nd-mcp - optionally set graph/plasticity persistence env vars
- then call tools through MCP
Persistence variables
| Variable | Purpose | If omitted |
|---|---|---|
M1ND_GRAPH_SOURCE | Persist the graph snapshot | graph is memory-only |
M1ND_PLASTICITY_STATE | Persist learned edge weights | learning is memory-only |
Recommendation: set both if you want continuity across restarts.
Important note on tool names
The live MCP registry exposes bare tool names like:
ingestactivatesearchimpact
Some clients or docs may show transport-prefixed aliases. Treat that as presentation sugar. The canonical live registry names are the bare tool names shown by tools/list.
First run
Once your client is configured, the first thing to do is ingest a project.
Step 1: ingest a repo
{
"method": "tools/call",
"params": {
"name": "ingest",
"arguments": {
"agent_id": "dev",
"path": "/path/to/your/project"
}
}
}
Example response shape:
{
"files_processed": 335,
"nodes_created": 9767,
"edges_created": 26557,
"languages": {
"python": 335
},
"elapsed_ms": 910
}
What happened:
- files were parsed
- structural nodes and edges were created
- references were resolved
- the graph was finalized for querying
Step 2: check server health
{
"method": "tools/call",
"params": {
"name": "health",
"arguments": {
"agent_id": "dev"
}
}
}
Current response shape in the repo:
{
"status": "ok",
"node_count": 9767,
"edge_count": 26557,
"queries_processed": 1,
"uptime_seconds": 12.4,
"memory_usage_bytes": 0,
"plasticity_state": "0 edges tracked",
"last_persist_time": null,
"active_sessions": [
{
"agent_id": "dev",
"query_count": 1
}
]
}
Step 3: run a first structural audit
audit is the fastest one-call orientation pass, and it requires the repo root path:
{
"method": "tools/call",
"params": {
"name": "audit",
"arguments": {
"agent_id": "dev",
"path": "/path/to/your/project",
"profile": "auto"
}
}
}
Use it when you want:
- top-level module shape
- likely risk seams
- git/filesystem verification
- a first recommendation for where to inspect next
Step 4: ingest a document root with the universal lane
If your investigation spans specs, notes, wiki pages, or office/PDF artifacts, ingest them into the same graph instead of keeping them outside the runtime:
{
"method": "tools/call",
"params": {
"name": "ingest",
"arguments": {
"agent_id": "dev",
"path": "/path/to/your/docs",
"adapter": "universal",
"mode": "merge"
}
}
}
Then resolve the canonical artifact set:
{
"method": "tools/call",
"params": {
"name": "document_resolve",
"arguments": {
"agent_id": "dev",
"path": "docs/specs/auth.md"
}
}
}
This is the shortest path from “there is a doc” to “the agent can reason over the canonical local copy, bind it to code, and check drift later.”
If you see node_count: 0, your ingest path was wrong or the ingest did not run.
Step 5: ask the graph something real
{
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"agent_id": "dev",
"query": "authentication",
"top_k": 5
}
}
}
Example response shape:
{
"query": "authentication",
"activated": [
{
"node_id": "file::auth.py",
"score": 0.89
},
{
"node_id": "file::middleware.py",
"score": 0.72
},
{
"node_id": "file::session.py",
"score": 0.61
}
],
"ghost_edges": [
{
"from": "file::auth.py",
"to": "file::rate_limiter.py",
"confidence": 0.34
}
],
"elapsed_ms": 31,
"proof_state": "triaging"
}
What happened:
- seed nodes were selected from the query
- spreading activation propagated across graph structure
- the result was ranked by graph signal, not just text matching
What to do next
After the first activate, these are the most useful next steps:
searchfor exact text or regexseekfor intent-based retrievalimpactbefore touching a central filesurgical_context_v2before multi-file editsvalidate_planwhen you already know the files you want to touch
Troubleshooting
“No graph snapshot found, starting fresh”
Normal on first run. The graph is empty until you call ingest.
Ingest returns 0 files
Check the path and confirm it points at a real project root.
My client cannot see the tools
Check:
- the
m1nd-mcppath - execute permissions on the binary
- the MCP client logs
Learned state disappears between restarts
Set:
M1ND_GRAPH_SOURCEM1ND_PLASTICITY_STATE
I want persistent low-latency operation for a large codebase
See:
That doc covers the persistent server + stdio proxy pattern for near-zero startup overhead.
First Query: The Full Cycle
This tutorial walks you through the core m1nd workflow: ingest, activate, learn, and observe the graph getting smarter. Then we explore structural holes and counterfactual simulation.
Prerequisites: You have completed the Quick Start and have m1nd running with a codebase ingested.
All examples use the JSON-RPC wire format. If you are working through an MCP client (Claude Code, Cursor, etc.), the client sends these calls for you when you invoke the tools by name.
Step 1: Ingest Your Codebase
If you have not already ingested, do it now:
{
"method": "tools/call",
"params": {
"name": "ingest",
"arguments": {
"path": "/your/project",
"agent_id": "dev"
}
}
}
Response:
{
"files_processed": 335,
"nodes_created": 9767,
"edges_created": 26557,
"languages": {"python": 335},
"elapsed_ms": 910
}
The graph now contains structural nodes (files, classes, functions) and edges (imports, calls, inheritance, co-change patterns). PageRank has been computed, giving each node a centrality score.
Step 2: First Activation
Ask the graph about session pool management:
{
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"query": "session pool management",
"agent_id": "dev",
"top_k": 5
}
}
}
Response:
{
"activated": [
{
"node_id": "file::session_pool.py",
"score": 0.89,
"dimension_scores": {
"structural": 0.92,
"semantic": 0.95,
"temporal": 0.78,
"causal": 0.71
}
},
{"node_id": "file::session_pool.py::class::SessionPool", "score": 0.84},
{"node_id": "file::worker_pool.py", "score": 0.61},
{"node_id": "file::session_pool.py::fn::acquire", "score": 0.58},
{"node_id": "file::process_manager.py", "score": 0.45}
],
"ghost_edges": [
{
"from": "file::session_pool.py",
"to": "file::healing_manager.py",
"confidence": 0.34
}
]
}
Reading the results:
score: Combined 4-dimensional activation score (0.0 to 1.0)dimension_scores: Breakdown by structural (graph distance, PageRank), semantic (token overlap), temporal (co-change history), and causal (suspiciousness)ghost_edges: Connections the graph inferred but that are not explicit in code. Here,session_pool.pyandhealing_manager.pyare structurally unconnected but co-activate together – a hidden dependency worth investigating.
Note the scores. We will come back to this query after teaching the graph.
Step 3: Teach the Graph (Hebbian Learning)
The top two results (session_pool.py and the SessionPool class) were exactly what we needed. Tell the graph:
{
"method": "tools/call",
"params": {
"name": "learn",
"arguments": {
"query": "session pool management",
"agent_id": "dev",
"feedback": "correct",
"node_ids": [
"file::session_pool.py",
"file::session_pool.py::class::SessionPool"
],
"strength": 0.2
}
}
}
Response:
{
"edges_strengthened": 740,
"edges_weakened": 0,
"plasticity_records": 740,
"learning_type": "hebbian_ltp"
}
What happened: Hebbian Long-Term Potentiation (LTP) strengthened 740 edges along paths connecting the confirmed-useful nodes. “Neurons that fire together wire together.” The next time anyone queries this region of the graph, those paths carry more signal.
Now suppose worker_pool.py (score 0.61) was not actually relevant. Mark it wrong:
{
"method": "tools/call",
"params": {
"name": "learn",
"arguments": {
"query": "session pool management",
"agent_id": "dev",
"feedback": "wrong",
"node_ids": ["file::worker_pool.py"],
"strength": 0.2
}
}
}
Response:
{
"edges_strengthened": 0,
"edges_weakened": 312,
"plasticity_records": 1052,
"learning_type": "hebbian_ltd"
}
Long-Term Depression (LTD) weakened 312 edges leading to worker_pool.py from this query region. The graph now knows: for session pool queries, worker_pool.py is noise.
Step 4: Activate Again – See the Improvement
Run the exact same query:
{
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"query": "session pool management",
"agent_id": "dev",
"top_k": 5
}
}
}
Expected changes:
{
"activated": [
{"node_id": "file::session_pool.py", "score": 0.93},
{"node_id": "file::session_pool.py::class::SessionPool", "score": 0.88},
{"node_id": "file::session_pool.py::fn::acquire", "score": 0.65},
{"node_id": "file::process_manager.py", "score": 0.47},
{"node_id": "file::healing_manager.py", "score": 0.39}
]
}
Compare with Step 2:
| Node | Before | After | Change |
|---|---|---|---|
session_pool.py | 0.89 | 0.93 | +0.04 (strengthened) |
SessionPool class | 0.84 | 0.88 | +0.04 (strengthened) |
worker_pool.py | 0.61 | dropped | Pushed below top-5 (weakened) |
healing_manager.py | ghost only | 0.39 | Promoted from ghost to main results |
The graph learned. worker_pool.py fell out of the top results. healing_manager.py, previously only a ghost edge, got promoted because the strengthened paths through session_pool.py now carry more signal to its neighborhood.
This is the core value proposition of m1nd: every interaction makes the graph smarter. No other code intelligence tool does this.
Step 5: Structural Hole Detection
Ask the graph what is missing around a topic:
{
"method": "tools/call",
"params": {
"name": "missing",
"arguments": {
"query": "database connection pooling",
"agent_id": "dev"
}
}
}
Response:
{
"holes": [
{
"region": "connection lifecycle",
"adjacent_nodes": 4,
"description": "No dedicated connection pool abstraction"
},
{
"region": "pool metrics",
"adjacent_nodes": 3,
"description": "No pool health monitoring"
},
{
"region": "graceful drain",
"adjacent_nodes": 2,
"description": "No connection drain on shutdown"
}
],
"total_holes": 9
}
What happened: m1nd activated the “database connection pooling” region of the graph and looked for gaps – areas where the graph’s structure predicts a node should exist but none does. These are structural holes: places where other codebases of similar shape would have components but yours does not.
This is not a linter or rule-based checker. It is topology-based gap detection. The graph’s shape implies these components should exist, based on the relationships between the nodes that do exist.
Step 6: Counterfactual Simulation
Before deleting or rewriting a module, simulate the consequences:
{
"method": "tools/call",
"params": {
"name": "counterfactual",
"arguments": {
"node_ids": ["file::spawner.py"],
"agent_id": "dev"
}
}
}
Response:
{
"cascade": [
{"depth": 1, "affected": 23},
{"depth": 2, "affected": 456},
{"depth": 3, "affected": 3710}
],
"total_affected": 4189,
"orphaned_count": 0,
"pct_activation_lost": 0.41
}
Reading the results:
- Depth 1: 23 nodes directly depend on
spawner.py - Depth 2: 456 more nodes depend on those 23
- Depth 3: 3,710 more – a cascade explosion
- Total: 4,189 nodes affected out of ~9,767 (42.9% of the graph)
- Activation lost: 41% of the graph’s total activation capacity would be disrupted
Compare this with removing config.py:
{
"method": "tools/call",
"params": {
"name": "counterfactual",
"arguments": {
"node_ids": ["file::config.py"],
"agent_id": "dev"
}
}
}
Response:
{
"cascade": [
{"depth": 1, "affected": 89},
{"depth": 2, "affected": 1234},
{"depth": 3, "affected": 1208}
],
"total_affected": 2531,
"orphaned_count": 3,
"pct_activation_lost": 0.28
}
Despite config.py having more direct dependents (89 vs 23), its total cascade is smaller (2,531 vs 4,189). spawner.py sits at a structural chokepoint where downstream nodes have more transitive dependencies. This insight is impossible to get from grep or import analysis alone – it requires full graph traversal.
Step 7: Hypothesis Testing (Bonus)
Test a structural claim against the graph:
{
"method": "tools/call",
"params": {
"name": "hypothesize",
"arguments": {
"claim": "worker_pool depends on whatsapp_manager at runtime",
"agent_id": "dev"
}
}
}
Response:
{
"verdict": "likely_true",
"confidence": 0.72,
"paths_explored": 25015,
"evidence": [
{
"path": [
"file::worker_pool.py",
"file::process_manager.py::fn::cancel",
"file::whatsapp_manager.py"
],
"hops": 2
}
],
"note": "2-hop dependency via cancel function -- invisible to grep"
}
The hypothesis engine explored 25,015 paths in 58ms and found a 2-hop dependency that no text search could reveal: the worker pool reaches the WhatsApp manager through a cancel function in the process manager. This is the kind of hidden coupling that causes production incidents.
Summary: The Learning Loop
The core m1nd workflow is a feedback loop:
ingest --> activate --> use results --> learn --> activate again
| |
| (graph gets smarter) |
+----------------------------------------------+
Every learn call shifts edge weights. Every subsequent activate benefits from accumulated learning. Over sessions, the graph adapts to how your team thinks about your codebase.
Additional tools layer on top of this foundation:
| Tool | When to Use |
|---|---|
missing | Before designing new features – find what your codebase lacks |
counterfactual | Before deleting or rewriting – simulate the blast radius |
hypothesize | When debugging – test assumptions about hidden dependencies |
impact | Before modifying a file – understand the blast radius |
predict | After modifying a file – which other files probably need changes too |
trace | When an error occurs – map stacktraces to structural root causes |
Next: Multi-Agent Tutorial – how multiple agents share one graph.
Multi-Agent Usage
m1nd is designed for multi-agent systems. One m1nd instance serves many agents simultaneously. This tutorial covers agent identity, concurrent access, perspective isolation, trail sharing, and a real-world example of how ROOMANIZER OS uses m1nd with 19 agents.
How It Works
m1nd runs as a single process with a single shared graph. Multiple MCP clients connect to the same instance (or multiple instances reading from the same persisted state). Every tool call includes an agent_id parameter that identifies the caller.
Agent A (Claude Code) ----+
|
Agent B (Cursor) ----+----> m1nd-mcp (single graph)
|
Agent C (custom agent) ----+
The graph is shared. Learning by one agent benefits all agents. Perspectives are isolated per agent. Trails can be shared across agents.
Agent ID Conventions
Every m1nd tool requires an agent_id parameter. This is a free-form string, but consistent naming matters:
agent_id: "jimi" -- orchestrator
agent_id: "hacker-auth" -- security hardening agent
agent_id: "forge-api" -- API building agent
agent_id: "analyst-perf" -- performance analysis agent
Recommended convention: {archetype}-{task} for short-lived task agents, simple names for persistent agents.
Rules:
- Agent IDs are case-sensitive
- Use lowercase with hyphens
- m1nd tracks all agent IDs it has seen (visible in
healthoutput) - Agent ID determines perspective ownership and trail ownership
Shared Graph, Individual Learning
When Agent A calls learn with feedback, the edge weight changes are visible to all agents immediately:
// Agent A: "session_pool.py was useful for my auth investigation"
{
"method": "tools/call",
"params": {
"name": "learn",
"arguments": {
"query": "authentication flow",
"agent_id": "agent-a",
"feedback": "correct",
"node_ids": ["file::session_pool.py"]
}
}
}
// -> 740 edges strengthened
// Agent B: immediately benefits from stronger session_pool.py edges
{
"method": "tools/call",
"params": {
"name": "activate",
"arguments": {
"query": "session management",
"agent_id": "agent-b",
"top_k": 5
}
}
}
// -> session_pool.py scores higher than it would have before Agent A's feedback
This is by design. The graph represents collective intelligence about the codebase. Every agent’s learning contributes to the whole.
Perspective Isolation
Perspectives are m1nd’s navigation system – a stateful exploration session anchored to a node, with a route surface, breadcrumb history, and focus tracking.
Perspectives are isolated per agent. Agent A’s perspectives are not visible to Agent B, and navigation in one perspective does not affect another.
Agent A: Start a Perspective
{
"method": "tools/call",
"params": {
"name": "perspective_start",
"arguments": {
"agent_id": "agent-a",
"query": "authentication middleware",
"anchor_node": "file::middleware.py"
}
}
}
Response:
{
"perspective_id": "persp-a1b2c3",
"focus": "file::middleware.py",
"routes": [
{"route_id": "r-001", "target": "file::auth.py", "label": "imports", "score": 0.92},
{"route_id": "r-002", "target": "file::session.py", "label": "calls", "score": 0.78}
],
"route_set_version": 1
}
Agent B: Has Its Own Perspectives
{
"method": "tools/call",
"params": {
"name": "perspective_start",
"arguments": {
"agent_id": "agent-b",
"query": "worker pool scaling"
}
}
}
Agent B gets a completely independent perspective. Its routes, focus, and history are separate from Agent A’s.
Listing Perspectives
Each agent only sees its own:
// Agent A sees only its perspectives
{
"method": "tools/call",
"params": {
"name": "perspective_list",
"arguments": {"agent_id": "agent-a"}
}
}
// -> [{"perspective_id": "persp-a1b2c3", "focus": "file::middleware.py", ...}]
// Agent B sees only its perspectives
{
"method": "tools/call",
"params": {
"name": "perspective_list",
"arguments": {"agent_id": "agent-b"}
}
}
// -> [{"perspective_id": "persp-d4e5f6", "focus": "file::worker_pool.py", ...}]
Comparing Perspectives Across Agents
You can explicitly compare two perspectives from different agents using perspective.compare. This is useful for discovering where two independent investigations overlap:
{
"method": "tools/call",
"params": {
"name": "perspective_compare",
"arguments": {
"agent_id": "agent-a",
"perspective_id_a": "persp-a1b2c3",
"perspective_id_b": "persp-d4e5f6"
}
}
}
Response:
{
"shared_nodes": ["file::process_manager.py", "file::config.py"],
"unique_to_a": ["file::auth.py", "file::middleware.py"],
"unique_to_b": ["file::worker_pool.py", "file::spawner.py"],
"dimension_deltas": {
"structural": 0.12,
"semantic": 0.34,
"temporal": 0.08
}
}
Lock System for Concurrent Access
When multiple agents might modify the same region of the codebase simultaneously, the lock system prevents conflicts.
Agent A: Lock the Auth Region
{
"method": "tools/call",
"params": {
"name": "lock_create",
"arguments": {
"agent_id": "agent-a",
"scope": "subgraph",
"root_nodes": ["file::auth.py"],
"radius": 2
}
}
}
Response:
{
"lock_id": "lock-xyz789",
"nodes_locked": 156,
"edges_locked": 423,
"scope": "subgraph",
"radius": 2
}
Set a Watch Strategy
{
"method": "tools/call",
"params": {
"name": "lock_watch",
"arguments": {
"agent_id": "agent-a",
"lock_id": "lock-xyz789",
"strategy": "on_ingest"
}
}
}
Now, whenever any agent triggers an ingest that touches the locked region, the lock records the changes.
Check for Changes
After Agent B modifies some code and re-ingests:
{
"method": "tools/call",
"params": {
"name": "lock_diff",
"arguments": {
"agent_id": "agent-a",
"lock_id": "lock-xyz789"
}
}
}
Response (in 0.08 microseconds):
{
"new_nodes": ["file::auth.py::fn::validate_token_v2"],
"removed_nodes": [],
"weight_changes": 3,
"structural_changes": true
}
Agent A now knows exactly what changed in its locked region, without scanning the entire graph.
Rebase or Release
// Accept changes and update baseline
{"method":"tools/call","params":{"name":"lock_rebase","arguments":{
"agent_id":"agent-a","lock_id":"lock-xyz789"
}}}
// Or release when done
{"method":"tools/call","params":{"name":"lock_release","arguments":{
"agent_id":"agent-a","lock_id":"lock-xyz789"
}}}
Trail Sharing
Trails are investigation snapshots: visited nodes, hypotheses, conclusions, open questions, and activation boosts. They persist across sessions and can be shared between agents.
Agent A: Save an Investigation
{
"method": "tools/call",
"params": {
"name": "trail_save",
"arguments": {
"agent_id": "agent-a",
"label": "auth-token-leak-investigation",
"hypotheses": [
{
"statement": "Auth tokens leak through session pool",
"confidence": 0.7,
"status": "investigating"
},
{
"statement": "Rate limiter missing from auth chain",
"confidence": 0.9,
"status": "confirmed"
}
],
"open_questions": [
"Does the healing manager observe token lifecycle?",
"Is there a token rotation policy?"
],
"tags": ["security", "auth", "session"]
}
}
}
Response:
{
"trail_id": "trail-abc123",
"nodes_captured": 47,
"hypotheses_saved": 2,
"activation_boosts_saved": 12
}
Agent B: Resume Agent A’s Trail
{
"method": "tools/call",
"params": {
"name": "trail_resume",
"arguments": {
"agent_id": "agent-b",
"trail_id": "trail-abc123"
}
}
}
Response:
{
"trail_id": "trail-abc123",
"nodes_reactivated": 47,
"stale_nodes": 2,
"hypotheses_restored": 2,
"hypotheses_downgraded": 0
}
Agent B now has Agent A’s exact cognitive context: the same nodes are activated, the same hypotheses are loaded, and any stale nodes (changed since the trail was saved) are flagged.
Merging Trails from Multiple Agents
When two agents investigate independently and you want to combine findings:
{
"method": "tools/call",
"params": {
"name": "trail_merge",
"arguments": {
"agent_id": "orchestrator",
"trail_ids": ["trail-abc123", "trail-def456"],
"label": "combined-auth-investigation"
}
}
}
Response:
{
"merged_trail_id": "trail-merged-789",
"total_nodes": 83,
"shared_nodes": 12,
"conflicts": [
{
"node": "file::auth.py",
"trail_a_hypothesis": "token leak source",
"trail_b_hypothesis": "not involved"
}
],
"conflict_count": 3
}
The merge automatically detects where independent investigations converged (12 shared nodes) and where they conflict (3 disagreements). This is essential for synthesizing multi-agent research.
Browsing Trails
{
"method": "tools/call",
"params": {
"name": "trail_list",
"arguments": {
"agent_id": "orchestrator",
"filter_tags": ["security"]
}
}
}
Real-World Example: ROOMANIZER OS
ROOMANIZER OS is a multi-agent orchestration system that uses m1nd as its shared code intelligence layer. Here is how it works in production:
Architecture
JIMI (orchestrator)
|
+-- hacker-auth (security agent) --+
+-- forge-api (API builder) --+-- All share one m1nd instance
+-- analyst-perf (performance) --+
+-- sentinel-files (file watcher) --+
+-- critic-quality (code reviewer) --+
+-- ... 14 more agents --+
One m1nd instance serves 19 agents. The graph covers a 335-file Python backend (~52K lines), a React frontend, and MCP server infrastructure.
Orchestrator Boot Sequence
When JIMI (the orchestrator) starts a session:
// Step 1: Check m1nd health
{"method":"tools/call","params":{"name":"health","arguments":{"agent_id":"jimi"}}}
// Step 2: Check what changed since last session
{"method":"tools/call","params":{"name":"drift","arguments":{"agent_id":"jimi","since":"last_session"}}}
// Step 3: Re-ingest if the graph is stale
{"method":"tools/call","params":{"name":"ingest","arguments":{
"path":"/project/backend","agent_id":"jimi","incremental":true
}}}
Task Delegation with Graph Context
When JIMI delegates a security hardening task:
// Before spawning the security agent, get blast radius context
{"method":"tools/call","params":{"name":"impact","arguments":{
"node_id":"file::auth.py","agent_id":"jimi"
}}}
// Warm up the graph for the security task
{"method":"tools/call","params":{"name":"warmup","arguments":{
"task_description":"harden authentication token validation","agent_id":"jimi"
}}}
// The security agent then uses the primed graph
{"method":"tools/call","params":{"name":"activate","arguments":{
"query":"token validation vulnerabilities","agent_id":"hacker-auth"
}}}
Collective Learning
After each agent completes its task, it provides feedback:
// Security agent found useful results
{"method":"tools/call","params":{"name":"learn","arguments":{
"query":"token validation vulnerabilities",
"agent_id":"hacker-auth",
"feedback":"correct",
"node_ids":["file::auth.py","file::middleware.py","file::session_pool.py"]
}}}
// Performance agent found different useful results
{"method":"tools/call","params":{"name":"learn","arguments":{
"query":"connection pool bottleneck",
"agent_id":"analyst-perf",
"feedback":"correct",
"node_ids":["file::worker_pool.py","file::process_manager.py"]
}}}
Over a session with 19 agents, the graph accumulates thousands of learning signals. Each agent benefits from every other agent’s discoveries.
Investigation Handoff
When Agent A finds something that Agent B needs to investigate:
// Agent A saves its investigation
{"method":"tools/call","params":{"name":"trail_save","arguments":{
"agent_id":"hacker-auth",
"label":"session-hijack-vector",
"tags":["security","critical"]
}}}
// Orchestrator merges with Agent B's independent findings
{"method":"tools/call","params":{"name":"trail_merge","arguments":{
"agent_id":"jimi",
"trail_ids":["trail-hacker-001","trail-analyst-002"]
}}}
Best Practices
-
Use consistent agent IDs. The same agent should always use the same ID across sessions. This enables drift detection and trail continuity.
-
Learn after every useful activation. The more feedback the graph gets, the smarter it becomes. Make
learncalls automatic in your agent loop. -
Use locks for overlapping work. If two agents might modify the same code region, lock it first. Lock diffs are essentially free (0.08 microseconds).
-
Save trails at investigation checkpoints. Trails are cheap to save and invaluable for handoff, resume, and post-mortem analysis.
-
Merge trails for synthesis. When multiple agents investigate the same area independently, merge their trails to find convergence and conflicts.
-
Warm up before focused tasks.
warmupprimes the graph for a specific task, boosting relevant regions before the agent starts querying. -
Use
driftat session start. After any period of inactivity, check what changed. This recovers context efficiently.
Frequently Asked Questions
General
What is m1nd?
m1nd is a local MCP runtime for coding agents. It turns repos, docs, history, and runtime-adjacent signals into a graph-backed operational model and exposes that model through MCP for structure, impact, connected context, continuity, audit, document grounding, and edit preparation. Built in Rust, it runs locally and works with any MCP-compatible client.
The current differentiator is not just that the graph learns. The runtime also exposes guidance surfaces such as proof_state, next_suggested_tool, next_suggested_target, and next_step_hint, plus observable progress for long-running writes like apply_batch.
How is m1nd different from grep / ripgrep?
Grep finds text. m1nd finds structure and relationships.
Grep tells you which files contain the word “authentication”. m1nd tells you which modules are structurally connected to authentication, which ones are likely co-changed when auth changes, which hidden dependencies exist between auth and seemingly unrelated modules, and what would break if you removed the auth module.
Grep is fast and essential. m1nd answers questions that grep cannot even formulate.
How is m1nd different from RAG?
RAG (Retrieval-Augmented Generation) embeds code chunks into vectors and retrieves the top-K most similar chunks for each query. Each retrieval is independent – RAG has no memory of previous queries and no understanding of relationships between results.
m1nd maintains a persistent graph where relationships are first-class citizens. The graph learns from feedback, remembers investigations across sessions, and can answer structural questions (“what breaks if I delete X?”, “does A depend on B at runtime?”) that RAG cannot.
RAG is useful for semantic similarity search. m1nd is useful for structural reasoning. They are complementary, not competing.
How is m1nd different from static analysis tools (tree-sitter, ast-grep, Sourcegraph)?
Static analysis tools parse code into ASTs and compute call graphs, type hierarchies, and cross-references. These are accurate but frozen – they represent the code at a single point in time and cannot answer “what if?” questions.
m1nd uses similar structural information as a starting point but adds three things static analysis cannot: (1) Hebbian learning that adapts the graph based on usage, (2) temporal intelligence from co-change history, and (3) simulation engines for hypotheses and counterfactuals.
Sourcegraph is a search engine. m1nd is a reasoning engine.
Do I need an LLM to use m1nd?
No. m1nd makes zero LLM calls. It is pure Rust computation: graph algorithms, spreading activation, Hebbian plasticity. No API keys, no network calls, no token costs.
m1nd is designed to work alongside LLMs (as an MCP tool that agents call), but the graph engine itself is completely self-contained.
What languages does m1nd support?
m1nd currently has:
- native/manual extractors for Python, Rust, TypeScript/JavaScript, Go, and Java
- 22 additional tree-sitter-backed languages across Tier 1 and Tier 2
- a generic fallback extractor for unsupported text files
Language breadth is broad, but semantic depth still varies by language. Python and Rust currently have more specialized handling than many of the tree-sitter-backed languages.
Is m1nd open source?
Yes. MIT license. Source at github.com/maxkle1nz/m1nd.
Installation
What platforms does m1nd support?
Any platform where Rust compiles. Tested on:
- macOS (ARM64 and x86_64)
- Linux (x86_64, ARM64)
- Windows (x86_64)
What is the minimum Rust version?
Rust 1.75 or later. The project uses the 2021 edition.
How large is the binary?
Approximately 8MB for a release build. No runtime dependencies, no shared libraries. The binary is fully self-contained.
Can I install from crates.io?
Yes: cargo install m1nd-mcp
Usage
How many files can m1nd handle?
Tested up to 10,000+ files. At ~2MB of memory for 10,000 nodes, a 100K-file codebase would need roughly 20MB for the graph. This is well within modern machine capacity.
At 400K+ files, the in-memory graph starts to become a consideration (~80MB), but it still works. m1nd was optimized for codebases in the 100 to 50,000 file range.
Does it work with non-code files?
Yes. The current ingestion surface is not code-only.
m1nd can now ingest:
- structured data through
json - text corpora through
memory L1GHTspecs throughlight- native document adapters for patents, articles, BibTeX, CrossRef, and RFCs
- best-effort ordinary documents through
universal
The universal lane can normalize markdown, HTML/wiki pages, office documents, and PDFs into canonical local artifacts and graph-native document structure. That is what powers document_resolve, document_bindings, document_drift, and the document auto_ingest_* runtime.
How much memory does m1nd use?
The graph itself is compact: ~2MB for a 10,000-node graph with 26,000 edges (Compressed Sparse Row format). The MCP server process uses additional memory for the JSON-RPC layer, perspective state, lock baselines, and trail storage. A typical production instance serving a 335-file codebase uses under 50MB total.
Can I ingest multiple codebases into one graph?
Yes. If you already know the repo list, use federate. If the current workspace only contains explicit path evidence to sibling repos, use federate_auto first and let m1nd suggest the namespace plan for you.
Direct federation:
{"method":"tools/call","params":{"name":"federate","arguments":{
"agent_id":"dev",
"repos":[
{"name":"backend","path":"/project/backend"},
{"name":"frontend","path":"/project/frontend"}
]
}}}
Auto-discovery first:
{"method":"tools/call","params":{"name":"federate_auto","arguments":{
"agent_id":"dev",
"scope":"docs",
"execute":false
}}}
Can I do incremental ingests?
Yes. Pass "incremental": true to the ingest tool. Incremental ingest only re-processes files that changed since the last ingest, preserving learned edge weights for unchanged regions.
Architecture
Where is the graph stored?
In memory during runtime. Optionally persisted to JSON files on disk via the M1ND_GRAPH_SOURCE and M1ND_PLASTICITY_STATE environment variables.
Without persistence configured, the graph is lost when the process exits. Always configure persistence for production use.
How often does it persist?
By default, every 50 queries. Also on shutdown. The auto_persist_interval configuration parameter controls this.
Can I export the graph?
The persisted graph state is a JSON file (graph_snapshot.json). You can read, copy, and process it with standard JSON tools. The format includes all nodes, edges, PageRank scores, and metadata.
The plasticity state is a separate JSON file (plasticity_state.json) containing per-edge synaptic records (learned weights).
What is the graph format?
Compressed Sparse Row (CSR) with forward and reverse adjacency lists. Each node has an external ID (e.g., file::auth.py), a type (file, class, function, module), metadata, and a PageRank score. Each edge has a type (imports, calls, inherits, co_change), a base weight, and an optional plasticity weight.
What is XLR noise cancellation?
Borrowed from professional audio engineering. Like a balanced XLR cable, m1nd transmits the activation signal on two inverted channels and subtracts common-mode noise at the receiver. This reduces false positives in activation queries by cancelling out signals that propagate through generic hub nodes (like config.py or utils.py) rather than through meaningful structural paths.
XLR is enabled by default. Pass "xlr": false to activate to disable it for a single query.
MCP Protocol
What MCP clients work with m1nd?
Any client that speaks MCP over stdio. Tested and verified:
- Claude Code
- Cursor
- Windsurf
- Zed
- Cline
- Roo Code
- Continue
- OpenCode
- GitHub Copilot (MCP mode)
- Amazon Q Developer
Can I use m1nd without MCP?
The server speaks JSON-RPC over stdio, which is the MCP transport. You can send raw JSON-RPC from any program that can write to stdin and read from stdout. No MCP client library is required – the protocol is just JSON over pipes.
What MCP protocol version does m1nd implement?
Protocol version 2024-11-05. The server reports this in the initialize response.
Does m1nd support MCP notifications?
Yes. The server silently ignores incoming notifications per the MCP specification.
Performance
How fast is spreading activation?
31-77ms for a 9,767-node graph, returning 15-20 ranked results. The variance depends on query specificity and graph density around the activated region.
How fast is ingest?
910ms for 335 Python files producing 9,767 nodes and 26,557 edges. This includes parsing, reference resolution, edge creation, and PageRank computation.
How fast is learning?
Sub-millisecond. A learn call with feedback on 2-3 nodes adjusts hundreds of edges in under 1ms.
What about 100K files?
Ingest would take roughly 30-45 seconds (linear scaling from the 335-file benchmark). Activation would be 100-200ms. The graph would occupy ~20MB in memory. These are estimates – actual performance depends on code density and language.
What is the lock diff speed?
0.08 microseconds (80 nanoseconds). Lock diff is a constant-time operation – it compares fingerprints, not individual nodes. It is essentially free.
Full benchmark table
All numbers from real execution against a production Python backend (335 files, ~52K lines):
| Operation | Time | Scale |
|---|---|---|
| Full ingest | 910ms | 335 files, 9,767 nodes, 26,557 edges |
| Spreading activation | 31-77ms | 15 results from 9,767 nodes |
| Blast radius (depth=3) | 5-52ms | Up to 4,271 affected nodes |
| Stacktrace analysis | 3.5ms | 5 frames, 4 suspects ranked |
| Plan validation | 10ms | 7 files, 43,152 blast radius |
| Counterfactual cascade | 3ms | Full BFS on 26,557 edges |
| Hypothesis testing | 58ms | 25,015 paths explored |
| Pattern scan (all 8) | 38ms | 335 files, 50 findings per pattern |
| Multi-repo federation | 1.3s | 11,217 nodes, 18,203 cross-repo edges |
| Lock diff | 0.08us | 1,639-node subgraph comparison |
| Trail merge | 1.2ms | 5 hypotheses, 3 conflicts detected |
Plasticity and Learning
How does Hebbian learning work?
“Neurons that fire together wire together.” When you call learn with feedback: "correct" and a list of node IDs, m1nd identifies all edges on paths between those nodes and increases their weights (Long-Term Potentiation, LTP). When you call with feedback: "wrong", it decreases weights on paths leading to the marked nodes (Long-Term Depression, LTD).
The strength parameter (default 0.2) controls how aggressively weights shift. The learning_rate server configuration (default 0.08) provides a global scaling factor.
Can I reset learned weights?
Yes. Delete the plasticity_state.json file and restart the server. Alternatively, re-ingest the codebase, which rebuilds the graph from scratch (but does not clear the plasticity state file – you need to delete it separately or pass "incremental": false and delete the plasticity file).
Does the graph overfit?
Hebbian learning includes homeostatic normalization to prevent runaway weight amplification. Edge weights are bounded and periodically normalized so that heavily-used paths do not completely dominate. The decay_rate parameter (default 0.005) provides a slow decay toward baseline weights over time.
In practice, overfitting requires thousands of feedback signals consistently reinforcing the same narrow paths. Normal usage produces a well-distributed weight landscape.
What is “partial” feedback?
The learn tool accepts three feedback types:
correct– strengthen paths (LTP)wrong– weaken paths (LTD)partial– mixed signal. Applies a mild strengthening (half the LTP strength) to acknowledged nodes while slightly weakening peripheral paths.
Perspectives
What are perspectives?
A perspective is a stateful navigation session through the graph. You start one with a query or anchor node, and m1nd synthesizes a “route surface” – a ranked set of paths you can follow. As you navigate (follow, back, branch), the perspective maintains breadcrumb history and updates the route surface.
Think of it as a browser for the code graph. You have a current “page” (focus node), links (routes), history (back button), and bookmarks (branches).
How many perspectives can be open simultaneously?
There is no hard limit. Each perspective occupies a small amount of memory (proportional to the number of visited nodes). In practice, 10-20 concurrent perspectives per agent is typical. Close perspectives you are done with to free memory.
Do perspectives persist across sessions?
Perspectives are in-memory only and are lost when the server restarts. For persistent investigation state, use the trail system (trail.save / trail.resume).
Can I branch a perspective?
Yes. perspective.branch forks the current navigation state into a new independent perspective. Both the original and the branch have the same history up to the branch point, and diverge afterward. This is useful for exploring “what if I go this way instead?” without losing your current position.
Comparison
m1nd vs tree-sitter
tree-sitter is a parser. It produces ASTs from source code. m1nd uses structural information similar to what tree-sitter extracts, but builds a weighted, learning graph on top of it. tree-sitter tells you what the code is. m1nd tells you what the code means in context, what changed, what might break, and what is missing.
They are complementary. tree-sitter integration is planned for m1nd to expand language support.
m1nd vs ast-grep
ast-grep is a structural search tool – it finds code patterns using AST matching. m1nd does not do pattern matching on syntax trees. Instead, it does graph-level reasoning: spreading activation, hypothesis testing, counterfactual simulation, learning. ast-grep answers “where does this pattern appear?” m1nd answers “what is connected to this, what breaks if it changes, and what am I missing?”
m1nd vs RAG (Retrieval-Augmented Generation)
RAG embeds code into vectors and retrieves similar chunks. Each retrieval is independent and stateless. m1nd maintains a persistent graph with relationships, learning, and investigation state. RAG cannot answer structural questions, simulate deletions, or learn from feedback.
RAG costs LLM tokens per query. m1nd costs zero tokens per query.
m1nd vs Sourcegraph / CodeGraph / SCIP
Sourcegraph provides cross-repository code search and navigation based on SCIP (Source Code Intelligence Protocol) indexing. It produces accurate, language-server-quality code intelligence.
m1nd is not a code search engine. It is a reasoning engine. Sourcegraph tells you “function X is defined here and called here.” m1nd tells you “if you change function X, here are the 4,189 nodes in the cascade, and the graph predicts these 3 files probably need changes too.”
Sourcegraph is a hosted SaaS product. m1nd is a local binary with zero cost per query.
m1nd vs GitHub Copilot context
Copilot uses a mix of embeddings and heuristics to select context files for the LLM. It does not maintain a persistent graph, does not learn from feedback, and does not support structural queries.
m1nd can be used alongside Copilot – through MCP, Copilot can call m1nd tools to get smarter context before generating code.
Contributing
How do I contribute?
See CONTRIBUTING.md. Fork the repo, create a branch, make your changes with tests, and open a PR.
What is the test suite?
cargo test --all
Tests cover the core graph engine, plasticity, spreading activation, hypothesis engine, all MCP tool handlers, and the JSON-RPC protocol layer.
What are the code style requirements?
cargo fmtbefore committingcargo clippy --all -- -D warningsmust pass- No
unsafewithout an explanatory comment - All new code needs tests
What areas need the most help?
- Language extractors: Adding tree-sitter integration or new language-specific extractors
- Graph algorithms: Community detection, better decay functions, embedding-based semantic scoring
- Benchmarks: Running m1nd on diverse codebases and reporting real-world performance numbers
- Documentation: Tutorials, examples, and translations
Where do I report bugs?
GitHub Issues at github.com/maxkle1nz/m1nd/issues. Use the bug label.
Tool Matrix SSOT
Machine-oriented routing matrix for the current m1nd tool surface.
Source Of Truth
m1nd-mcp/src/server.rstool schema registry and the livetools/listresponse are the callable surface. Usetools/listfor the exact count in your current build.- This matrix is the routing SSOT for the canonical tool surface used by agents and LLMs.
- Any page that advertises a fixed tool count can drift. Treat
tools/listas counting truth.
Row Contract
| field | meaning |
|---|---|
command | canonical bare MCP call name returned by tools/list |
category | routing bucket |
core_function | shortest truthful description of what the tool does |
choose_when | positive routing trigger |
avoid_when | disqualifier / better alternative |
required | minimum required args |
returns | stable output surface an LLM can plan around |
next | adjacent tools / likely next hop |
Foundation
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
ingest | Parse a codebase, markdown docs, or JSON domain graph into the semantic graph. | Start or refresh graph truth for a repo, file, docs corpus, or descriptor before higher-order queries. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; path | graph_counts; files_scanned; files_parsed; nodes_created; edges_created; elapsed_ms | activate -> impact -> why |
activate | Spreading activation query — fires signal into the graph and returns a 4D-scored activation pattern. | Use when the query is associative or subsystem-shaped and you want connected structure, not exact text. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; query | seeds; activated; ghost_edges; structural_holes; plasticity; elapsed_ms | activate -> impact -> why |
impact | Blast radius of a code change — BFS-propagated signal strength from a source node. | Use before edits to estimate blast radius. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; node_id | blast_radius; total_energy; causal_chains | activate -> impact -> why |
why | Shortest path between two nodes — understand how A depends on B. | Use to explain how two nodes are connected. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; source; target | path; hops; edge_types; path_weight | activate -> impact -> why |
learn | Hebbian feedback — tell the graph which results were correct or wrong. Edge weights strengthen or weaken accordingly. | Hebbian feedback — tell the graph which results were correct or wrong. Edge weights strengthen or weaken accordingly. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; query; feedback; node_ids | edges_modified; trust_records_updated; tremor_observations_recorded | activate -> impact -> why |
drift | What changed in the graph since your last session — structural delta for context recovery. | What changed in the graph since your last session — structural delta for context recovery. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id | baseline; changed_nodes; weight_drift; summary | activate -> impact -> why |
health | Server diagnostics — verify the MCP server is alive and the graph is loaded. | Server diagnostics — verify the MCP server is alive and the graph is loaded. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id | node_count; edge_count; sessions; graph_state | activate -> impact -> why |
seek | Find code by natural language intent — more targeted than activate for specific lookups. | Use when you know the purpose of code but not its location. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; query | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
scan | Run structural pattern scanners. Use a predefined pattern ID or a custom pattern string. | Run structural pattern scanners. Use a predefined pattern ID or a custom pattern string. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; pattern | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
timeline | Temporal evolution of a node — change history, co-change partners, velocity, stability. | Temporal evolution of a node — change history, co-change partners, velocity, stability. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; node | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
diverge | Structural drift analysis — compare graph state against a baseline reference. | Structural drift analysis — compare graph state against a baseline reference. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; baseline | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
warmup | Prime the graph for an upcoming task — pre-activates seed nodes to improve subsequent query relevance. | Prime the graph for an upcoming task — pre-activates seed nodes to improve subsequent query relevance. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; task_description | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
federate | Unify multiple repositories into one graph — cross-repo blast radius and dependency analysis. | Use when you already know the repo list to unify. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id; repos | tool-specific JSON payload; inspect API reference / tools/list | activate -> impact -> why |
federate_auto | Discover candidate sibling repositories from explicit external path references, local manifest/workspace hints, import/package-name evidence, shared API-route signals, or basic contract artifacts and optionally execute federation in one step. | Use when you suspect neighboring repos matter but only have path/import/contract evidence. | Avoid when you already have the exact file/line or only need raw compiler/runtime truth. | agent_id | discovered_repos; suggested_repos; executed; federate_result | activate -> impact -> why |
Document Intelligence
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
document_resolve | resolve canonical local artifacts for a universally ingested document | Use when a doc already exists in the graph and you need the local canonical artifact paths. | Avoid when you only need a fresh ingest or raw source text. | agent_id | canonical_markdown_path; canonical_json_path; claims_path; producer; binding_count; drift_summary | document_bindings -> document_drift -> view |
document_provider_health | report optional provider availability, mode, detail, and install hints | Use before relying on richer HTML/PDF/office extraction or when a provider lane seems to be falling back unexpectedly. | Avoid when provider setup is irrelevant to the current task. | agent_id | python; providers[] | ingest -> document_resolve -> auto_ingest_start |
document_bindings | show deterministic document-to-code bindings for a universal document | Use when the question is “which code implements this doc?” or “what should I inspect first?” | Avoid when you only need the artifact paths or provider status. | agent_id | bindings[]; source_path | document_drift -> impact -> surgical_context_v2 |
document_drift | detect stale, missing, or ambiguous document/code links | Use after refactors, repo moves, or suspected stale specs. | Avoid before the document has been ingested or bound. | agent_id | findings[]; summary | document_bindings -> impact -> timeline |
auto_ingest_start | start local-first document watchers for supported roots and formats | Use when a docs/specs/wiki root should stay synchronized with the graph while you work. | Avoid for one-shot ingest or read-only inspection. | agent_id; roots | running; provider_status; bootstrap | auto_ingest_status -> auto_ingest_tick -> document_resolve |
auto_ingest_status | inspect the document auto-ingest runtime, semantic counts, provider status, and route/fallback counts | Use to monitor watcher state, queue depth, or document runtime telemetry. | Avoid if you only need a direct document refresh or binding result. | agent_id | running; queue_depth; semantic_*; provider_*; recent_events | auto_ingest_tick -> document_drift -> document_provider_health |
auto_ingest_tick | drain queued document changes immediately and apply them to the graph | Use when a caller needs deterministic immediate reconciliation instead of waiting for opportunistic ticks. | Avoid when no watched roots are active. | agent_id | ingested_paths; removed_paths; skipped_paths; errored_paths | document_resolve -> document_drift -> lock_diff |
auto_ingest_stop | stop document watchers and persist manifest state | Use when shutting down or changing watched roots. | Avoid during active watch-driven reconciliation you still need. | agent_id | stopped; manifest_entries | auto_ingest_start -> auto_ingest_status |
Perspective Navigation
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
perspective_start | Open a perspective anchored to a node. Perspectives are stateful navigation sessions. | Open a perspective anchored to a node. Perspectives are stateful navigation sessions. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; query | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_routes | List available navigation routes from the current focus node. | List available navigation routes from the current focus node. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_follow | Move the perspective focus to a route target. | Move the perspective focus to a route target. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_back | Navigate backward to the previous focus node. | Navigate backward to the previous focus node. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_peek | Read source code at the currently focused node (returns file content at the node’s location). | Read source code at the currently focused node (returns file content at the node’s location). | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_inspect | Deep metadata + 5-factor score breakdown for a route’s target node. | Deep metadata + 5-factor score breakdown for a route’s target node. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_suggest | AI navigation recommendation — the graph suggests which route to follow next. | AI navigation recommendation — the graph suggests which route to follow next. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_affinity | Check route relevance to the current investigation. | Check route relevance to the current investigation. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_branch | Fork an independent perspective copy for parallel exploration. Two agents can investigate the same starting point independently, then compare findings. | Fork an independent perspective copy for parallel exploration. Two agents can investigate the same starting point independently, then compare findings. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_compare | Diff two perspectives — shared nodes, unique nodes, divergent findings. | Diff two perspectives — shared nodes, unique nodes, divergent findings. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id; perspective_id_a; perspective_id_b | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_list | All active perspectives for this agent, with memory usage. | All active perspectives for this agent, with memory usage. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
perspective_close | Release perspective state and free memory. | Release perspective state and free memory. | Avoid for stateless one-shot lookups; prefer seek/search/view when you do not need navigation state. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | perspective_routes -> perspective_follow -> perspective_inspect |
Lock System
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
lock_create | Snapshot a subgraph region. | Snapshot a subgraph region. | Avoid for solo read-only work; use only when coordination or baseline diffs matter. | agent_id; scope; root_nodes | tool-specific JSON payload; inspect API reference / tools/list | lock_watch -> lock_diff -> lock_rebase |
lock_watch | Register a change strategy on a lock. | Register a change strategy on a lock. | Avoid for solo read-only work; use only when coordination or baseline diffs matter. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | lock_watch -> lock_diff -> lock_rebase |
lock_diff | Compare current graph state against the locked baseline. Returns changes since the lock was created. | Compare current graph state against the locked baseline. Returns changes since the lock was created. | Avoid for solo read-only work; use only when coordination or baseline diffs matter. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | lock_watch -> lock_diff -> lock_rebase |
lock_rebase | Advance the lock baseline to the current graph state (after reviewing and accepting changes). | Advance the lock baseline to the current graph state (after reviewing and accepting changes). | Avoid for solo read-only work; use only when coordination or baseline diffs matter. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | lock_watch -> lock_diff -> lock_rebase |
lock_release | Free lock state and release memory. | Free lock state and release memory. | Avoid for solo read-only work; use only when coordination or baseline diffs matter. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | lock_watch -> lock_diff -> lock_rebase |
Superpowers
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
hypothesize | Test a claim against graph structure using Bayesian path scoring. 89% accuracy validated on 10 live claims against a production codebase. | Test a claim against graph structure using Bayesian path scoring. 89% accuracy validated on 10 live claims against a production codebase. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; claim | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
counterfactual | Simulate module removal — compute full cascade of what breaks. | Simulate module removal — compute full cascade of what breaks. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; node_ids | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
missing | Find structural holes — what should exist in a region of the graph but doesn’t. | Find structural holes — what should exist in a region of the graph but doesn’t. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; query | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
resonate | Standing wave analysis — find structural hubs where signal reinforces across multiple dimensions simultaneously. | Standing wave analysis — find structural hubs where signal reinforces across multiple dimensions simultaneously. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
fingerprint | Find structural twins — nodes with identical or near-identical topology. | Find structural twins — nodes with identical or near-identical topology. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
trace | Map a stacktrace to root cause suspects — ranks files by suspiciousness × centrality. | Map a stacktrace to root cause suspects — ranks files by suspiciousness × centrality. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; error_text | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
validate_plan | Pre-flight risk assessment for a set of planned changes — blast radius, gap count, risk score. | Use before editing or merging to pre-flight risk and test impact. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; actions | risk_score; gaps; suggested_additions; heuristic_summary | hypothesize -> validate_plan -> trace |
predict | Co-change prediction — given a file you just changed, which other files likely need changes too. | Co-change prediction — given a file you just changed, which other files likely need changes too. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; changed_node | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
trail_save | Persist investigation state — hypotheses, conclusions, open questions, visited nodes, and activation boosts. | Persist investigation state — hypotheses, conclusions, open questions, visited nodes, and activation boosts. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; label | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
trail_resume | Restore exact investigation context — nodes, weights, hypotheses — from a saved trail. | Restore exact investigation context — nodes, weights, hypotheses — from a saved trail. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; trail_id | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
trail_merge | Combine two or more agents’ independent investigations. Auto-detects where they converged and flags conflicts on shared hypotheses. | Combine two or more agents’ independent investigations. Auto-detects where they converged and flags conflicts on shared hypotheses. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; trail_ids | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
trail_list | Browse all saved investigations with optional filters. | Browse all saved investigations with optional filters. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
differential | Focused structural diff between two graph snapshots — surface what changed structurally between two points in time. | Focused structural diff between two graph snapshots — surface what changed structurally between two points in time. | Avoid for plain text lookup or trivial file reads; these are structural reasoning tools. | agent_id; snapshot_a; snapshot_b | tool-specific JSON payload; inspect API reference / tools/list | hypothesize -> validate_plan -> trace |
Superpowers Extended
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
antibody_scan | Scan the graph against all stored bug antibody patterns (known bug shapes). Returns matches with confidence and bindings. | Scan the graph against all stored bug antibody patterns (known bug shapes). Returns matches with confidence and bindings. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
antibody_list | List all stored antibodies with match history. | List all stored antibodies with match history. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
antibody_create | Create, disable, enable, or delete an antibody pattern. | Create, disable, enable, or delete an antibody pattern. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
flow_simulate | Concurrent execution flow simulation — particles travel the graph in parallel, turbulence points are shared mutable state collisions (race conditions). | Concurrent execution flow simulation — particles travel the graph in parallel, turbulence points are shared mutable state collisions (race conditions). | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
epidemic | SIR epidemiological bug propagation — given known-buggy modules, predict which neighbors are most likely to harbor undiscovered bugs. | SIR epidemiological bug propagation — given known-buggy modules, predict which neighbors are most likely to harbor undiscovered bugs. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id; infected_nodes | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
tremor | Change frequency acceleration detection — identifies modules with accelerating change frequency. Acceleration precedes bugs, not just high churn. | Change frequency acceleration detection — identifies modules with accelerating change frequency. Acceleration precedes bugs, not just high churn. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
trust | Per-module actuarial trust scores from defect history. More confirmed bugs = lower trust = higher risk weighting in activation queries. | Per-module actuarial trust scores from defect history. More confirmed bugs = lower trust = higher risk weighting in activation queries. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
layers | Auto-detect architectural layers from graph topology. Uses BFS longest-path depth assignment + Tarjan SCC for circular groups. Reports dependency violations. | Auto-detect architectural layers from graph topology. Uses BFS longest-path depth assignment + Tarjan SCC for circular groups. Reports dependency violations. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
layer_inspect | Inspect a specific architectural layer: nodes, inter-layer connections, health metrics. | Inspect a specific architectural layer: nodes, inter-layer connections, health metrics. | Avoid before you have a scoped suspect area; these are deeper risk/simulation tools. | agent_id; level | tool-specific JSON payload; inspect API reference / tools/list | trust -> tremor -> heuristics_surface |
RETROBUILDER
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
ghost_edges | temporal co-change ghost edges from git history | temporal co-change ghost edges from git history | Avoid without git/runtime/security context; use only when that extra plane matters. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | runtime_overlay -> trace -> impact |
taint_trace | taint propagation over graph structure | taint propagation over graph structure | Avoid without git/runtime/security context; use only when that extra plane matters. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | runtime_overlay -> trace -> impact |
twins | structural equivalence / near-equivalence discovery | structural equivalence / near-equivalence discovery | Avoid without git/runtime/security context; use only when that extra plane matters. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | runtime_overlay -> trace -> impact |
refactor_plan | graph-native refactoring proposals | graph-native refactoring proposals | Avoid without git/runtime/security context; use only when that extra plane matters. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | runtime_overlay -> trace -> impact |
runtime_overlay | runtime heat and error overlays from OTel spans | runtime heat and error overlays from OTel spans | Avoid without git/runtime/security context; use only when that extra plane matters. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | runtime_overlay -> trace -> impact |
Surgical
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
heuristics_surface | Explain why a node/file ranked as risky or important. | Explain why a node/file ranked as risky or important. | Avoid before locating the target surface; use seek/search/impact first. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
surgical_context | Return complete context for a file in one call: full source, symbol table, callers, callees, and test coverage neighbours. Use before apply for single-file edits. | Use before single-file edits. | Avoid before locating the target surface; use seek/search/impact first. | agent_id; file_path | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
apply | apply now returns proactive_insights when a write lands on a risky or historically unstable surface. | Use to write one file and keep the graph coherent. | Avoid before locating the target surface; use seek/search/impact first. | agent_id; file_path; new_content | updated_node_ids; proactive_insights; elapsed_ms | heuristics_surface -> batch_view -> apply_batch |
view | Fast file reader with line numbers and optional auto-ingest. | Use when you already know the file and need bounded line-numbered reading. | Avoid before locating the target surface; use seek/search/impact first. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
surgical_context_v2 | Superset of surgical_context — returns the target file’s full contents AND source excerpts of all connected files (callers, callees, tests) in one call. Eliminates the need to read multiple files separately. Use before apply_batch when editing a file and its callers/tests together. | Use before multi-file edits where connected source context matters. | Avoid before locating the target surface; use seek/search/impact first. | agent_id; file_path | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
apply_batch | apply_batch now returns proactive_insights, and the final batch_completed progress event carries the same payload for streaming clients. | Use to write multiple files atomically and optionally verify. | Avoid before locating the target surface; use seek/search/impact first. | agent_id; edits | results; verification; proactive_insights; progress_events | heuristics_surface -> batch_view -> apply_batch |
edit_preview | Preview a write without touching disk. | Preview a write without touching disk. | Avoid before locating the target surface; use seek/search/impact first. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
edit_commit | Commit a previewed edit after confirmation and freshness check. | Commit a previewed edit after confirmation and freshness check. | Avoid before locating the target surface; use seek/search/impact first. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | heuristics_surface -> batch_view -> apply_batch |
Search & Efficiency
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
search | Unified literal/regex/semantic search — graph-aware grep replacement. Searches both node labels in the graph and file contents on disk, returning results with line context. | Use for exact text, regex, or semantic grep across graph + file content. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id; query | results; total_matches; proof_state; next_suggested_tool | search/glob -> batch_view -> coverage_session |
glob | Graph-aware file globbing over indexed files. | Use when you need files by path pattern, not content. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | search/glob -> batch_view -> coverage_session |
help | Self-documenting tool reference with m1nd’s visual identity. Returns a formatted index of all tools or detailed docs for a specific tool — including params, examples, and NEXT suggestions. | Self-documenting tool reference with m1nd’s visual identity. Returns a formatted index of all tools or detailed docs for a specific tool — including params, examples, and NEXT suggestions. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | search/glob -> batch_view -> coverage_session |
panoramic | Full module risk panorama — ranked view of every file-level node by combined risk score. Combines blast radius, centrality, and churn into one sorted list. Critical modules (risk ≥ 0.7) trigger alerts. | Full module risk panorama — ranked view of every file-level node by combined risk score. Combines blast radius, centrality, and churn into one sorted list. Critical modules (risk ≥ 0.7) trigger alerts. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | modules; critical_alerts; summary | search/glob -> batch_view -> coverage_session |
savings | Token economy report — session and global tracking of tokens saved by using m1nd instead of grep/glob, with CO2 estimation. | Token economy report — session and global tracking of tokens saved by using m1nd instead of grep/glob, with CO2 estimation. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | search/glob -> batch_view -> coverage_session |
report | Session summary — query log, timing statistics, tokens saved, and graph state for the calling agent. Provides a markdown-formatted report ready for display or logging. | Session summary — query log, timing statistics, tokens saved, and graph state for the calling agent. Provides a markdown-formatted report ready for display or logging. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | recent_queries; savings; hotspot_summary | search/glob -> batch_view -> coverage_session |
metrics | Return structural metrics per file/function/module. | Use when you want ranked structural measures, not semantic retrieval. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | entries; summary | search/glob -> batch_view -> coverage_session |
type_trace | Trace where a type/struct/enum is used across the graph. | Use when following a type across files and callsites. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | usages; file_groups | search/glob -> batch_view -> coverage_session |
diagram | Generate Mermaid or DOT graph diagrams. | Use when you need a visual graph slice for humans or downstream agents. | Avoid for write/plan validation; these help orientation and summary, not mutation safety. | agent_id | format; diagram; node_count | search/glob -> batch_view -> coverage_session |
Audit & Session
| command | core_function | choose_when | avoid_when | required | returns | next |
|---|---|---|---|---|---|---|
batch_view | multi-file read surface with stable delimiters and summaries | multi-file read surface with stable delimiters and summaries | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
scan_all | run all structural patterns in one call | run all structural patterns in one call | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
cross_verify | graph vs disk verification (existence, loc, hash) | graph vs disk verification (existence, loc, hash) | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
coverage_session | what this agent has visited so far | what this agent has visited so far | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
external_references | explicit paths outside ingest roots | explicit paths outside ingest roots | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
audit | profile-aware one-call audit | profile-aware one-call audit | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
daemon_start | activate the persisted daemon control plane and set watch roots / poll interval | activate the persisted daemon control plane and set watch roots / poll interval | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
daemon_stop | stop the daemon control plane without discarding alert history | stop the daemon control plane without discarding alert history | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | tool-specific JSON payload; inspect API reference / tools/list | audit -> batch_view -> cross_verify |
daemon_status | inspect daemon runtime state, watch roots, and alert counts | Use to inspect daemon liveness, tracked files, and recent tick metrics. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | active; tracked_files; tick_count; last_tick_duration_ms; last_tick_changed_files; last_tick_deleted_files; last_tick_alerts_emitted | audit -> batch_view -> cross_verify |
daemon_tick | poll watched roots once, incrementally re-ingest changed files, and emit deletion drift alerts | Use when you want one explicit delta-processing pass over watched roots. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | changed_files_detected; deleted_files_detected; files_reingested; alerts_emitted | audit -> batch_view -> cross_verify |
alerts_list | list persisted daemon/proactive alerts | Use to review durable daemon/proactive alerts. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | alerts; total; active | audit -> batch_view -> cross_verify |
alerts_ack | acknowledge one or more persisted daemon/proactive alerts | Use after reviewing alerts so they do not keep resurfacing. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | acked; requested; acked_at_ms | audit -> batch_view -> cross_verify |
persist | Force graph and sidecar state persistence to disk. | Use when you want to force-save graph and sidecars now. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | status; snapshot_path; plasticity_path | audit -> batch_view -> cross_verify |
boot_memory | Persist small canonical hot-state values next to the graph. | Use for tiny canonical doctrine/state values that should stay hot. | Avoid for tiny one-file questions; these are orchestration and session-shaping tools. | agent_id | action; key; value; entries | audit -> batch_view -> cross_verify |
Routing Rules
- Prefer the cheapest tool that preserves structural truth.
- Use
search/glob/viewfor exact known targets; useseek/activatewhen the question is semantic or subsystem-shaped. - Use
impact,predict,validate_plan, and the surgical tools before risky edits. - Use
audit,cross_verify,coverage_session,batch_view, and daemon tools to manage long-running sessions. - Use perspective and lock tools only when stateful navigation or multi-agent coordination is worth the overhead.
Benchmarks
For the raw research artifacts and patch backlog that produced the current public numbers, see:
docs/benchmarks/BENCHMARK_RESEARCH_2026-03-24.mddocs/benchmarks/BENCHMARK_PATCH_PLAN_2026-03-24.mddocs/benchmarks/BENCHMARK_HARNESS_SPEC_2026-03-24.md
This page is the short product-truth layer for the current benchmark corpus.
What We Measure Now
The current benchmark system is not only about token proxy. It also tracks whether m1nd improves workflow behavior:
- token proxy / context churn
false_starts- guided follow-through
- recovery loops
- proof-state progression
- progress observability on long-running writes
That matters because some of m1nd’s strongest wins are continuity, repair, and execution clarity rather than raw compression in every single scenario.
Current Warm-Graph Corpus
The current recorded aggregate warm-graph corpus shows:
| Metric | Manual | warm | Result |
|---|---|---|---|
| Aggregate token proxy | 10518 | 5182 | 50.73% reduction |
| False starts | 14 | 0 | m1nd eliminates the recorded false starts |
| Guided follow-throughs | 0 | 31 | guided next-step behavior is being followed in real runs |
| Successful recovery loops | 0 | 12 | repair loops are closing instead of restarting from scratch |
These are the public numbers reflected in the README and landing. They are the benchmark truth to mirror across docs.
Representative Engine Timings
The underlying engine remains fast on the measured production backend (~335 files, ~52K lines, 9,767 nodes, 26,557 edges):
| Operation | Time | Notes |
|---|---|---|
| Full ingest | ~910ms | Walk + extract + resolve + finalize |
| Activate query | ~31ms | Four-dimensional ranking |
| Impact analysis | ~5ms | Blast-radius path |
| Trace analysis | ~3.5ms | Stacktrace to suspects |
| Trail resume | ~0.2ms | Continuity restore + hints |
| Apply batch | ~165ms | Atomic multi-file write before deeper verification |
These timings are useful, but they are no longer the whole story. Current m1nd is also measured on guided behavior and recovery quality.
Where m1nd Wins
m1nd wins most clearly when the task is structural, stateful, or risky:
- stacktrace triage with
trace - blast-radius analysis with
impact - continuity restoration with
trail_resume - edit preparation with
surgical_context_v2andvalidate_plan - long-running writes with
apply_batch - repair loops after invalid regex, stale route sets, stale trails, protected writes, and stale edit previews
Where Plain Tools Still Win
m1nd is not the headline tool for:
- exact text search
- one-file lookup when you already know the file
- compiler truth
- runtime logs and debugger work
Use rg, the compiler, the test runner, and logs when execution truth is the question. Use m1nd when navigation and connected structure are the bottleneck.
Why The Corpus Matters
The benchmark corpus is now part of product development, not just a marketing appendix.
Recent runtime and UX improvements were driven directly by measured benchmark pain:
proof_stateand next-step guidance on core flows- more actionable
trail_resume - better
seekhandling for natural-language prompts - reduced
validate_plannoise - more useful
surgical_context_v2 - observable
apply_batchprogress and SSE handoff - recovery-oriented error payloads for invalid or stale tool calls
Reproducibility
To inspect the current benchmark system:
git clone https://github.com/maxkle1nz/m1nd.git
cd m1nd
cargo build --release --workspace
python3 scripts/benchmark/run_benchmark.py --help
python3 scripts/benchmark/summarize_benchmarks.py --help
The versioned scenarios, events, and run outputs live under docs/benchmarks/.
Memory
| Component | Size |
|---|---|
| Graph (9,767 nodes, 26,557 edges) | ~2MB |
| Plasticity state | ~500KB |
| Perspective state (per active perspective) | ~100KB |
| Lock baselines (per lock) | ~200KB |
| Trail storage (per saved trail) | ~50KB |
| JSON-RPC server overhead | ~5MB |
| Typical total | ~50MB |
Memory scales linearly with graph size. A 100K-node graph would use approximately 20MB for the graph alone, with similar overhead for the server.
CPU
m1nd is single-threaded for graph operations (no lock contention, deterministic results). Ingest uses Rayon for parallel file parsing. During query serving, CPU usage is negligible between queries and spikes briefly during activation (31-77ms of computation).
On an Apple M2, the server at idle uses <0.1% CPU. During a burst of queries, it peaks at ~5% of a single core.
Scaling Characteristics
Ingest Time vs Codebase Size
Ingest scales linearly with file count. Reference resolution is roughly O(n log n) where n is the number of cross-file references.
| Files | Estimated Ingest Time | Estimated Nodes |
|---|---|---|
| 100 | ~270ms | ~3,000 |
| 335 | 910ms (measured) | 9,767 (measured) |
| 1,000 | ~2.7s | ~29,000 |
| 10,000 | ~27s | ~290,000 |
| 100,000 | ~4.5min | ~2,900,000 |
Activation Time vs Graph Size
Spreading activation is bounded by the number of edges traversed, which depends on graph density and query specificity rather than total graph size. Activation in a 100K-node graph is estimated at 100-200ms.
Persistence Time vs State Size
JSON serialization scales linearly with state size. A 10K-node graph persists in under 100ms. A 100K-node graph would take approximately 1 second.
Reproducibility
To reproduce these benchmarks:
git clone https://github.com/maxkle1nz/m1nd.git
cd m1nd
cargo build --release
# Start the server
./target/release/m1nd-mcp
Then send the JSON-RPC calls from the Examples document against your own codebase. Times will vary based on:
- Hardware (CPU speed, memory bandwidth)
- Codebase size and language
- Graph density (codebases with many cross-references produce denser graphs)
- Plasticity state (learned weights affect activation propagation paths)
Report your benchmarks via GitHub Issues with the benchmark label.
Changelog
All notable changes to m1nd are documented here. This project uses Semantic Versioning.
[Unreleased]
The next release train starts here.
[0.8.0] — 2026-04-10
Added
Daemon control plane + persistent structural alerts
The audit/runtime layer now graduates from one-shot inspection into a persisted daemon-era control plane:
daemon_startdaemon_stopdaemon_statusdaemon_tickalerts_listalerts_ack
These tools keep daemon state and a small proactive alert queue alive under the runtime root, so structural warnings can survive past the exact write or ingest that produced them.
The daemon control plane also gained the operational behavior needed to make it useful in live agent sessions:
- opportunistic auto-ticks between ordinary tool calls
- daemon ticks during idle server time
- scheduler timing exposure in
daemon_status - tick metrics exposure in
daemon_status - adaptive backoff when watch activity is low
- native filesystem watcher wakeups
- burst coalescing before reconciliation
- Git-aware changed-set reconciliation when watched roots are repositories
- SCM-aware daemon baselines instead of a moving cursor model
Proactive structural insights on writes
apply and apply_batch now attach proactive_insights directly to write results instead of forcing the agent to remember the next structural checks.
Initial insight kinds include:
co_change_predictionuntouched_test_companionantibody_recurrencetrust_droptremor_hotspotcross_repo_contract_riskschema_contract_drift
When the daemon is active, the strongest write-time insights are also promoted into the persisted alert queue so they can be reviewed and acknowledged later.
federate_auto becomes a real evidence-to-federation bridge
federate_auto now turns external evidence into an actionable federation plan instead of just reporting raw hints.
It can:
- scan
external_referencesoutput - lift referenced files to repo roots via
.gitor manifest markers - suggest stable namespace names for the current repo and sibling repos
- optionally execute
federatedirectly in one call
Its discovery surface now includes:
- manifest/workspace evidence such as Cargo workspaces,
package.jsonworkspaces,pnpm-workspace.yaml,pyproject.toml, andgo.work - import/package-name matches against nearby repo identities
- contract artifacts such as
.protodefinitions, MCP tool-name surfaces, and OpenAPI/Swagger routes and schemas - shared
/api/...route evidence between the current workspace and nearby repos - schema and component-name recognition for stronger contract matching
- scope/evidence-strength hardening so the bridge stays conservative
Universal document intelligence in the canonical engine
The universal document lane is now ported into canonical m1nd instead of living only in the integration repo.
This adds:
- canonical local artifact resolution for universal documents
- deterministic document-to-code bindings
- document/code drift detection
- provider health reporting
- local-first document watcher/runtime control
New MCP surfaces:
document_resolvedocument_bindingsdocument_driftdocument_provider_healthauto_ingest_startauto_ingest_statusauto_ingest_tickauto_ingest_stop
The universal lane also now preserves source-byte fidelity and writes a fuller canonical artifact set:
source.<ext>canonical.mdcanonical.jsonclaims.jsonmetadata.json
Optional provider lanes are now surfaced operationally instead of implicitly:
DoclingTrafilaturaMarkItDownGROBID
auto_ingest_status also reports provider route/fallback counts so agents can see whether rich extraction actually happened or whether the runtime fell back.
Changed
The public surface is finally aligned with the live runtime
The docs and public product surfaces now match the real engine instead of the pre-document-runtime story.
- the tool matrix SSOT is now published and wired into the docs flow
- API coverage is complete for the current MCP surface
- GitHub Pages now publishes the real
wiki-buildoutput - the canonical docs wave aligned README, examples, wiki pages, API docs, and the published tool matrix with the universal document runtime
- the GitHub wiki mirror and localized READMEs were synced with the canonical docs
- stale public counts from the old
63/77/78eras were replaced with the live93-tool surface
Document runtime hardening
The universal runtime was tightened in several ways before and after the port:
- post-ingest semantic refresh is now restricted to the universal document lane
- file-root watchers use non-recursive mode when the watched root is a single file
- queue waiting now fails with explicit diagnostics instead of a silent timeout
- false
binding_ambiguouscases were reduced when multiple relations hit the same target
Tool count: 77 → 93.
Fixed
Provider-gated regression coverage for scholarly PDFs
The GROBID lane now has a provider-gated regression path that verifies the runtime resolves to universal:grobid for a minimal generated PDF when the provider environment is configured.
Canonical artifact correctness
- universal content hashes now track original source bytes instead of only the normalized canonical text
- canonical caches preserve reachable original source bytes instead of quietly rewriting everything into plain text
- binding/drift summaries refresh against graph generation instead of reusing stale semantic state
[0.7.0] — 2026-04-05
Added
Audit Mode + Session Foundations
Six new MCP tools reduce orchestration overhead in long structural sessions:
| Tool | What It Does |
|---|---|
batch_view | Read multiple files or glob expansions in one call with stable delimiters, optional summaries, and auto-ingest |
scan_all | Run all structural scan patterns in one call and return grouped findings |
cross_verify | Compare graph state against current disk truth (existence, loc, hash) |
coverage_session | Report which files/nodes the current agent has already visited |
external_references | Discover explicit references to paths outside current ingest roots |
audit | Profile-aware one-call audit for topology, scans, verification, git state, and recommendations |
Related contract upgrades:
healthnow exposes git context (branch,clean,head, recent commits, uncommitted files)ingestnow acceptsinclude_dotfilesanddotfile_patternsview,search,report, andauditnow support inline truncation metadata instead of forcing file-only spill paths
Tool count: 71 → 77.
RETROBUILDER: 5 Advanced Graph Analysis Tools
Five new MCP tools expose the RETROBUILDER core modules (RB-01 through RB-05), adding temporal analysis, security taint propagation, structural duplication detection, refactoring planning, and runtime observability to the tool surface.
| Tool | Module | What It Does |
|---|---|---|
ghost_edges | RB-01: 4D Git Graph | Parse git history and inject temporal co-change ghost edges — hidden coupling between files that always change together but have no static dependency |
taint_trace | RB-02: Graph Fuzzing | Inject taint at entry points, track propagation through the graph, detect missed security boundaries (validation, auth, sanitization) |
twins | RB-03: Structural Twins | Find structurally identical code via topological signature cosine similarity — detects duplicate retry logic, CRUD handlers, state machines |
refactor_plan | RB-04: Intent-Driven Refactoring | Community detection + bridge analysis + counterfactual simulation for safe module extraction planning |
runtime_overlay | RB-05: OTel Overlay | Ingest OpenTelemetry trace data to paint runtime heat (call counts, latency, error rates) onto graph nodes |
New types in protocol/layers.rs: GhostEdgesInput, TaintTraceInput, TwinsInput, RefactorPlanInput, RuntimeOverlayInput, RuntimeOverlaySpan.
Tool count: 63 → 68.
Diagnostic Tools: 3 Structural Observability Tools
Three new MCP tools provide structural observability, type-dependency tracing, and visual graph generation — moving m1nd from a passive graph engine to an active diagnostic platform.
| Tool | What It Does |
|---|---|
metrics | Per-node structural metrics: LOC (with 3-tier fallback: provenance → child span → disk read), child counts (functions, structs, enums, classes), in/out degree, PageRank, density ratio. Supports scope filtering and sorting by LOC, complexity, or name. |
type_trace | Cross-file type usage tracing via BFS from a type/struct/enum node. 4-tier target resolution (exact ID → label exact → segment match → substring) with explicit preference for type-defining nodes over impl blocks. Forward, reverse, and bidirectional tracing with file grouping. |
diagram | Generate visual graph diagrams in Mermaid or DOT format. Centers on a node/query via BFS or shows top-N by PageRank. Supports scope filtering, type filtering, edge label display, PageRank annotation, and layout direction (TD/LR). |
New types in protocol/layers.rs: MetricsInput, MetricsOutput, MetricsEntry, MetricsSummary, TypeTraceInput, TypeTraceOutput, TypeTraceUsage, TypeTraceFileGroup, DiagramInput, DiagramOutput.
Tool count: 68 → 71.
Native OpenClaw fast path
m1nd now includes a native OpenClaw-facing bridge crate and fast path so the project can integrate with that execution fabric without giving up the MCP-first contract.
m1nd-openclawwas added as an auxiliary bridge crate- the native fast path preserves MCP compatibility instead of forking the product
Changed
Public product surfaces were repositioned around the real runtime
The product story was reworked around current agent use, speed, and grounded structural navigation:
- the visual wiki became the primary documentation surface
- the landing/site flow was rebuilt around the product story instead of the old root page
- editor/client integration entrypoints were documented across the major MCP clients
- localized READMEs were refreshed to match the new public story
- README language around limits, scope, and grounded retrieval was clarified
Fixed
CI and release operations were re-stabilized
- fresh rustfmt/clippy regressions on main were resolved
- the required
Teststatus was restored for branch protection - release prep and help/workflow surfaces were aligned before the
v0.7.0cut
[0.6.1] — 2026-03-25
Fixed
Release and Publish Alignment
This patch release aligns the public release surfaces after the v0.6.0 rollout.
- added missing crates.io metadata to workspace crates so publish succeeds cleanly
- added explicit published-version constraints on internal workspace dependencies
- hardened the release workflow so crates.io publish is skipped cleanly when
CARGO_REGISTRY_TOKENis not configured, instead of failing the whole release job
[0.6.0] — 2026-03-25
Added
Guided Proof State Across Core Agent Flows
Several high-value tools now surface proof_state plus explicit handoff guidance so
an agent can tell whether it is still triaging, actively proving, or ready to move
into edit preparation.
seek,trace,impact,timeline,hypothesize,validate_plan, andsurgical_context_v2now participate in a shared proof-state model- guided outputs now include
next_suggested_tool,next_suggested_target, andnext_step_hintacross the main structural triage and edit-prep paths trail_resumenow behaves more like continuity orchestration than bookmark restore, returning compact resume hints, next-focus guidance, and tool-aware follow-up
apply_batch Progress, Correlation, and Handoff Signals
apply_batch has been upgraded from a “wait until the batch finishes” write surface
into an observable execution flow with stable correlation and final handoff data.
- final outputs now expose
batch_idfor correlating progress and final result - progress reporting now includes coarse lifecycle fields such as
active_phase,completed_phase_count,phase_count,remaining_phase_count,progress_pct, andnext_phase phasesnow act as a structured execution timeline acrossvalidate,write,reingest,verify, anddoneprogress_eventsnow provide a streaming-friendly event log for the same lifecycle- live
apply_batch_progressSSE emission now happens during execution in serve mode - replay and live transports now carry consistent batch correlation data
- the final
batch_completedevent now carries the batch’sproof_stateand next-step guidance, so clients do not need to wait for a separate final blob to recover the cognitive handoff
Benchmark Harness Expansion
The benchmark system has been extended so progress UX and workflow guidance can be measured as first-class product behavior, not only token proxy.
- benchmark runs now record
execution_originandsource_ref - long-running flows can now distinguish
live,replay, andsnapshotprogress delivery - the harness now records progress event counts, delivery modes, phase sequences, and guidance-followed behavior
- the
warm_structural_proof_apply_batchscenario now captures live progress delivery explicitly instead of treating progress as an undifferentiated blob
Changed
Help and Docs Are More Agent-Operational
The help surface and public docs now reflect the real working style of current m1nd, with less catalog-style listing and more decision support.
- help entries now include
WHEN TO USE,AVOID WHEN, benchmark-aware guidance, composed workflows, and proof-state handoff cues - help and docs now frame common tool failures as short repair loops, with hint/example/next-step guidance that agents can use to self-correct
- README, examples, and benchmark docs now describe the current guided behavior of
apply_batch,proof_state, and long-running progress updates more accurately - benchmark truth now explicitly includes recovery-loop scenarios such as invalid regex retry, ambiguous scope retry, stale route refresh, and protected-write reroute
- benchmark research now documents progress observability and delivery modes as part of product truth, not only token savings
Notes
- Current benchmark corpus summary shows
10518 -> 5182token proxy on the aggregate warm-graph corpus, for50.73%savings - The same corpus now measures more than token compression:
false_starts, guided follow-through, recovery loops, progress events, and proof-state transitions - Across the recorded corpus,
m1nd_warmreducedfalse_startsfrom14to0, recorded31guided follow-throughs, and recorded12successful recovery loops
[0.5.0] — 2026-03-16
Added
apply_batch 5-Layer Post-Write Verification (verify=true)
When apply_batch is called with verify: true, every write now passes through a
five-layer verification pipeline before the tool reports success. A single VerificationReport
aggregates all layer outcomes and produces a final verdict.
Layer A — Expanded Trivial-Return Detection
Detects files that look syntactically valid but are semantically hollow.
- 30+ trivial-return patterns (empty body, constant return, pass/noop, single-line
no-op closures, stub
unimplemented!()/todo!()bodies) has_real_logic()heuristic: a file passes only when it contains at least one non-trivial expression — assignment, function call, conditional, loop, or match arm with a real body- Pattern set is language-aware; Rust, Python, TypeScript, and Go each have dedicated pattern lists
Layer B — Post-Write Compilation Check
After the file is written to disk, Layer B runs the relevant compiler/checker in a subprocess and captures stdout + stderr.
| Language | Command |
|---|---|
| Rust | cargo check --message-format=short |
| Go | go build ./... |
| Python | python -c "import ast; ast.parse(open('<file>').read())" |
| TypeScript | tsc --noEmit |
- Timeout: 60 seconds per command
- Failures produce a structured
CompileErrorwith command, exit code, and trimmed output - Result surfaced in
ApplyBatchOutput.compile_check
Layer C — BFS Blast Radius via CSR Edges
Uses the in-memory CSR adjacency structure to compute 2-hop reachability from every modified file node.
- Forward + backward BFS to 2 hops
- Deduplicates reachable nodes and maps each back to a file path
- Produces a
Vec<BlastRadiusEntry>— one entry per affected file withdistance(1 or 2) and therelationtype along the path - Surfaced in
ApplyBatchOutput.blast_radius
Layer D — Affected Test Execution
After computing the blast radius, Layer D identifies test files within 2 hops and runs them.
| Language | Command |
|---|---|
| Rust | cargo test <module> |
| Go | go test ./... |
| Python | pytest <file> -x -q |
- Per-test-run timeout: 30 seconds
tests_run,tests_passed,tests_failed, andtest_outputfields added toApplyBatchOutput- Zero test files found = Layer D skipped (not counted as failure)
Layer E — Anti-Pattern Detection
Scans the new file content for patterns that indicate a semantic regression even when the file compiles cleanly.
Detected anti-patterns:
| Pattern | Signal |
|---|---|
todo!() / unimplemented!() inserted | Stub replacing real logic |
.unwrap() added where none existed before | Error handling removed |
panic!() / unreachable!() in non-test code | Crash path introduced |
Empty catch / except block | Silent error swallowing |
| Explicit error handler replaced with no-op | Regression in error handling |
- Comparison is pre-write content vs post-write content (diff-based)
- Each detected anti-pattern produces an
AntiPatternMatchwith location and description
Graph-Diff Verification
apply_batch now snapshots the node set before writing and re-ingests after. The delta
is compared:
- Node set shrinkage — if the post-write graph has fewer nodes than pre-write for the affected files, this is flagged as a potential symbol deletion
- Edge set regression — significant edge count drop triggers a
RISKYsignal - Result stored as a structured
GraphDiffembedded inVerificationReport
New Types
| Type | Location | Purpose |
|---|---|---|
VerificationReport | m1nd-core/src/verify.rs | Top-level verification result: layers A–E + graph-diff + verdict |
VerificationImpact | m1nd-core/src/verify.rs | Aggregated impact summary: compile status, test counts, anti-patterns |
BlastRadiusEntry | m1nd-core/src/verify.rs | Single affected-file record from Layer C BFS |
CompileCheckResult | m1nd-core/src/verify.rs | Structured compile output: command, exit code, stderr |
AntiPatternMatch | m1nd-core/src/verify.rs | Single anti-pattern detection hit with location |
GraphDiff | m1nd-core/src/verify.rs | Pre/post node+edge delta from graph-diff step |
Verdict | m1nd-core/src/verify.rs | SAFE / RISKY / BROKEN — final write verdict |
New Fields in ApplyBatchOutput
| Field | Type | Description |
|---|---|---|
verification | Option<VerificationReport> | Full verification report (present when verify=true) |
compile_check | Option<CompileCheckResult> | Layer B compile result |
tests_run | u32 | Total test cases executed in Layer D |
tests_passed | u32 | Passing test count |
tests_failed | u32 | Failing test count |
test_output | Option<String> | Raw test runner output (trimmed to 2 KB) |
blast_radius | Vec<BlastRadiusEntry> | Layer C 2-hop affected files |
Verdict System
The Verdict enum drives the final apply_batch outcome when verify=true:
| Verdict | Meaning | Condition |
|---|---|---|
SAFE | All layers passed; write accepted | Compiles, tests pass, no anti-patterns, graph stable |
RISKY | Write accepted with warnings | Compile OK, but anti-patterns detected OR graph shrinkage OR some tests failed |
BROKEN | Write rejected; file restored to pre-write content | Compile failure OR Layer A trivial-only content detected |
On BROKEN, the pre-write content is automatically restored and the error is surfaced
in VerificationReport.error.
12/12 Test Accuracy — Exhaustive Hardening
The verification pipeline passed an exhaustive test suite of 12 scenarios designed to cover every combination of layer outcomes:
- Clean write — all layers pass →
SAFE - Compile error — Layer B fails →
BROKEN+ auto-restore - Trivial stub replacement — Layer A triggers →
BROKEN - Anti-pattern insertion — Layer E triggers →
RISKY - Test regression — Layer D fails →
RISKY - Graph node shrinkage — graph-diff triggers →
RISKY - Multi-file batch — blast radius correct across 3 files
- No test files in radius — Layer D skipped cleanly
- Python AST parse failure — Layer B Python path →
BROKEN - TypeScript
tscclean — Layer B TS path →SAFE .unwrap()added where absent — Layer E Rust pattern →RISKY- Empty except block added — Layer E Python pattern →
RISKY
All 12 scenarios produced the expected verdict with correct field population.
Changed
Tool Names: All 61 Tools Use Underscores
dispatch_tool previously reversed dot-notation to underscore normalization selectively.
As of v0.5.0, all 61 tools are registered and dispatched exclusively with underscore
names. The dot-to-underscore reversal in dispatch_tool has been removed.
- MCP tool names:
m1nd_apply_batch,m1nd_surgical_context_v2,m1nd_antibody_scan, etc. - HTTP bridge endpoint paths:
/api/tools/m1nd.apply_batchstill accepted at the HTTP layer for backward compatibility, but the canonical name is underscore throughout - Callers using dot notation in direct MCP calls must update to underscore names
- All 61 tool names documented in
reference_m1nd_all_tools.mdandmcp/m1nd/README.md
Crate Versions Bumped to 0.4.0
All three crates in the workspace have been bumped from 0.3.x to 0.4.0 in Cargo.toml:
| Crate | Previous | New |
|---|---|---|
m1nd-core | 0.3.x | 0.4.0 |
m1nd-ingest | 0.3.x | 0.4.0 |
m1nd-mcp | 0.3.x | 0.4.0 |
The version bump reflects the addition of the verification subsystem, which introduces
new public types (VerificationReport, VerificationImpact, BlastRadiusEntry, etc.)
into the m1nd-core API surface.
[0.2.0] — 2026-03-14
Added
9 New MCP Tools — “Superpowers Extended”
The server now registers 52 tools (up from 43). The 9 additions form a new Superpowers Extended category focused on operational intelligence: bug immunity, execution dynamics, propagation risk, and architectural health.
| Tool | Category | What It Does |
|---|---|---|
m1nd.antibody_scan | Immune Memory | Scan the entire graph against all stored bug antibody patterns |
m1nd.antibody_list | Immune Memory | List stored antibodies with metadata and specificity scores |
m1nd.antibody_create | Immune Memory | Create, disable, enable, or delete antibody patterns |
m1nd.flow_simulate | Execution Dynamics | Particle-based concurrent execution simulation |
m1nd.epidemic | Propagation Risk | SIR model predicting bug spread from known-infected modules |
m1nd.tremor | Change Acceleration | Second-derivative detection of accelerating change frequency |
m1nd.trust | Defect History | Actuarial per-module defect density with Bayesian prior adjustment |
m1nd.layers | Architecture | Automatic layer detection + dependency violation reporting |
m1nd.layer_inspect | Architecture | Layer-specific node, edge, and violation inspection |
Bug Antibodies (m1nd-core/src/antibody.rs)
Immune memory system that learns structural bug patterns from confirmed defects and automatically scans new code for recurrences.
Antibody/AntibodyPattern/AntibodyMatchstructsPatternNodewithmatch_mode: Exact / Substring / Regex label matchingnegative_edgesin patterns — detect structural absence (pattern must NOT have this edge)- DFS graph matching with per-antibody timeout budget (10ms / pattern, 100ms total scan)
extract_antibody_from_learn()— auto-extract patterns fromm1nd.learnfeedbackcompute_specificity()— reject patterns too broad to be useful (MIN_SPECIFICITY=0.15)pattern_similarity()— duplicate detection at registration time (threshold=0.9)- Persistence:
antibodies.jsonalongside graph, atomic write with.bakbackup - Registry capacity: 500 antibodies max
- Severity levels: Critical / High / Medium / Low
Flow Simulation (m1nd-core/src/flow.rs)
Particle-based concurrent execution analysis. Launches simulated particles from entry points and detects where concurrent paths collide.
FlowEnginewith configurableFlowConfig(max_depth, num_particles, turbulence_threshold)TurbulencePoint— race condition hotspot withentry_pairsattribution and path trackingValvePoint— lock/bottleneck detection via label pattern matchingFlowEngine::discover_entry_points()— auto-discover entry nodes from graph structurescope_filter— limit simulation to a subgraph region- Hard caps: MAX_PARTICLES=100, MAX_ACTIVE_PARTICLES=10,000 total steps
M1ndError::NoEntryPointsraised when graph has no identifiable entry points- Turbulence severity: Critical / High / Medium / Low
Epidemic Prediction (m1nd-core/src/epidemic.rs)
SIR (Susceptible-Infected-Recovered) model for predicting how a bug in one module propagates through the dependency graph.
EpidemicEngine/EpidemicConfig/EpidemicResult/EpidemicPredictionEpidemicDirectionenum: Forward / Backward / Both propagation- Per-edge-type transmission coupling factors: imports=0.8, calls=0.7, inherits=0.6, references=0.4, contains=0.3
- Union probability combination across multiple paths to the same node
R0(basic reproduction number) estimate inEpidemicSummaryunreachable_componentscount — modules guaranteed safe from this seed- Burnout detection: auto-calibrates infection rate when >80% of graph would be infected
- Dense graph node promotion via configurable
promotion_threshold EpidemicPersistentStatefor disk persistence across sessions- Hard cap: MAX_ITERATIONS=500; default: 50
M1ndError::EpidemicBurnout— graph too densely connected for meaningful predictionM1ndError::NoValidInfectedNodes— seed nodes not found in graph
Code Tremors (m1nd-core/src/tremor.rs)
Second-derivative acceleration detection on edge weight time series. Like seismic tremors as earthquake precursors — accelerating change frequency predicts instability.
TremorRegistryring buffer (256 observations per node)TremorObservation— timestamped weight delta recorded on everylearncallTremorWindowenum: Days7 / Days30 / Days90 / AllTremorDirectionenum: Accelerating / Decelerating / StableRiskLevelenum: Critical / High / Medium / Low / Unknown- Magnitude formula:
|mean_acceleration| × sqrt(edge_events) - Linear regression slope for trend detection
- Risk classification: Critical = magnitude>5 AND slope>0.5
node_filterparameter to scope analysis to a subgraph- Minimum observation gap: 1 second (dedup interval)
- Persistence:
tremor_state.jsonalongside graph
Module Trust Scores (m1nd-core/src/trust.rs)
Actuarial per-module defect density. Records confirmed bugs, false alarms, and partial matches per node, then computes a time-weighted trust score with Bayesian adjustment.
TrustLedger— defect history storeTrustEntry— per-node defect data with timestampsTrustScorewithTrustTier: HighRisk (<0.4) / MediumRisk (<0.7) / LowRisk (>=0.7)record_defect()/record_false_alarm()/record_partial()— feedback APIcompute_trust()— time-weighted density:base × (FLOOR + (1-FLOOR) × recency)RECENCY_HALF_LIFE_HOURS=720(30-day half-life),RECENCY_FLOOR=0.3adjust_prior()— Bayesian prior update; handles both positive and negative claimsreport()— full trust report withmin_history,tier_filter,sort_byoptionsTrustSortBy: TrustAsc / TrustDesc / DefectsDesc / Recency- Cold-start default: 0.5 (neutral trust until evidence accumulates)
- Persistence:
trust_state.jsonalongside graph
Architectural Layer Detection (m1nd-core/src/layer.rs)
Automatically assigns modules to architectural layers using Tarjan SCC + BFS longest-path depth. Detects upward dependencies, circular dependencies, and skip-layer violations.
LayerDetectorwithLayerDetectionResultArchLayer— detected layer with node membership and health metricsLayerViolationwithViolationType: UpwardDependency / CircularDependency / SkipLayerViolationSeverity: Critical / High / Medium / LowUtilityNodewithUtilityClassification: CrossCutting / Bridge / OrphanLayerHealth— per-layer metrics includinglayer_separation_scoretarjan_scc()— iterative (non-recursive) SCC to avoid stack overflow on deep graphs- BFS longest-path depth assignment algorithm
- Layer merging for sparse layers (min 2 nodes per layer)
- Layer naming strategies: heuristic / path_prefix / pagerank
exclude_testsandnode_type_filterparametersLayerCache— detection results cached against graph generation counter- Hard cap: DEFAULT_MAX_LAYERS=8
M1ndError::LayerNotFoundwhen requested layer index is out of range
Tree-sitter Tier 1 and Tier 2 (22 languages total)
Tree-sitter integration is no longer “planned” — it shipped. The default build
(cargo build --release) includes all 22 languages.
Tier 1 (--features tier1) — 14 languages:
C/H, C++, C#, Ruby, PHP, Swift, Kotlin, Scala, Bash/Shell, Lua, R, HTML, CSS, JSON
Tier 2 (--features tier2, default) — 8 additional languages:
Elixir, Dart, Zig, Haskell, OCaml, TOML, YAML, SQL
TreeSitterExtractor is a universal extractor driven by LanguageConfig structs.
Per-language configs specify function_kinds, class_kinds, name_field,
alt_name_fields, and the name_from_first_child flag for complex AST layouts.
Four-layer name extraction strategy for each definition: (1) name_field child,
(2) alt_name_fields fallback, (3) recursive declarator drill for C/C++,
(4) first named child scan for languages with name_from_first_child=true.
MemoryIngestAdapter (m1nd-ingest/src/memory_adapter.rs)
Turns markdown and plain text files into a queryable graph. Enables using m1nd as an AI agent memory layer.
- Parses
.md,.markdown,.txt(single file or directory walk) - Configurable
namespaceparameter scopes all node IDs (default:"memory") - Section parsing: H1–H6 headings →
Modulenodes taggedmemory:section - Bullet parsing:
- / * / +→Concept/Processnodes - Checkbox parsing:
- [x] / - [ ]→Processnodes taggedmemory:task - Table row parsing:
| col | col |→ nodes from joined cell text - Entry classification by keyword: todo/task → task, decision/decided → decision, mode/state → state, meeting/session → event, default → note
- Canonical source detection:
YYYY-MM-DD.md,memory.md,*-active.md,*-history.md, files containingbriefing→canonical=truein provenance - Cross-reference extraction: file paths in entry text →
Referencenodes withreferencesedges - Code block skipping: fenced blocks are excluded from entry extraction
- File timestamp from filesystem metadata → temporal scoring dimension
- Node ID scheme:
memory::<namespace>::{file,section,entry,reference}::<slug> - Invoked via
m1nd.ingestwithadapter: "memory"
JsonIngestAdapter (m1nd-ingest/src/json_adapter.rs)
Escape hatch for any domain. Describe any graph as JSON and ingest it without writing a custom adapter.
- Accepts a single JSON file:
{"nodes": [...], "edges": [...]} - Node fields:
id(required),label,type(17 supported types),tags - Edge fields:
source,target,relation,weight - Auto-assigned
causal_strengthby relation type containsrelation →EdgeDirection::Bidirectionalauto-promotion- Invoked via
m1nd.ingestwithadapter: "json"
15 Calibration Knobs
New tools expose agent-controllable parameters for tuning behavior without recompilation:
| Tool | Key Parameters |
|---|---|
antibody_scan | match_mode (Exact/Substring/Regex), min_severity |
antibody_create | severity, description, tags |
flow_simulate | num_particles, max_depth, turbulence_threshold, scope_filter |
epidemic | iterations, direction (Forward/Backward/Both), promotion_threshold |
tremor | window (Days7/Days30/Days90/All), node_filter, min_magnitude |
trust | min_history, tier_filter, sort_by, half_life_hours |
layers | exclude_tests, node_type_filter |
HTTP Server + Embedded GUI (--features serve)
Optional feature flag adds an axum HTTP server and embedded React UI.
Build with cargo build --release --features serve.
Modes:
m1nd-mcp --serve— HTTP server + embedded UI on port 1337 (default)m1nd-mcp --serve --stdio— Both transports simultaneously. SSE cross-process bridge: stdio and HTTP share the same graph state. SSE/api/eventsendpoint streams tool results to browser in real time.m1nd-mcp --serve --dev— HTTP with frontend served fromm1nd-ui/dist/on disk (supports Vite HMR during UI development)m1nd-mcp --serve --open— HTTP + auto-open browser on launchm1nd-mcp --serve --stdio --event-log /tmp/e.jsonl— Option A+B: in-process broadcast + append-to-file event log for external consumers
HTTP API endpoints:
GET /api/health— server health: node/edge counts, domain, uptime, query countGET /api/tools— full tool schema list (same as MCPtools/list)POST /api/tools/{tool_name}— invoke any of the 52 tools via REST (30s timeout, FM-C-004)GET /api/graph/stats— node/edge counts, domain, namespacesGET /api/graph/subgraph?query=<q>&top_k=<n>— activate + return subgraph for visualizationGET /api/graph/snapshot— full graph dump (nodes + edges) for external exportGET /api/events— SSE stream of tool results (event_type, data, timestamp_ms)
Cross-process SSE bridge: stdio MCP clients (Claude Code, Cursor) and the browser UI can share
the same graph state via event log (--event-log) and watch (--watch-events). Each tool call
from either transport is broadcast to all SSE subscribers.
Body limit: 1MB per tool call (FM-A-004). Request timeout: 30s (FM-C-004). CORS: permissive
(disable in production). Binding to 0.0.0.0 emits a network exposure warning.
Other Additions
DomainConfigmulti-domain system —code,music,memory,genericpresets, each with different temporal decay half-lives and co-change behaviorGraphBuilderfluent API for programmatic graph construction in m1nd-coreM1ND_DOMAINenv var anddomainconfig file field- Config file via CLI arg:
./m1nd-mcp config.json(first argument, JSON) - MCP instructions injection on
initialize— 73-line workflow guide injected into the MCP handshake response so clients automatically understand usage patterns
Fixed
- Epidemic burnout on dense graphs: auto-calibrate infection rate when >80% saturation rather than hard-failing
- Antibody
match_modenow propagated correctly through recursive DFS subgraph matching - Flow simulation enforces
max_depthandmax_total_stepshard caps independently (previously max_depth could be bypassed by particle branching) - Tool dispatch normalization (underscore ↔ dot) now applies uniformly to all 52 tools including the 9 new ones; previously new tools required exact dot notation
- Lock
watchstrategy validation rejects"periodic"withM1ndError::WatchStrategyNotSupportedinstead of silently accepting and never firing lock.diffcorrectly drains watcher event queue before computing delta- Peek security allowlist enforced for all perspective branches, not only the root perspective (previously branched perspectives bypassed the ingest-scope check)
GraphDiffincremental mode countsRemoveNode/RemoveEdgeactions in stats even though CSR does not physically remove them (clarified behavior, no silent drop)
Changed
- README tool count updated from 43 to 52
- Default build now includes Tier 2 tree-sitter languages (
default = ["tier2"]) SNAPSHOT_VERSIONbumped to 3;load_graph()performs version migration on older filesresonateoutput now includes all 5 fields:harmonics,sympathetic_pairs,resonant_frequencies,wave_pattern,harmonic_groupscounterfactualoutput includessynergy_factorwhen >1 node removed, andreachability_before/reachability_aftermetrics- Ingest response includes
commit_groupsinIngestStats(was populated but not surfaced in the JSON response)
[0.1.0] — initial release
Foundation release: 43 MCP tools across Foundation (13), Perspective Navigation (12), Lock System (5), and Superpowers (13) categories. Hebbian plasticity, spreading activation, XLR noise cancellation, trail system, hypothesis engine, counterfactual engine. Native extractors for Python, Rust, TypeScript/JavaScript, Go, Java.