> your AI agent picks dependencies from memory; give it dated facts — try starlog.dev ↗ vet your agent's deps ↗ vibe-coding is fine. vibe-importing isn’t. — try starlog.dev ↗ vibe-importing isn’t fine ↗ your agent has never seen your private packages — try starlog.dev ↗ facts for private packages ↗ a linter for the dependencies your AI agent picks — try starlog.dev ↗ a linter for agent deps ↗

Back to Articles

mex: How Append-Only Logs and Routing Tables Solve AI Agent Amnesia

[ View on GitHub ]

mex: How Append-Only Logs and Routing Tables Solve AI Agent Amnesia

Hook

Your AI agent rewrites the same bug every week because it forgot last Tuesday's architectural decision. mex fixes this with append-only JSONL logs and a routing table that cuts context tokens by 60%.

Context

AI coding agents like Claude, Cursor, and GitHub Copilot have a memory problem. Every new chat session starts from scratch. You explain your testing conventions, architecture boundaries, or deployment quirks—then three days later, you're typing the same instructions again because the agent's context window reset. The standard workaround is stuffing everything into CLAUDE.md or .cursorrules files, but these monolithic instruction dumps create new problems: they burn tokens on irrelevant context (why load database schema when fixing CSS?), go stale within weeks as code evolves, and offer no temporal view of why decisions changed.

Vector databases and RAG pipelines solve part of this—semantic search over past conversations—but they require embedding infrastructure, cost API calls on every query, and bury decisions in opaque vector spaces instead of human-readable files. mex takes a radically simpler approach: treat agent memory like a filesystem with routing tables, drift detection, and append-only logs. It generates a structured markdown scaffold in .mex/ that agents load conditionally based on task type, runs zero-LLM validators to catch when docs desync from code, and maintains a JSONL decision log that survives refactors. The result is persistent memory that works offline, commits to git, and doesn't require you to run a vector database.

Technical Insight

Memory Layer

Failures Detected

Routes to

References

CLI Entry Point

Setup Command

Check Command

Sync Command

Filesystem & AST Scanner

Project Brief JSON

LLM Converter

.mex/ Markdown Files

ROUTER.md

Context Files

decisions.jsonl

11 Drift Validators

Path Existence

Dependency Diff

Staleness Score

Drift Score Report

Repair Prompt Builder

Configured AI Tool

System architecture — auto-generated

mex's architecture separates three concerns that most agent tools conflate: memory scaffolding, drift detection, and repair orchestration. The scaffold lives in .mex/ as plain markdown files with YAML frontmatter. Instead of one giant instruction file, you get ROUTER.md—a decision tree that points agents to relevant context based on task type. Here's the routing structure:

---
type: router
version: 1.0
---

# Task Routing

## Frontend Changes
- context/stack.md (React + Vite conventions)
- patterns/component-structure.md
- decisions.jsonl (last 10 UI decisions)

## API Development
- context/stack.md (Express + PostgreSQL)
- patterns/error-handling.md
- context/database-schema.md

## Testing
- patterns/testing-strategy.md
- context/ci-pipeline.md

Agents load a 50-line anchor file that references ROUTER.md, which dynamically pulls 5-10 relevant files per task instead of dumping the entire scaffold. The YAML frontmatter enforces metadata contracts—each context file declares dependencies, staleness thresholds, and related files—which the drift detectors validate.

Drift detection runs 11 synchronous validators without touching an LLM. The path checker scans frontmatter references and verifies files exist. The dependency validator diffs package.json against context/stack.md to catch when you add libraries but don't update docs. The staleness scorer parses git log to flag files untouched for 90+ days. Here's the validator interface:

interface DriftChecker {
  name: string;
  run(scaffold: ScaffoldState): CheckResult;
}

interface CheckResult {
  passed: boolean;
  errors: string[];
  warnings: string[];
  deductions: number; // from 100-point budget
}

Each checker returns file-level errors and a point deduction (10 for errors, 3 for warnings). If total drift exceeds 20 points, mex sync kicks in. Sync doesn't dump the entire validation report to the LLM—it groups errors by checker type and generates targeted repair prompts. For example, if the dependency checker finds three missing libraries in stack.md, sync builds this prompt:

The following dependencies are in package.json but missing from context/stack.md:
- zod (^3.22.0) 
- drizzle-orm (^0.29.0)
- @t3-oss/env-core (^0.7.1)

Update context/stack.md to document these libraries and their purpose.

The LLM edits the file, mex re-runs validators, and if drift score drops below threshold, sync exits. If the fix fails (maybe the LLM hallucinates invalid YAML), sync retries with the same prompt—there's no conflict resolution beyond manual intervention after 2-3 attempts.

The most novel piece is the append-only decision log. Instead of embedding past conversations in a vector store, mex writes timestamped JSONL entries that agents tail for recent context:

{"timestamp":"2024-01-15T10:30:00Z","type":"architecture","decision":"Switched from REST to tRPC for type safety","files":["src/server/api/"]}
{"timestamp":"2024-01-16T14:20:00Z","type":"testing","decision":"Vitest replaces Jest for ESM support","files":["vitest.config.ts"]}

Agents run mex log --last 10 to get recent decisions without re-reading all markdown files. This gives temporal memory—you can trace why a pattern changed over time—and survives git rebases because JSONL is append-only.

Setup uses a two-phase bootstrap. First, a deterministic scanner extracts codebase structure (package.json, tsconfig, file counts, detected frameworks) into structured JSON. That brief becomes an LLM prompt to populate the scaffold, separating static analysis from generative work. The initial prompt is reproducible—same codebase generates same brief—so scaffold regeneration is deterministic.

Agent memory mode extends this for operational environments. Instead of tracking code drift, it tracks heartbeat contracts and uptime metrics for persistent agents managing infrastructure. Staleness detection swaps to 24-hour heartbeat windows, and memory cleanup thresholds prune old JSONL entries. It's the same routing and logging primitives, reframed for homelabs running agents that deploy services or monitor systems.

Gotcha

mex assumes a single project root with one .mex/ directory. If you're in a monorepo with shared services, you're either duplicating scaffolds per package or manually merging routing tables—the CLI doesn't understand workspace boundaries. Multi-repo setups are worse: there's no way to reference external context files or share routing logic across repositories.

Drift scoring is hardcoded and arbitrary. The 100-point budget and per-checker deductions (10 for errors, 3 for warnings) work for the author's workflow but might not match yours. If you want to weight dependency drift higher than staleness, you're editing TypeScript validators. There's no config file for tuning thresholds per project. The staleness detector is especially naive—it flags files untouched for 90 days even if the underlying code hasn't changed, forcing manual reviews of perfectly accurate docs. The sync repair loop has no rollback mechanism. If an LLM breaks YAML frontmatter during a fix attempt, mex edits in-place and you're manually diffing git commits to recover. There's no transactional updates or mex undo command.

Verdict

Use mex if you're a solo developer or small team repeatedly briefing AI agents on the same codebase, tired of re-explaining conventions every session, and want agents to self-serve from versioned memory that commits to git. It's especially compelling for homelabbers running persistent infrastructure agents—agent memory mode with heartbeat contracts is a genuinely novel fit for operational memory. Skip it if you're working in a monorepo (routing breaks), running agents on ephemeral tasks where setup overhead isn't justified, need fine-grained control over drift scoring (hardcoded weights will frustrate you), or already have RAG infrastructure where semantic search over embedded conversations is more valuable than human-readable markdown scaffolds.