Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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_state on the main structural flows
  • next_suggested_tool, next_suggested_target, and next_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:

  1. grep for a symbol or phrase
  2. open a file
  3. grep for callers, callees, or related paths
  4. open more files
  5. 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:

  • trace maps stacktraces to likely suspects
  • impact inspects blast radius before edits
  • seek and activate find intent and connected structure
  • document_resolve, document_bindings, and document_drift connect docs/specs to likely code targets and surface stale links
  • document_provider_health and auto_ingest_* expose the local-first document runtime
  • validate_plan and surgical_context_v2 prepare safer multi-file changes
  • trail_resume restores investigations with next-focus and next-tool hints
  • apply_batch exposes 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 -> 5182 aggregate token proxy
  • 50.73% aggregate reduction
  • 14 -> 0 false starts
  • 39 guided follow-throughs
  • 12 successful 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_state
  • next_suggested_tool
  • next_suggested_target
  • next_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_message
  • proof_state
  • lifecycle phases such as validate, write, reingest, verify, and done
  • 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"
CrateRoleKey Dependency
m1nd-coreGraph engine, activation, plasticity, XLR, resonance, temporal, semanticparking_lot, smallvec, static_assertions
m1nd-ingestFile walking, language-specific extraction, reference resolution, diffrayon, walkdir, regex
m1nd-mcpJSON-RPC transport, tool dispatch, session management, persistencetokio, serde_json
m1nd-openclawAuxiliary OpenClaw bridge and transport integrationm1nd-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

  1. Transport: JSON-RPC message arrives on stdin (either Content-Length framed or raw line JSON).
  2. Dispatch: McpServer.serve() parses the JSON-RPC request, matches the tool name, extracts parameters.
  3. Session: The tool handler acquires a read lock on SharedGraph (Arc<parking_lot::RwLock<Graph>>).
  4. Seed Finding: SeedFinder locates matching nodes via a 5-level cascade: exact label, prefix, substring, tag, fuzzy trigram.
  5. Activation: HybridEngine auto-selects heap or wavefront strategy based on seed ratio and average degree.
  6. Dimensions: Four dimensions run: Structural (BFS/heap propagation), Semantic (trigram TF-IDF + co-occurrence PPMI), Temporal (decay + velocity), Causal (forward/backward with discount).
  7. 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).
  8. XLR: If enabled, AdaptiveXlrEngine runs spectral noise cancellation with dual hot/cold pulses and sigmoid gating.
  9. Plasticity: PlasticityEngine.update() runs the 5-step Hebbian cycle on co-activated edges.
  10. 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:

  1. Cognitive state: tools can expose proof_state such as triaging, proving, or ready_to_edit
  2. Next-step guidance: tools can return next_suggested_tool, next_suggested_target, and next_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:

  1. ingest code
  2. ingest or auto-ingest document roots through the universal lane
  3. resolve canonical document artifacts
  4. bind documents to likely code
  5. 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:

  1. engine/runtime timings
  2. 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:

OperationTimeNotes
Full ingest~910msWalk + parallel extract + resolve + finalize (CSR + PageRank)
Activate query~31ms4-dimension with XLR, top-20 results
Impact analysis~5msBFS blast radius, 3-hop default
Trace analysis~3.5msStacktrace to ranked suspects
Trail resume~0.2msRestore continuity and next-step hints
Apply batch~165msAtomic multi-file write before deeper verification

Workflow benchmark truth lives on the Benchmarks page. The current recorded warm-graph corpus shows:

  • 10518 -> 5182 aggregate token proxy
  • 50.73% aggregate reduction
  • 14 -> 0 false starts
  • 39 guided follow-throughs
  • 12 successful 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 the max_nodes config (default: 500K).
  • Edge weights: AtomicU32 CAS with 64-retry limit. Under high contention (>32 concurrent plasticity updates on the same edge), CAS may exhaust retries and return CasRetryExhausted.
  • 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:

TypeInvariantUse
FiniteF32Never NaN or InfAll activation scores, edge weights, scores
PosF32Strictly positive, finiteWavelength, 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:

TypeWrapsPurpose
NodeId(u32)Node indexIndex into NodeStorage parallel arrays
EdgeIdx(u32)Edge indexIndex into CSR parallel arrays
InternedStr(u32)String handleOpaque index into StringInterner
CommunityId(u32)CommunityLouvain community membership
Generation(u64)Mutation counterPlasticity 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 BloomFilter for 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:

  1. Seed nodes initialized with their scores (capped at saturation_cap).
  2. For each depth (up to max_depth=5, hard cap 20):
    • Each frontier node src with activation above threshold=0.04 fires.
    • 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).
  3. 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:

  1. For each activated node, compute weighted sum: score = sum(dim_score * DIMENSION_WEIGHTS[dim]).
  2. If a dimension produced no results, redistribute its weight proportionally to active dimensions.
  3. Apply resonance bonus: 1.5x if all 4 dimensions contributed, 1.3x if 3 contributed.
  4. Sort by final score, truncate to top_k.

Seed Finding

SeedFinder resolves query strings to graph nodes via a 5-level matching cascade:

  1. Exact label match (highest priority)
  2. Prefix match (e.g., “chat_” matches “chat_handler”)
  3. Substring match (e.g., “handler” matches “chat_handler”)
  4. Tag match (e.g., “#api” tag)
  5. 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):

ParameterValuePurpose
DEFAULT_LEARNING_RATE0.08Hebbian weight change rate
DEFAULT_DECAY_RATE0.005Inactive synapse decay per query
LTP_THRESHOLD5Consecutive strengthens before permanent bonus
LTD_THRESHOLD5Consecutive weakens before permanent penalty
LTP_BONUS0.15Permanent weight increase
LTD_PENALTY0.15Permanent weight decrease
HOMEOSTATIC_CEILING5.0Max sum of incoming weights per node
WEIGHT_FLOOR0.05Minimum edge weight (prevents extinction)
WEIGHT_CAP3.0Maximum 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:

ParameterValuePurpose
F_HOT1.0Hot signal frequency
F_COLD3.7Cold signal frequency
SPECTRAL_BANDWIDTH0.8Gaussian kernel bandwidth
IMMUNITY_HOPS2BFS immunity radius
SIGMOID_STEEPNESS6.0Gating function steepness

6-step pipeline:

  1. Anti-seed selection: Pick nodes dissimilar to seeds (Jaccard similarity < 0.2, degree ratio filter).
  2. Immunity computation: BFS 2 hops from seeds. Immune nodes cannot be suppressed.
  3. Hot propagation: Spread SpectralPulse with frequency F_HOT=1.0 from seed nodes. Pulses carry amplitude, phase, frequency, and a bounded recent path ([NodeId; 3], not unbounded Vec – FM-RES-007).
  4. Cold propagation: Spread from anti-seeds with frequency F_COLD=3.7.
  5. 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]).
  6. 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

ModulePurpose
lib.rsIngestor pipeline, IngestAdapter trait, config, stats
walker.rsDirectoryWalker, binary detection, git enrichment
extract/mod.rsExtractor trait, comment stripping, CommentSyntax
extract/python.rsPython: classes, functions, decorators, imports
extract/typescript.rsTypeScript/JS: classes, functions, interfaces, imports
extract/rust_lang.rsRust: structs, enums, impls, traits, functions, mods
extract/go.rsGo: structs, interfaces, functions, packages
extract/java.rsJava: classes, interfaces, methods, packages
extract/generic.rsFallback: file-level node with tag extraction
resolve.rsReferenceResolver, proximity disambiguation
cargo_workspace.rsCargo workspace/crate/dependency enrichment for Rust repos
cross_file.rsPython-weighted cross-file enrichment (imports, tests, registers)
diff.rsGraphDiff for incremental updates
json_adapter.rsGeneric JSON-to-graph adapter
memory_adapter.rsMarkdown/memory document adapter
canonical.rsCanonical document substrate used by the universal lane
merge.rsGraph merge utilities
patent_adapter.rsUSPTO/EPO patent XML ingestion
jats_adapter.rsPubMed/JATS scientific article XML ingestion
bibtex_adapter.rsBibTeX bibliography file ingestion
rfc_adapter.rsIETF RFC XML v3 ingestion
crossref_adapter.rsCrossRef API JSON (DOI metadata) ingestion
document_router.rsAuto-detect document format and route to correct adapter
universal_adapter.rsBest-effort document canonicalization, provider routing, and graphification
cross_domain.rsCross-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 CoChangeMatrix after 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:

ExtensionExtractorExtracted Entities
.py, .pyiPythonExtractorClasses, functions, decorators, imports, global assignments
.ts, .tsx, .js, .jsx, .mjs, .cjsTypeScriptExtractorClasses, functions, interfaces, type aliases, imports, exports
.rsRustExtractorStructs, enums, traits, impls, functions, modules, macros
.goGoExtractorStructs, interfaces, functions, methods, packages
.javaJavaExtractorClasses, interfaces, methods, fields, packages
everything elseGenericExtractorFile-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:

  1. Look up the file timestamp from git enrichment (or filesystem mtime).
  2. Compute change_frequency from git commit count: (commits / 50).clamp(0.1, 1.0). Default 0.3 for non-git repos.
  3. Call graph.add_node() with the external ID, label, node type, tags, timestamp, and change frequency.
  4. Set provenance: source_path, line_start, line_end, namespace="code".
  5. On DuplicateNode error, increment collision counter and continue.

Edge Creation

For each ExtractedEdge (skipping ref:: targets, which are deferred):

  1. Resolve source and target IDs to NodeId.
  2. Assign causal strength by relation type:
RelationCausal StrengthDirection
contains0.8Bidirectional
implements0.7Bidirectional
imports0.6Forward
calls0.5Forward
references0.3Forward
other0.4Forward

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:

