← Back to list

Stop Writing Markdown. Start Writing Memory.

Since I’ve been coding with AI, it’s always been one big blob of markdown files. It’s the default in all the agentic coding platforms for…

Aria Han · 2026-02-20 16:56 · 5 claps · 6.1 min read
#claude-code #claude-code-tips #agentic-coding #agentic-ai #context-engineering
Open on Medium ↗
Wiki topics: LLM · Large Language Models AGT · AI Agents AI · AI · General 💻 · Programming

Stop Writing Markdown. Start Writing Memory.

Since I’ve been coding with AI, it’s always been one big blob of markdown files. It’s the default in all the agentic coding platforms for “planning” mode, and widely accepted as the canonical way to plan and execute a coding task with agentic AI.

Research summaries. Architecture plans. Debug traces. Session notes. Feature requirement distillations. Each one dutifully generated by an AI agent, each one formatted for human consumption, each one completely unqueryable by the very agents that created them.

We’ve settled for a system where machines talk to machines through human-readable documents. Like passing notes in class by printing them first.

The Markdown Problem

Here’s what happens when you use AI agents to code:

You ask an agent to plan a feature. It writes feature-plan.md. Implementation happens. The plan sits there, never again referenced, slowly drifting from reality. By week three, it’s archaeological artifact.

This is the default behavior of every AI coding assistant. Generate markdown. Pile it up. Hope someone reads it.

The fundamental problem: markdown is a human-readable format being used for machine-to-machine communication. Your agents generate structured knowledge, then immediately flatten it into prose that only humans can parse efficiently.

The Inversion

What if you use a database as a communication layer?

Not storage you dump things into. Not a backup system. The actual protocol agents use to coordinate.

I rebuilt my agent workflow around a single SQLite file. Three tables: context, learnings, and errors. No markdown generation unless a human explicitly needs to read something.

I call it AgentDB. It changed everything.

The Startup Hook

Before diving into the schema, here’s what makes this actually work: the session startup hook.

When I open Claude Code, before I type anything, a hook runs. It reads from the database, from git, from the file system (whatever I’ve configured) and injects the result directly into the session context. It’s unique to each folder/repo.

Here’s a sample:

## Git State
Branch: feat/streaming-responses
Changes: M src/chat/stream.ts, M src/api/completions.ts
Recent:
 a3f7c21 feat(chat): SSE streaming for long responses
 8b2e4d9 fix(context): token count before truncation
## Active Contracts
| ID | Goal | Status |
| CR-031 | Streaming responses for messages >500 tokens | in_progress |
| CR-028 | Context window management for long threads | blocked |
## Recent Learnings
| Category | Summary |
| failure | ReadableStream must be returned, not piped |
| pattern | Chunked transfer encoding requires explicit headers |
| gotcha | Vercel edge functions have 25s timeout, not 30s |
## Recent Errors
| Tool | Error |
| Edit | File not found: src/old-path.ts |
| Bash | npm test exit code 1 |
## Active Agents
- steady-pulse (branch: feat/streaming-responses)
- quick-spark (branch: main)

The agent sees this before I say a word. It knows what changed recently. It knows what patterns I’ve discovered. It knows what errors occurred. It knows what I was working on.

This is ambient context. No retrieval step. No “let me check my notes.” The context is present before you ask.

The hook can read from anything:

  • The database (learnings, checkpoints, contracts, errors)
  • Git (branch, commits, file changes)
  • File system (folder structure, counts, specific files)
  • External APIs (if you want)

The database is the storage layer. The startup hook is the delivery layer. Together, they create ambient context that survives sessions without manual effort.

The Three Tables

Now let’s look at what gets stored.

context: The Communication Protocol

This replaced every “Hey here’s what I found” markdown file.

-- CONTEXT: Work state (ephemeral per-contract)
-- Types: contract, checkpoint, handoff, verdict

CREATE TABLE IF NOT EXISTS context (
 id TEXT PRIMARY KEY,
 ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
 type TEXT NOT NULL CHECK(type IN ('contract', 'checkpoint', 'handoff', 'verdict')),
 contract_id TEXT, - links context entries to a contract
 agent TEXT, - which agent wrote this (orchestrator, surgeon, adversary)
 content TEXT NOT NULL - JSON blob
);

When the orchestrator assigns work, it writes a contract. The surgeon reads that, writes checkpoints as it works. The adversary reads the contract, checkpoints, and the implementation to write a verdict. All context can easily be transferred to a fresh conversation with handoffs.

All through the database. All queryable. All with typed structure.

No copy-paste relay. No “let me summarize what the previous agent said.” Each agent queries what it needs and writes what the next one will need.

learnings: Knowledge That Compounds

This is where the markdown graveyard problem gets solved.

-- LEARNINGS: Cross-session memory (survives forever)
-- Read these at session start to avoid repeating mistakes

CREATE TABLE IF NOT EXISTS learnings (
 id TEXT PRIMARY KEY,
 ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
 type TEXT NOT NULL CHECK(type IN ('failure', 'pattern', 'gotcha', 'preference')),
 insight TEXT NOT NULL,
 evidence TEXT,
 domain TEXT, - e.g., 'auth', 'database', 'frontend'
 hit_count INTEGER DEFAULT 0,
 last_hit TEXT
);

