Decision record Updated 2026‑08‑09 gbrain v0.42.74.0 Status: adopt, phased

Turning a 1,076‑note archive into something you can ask a question.

Noto works as a capture system and fails as a retrieval one — 23 wikilinks across 1,076 files is not a knowledge base, it is a pile. This is the record of what we decided, what we rejected, what we had to patch, and what will go wrong.

DecisionAdopt, phases 0–1
System of recordThe Noto vault
HostMacBook Pro, nonstop
Vault layoutMigrates to the pack
Carried patches1
Setup cost~4 hrs · ~$0.50
01  /  The verdict

Adopt it, scoped — and keep a kill criterion

gbrain is a retrieval-and-synthesis daemon over a markdown corpus, exposed to agents over MCP. Markdown in git is the system of record; Postgres is a derived cache that can be dropped and rebuilt. The two things it adds that nothing in the current stack does are gbrain think — a cited answer plus an explicit note on what the brain doesn't know — and a knowledge graph that wires itself on every write.

The recommendation is phases 0–1 only: stand up Postgres, migrate the vault layout, index it, wire the MCP server into Claude Code. Roughly four hours and about fifty cents. Everything beyond that — the entity backfill, the takes-and-calibration subsystem — is a separate decision made after the first one has proven itself.

The kill criterion — agree to this before starting

After the first import, ask three questions you genuinely cannot answer today: what have I written about consumer AI ideas, and which did I abandon and why? · what recurring themes run through my Writings? · what did I save about note-taking systems, and what's the through-line?

If gbrain think does not beat ten minutes of manual grep on all three, stop at the gate. Do not build the rest on faith.

What we are explicitly not doing

Not the full INSTALL_FOR_AGENTS.md path — 53 skills, the whole dream cycle, OpenClaw/Hermes as designed. That is built for a 146,000‑page brain holding 24,585 people and 5,339 companies. This corpus is a thousand pages of essays, saved articles and project notes with almost no person‑entities in it. Most of that machinery — person dedup, expert routing, deal tracking — would have nothing to operate on.

02  /  Diagnosis

The vault has a capture verb and no read verb

Measured against the live vault on 2026‑08‑08:

MeasureValueReading
Markdown files1,07611.9 MB · ~1.57M words
Captures/81676% of the vault — saved articles, threads, video summaries
Daily Notes/13278% are empty templates. 21 files hold real content — 3,436 words total
Writings/37Original essays, mostly Chinese
Projects/ · Ideas/38 · 1413 project notes + 25 podcast files; 14 thematic buckets
Wikilinks, entire vault23There is no graph. None.
Files with any tag13No faceting either

This reframes the problem. It is not a discipline failure — it is structural. The noto-notes skill has four intents (daily capture, append, route, answer) and none of them create structure. Routing appends to an existing note but never adds a link or a tag. So the vault grows monotonically with zero connective tissue, and the only way in is lexical search plus memory of what you saved.

1.57 million words of your own thinking that you cannot ask a question of. That gap is precisely and only what gbrain closes.

The headline benchmark will not apply on day one

gbrain's advertised +31.4 point P@5 lift comes entirely from its graph. But the auto-linker is pattern matching on wikilinks, not entity extraction — and the vault has 23. On import you get the graph-disabled variant, which is the thing that lift is measured against.

Hybrid vector + keyword search and think synthesis still work, and are still a large upgrade over nothing. The graph is a later project, not a flag.

03  /  The shape

iCloud is the transport. One Mac runs everything.

The requirement that drove the design: Noto stays a reader and a writer across all four devices, and the vault itself is where gbrain's markdown lives — not a copy of it.

   ALL FOUR DEVICES                        MacBook Pro — runs nonstop

   iPhone ─┐
   iPad  ──┤                               Postgres 17 + pgvector
   Air   ──┼── Noto ──► iCloud vault ──►   gbrain sync · dream · autopilot
   Pro   ──┘                              Hermes (co-located)
                                          
                                            git --separate-git-dir
                                            (objects live OUTSIDE iCloud)
                              └────────────┘
                                 atoms/ · wiki/ · reports/ · life/events/

Three lifetimes, kept apart

ArtifactLivesSyncedRebuildable
The vault — markdown, the system of record iCloud Drive all 4 devices It is the source
Postgres — pages, chunks, embeddings, links, facts, takes Pro onlyno Yessync + extract all
~/.gbrain/ — config, locks, logs, audit JSONL Pro onlyno Yes — regenerated
~/.gbrain/noto.git — git object store Pro onlyno no — the exception

