This document explains the core concepts from the AINativeLang (AINL) unified graph execution engine that inspire this Claude Code plugin.
Traditional approach:
Agent Execution ─→ Separate Memory Layer ─→ Retrieval System ─→ Context
AINL approach:
Execution Graph IS the Memory ─→ Graph Traversal ─→ Context
No impedance mismatch. No separate "memory consolidation" step. The execution trace itself is the queryable knowledge representation.
The execution graph IS the memory, not a separate retrieval layer.
Every coding turn, tool invocation, and decision becomes a typed node in a persistent graph. There are no separate episodic/semantic/procedural silos—unified graph traversal provides all context retrieval needs.
Traditional memory systems store execution traces separately from semantic facts, then try to reconstruct causality during retrieval. This creates:
- Synchronization problems (when to consolidate?)
- Lossy compression (what to keep?)
- Retrieval mismatch (execution context lost)
Graph-as-memory eliminates these issues:
- Execution and memory are the same data structure
- Natural temporal ordering through graph edges
- Causality is first-class (FIXED_BY, DERIVES_FROM edges)
- Composable queries via graph traversal
In this plugin:
- Each
PostToolUsehook creates potential episode nodes - Tool sequences become FOLLOWS edge chains
- Semantic facts link to source episodes via DERIVES_FROM edges
- Patterns link to evidence via PATTERN_FOR edges
Five core node types form the vocabulary of agent memory:
What happened during a coding turn.
{
"turn_id": "uuid",
"task_description": "Fix authentication bug",
"tool_calls": ["read", "edit", "bash"], # Canonicalized
"files_touched": ["src/auth.py"],
"outcome": "success"
}- Records the execution trace
- Tool calls are canonicalized (
Bash/shell/sh→bash) - Outcome tracks success/failure/partial
- Links to previous episode via FOLLOWS edge
What was learned as a fact.
{
"fact": "Authentication requires JWT validation",
"confidence": 0.85,
"source_turn_id": "episode-uuid",
"recurrence_count": 3,
"tags": ["authentication", "security"]
}- Facts have confidence scores (0.0-1.0)
- Recurrence count tracks repetition (increases confidence)
- Links to source episode via DERIVES_FROM edge
- Topic tags enable ranked retrieval
How to do something as a reusable workflow.
{
"pattern_name": "rust-cargo-check-fix-loop",
"trigger": "cargo check error",
"tool_sequence": ["bash", "read", "edit", "bash"],
"success_count": 5,
"fitness": 0.83 # EMA fitness score
}- Represents compiled workflows
- Fitness tracked via EMA (Exponential Moving Average)
- Success/failure counts inform promotion decisions
- Links to evidence episodes via PATTERN_FOR edges
Who I am / What this project is.
{
"trait_name": "prefers_explicit_types",
"strength": 0.75,
"axis": "type_safety",
"layer": "adaptive"
}- Traits evolve from metadata signals (no LLM calls)
- Strength decays over time without reinforcement
- Axes represent spectrums (concise ↔ detailed)
- Links to evidence episodes via LEARNED_FROM edges
What went wrong and how it was fixed.
{
"error_type": "compilation",
"tool": "bash",
"error_message": "missing field `user_id`",
"file": "src/models.rs",
"line": 42,
"resolution": "Added user_id field",
"resolution_turn_id": "fix-episode-uuid"
}- Captures error details with context
- Links to fix episode via RESOLVES edge
- Enables "known issues" retrieval
- Prevents repeated mistakes
Traditional agent personas use binary traits:
- "Prefers concise responses" (on/off)
- Requires manual configuration
- No adaptation to changing context
Persona traits live on continuous spectrums:
verbosity: concise ←───────○─────→ detailed
0.3 (slightly concise)
testing_rigor: minimal ←─────────────○→ comprehensive
0.85 (very thorough)
No LLM calls needed. Signals extracted from tool usage:
# User ran 3 test commands in this session
signal = EvolutionSignal(
axis='testing_rigor',
direction=1.0, # Toward "comprehensive"
strength=0.7, # Strong signal
evidence='episode-uuid'
)
# Apply signal with EMA smoothing
axis.current = axis.current * 0.95 + signal * 0.05Exponential Moving Average prevents overfitting:
- Recent behavior has more weight
- Old traits decay without reinforcement
- Noise doesn't create permanent traits (threshold)
Developer preferences:
verbosity: concise ↔ detailedtesting_rigor: minimal ↔ comprehensivetype_safety: dynamic ↔ stricterror_handling: permissive ↔ defensive
Project style:
architecture: monolithic ↔ modulardocumentation: sparse ↔ richperformance_focus: dev_speed ↔ optimization
Successful tool sequences become reusable patterns:
Sequence observed 3 times:
bash → read → edit → bash
(all outcomes: success)
↓ Promote to pattern
Procedural node:
name: "error-read-fix-verify"
trigger: "compilation error"
fitness: 0.90
- Min occurrences: 2+ successful executions
- Min fitness: 0.7+ success rate
- Min sequence length: 2+ tools (avoid trivial patterns)
Before pattern detection, tools are normalized:
TOOL_CANON = {
'Bash': 'bash',
'Shell': 'bash',
'sh': 'bash',
# ... more mappings
}This ensures [Bash, Read, Edit] and [shell, read, Edit] are recognized as the same pattern.
Fitness uses EMA to track success rate:
# Pattern used successfully
new_fitness = 0.2 * 1.0 + 0.8 * old_fitness
# Pattern failed
new_fitness = 0.2 * 0.0 + 0.8 * old_fitnessLow-fitness patterns are not promoted to avoid noise.
Multiple processes need to write to graph memory:
- Hooks (fast, triggered by events)
- MCP server (authoritative, handles queries)
- Pattern extraction (background job)
Direct SQLite writes from all processes → locking conflicts.
┌──────┐ ┌──────────┐ ┌─────────┐
│ Hook │ ─► append ────► │ Inbox │ ─► drain ──────► │ SQLite │
└──────┘ (no lock) │ (JSONL) │ (transactional)│ Graph │
└──────────┘ └─────────┘
Hooks:
- Append lightweight JSON to
inbox/project_captures.jsonl - Never block on DB locks
- Always return immediately
MCP Server:
- Drains inbox on queries (eventual consistency)
- Consolidates captures into proper graph nodes
- Handles all DB transactions
Benefits:
- No locking conflicts
- Hooks remain fast and reliable
- Graph remains consistent (single writer)
This pattern mirrors ArmaraOS's approach:
- Python AINL writes to
ainl_graph_memory_inbox.json - Rust daemon drains inbox on turn start
- Separate projections remain eventually consistent
When recalling context, nodes are ranked by relevance:
score = 0.0
# Project match (critical)
if node.project_id == current_project:
score += 10.0
# Recency (decay over 30 days)
age_days = (now - node.created_at) / 86400
score += 5.0 * (1 - age_days / 30)
# Type-specific bonuses
if node.type == 'episode' and node.outcome == 'success':
score += 3.0
if node.type == 'procedural':
score += node.fitness * 2.0
if node.type == 'semantic':
score += node.recurrence_count * 0.3
# File/topic overlap
overlap = len(context_files & node_files)
score += overlap * 2.0
# Confidence multiplier
score *= node.confidenceOnly the top-ranked nodes are injected:
## Relevant Graph Memory
**Recent Work:**
- [2026-04-19] Fix auth bug in login.py → success
- [2026-04-18] Add JWT validation → success
**Known Facts:**
- JWT tokens require secret validation (conf: 0.92)
- Auth middleware runs before route handlers (conf: 0.85)
**Reusable Patterns:**
- "error-read-fix-verify": bash → read → edit → bash (fitness: 0.88)
**Known Issues:**
- src/auth.py:42: Unclosed session in logout handler
**Project Style:** testing_rigor (0.78), type_safety (0.65)Budget: ~800 tokens to preserve Claude's context for reasoning.
The system is designed to never break Claude Code:
# Every hook ALWAYS exits 0
try:
# ... hook logic ...
except Exception as e:
log_error(e)
print(json.dumps({})) # Empty response
finally:
sys.exit(0) # Always succeed- Hook script error → Empty output, Claude continues
- MCP server unavailable → Retrieval returns empty context
- Database corruption → Validation warns, queries degrade gracefully
- Memory over budget → Brief is truncated with notice
Philosophy: A coding assistant with degraded memory is still useful. A coding assistant that crashes is not.
| Aspect | Traditional RAG | AINL Cortex |
|---|---|---|
| Storage | Vector DB + metadata | Typed graph nodes + edges |
| Retrieval | Embedding similarity | Graph traversal + ranking |
| Context | Flat chunks | Structured episode → fact → pattern hierarchy |
| Evolution | Static embeddings | Dynamic persona + pattern fitness |
| Causality | Implicit in timestamps | Explicit in graph edges |
| Composability | Limited (chunk boundaries) | Natural (graph operations) |
| Tool sequences | Lost in consolidation | First-class procedural nodes |
| Multi-writer | Lock contention | Inbox pattern (eventual consistency) |
- ArmaraOS Architecture: https://ainativelang.com/armaraos/blob/main/ARCHITECTURE.md
- AINL Cortex Guide: https://github.com/sbhooley/ainl-cortex
- ainl-memory crate: https://crates.io/crates/ainl-memory
- ainl-persona crate: https://crates.io/crates/ainl-persona
- ainl-runtime crate: https://crates.io/crates/ainl-runtime
These concepts are attributed to the AINL (AINativeLang) project.
This plugin is a practical demonstration of how these principles can enhance coding assistants like Claude Code.