← all posts

8 Tools in 12 Days, All Building the Same Thing

ai-assisted engineering — part 4 of 4

I stopped counting at six. It's eight now. Same architecture, same week, nobody coordinated this.


On February 7, I started building a knowledge system for my AI agent. Python script, vector embeddings, Obsidian sync. By February 12 it was working: 809 lines of code that let my agent search its own memory, generate daily briefings, and extract action items from everything it reads.

Then I watched seven other people ship nearly the same thing over the next ten days.

MemSearch on February 13. ClawVault on February 14. Rowboat on February 14. Levelsio's thread on February 15. Heinrich's 2,000-line Obsidian system, building since January, hitting traction around the same time. Then Wax on February 17 — a Swift library that runs the same architecture natively on iOS. And Supermemory on February 18 — a cloud-hosted memory API that takes the opposite approach from every local-first tool on this list.

Eight independent projects in twelve days. The wave isn't done. Tools are still shipping as I write this.

the week ai discovered its own amnesia

The catalyst was visible in real time. Levelsio, with 822,000 followers, asked how to give AI agents persistent memory. He got 143 replies. Vadim Strizheus posted the simplest answer: a memory/ folder with daily journal files and a curated MEMORY.md. Vadim's reply got 816 likes and 1,933 bookmarks.

143 replies to a single question tells you this isn't idle curiosity. It's a problem people are stuck on.

Anyone who's used AI coding assistants on real projects knows the feeling. Every session starts cold. The agent doesn't remember the bug you fixed yesterday, doesn't know your deployment target is Hetzner and not AWS, doesn't know the last three prompts you tried and why they failed. I learned the hard way that node:22-bookworm-slim doesn't include Python, 43 seconds into a Docker build. The AI learned nothing from that. Next session, same mistake.

CLAUDE.md and AGENTS.md solve part of this. They give the agent project context at session start. But they're static. They encode what you knew when you wrote them, not what the agent learned since. The memory problem is the next layer: how does accumulated knowledge persist across sessions, grow over time, and stay searchable?

the architecture everyone arrived at

Despite building independently, all eight projects gravitated toward similar decisions.

Markdown as source of truth. Not a database. Not a proprietary format. Plain .md files that humans can read, edit, and version control. MemSearch's docs say it directly: "human-readable, git-friendly, zero vendor lock-in." Rowboat stores everything as "plain Markdown on your machine, no proprietary formats." My brain.py reads and writes Obsidian-compatible markdown. Even Vadim's prompt from the Levelsio thread, the simplest implementation, uses markdown journals.

Vector embeddings for semantic search. Three of the eight use vector embeddings to make memory searchable by meaning, not just keywords. I used sentence-transformers with all-MiniLM-L6-v2, which runs locally on CPU. MemSearch offers pluggable providers. The point is the same: you need to find related memories without remembering the exact words you used.

Obsidian-compatible output. Several of the projects work with Obsidian or produce Obsidian-readable files. Mine and Heinrich's are explicitly built around it. This wasn't coordinated. Obsidian just happens to be the tool that treats markdown as a first-class knowledge format with backlinks, graph views, and plugin ecosystems.

Git-friendly, local-first. No hosted services. No cloud sync requirements. Everything lives on disk, diffs cleanly, and can be committed to a repository.

These choices solve the same constraints: AI memory needs to be human-auditable, semantically searchable, interoperable with existing tools, and version-controllable. When constraints are this clear, the solutions converge.

my implementation: brain.py

I built mine as part of my OpenClaw agent config system. 809 lines of Python, two working days, mostly co-authored with Claude.

input sources articles, retros, notes .md + yaml frontmatter obsidian wikilinks brain.py embed MiniLM-L6-v2 sqlite-vec vector database brain.py search query → context brain.py briefing cron → telegram brain.py kanban → action items knowledge-sync.sh git ↔ obsidian vault .claude/memory/ persistent learnings