Everything machine-local is derived and disposable except git history, which exists only on this Mac and has no upstream. Since the object store sits inside the otherwise-disposable ~/.gbrain, the fix is to give history an upstream: push to a private remote on the same cron as the sync. One line, and ~/.gbrain becomes genuinely safe to delete again.

Why git's objects leave iCloud

git init --separate-git-dir splits the two halves of a repo. The working tree — your notes, the thing git actually tracks — stays in iCloud where every device sees it. The git directory — thousands of tiny loose objects, independently synced, subject to placeholder eviction — moves to local disk. What remains in the vault is a one-line .git pointer file that never changes.

The Pro does not sleep — verified, and it corrected an earlier draft

pmset reports sleep 1, but MacCommandCenter.app holds a persistent caffeinate -i -s -w asserting both PreventUserIdleSystemSleep and PreventSystemSleep — 39 continuous hours at time of checking, on AC, 1d 15h uptime. An earlier version of this record planned a wake schedule and weighed moving the daemon to a VPS. Both were solving a non-problem; both are gone.

Residual condition worth knowing rather than acting on: -s holds only on AC, and the assertion is owned by a process, not by power policy. If interfaces ever fail overnight, pmset -g assertions is the first check.

04  /  What writes where

Most of what gbrain produces never touches a file

DestinationWhat lands thereSynced
The vault Pages — atoms, concepts, brainstorm saves, dream originals, drift reports, life events, enriched pages. Plus ## Facts / ## Takes fences inside existing notes, and timeline lines. yes
Postgres The derived index, plus DB‑only state: the take_proposals queue, minion_jobs, dream_verdicts, calibration_profiles, mcp_request_log, oauth_tokens. no
~/.gbrain/ Config, locks, logs, audit JSONL, import checkpoints, eval receipts, git objects. no

The middle row matters more than it looks. propose_takes writes to a database queue, not to markdown — the only thing that ever edits a takes fence in the vault is you running gbrain takes propose --accept N. Grading, calibration profiles, contradiction verdicts and job state are all DB-only.

Facts and Takes are literal sections in your notes

Not separate files. The entity's own markdown gets a ## Facts and a ## Takes heading, each wrapping an HTML-comment-fenced table. The markers render as nothing, so in Noto you see a heading and a table. Row numbers are append-only, so cross-page references of the form slug#N stay valid forever. Facts land on the entity's page, not on the page where you said them.

Where the vault writes actually land

There is no global "write everything under one folder" setting — output paths are mostly hardcoded in the phases, not driven by the schema pack:

ProducerPathConfigurable
extract_atomsatoms/<source-date>/…hardcoded
synthesize_conceptswiki/concepts/…via pack prefixes
brainstorm · lsd --savewiki/ideas/…hardcoded
driftreports/drift-<date>hardcoded
chronicle eventslife/events/…hardcoded
dream synthesize<output_root>/originals/…yes
enrichthe existing page, in placeno — by design
The one genuine exception

gbrain enrich rewrites an existing thin page where it already lives. Point it at an ideas stub and it rewrites your file. Not containable by configuration — the control is whether you run it, and with which --types.

05  /  Migration

The vault moves to the pack, not the other way round

Rather than author a custom schema pack pointing at existing folder names, the vault adopts gbrain-base-v2's conventions. Most of it is a rename.

TodayFilesBecomesTypeNote
Captures/816media/mediaextractable: true — 816 atom candidates when ever enabled
Projects/13projects/projectclean rename
Podcast transcripts12stay in projects/podcast/source via frontmatterfolder for organisation, type for extractable: true
Podcast episode notes13projects/podcast/projectyour own work, not consumed media
Writings/37writing/writingsingularwritings/ matches nothing
On series/1notes/noteforgoes writing/'s 1.4× boost + 365d decay
Ideas/14notes/notegeneric notes, not concepts
deep-research/2wiki/analysis/analysis
root .md + logs22notes/note
Daily Notes/132life/diary/<date>-<hash8>diarysplit into thoughts; 103 empty templates dropped
Chats/14dropped from the brain; files kept on disk
Daily Notes: 78% of them are empty

Measured 2026‑08‑09: of 132 files, 103 are empty templates (frontmatter plus four unanswered journalling prompts), 8 are thin, and 21 hold real content — 3,436 words total, about 0.2% of the vault. Today's is empty. The prompt-journalling habit did not stick.

