Building an Agent-Maintained Second Brain Out of Markdown and Git
Building an Agent-Maintained Second Brain Out of Markdown and Git
+-------------------+ (Unattended Sync) +--------------------+
| Abstract Sources | ----------------------> | raw/ (Append-only) |
+-------------------+ +--------------------+
|
(Agent, human reviews)
|
v
+-------------------+ (Dumb Build) +--------------------+
| Surface (UI) | <---------------------- | wiki/ (Compiled) |
+-------------------+ +--------------------+
We talk a lot about scaling systems up, but there is an entirely different joy in scaling a system precisely inward.
Recently, I built a personal “operating system”, a second brain designed strictly for one person’s life. Rather than writing endless boilerplate, this system is maintained primarily by LLM agents (specifically Claude Code). My job is to curate the raw inputs and set the direction; the agent handles the heavy lifting of maintaining the derived, structured output.
If you are tired of wrangling bloated databases just to track your own calendar and notes, let’s break down how this agentic, backend-free architecture actually works.
The Two-Repo Split
The system consists of two companion repositories: daily-dashboard and personal-wiki.
-
daily-dashboard: A single static HTML page that gives me a morning glance at my life. It surfaces calendar events, finance alerts, open loops, learning streaks, inbox highlights, captured ideas, subscriptions, recent spending, and new senders worth reviewing.
-
personal-wiki: A Karpathy-style personal knowledge wiki. This is a directory of interlinked concept notes, people notes, project notes, a daily journal, a life plan document, and yearly goals. This wiki is ultimately published outward to Notion to give me a human-friendly reading experience.
You might be wondering: why not put both in one repository?
I deliberately kept them separate because their core conventions actively conflict. The dashboard must scrub internal cross-reference links because they would leak into the rendered HTML. The wiki, conversely, mandates a minimum number of those exact same cross-reference links per page.
Each repository operates under its own build contract, maintains its own decision-record namespace, and enforces its own immutability rules for raw source data. One shared config file simply couldn’t hold both rule sets without turning into spaghetti. They act like any two subsystems whose invariants genuinely disagree.
They touch at three narrow, deliberate seams:
-
Inbound, journal: A task-completion sync appends the day’s finished tasks into the wiki’s daily journal.
-
Inbound, sources: When the dashboard distills my inbox, it also writes one file of candidate reading sources (newsletters and articles worth ingesting) into the wiki’s raw inbox.
-
Outbound: An on-demand generated audio digest of the wiki, a podcast episode narrated with text-to-speech, gets copied into the dashboard as a static asset.
Each seam has its own decision record that names exactly which file it may write. Everything else stays fully decoupled.
The Pipeline: Raw → Compiled → Surface
Both repositories share a strict three-layer architectural pattern.
First, we have the raw/ layer. This is where immutable, dated, append-only source captures land (one file per pull or entry). This data is the audit trail. The default rule is that nothing in raw/ gets edited after it is written.
The default has exceptions, and I like that they are written down. The wiki’s rulebook lists five scoped ones today, each backed by its own ADR. The task sync may append a single subsection to one day’s journal file. A voice capture bot may create new transcript files, never touch an old one. The morning skill may write my dictated answer into that day’s empty Morning section. Every exception names one file pattern, one writer, and one thing it may never do. Without that list, “immutable” would quietly erode.
Next is the compiled layer (like wiki/). This is model-owned, distilled knowledge derived entirely from the raw sources. When I want to query data, I query this layer, not the raw captures themselves. Answers follow a contract too: every claim cites the page it came from, and every answer ends with its sources and the gaps the wiki could not fill.
Finally, we have the human-facing surface. For the dashboard, this is the generated static HTML page. For the wiki, it’s pages in my Notion notes database, pushed with Notion’s ntn CLI.
The magic here is in the build step. A dumb, dependency-free, idempotent build script regenerates the static page from marked blocks in the compiled layer. All the heavy intellectual lifting (classification, deduplication, date math, categorization) happens agent-side at ingest time. None of it happens in the renderer. This keeps the renderer trivially testable and ensures that the “smart” work is fully auditable as agent-produced text.
More Ways In
The raw layer keeps growing new doors, and none of them required touching the pipeline behind it.
- Voice. I dictate a braindump into Samsung Voice Recorder and paste the transcript into a Discord channel. A small bot, Hermes, redacts personal identifiers and commits it as a new file under
raw/voice/. From there abraindumpskill routes it into my plan, goals, and decisions. - A physical button box. An M5Stack Core2 on my desk publishes taps over MQTT to a broker on my Mac. A drain script turns them into dated
raw/diary snapshots for the dashboard. - Feeds I already consume. A live YouTube snapshot, a daily digest of posts from X, Samsung Health workouts, and articles from Meridiano, my news briefing project, each land in
raw/as dated files.
The No-Backend Stack
When you are the only user, you don’t need a web app. You need Markdown.
I use Markdown-as-database and treat git as my durable store. Instead of application code, agent-run “skills” act as the orchestration unit.
The daily-dashboard is just a self-contained index.html with dark-mode-first CSS. There is no backend and no build pipeline beyond a small Python script (build.py) that rebuilds marked sections from the compiled markdown layer. Crucially, no live third-party API calls happen from the page itself. Everything external is pulled in an agent session ahead of time and baked directly into static markup. The one exception is a small streak-tracking widget that uses browser localStorage, the only piece of hand-written client-side logic in the whole page.
The personal-wiki relies on plain Markdown files with YAML frontmatter and cross-reference links. Small Python scripts handle the mechanical parts: journaling, sync merging, the lint, snapshotting YouTube, and turning a podcast script into audio. Most of them have zero third-party dependencies. A few do call out to the network now, for YouTube, the X digest, and text-to-speech. Tests are plain unittest, deliberately scoped to the decision-making logic (like merge and dedup rules) rather than thin glue code.
Early on, every pull went through MCP connectors. I have since moved local pulls to CLIs: td for Todoist, Google’s gws for Gmail and Calendar, ntn for Notion. A connector turns rules that should be code into prose the model re-reads every run, and a response-shape change shows up as a bad snapshot instead of a failing test. The cloud routines still use connectors, since that is what they have. Local work uses CLIs.
Deploying a Git Pull
There is no deployment pipeline here, no CI/CD in the conventional sense.
The dashboard lives on a Raspberry Pi. A systemd timer runs git fetch and git reset --hard origin/main every 60 seconds, and Caddy serves the checkout as static files. Pushing to main is the deploy. The wiki never reaches the Pi at all; only its podcast episode does, copied into the dashboard repo as a file.
The more interesting split is between what runs unattended and what waits for me:
-
Unattended capture. A scheduled cloud agent routine pulls Gmail, Calendar, and Todoist into the immutable raw layer twice a day, at 07:00 and 16:00, and pushes a commit. No distillation, no rendering.
-
Unattended distillation, with a tripwire. An hour after each capture, a second routine distills the new raw snapshots into the compiled layer, rebuilds the page, and pushes. If it finds a new finance alert or a new open loop that needs my response, it still commits, but it tags the commit
[REVIEW]and creates a Todoist task so I check it later. -
Human-only. Anything that writes to an external system of record (my spending ledger in Notion) or touches personal journaling stays in a session I start myself.
-
Unattended lint, report only. A nightly routine lints the wiki for orphan pages, dangling links, thin pages, stale claims, and tag clusters with no hub. It may fix three mechanical things on its own. Everything else becomes a report, and working through it is a skill I run by hand, with one approval for the batch of ready-to-apply fixes.
When I first built this, distillation always waited for me. Now the rule is narrower and, I think, more honest: the agent can do the work unattended as long as anything surprising gets flagged for a human. The engineering fun lies in that pipeline discipline, immutability, idempotent rebuilds, and the agent-vs-code division of labor, not in scale or uptime.
Final Thoughts: The Dev Workflow
Building this system fundamentally changed how I write software. It was built and maintained almost entirely through Claude Code operating against a written rulebook per repository.
I use a lightweight architecture-decision-record (ADR) process for both repos, and it earns its keep. The wiki has fourteen ADRs now, and most of them exist to answer one question: who is allowed to write where. Each time a new source wanted into raw/, the ADR forced me to name the file pattern, the writer, and what it must never do.
The wiki also reserves a separate namespace for personal, non-technical decision records, next to the technical ADRs. I’ll be honest, that folder is still empty. The structure is there for the first life choice big enough to deserve one.
By treating personal life choices with the same rigor as architectural invariants, you end up with a system that doesn’t just track what you do, but remembers why you do it.
Down the Rabbit Hole
- Karpathy’s Notes Setup - The inspiration for flat-file, heavily interlinked knowledge systems.
- Architecture Decision Records - Why writing down your “why” matters before you write the code.
- Model Context Protocol (MCP) - How agents securely connect to local and external tools.