Lár DMN — The Memory Layer for the Snath AI Cognitive Architecture
Every deployed AI system eventually encounters situations its training didn't anticipate — sensor drift, physics assumption violations, distribution shift, edge cases at the boundary of known failure modes. Standard practice discards these high-divergence events. The DMN accumulates them.
DMN solves adaptation architecturally. No weight updates to the base model. No full retraining loops. Hard cases accumulate at runtime, consolidate overnight into HMAC-signed failure-class centroids and LoRA adapters, and correct inference forward — without touching the base weights.
This repo is a blueprint — two abstract base classes that define how any domain accumulates high-divergence events, consolidates them into signed adapters, and applies them at inference time. Clone it, extend it, and the OS-level promise holds by construction.
DMN is the memory layer of a three-part cognitive architecture:
| Repository | Role |
|---|---|
| Lár | The execution spine — deterministic graph engine, HMAC audit trail, 20 EU AI Act compliance primitives |
| Lár-JEPA | The world model — 10 ABCs spanning the full inference-time contract (divergence routing, modal encoding, fault localisation, adapter routing) |
| Lár DMN ← you are here | The memory blueprint — AbstractDMN + AbstractAdapterRouter; domain implementations own storage and LoRA fitting; zero runtime dependencies |
The industry is building the Brain (LLMs, JEPAs). We are building the Nervous System.
from brain.abstract_dmn import AbstractDMN
class AbstractDMN(ABC):
def ingest(self, event) -> None: ... # Tier 1: accept event, non-blocking
def consolidate(self, **kwargs) -> List[dict]: ... # Tier 2/3: D_hard → signed adapters
def recall(self, query, **kwargs) -> Any: ... # Tier 2: retrieve context, silent on miss
def stats(self) -> dict: ... # queue / memory introspectionThree invariants that every implementation must satisfy:
| Invariant | Rule |
|---|---|
| D1 — Non-blocking ingest | ingest() must never raise. Catch and log. |
| D2 — Signed artifacts | Every durable artifact from consolidate() carries hmac_hex. |
| D3 — Silent recall | recall() returns None / "" / {} on miss. Never raises. |
from brain.abstract_adapter_router import AbstractAdapterRouter
class AbstractAdapterRouter(ABC):
def _load_all(self) -> None: ... # load + HMAC-verify all centroid adapters
def _nearest(self, delta) -> Optional[Any]: ... # System 1: trust-invariant centroid match
def resolve(self, z_a, z_b, base_decision,
conf_a, conf_b, enc_a=None, enc_b=None) -> Tuple[Any, str]: ...
def available(self) -> List[str]: ...
def refresh(self) -> None: ... # concrete — calls _load_all()
@staticmethod
def decay_weight(created_at_iso, lam=0.10) -> float: ... # W = exp(-λ·Δt)_nearest() is trust-invariant — it fires regardless of adapter age. Failure-class geometry is durable: "ice causes pitot freeze" clusters identically across sensor generations. The temporal gate W = exp(-λ·Δt) lives exclusively in resolve(), at the .pt loading step.
When W < min_trust, System 1 still identifies and commits. System 2 correction is withheld. Identify correctly, correct conservatively.
Proved in The Encoder Is Not the Memory (EIM, Sajeev 2026, DOI 10.5281/zenodo.20614051) — see Papers & Research.
Together with AbstractModalEncoder and AbstractDivergenceRouter from Lár-JEPA, the full loop is formally contracted:
AbstractModalEncoder → encode z_a, z_b
AbstractDivergenceRouter → V1–V6 routing decision
AbstractDMN → ingest → consolidate → recall
AbstractAdapterRouter → resolve → (decision, audit_note)
Any new domain implements four classes and the OS-level promise holds by construction.
All five DMN implementations satisfy AbstractDMN. All four domain adapter routers satisfy AbstractAdapterRouter:
| Project | DMN class | AdapterRouter class | Domain |
|---|---|---|---|
| Snath Robotics | RoboticsDMN |
RoboticsAdapterRouter |
Dual-stream sensor fusion (vision + proprioception) |
| Snath Aviation | AviationDMN |
AviationAdapterRouter |
Flight anomaly detection (pitot / radar) |
| Snath Basis | BasisDMN |
BasisAdapterRouter |
Factor-model divergence (fundamentals / market) |
| Snath Research | ResearchDMN |
ResearchAdapterRouter |
Paper review routing (claims / reviews) |
Each domain owns its HMAC key, λ-table, and centroid field names. AbstractDMN and AbstractAdapterRouter own the structural guarantee.
Human brains don't rewrite neural weights every night. The Hippocampus consolidates the day's experiences into long-term cortical storage during sleep. Raw sensory data is gone by morning; the meaning persists.
DMN implements this exact strategy as software:
| Human Brain | Lár DMN |
|---|---|
| Sensory Input | D_hard events / domain observations |
| Hippocampal Consolidation (Sleep) | consolidate() — D_hard queue → signed adapters |
| Long-Term Cortical Storage | Tier 2 semantic centroids + Tier 3 LoRA .pt adapters |
| Working Memory | Tier 1 episodic queue |
| Synaptic Depression | W = exp(-λ·Δt) — stale adapters refused at inference |
| Tier | Contents | Lifetime | Written by |
|---|---|---|---|
| Tier 1 — Episodic | D_hard events (divergence vectors, failure labels) | Perishable | ingest() only |
| Tier 2 — Semantic | HMAC-signed centroid store — geometry-stable | Durable | consolidate() only |
| Tier 3 — Procedural | HMAC-signed LoRA .pt adapters |
Perishable (time-gated by W) | consolidate() only |
Write direction is strictly upward: Tier 1 → Tier 2 → Tier 3. recall() reads from Tier 2. AdapterRouter.resolve() reads from Tier 2 (System 1) and Tier 3 (System 2).
The Tier 2 storage format is an implementation choice — not mandated by the contract:
- Fixed failure-class vocabulary (Robotics, Aviation, Basis, Research): flat-file JSON centroids per class, HMAC-signed before write
- Open-vocabulary / semantic search: ANN index or vector collection, HMAC-signed on journal entries
Not every domain reaches Tier 3. Domains with a fixed failure-class vocabulary go all the way to signed LoRA .pt adapters. Open-vocabulary domains consolidate into Tier 2 only.
from brain.abstract_dmn import AbstractDMN
from brain.abstract_adapter_router import AbstractAdapterRouter
class MyDMN(AbstractDMN):
def ingest(self, event) -> None:
try:
self.queue.push(event) # Tier 1: non-blocking write
except Exception as e:
print(f"[DMN] ingest failed: {e}")
def consolidate(self, **kwargs) -> List[dict]:
# pull resolved D_hard events, build signed centroids + LoRA adapters
...
def recall(self, query, **kwargs) -> Any:
return self.load_centroid(query) # Tier 2: silent on miss
class MyAdapterRouter(AbstractAdapterRouter):
def _load_all(self) -> None:
# load + HMAC-verify *.json centroid adapters
...
def _nearest(self, delta) -> Optional[dict]:
# cosine similarity match — NO temporal gate here
...
def resolve(self, z_a, z_b, base_decision, conf_a, conf_b,
enc_a=None, enc_b=None):
# System 1 centroid match → System 2 LoRA injection if W ≥ min_trust
...
def available(self) -> List[str]:
return [c["failure_class"] for c in self._centroids]consolidate() is the heart of the continual learning contract. Here is the full blueprint — the same pattern used across all Snath domain implementations.
- Pull resolved D_hard events from the queue (events labelled with a winner stream)
- Group events by
failure_class - For each class with enough events: build a System 1 centroid (JSON) and a System 2 LoRA adapter (
.pt) - HMAC-sign both artifacts before writing to disk
import hashlib, hmac, json
from pathlib import Path
ADAPTER_KEY = b"your-domain-hmac-secret" # keep per-domain, never commit
def build_centroid(failure_class, group, adapter_dir):
dim = len(group[0].z_a)
centroid_a = [sum(e.z_a[i] for e in group) / len(group) for i in range(dim)]
centroid_b = [sum(e.z_b[i] for e in group) / len(group) for i in range(dim)]
payload = {
"failure_class": failure_class,
"centroid_a": centroid_a,
"centroid_b": centroid_b,
"n_events": len(group),
}
sig = hmac.new(
ADAPTER_KEY,
json.dumps(payload, sort_keys=True).encode(),
hashlib.sha256,
).hexdigest()
payload["hmac_hex"] = sig
Path(adapter_dir, f"{failure_class}.json").write_text(json.dumps(payload, indent=2))import torch, torch.nn as nn, torch.optim as optim
def build_lora(failure_class, group, winner_stream, adapter_dir,
n_epochs=100, lr=0.01):
target = torch.tensor([e.z_a if winner_stream == "a" else e.z_b
for e in group], dtype=torch.float32)
faulty = torch.tensor([e.z_b if winner_stream == "a" else e.z_a
for e in group], dtype=torch.float32)
dim = faulty.shape[1]
A = nn.Parameter(torch.randn(dim, 1) * 0.01)
B = nn.Parameter(torch.randn(1, dim) * 0.01)
opt = optim.AdamW([A, B], lr=lr)
for _ in range(n_epochs):
opt.zero_grad()
loss = nn.functional.l1_loss(faulty + (faulty @ A) @ B, target)
loss.backward()
opt.step()
a_hex = hashlib.sha256(A.detach().numpy().tobytes()).hexdigest()[:16]
b_hex = hashlib.sha256(B.detach().numpy().tobytes()).hexdigest()[:16]
sig = hmac.new(
ADAPTER_KEY,
f"{failure_class}|{winner_stream}|{a_hex}|{b_hex}".encode(),
hashlib.sha256,
).hexdigest()
torch.save({
"A": A.detach(), "B": B.detach(),
"failure_class": failure_class, "winner_stream": winner_stream,
"n_events": len(group), "final_loss": round(float(loss), 6),
"hmac_hex": sig,
}, str(Path(adapter_dir) / f"{failure_class}.pt"))MIN_EVENTS = 3 # tune per domain — higher for noisier sensors
for failure_class, group in by_class.items():
if len(group) < MIN_EVENTS:
continue
build_centroid(failure_class, group, adapter_dir)
build_lora(failure_class, group, winner_stream, adapter_dir)def _load_all(self):
self._centroids = []
for path in Path(self.adapter_dir).glob("*.json"):
data = json.loads(path.read_text())
sig = data.pop("hmac_hex")
expected = hmac.new(
ADAPTER_KEY,
json.dumps(data, sort_keys=True).encode(),
hashlib.sha256,
).hexdigest()
if hmac.compare_digest(sig, expected):
data["hmac_hex"] = sig
self._centroids.append(data)
# silently skip tampered or unsigned adaptersSee snath-robotics for a complete working implementation: dual-stream sensor fusion, full consolidate() with centroid + LoRA training, HMAC signing, and RoboticsAdapterRouter with System 1/2 resolution.
consolidate() is designed to run off the hot path — typically in a nightly or post-session background process. The pattern is the same across all domain implementations:
dmn = YourDMN(queue_path="d_hard.jsonl", adapter_dir="models/adapters")
# run after a session or on a cron schedule
built = dmn.consolidate()
print(f"Built {len(built)} adapter(s).")When to call it is the domain's responsibility — the contract only guarantees that consolidate() reads resolved D_hard events, produces HMAC-signed artifacts, and is safe to call repeatedly.
DMN is the memory layer for Lár-JEPA. After a COMMIT_TRAJECTORY routing decision, the JEPA world model writes D_hard events into the domain's AbstractDMN implementation via ingest(). Overnight, consolidate() clusters those events into signed failure-class centroids and LoRA adapters. At the next planning cycle, recall() and AdapterRouter.resolve() apply the learned corrections without retraining the encoder.
The integration bridge lives in Lár-JEPA (not this repo). Clone snath-ai/Lar-JEPA and see dmn/ for the consolidation node that connects the two repos.
The formal foundations of the Lár DMN blueprint. All papers are published open-access on Zenodo (Sajeev 2026):
| Paper | Short name | DOI | What it establishes for DMN |
|---|---|---|---|
| Divergence Is Not Noise (Sajeev 2026) | DAS | 10.5281/zenodo.20278781 | The routing signal detects hard cases better than fusion — proves that D_hard events are a valid, information-rich curriculum rather than noise to be discarded |
| Universal Cognitive Routing (Sajeev 2026) | UCR | 10.5281/zenodo.20278775 | The V1–V7 AbstractDivergenceRouter contract is domain-universal across 7 verticals — proves that the same AbstractDMN / AbstractAdapterRouter spine applies without modification across fields |
| The Lár Training Loop (Sajeev 2026) | LTL | 10.5281/zenodo.20581128 | Routing divergence flags are gradient signals — the formal basis for annotation-free continual learning via ingest → consolidate |
| The Encoder Is Not the Memory (Sajeev 2026) | EIM | 10.5281/zenodo.20614051 | V7 (Difficulty Invariance): D_hard centroid geometry is world-grounded and persists across encoder upgrades — the formal proof that _nearest() carries no temporal gate |
| Physics Assumption Violations (Sajeev 2026) | PAV | 10.5281/zenodo.20682615 | First physical-world validation of the DMN stack — D_hard events → consolidate() → LoRA adapters reduce divergence 65% overnight on MuJoCo Walker2d; one robot's physical surprise becomes fleet-wide knowledge by morning |
The AbstractAdapterRouter trust-invariant design (_nearest() fires regardless of adapter age) is a direct consequence of EIM/V7: because D_hard geometry is world-grounded, identification does not decay — only correction does.
Lár DMN is structurally designed to support EU AI Act compliance for high-risk systems:
- Article 15 (Robustness): Proven correction geometry is deterministically recalled, eliminating stochastic drift. Stale adapters are refused via
W = exp(-λ·Δt)before injection. - Article 12 (Record-Keeping): Every durable artifact from
consolidate()is HMAC-SHA256 signed.AbstractAdapterRouterverifies before trusting. The background daemon invokesconsolidate()via theAbstractDMNcontract — no bypassing the signed artifact pipeline.
See EU_AI_ACT_COMPLIANCE.md for full architectural details.
Apache 2.0. Built on the Lár Engine.