So the migration splits 21 files into one page per thought at life/diary/<note-date>-<hash8> and drops the 103 templates — near-identical documents that would chunk, embed and compete in retrieval while carrying nothing. Deprecating the daily-note mechanism also stops manufacturing one noise page per day. The earlier plan called this folder immovable; that constraint was noto-agent's compiled-in path, and it dies with the mechanism.

Migrating does not automatically buy the tuned retrieval

gbrain's boost and decay maps key on an older path convention than the pack's own prefixes — they come from Garry's brain layout, not from gbrain-base-v2. A concept page at the pack's wiki/concepts/ gets no 1.3× boost; a diary at life/diary/ gets no demote. Both maps are overridable (GBRAIN_SOURCE_BOOST, gbrain.yml recency:) and must be set explicitly whichever layout you pick.

Type and path are two different keys

A path_prefixes entry is an inference rule — "a file here gets this type unless frontmatter says otherwise" — not a storage requirement. frontmatter.type overrides it, so organisation and semantics never have to agree.

Type governs what the extraction phases do to a pageextractable, primitive defaults, expert routing. Path governs ranking, git tracking and some eligibility gates — the source-boost and recency-decay maps, db_only, the facts rescue prefixes. Conventional directories do both at once, which is why they feel like one thing. It is also why migrating to the pack does not automatically buy the tuned retrieval: the pack declares wiki/concepts/ as a type prefix while the boost map keys concepts/ as a path.

Sequence it so it is revertible

Renaming a thousand files inside iCloud — in a vault that already contains conflict copies (2026-08-05.md and 2026-08-05 2.md both exist) — is the riskiest operation in this plan. git init and commit the vault as-is first, then migrate with git mv as a separate commit. git revert undoes it. Doing the rename before git init throws away the only undo you had.

06  /  Interfaces

One door, one scoped client per consumer

Three interfaces are wanted: WhatsApp capture through Hermes, vault edits triggering reprocessing, and an API for apps not yet built. Almost none of this needs building — gbrain serve --http already ships it.

RouteWhat it isAuth
POST GET /mcpthe MCP surface — 96 tools (10 localOnly ops filtered)OAuth 2.1, per-op scope
POST /ingestraw webhook write, ≤1 MB, rate-limited, queues a jobbearer, write
/admin/api/register-clientregister a client, DCR-styleadmin
/admin/api/rescope-clientchange scopes, token TTLadmin
/admin/api/agents/spendper-client LLM spendadmin
/admin/api/requestsrequest log, live job feedadmin
Why Hermes gets HTTP even though it is on the same machine

stdio would give it all 106 tools and no auth. HTTP gives three things stdio cannot: per-client spend attribution, a request log of what an autonomous agent did to your brain, and scope enforcement — the 10 withheld ops are file operations and hard purges Hermes should not have.

Claude Code stays on stdio: that is you at the keyboard, it benefits from the full surface, and it is already attributable. Bind the HTTP server to the Tailscale interface, not 0.0.0.0.

07  /  Decisions

The ledger