ProximityScoreCondition
Same file100Source and target share the same file:: prefix
Same directory50Source and target share the same directory
Same project10Default (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 contains edges
  • crate -> crate depends_on edges 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:

  • imports
  • tests
  • registers

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:

  1. Sort edges by source: All pending edges are sorted by source.0 (node index).
  2. Build forward CSR: Compute offsets array, pack targets/weights/relations/etc into parallel arrays.
  3. Expand bidirectional edges: For each bidirectional edge (A, B), ensure both A->B and B->A exist in the CSR.
  4. Build reverse CSR: Sort edges by target, build rev_offsets, rev_sources, rev_edge_idx (mapping back to forward array indices).
  5. Rebuild plasticity arrays: Allocate PlasticityNode for each node with default ceiling.
  6. 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:

ScenarioStrategy
Single file changedIncremental diff (fast, ~10ms)
Many files changed (>20%)Full re-ingest (cleaner CSR, correct PageRank)
New codebaseFull ingest
Plasticity state importantFull 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:

ClassificationPatternExample
TaskContains “TODO”, “FIXME”, “pending”, “implement”“- TODO: add tests”
DecisionContains “decision:”, “decided:”, “chose”“- Decision: use CSR format”
StateContains “status:”, “state:”, “current:”“- Status: in progress”
EventContains date pattern (YYYY-MM-DD)“- 2026-03-12: deployed”
NoteDefault“- Config lives in settings.py”

Edges

The adapter creates:

  • contains edges from section to child entries.
  • references edges from entries to file reference nodes.
  • follows edges 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:

AdapterDomainInputMCP adapter=
Ingestor"code"Source code directoriescode
MemoryIngestAdapter"memory"Markdown/text documentsmemory
JsonIngestAdapter"generic"Arbitrary JSON with nodes[] and edges[]json
PatentIngestAdapter"patent"USPTO/EPO patent XMLpatent
JatsArticleAdapter"article"PubMed NLM / JATS Z39.96 XMLarticle
BibTexAdapter"bibtex"BibTeX bibliography filesbibtex, bib
RfcAdapter"rfc"IETF RFC XML v3rfc
CrossRefAdapter"crossref"CrossRef API JSON (DOI metadata)crossref, doi
L1ghtIngestAdapter"light"L1GHT protocol Markdownlight

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:

  1. detect document family
  2. normalize into a CanonicalDocument
  3. graphify sections, blocks, tables, citations, entities, and claims

Optional providers can enrich the lane when available:

  • Trafilatura for HTML/wiki/article extraction
  • Docling for office and broad document canonicalization
  • MarkItDown as a lightweight fallback lane
  • GROBID for 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 MethodFormatHeuristic
Extension .bib / .bibtexBibTeXExtension only
Extension .md + Protocol: L1GHTL1GHTContent check
Extension .md without L1GHT, .txt, .rst, .adoc, .html, .pdf, .docx, .pptx, .xlsxUniversalExtension + universal lane
Extension .xml / .nxmlPatent, JATS, or RFCRoot element inspection
Extension .jsonCrossRefChecks for DOI + publisher + type keys
FallbackCodeDefault 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

BridgeWeightSourceDescription
same_as1.0DOI/PMIDSame identifier in different domains → identity edge
cross_cites0.95Citation edgesCitation target exists as a full node in another domain
same_orcid0.95ORCID tagsSame researcher ORCID across different domains
same_author0.7Author nameSame author name across different namespaces
shared_keyword0.6Keyword tagsShared keyword:, article:keyword:, or subject: tags
citation_chain0.5Citation adjacencyTransitive 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

ModulePurpose
main.rsBinary entry point, config loading, tokio runtime, SIGINT handling
server.rsMcpServer, JSON-RPC transport (framed + line), tool schema registry
session.rsSessionState, engine lifecycle, auto-persist, perspective/lock management
tools.rsTool handler implementations for the exported MCP surface
auto_ingest.rsDocument watcher runtime, persisted manifest, queue/tick orchestration
universal_docs.rsCanonical document artifacts, provider health, resolve/bindings/drift surfaces
engine_ops.rsRead-only engine wrappers for perspective synthesis
protocol/auto_ingest.rsRequest/response types for document and auto-ingest tools
perspective/Perspective branching, lock state, watcher events
layer_handlers.rsLayer-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

  1. load_config(): Resolve config from CLI args, env vars, or defaults.
  2. McpServer::new(config): Load graph snapshot from disk (or create empty graph). Load plasticity state. Initialize SessionState with all engines.
  3. server.start(): Prepare the server for serving (no-op currently, reserved for future setup).
  4. tokio::task::spawn_blocking(server.serve()): The serve loop does synchronous stdio I/O in a blocking task.
  5. tokio::select! waits for either SIGINT (ctrl_c()) or serve loop completion.

Shutdown

On SIGINT or stdin EOF:

  1. server.shutdown(): Final persist of graph and plasticity state.
  2. 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 purpose
  • inputSchema: JSON Schema with properties, 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_resolve
  • document_provider_health
  • document_bindings
  • document_drift
  • auto_ingest_start
  • auto_ingest_stop
  • auto_ingest_status
  • auto_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:

ToolPurposeKey Parameters
activateSpreading activation queryquery, top_k, dimensions, xlr
impactBlast radius analysisnode_id, direction (forward/reverse/both)
missingStructural hole detectionquery, min_sibling_activation
whyPath explanation between nodessource, target, max_hops
warmupTask-based primingtask_description, boost_strength
counterfactualNode removal simulationnode_ids, include_cascade
predictCo-change predictionchanged_node, top_k, include_velocity
fingerprintEquivalence detectiontarget_node
driftWeight changes since baselinesince
learnHebbian feedbackfeedback (correct/wrong)
resonateStanding wave analysisquery, frequencies, num_harmonics
seekSeed-level node lookupquery
scanFull graph summary(none)

Graph Mutation Tools:

ToolPurpose
ingestIngest codebase into graph
healthServer diagnostics
timelineTemporal event timeline

Perspective Tools:

ToolPurpose
perspective_startOpen a named perspective branch
perspective_closeClose a perspective
perspective_listList open perspectives for an agent
perspective_inspectView perspective state and cached results
perspective_compareDiff two perspectives
perspective_branchFork a perspective
perspective_suggestGenerate suggestions from perspective context
perspective_backUndo last perspective operation
perspective_peekRead source file content from within perspective
perspective_followFollow links from perspective results
perspective_routesView cached activation routes
perspective_affinityCross-perspective affinity analysis

Lock Tools:

ToolPurpose
lock_createCreate a baseline snapshot for change tracking
lock_diffDiff current state against lock baseline
lock_rebaseUpdate lock baseline to current state
lock_releaseRelease a lock
lock_watchWatch for changes against lock baseline

Trail Tools:

ToolPurpose
trail_saveSave current exploration trail
trail_listList saved trails
trail_resumeResume a saved trail
trail_mergeMerge trails

Topology Tools:

ToolPurpose
federateCross-graph federation
divergeDivergence analysis between graph regions
differentialDifferential activation (compare two queries)
hypothesizeGenerate hypotheses from graph structure
validate_planValidate 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:

  1. Writer starvation prevention: parking_lot uses a fair queue, so plasticity writes do not starve behind continuous read queries.
  2. 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:

  1. Graph first: save_graph() writes the CSR graph to JSON via atomic temp-file-then-rename.
  2. Plasticity second: export_state() extracts per-edge SynapticState, then save_plasticity_state() writes to JSON.
  3. If graph save fails, plasticity save is skipped (prevents inconsistent state).
  4. 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:

CounterBumped ByPurpose
graph_generationIngest, rebuild_enginesDetects stale engine indexes
plasticity_generationLearnDetects stale plasticity state
cache_generationmax(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_roots allow-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&lt;parking_lot::RwLock&lt;Graph&gt;&gt;"]
    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:

StrategyRelevance scoreExample
Exact label match1.0“auth” matches node “auth”
Prefix match0.9“auth” matches “auth_handler”
Substring match0.8“auth” matches “pre_auth_check”
Tag match0.85“auth” matches node tagged “authentication”
Fuzzy trigram0.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.495
  • user_model.py: 0.9 * 1.0 * 0.55 = 0.495
  • database.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.py imports auth.py, so routes.py receives: 0.9 * 1.0 * 0.55 = 0.495
  • main.py imports auth.py, so main.py receives: 0.9 * 1.0 * 0.55 = 0.495
  • middleware.py imports session.py, so middleware.py receives: 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:

NodeD1D2D3D4DimsBonusFinal
auth.py0.900.880.700.6541.5x0.88
session.py0.850.400.500.4541.5x0.64
user_model.py0.500.300.100.2041.5x0.40
middleware.py0.470.150.600.3041.5x0.39
database.py0.500.050.200.1031.3x0.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:

  1. 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.”

  2. Seed bigrams: pairs of seeds that co-occur across multiple queries are tracked. This supports the warmup tool, 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:

  1. 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).
  2. The strengthen counters increment, moving edges closer to the LTP threshold.
  3. 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:

  1. The edges connecting those nodes receive decay as if they were inactive, even though they were activated.
  2. The weaken counters increment, moving edges closer to the LTD threshold.
  3. 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:

  1. Short-term adaptation (within a session): edges on frequently queried paths strengthen immediately. The next query about the same topic converges faster.

  2. Long-term memory (across sessions): edges that cross the LTP threshold receive a permanent bonus. Persistent investigation patterns are encoded in the graph structure.

  3. 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.

  4. 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

ParameterDefaultPurpose
DEFAULT_LEARNING_RATE0.08Hebbian delta_w scaling
DEFAULT_DECAY_RATE0.005Per-query inactive edge decay
LTP_THRESHOLD5Consecutive strengthens for long-term bonus
LTD_THRESHOLD5Consecutive weakens for long-term penalty
LTP_BONUS0.15One-time weight bonus at LTP threshold
LTD_PENALTY0.15One-time weight penalty at LTD threshold
HOMEOSTATIC_CEILING5.0Max total incoming weight per node
WEIGHT_FLOOR0.05Minimum edge weight (never decays below)
WEIGHT_CAP3.0Maximum edge weight (never strengthens above)
DEFAULT_MEMORY_CAPACITY1000Ring buffer size for query memory
CAS_RETRY_LIMIT64Atomic 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+ files
  • logger.py – called from every module
  • database.py – the persistence layer for everything
  • utils.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”

RankNodeActivationRelevant?
1config.py0.82No – imports everywhere
2payment_handler.py0.78Yes
3database.py0.75No – generic persistence
4logger.py0.72No – called from everything
5billing.py0.70Yes
6utils.py0.68No – shared utilities
7invoice.py0.65Yes
8middleware.py0.63No – request pipeline
9refund.py0.60Yes
10routes.py0.58No – 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.

RankNodeHotColdDifferentialRelevant?
1payment_handler.py0.780.050.73Yes
2billing.py0.700.080.62Yes
3invoice.py0.650.060.59Yes
4refund.py0.600.040.56Yes
5payment_models.py0.550.030.52Yes
6stripe_adapter.py0.500.020.48Yes
7config.py0.820.790.03No (cancelled)
8database.py0.750.710.04No (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

ParameterValuePurpose
F_HOT1.0Hot channel frequency
F_COLD3.7Cold channel frequency
SPECTRAL_BANDWIDTH0.8Gaussian kernel width for overlap
IMMUNITY_HOPS2BFS depth for seed immunity
SIGMOID_STEEPNESS6.0Sharpness of activation gate
SPECTRAL_BUCKETS20Resolution of frequency overlap
DENSITY_FLOOR0.3Minimum density modulation
DENSITY_CAP2.0Maximum density modulation
INHIBITORY_COLD_ATTENUATION0.5Cold signal reduction at inhibitory edges
Default anti-seeds3Number of cold-channel origins
Default pulse budget50,000Total 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:

  1. Finds seed nodes matching the query text.
  2. Runs full four-dimension spreading activation (structural, semantic, temporal, causal).
  3. Analyzes the activation pattern for structural holes using the neighborhood algorithm.
  4. 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.

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.py being 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 typeWhat it meansAction
Missing test fileA module has no tests while its siblings doWrite tests or mark as intentionally untested
Missing error handlingA module does not use the error patterns its siblings useAdd error handling or document why it is unnecessary
Missing validationAn endpoint lacks input validation that peer endpoints haveAdd validation – likely a security gap
Missing importA module does not import a shared utility that all siblings importCheck if the module implements the functionality differently
Missing documentationA module has no doc-edges while siblings doWrite documentation or accept the gap
Intentional isolationA module is deliberately decoupled from a clusterNo 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_state
  • next_suggested_tool
  • next_suggested_target
  • next_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

ToolDescription
activateSpreading activation query across the graph
warmupTask-based warmup and priming
resonateResonance analysis: harmonics, sympathetic pairs, and resonant frequencies

Analysis

ToolDescription
impactImpact radius / blast analysis for a node
predictCo-change prediction for a modified node
counterfactualWhat-if node removal simulation
fingerprintActivation fingerprint and equivalence detection
hypothesizeGraph-based hypothesis testing against structural claims
differentialFocused structural diff between two graph snapshots
divergeStructural drift between a baseline and current graph state

Memory, Trails, and Learning

ToolDescription
learnExplicit feedback-based edge adjustment
driftWeight and structural drift analysis
whyPath explanation between two nodes
trail_savePersist current investigation state
trail_resumeRestore a saved investigation with next-step guidance
trail_listList saved investigation trails
trail_mergeCombine two or more investigation trails

Exploration

ToolDescription
seekIntent-aware semantic code search
scanPattern-aware structural code analysis
missingDetect structural holes and missing connections
traceMap runtime errors to structural root causes
timelineGit-based temporal history for a node
federateMulti-repository federated graph ingestion
federate_autoDiscover repo candidates from external path evidence and optionally federate them

Perspectives

ToolDescription
perspective_startEnter a perspective: navigable route surface from a query
perspective_routesBrowse the current route set with pagination
perspective_inspectExpand a route with metrics, provenance, and affinity
perspective_peekExtract a code/doc slice from a route target
perspective_followFollow a route: move focus to target, synthesize new routes
perspective_suggestGet the next best move suggestion
perspective_affinityDiscover probable connections a route target might have
perspective_branchFork navigation state into a new branch
perspective_backNavigate back to previous focus
perspective_compareCompare two perspectives on shared/unique nodes
perspective_listList all perspectives for an agent
perspective_closeClose a perspective and release associated locks

Lifecycle, Search, and Surgical

ToolDescription
ingestIngest or re-ingest a codebase, descriptor, or memory corpus
document_resolveResolve canonical local artifacts for a universal document
document_provider_healthReport optional document provider availability and install hints
document_bindingsShow deterministic document-to-code bindings
document_driftDetect stale, missing, or ambiguous document/code links
auto_ingest_startStart local-first document auto-ingest watchers
auto_ingest_statusInspect the document auto-ingest runtime and counters
auto_ingest_tickDrain queued document changes immediately
auto_ingest_stopStop document watchers and persist manifest state
healthServer health and statistics
searchLiteral, regex, or semantic-graph-aware content search
globGraph-aware file globbing
viewFast line-numbered file inspection
validate_planValidate a modification plan against the code graph
surgical_context_v2Pull connected edit context with proof-oriented options
apply_batchAtomic 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:

ParameterTypeRequiredDescription
agent_idstringYesIdentifier 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

CodeMeaning
-32700Parse error – invalid JSON
-32601Method not found – unknown JSON-RPC method
-32603Internal 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:

  • activate is 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

ParameterTypeRequiredDefaultDescription
querystringYesSearch query for spreading activation. Matched against node labels, tags, and provenance to find seed nodes.
agent_idstringYesCalling agent identifier.
top_kintegerNo20Number of top activated nodes to return.
dimensionsstring[]No["structural", "semantic", "temporal", "causal"]Activation dimensions to include. Each dimension contributes independently to the final activation score. Values: "structural", "semantic", "temporal", "causal".
xlrbooleanNotrueEnable XLR noise cancellation. Filters low-confidence activations to reduce false positives.
include_ghost_edgesbooleanNotrueInclude ghost edge detection. Ghost edges are probable but unconfirmed connections inferred from activation patterns.
include_structural_holesbooleanNofalseInclude 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_holes to 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.

  • warmup – activate + prime for a specific task
  • seek – intent-aware search (finds code by purpose, not just keywords)
  • perspective_start – wraps activate into a navigable perspective
  • learn – 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

ParameterTypeRequiredDefaultDescription
task_descriptionstringYesDescription of the task to warm up for. Natural language.
agent_idstringYesCalling agent identifier.
boost_strengthnumberNo0.15Priming 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, and why queries 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.

  • activate – raw activation query without the priming boost
  • trail_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

ParameterTypeRequiredDefaultDescription
querystringNoSearch query to find seed nodes for resonance analysis. Provide either query or node_id (or neither for global resonance).
node_idstringNoSpecific node identifier to use as seed. Alternative to query.
agent_idstringYesCalling agent identifier.
top_kintegerNo20Number 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.

  • activate – simpler spreading activation without harmonic analysis
  • fingerprint – finds structurally equivalent nodes
  • missing – 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

ParameterTypeRequiredDefaultDescription
node_idstringYesTarget node identifier. Can be an external_id (file::backend/config.py) or a node label.
agent_idstringYesCalling agent identifier.
directionstringNo"forward"Propagation direction. Values: "forward" (what does this affect?), "reverse" (what affects this?), "both".
include_causal_chainsbooleanNotrueInclude 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
  • predict – predicts which files will co-change (more actionable than blast radius)
  • counterfactual – simulates deletion rather than change
  • validate_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

ParameterTypeRequiredDefaultDescription
changed_nodestringYesNode identifier that was changed.
agent_idstringYesCalling agent identifier.
top_kintegerNo10Number of top predictions to return.
include_velocitybooleanNotrueInclude 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
  • impact – blast radius (broader, less actionable)
  • timeline – detailed co-change history
  • validate_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

ParameterTypeRequiredDefaultDescription
node_idsstring[]YesNode identifiers to simulate removal of.
agent_idstringYesCalling agent identifier.
include_cascadebooleanNotrueInclude 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
  • 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

ParameterTypeRequiredDefaultDescription
target_nodestringNoTarget node to find equivalents for. If omitted, performs global fingerprinting.
agent_idstringYesCalling agent identifier.
similarity_thresholdnumberNo0.85Cosine similarity threshold for equivalence. Range: 0.0 to 1.0. Lower values find more (but weaker) matches.
probe_queriesstring[]NoOptional 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
  • 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

ParameterTypeRequiredDefaultDescription
claimstringYesNatural 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_idstringYesCalling agent identifier.
max_hopsintegerNo5Maximum BFS hops for evidence search.
include_ghost_edgesbooleanNotrueInclude ghost edges as weak evidence. Ghost edges count as lower-weight supporting evidence.
include_partial_flowbooleanNotrueInclude partial flow when full path not found. Shows how far the search reached.
path_budgetintegerNo1000Budget 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

VerdictConfidence RangeMeaning
"likely_true"> 0.8Strong structural evidence supports the claim
"likely_false"< 0.2Strong structural evidence contradicts the claim
"inconclusive"0.2 – 0.8Evidence 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”)
  • why – finds the path between two specific nodes
  • impact – measures downstream impact rather than testing a claim
  • scan – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
snapshot_astringYesPath to snapshot A, or "current" for the in-memory graph.
snapshot_bstringYesPath to snapshot B, or "current" for the in-memory graph.
questionstringNoFocus filter question. Narrows the diff output. Examples: "what new coupling was introduced?", "what modules became isolated?".
focus_nodesstring[]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
  • diverge – higher-level drift analysis with anomaly detection
  • lock_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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
baselinestringYesBaseline reference. Values: ISO date ("2026-03-01"), git ref (SHA or tag), or "last_session" to use the saved GraphFingerprint.
scopestringNoFile path glob to limit scope. Example: "backend/stormender*". None = all nodes.
include_coupling_changesbooleanNotrueInclude coupling matrix delta between communities.
include_anomaliesbooleanNotrueDetect 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

TypeDescription
test_deficitNew or modified file with no corresponding test file
velocity_spikeUnusually high churn rate
new_couplingPreviously independent modules are now coupled
isolationModule that was connected became isolated

When to Use

  • Session startdrift shows weight-level changes; diverge shows structural-level changes
  • Sprint retrospective – how much did the architecture change this sprint?
  • Quality gate – flag files with test deficits before merging
  • drift – weight-level drift (lighter, faster)
  • differential – lower-level snapshot diff
  • timeline – 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

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoOptional path prefix.
depthstringNoGit history window such as 7d, 30d, 90d, or all.
top_kintegerNoMaximum ghost edges to return.

When to Use

  • To find hidden temporal coupling
  • Before refactors in churn-heavy areas

taint_trace

Inject taint at entry points and trace propagation through the graph to expose missed validation, auth, or sanitization boundaries.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
entry_nodesstring[]YesEntry points to taint.
taint_typestringNouser_input, sensitive_data, or custom.
max_depthintegerNoMaximum propagation depth.

When to Use

  • Security reviews on trust boundaries
  • Input validation and auth flow audits

twins

Find structurally similar or identical nodes by topology signature.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoOptional path prefix.
node_typesstring[]NoRestrict to node families.
similarity_thresholdnumberNoMinimum similarity threshold.

When to Use

  • Duplicate logic detection
  • Consolidation/refactor candidate discovery

refactor_plan

Graph-native refactoring proposals: community detection and extraction candidates for a scoped region.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoOptional path prefix.
max_communitiesintegerNoUpper bound on candidate communities.
min_community_sizeintegerNoSmallest extractable group to report.

When to Use

  • Before modularization/extraction work
  • To identify communities with high internal cohesion

runtime_overlay

Overlay OpenTelemetry span activity onto the graph to paint runtime heat, latency, and error signals onto nodes.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
spansobject[]YesOTel-like spans to ingest.
service_namestringNoOptional service scope.
mapping_strategystringNoMapping 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

metrics

Return structural metrics per file, function, class, struct, or module.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoOptional path prefix.
node_typesstring[]NoRestrict output to certain node types.
sortstringNoSort 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

type_trace

Trace where a type, struct, or enum is used across the graph.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
targetstringYesType name or external_id to trace.
directionstringNoforward, reverse, or both.
group_by_filebooleanNoGroup usage sites by file.

When to Use

  • Following type spread before edits
  • Understanding where a central data model is consumed

diagram

Generate Mermaid or DOT graph diagrams from a query or node-centered slice.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
centerstringNoSeed query or node_id to center the diagram.
formatstringNomermaid or dot.
directionstringNoTD or LR.
max_nodesintegerNoMaximum nodes to include.

When to Use

  • Human-readable architecture explanations
  • Sharing graph context with another agent or reviewer

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

ParameterTypeRequiredDefaultDescription
querystringYesThe original query this feedback relates to. Must match the query used in the activation.
agent_idstringYesCalling agent identifier.
feedbackstringYesFeedback type. Values: "correct" (strengthen edges), "wrong" (weaken edges), "partial" (strengthen confirmed nodes only).
node_idsstring[]YesNode identifiers to apply feedback to. For "correct", these are the relevant results. For "wrong", these are the irrelevant ones.
strengthnumberNo0.2Feedback 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.

  • activate – the query tool whose results you are providing feedback on
  • drift – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
sincestringNo"last_session"Baseline reference point. Values: "last_session" (saved state from previous session), or a timestamp.
include_weight_driftbooleanNotrueInclude 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 health to recover context
  • After ingest – see what the new ingest changed
  • After extended learning – track cumulative drift from feedback
  • diverge – higher-level structural drift with anomaly detection
  • health – 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

ParameterTypeRequiredDefaultDescription
sourcestringYesSource node identifier.
targetstringYesTarget node identifier.
agent_idstringYesCalling agent identifier.
max_hopsintegerNo6Maximum 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
  • 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
labelstringYesHuman-readable label for this investigation.
hypothesesobject[]No[]Hypotheses formed during investigation. Each object has: statement (string, required), confidence (number, default 0.5), supporting_nodes (string[]), contradicting_nodes (string[]).
conclusionsobject[]No[]Conclusions reached. Each object has: statement (string, required), confidence (number, default 0.5), from_hypotheses (string[]), supporting_nodes (string[]).
open_questionsstring[]No[]Open questions remaining for future investigation.
tagsstring[]No[]Tags for organization and search.
summarystringNoOptional summary. Auto-generated if omitted.
visited_nodesobject[]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_boostsobjectNo{}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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
trail_idstringYesTrail ID to resume (from trail_save or trail_list).
forcebooleanNofalseResume 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
  • trail_save – save a trail to resume later
  • warmup – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
filter_agent_idstringNoFilter to a specific agent’s trails. None = all agents.
filter_statusstringNoFilter by status: "active", "saved", "archived", "stale", "merged".
filter_tagsstring[]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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
trail_idsstring[]YesTwo or more trail IDs to merge.
labelstringNoLabel 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

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

ParameterTypeRequiredDefaultDescription
querystringYesNatural language description of what the agent is looking for. Example: "code that validates user credentials".
agent_idstringYesCalling agent identifier.
top_kintegerNo20Maximum results to return.
scopestringNoFile path prefix to limit search scope. Example: "backend/". None = entire graph.
node_typesstring[]No[]Filter by node type: "function", "class", "struct", "module", "file". Empty = all types.
min_scorenumberNo0.1Minimum combined score threshold. Range: 0.0 to 1.0.
graph_rerankbooleanNotrueWhether 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
  • 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

ParameterTypeRequiredDefaultDescription
patternstringYesPattern ID or custom pattern string. Built-in patterns: "error_handling", "resource_cleanup", "api_surface", "state_mutation", "concurrency", "auth_boundary", "test_coverage", "dependency_injection".
agent_idstringYesCalling agent identifier.
scopestringNoFile path prefix to limit scan scope.
severity_minnumberNo0.3Minimum severity threshold. Range: 0.0 to 1.0.
graph_validatebooleanNotrueValidate findings against graph edges (cross-file analysis). Disable for raw pattern matching only.
limitintegerNo50Maximum 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

StatusMeaning
"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
  • hypothesize – test a specific structural claim
  • trace – 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

ParameterTypeRequiredDefaultDescription
querystringYesSearch query to find structural holes around.
agent_idstringYesCalling agent identifier.
min_sibling_activationnumberNo0.3Minimum 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
  • activate – activate with include_structural_holes: true for inline hole detection
  • hypothesize – 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

ParameterTypeRequiredDefaultDescription
error_textstringYesFull error output (stacktrace + error message).
agent_idstringYesCalling agent identifier.
languagestringNoLanguage hint: "python", "rust", "typescript", "javascript", "go". Auto-detected if omitted.
window_hoursnumberNo24.0Temporal window (hours) for co-change suspect scan.
top_kintegerNo10Max 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

SignalWeightDescription
trace_depth_scoreHigh1.0 = deepest frame (most specific); decays linearly
recency_scoreMediumExponential decay from last modification time
centrality_scoreMediumNormalized 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 scopingfix_scope tells you which files to inspect and the risk level
  • hypothesize – test a hypothesis about the root cause
  • impact – assess the blast radius of a fix
  • validate_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

ParameterTypeRequiredDefaultDescription
nodestringYesNode external_id. Example: "file::backend/chat_handler.py".
agent_idstringYesCalling agent identifier.
depthstringNo"30d"Time depth. Values: "7d", "30d", "90d", "all".
include_co_changesbooleanNotrueInclude co-changed files with coupling scores.
include_churnbooleanNotrueInclude lines added/deleted churn data.
top_kintegerNo10Max 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

ValueMeaning
"accelerating"Change frequency is increasing
"decelerating"Change frequency is decreasing
"stable"Consistent change rate

Pattern Values

ValueMeaning
"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
  • diverge – structural drift across the whole codebase
  • predict – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
reposobject[]YesList of repositories to federate. Each object has: name (string, required – namespace prefix), path (string, required – absolute path), adapter (string, default "code").
detect_cross_repo_edgesbooleanNotrueAuto-detect cross-repo edges: config, API, import, type, deployment, MCP contract.
incrementalbooleanNofalseOnly re-ingest repos that changed since last federation.

Cross-Repo Edge Types

Edge TypeDescription
shared_configTwo repos reference the same configuration key
api_contractOne repo’s API client matches another’s API server
package_depDirect package dependency
shared_typeSame type/interface definition used across repos
deployment_depDeployment configuration dependency
mcp_contractMCP 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
  • ingest – single-repo ingestion
  • impact – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
scopestringNoFile path prefix to limit discovery sources.
current_repo_namestringNoautoOptional namespace override for the current workspace.
max_reposintegerNo8Maximum discovered external repos to include.
detect_cross_repo_edgesbooleanNotrueWhether execute=true should auto-detect cross-repo edges.
executebooleanNofalseIf 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 audit or external_references already surfaced sibling repo paths
  • Manifest-driven workspaces – when Cargo.toml, package.json, pnpm-workspace.yaml, pyproject.toml, or go.work already 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 .proto or 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 .proto or 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
  • external_references – raw external path evidence
  • federate – explicit multi-repo federation
  • audit – 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

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
filesstring[]YesFile paths and/or glob patterns to expand.
auto_ingestbooleanNoAuto-ingest discovered files before reading.
summary_modebooleanNoAdd inline per-file summaries.

When to Use

  • Audit proof packets
  • Compact multi-file reading after search/glob
  • Agent handoff packages

scan_all

Run all structural scan patterns in one call and return grouped findings by pattern.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoScope path prefix.
patternsstring[]NoSubset of scan patterns to run.
limit_per_patternintegerNoMaximum findings per pattern.

When to Use

  • Broad first-pass quality/security audit
  • Pre-hardening sweep before prioritizing fixes

cross_verify

Compare graph state against filesystem truth: existence, LOC drift, and optional hash mismatches.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoPath prefix to limit verification.
checkstring[]NoChecks to run such as existence, loc, hash.
include_dotfilesbooleanNoInclude 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

coverage_session

Report what the current agent session has already read or touched.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.

When to Use

  • To avoid re-reading files during long sessions
  • To build a compact unread-next list after search/audit sweeps

external_references

Find explicit path references that point outside current ingest roots.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
scopestringNoOptional scope prefix.

When to Use

  • Before federation when docs/config already point outside the repo
  • To surface repo-boundary evidence in audits

glob

Graph-aware file globbing over already indexed files.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
patternstringYesGlob pattern.
scopestringNoOptional path prefix.
sortstringNoSort order such as path or activation.

When to Use

  • When you need filenames by pattern, not content
  • Before batch_view on a file family

view

Fast line-numbered file inspection with optional auto-ingest if the file is not yet in the graph.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
file_pathstringYesAbsolute or workspace-relative file path.
auto_ingestbooleanNoAuto-ingest file into the graph if not present.
offsetintegerNoStart line offset.
limitintegerNoMax lines to return.

When to Use

  • Quick inspection when you already know the file
  • Narrow, bounded reads before a surgical context call

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
querystringYesSeed query for route synthesis.
anchor_nodestringNoAnchor to a specific node. If provided, activates anchored mode where all routes maintain relevance to this node.
lensobjectNoStarting 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

perspective_routes

Browse the current route set with pagination.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective to browse.
pageintegerNo1Page number (1-based).
page_sizeintegerNo6Routes per page. Clamped to [1, 10].
route_set_versionintegerNoVersion 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective containing the route.
route_idstringNoStable content-addressed route ID.
route_indexintegerNo1-based page-local position.
route_set_versionintegerYesRoute 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective containing the route.
route_idstringNoStable content-addressed route ID.
route_indexintegerNo1-based page-local position.
route_set_versionintegerYesRoute 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective to navigate within.
route_idstringNoStable content-addressed route ID.
route_indexintegerNo1-based page-local position.
route_set_versionintegerYesRoute 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective to get a suggestion for.
route_set_versionintegerYesRoute 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective containing the route.
route_idstringNoStable content-addressed route ID.
route_indexintegerNo1-based page-local position.
route_set_versionintegerYesRoute 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
  • 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective to branch from.
branch_namestringNoOptional 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective 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

perspective_compare

Compare two perspectives on shared/unique nodes and dimension deltas. Both perspectives must belong to the same agent (V1 restriction).

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_id_astringYesFirst perspective ID.
perspective_id_bstringYesSecond perspective ID.
dimensionsstring[]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

perspective_list

List all perspectives for an agent. Returns compact summaries with status, focus, route count, and memory usage.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
perspective_idstringYesPerspective 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

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

ParameterTypeRequiredDefaultDescription
pathstringYesFilesystem path to the source root or memory corpus.
agent_idstringYesCalling agent identifier.
incrementalbooleanNofalseIncremental ingest (code adapter only). Only re-processes files that changed since the last ingest.
adapterstringNo"code"Adapter to use for parsing. Values include "code", "json", "memory", "light", "patent", "article", "bibtex", "rfc", "crossref", "universal", and "auto" / "document" for format detection.
modestringNo"replace"How to handle the existing graph. Values: "replace" (clear and rebuild), "merge" (add new nodes/edges into existing graph).
namespacestringNoOptional 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

AdapterInputNode TypesEdge Types
codeSource code directoryfile, class, function, struct, moduleimports, calls, registers, configures, tests, inherits
jsonGraph snapshot JSON(preserved from snapshot)(preserved from snapshot)
memoryMarkdown filesdocument, concept, entityreferences, relates_to
lightL1GHT protocol markdowndocument, section, entity, typed semantic nodesexplicit semantic edges from frontmatter and markers
patent / article / bibtex / rfc / crossrefStructured document formatsdocument, section, citation, entitycitation and cross-domain edges
universalBest-effort document canonicalizationdocument, section, block, table, citation, entity, claimdocument containment, references, bindings, supports
auto / documentFormat detection wrapperroutes to the strongest detected adapteradapter-specific

Mode Behavior

ModeBehavior
replaceClears the existing graph, ingests fresh, finalizes (PageRank + CSR). All perspectives and locks are invalidated.
mergeAdds 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 federate instead 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
  • health – check graph status before deciding to ingest
  • drift – see what changed since last session
  • federate – multi-repo ingestion
  • document_resolve – resolve canonical artifacts for a universal document
  • auto_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

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
pathstringNoOriginal source path or canonical markdown path.
node_idstringNoUniversal 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.md or claims.json directly

document_provider_health

Report availability, mode, detail, and install hints for optional universal-document providers.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling 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

document_bindings

Resolve deterministic document-to-code bindings for a universal document.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
pathstringNoOriginal source path or canonical markdown path.
node_idstringNoUniversal graph node id for the document.
top_kintegerNoMaximum 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

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

auto_ingest_start

Start local-first document watchers for one or more roots and supported document families.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
rootsstring[]YesFilesystem roots to watch recursively.
formatsstring[]NoSupported document formats to auto-ingest.
debounce_msintegerNoMinimum quiet period before a change is eligible for ingestion.
namespacestringNoOptional 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.


health

Server health and statistics. Returns node/edge counts, query count, uptime, memory usage, plasticity state, and active sessions.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling 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
  • ingest – load data if the graph is empty
  • drift – check what changed since last session

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
actionsobject[]YesOrdered 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_impactbooleanNotrueAnalyze test coverage for modified files.
include_risk_scorebooleanNotrueCompute 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

LevelScore RangeMeaning
"low"< 0.3Small, well-tested change
"medium"0.3 – 0.6Moderate scope, some gaps
"high"0.6 – 0.8Large scope or missing tests
"critical">= 0.8Very 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
  • impact – blast radius for a single node
  • predict – co-change prediction for a single node
  • trace – 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier. Lock is owned by this agent.
scopestringYesScope type. Values: "node" (single nodes only), "subgraph" (BFS expansion from roots), "query_neighborhood" (nodes matching a query), "path" (ordered node list).
root_nodesstring[]YesRoot nodes for the lock scope. Non-empty. Matched by external_id (exact), then label, then substring.
radiusintegerNoBFS radius for subgraph scope. Range: 1 to 4. Required for subgraph scope.
querystringNoQuery string for query_neighborhood scope.
path_nodesstring[]NoOrdered node list for path scope.

Scope Types

ScopeDescription
nodeLock only the specified root nodes
subgraphBFS expansion from root nodes up to radius hops
query_neighborhoodNodes matching a query activation
pathAn 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

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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier. Must own the lock.
lock_idstringYesLock to set the watcher on.
strategystringYesWatcher 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_ingest to detect changes after every code re-ingest
  • Learning feedback – set on_learn to detect when learning shifts edge weights in your region
  • Manual control – set manual to disable automatic detection
  • 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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier. Must own the lock.
lock_idstringYesLock 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
  • lock_rebase – re-capture baseline after acknowledging changes
  • lock_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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier. Must own the lock.
lock_idstringYesLock 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.diff returns baseline_stale: true, rebase to fix it
  • Periodic refresh – rebase periodically in long sessions to keep baselines current
  • lock_diff – the diff that triggers a rebase
  • lock_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

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier. Must own the lock.
lock_idstringYesLock 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

daemon_start

Start the persisted daemon control plane. Stores watched roots, initializes daemon counters, and begins the long-lived structural monitoring lane.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
watch_pathsstring[]Nocurrent ingest rootsPaths the daemon should monitor.
poll_interval_msintegerNo500Poll 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

daemon_stop

Stop the daemon control plane without deleting persisted alert history.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.

When to Use

  • End of a daemon-backed session
  • Before shutting down a host that should not keep reconciling

daemon_status

Inspect daemon liveness and runtime counters. Returns watched roots, tracked files, recent tick metrics, and alert counts.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.

Typical Output Fields

  • active
  • watch_paths
  • poll_interval_ms
  • tracked_files
  • tick_count
  • last_tick_duration_ms
  • last_tick_changed_files
  • last_tick_deleted_files
  • last_tick_alerts_emitted
  • alert_count

When to Use

  • To verify daemon startup worked
  • To inspect whether reconciliation is actually happening
  • To debug daemon slowness or alert silence

daemon_tick

Run one explicit daemon reconciliation pass. Polls watched roots, re-ingests changed files, detects deletions, and emits drift alerts.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
max_filesintegerNo32Maximum changed files to process in one tick.

Typical Output Fields

  • changed_files_detected
  • deleted_files_detected
  • files_reingested
  • ingested_files[]
  • alerts_emitted
  • alert_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

alerts_list

List persisted daemon and proactive alerts.

Parameters

ParameterTypeRequiredDefaultDescription
agent_idstringYesCalling agent identifier.
include_ackedbooleanNofalseInclude already acknowledged alerts.
limitintegerNo50Maximum alerts to return.

When to Use

  • Reviewing daemon findings after a session
  • Building an alert inbox for an agent or UI

alerts_ack

Acknowledge one or more persisted daemon/proactive alerts so they stop resurfacing in the unread queue.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
alert_idsstring[]YesAlert IDs to acknowledge.

When to Use

  • After reviewing or actioning daemon findings
  • To keep the alert queue focused on new drift

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

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
file_pathstringYesAbsolute or workspace-relative file path.
new_contentstringYesCandidate replacement content.
descriptionstringNoHuman-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

edit_commit

Commit a previously previewed edit after freshness re-check and explicit confirmation.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
preview_idstringYesPreview handle returned by edit_preview.
confirmbooleanYesMust be true to commit the preview.
reingestbooleanNoRe-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

persist

Force graph and sidecar persistence immediately.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
actionstringYesPersistence 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

boot_memory

Persist small canonical hot-state values next to the graph without polluting larger investigation trails.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
actionstringYesMemory action (set, get, list, delete, etc.).
keystringNoCanonical key to address.
valuejsonNoJSON value to store.

When to Use

  • Short doctrine/state values that should stay hot
  • Session bootstrapping facts an agent should retrieve quickly

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

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
node_idstringNoGraph node to inspect.
file_pathstringNoFile 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

audit

Profile-aware one-call audit over topology, scans, verification, filesystem truth, and git state.

Parameters

ParameterTypeRequiredDescription
agent_idstringYesCalling agent identifier.
pathstringYesRoot path to audit.
profilestringNoAudit profile such as auto, quick, coordination, or production.
depthstringNoAudit depth.
cross_verifybooleanNoInclude graph-vs-disk verification.
external_refsbooleanNoInclude explicit external reference discovery.

When to Use

  • First pass on an unfamiliar repo
  • Long-running session orientation
  • Pre-handoff or pre-merge structural review

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_64
  • m1nd-mcp-macos-x86_64
  • m1nd-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

VariablePurposeIf omitted
M1ND_GRAPH_SOURCEPersist the graph snapshotgraph is memory-only
M1ND_PLASTICITY_STATEPersist learned edge weightslearning 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:

  • ingest
  • activate
  • search
  • impact

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:

  • search for exact text or regex
  • seek for intent-based retrieval
  • impact before touching a central file
  • surgical_context_v2 before multi-file edits
  • validate_plan when 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-mcp path
  • execute permissions on the binary
  • the MCP client logs

Learned state disappears between restarts

Set:

  • M1ND_GRAPH_SOURCE
  • M1ND_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.py and healing_manager.py are 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:

NodeBeforeAfterChange
session_pool.py0.890.93+0.04 (strengthened)
SessionPool class0.840.88+0.04 (strengthened)
worker_pool.py0.61droppedPushed below top-5 (weakened)
healing_manager.pyghost only0.39Promoted 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:

ToolWhen to Use
missingBefore designing new features – find what your codebase lacks
counterfactualBefore deleting or rewriting – simulate the blast radius
hypothesizeWhen debugging – test assumptions about hidden dependencies
impactBefore modifying a file – understand the blast radius
predictAfter modifying a file – which other files probably need changes too
traceWhen 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 health output)
  • 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

  1. Use consistent agent IDs. The same agent should always use the same ID across sessions. This enables drift detection and trail continuity.

  2. Learn after every useful activation. The more feedback the graph gets, the smarter it becomes. Make learn calls automatic in your agent loop.

  3. 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).

  4. Save trails at investigation checkpoints. Trails are cheap to save and invaluable for handoff, resume, and post-mortem analysis.

  5. Merge trails for synthesis. When multiple agents investigate the same area independently, merge their trails to find convergence and conflicts.

  6. Warm up before focused tasks. warmup primes the graph for a specific task, boosting relevant regions before the agent starts querying.

  7. Use drift at 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
  • L1GHT specs through light
  • 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):

