How I architected a Zettelkasten-powered knowledge graph as a Claude Skill — memory management, node creation, session flow, and the decisions behind each.
I've been building personal knowledge management systems for years, trying to solve a specific problem: losing context, losing ideas, and losing the thread of my own thinking across tools and time. MemSpren is my attempt to solve that properly — using Claude as the intelligence layer and Obsidian as the storage layer.
The name is a nod to Brandon Sanderson's Stormlight Archive, where spren are spirits of concepts that have come to life and bond with a human to guide them. That felt right.
If you want to understand the deeper reasoning behind why I built this — the mindset, the frustrations, the years of failed PKM experiments, and the philosophy underneath the architecture — I've written that on Substack. That's the "why." This article is the "how."
What follows is a technical breakdown of how the V1 system is structured: how it manages memory across sessions, how it creates and links nodes, how the session startup sequence works, and what architectural decisions I made and why.
Prerequisites
MemSpren requires Claude Cowork, not Claude.ai chat or Claude Code. Cowork mounts a real folder from your local filesystem into the sandboxed environment. When the skill asks for your Obsidian vault path during setup, that folder must already be mounted in Cowork. Without this, the skill cannot read or write to your vault.
The implementation is file-based — no external databases, no cloud infrastructure beyond Cowork itself. That's a deliberate choice: auditable in a text editor, runnable without extra accounts, forkable by anyone.
Why Native Claude Memory Isn't Enough
Claude now has a built-in memory feature. As of early 2026, it's available to all users — free and paid. It generates summaries from your conversation history, updates every 24 hours, and gives Claude some continuity between sessions. There's also chat search, which uses RAG to pull relevant context from past conversations when you ask for it.
That's genuinely useful. But it's not what MemSpren is doing, and the difference matters.
Claude's native memory is generalized and unstructured. It summarizes your conversations into a broad synthesis — helpful for not re-explaining your general context, but it has no concept of typed entities. It doesn't know the difference between a project and a person. It can't tell you which of your ideas are linked to which projects. It can't load a specific protocol when you say you want to do a check-in. It can't track that a particular task has been idle for 10 days. It doesn't maintain a graph.
What MemSpren builds is structured, typed, and programmable memory:
- Hot memory with a hard token cap that forces prioritization of what matters right now
- A graph index that tracks every node and its connections without loading every file
- Protocol files that define exactly how Claude should behave for specific intents
- Entity files with typed frontmatter that make the vault queryable from metadata alone
- A session startup sequence that loads different context depending on what you're trying to do
Native memory solves "I have to re-explain myself." MemSpren solves something harder: how do you build a knowledge graph that gets smarter over time, with surgical memory loading, typed nodes, and evolvable behavioral protocols? That's a different problem. The architecture below is the answer.
Two Separate File Locations
The system uses two distinct locations. Mixing them up breaks things.
# Location 1: Skill operational files
# Dot-prefix hides this from Obsidian's graph view
.second-brain/
├── config.md ← static system settings
└── Memory/
├── hot-memory.md ← always loaded each session
└── system-state.md ← active protocols, flags, graph index
# Location 2: Vault content
# Lives at vault_path defined in config.md
# This is what Obsidian renders
vault/
├── Vision/
├── Strategy/
├── Work/
│ ├── Projects/
│ └── Ideas/
├── People/
├── Tasks/
│ └── tasks-inbox.md
├── Log/
│ ├── Daily/
│ └── Weekly/
├── Notes/
│ ├── Learnings/
│ └── Resources/
├── Archive/
└── Inbox/
.second-brain/ is Claude's operational layer — config, memory files, protocol files. These should never appear in the Obsidian graph. The dot-prefix keeps them hidden from Obsidian by default.
vault_path is where all content lives — everything Obsidian renders and displays. This is what grows over time. MemSpren writes every note, project file, and daily log into this location.
If vault_path points to the same root as the mounted folder, the two locations coexist in the same directory. That's fine — .second-brain/ is still separate and still hidden.
The Memory Architecture
This is the core architectural problem in any AI-native knowledge system: you cannot load everything into every session, but you need Claude to know enough to be useful from the moment the conversation starts.
The solution is a three-tier loading strategy.
Tier 1: Hot Memory
hot-memory.md is loaded every single session without exception. It is capped at approximately 800 tokens — a deliberate hard constraint. The cap forces prioritization: only what matters right now belongs here. What falls outside the cap belongs in long-term storage.
What lives in hot memory:
- Currently active projects (name, status, immediate next action)
- This week's tasks and commitments
- Friction points or blockers flagged in recent check-ins
- Any patterns Claude has been tracking
- Current journaling context summary
The daily sync refreshes hot memory at the end of each check-in, based on what was captured during the session.
Tier 1: System State
system-state.md is the operational counterpart to hot memory. Where hot memory is contextual (what are we working on), system state is operational (how is the skill configured to behave right now).
What lives in system state:
- Which protocol files are active (lifestyle tracking on/off, journaling enabled, etc.)
- A graph index — a compact summary of what nodes exist and how they connect, without the full file content
- Inbox sorting rules
- Any behavioral flags set by the user in previous sessions
The graph index in system state is what lets Claude answer "what do I have on X?" without loading every vault file. It's a lightweight map of the graph, not the graph itself.
Tier 2: On-Demand Loading
Protocol files and entity files are only loaded when the current conversation intent requires them. The protocol files define how Claude behaves for specific actions (check-ins, entity creation, linking). Entity files contain the actual content of a specific node.
A key rule in SKILL.md: when loading an entity file is warranted, Claude reads the YAML frontmatter first. Only if the conversation requires deeper context does it read the full body. This matters at scale — a vault with hundreds of files should not require reading hundreds of full file bodies to answer a simple question.
Tier 3: The Full Vault
Historical log entries, archived notes, completed project files — these exist in the vault but are never loaded in bulk. They're accessible if specifically needed, but the system is designed so that the vast majority of sessions never need to touch them.
The Session Startup Sequence
Every session follows the same startup sequence, defined in SKILL.md.
A few things worth noting in this flow:
Config is always read first. config.md contains the vault path and the setup_complete flag. Without the vault path, the skill doesn't know where to write anything. Without the setup flag, it doesn't know whether to run the setup flow or skip to normal operation.
Intent detection determines what gets loaded next. MemSpren doesn't load everything upfront and then figure out what to do. It loads the minimum (config + hot memory + system state), detects intent, and only then loads the protocol files relevant to that intent. A session where the user just asks "what am I working on" never loads a single protocol file.
Protocol files are loaded by section, not in full. When creating a project, only the Project section of entity-protocol.md gets read. The Idea and Person sections don't load. This is enforced in the intent detection table in SKILL.md.
The Node System
Every piece of information in the vault is a node. Projects, ideas, people, tasks, daily logs, learnings — all of them follow the same structural rules.
Node Types and Locations
| Node Type | Folder | Filename Pattern | Notes |
|---|---|---|---|
| Project | Work/Projects/ |
project-name.md |
New file per project |
| Idea | Work/Ideas/ |
idea-name.md |
New file per idea |
| Person | People/ |
firstname-lastname.md |
New file per person |
| Task | Tasks/tasks-inbox.md |
— | Appended, not a new file |
| Learning | Notes/Learnings/ |
learning-topic.md |
New file per learning |
| Daily Log | Log/Daily/ |
YYYY-MM-DD.md |
One per day |
Tasks are the exception — they don't get their own files. Everything goes into tasks-inbox.md as an appended entry using Obsidian Tasks plugin syntax. This keeps the task layer flat and queryable without spawning hundreds of individual files.
YAML Frontmatter — Non-Negotiable on Every File
Every node gets YAML frontmatter at creation. No exceptions. The reason: retrofitting metadata onto hundreds of notes later is painful. Starting from day one means the graph is always queryable from metadata alone, even as it scales.
---
node_type: project # project | idea | person | task | learning | log
status: active # active | idle | archived | completed
created: 2026-03-01
updated: 2026-03-01
connected:
- "[[Work/Ideas/progressive-disclosure-idea]]"
- "[[People/niklas-luhmann]]"
tags: [second-brain, architecture, claude-skills]
---
The connected field mirrors the [[links]] written in the body. This redundancy is intentional. System state's graph index is built from frontmatter, not from parsing body content. Claude can understand the shape of your entire graph by scanning frontmatter alone — without reading thousands of full note bodies.
The No-Orphan-Notes Rule
Every new note must contain at least one [[link]] to an existing node before it gets saved. If there is no existing node to link to, Claude creates the minimal stub for that linked entity first, then saves the original note with a link to it.
This is enforced in linking-protocol.md and is referenced as a hard invariant in SKILL.md. The rule exists because the value of the system is in the connections — a vault full of disconnected notes is just a graveyard. The no-orphan rule is what ensures the graph actually builds over time.
Atomization — One Idea Per Note
When a brain dump contains multiple ideas, Claude doesn't create one note with everything in it. It creates multiple atomic notes, one per idea, each linked to the others and to relevant existing nodes.
This is the Zettelkasten principle applied directly: small, discrete, highly-connected notes are more valuable than large, comprehensive notes that try to contain everything. A note with five [[links]] to existing nodes is more useful than a perfectly organized note with no links.
The atomization happens during the check-in. The user talks in streams. Claude writes in atoms.
The Check-In Flow
The check-in is the primary interface of the system. Everything else is infrastructure in service of this interaction.
The check-in protocol defines exactly what Claude should extract from the brain dump: which entities to create, which existing nodes to update, what goes into tasks-inbox, and what belongs in the daily log vs a separate entity file.
Claude follows up only on gaps that weren't naturally covered — not a read-out-loud checklist. The distinction matters for sustainability. A conversational check-in takes 5–10 minutes and is easy to maintain daily. A structured form with required fields is homework.
Evolvable Protocol Files
Behavioral settings don't live in config.md. They live in protocol files that Claude creates and rewrites over time based on instructions you give it.
The principle: config.md holds static settings that almost never change (vault path, check-in time, idle thresholds). Protocol files hold behavioral settings that should evolve as you learn what works for you.
When you give Claude an instruction, it gets persisted. The relevant protocol file gets updated. System state gets flagged. The next session picks it up without you having to repeat yourself. This is how the system learns your preferences over time without you managing configuration manually.
The distinction between static config and evolvable protocol files is one of the more important architectural decisions in the system. Anything that might change based on user behavior or preference is a protocol file, not a config value.
SKILL.md: Executable Logic in Natural Language
SKILL.md is the operating system of the skill. It is not documentation — it's a structured instruction set that Claude loads and executes at the start of every session.
What SKILL.md defines:
- Session startup sequence — what to load, in what order, under what conditions
- Intent detection table — maps what the user says to which protocol gets loaded
- Entity creation rules — which type goes where, what filename pattern, what frontmatter fields are required
- Memory loading rules — what loads first, when to stop loading
- Hard invariants — what never happens (no orphan notes, no deletion, no Windows-style paths, no loading all files in bulk)
The design philosophy is progressive disclosure applied to the skill itself. SKILL.md is the thin always-loaded layer. Protocol files are detail that loads on demand. A monolithic SKILL.md that tries to define every template and every edge case is slow to load, expensive in tokens, and fragile to maintain. Keep it lean. Push detail into the protocol files it references.
The hard invariants section of SKILL.md is worth calling out specifically — it's a list of things that categorically never happen, regardless of what the user asks:
- No note is saved without YAML frontmatter
- No note is saved without at least one
[[link]] - No vault file is ever deleted — archived instead
- Protocol files and memory files are never written into vault content folders
- All file paths use forward slashes
- Entity files are never loaded all at once in bulk
These aren't guidelines. They're hard stops baked into the system's behavior from day one.
Getting Started
Everything you need to run MemSpren V1 is in the GitHub repository.
- Install Claude Cowork — MemSpren requires Cowork's local filesystem access. It will not work in Claude.ai chat or Claude Code.
- Mount your Obsidian vault — In Cowork, mount the folder that contains (or will contain) your Obsidian vault. Note the full path.
-
Clone the repo and copy
SKILL.md— Drop the skill file into your Cowork environment following the instructions in the README. - Run the setup flow — Open a Cowork session and say "Run the MemSpren skill." It will detect that setup hasn't run, ask for your vault path, and scaffold the full directory structure automatically.
- Do your first check-in — Say "Let's do a check-in." The rest follows from there.
The README covers troubleshooting, vault path formatting, and how to verify the skill is reading and writing correctly.
Decisions Reference
| Decision | What | Why |
|---|---|---|
| No external database | File-based only | Auditable, no cloud dependency, runs anywhere |
| Deletion policy | Hard no-deletion, archive forever | Past is data. Patterns require historical nodes. |
| Config scope | Static settings only | Behavioral = protocol files. Evolving config is state, not config. |
| Always-loaded files | hot-memory + system-state only | Everything else on demand. Token budget discipline. |
| Hot memory cap | ~800 tokens | Forces prioritization. No free lunch on context. |
| YAML on every file | Required at creation | Retrofitting metadata later is painful. Graph index needs it. |
connected field + [[links]]
|
Both, always | Dual representation — navigable in Obsidian, queryable from metadata. |
| Tasks as append-only inbox | Single file, not per-task files | Flat, queryable, no file sprawl. |
| Atomicity rule | One idea per note | Connection density over comprehensiveness. Small notes link more. |
| No orphan notes | Hard invariant | Graph only works if everything connects. |
| MOC creation | Emergent, never upfront | Synthesized when link density warrants it, not pre-planned. |
| SKILL.md design | Thin always-loaded layer | Detail lives in protocol files. Lean core, deep protocols. |
What's Not in V1
V1 has a deliberately tight scope. The goal was one working loop: open Cowork, run the skill, do a check-in, see files appear in your Obsidian vault with frontmatter and links. Everything below is deferred until that loop is proven reliable.
- No automated cron jobs — check-in is manually triggered
- No weekly or quarterly planning flows
- MOC detection exists in system state, but MOC creation is suggestion-only
- No pattern correlation analysis
- No graph visualization beyond Obsidian native
- No financial tracking, calendar integration, or script execution
- No Telegram or WhatsApp integration (planned for a future version)
The non-technical story behind MemSpren — the years of failed PKM experiments, the philosophy behind the architecture, and what it actually feels like to use it daily — is on Substack. Start there if you want the "why" before the "how."
Follow the build on dev.to and Substack.
Supreet is a Senior Software Engineer building AI-native tools in public.




Top comments (0)