DecisionChoseOver
Where the markdown livesThe Noto vault, directlyA one-way rsync mirror
Vault layoutMigrate to gbrain-base-v2Custom pack matching existing names
Git object store~/.gbrain/noto.gitA path outside the runtime dir
How many hostsOne — the Pro, running nonstopBoth Macs; a VPS daemon
Storage engineHomebrew Postgres 17 + pgvectorPGLite (every quickstart's path)
Daily notes typediary, via pack prefixnote; retyping as conversation
Chronicle eventsRepurposed — things done and things that happenedLeaving them meeting-shaped and off
Self-knowledge synthesisA separate pass with our own promptThe stock atom pipeline
Nightly cycle scopeMovements ①–④ to startAll 23 phases from day one
Local modificationsA carried patch set, source cloneEditing the global install
Hermes transportHTTP with a scoped clientstdio MCP

The four that carry the most weight

The vault is the brain repo, not a copy of it

Rejected
A one-way rsync mirror into a separate git repo, with gbrain forbidden anywhere near iCloud.
Because
The mirror was the safe answer to "don't let an agent touch iCloud" and the wrong answer to "Noto is where the markdown lives." It stranded every atom, concept and report in a directory the phone and iPad cannot see — defeating the entire multi-device requirement. The risk is contained structurally instead: git objects outside iCloud, agent writes in their own directories, one host.

Events mean "what I did today", not "meeting segments"

Rejected
Leaving auto_chronicle off, as an earlier version of this record decided.
Because
Daily notes are records of what happened, and the stock judge's kind vocabulary is already life-shaped — meal · solo · travel · work · milestone alongside meeting · call. No prompt rewrite needed. Only the diary exclusion blocks it, and that exclusion is a policy written for multi-user company brains.
Costs
One carried patch, auto_chronicle on, and life/events/ pages appearing in the vault on every device.

Self-knowledge gets its own pass, not the atom pipeline

Rejected
Flipping extractable: true on diary to mine recurring themes.
Because
The clustering machinery is exactly right — atoms grouped by shared topic label, tiered by count, narrated at T1/T2. The prompt is not: it asks for "an idea that could become a tweet, quote, or short essay angle" and emits a virality_score. Pointed at a diary, that ranks your feelings by tweetability. The pack's prompt_template escape hatch is scaffolded but never loaded into an LLM call, so it cannot be swapped by config.
Instead
A purpose-built pass writing concept-shaped pages into wiki/concepts/. Same output shape, prompt fully ours, works today, no fork.

Everything runs on the Pro

Rejected
Splitting the daemon onto hostinger-openclaw; running a second index on the Air.
Because
The availability argument rested on the laptop sleeping, and it does not — verified. Autopilot's lockfile is machine-local and gives zero mutual exclusion across machines, so two hosts would have nothing stopping them interleaving writes. One host makes the question disappear. Hermes is co-located, so noto-agent needs no bridge.
Kept open
Relocating later is cheap by construction — git and iCloud hold the system of record, the index is rebuildable.
08  /  Patches

One hunk, one file, and rules to keep it that way

Every deviation from stock gbrain must survive upstream updates. That constraint shapes the install method and the patches themselves.

Install from a source clone (git clone + bun link), not bun install -g. gbrain detects a bun-link install and upgrades by running git pull --ff-only — which fails cleanly on a branch carrying local commits rather than destroying them. That failure is correct behaviour here, not a bug: it is the signal to rebase. Leave self_upgrade.mode unset.

Shaping rules

  1. Additive over modifying. A guard in a caller beats editing shared logic.
  2. Config-gated, stock default preserved. Makes it upstreamable as a feature and makes conflicts obvious rather than silent.
  3. Small stable files over large hot ones. operations.ts is 6,246 lines and changes most releases.
  4. One-off work is a script, never a patch. Backfills should not be rebased forever.
  5. Upstream aggressively. The cheapest patch is a merged one.

PATCH-001 — allow chronicle events from diary pages

Where
core/chronicle/backstop.ts — one hunk, ~4 lines, in a ~40-line file that rarely changes.
Not
chronicle/eligibility.ts (a shared pure predicate with tests pinning diary_excluded) plus operations.ts (6,246 lines). That was the first design; rule 3 killed it.
Shape
Config-gated bypass: when eligibility returns diary_excluded and chronicle.include_diary is set, proceed. Stock behaviour when unset.
Upstream as
"Make the diary chronicle exclusion configurable" — defensible on its own merits, which would delete the patch entirely.

Backfilling the existing 131 daily notes is not part of the patch — the backstop fires on real imports, so forcing a re-import triggers extraction for all of them. A one-off command, per rule 4.

The tripwire

One patch is the target steady state. If the register reaches four or five entries, the question stops being "how do we rebase these" and becomes "should we be running a fork."

09  /  Risks

What will go wrong, ranked

RiskSeverityMitigation
iCloud "Optimize Mac Storage" evicts file contents. Sync reads a placeholder, sees empty content, overwrites a good page with an empty one. high Turn it off on the host, or brctl download the vault before syncing. Already a known failure mode in the noto-notes skill.
Renaming ~1,000 files inside iCloud, in a vault that already contains conflict copies. high Commit the vault as-is first; migrate with git mv as a separate revertible commit; other devices idle; Optimize Storage already off.
The facts backstop fires on the bulk import. It defaults on, its model defaults to Sonnet, and it runs from the sync post-import hook — ~1,076 pages, most eligible. high facts.extraction_enabled false before the first sync; re-enable deliberately after. Confirm against a dry run.
Budget caps silently stop applying if the dream cycle's subagent points at a non-Anthropic model — the meter prices Anthropic rates only and skips the gate with one warn line. medium Keep the subagent on an Anthropic model, or the cap is decorative.
Half the corpus is Chinese and Postgres has no Chinese stemmer. The keyword half of hybrid search degrades; vector + reranker carry it. medium Measure early — query a known Chinese essay by exact phrase. Untested on this corpus.
The default search mode is the expensive corner. init auto-applies tokenmax; documented spread is 25× ($40–$1,000/mo at 10K queries). medium Set deliberately at init. For a few hundred queries a month, conservative.
Patch-set rot. An undocumented local edit is how a patch set decays into an unmaintainable fork. medium The register is the contract; the five shaping rules keep conflicts cheap; the tripwire at 4–5 entries forces the question.
One host means one failure domain. A runaway now takes down WhatsApp capture and every app, not just gbrain — this Mac was frozen once by a 31 GB leak. medium autopilot --max-rss 2048; conservative Postgres shared_buffers. Relocating stays cheap if it recurs.
Folder names mean nothing to the default pack if the migration is half-done — writings/ is not writing/. low Verify with gbrain schema stats after migrating; the untyped-page warning fires above 10%.
106 MCP tools land in every Claude Code session — not the "30+" advertised — paid for on every request. low Accept, or use HTTP with a read-scope token, which collapses the surface to the 60 read ops.
10  /  Build order

Thirteen steps, one gate

Steps 1–8 are one evening. The gate at step 8 is the kill criterion — everything after it is a second decision.

  1. Turn off iCloud Optimize Mac Storage; brctl download the vault; confirm other devices idle
    Pro
  2. brew install postgresql@17 pgvector, start it, create the database with shared_buffers at 1–2 GB
    Pro
  3. Source clone + bun link — not bun install -g. Add upstream remote. Then gbrain init against Postgres and gbrain doctor
    Pro
  4. git init --separate-git-dir=~/.gbrain/noto.git in the vault; commit as-is — the restore point; add a private remote
    Pro
  5. Migrate folders with git mv per the map. Daily Notes/ stays. Commit as a separate revertible commit
    Pro
  6. gbrain sources add noto --path "$VAULT"; gbrain.yml marking media/ as db_only; add daily-notes/ as a diary prefix
    Pro
  7. Set GBRAIN_SOURCE_BOOST and recency: explicitly; facts.extraction_enabled false for the import; auto_chronicle true
    Pro
  8. First sync — then the gate. Run the three questions. If think doesn't beat ten minutes of grep, stop here
    Pro · decision point
  9. claude mcp add gbrain -- gbrain serve; paste the brain-first protocol into the global CLAUDE.md
    Pro
  10. Apply PATCH-001; gbrain config set chronicle.include_diary true; force a re-import of the diary pages to backfill events
    Pro · fork
  11. gbrain autopilot --install --max-rss 2048 — the cap is the mitigation for a leak that already froze this machine once
    Pro only
  12. Nightly gbrain dream, movements ①–④; weekly gbrain doctor; re-enable facts.extraction_enabled
    Pro
  13. gbrain serve --http bound to Tailscale; register scoped clients for Hermes and the Air; confirm agent pages appear in Noto on the phone
    Pro · Air · phone
What to run, and what not to

Three cron lines and a daemon, not the twenty-job reference schedule. No email, X, calendar or meeting collectors — four credentials and four scripts serving entity types this corpus barely has. No morning briefing until there is something to brief on.

11  /  Cost & still open

What it costs, and what we have not decided

ItemEstimateConfidence
Initial embedding — 1.57M words, CJK-inflated to ~3–5M tokens$0.10–0.50 onceHigh — negligible
gbrain think, per query$0.01–0.05Medium — search mode spans 25×
Chronicle event backfill — 131 daily notes~$1–3 onceMedium
brainstorm · lsd, per run$0.05–0.40High — from the CLI's own help
Dream cycle, nightlyunmeasuredlow — cap before movements ⑤–⑥
Postgres + pgvector, local$0High

Still open

Sources

Every claim here is from github.com/garrytan/gbrain at v0.42.74.0, shallow-cloned and read directly — not from the README. Vault figures measured live 2026‑08‑08; machine state verified 2026‑08‑09. Companion documents: the multi-device architecture, the interface design, the patch register, and the operator's field manual.

Decision record · gbrain × Noto Updated 2026‑08‑09 Status: adopt, phases 0–1 Next review: after the gate