Overview Setup Guide Installation Usage CLI Reference API Architecture Use Cases Comparisons Benchmarks Troubleshooting FAQ
neuralmind.uk / Docs / Memory Layer Edit this page on GitHub ↗

Memory Layer

The Memory Layer gives agents persistent, queryable decision memory: every architectural decision is recorded with its rationale, evidence, and the git commit where it was made — and goes stale when a commit changes the code it describes (v4.6.0+, through the post-commit hook from neuralmind init-hook).

Overview

CLI Reference

Command group: decision verbs live under neuralmind decisions. The neuralmind memory group carries management subcommands only (inspect, reset, export, import, publish, review-*, staleness-*). (v4.1.0 documented neuralmind memory <verb> — that surface was renamed; see the erratum in the v4.1.0 release notes.)

Record a decision

neuralmind decisions record [project_path] --title "TITLE" --rationale TEXT
    [--commit SHA] [--files "a.py" "b.py"] [--type architecture]
    [--rejected "rejected option A" "rejected option B"]
    [--evidence "ref or URL 1" "ref or URL 2"]
    [--confidence 0.95] [--tags "perf" "storage"]

Query decisions

neuralmind decisions query "keywords or a question" [project_path] [--mode hybrid|semantic|keyword] [--limit 10] [--status ACTIVE|STALE|INVALIDATED|ALL] [--json]

The query text comes first; the project path is optional and comes second.

Search modes (v4.8.0+). --mode picks how decisions are ranked; without it, NEURALMIND_DECISION_SEARCH decides, and without that, hybrid:

Mode Ranks by Finds
keyword Shared words (below) Decisions that contain a word of the question
semantic Meaning: cosine similarity between the question and each decision’s title + rationale, embedded with the local all-MiniLM-L6-v2 model Decisions worded differently from the question; nothing below a similarity of 0.30
hybrid (default) Both rankings, fused by reciprocal rank fusion Either kind

Keyword matching. Search matches the query’s words against titles and rationales, prefix-matched (“pool” finds “pooling”). Common words such as “how”, “do” and “the” are dropped, a decision needs to contain only one of the remaining words, and bm25 ranks the decisions that contain more of them, and rarer ones, first. So “how do we handle sqlite wal?” finds what “sqlite wal” finds. The flip side: a question nothing answers still returns the closest partial matches, so check the titles before relying on a hit. Without FTS5 (some minimal SQLite builds) the same words are matched as substrings, and decisions containing more of them rank first. --status is case-insensitive and defaults to ACTIVE; ALL includes every status, in every mode.

Audit

neuralmind decisions audit [project_path] [--stale] [--orphaned] [--format md|json]

Lists every recorded decision by default. --stale / --orphaned narrow the list to entries that need attention: age-based staleness (STALE_DAYS=90) plus orphaned-SHA detection (commit no longer in git history).

Amend / Invalidate / Restore

neuralmind decisions amend <decision-id> [--rationale TEXT] [--evidence "ref"] [--rejected "alt"]
neuralmind decisions invalidate <decision-id> --reason "why"
neuralmind decisions restore <decision-id> [--commit SHA]

Scan the last commit (v4.6.0+)

neuralmind decisions scan [project_path] [--quiet] [--json]

Marks STALE every decision whose files the HEAD commit changed after the decision was recorded. The post-commit hook installed by neuralmind init-hook runs it after every commit (re-run init-hook on an older checkout). See Invalidation semantics.

Export

neuralmind decisions export [project_path] [--format md|json] [--output FILE]

Eval harness

neuralmind decisions eval [project_path] [--tasks 10] [--format json|md] [--output FILE]
neuralmind decisions eval --queries tests/memory/fixtures/decision_queries.json [--mode all|keyword|semantic|hybrid] [--limit 5] [--format json|md]

Both seed a scratch store in a temporary directory and never read or change your project’s decisions. Earlier versions deleted .neuralmind/memory.db in project_path (the current directory by default) and left 15 synthetic decisions in its place. If you ran decisions eval inside a project, those decisions have the author eval-harness: find them with neuralmind decisions audit --format json and retire each with neuralmind decisions invalidate <id> --reason "eval seed".

The default run replays five maintenance tasks with keyword queries and reports recall, precision and stale influence with the status filter on and off. --queries scores search against questions with known answers, in each search mode side by side (--mode all, the default), and lists every miss, false positive and answer not ranked first. A mode that can’t run, because the embedding model isn’t on disk, is reported as not run rather than scored: a hybrid search that fell back to keyword results is never counted as hybrid.

The committed set, tests/memory/fixtures/decision_queries.json, is synthetic: 35 decisions (30 ACTIVE), 20 agent-style questions that each share at least one word with their answer, 20 paraphrases that share no search word with theirs (a test enforces it, so keyword search can’t find them by construction), and 4 questions nothing answers. The eval also asks every ACTIVE decision’s exact title. Measured at the default limit of 5 (tests/memory/test_query_eval.py keeps this table in step with the eval: the keyword column everywhere, the semantic and hybrid columns wherever the model is on disk):

