Building a Second Brain for a Family, and Deciding Not to Ship It Yet
A 51-item plan, 43 production deploys in a week, a 14-day canary on one real household, and a quality bar the instrument could not even measure. How the Family Second Brain went from a question to HOLD, and why HOLD is the right closeout.
Claude (Fable 5.1) via Claude Code, Session archaeology, read-only production readout, drafting
Governed by curate-me.ai
On July 13 I asked a question in a Claude Code session: could a "second brain", the Obsidian or gbrain kind of AI-correlated knowledge, help Nod understand a family under the hood? And was the platform org that already backs the app secretly that thing, or did we need to build one?
Fifty-six days later the answer is a 51-item plan with 45 items done, twenty brain modules and 12,414 lines on the platform, a brain that has compiled 64 immutable revisions for my own household at a total LLM cost of sixteen cents, and a decision I recorded today: HOLD. Do not promote it to every household. Do not turn it off either.
This post is the whole arc, including the part where the quality bar came back red because the instrument that measures it had never written a row.
The question, and the four sources behind it
Nod is the family logistics app I wrote about in June (the product argument, the build, the backend). It already had a memory. The platform stored per-household facts with provenance, kept an entity registry (schools, coaches, fields, vendors), and grounded its Ask feature against both. So the honest first step was to check whether "second brain" was a new thing or a rename.
The agent read four sources in full and boiled them down to one pattern:
| Source | What it contributed |
|---|---|
| Karpathy's "LLM Wiki" gist | Three layers: immutable raw sources, LLM-compiled wiki pages, and a schema that is a living constitution. Ingest, query, and lint as the three operations. His critique of RAG: it rediscovers knowledge from scratch on every question, while the wiki is a persistent, compounding artifact. |
| Garry Tan's gbrain | The dossier format. Current best understanding on top, rewritten as it changes. Append-only timestamped evidence on the bottom, never edited. A salience ladder: one mention is a stub, three across sources gets enriched, eight or more gets a full profile. |
| eugeniughelbur's obsidian-second-brain | Self-maintenance mechanics. Nightly consolidation, contradiction reconciliation, and the freshness rule: every fact is timeless, dated, or a pointer, enforced by a linter. |
| Cathryn Lavery's "AI house manager" thread | The household application. Memory before automation. The weekly reset as the place trust is earned. Her challenge to app-first products: apps require you to go to them, and remembering to check is the mental load. |
The plan's verdict was that the platform org already was the substrate. Raw sources with provenance, an entity registry, a facts store, grounded Ask. What was missing was the difference between a library of fragments and a compounding understanding layer. Compiled pages, cross-links, a content index and change log, salience, lint, revision history, synthesis. Karpathy's RAG critique applied verbatim to our Ask.
Two decisions came out of that reading. First, not Obsidian. It is a single-user local vault; we are Mongo-backed, multi-tenant, and consent-governed with per-org flags and sensitive-class write blocks. We would adopt the disciplines (dossier format, links, freshness rule, lint) as data model and jobs, and ship an Obsidian-compatible markdown export for portability instead of embedding a vault. Second, every team-scale LLM-wiki implementation the agent found had converged on PR-gated writes. Nod already had that, productized, as the propose-then-approve spine. So brain rewrites that change meaning would route through the same approval card as everything else.
A plan written for agents, not for people
The plan itself is 685 lines of markdown in the iOS repo. It names both repositories, the exact seams, 26 verified gaps (numbered G2 through G26), eight phases, and 51 items with IDs like FSB-3.1, each tagged iOS, platform, or both. It has a section of hard boundaries and human gates, and a status ledger at the bottom that is declared the single source of progress truth.
The header says who it is for: "an executing dev team (agents running Opus, fresh sessions)". Every item carries its own acceptance evidence, so a fresh session can pick up one row without re-auditing the world. The estimates in the phase headings add up to somewhere between fourteen and twenty weeks.
The core of phases 0 through 5 shipped in six days.
July 13 to September 7
That cadence was possible because the platform repo's main is production and deploys on merge, and because each item ran in its own git worktree with its own agent. The plan's ledger commit messages from July 14 read like a shipping log: "wave 3 landed (11 platform deploys, 7 iOS merges today)". Across the two weeks, 43 brain PRs merged to platform main in the first seven days and 48 of the 90 iOS PRs merged in the window belonged to the program.
What the brain is, mechanically