OperationTimeScale
Full ingest910ms335 files, 9,767 nodes, 26,557 edges
Spreading activation31-77ms15 results from 9,767 nodes
Blast radius (depth=3)5-52msUp to 4,271 affected nodes
Stacktrace analysis3.5ms5 frames, 4 suspects ranked
Plan validation10ms7 files, 43,152 blast radius
Counterfactual cascade3msFull BFS on 26,557 edges
Hypothesis testing58ms25,015 paths explored
Pattern scan (all 8)38ms335 files, 50 findings per pattern
Multi-repo federation1.3s11,217 nodes, 18,203 cross-repo edges
Lock diff0.08us1,639-node subgraph comparison
Trail merge1.2ms5 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 fmt before committing
  • cargo clippy --all -- -D warnings must pass
  • No unsafe without an explanatory comment
  • All new code needs tests

What areas need the most help?

  1. Language extractors: Adding tree-sitter integration or new language-specific extractors
  2. Graph algorithms: Community detection, better decay functions, embedding-based semantic scoring
  3. Benchmarks: Running m1nd on diverse codebases and reporting real-world performance numbers
  4. 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.rs tool schema registry and the live tools/list response are the callable surface. Use tools/list for 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/list as counting truth.

Row Contract

fieldmeaning
commandcanonical bare MCP call name returned by tools/list
categoryrouting bucket
core_functionshortest truthful description of what the tool does
choose_whenpositive routing trigger
avoid_whendisqualifier / better alternative
requiredminimum required args
returnsstable output surface an LLM can plan around
nextadjacent tools / likely next hop

