Sprint 3 closed: Memory Engine v0
Sprint 3 is closed.
The goal was to persist CEM entities, relations, and evidence locally — so project understanding survives between sessions instead of being rebuilt from scratch every handshake. The new @meronq/memory package, CLI commands, and an upgraded handshake now read and write engineering memory in SQLite.
What Sprint 3 delivered
Every item on the roadmap is checked off:
- SQLite schema — entities, relations, evidence, access events, reinforcement events
- Entity storage — full CEM snapshot sync from scanner output
- Relation and evidence storage — foreign keys with cascade on full resync
- Access events — recorded when handshake or tools touch entities
- Reinforcement events — lifecycle signals aligned with ADR-0005
- Handshake from memory — scan refreshes SQLite; CEM returned from the store
Local MCP is now v1.12.0 with memory_sync and a memory block in handshake.
New package: @meronq/memory
Memory is the persistence layer between scanner and AI clients:
| Module | Role |
|---|---|
MemoryStore.open() |
Open .meronq/local/memory.db |
syncFromSnapshot() |
Full CEM replace from live scan |
loadSnapshot() |
Read entities, relations, evidence back as CEM |
recordAccess() |
Log which entities were touched and by whom |
recordReinforcement() |
Log lifecycle reinforcement (sync, handshake, …) |
getStats() |
Count rows for diagnostics |
Storage uses Node's built-in node:sqlite (DatabaseSync) — no native addon build step. Node ≥ 22.5 is required.
Example stats after a handshake:
entities: 21 · relations: 20 · evidence: 13 · access events: 21
Handshake pipeline: scan → sync → read
Sprint 2: handshake returned a live CEM snapshot from disk — read-only, ephemeral across process restarts in practice.
Sprint 3: handshake still scans the repo for freshness, but returns CEM from SQLite:
scanProject(root)
└── indexToCemSnapshot()
├── syncFromSnapshot() → .meronq/local/memory.db
├── loadSnapshot() → handshake.cem
├── recordAccess() → access_events
└── recordReinforcement()
The memory block in handshake confirms the source:
{
"source": "sqlite",
"path": ".meronq/local/memory.db",
"stats": { "entities": 21, "relations": 20, "evidence": 13 }
}
The scanner remains the writer (via sync). Handshake is a reader with automatic refresh.
Stable entity IDs
Sprint 3 also hardens CEM identity across environments:
- Project:
project:c:/users/.../meronq— canonical path (Windows and WSL map to the same id) - Components:
component:apps/cli,component:packages/memory - ADRs / commits: unchanged stable ids
This keeps SQLite rows consistent when the same repo is opened from different path spellings.
CLI: memory sync and stats
pnpm --filter meronq-cli build
node apps/cli/dist/index.js memory sync
node apps/cli/dist/index.js memory stats
memory sync scans the repo, saves project-index.json, and syncs CEM into SQLite. memory stats prints row counts without rescanning.
MCP tools
| Tool | Behavior |
|---|---|
handshake |
Scan → sync → load CEM from memory + record access |
project_scan |
Scan, save index, sync memory |
memory_sync |
Explicit CEM sync into SQLite |
Sprint 4 — GitHub Integration
Sprint 4 goal: connect Meronq to GitHub as the first external engineering system.
Planned work:
- Read repository metadata, issues, and pull requests
- Translate GitHub data into CEM entities and evidence
- Generate issue and PR summaries
- Draft documentation from Git history
Memory becomes the merge target for external translators — not just local scans.
Follow along
- Roadmap — sprint plan
- ADRs — architecture decisions
- Architecture — system design
- Development blog
- GitHub: Meronq-dev/meronq
Sprint 0 gave us a durable repository. Sprint 1 gave us a shared language. Sprint 2 connected that language to real projects. Sprint 3 makes it stick.