Four layers, and data only moves rightward.
Evidence is immutable. Emails, captures, documents, approvals, corrections, and versioned user assertions. Before the brain existed, an explicit fact could have no source and a correction overwrote the old value in place, so a composer could not honestly cite what a parent had said last month. Phase 0 added an append-only assertion chain so every fact points at something that still exists.
Normalized rows are derived and mutable. Facts, entities, and one canonical typed-links relation. They carry evidence references and valid time, and they never masquerade as the original source.
Compiled pages are the new thing. One current-pointer document per subject, with an immutable revision history behind it. The doctrine in the module docstring is the part I would defend hardest:
Claims are derived deterministically from the normalized stores. The model NEVER invents a claim; the optional LLM pass writes summary prose only, through the governed
brain_llmseam, and the deterministic template is the no-consent fallback.
A claim's id is a hash of org, page type, subject, predicate, and normalized value, so an unchanged claim keeps its id across recompiles and a changed value is a new claim. The inputs are hashed into a digest, and an unchanged digest produces no new revision, which makes the hourly sweep structurally idempotent. Rollback moves the pointer and never edits history. A revision auto-publishes only if every claim is additive against the published one, nothing trips the sensitive classifier, and every claim is household-shared. Anything that removes or changes meaning stays a draft and becomes an approval card.
Surfaces read from compiled pages first. Ask reads the compact content index, picks a bounded page set, and only then retrieves. The change log is content-free by construction: actions, page ids, claim ids, and evidence categories, pinned by tests. That is what lets the B2B dashboard show a per-family brain health page without seeing a single household fact.
The constitution is a pure Python module with closed enums for page types, freshness classes, visibility scopes, and evidence kinds. Per-org overlays can narrow (suppress sources, shorten retention, disable proactive delivery) and can never widen. That asymmetry is the whole governance story in one validator.
Evals that were allowed to say no
Two evaluations shaped the plan more than any feature.
The first was a leftover from an earlier sprint plan: does grounding extraction on the knowledge graph help at all? The agent ran it on the VPS with a held-out corpus, both arms on the same 21 governed Opus requests, about $0.28 per arm. The pre-registered primary metric moved by exactly zero. The ledger records it as "P1 MISSED" and moves on. Nobody had to argue the graph into existence after that; it stays on because the secondary floors were clean, and no one claims lift for it.
The second was the one the brain exists for. A two-arm question set of multi-source household questions ("who usually handles Tuesday pickup?", "what changed about swim this month?") with a delta pre-registered on July 14 before any code could answer them. Compiled-first Ask answered 5 of 5. Baseline RAG over fragments answered 1 of 5. The +0.8 delta has held every night since on the production eval lane, between 0.60 and 0.80 depending on the night.
The nightly lane also runs eight compiled-page cases with an LLM judge: two grounded pages that must pass and six negatives that must fail closed (prompt injection, cross-org, a child's private data, a deleted source, a circular derived claim, an ungrounded claim). Those six have failed closed every night since the lane went live on July 20.
The canary, and the bar that could not be measured
On July 19 the flags flipped for one household. It was mine, because no external family exists yet. Fourteen days later the agent produced a decision packet, read-only, content-free, with the explicit rule that it recommends and I decide.
The plan had pre-registered exactly one numeric quality bar, on July 14, before any live decision existed: brain proposals should be approved or edited at least 60% of the time and muted less than 10%, measured on the canary household's real decisions through the existing quality aggregator.
The aggregator returned nothing. It reads a feedback ledger, and that ledger had zero rows for the household despite 52 real decisions in the window. The iOS chip bar wrote reasons into it, but the decide route itself had no producer. The instrument had been dead since it was built, and the canary was the first time anyone read it.
Falling back to a raw join of the automation run ledger against approvals gave the honest proxy: brain composers emitted 31 cards, 26 were decided, 9 were approved. About 34.6%, against a bar of 60%. Almost all of the reject mass sat in one composer, attribute_correction, at 4 approved of 20 decided.
Brain composers in the canary window (distinct approvals, content-free)
| Composer | Emitted | Approved | Rejected | Superseded | Approve share |
|---|---|---|---|---|---|
| attribute_correction | 23 | 4 | 16 | 3 | 20% |
| memory_conflict_digest | 6 | 4 | 1 | 1 | 80% |
| link_allowlist | 1 | 1 | 0 | 0 | 100% |
| Total | 31 | 9 | 17 | 4 | 34.6% |
The packet's recommendation was HOLD, and the reasoning was symmetric. Do not promote, because the plan's own exit gate says bars green and the bar was not green. Do not roll back, because there was nothing to protect against: every security negative had failed closed, every brain call was governed and attributed, cost was fifteen cents, and the caps had held (1,782 candidate cards suppressed, 283 deduplicated, 23 emitted). The failure mode was "lint cards not useful enough yet", which the approval spine absorbs by design.
What the 20% was
The same day, a second agent did read-only forensics on those 23 cards. The finding relocated the problem.
The 23 cards covered seven distinct questions. Every re-mint carried the byte-identical entity, attribute, and keep-versus-prior pair. Only six of the 23 were ever decided per-card in the app, and those six went 4 approve, 2 reject. The other 14 rejections had no decided_via stamp and arrived in two bursts, four within one minute on July 20 and eight within 90 seconds on July 24. Those were me clearing a queue of stale duplicates, not verdicts on the proposals.
Counted per card, the composer's human score for the window was 4 of 6, above the bar. The 20% headline was manufactured by counting 23 cards for 7 questions and 14 bulk settlements as rejections. That does not exonerate the composer. Asking the same question 23 times is exactly the trust bug the whole product exists to avoid. It just moved the bug from "bad proposals" to "too many cards".

Three named mechanisms, each fixed in one platform PR:
- Cross-day re-mint. The durable per-question gate that had just shipped for event proposals derived identity from a linked event id plus an absolute instant. A correction card has neither, so its identity resolved to
Noneand it reached no gate at all. Sixteen of the 23 cards were re-asks; one target was asked six times over six days. The fix is a second identity scheme for correction-shaped proposals: action kind, target scope, and the set of values in question. - A reject teaches nothing. The lint doctrine said a rejected pair "is never asked again", but the marker that enforces it was only ever written by the undo of an auto-merge, never by rejecting a card. So the next hourly sweep re-detected the identical contradiction. Now a reject writes the marker.
- List-shaped keys asked as if singular. Attribute contradiction detection grouped by key with no notion of which keys hold one value. A
noteattribute legitimately has several. Those cards went 0 approved of 6 decided, and both of the only two in-app rejects on their merits werenotecards. Fact contradiction detection already restricted itself to single-valued types; attribute detection now does too.
The part I keep coming back to is the cap machinery. It looked like a success in the packet: 1,782 suppressed, 23 emitted, no proposal spam. It was absorbing a storm the brain was generating against itself. The backlog cap counts pending cards org-wide, the same seven questions re-detected hourly filled that budget, and the cap then shed work from composers that had nothing to do with the brain. The travel-prep composer had 23 suppressed against 13 emitted in the same window. A safety mechanism was quietly making an unrelated feature worse.
What "done" means in this repo
The July 22 re-audit that reopened five items was not looking for bugs. It was enforcing vocabulary. The technical closeout artifact states the rule:
Never convert "implemented," "locally tested," "hosted green," "merged," "deployed," and "proven live" into synonyms. Each is a separate evidence state.
Under that rule, a routine-proposal composer that is merged and deployed but has no production caller is not done. A one-tap re-confirm affordance that ships in the app but never receives the field it needs from real data is not done. A vault export that lacks the step-up re-auth the plan required is not done, even with a loud TODO at the route. The audit wrote each one down with its exit gate, and each one closed over the next five days with a PR, a hosted CI run id, a deployed SHA, and a live-proof workflow run against the production container.
The same rule is why today's closeout marks two items "deployed with a human proof gate open" rather than done. The export flow's positive path needs a physical device and a real Sign in with Apple. The two-engine runner conformance run last failed on a Codex subscription quota, which is a quota state, not a code defect. Neither is in progress. Neither is done. Both are written down as exactly what they are.
Where it stands today
I closed the plan this morning with a read-only pass over production. Six brain flags for my household, unchanged since July. Twenty-one compiled pages: sixteen entities, two members, one household, one routine, one responsibility. Sixty-four immutable revisions, sixty-four content-free change events. Fifty-three governed compile calls since July 19, every one attributed to the org and the job, for a lifetime total of $0.158. Zero brain proposal cards emitted since the August 2 fixes. Forty of forty-one nights fully green on the compiled-page judge lane since the judge got a reasoning-sized token budget.
The decision I recorded is the packet's decision, five weeks later and with more evidence behind it. HOLD. There is nothing to promote to yet, because the eligible cohort is every consumer household and the only one is mine. There is nothing to roll back from, because the thing runs for a few cents a month and fails closed every night. The promotion checklist (preset commit, tenancy and role backfills, new-signup default-on test, rollback drill) is written down for the first external family, when there is one.
The ledger closes at 45 done, 2 deployed with a proof gate open, 4 backlog, 0 in progress, 0 blocked, out of 51. Two things the closeout explicitly does not claim: that the brain makes the product measurably smarter for a family (the learning-loop audit on July 26 said the mechanism is built and consent-gated but the end-to-end product claim needs a pre-registered lift experiment that has not run), and that the quality bar was ever met. It records that the bar could not be certified in the only window observed, and that no new window has opened because the brain has had nothing to propose.
Takeaways
- Check whether you already have the thing. The platform org was most of a second brain. The plan's value was naming the exact delta (compile, index, log, lint, revisions) instead of rebuilding the substrate.
- Write plans for the executor you have. Item IDs, per-item acceptance evidence, a ledger declared as the single source of truth, and human gates named up front are what let fresh agent sessions ship 43 production PRs in a week without a standup.
- Let the model write prose and nothing else. Deterministic claims with stable hashed ids made idempotency, diffs, rollback, and content-free logging fall out for free. Every hard governance property traces back to that one line.
- Pre-register the bar, then trust it when it says no. The +0.8 Ask delta and the 60% approval bar were both written down before code existed. One was met and one was not, and the plan's own exit gate is what produced HOLD.
- A dead instrument looks like a quiet system. Zero feedback rows read as "no complaints" until the canary tried to use them. Wire the producer the day you write the reader, and test the full loop end to end.
- Caps that hold can still be hiding a storm. Suppression counts are a symptom to explain, not a success to report. Ask what filled the budget.
- Keep the evidence states separate. Merged is not deployed. Deployed is not proven live. A closeout that says "deployed with a device gate open" is more useful than one that says done.
Comments
Loading comments...