Foundation

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
ingestParse 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; pathgraph_counts; files_scanned; files_parsed; nodes_created; edges_created; elapsed_msactivate -> impact -> why
activateSpreading 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; queryseeds; activated; ghost_edges; structural_holes; plasticity; elapsed_msactivate -> impact -> why
impactBlast 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_idblast_radius; total_energy; causal_chainsactivate -> impact -> why
whyShortest 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; targetpath; hops; edge_types; path_weightactivate -> impact -> why
learnHebbian 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_idsedges_modified; trust_records_updated; tremor_observations_recordedactivate -> impact -> why
driftWhat 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_idbaseline; changed_nodes; weight_drift; summaryactivate -> impact -> why
healthServer 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_idnode_count; edge_count; sessions; graph_stateactivate -> impact -> why
seekFind 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; querytool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
scanRun 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; patterntool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
timelineTemporal 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; nodetool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
divergeStructural 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; baselinetool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
warmupPrime 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_descriptiontool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
federateUnify 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; repostool-specific JSON payload; inspect API reference / tools/listactivate -> impact -> why
federate_autoDiscover 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_iddiscovered_repos; suggested_repos; executed; federate_resultactivate -> impact -> why

Document Intelligence

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
document_resolveresolve canonical local artifacts for a universally ingested documentUse 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_idcanonical_markdown_path; canonical_json_path; claims_path; producer; binding_count; drift_summarydocument_bindings -> document_drift -> view
document_provider_healthreport optional provider availability, mode, detail, and install hintsUse 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_idpython; providers[]ingest -> document_resolve -> auto_ingest_start
document_bindingsshow deterministic document-to-code bindings for a universal documentUse 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_idbindings[]; source_pathdocument_drift -> impact -> surgical_context_v2
document_driftdetect stale, missing, or ambiguous document/code linksUse after refactors, repo moves, or suspected stale specs.Avoid before the document has been ingested or bound.agent_idfindings[]; summarydocument_bindings -> impact -> timeline
auto_ingest_startstart local-first document watchers for supported roots and formatsUse 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; rootsrunning; provider_status; bootstrapauto_ingest_status -> auto_ingest_tick -> document_resolve
auto_ingest_statusinspect the document auto-ingest runtime, semantic counts, provider status, and route/fallback countsUse to monitor watcher state, queue depth, or document runtime telemetry.Avoid if you only need a direct document refresh or binding result.agent_idrunning; queue_depth; semantic_*; provider_*; recent_eventsauto_ingest_tick -> document_drift -> document_provider_health
auto_ingest_tickdrain queued document changes immediately and apply them to the graphUse when a caller needs deterministic immediate reconciliation instead of waiting for opportunistic ticks.Avoid when no watched roots are active.agent_idingested_paths; removed_paths; skipped_paths; errored_pathsdocument_resolve -> document_drift -> lock_diff
auto_ingest_stopstop document watchers and persist manifest stateUse when shutting down or changing watched roots.Avoid during active watch-driven reconciliation you still need.agent_idstopped; manifest_entriesauto_ingest_start -> auto_ingest_status

