DEV Community

Karthik Bommineni
Karthik Bommineni

Posted on

Ledger: git blame for your thinking

Make sure your agent never forgets — and make sure you can still see why you chose it.

Most “AI memory” products store facts and preferences. What I actually needed was why I decided something, what that choice rested on, and whether it still holds. Ledger is git blame aimed at yourself: not who merged the code, but which chat, which assumption, and which rejected option sat under a decision.

Ledger is a local project that turns chat exports into a graph in FalkorDB. Conversations and messages go in first. An LLM then extracts entities, claims, and decisions, each linked back to the source message. I query it from the CLI or from Cursor over stdio MCP.

The real cost is re-explaining yourself

A lot of people bounce between Cursor, Claude Code, Codex, Antigravity (being optimistic here), and the web apps — ChatGPT, Gemini, Claude. The work is one project. The memory is not. You burn tokens (and patience) pasting the same stack choices, constraints, and “we already tried X” into the next tool so it can pretend it was in the room.

One graph, behind MCP, is how those clients share decisions, not entire chat logs. Today Ledger imports history you already have. It does not yet siphon the live thread. The next version should add an MCP tool that captures the current conversation when you ask — user-gated, not a silent scraper — so the graph stays current without another takeout zip.

That live ingest is the difference between a weekend importer and a memory that actually follows you. Import still matters: it is how you stop paying to re-teach last month.

What the graph looks like

Ledger knowledge graph in the FalkorDB browser

This is a snapshot of few chats after ingest and extract.

  • Orange nodes are conversations.
  • Pink nodes are messages. A long thread becomes a star: one hub, many HAS_MESSAGE spokes. That is expected, not a bug.
  • Blue nodes are claims (facts, preferences, constraints, assumptions).
  • Decisions attach the same way, via EXTRACTED_FROM.
  • Separate blobs are different topics. Chats only join when they share an entity (same project, tool, person). Vector search can retrieve similar text. It cannot tell you which decisions are now stale; a traversal can.

Pipeline (today)

  1. Export — Claude web takeout first (conversations.json). ChatGPT, Gemini, Claude Code, Antigravity, Codex, and the rest are adapters: same JSONL contract, different parsers.
  2. Redact — emails, phones, common API-key patterns. Not a full PII system; still don’t commit data/private/ or .env.
  3. Ingest — embed each message (text-embedding-3-small via OpenRouter), write Conversation and Message nodes.
  4. Extract — Haiku reads the chat in 8-message windows, then embeddings are batched and claims are adjudicated (same / supersedes / contradicts) before they land in the graph.

I query with search, trace, and check, or I enable the ledger MCP server in Cursor and ask in a new Agent chat. Claude Desktop can attach the same server. Web ChatGPT cannot speak this local stdio MCP until there is an HTTP surface — import is how those chats enter the graph for now.

Why a graph

Staleness, “why did I pick this?”, and “forget this conversation” are walks: DEPENDS_ON, SUPERSEDES, EXTRACTED_FROM. That is the git-blame moment: you get the rationale, the options you did not take, and the message that produced them.

If you want to try it

The code is local-first: Docker for FalkorDB, OpenRouter for embeddings and extract, MCP 1.x for Cursor. Personal dumps stay gitignored.

Repo: https://github.com/karthik-bommineni/decision-memory-graph

Make sure your agent never forgets. Keep a blame trail for the decisions you would otherwise explain again.

Top comments (0)