SPINE provides an optional integration with Minna Memory (mem-system-lite-mcp) for persistent cross-session memory capabilities.
Minna Memory enables SPINE to remember across sessions. Unlike ephemeral in-context memory, Minna provides persistent storage for decisions, patterns, errors, and preferences that survive session boundaries.
Key principle: SPINE continues to work normally if Minna is unavailable. The integration uses graceful degradation—every memory call has a sensible default (empty list, None) so workflows continue uninterrupted.
| Capability | Without Minna | With Minna |
|---|---|---|
| Entity tracking | No structured storage | Yes (people, projects, concepts) |
| Cross-session persistence | Ephemeral | Yes (SQLite) |
| Confidence decay | No | Yes (configurable half-life) |
| Conflict detection | No | Yes (memory_detect_conflicts) |
| Session summaries | Manual NEXT.md updates | Automatic storage |
| Full-text search | Limited | Yes (FTS5) |
| Preference tracking | Scattered configs | Structured (category/key/value) |
| Portable database | Scattered files | Single .spine/minna.db |
Each SPINE-managed project gets its own isolated Minna database:
SPINE (backbone)
│
├── .spine/minna.db ← SPINE's cross-project knowledge
│
├── Project A
│ ├── .spine/config.json ← Contains Minna settings
│ ├── .spine/minna.db ← Project A's memories
│ └── ai-coordination/
│
├── Project B
│ ├── .spine/config.json
│ ├── .spine/minna.db ← Project B's memories
│ └── ai-coordination/
│
└── Project C (no Minna)
└── .spine/config.json ← minna.enabled: false
Benefits:
| Benefit | Why It Matters |
|---|---|
| Project Isolation | Memories don’t cross-contaminate between projects |
| Portability | .spine/minna.db travels with the project (SQLite) |
| Optional | Projects work fine without Minna configured |
| Per-Machine | Database is gitignored (.spine/minna.db not committed) |
Minna exposes 16 MCP tools for Claude Code integration:
| Tool | Purpose |
|---|---|
memory_store |
Store a memory about an entity |
memory_recall |
Retrieve memories with filters |
memory_search |
Full-text search across all memories |
memory_who |
Get comprehensive entity profile |
memory_add_entity |
Create a new entity (person, project, concept) |
memory_get_entity |
Get entity by name or alias |
memory_find_entities |
Search entities by type/query |
memory_relate |
Create relationship between entities |
memory_get_related |
Get related entities |
memory_set_preference |
Set/get preferences |
memory_get_preferences |
List all preferences |
memory_summarize_session |
Store conversation summary |
memory_get_context |
Get recent conversation context |
memory_stats |
Memory statistics |
memory_detect_conflicts |
Find contradicting memories |
memory_verify |
Reset confidence decay (re-confirm memory) |
Critical: The MCP server only accepts 5 entity types:
| Type | Use For |
|---|---|
person |
People, contacts, stakeholders |
project |
Project records |
concept |
ADRs, errors, tasks, tools, patterns (use naming prefixes) |
topic |
Topics, categories |
preference |
Stored preferences |
Use concept type with prefixed names for semantic distinction:
| Subtype | Naming Pattern | Example |
|---|---|---|
| ADRs | adr:{project}:{id} |
adr:spine:minna-mandatory |
| Errors | error:{project}:{hash} |
error:spine:12345678 |
| Tasks | task:{project}:{id} |
task:spine:task-001 |
| Tools | tool:{project}:{name} |
tool:spine:grep |
| Patterns | pattern:{project}:{name} |
pattern:spine:retry-on-failure |
| Resolutions | resolution:{project}:{hash} |
resolution:spine:87654321 |
Problem: Design decisions scattered across NEXT.md, ai-memory/, human memory.
Minna Solution:
# After making architectural decision:
memory_add_entity(name="adr:spine:auth-jwt", entity_type="concept")
memory_store(
entity="adr:spine:auth-jwt",
attribute="decision",
value="Chose JWT over sessions for stateless API"
)
memory_store(
entity="adr:spine:auth-jwt",
attribute="rationale",
value="Trade-offs: token size vs server load, chosen for horizontal scaling"
)
memory_store(
entity="adr:spine:auth-jwt",
attribute="alternatives",
value="Sessions (rejected: requires sticky sessions), API keys (rejected: no user context)"
)
# Later, when implementing auth:
memory_search(query="authentication decision")
→ Returns ADR with full context
Problem: Claude encounters the same error repeatedly across sessions.
Minna Solution:
# When error occurs:
error_hash = hash(error_output[:200])
memory_add_entity(name=f"error:spine:{error_hash}", entity_type="concept")
memory_store(entity=f"error:spine:{error_hash}", attribute="error_pattern", value=error_output)
# When resolved:
memory_store(entity=f"error:spine:{error_hash}", attribute="resolution", value="Fixed by adding retry logic to API calls")
# Future session encounters same error:
prior = memory_search(query=f"error:{error_hash}")
if prior:
resolution = memory_recall(entity=prior[0].entity, attribute="resolution")
→ "Previously resolved by: Fixed by adding retry logic to API calls"
Problem: Claude doesn’t know which tools work best for which task types.
Minna Solution:
# After successful tool use:
memory_store(
entity="tool:spine:ripgrep",
attribute="tool_success",
value="code_search tasks - fast and accurate",
context="Used for finding function definitions"
)
# After tool failure:
memory_store(
entity="tool:spine:find",
attribute="tool_failure",
value="large codebases - times out",
context="Better to use ripgrep or glob patterns"
)
# Before selecting tool:
memory_search(query="code_search tool")
→ Returns tool preference history
Problem: Claude needs to re-learn stakeholder preferences each session.
Minna Solution:
# When learning stakeholder preferences:
memory_add_entity(name="Jack", entity_type="person")
memory_store(entity="Jack", attribute="prefers", value="dark mode code blocks")
memory_store(entity="Jack", attribute="communication", value="concise, no emojis")
memory_store(entity="Jack", attribute="coding_style", value="snake_case, 100 char lines")
# Any session:
memory_who("Jack")
→ Returns complete profile with all preferences
Problem: Context lost when session compacts or ends.
Minna Solution:
# At session end:
memory_summarize_session(
session_id="session-2026-01-18",
summary="Completed Minna integration for SPINE. Added 16 MCP tools, created showcase docs.",
topics=["minna-memory", "spine-showcase", "documentation"]
)
# Next session:
memory_get_context(limit=5)
→ Returns recent session summaries for continuity
Minna implements a half-life model for memory confidence:
Confidence = base_confidence × 2^(-days_since_verified / half_life)
| Parameter | Default | Description |
|---|---|---|
half_life_days |
90 | Days for confidence to halve |
base_confidence |
1.0 | Initial confidence (0.0-1.0) |
Why It Matters:
# Recall with minimum confidence threshold:
memory_recall(entity="Jack", min_confidence=0.5)
→ Returns only memories with effective_confidence >= 0.5
# Re-verify a memory (resets decay):
memory_verify(memory_id=123)
→ Memory's verified_at updated to now
Minna can detect contradicting facts:
# Store conflicting information:
memory_store(entity="Jack", attribute="timezone", value="UTC-5")
memory_store(entity="Jack", attribute="timezone", value="UTC+1")
# Detect conflicts:
memory_detect_conflicts(entity="Jack")
→ Returns:
{
"conflicts": [{
"attribute": "timezone",
"values": ["UTC-5", "UTC+1"],
"memory_ids": [45, 67]
}]
}
Before subagent execution, Minna can enrich context:
# In context stack building:
if minna_available:
# Get relevant ADRs
adrs = minna.memory_search(f"adr:{project_id}")
# Get code style preferences
prefs = minna.get_preferences(category="code_style")
# Get stakeholder preferences
stakeholder = minna.memory_who("Jack")
# Inject into context_stack.context.background
context_stack.context.background += f"\n\nADRs: {adrs}\nPreferences: {prefs}"
When OscillationTracker detects stuck states:
# Store oscillation pattern for future reference
minna.memory_store(
entity=f"pattern:{project_id}:oscillation",
attribute="anti_pattern",
value=oscillation.error_signature,
context=oscillation.error_output[:500]
)
# Future session can check:
prior_oscillations = minna.memory_search(f"oscillation {error_signature}")
if prior_oscillations:
resolution = minna.memory_recall(
entity=prior_oscillations[0].entity,
attribute="resolution"
)
At session end:
minna.memory_summarize_session(
session_id=f"session-{date.today()}",
summary=summary_text,
topics=["feature-x", "bug-fix-y"],
entity_ids=[project_entity_id, stakeholder_id]
)
{
"mcpServers": {
"minna-memory": {
"command": "bash",
"args": ["-c", "cd /path/to/mem-system-lite-mcp && .venv/bin/python -m mem_system_lite.server"],
"env": {
"MINNA_DB_PATH": "/path/to/project/.spine/minna.db"
}
}
}
}
{
"project_id": "my-project",
"project_label": "PROJ-MY-PROJECT",
"minna": {
"enabled": true,
"database": ".spine/minna.db",
"auto_summarize": true,
"entity_types_available": [
"person", "project", "concept", "topic", "preference"
],
"naming_conventions": {
"adrs": "adr:{project}:{id}",
"errors": "error:{project}:{hash}",
"tasks": "task:{project}:{id}",
"tools": "tool:{project}:{name}",
"patterns": "pattern:{project}:{name}"
},
"memory_attributes": [
"decision", "rationale", "alternatives", "trade_offs",
"task_outcome", "error_pattern", "resolution",
"tool_success", "tool_failure"
],
"context_enrichment": {
"enabled": true,
"include_decisions": true,
"include_patterns": true,
"include_error_resolutions": true,
"max_memories_per_category": 10
}
}
}
Every Minna call has a sensible default:
class MinnaIntegration:
def is_available(self):
try:
return self._health_check()
except:
return False
def search_memories(self, query, **kwargs):
if not self.is_available():
return [] # Graceful no-op
return self._client.memory_search(query, **kwargs)
def store_memory(self, entity, attribute, value, **kwargs):
if not self.is_available():
return None # Graceful no-op
return self._client.memory_store(entity, attribute, value, **kwargs)
| Scenario | Behavior |
|---|---|
| Minna MCP server not running | Returns empty results, SPINE continues |
| Database file doesn’t exist | Created automatically on first use |
| Network/connection error | Graceful fallback, warning logged |
| Tool call fails | Returns None/empty, workflow continues |
| Benefit | Impact |
|---|---|
| Session Continuity | No more “where were we?” at session start |
| Reduced Re-Discovery | Don’t re-learn stakeholder preferences every time |
| Pattern Recognition | Detect recurring errors across sessions |
| Decision Traceability | Know WHY past decisions were made |
| Preference Consistency | Code style, communication style persisted |
| Benefit | How Minna Helps |
|---|---|
| Subagent Context | Enrich prompts with relevant memories before execution |
| Error Recovery | Look up past resolutions before attempting fixes |
| Tool Selection | Learn which tools work best for which tasks |
| Conflict Resolution | Detect contradicting information early |
| Approach | Pros | Cons |
|---|---|---|
| In-context memory | Fast, no setup | Lost on compaction/session end |
| File-based (ai-memory/) | Simple, git-tracked | Unstructured, hard to search |
| Vector store | Semantic search | Complex setup, costly |
| Minna | Structured, searchable, portable | Requires MCP server setup |
Beyond the standard patterns, Minna can be stretched for creative applications:
Scenario: Learning from one project applies to another.
# Project A discovers a useful pattern
memory_store(entity="pattern:projectA:retry-backoff", attribute="pattern",
value="Exponential backoff with jitter for API calls")
# Project B (different Minna DB) could import this
# Manual: Export from A, import to B
# Or: Use a shared "patterns" database path
Fringe aspect: Minna is per-project by design, but nothing stops you from pointing multiple projects at a shared patterns.db for cross-project learning.
Scenario: Remember which prompt phrasings work best.
memory_add_entity(name="prompt:code-review", entity_type="concept")
memory_store(entity="prompt:code-review", attribute="effective",
value="Focus on security vulnerabilities, not style nitpicks")
memory_store(entity="prompt:code-review", attribute="ineffective",
value="Generic 'review this code' produces low-quality output")
Fringe aspect: Using Minna to evolve prompts over time rather than just storing facts.
Scenario: Multi-day debugging where context is critical.
# Day 1: Initial investigation
memory_store(entity="debug:issue-123", attribute="hypothesis",
value="Memory leak in connection pool - connections not released")
memory_store(entity="debug:issue-123", attribute="ruled_out",
value="Not garbage collection - heap dumps normal")
# Day 2: Continue where you left off
profile = memory_who("debug:issue-123")
# Returns full investigation history
Fringe aspect: Treating a debug session as an entity that accumulates knowledge.
Scenario: Remember reviewer preferences per codebase.
memory_store(entity="reviewer:security-bot", attribute="focus",
value="SQL injection, XSS, auth bypass - ignore style")
memory_store(entity="reviewer:perf-bot", attribute="focus",
value="N+1 queries, missing indexes, unbounded loops")
# Before review, load persona preferences
persona = memory_who("reviewer:security-bot")
Fringe aspect: Personas that evolve based on feedback rather than static prompts.
Scenario: Document tribal knowledge as you discover it.
# While exploring legacy code
memory_store(entity="legacy:payment-module", attribute="warning",
value="Do NOT modify calculateTax() - breaks 3 downstream systems")
memory_store(entity="legacy:payment-module", attribute="history",
value="Originally written for EU only, US bolted on in 2019")
# Future sessions get this context automatically
Fringe aspect: Building institutional knowledge that persists beyond any single developer.
Scenario: Track which approaches were tried and their outcomes.
memory_store(entity="experiment:auth-flow", attribute="approach_a",
value="OAuth2 with PKCE - worked but complex setup")
memory_store(entity="experiment:auth-flow", attribute="approach_b",
value="Magic links - simpler but email deliverability issues")
memory_store(entity="experiment:auth-flow", attribute="chosen",
value="Hybrid: Magic links for signup, OAuth for returning users")
Fringe aspect: Decision journaling that captures the exploration process, not just the outcome.
Scenario: Agents leave messages for future agents.
# Architect agent leaves note
memory_store(entity="handoff:task-123", attribute="for_implementer",
value="I chose microservices over monolith - see adr:arch-001 for rationale")
# Implementer agent picks up
notes = memory_recall(entity="handoff:task-123", attribute="for_implementer")
Fringe aspect: Asynchronous agent-to-agent communication via persistent memory.
Only 5 types allowed: person, project, concept, topic, preference
Workaround: Use concept with naming prefixes (see naming conventions above)
.spine/minna.db is gitignored (binary file, per-machine state).
Implication: Each developer/machine has their own memory store.
If shared memory needed: Consider a shared database path or export/import workflows.
Memories persist indefinitely. Use confidence decay + filtering to surface relevant memories.
Minna assumes single-user access to the database. No built-in locking for concurrent access.
FTS5 search is text-based, not semantic. Use specific keywords for best results.
# Store a decision
memory_add_entity(name="adr:project:decision-id", entity_type="concept")
memory_store(entity="adr:project:decision-id", attribute="decision", value="...")
memory_store(entity="adr:project:decision-id", attribute="rationale", value="...")
# Find past decisions
memory_search(query="authentication decision")
# Store error resolution
memory_store(entity="error:project:hash", attribute="resolution", value="...")
# Get stakeholder profile
memory_who("Jack")
# Set preference
memory_set_preference(category="code_style", key="line_length", value="100")
# Session summary
memory_summarize_session(session_id="...", summary="...")
| Resource | Description |
|---|---|
| SPINE Integration Strategy | Strategy doc in ai-memory/integrations/ |
| spine-integrations | Implementation package [PROJ-SOLAR-SPARK] |
| mem-system-lite-mcp | Core Minna library |
| MCP Orchestrator | Another optional integration |
Minna Memory transforms SPINE from a stateless orchestrator into a learning system that:
All with graceful degradation—SPINE works identically with or without Minna.
| ← Back to Docs | MCP Orchestrator → |