The pipeline works like this: I digest industry articles and project retrospectives into structured markdown files with YAML frontmatter and Obsidian-native [[wikilinks]]. Then brain.py embed processes those files into vector embeddings using sentence-transformers (all-MiniLM-L6-v2), stored in sqlite-vec, a single-file vector database. Local, free, CPU-only. No Pinecone, no Weaviate, no running services.

brain.py search "query" returns semantically similar notes ranked by relevance. Searching "AI code quality" surfaces notes about fix-to-feature ratios, StrongDM's validation approach, and the HBR study on AI work intensification, even though none of those notes contain that exact phrase. The agent uses this to pull context before answering questions or generating content.

The part that surprised me most was the curation layer. A cron job triggers brain.py briefing every morning, which outputs structured context (old notes, recent notes, semantic connections between them) and the agent generates a daily briefing delivered as a markdown file and a Telegram message at 08:00. The first briefing connected a note about Hashimoto's agent patterns with a note about my own deployment failures that I hadn't linked myself. That felt different from search. It felt like the system was thinking. Key design decision: brain.py outputs the raw context, the agent generates the briefing. Keeps API keys out of the Python script entirely.

Then there's brain.py kanban, which parses notes for actionable items and deduplicates them semantically. Categories: tool-to-try, person-to-research, idea-to-prototype, read-deeper, reach-out. 280 lines of parsing logic, generated from a short spec document. Compatible with Obsidian's Kanban plugin.

The whole thing syncs bidirectionally with my local Obsidian vault via knowledge-sync.sh (154 lines, git-based). I edit notes in Obsidian on my laptop, the agent reads them on the server, and new agent-generated notes appear in Obsidian automatically.

the bugs they haven't hit yet

Every demo of AI memory looks clean. The happy path works. Then you deploy it to a real server with real data and spend your evening debugging.

L2 vs cosine distance. sqlite-vec uses L2 (Euclidean) distance internally, not cosine similarity. The AI assumed cosine when writing brain.py and produced score = 1.0 - distance. Search results came back nonsensical. It took me 90 minutes of testing to figure out what was wrong. The fix required a formula the AI couldn't have known without reading sqlite-vec's source: score = 1.0 - (distance ** 2 / 2.0), converting L2 to cosine for normalized vectors. MemSearch uses Milvus which handles this internally. My approach needed the math.

Dedup thresholds that don't match intuition. I set kanban dedup at 0.8 cosine similarity, thinking that was a reasonable "same item" threshold. Six minutes after shipping, I had to lower it to 0.65. MiniLM embeddings on short documents produce lower similarity scores than you'd expect. The "obvious" threshold was wrong, and you can only learn this from testing on real data.

SSH permissions in Docker. knowledge-sync.sh had setup_ssh() called inside do_init(), but do_sync() and do_loop() also need SSH for git operations. Three separate bugs across three test runs: nested directory creation, SSH key scope in the wrong function, and file conflict resolution where remote files overwrote local edits (cp -rn vs cp -r). Each fix was clean. Finding each one required a real git repo on a real VPS with real file conflicts.

OOM on chained embedding commands. Running three sequential brain.py kanban add invocations caused a SIGKILL. Loading the sentence-transformers model three times in a row exceeded container memory. Had to run them individually. Nobody documents these constraints because nobody runs their demo in a memory-constrained Docker container.

The L2 vs cosine distance bug cost me 90 minutes. It'll cost the next person 90 minutes too, unless they read this first. That's the whole point of persistent memory.

These are the same class of bugs I documented in part 1 of this series: AI generates structurally correct code that fails on contact with real environments. Memory systems are no different.

flat markdown vs knowledge graphs vs typed memory

The eight implementations span a spectrum from simple to complex.

Approach Examples Strengths Weaknesses
Flat markdown Vadim's prompt, my brain.py Simple, portable, zero setup cost No explicit relationships, retrieval depends on embeddings
Typed memory ClawVault, Supermemory Categorized storage, explicit relationships, user profiling Higher setup cost, schema decisions upfront
Knowledge graph Rowboat, Heinrich Rich relationships, Obsidian graph view, compounding connections Complexity, maintenance overhead, harder to migrate
Native embedded Wax Single-file (.mv2s), HNSW + BM25 hybrid, Metal GPU, sub-millisecond on-device Apple-only, no cross-platform, library not standalone tool