Perspective Navigation

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
perspective_startOpen 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; querytool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_routesList 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_followMove 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_backNavigate 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_peekRead 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_inspectDeep 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_suggestAI 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_affinityCheck 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_branchFork 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_compareDiff 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_btool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_listAll 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect
perspective_closeRelease 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_idtool-specific JSON payload; inspect API reference / tools/listperspective_routes -> perspective_follow -> perspective_inspect

Lock System

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
lock_createSnapshot 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_nodestool-specific JSON payload; inspect API reference / tools/listlock_watch -> lock_diff -> lock_rebase
lock_watchRegister 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_idtool-specific JSON payload; inspect API reference / tools/listlock_watch -> lock_diff -> lock_rebase
lock_diffCompare 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_idtool-specific JSON payload; inspect API reference / tools/listlock_watch -> lock_diff -> lock_rebase
lock_rebaseAdvance 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_idtool-specific JSON payload; inspect API reference / tools/listlock_watch -> lock_diff -> lock_rebase
lock_releaseFree 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_idtool-specific JSON payload; inspect API reference / tools/listlock_watch -> lock_diff -> lock_rebase

Superpowers

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
hypothesizeTest 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; claimtool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
counterfactualSimulate 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_idstool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
missingFind 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; querytool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
resonateStanding 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_idtool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
fingerprintFind 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_idtool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
traceMap 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_texttool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
validate_planPre-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; actionsrisk_score; gaps; suggested_additions; heuristic_summaryhypothesize -> validate_plan -> trace
predictCo-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_nodetool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
trail_savePersist 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; labeltool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
trail_resumeRestore 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_idtool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
trail_mergeCombine 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_idstool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
trail_listBrowse 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_idtool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace
differentialFocused 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_btool-specific JSON payload; inspect API reference / tools/listhypothesize -> validate_plan -> trace