When an agent discovers something (API returns 500 on expired tokens; this library doesn’t handle concurrent requests; always validate before calling that endpoint) it writes a learning. Typed. Categorized. Queryable.

Recent failures surface automatically at session start. Patterns with high hit counts stay visible. Gotchas resurface before you hit them again.

errors: Automatic Failure Capture

This one is different. Agents don’t write to it directly; hooks do.

I can assign Claude Code the most complex, architecture-spanning task, and the biggest issues won’t be the logic. They’ll be: figuring out how to cd into the right directory, reading a file that moved, calling an MCP tool with the wrong parameters.

These tool errors seem harmless. They compound. One failed Edit leads to a retry, which leads to a different approach, which leads to confusion about what state the file is in. An hour later, you’re debugging the debug session.

-- ERRORS: Automatic capture of failures
CREATE TABLE IF NOT EXISTS errors (
 id INTEGER PRIMARY KEY AUTOINCREMENT,
 ts TEXT NOT NULL DEFAULT (strftime('%Y-%m-%dT%H:%M:%fZ','now')),
 tool TEXT NOT NULL,
 error TEXT NOT NULL,
 file TEXT,
 context TEXT
);

This table captures tool failures automatically via hooks. Edit can’t find a file? Logged. Bash returns non-zero? Logged. MCP server returns an error? Logged. The agent doesn’t decide whether to record it. The system does.

Next session, the startup hook surfaces recent errors: “Edit failed on src/old-path.ts.” The agent immediately knows that file moved or was deleted. It doesn’t waste twenty minutes trying the same path.

Learnings are intentional: “I discovered this pattern.” Errors are automatic: “this tool call broke.” Both matter. Only one requires the agent to remember to write it down.

The Agent Lifecycle

With the schema in place, here’s how agents actually use it. I made a lightweight Python CLI so they don’t have to deal with SQL syntax.

Session start:

agentdb read-start

The agent gets: recent failures to avoid, active patterns, the last checkpoint, any active contract, recent errors. One command. Everything needed to resume.

During work, when something is learned:

agentdb learn failure "Stripe webhook returns 200 but event.verified is false" "found in logs"
agentdb learn pattern "always check verified flag explicitly" "fixed 3 bugs"

Session end:

agentdb write-end '{"did":"implemented webhook handler","next":"add retry logic","blocked":""}'

This writes a checkpoint. Tomorrow, read-start shows exactly where things left off.

Before complex work:

agentdb contract '{"goal":"timeout message after 5s","scope":["src/auth/login.ts"],"constraints":["no new deps"]}'

You don’t run these commands. The agents do. The hooks are non-negotiable: every artifact reads on start, writes on end.

The Point

Here’s the bigger insight:

Representation is the bottleneck.

Not intelligence. Not scale. Not model size. How you structure information for machine consumption determines what machines can do with it.

Markdown is optimized for human eyes. Great for documentation you’ll read. Terrible for knowledge agents need to query.

SQL is optimized for structured retrieval. Terrible for prose. Perfect for typed knowledge with categories, timestamps, hit counts, relationships.

The endless markdown files weren’t a storage problem. They were a representation problem. Information structured for the wrong consumer.

When I switched to SQLite, I didn’t add capabilities. I removed friction. Agents could suddenly query exactly what they needed instead of scanning documents hoping to find relevant passages.

This generalizes beyond agent coordination. Every time you’re tempted to generate a markdown report, ask: who consumes this? If it’s another machine, the answer probably isn’t prose.

Where This Goes

The database becomes the source of truth. Markdown becomes a rendering layer: something you generate FROM the database when humans need to read it, not something you store.

Session summaries? Query the context table, format as prose.

What did we learn this week? Query learnings, render as bullet points.

What’s the status of this project? Query context for active contracts, generate markdown.

The inversion: markdown is output, not storage. The database is storage. Machines talk to machines through structured queries. Humans get rendered views when they ask.

This is where agentic systems are heading. Not smarter models generating better prose. Smarter architectures where machines communicate in formats optimized for machines.

One SQLite file. Three tables. Zero rotting markdown.

AgentDB is part of the Kernel Claude Code plugin for self-evolving configuration and multi-agent coordination. It comes with the AgentDB and all the agents, commands, hooks, and more detailed in this article.

[embed]GitHub - ariaxhan/kernel-claude: KERNEL is a Claude Code plugin that makes your setup evolve… KERNEL is a Claude Code plugin that makes your setup evolve automatically based on how you actually work. …github.com

Taking on consulting projects for custom Claude Code/agentic coding architecture. Also happy to trade notes if you’re deep in this space. Reach out either way.

[embed]Aria Han | AI Systems Architect AI systems architect. Writer. Systems thinker. Three companies, six hackathon wins, 3,300+ hours building production AI…ariaxhan.com


메타데이터
post_id
e4a69c57caa9
slug
stop-writing-markdown-start-writing-memory-e4a69c57caa9
url
https://medium.com/@ariaxhan/stop-writing-markdown-start-writing-memory-e4a69c57caa9
canonical_url
https://medium.com/@ariaxhan/stop-writing-markdown-start-writing-memory-e4a69c57caa9
author_url
https://medium.com/@ariaxhan
status
ok
fetched_at
2026-06-17 08:20:12