The spectrum now has a second axis: deployment target. Server CLI (brain.py, MemSearch), desktop app (Rowboat), cloud API (Supermemory), native mobile (Wax). Same architecture, four different runtimes. That's convergence at the pattern level, not just the tool level.

My take: start flat. Markdown files with vector search cover 80% of memory use cases with 20% of the complexity. Add typed categories when you find yourself searching for "that decision I made about the database" and getting back recipes and book notes. Add a knowledge graph when you need to trace relationships between ideas that don't share vocabulary. Most people don't need a graph on day one.

Heinrich's system, the most mature of the eight, supports this progression. He started with flat markdown and evolved to MOCs (Maps of Content) and vault indexes over months of refinement. His 2,000-line CLAUDE.md encodes the structure that emerged organically. As he puts it: "knowledge bases and codebases are structurally identical." Both are folders of text files with relationships and conventions. Both benefit from agents that navigate them.

memory that compounds vs retrieval that starts cold

Rowboat's framing captures an important distinction. RAG pulls documents for a single query and forgets them after. Memory builds on itself over time. Different problem, different architecture.

My daily briefings connect old notes to new ones. The kanban extraction pulls action items out of notes and tracks which ones are stale. The dedup catches when I'm researching the same topic from different angles and surfaces the connection. Each interaction with the memory changes the memory.

But compounding requires curation. Without it, your memory system becomes a write-only graveyard. I've seen it in my own system already: notes that get embedded but never surface in briefings because they're too generic, action items that go stale because nobody reviewed the kanban.

The answer turns out to be embarrassingly simple. Markdown files with daily journals. The hard part isn't the format. It's building the curation layer that keeps the knowledge alive: briefings that surface connections, dedup that catches redundancy, kanban that turns notes into action.

My briefing generation and kanban extraction are early versions of this. They're crude. The briefings sometimes surface irrelevant connections. The kanban dedup threshold needed manual tuning on day one. But they convert passive notes into active knowledge, and that's the difference between a filing cabinet and something useful.

what this changes

Eight tools in twelve days isn't a coincidence. It's a gap in the toolchain that just became obvious. By the time you read this, there will be more. That's the point — this is infrastructure the ecosystem needs, and everyone's building it simultaneously because the pain is universal.

CLAUDE.md gives the AI project context at session start. The memory systems shipping now are the next layer: cross-project knowledge that the AI carries between sessions and across repos. The pattern jumped from server CLI tools to native mobile in under two weeks. Wax runs the same vector-search-over-markdown architecture on an iPhone. Supermemory runs it as a cloud API. The abstraction is stabilizing fast.

Mitchell Hashimoto's "harness engineering" principle captures why this matters. Every mistake gets encoded into a persistent file so the AI never repeats it. That's exactly what my .claude/memory/ directory does. "sqlite-vec uses L2 distance, NOT cosine." "node:22-bookworm-slim does NOT include Python." "Volume mount shadows COPY, seed files at startup instead." Each entry cost me 30-90 minutes to learn. Each one saves that time in every future session.

The human's role shifts from context provider (telling the AI what it needs to know every session) to knowledge curator (maintaining a system the AI navigates on its own). The models will keep getting better. The edge goes to whoever builds the best knowledge base around them.

The architecture is settling. The count keeps growing. The question now is who builds the curation layer that makes it compound.


Part 4 of a series on AI-assisted engineering. Part 1: "Your AI Productivity Metric Is a Lie" covers the fix-to-feature ratio. Part 2: "The Most Important File in Your Repository" covers the instruction file pattern. Part 3: "The AI Doesn't Learn. The Prompt Evolves." covers prompt evolution.

Built in Porto. Data from real repos, real commits, real production servers.