Superpowers Extended

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
antibody_scanScan 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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
antibody_listList 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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
antibody_createCreate, 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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
flow_simulateConcurrent 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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
epidemicSIR 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_nodestool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
tremorChange 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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
trustPer-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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
layersAuto-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_idtool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface
layer_inspectInspect 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; leveltool-specific JSON payload; inspect API reference / tools/listtrust -> tremor -> heuristics_surface

RETROBUILDER

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
ghost_edgestemporal co-change ghost edges from git historytemporal co-change ghost edges from git historyAvoid without git/runtime/security context; use only when that extra plane matters.agent_idtool-specific JSON payload; inspect API reference / tools/listruntime_overlay -> trace -> impact
taint_tracetaint propagation over graph structuretaint propagation over graph structureAvoid without git/runtime/security context; use only when that extra plane matters.agent_idtool-specific JSON payload; inspect API reference / tools/listruntime_overlay -> trace -> impact
twinsstructural equivalence / near-equivalence discoverystructural equivalence / near-equivalence discoveryAvoid without git/runtime/security context; use only when that extra plane matters.agent_idtool-specific JSON payload; inspect API reference / tools/listruntime_overlay -> trace -> impact
refactor_plangraph-native refactoring proposalsgraph-native refactoring proposalsAvoid without git/runtime/security context; use only when that extra plane matters.agent_idtool-specific JSON payload; inspect API reference / tools/listruntime_overlay -> trace -> impact
runtime_overlayruntime heat and error overlays from OTel spansruntime heat and error overlays from OTel spansAvoid without git/runtime/security context; use only when that extra plane matters.agent_idtool-specific JSON payload; inspect API reference / tools/listruntime_overlay -> trace -> impact

