Life OS

A personal cognitive system that reads my life across three contexts and hands the day back to me already synthesized.

● LIVE STARTED FEB 2026 · FLAGSHIP PYTHON · FASTAPI · SQLITE · MCP · REACT · CLAUDE API · OPENROUTER EMBEDDINGS
4,141
Entities
22,366
Ledger rows
6,209
Links
579
Tests
Nothing here is public. The whole stack sits on a private network, and the only way in is a session I've already authenticated.

What this is

Life OS is the memory and judgment layer I sit on top of. One SQLite database on a small server, one FastAPI in front of it, 26 MCP tools so any Claude session can read and write it. It holds goals, projects, commitments, people, ideas, meetings, and a running activity ledger across my three contexts... personal, my work at a headwear company, and a pizza franchise I own a piece of.

The problem is embarrassingly simple to state. I was having the same conversation with an AI over and over. Every session started from zero. Every decision I'd already made had to be re-explained, re-argued, and half the time re-decided differently. The context sat in files and transcripts and meeting notes... it just wasn't reachable at the moment I needed it.

As of late July 2026 it holds 4,141 entities, a 22,366-row ledger, 6,209 links between things, and 335 synthesized meetings. 579 tests keep it honest. Those meetings gave up 774 commitments with an owner attached, which is the number I actually watch... a meeting that produces no owed work is a meeting I didn't need to be in.

Why it's built this way

Three calls shaped everything else.

One table, not fifty

Every object is a row in entities with a JSON metadata blob and a type string. A goal, a person, a meeting, and a principle all live in the same place. Purists hate this. But the whole point is cross-domain linking, and a rigid per-type schema makes every new kind of thing a migration, a set of joins, and a week I don't have. The graph lives in a separate links table and doesn't care what it connects.

Capture stays somewhere else

Voice notes go to Mem. Meetings come out of Granola. Life OS pulls from those, synthesizes, pushes the result back. Capture is cheap, cognition is the hard part, don't confuse them.

Agents are roles, not applications

Strategist, Connector, Challenger, Curator, People, Coach... prompt-defined personas over shared Python functions and one database. Nobody needed six microservices. The Challenger exists because I'm logically minded and routinely miss how a decision will land on people, so the system asks that question whether or not I want it to.

What I rejected: a vector-only store (I need exact tag and status filters constantly), a hosted PKM tool (I wanted the API to be mine), and per-session manual context loading, which is the exact failure mode I built this to kill.

The build log, failures left in

2026-02-08

— First commit. Core scaffolding and the six agents in the same week.

Spring 2026

— Synthesis layer. Entities got a validity field, five declarative lifecycle rules fire on every PATCH, high-confidence changes auto-apply and low-confidence ones queue for me. Then a dashboard and a watchtower service that pings everything every 60 seconds.

2026-06-18

— Hybrid retrieval shipped. Lexical plus vector, fused by reciprocal rank fusion, embeddings via OpenRouter, in-memory cosine index.

2026-06-27 · THE ONE THAT STUNG

I ran a forensic audit on retrieval and found that I was writing to Life OS constantly and almost never reading from it. Worse: when I did read, search was running lexical-only, because semantic was an opt-in flag nobody passed. I built a 16-query benchmark of natural-language paraphrases, the way a person actually asks ("the payment work stuck on a legal entity decision"). It scored 0 out of 16. Zero. A retrieval system I'd shipped, tested, and been pleased with could not find a single thing when asked in plain English. Flipping the default to semantic and adding a first-prompt auto-load hook took the grade from F to A, MRR 0.19 to 0.90.

2026-07-09

— Found that meetings had been silently missing for about a month. MEM_API_KEY was absent from the server env, so that source skipped with a warning in a log nobody reads. Separately, Granola had encrypted its local cache back in April, so the sync I was scraping had synced nothing since May 6 while its heartbeat stayed green. Rebuilt on Granola's public REST API and made meetings a first-class entity type.

2026-07-22

— Story Layer. One job at 05:30 reads across all three vaults and writes a narrative that voice, the morning brief, and email triage all open on, instead of each re-deriving context.

2026-07-24 · A REVERSAL I HAD TO ACCEPT

Retrieval measured 0.762 and I'd been calling that a decline from June's 0.85. It wasn't. The benchmark had been quietly failing its own gate, and June's number came from a different recipe and a different query set. I also tried enriching person records with role and company metadata to fix role lookups. It made those queries worse... short records get diluted, and extracted roles are often wrong. Role lookups now route to a dedicated people search and stay out of the vector space. Deliberately unfixed.

⚠ What would break

The heartbeats lie. Both of my worst outages were green-lit by monitoring the whole time. A source that skips with a warning is invisible, and a sync that processes zero records looks identical to a sync with nothing to do. I've added outcome assertions where it hurt most, but the class of failure is still live everywhere I haven't specifically hardened.

The server is a partial checkout. Individual files get copied to the box, so deployed code and the repo can differ, and whole directories in git were never deployed at all. It works because I check checksums first, which is a discipline, not a guarantee.

In-memory cosine has a ceiling. Good to roughly 28,000 entities. I'm at 4,025 embedded, so there's room, but the swap to sqlite-vec is a documented plan, not built code.

It's one person's system. No tenancy, no auth model beyond a key, no concurrency story. Every call optimized for me. The whole stack sits behind a private network with nothing exposed to the open internet, which is the only reason a single key is defensible... open a port and the auth model collapses the same day.

What I learned

The initial build felt like an accomplishment. Then I sat with it for a few dozen sessions and something was off... conversations weren't as contextual as a system holding my whole life should have made them. So I stopped guessing and measured: only about 20% of sessions were pulling anything out of Life OS at all. Months of clean writes, almost no reads. Writing to a system feels like using it. It isn't.

The first fix was plumbing. I wired retrieval into the session hook itself, so the first real prompt of every session fires a Life OS search and injects whatever comes back... every conversation now starts already knowing what I'm working on. That felt better immediately. But it wasn't done, because firing a search isn't the same as the search finding the right things. Lexical matching whiffed on any phrasing that didn't echo the stored wording. That's what pushed me to build the semantic embedding layer and hybrid retrieval for fuzzy lookups, and that's when the whole thing finally clicked.

Benchmarks rot silently. Mine had been failing its gate for weeks while I compared numbers across incompatible recipes and drew the wrong conclusion twice before settling it against a written diagnostic.

Where the AI was confidently wrong: it kept proposing to fix role-lookup misses by enriching and re-embedding person records. Reasonable-sounding, and the experiment proved it backwards. Synthesis also generated hundreds of plausible project proposals that piled into an icebox nobody accepted... 461 of 571 got archived in one sweep. Confident output volume is not signal.

What this demonstrates

Retrieval and grounding over private data. MCP tool design. Multi-agent synthesis with confidence-gated auto-apply. Production agent operations: cron jobs, heartbeats, outcome assertions, deploy discipline.

The bigger thing is that the scarce resource in AI work is no longer generation. It's context arriving at the right moment without being asked for. Every system I build now gets judged on whether it reads before it writes, and mine failed that test for months before anyone measured it.