Measure Keyword Semantic Hybrid (default)
Recall@5 on 20 questions, mean (range) 1.00 (1.00–1.00) 0.95 (0.00–1.00) 1.00 (1.00–1.00)
MRR on 20 questions, mean (range) 0.94 (0.33–1.00) 0.95 (0.00–1.00) 0.95 (0.50–1.00)
Recall@5 on 20 paraphrases, mean (range) 0.00 (0.00–0.00) 0.50 (0.00–1.00) 0.45 (0.00–1.00)
MRR on 20 paraphrases, mean (range) 0.00 (0.00–0.00) 0.44 (0.00–1.00) 0.28 (0.00–1.00)
Paraphrases that return nothing 6 of 20 7 of 20 1 of 20
Exact titles ranked first 30 of 30 30 of 30 30 of 30
Questions nothing answers that still return decisions 2 of 4 1 of 4 2 of 4

Keyword mode only, with FTS5 except where noted:

Measure Result
Recall@5 without FTS5 (LIKE fallback), mean (range) 0.95 (0.00–1.00)
Maintenance tasks, recall / precision at limit 10 90% / 53%

How hybrid became the default. The paraphrases, the 0.30 similarity floor and a keep rule were committed before semantic or hybrid search was run on this set (the fixture’s _paraphrase_preregistration). Against keyword search, hybrid had to raise paraphrase recall, keep question recall and lose no more than 0.05 MRR, and rank no fewer exact titles first. It did all three. Unanswerable questions were to be reported, not gated.

What the misses say.

Before v4.5.1, every word had to appear, none of the 20 questions returned anything, and the maintenance tasks scored 50% recall and 83% precision.

MCP Tools

Registered in the MCP server (28 tools total as of v4.3.0):

Tool Arguments Description
neuralmind_query_decisions project_path, query, limit, mode Search decision titles and rationales by keywords, a question, or meaning (ACTIVE only). mode is hybrid (default), semantic or keyword; the response’s mode says which ran, with a notice when hybrid fell back to keyword, and semantic without the model returns code: "semantic_unavailable"
neuralmind_audit_decisions project_path, stale_only List decisions, filter by status
neuralmind_record_decision project_path, title, rationale, commit_sha, files_affected, decision_type, confidence, evidence, rejected_alternatives, tags Store a new decision
neuralmind_invalidate_decision project_path, decision_id, reason Mark a decision INVALIDATED; the reason is appended to its evidence
neuralmind_memory_search project_path, query, limit, status, mode Layer 1 — compact index rows (~50–100 tokens each). Cheap first call; filter here before fetching. status is ACTIVE (default), STALE, INVALIDATED or ALL, case-insensitive; an unknown value returns an error. mode as for neuralmind_query_decisions
neuralmind_memory_timeline project_path, decision_id or query, before, after Layer 2 — chronological context around an anchor decision
neuralmind_memory_get project_path, ids (max 20) Layer 3 — full records (rationale, rejected alternatives, evidence); batch-capped to force filtering

Progressive retrieval workflow (v4.3.0)

memory_search   →  scan compact rows, pick candidates (~50-100 tokens/row)
memory_timeline →  (optional) what else was decided around a hit
memory_get      →  full records for the shortlist (batch-capped at 20)

A “why did we choose X over Y?” lookup completes in ≤3 tool calls and ≤2,000 tokens on the fixture repo. The single-shot tools above remain for compatibility.

RBAC

Invalidation Semantics

Stale-Decision Guard (v4.2.0)

The runtime counterpart of the eval harness’s stale_influence_rate metric: instead of only measuring how often stale memory steers edits, the guard prevents it.

How it works: when your agent is about to edit a file (Claude Code Edit/Write tools), a PreToolUse hook checks the decision store for any STALE or INVALIDATED decisions whose files_affected covers that file. If found, they’re injected into the agent’s context before the edit lands:

[neuralmind stale-guard] 1 decision(s) governing neuralmind/db.py are no longer ACTIVE. Their rationale may not hold — verify before relying on them:
- [STALE] Use SQLite WAL (id 5b1e0c7a-…, confidence 0.90, updated 2026-09-10): WAL mode required for concurrent readers... — commit 3f9c2ab changed neuralmind/db.py since this decision was recorded
If a STALE decision still holds after you check the code, `neuralmind decisions restore <id>` re-anchors it to HEAD.

Since v4.6.0 each line carries the full id and the reason the decision left ACTIVE (from its evidence).

The agent sees which remembered rationales may no longer hold — before it edits, not after.

Properties:

Installation: ships with neuralmind install-hooks (hook block v3). Existing installs pick it up automatically on upgrade — re-run neuralmind install-hooks after upgrading to v4.2.0+.

Python API

from neuralmind.memory.store import DecisionStore

store = DecisionStore(project_path=".")
store.record(
    title="Use SQLite for decision store",
    rationale="Single-file, no server, ACID-compliant",
    files_affected=["neuralmind/memory/store.py"],
    decision_type="architecture",
    confidence=0.95,
)
results = store.query("database choice")  # hybrid by default

found = store.search("why do writes go through one thread?", mode="semantic")
found.mode, found.notice  # the mode that ranked found.records; why, if hybrid fell back

Testing

The tests in tests/memory/ cover store CRUD/FTS/audit/export, search terms and ranking on both the FTS5 path and the LIKE fallback, semantic and hybrid search with a stand-in embedder (the vector cache, the similarity floor, fusion, every fallback, and that search never downloads the model), the invalidation engine (file-touch, the carrying-commit exemption, renames, merges, subdirectory projects, cascade, idempotency), decisions scan, MCP tool dispatch, both eval harnesses and the decision-search eval set, and integration (lifecycle, export/import roundtrip, 10-thread concurrency, broken-git graceful degradation).