Surgical

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
heuristics_surfaceExplain 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_idtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch
surgical_contextReturn 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_pathtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch
applyapply 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_contentupdated_node_ids; proactive_insights; elapsed_msheuristics_surface -> batch_view -> apply_batch
viewFast 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_idtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch
surgical_context_v2Superset 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_pathtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch
apply_batchapply_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; editsresults; verification; proactive_insights; progress_eventsheuristics_surface -> batch_view -> apply_batch
edit_previewPreview a write without touching disk.Preview a write without touching disk.Avoid before locating the target surface; use seek/search/impact first.agent_idtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch
edit_commitCommit 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_idtool-specific JSON payload; inspect API reference / tools/listheuristics_surface -> batch_view -> apply_batch

Search & Efficiency

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
searchUnified 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; queryresults; total_matches; proof_state; next_suggested_toolsearch/glob -> batch_view -> coverage_session
globGraph-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_idtool-specific JSON payload; inspect API reference / tools/listsearch/glob -> batch_view -> coverage_session
helpSelf-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_idtool-specific JSON payload; inspect API reference / tools/listsearch/glob -> batch_view -> coverage_session
panoramicFull 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_idmodules; critical_alerts; summarysearch/glob -> batch_view -> coverage_session
savingsToken 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_idtool-specific JSON payload; inspect API reference / tools/listsearch/glob -> batch_view -> coverage_session
reportSession 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_idrecent_queries; savings; hotspot_summarysearch/glob -> batch_view -> coverage_session
metricsReturn 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_identries; summarysearch/glob -> batch_view -> coverage_session
type_traceTrace 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_idusages; file_groupssearch/glob -> batch_view -> coverage_session
diagramGenerate 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_idformat; diagram; node_countsearch/glob -> batch_view -> coverage_session

Audit & Session

commandcore_functionchoose_whenavoid_whenrequiredreturnsnext
batch_viewmulti-file read surface with stable delimiters and summariesmulti-file read surface with stable delimiters and summariesAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
scan_allrun all structural patterns in one callrun all structural patterns in one callAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
cross_verifygraph 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_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
coverage_sessionwhat this agent has visited so farwhat this agent has visited so farAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
external_referencesexplicit paths outside ingest rootsexplicit paths outside ingest rootsAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
auditprofile-aware one-call auditprofile-aware one-call auditAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
daemon_startactivate the persisted daemon control plane and set watch roots / poll intervalactivate the persisted daemon control plane and set watch roots / poll intervalAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
daemon_stopstop the daemon control plane without discarding alert historystop the daemon control plane without discarding alert historyAvoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idtool-specific JSON payload; inspect API reference / tools/listaudit -> batch_view -> cross_verify
daemon_statusinspect daemon runtime state, watch roots, and alert countsUse to inspect daemon liveness, tracked files, and recent tick metrics.Avoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idactive; tracked_files; tick_count; last_tick_duration_ms; last_tick_changed_files; last_tick_deleted_files; last_tick_alerts_emittedaudit -> batch_view -> cross_verify
daemon_tickpoll watched roots once, incrementally re-ingest changed files, and emit deletion drift alertsUse 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_idchanged_files_detected; deleted_files_detected; files_reingested; alerts_emittedaudit -> batch_view -> cross_verify
alerts_listlist persisted daemon/proactive alertsUse to review durable daemon/proactive alerts.Avoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idalerts; total; activeaudit -> batch_view -> cross_verify
alerts_ackacknowledge one or more persisted daemon/proactive alertsUse after reviewing alerts so they do not keep resurfacing.Avoid for tiny one-file questions; these are orchestration and session-shaping tools.agent_idacked; requested; acked_at_msaudit -> batch_view -> cross_verify
persistForce 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_idstatus; snapshot_path; plasticity_pathaudit -> batch_view -> cross_verify
boot_memoryPersist 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_idaction; key; value; entriesaudit -> batch_view -> cross_verify

Routing Rules

  1. Prefer the cheapest tool that preserves structural truth.
  2. Use search / glob / view for exact known targets; use seek / activate when the question is semantic or subsystem-shaped.
  3. Use impact, predict, validate_plan, and the surgical tools before risky edits.
  4. Use audit, cross_verify, coverage_session, batch_view, and daemon tools to manage long-running sessions.
  5. 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.md
  • docs/benchmarks/BENCHMARK_PATCH_PLAN_2026-03-24.md
  • docs/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:

MetricManualwarmResult
Aggregate token proxy10518518250.73% reduction
False starts140m1nd eliminates the recorded false starts
Guided follow-throughs031guided next-step behavior is being followed in real runs
Successful recovery loops012repair 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):

OperationTimeNotes
Full ingest~910msWalk + extract + resolve + finalize
Activate query~31msFour-dimensional ranking
Impact analysis~5msBlast-radius path
Trace analysis~3.5msStacktrace to suspects
Trail resume~0.2msContinuity restore + hints
Apply batch~165msAtomic 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_v2 and validate_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_state and next-step guidance on core flows
  • more actionable trail_resume
  • better seek handling for natural-language prompts
  • reduced validate_plan noise
  • more useful surgical_context_v2
  • observable apply_batch progress 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

ComponentSize
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.

FilesEstimated Ingest TimeEstimated Nodes
100~270ms~3,000
335910ms (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_start
  • daemon_stop
  • daemon_status
  • daemon_tick
  • alerts_list
  • alerts_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_prediction
  • untouched_test_companion
  • antibody_recurrence
  • trust_drop
  • tremor_hotspot
  • cross_repo_contract_risk
  • schema_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_references output
  • lift referenced files to repo roots via .git or manifest markers
  • suggest stable namespace names for the current repo and sibling repos
  • optionally execute federate directly in one call

Its discovery surface now includes:

  • manifest/workspace evidence such as Cargo workspaces, package.json workspaces, pnpm-workspace.yaml, pyproject.toml, and go.work
  • import/package-name matches against nearby repo identities
  • contract artifacts such as .proto definitions, 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_resolve
  • document_bindings
  • document_drift
  • document_provider_health
  • auto_ingest_start
  • auto_ingest_status
  • auto_ingest_tick
  • auto_ingest_stop

The universal lane also now preserves source-byte fidelity and writes a fuller canonical artifact set:

  • source.<ext>
  • canonical.md
  • canonical.json
  • claims.json
  • metadata.json

Optional provider lanes are now surfaced operationally instead of implicitly:

  • Docling
  • Trafilatura
  • MarkItDown
  • GROBID

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-build output
  • 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 / 78 eras were replaced with the live 93-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_ambiguous cases 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:

ToolWhat It Does
batch_viewRead multiple files or glob expansions in one call with stable delimiters, optional summaries, and auto-ingest
scan_allRun all structural scan patterns in one call and return grouped findings
cross_verifyCompare graph state against current disk truth (existence, loc, hash)
coverage_sessionReport which files/nodes the current agent has already visited
external_referencesDiscover explicit references to paths outside current ingest roots
auditProfile-aware one-call audit for topology, scans, verification, git state, and recommendations

Related contract upgrades:

  • health now exposes git context (branch, clean, head, recent commits, uncommitted files)
  • ingest now accepts include_dotfiles and dotfile_patterns
  • view, search, report, and audit now 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.

ToolModuleWhat It Does
ghost_edgesRB-01: 4D Git GraphParse git history and inject temporal co-change ghost edges — hidden coupling between files that always change together but have no static dependency
taint_traceRB-02: Graph FuzzingInject taint at entry points, track propagation through the graph, detect missed security boundaries (validation, auth, sanitization)
twinsRB-03: Structural TwinsFind structurally identical code via topological signature cosine similarity — detects duplicate retry logic, CRUD handlers, state machines
refactor_planRB-04: Intent-Driven RefactoringCommunity detection + bridge analysis + counterfactual simulation for safe module extraction planning
runtime_overlayRB-05: OTel OverlayIngest 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.

ToolWhat It Does
metricsPer-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_traceCross-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.
diagramGenerate 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-openclaw was 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 Test status was restored for branch protection
  • release prep and help/workflow surfaces were aligned before the v0.7.0 cut

[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_TOKEN is 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, and surgical_context_v2 now participate in a shared proof-state model
  • guided outputs now include next_suggested_tool, next_suggested_target, and next_step_hint across the main structural triage and edit-prep paths
  • trail_resume now 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_id for 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, and next_phase
  • phases now act as a structured execution timeline across validate, write, reingest, verify, and done
  • progress_events now provide a streaming-friendly event log for the same lifecycle
  • live apply_batch_progress SSE emission now happens during execution in serve mode
  • replay and live transports now carry consistent batch correlation data
  • the final batch_completed event now carries the batch’s proof_state and 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_origin and source_ref
  • long-running flows can now distinguish live, replay, and snapshot progress delivery
  • the harness now records progress event counts, delivery modes, phase sequences, and guidance-followed behavior
  • the warm_structural_proof_apply_batch scenario 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 -> 5182 token proxy on the aggregate warm-graph corpus, for 50.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_warm reduced false_starts from 14 to 0, recorded 31 guided follow-throughs, and recorded 12 successful 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.

LanguageCommand
Rustcargo check --message-format=short
Gogo build ./...
Pythonpython -c "import ast; ast.parse(open('<file>').read())"
TypeScripttsc --noEmit
  • Timeout: 60 seconds per command
  • Failures produce a structured CompileError with 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 with distance (1 or 2) and the relation type 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.

LanguageCommand
Rustcargo test <module>
Gogo test ./...
Pythonpytest <file> -x -q
  • Per-test-run timeout: 30 seconds
  • tests_run, tests_passed, tests_failed, and test_output fields added to ApplyBatchOutput
  • 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:

PatternSignal
todo!() / unimplemented!() insertedStub replacing real logic
.unwrap() added where none existed beforeError handling removed
panic!() / unreachable!() in non-test codeCrash path introduced
Empty catch / except blockSilent error swallowing
Explicit error handler replaced with no-opRegression in error handling
  • Comparison is pre-write content vs post-write content (diff-based)
  • Each detected anti-pattern produces an AntiPatternMatch with 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 RISKY signal
  • Result stored as a structured GraphDiff embedded in VerificationReport

New Types

TypeLocationPurpose
VerificationReportm1nd-core/src/verify.rsTop-level verification result: layers A–E + graph-diff + verdict
VerificationImpactm1nd-core/src/verify.rsAggregated impact summary: compile status, test counts, anti-patterns
BlastRadiusEntrym1nd-core/src/verify.rsSingle affected-file record from Layer C BFS
CompileCheckResultm1nd-core/src/verify.rsStructured compile output: command, exit code, stderr
AntiPatternMatchm1nd-core/src/verify.rsSingle anti-pattern detection hit with location
GraphDiffm1nd-core/src/verify.rsPre/post node+edge delta from graph-diff step
Verdictm1nd-core/src/verify.rsSAFE / RISKY / BROKEN — final write verdict

New Fields in ApplyBatchOutput

FieldTypeDescription
verificationOption<VerificationReport>Full verification report (present when verify=true)
compile_checkOption<CompileCheckResult>Layer B compile result
tests_runu32Total test cases executed in Layer D
tests_passedu32Passing test count
tests_failedu32Failing test count
test_outputOption<String>Raw test runner output (trimmed to 2 KB)
blast_radiusVec<BlastRadiusEntry>Layer C 2-hop affected files

Verdict System

The Verdict enum drives the final apply_batch outcome when verify=true:

VerdictMeaningCondition
SAFEAll layers passed; write acceptedCompiles, tests pass, no anti-patterns, graph stable
RISKYWrite accepted with warningsCompile OK, but anti-patterns detected OR graph shrinkage OR some tests failed
BROKENWrite rejected; file restored to pre-write contentCompile 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:

  1. Clean write — all layers pass → SAFE
  2. Compile error — Layer B fails → BROKEN + auto-restore
  3. Trivial stub replacement — Layer A triggers → BROKEN
  4. Anti-pattern insertion — Layer E triggers → RISKY
  5. Test regression — Layer D fails → RISKY
  6. Graph node shrinkage — graph-diff triggers → RISKY
  7. Multi-file batch — blast radius correct across 3 files
  8. No test files in radius — Layer D skipped cleanly
  9. Python AST parse failure — Layer B Python path → BROKEN
  10. TypeScript tsc clean — Layer B TS path → SAFE
  11. .unwrap() added where absent — Layer E Rust pattern → RISKY
  12. 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_batch still 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.md and mcp/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:

CratePreviousNew
m1nd-core0.3.x0.4.0
m1nd-ingest0.3.x0.4.0
m1nd-mcp0.3.x0.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.

ToolCategoryWhat It Does
m1nd.antibody_scanImmune MemoryScan the entire graph against all stored bug antibody patterns
m1nd.antibody_listImmune MemoryList stored antibodies with metadata and specificity scores
m1nd.antibody_createImmune MemoryCreate, disable, enable, or delete antibody patterns
m1nd.flow_simulateExecution DynamicsParticle-based concurrent execution simulation
m1nd.epidemicPropagation RiskSIR model predicting bug spread from known-infected modules
m1nd.tremorChange AccelerationSecond-derivative detection of accelerating change frequency
m1nd.trustDefect HistoryActuarial per-module defect density with Bayesian prior adjustment
m1nd.layersArchitectureAutomatic layer detection + dependency violation reporting
m1nd.layer_inspectArchitectureLayer-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 / AntibodyMatch structs
  • PatternNode with match_mode: Exact / Substring / Regex label matching
  • negative_edges in 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 from m1nd.learn feedback
  • compute_specificity() — reject patterns too broad to be useful (MIN_SPECIFICITY=0.15)
  • pattern_similarity() — duplicate detection at registration time (threshold=0.9)
  • Persistence: antibodies.json alongside graph, atomic write with .bak backup
  • 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.

  • FlowEngine with configurable FlowConfig (max_depth, num_particles, turbulence_threshold)
  • TurbulencePoint — race condition hotspot with entry_pairs attribution and path tracking
  • ValvePoint — lock/bottleneck detection via label pattern matching
  • FlowEngine::discover_entry_points() — auto-discover entry nodes from graph structure
  • scope_filter — limit simulation to a subgraph region
  • Hard caps: MAX_PARTICLES=100, MAX_ACTIVE_PARTICLES=10,000 total steps
  • M1ndError::NoEntryPoints raised 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 / EpidemicPrediction
  • EpidemicDirection enum: 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 in EpidemicSummary
  • unreachable_components count — 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
  • EpidemicPersistentState for disk persistence across sessions
  • Hard cap: MAX_ITERATIONS=500; default: 50
  • M1ndError::EpidemicBurnout — graph too densely connected for meaningful prediction
  • M1ndError::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.

  • TremorRegistry ring buffer (256 observations per node)
  • TremorObservation — timestamped weight delta recorded on every learn call
  • TremorWindow enum: Days7 / Days30 / Days90 / All
  • TremorDirection enum: Accelerating / Decelerating / Stable
  • RiskLevel enum: 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_filter parameter to scope analysis to a subgraph
  • Minimum observation gap: 1 second (dedup interval)
  • Persistence: tremor_state.json alongside 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 store
  • TrustEntry — per-node defect data with timestamps
  • TrustScore with TrustTier: HighRisk (<0.4) / MediumRisk (<0.7) / LowRisk (>=0.7)
  • record_defect() / record_false_alarm() / record_partial() — feedback API
  • compute_trust() — time-weighted density: base × (FLOOR + (1-FLOOR) × recency)
  • RECENCY_HALF_LIFE_HOURS=720 (30-day half-life), RECENCY_FLOOR=0.3
  • adjust_prior() — Bayesian prior update; handles both positive and negative claims
  • report() — full trust report with min_history, tier_filter, sort_by options
  • TrustSortBy: TrustAsc / TrustDesc / DefectsDesc / Recency
  • Cold-start default: 0.5 (neutral trust until evidence accumulates)
  • Persistence: trust_state.json alongside 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.

  • LayerDetector with LayerDetectionResult
  • ArchLayer — detected layer with node membership and health metrics
  • LayerViolation with ViolationType: UpwardDependency / CircularDependency / SkipLayer
  • ViolationSeverity: Critical / High / Medium / Low
  • UtilityNode with UtilityClassification: CrossCutting / Bridge / Orphan
  • LayerHealth — per-layer metrics including layer_separation_score
  • tarjan_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_tests and node_type_filter parameters
  • LayerCache — detection results cached against graph generation counter
  • Hard cap: DEFAULT_MAX_LAYERS=8
  • M1ndError::LayerNotFound when 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 namespace parameter scopes all node IDs (default: "memory")
  • Section parsing: H1–H6 headings → Module nodes tagged memory:section
  • Bullet parsing: - / * / +Concept / Process nodes
  • Checkbox parsing: - [x] / - [ ]Process nodes tagged memory: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 containing briefingcanonical=true in provenance
  • Cross-reference extraction: file paths in entry text → Reference nodes with references edges
  • 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.ingest with adapter: "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_strength by relation type
  • contains relation → EdgeDirection::Bidirectional auto-promotion
  • Invoked via m1nd.ingest with adapter: "json"

15 Calibration Knobs

New tools expose agent-controllable parameters for tuning behavior without recompilation:

ToolKey Parameters
antibody_scanmatch_mode (Exact/Substring/Regex), min_severity
antibody_createseverity, description, tags
flow_simulatenum_particles, max_depth, turbulence_threshold, scope_filter
epidemiciterations, direction (Forward/Backward/Both), promotion_threshold
tremorwindow (Days7/Days30/Days90/All), node_filter, min_magnitude
trustmin_history, tier_filter, sort_by, half_life_hours
layersexclude_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/events endpoint streams tool results to browser in real time.
  • m1nd-mcp --serve --dev — HTTP with frontend served from m1nd-ui/dist/ on disk (supports Vite HMR during UI development)
  • m1nd-mcp --serve --open — HTTP + auto-open browser on launch
  • m1nd-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 count
  • GET /api/tools — full tool schema list (same as MCP tools/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, namespaces
  • GET /api/graph/subgraph?query=<q>&top_k=<n> — activate + return subgraph for visualization
  • GET /api/graph/snapshot — full graph dump (nodes + edges) for external export
  • GET /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

  • DomainConfig multi-domain system — code, music, memory, generic presets, each with different temporal decay half-lives and co-change behavior
  • GraphBuilder fluent API for programmatic graph construction in m1nd-core
  • M1ND_DOMAIN env var and domain config 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_mode now propagated correctly through recursive DFS subgraph matching
  • Flow simulation enforces max_depth and max_total_steps hard 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 watch strategy validation rejects "periodic" with M1ndError::WatchStrategyNotSupported instead of silently accepting and never firing
  • lock.diff correctly 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)
  • GraphDiff incremental mode counts RemoveNode / RemoveEdge actions 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_VERSION bumped to 3; load_graph() performs version migration on older files
  • resonate output now includes all 5 fields: harmonics, sympathetic_pairs, resonant_frequencies, wave_pattern, harmonic_groups
  • counterfactual output includes synergy_factor when >1 node removed, and reachability_before / reachability_after metrics
  • Ingest response includes commit_groups in IngestStats (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.