Everything gbrain does — the graph, the search, the nightly cycle, the calibration of your own predictions — falls out of one contract, enforced by a CI gate.
---type: persontags: [yc, ai-infra]---# Bob Smith Runs engineering at [[companies/acme]]. Met through [[people/alice]]. ## Facts <!--- gbrain:facts:begin ---> | # | claim | kind | confidence | visibility | valid_from | |---|--------------|------|------------|------------|------------| | 1 | Founded Acme | fact | 1.0 | world | 2017-01-01 | <!--- gbrain:facts:end ---> ## Takes <!-- gbrain:takes:begin --> | # | claim | kind | who | weight | since | |---|--------------------------|------|--------|--------|---------| | 1 | They'll raise a B by Q3 | bet | eugene | 0.7 | 2026-01 | <!-- gbrain:takes:end --> <!-- timeline --> ## Timeline - 2026-04-22 — pricing chat [Source: notes/2026-04-22]
Read this and the rest of the system becomes predictable.
The git repo of markdown files is the system of record. The database is a derived cache. We do not back up the database — we rebuild it from the repo.
docs/architecture/system-of-record.md
This isn't aspirational. scripts/check-system-of-record.sh runs in CI and
bans direct calls to insertFact, addLink,
addTimelineEntry, upsertTake anywhere outside a designated
reconciler. Code that writes a derived table without going through markdown first
fails the build.
Two consequences you feel immediately:
# Disaster recovery is boring — there is no "restore backup" steppsql -c 'DELETE FROM facts; DELETE FROM links; DELETE FROM takes;' gbrain sync# re-read markdown from the repogbrain extract all# rebuild every derived table, byte-identically# Multi-machine sync is git. That is the entire mechanism.git push# laptopgit pull# desktop — its DB rebuilds on next sync
Every table sits in exactly one of three buckets:
| Bucket | Tables | Rebuildable |
|---|---|---|
| FS-canonical | takes · facts · links · timeline_entries · tags | Yes — byte-identically |
| Derived | pages · content_chunks · page_versions | Yes — re-chunk + re-embed |
| Runtime only | oauth_tokens · minion_jobs · mcp_request_log · raw_data | No — but it isn't knowledge |
Five moving parts. Everything else is a reconciler between the repo and the index.
┌──────────────────────────────────────────────────────────────────┐
│ BRAIN REPO git · markdown · the system of record │
│ ~/brain/{people, companies, concepts, ideas, projects, media} │
└───────────────────────────┬──────────────────────────────────────┘
│ gbrain sync — walks the git diff,
│ parses frontmatter + fences
▼
┌──────────────────────────────────────────────────────────────────┐
│ POSTGRES / PGLITE derived index · ~45 tables │
│ pages · content_chunks (pgvector HNSW) · links · facts · takes │
│ timeline_entries · tags · sources · minion_jobs │
└───────┬───────────────┬───────────────┬──────────────┬───────────┘
▼ ▼ ▼ ▼
CLI MCP MINIONS DREAM CYCLE
gbrain … gbrain serve job queue gbrain dream
~90 verbs ~120 ops as crash-safe 19 phases
MCP tools subagents nightly cron
BrainEngine declares ~47 operations, implemented twice: PGLite (Postgres 17 in WASM, zero-config, single-writer, good to ~50K pages) and real Postgres + pgvector.
One 6,246-line file declares ~120 named ops with params, OAuth scope, and a localOnly flag. CLI and MCP are both generated from it, so they cannot drift apart.
Every op context carries remote: boolean, enforced by the type system. Private fact rows are stripped by the chunker before embedding, so they never reach search at all.
A Postgres-native, BullMQ-shaped queue. The point is crash-safe subagents: LLM tool loops persist pending → done, so a crash resumes instead of losing work.
Plain markdown, tool-agnostic, routed by a RESOLVER file. Signal detection, ingest, citation repair, briefings, schema authoring. Recipes an agent reads — not code.
Your folder names aren't hardcoded — they come from the active pack. It threads through every read and write path, and its name+version is folded into the search cache key.
query
→ intent-aware rewrite
→ ┌ pgvector HNSW — max-pool: best chunk per page, so a page
│ surfaces on its strongest evidence
└ BM25 — websearch_to_tsquery, GBRAIN_FTS_LANGUAGE
→ reciprocal rank fusion
→ source-tier boost ← weight your own writing above saved articles
→ graph signals ← adjacency · cross-source · session demote
→ title / alias boost
→ ZeroEntropy reranker
→ results tagged evidence (why it matched)
and create_safety (exists / probable / unknown)
gbrain search "…" --explain prints per-stage attribution: base score, every
boost that fired, what it multiplied by. gbrain search diagnose traces which
layer surfaced — or missed — a specific page.
gbrain search returns pages. gbrain think runs the same retrieval,
then composes a cited answer plus gap analysis — what's stale, what's
uncited, where two pages contradict each other, what hole you should go fill.
That second verb is the whole reason to run this.
gbrain invents about sixteen nouns. Most confusion with the tool comes from collapsing two of them that look alike. Each below says what it is, where it physically lives, and what it is not.
One markdown file. The atom of storage.
Everything is a page: a person, a company, an essay, a saved article, a day.
A page carries a body (compiled_truth), frontmatter, a type, a
content hash, and a salience score. Deletes are soft — a page sits in a 72-hour
recovery window before purge actually removes it.
A page's address, shaped like a path.
people/bob-smith, writing/no-way-back,
inbox/2026-08-08-a91f2c04. The leading segment is what the schema
pack reads to decide a page's type.
Slugs are unique per source, not globally. The same
topics/ai can exist in two sources and be two different pages.
A named content repo inside one brain.
Your notes vault is one source. A codebase is another. An essay collection a
third. Every page row carries a source_id. A
.gbrain-source dotfile in a directory pins every command run there
to that source, so you never pass a flag.
A whole database. You can have several.
Your personal brain is one. A team's published brain that you mount is another. Separate pages tables, separate access control, separate lifecycle.
The rule that separates the two axes: if the data owner changes, it's a brain boundary. If the owner stays the same and only the topic changes, it's a source boundary.
How far a source has been read.
Three fields on the source row: the last git commit imported, when that happened,
and which chunker version did it. gbrain sync walks only the diff
since last_commit — that's what makes re-syncing a thousand-page
vault take seconds instead of minutes.
Bump the chunker version in a release and the mismatch forces a full re-walk
automatically. If a sync is interrupted it exits cleanly with
last_commit unchanged, so the next run re-walks the same
diff and content hashes skip whatever already landed.
Whether a source joins general search.
A per-source boolean. federated: true and its pages show up in every
unqualified query. false and the source is invisible unless you name
it — the right setting for draft writing, or anything you don't want bleeding
into unrelated answers.
Across brains there is deliberately no federation at all. The agent sees the list of brains and decides when to query each one. Keeping that out of SQL is what keeps access control and debugging sane.
The declaration of your brain's shape.
A YAML manifest naming the page types that exist, which folder prefixes map to each, which types get facts mined out of them, which participate in expertise routing, what edge types are legal — and which dream-cycle phases run at all.
Swap the pack and the brain re-interprets itself; swap back and nothing is lost.
The pack's name and version are folded into the search cache key, so results from
one pack can never leak into another. gbrain schema detect clusters
your actual filesystem into proposed types when none of the bundled packs fit.
Typed edges between pages. The graph.
Written by the auto-linker on every page save — pure pattern matching on
[[wikilinks]], zero LLM calls. An edge carries a
type (works_at, founded, discusses,
derived_from, supersedes) and the surrounding context.
Two grades exist and it matters: a real wikilink, versus a
mentions edge inferred from body text. The weak kind is
excluded from backlink-count ranking and only counts toward graph
traversal, so a name appearing everywhere doesn't inflate its own importance.
Point at a page that doesn't exist yet and a stub is created. That's how the graph grows without anyone curating it.
Flat labels from frontmatter.
The simplest primitive: a YAML array, reconciled into a table on import. No hierarchy, no types, no relationships. They filter and they feed the salience score — that's it.
One sharp edge: since v0.41.37 tag reconciliation is add-only. Removing a tag from frontmatter no longer removes it from the database, because enrichment writes tags of its own that a re-index must not wipe.
This is the distinction the codebase calls out by name as the category error. Both are claims. They differ in who said it, when it was captured, and how long it lives.
| facts — hot memory | takes — cold epistemology | |
|---|---|---|
| whose | Only yours. What you said. | Anyone's. Attributed by holder. |
| when | Real time, per conversation turn | Retrospective, extracted from pages |
| kinds | event · preference · commitment · belief · fact | take · fact · bet · hunch |
| carries | The claim, its kind, visibility | Holder, weight, since-date, source |
| read via | gbrain recall | gbrain takes list / search |
| lifecycle | Promoted overnight, then marked consolidated | Durable; graded and scored over time |
What you told the brain, captured as you say it.
“I have a call with Arya Thursday.” “I don't drink coffee.” “We decided to cut the second podcast season.” A cheap model pulls these out per turn and files them on the relevant entity page.
Each row carries visibility: world | private. Private text is
stripped by the chunker before embedding — it never becomes a
vector, never appears in a search result, and never leaves the machine through the
MCP surface.
Who believes what, how strongly, since when.
A take is a claim plus an owner. holder=self kind=bet
“the AI hardware wave peaks in 2027” at weight 0.7 is a different
object from the same sentence held by someone you interviewed. Four kinds:
an opinion, a verifiable fact, a dated prediction, an intuition.
Weights use 0.05 increments on purpose. The extraction guidance is explicit that 0.74 is false precision, that amplifying someone else's claim caps out around 0.55, and that self-reported numbers never reach the confidence of verified ones.
Because takes carry a weight and a date, they are the only thing in the system that can later be graded against what actually happened.
The reusable unit pulled out of long-form material.
A cheap model reads a transcript or article and extracts one to three atoms, each
typed from a closed list: insight, anecdote, quote, framework, statistic,
story angle, strategy angle, strategy, endorsement, critique, collection.
Each becomes its own page under atoms/{date}/.
This is the creator's primitive. Twelve podcast transcripts sitting inert become a few dozen individually addressable, retrievable pieces — the anecdote you half remember telling, findable without re-listening.
Atoms that recur, promoted into a real page.
Nightly, atoms are grouped by the concept they reference and tiered by how often the theme appears: ten or more is tier one, five is tier two, two is tier three. The top two tiers get a written narrative; the bottom gets a plain stub.
The tier is the finding. A theme reaching tier one means you have circled it ten separate times without noticing — which is a different and more useful signal than any single note.
Where unfiled things land, on purpose.
Two things share the name. gbrain capture writes to
inbox/YYYY-MM-DD-<hash> by default, so anything captured
without a destination clusters in one predictable place to triage later. And
~/.gbrain/inbox/ is a watched folder — anything dropped in from a
Shortcut, AirDrop, or Finder gets ingested.
The hash makes capture idempotent: the same text captured twice is one page, not two.
The two temporal page types.
Added for the Life Chronicle feature. An event under
life/events/ is something that happened on a date. A
diary entry under life/diary/ is what you thought about
it. Neither has facts mined out of it — they're read chronologically, not queried
for claims.
These power the chronicle verbs: what happened in a window, what happened on this day in previous years, what has changed since a date.
Two different things wearing one word.
The trust flag. Every operation runs with
remote: true|false, required by the type system. You at your own
terminal are local and see everything. Anything reaching the brain through MCP —
including an agent on this same machine — is remote, and gets private fact rows
and the entire takes fence stripped from its responses.
The install shape. A thin client is a gbrain with no local content at all, pointed at someone else's brain server over HTTP. Same commands, the data lives elsewhere. Local-only operations refuse with a specific hint rather than silently querying an empty database.
They are easy to confuse and they do completely different jobs.
Governed by gbrain.yml at the repo root. The split here is the useful lever:
storage: db_tracked:# version-controlled, human-curated- people/ - companies/ - concepts/ - ideas/ - projects/ db_only:# on disk + in the DB, auto-gitignored by sync- media/articles/# searchable, but never bloats the repo- media/x/# restore with: gbrain export --restore-only- meetings/transcripts/
Mark a directory db_only and its contents stay fully searchable while
never entering git. That is exactly the right home for hundreds of saved articles,
clipped threads, and transcripts — the bulk that you want retrievable but never want
to review in a diff.
The directory names come from the active schema pack. gbrain-base-v2 is the
default: 15 types, each declaring path_prefixes, whether facts get extracted
from it, and whether it participates in expertise routing.
| Type | Path prefixes | Extractable | Expert routing |
|---|---|---|---|
| person | people/ · person/ | no | yes |
| company | companies/ · products/ · orgs/ | no | yes |
| media | media/ | yes | no |
| writing | writing/ | yes | no |
| project | projects/ | no | no |
| concept | concepts/ | yes | no |
| atom | atoms/ | no | no |
| diary | diary/ | no | no |
| note | catch-all | yes | no |
Plus tweet, social-digest, analysis,
source, deal, email, slack,
event — and typed edges: works_at, founded,
invested_in, mentions, discusses,
derived_from, supersedes, redirects_to.
Never contains your knowledge. Wipe it and rebuild from the repo.
~/.gbrain/ ├── config.jsonbrain registry, embedding model, search mode├── brain/PGLite data dir, if using PGLite├── inbox/drop files here → ingested automatically├── autopilot.lock ├── advisor-history.jsonl └── audit/~25 weekly-rotated JSONL streams├── dream-budget-YYYY-Www.jsonl ├── batch-retry-YYYY-Www.jsonl ├── db-disconnect-YYYY-Www.jsonl ├── schema-mutations-YYYY-Www.jsonl ├── rerank-failures-YYYY-Www.jsonl └── … quality-probe, phantoms, shell-jobs, lock-renewal
gbrain doctor reads those audit streams to produce its health checks —
which is why a failure three days ago is still diagnosable today.
Two independent axes. A brain is a database. A source is
a repo inside it. One brain can hold your notes, a codebase, and an essay collection as
separate sources, each with its own sync state and a federated flag
controlling cross-source search. A .gbrain-source dotfile pins every command
run inside a directory to that source.
The docs say eight phases. src/core/cycle.ts declares nineteen, in strict
dependency order. Each is individually gated and can be run alone.
| # | Phase | Kind | What it does |
|---|---|---|---|
| 01 | lint | markdown | Frontmatter and structure hygiene |
| 02 | backlinks | index | Reconcile incoming links |
| 03 | sync | markdown | Git diff → database |
| 04 | synthesize | llm | Consolidate yesterday's conversations |
| 05 | extract | index | Materialise links and timeline |
| 06 | extract_facts | index | Rebuild facts from every ## Facts fence |
| 07 | extract_atoms | llm | Haiku pass: transcripts and articles → atom pages |
| 08 | resolve_symbol_edges | index | Two-pass code-symbol resolution |
| 09 | patterns | llm | Cross-session pattern detection |
| 10 | synthesize_concepts | llm | Atoms → tier-promoted concept pages |
| 11 | recompute_emotional_weight | index | Salience signal from takes and tags |
| 12 | consolidate | llm | Ephemeral memory → durable pages |
| 13 | propose_takes | llm | Scan your prose, propose gradeable claims to a review queue |
| 14 | grade_takes | llm | Retrieve evidence, judge model verdicts them (auto-resolve off by default) |
| 15 | calibration_profile | llm | Aggregate resolved takes into narrative bias statements |
| 16 | drift | llm | Judge soft-band takes against recent evidence (off, report-only) |
| 17 | schema-suggest | llm | Passive schema-evolution proposals |
| 18 | embed | index | Embed stale chunks |
| 19 | orphans / purge | index | Orphan detection, soft-delete purge |
Phases hold a database lock, are scoped global or per-source, meter their own budget to
~/.gbrain/audit/dream-budget-*.jsonl, and run singly:
gbrain dream --phase extract_facts.
Nothing in the marketing mentions them. The cycle reads your prose, extracts the claims you actually made, waits, grades them against what happened, and produces a profile of your standing biases. It turns a pile of opinions into a scored record of how your judgment performs.
Every essay, idea note, and diary entry you already have is input for this. Nothing else in a normal notes stack does it.
The reference schedule runs 20+ jobs. The docs' own advice: don't. Start with three.
# ── the three that matter ────────────────────────────────*/15 * * * * gbrain sync --repo ~/brain && gbrain embed --stale 0 2 * * * gbrain dream# the 19-phase cycle0 6 * * 1 gbrain doctor --json && gbrain embed --stale# ── add only as you add the matching integration ─────────*/30 * * * * email collector# → people pages*/30 * * * * x/twitter collector# → media pages0 10,16,21 * * 1-5 meeting sync# → attendee propagation0 10 * * 0 calendar sync
Every job that can notify you checks quiet-hours-gate.sh first. Held
output goes to /tmp/cron-held/ and must be folded into the
next morning briefing, or information is silently lost.
The docs put it bluntly: one 3 AM ping and you'll disable the whole system.
The agent reads your calendar for flights and out-of-office blocks, infers the destination timezone, and shifts quiet hours to follow you. No reconfiguration when you land.
gbrain autopilot --install
A launchd daemon that supervises a gbrain jobs work child, submits one
cycle job per interval, respawns on crash with a five-crash cap, and caps child memory
at 2048 MB. For a single laptop this is clearly the better path — the
supervision, locking, and memory ceiling are handled rather than hand-rolled.
The primitives only make sense in motion. Each flow below names what you actually type, what the system does with it, and which primitives it touches on the way through.
The lowest-ceremony path in. No destination, no decision, no filing.
You type one line.
gbrain capture "the podcast's best episodes all start with
a disagreement, not a topic"
A page is written to disk at
inbox/2026-08-08-3f1a9c02 — the hash is of the content, so
capturing the same sentence twice produces one page, not two.
The next sync reads it, chunks it, embeds it. It is now retrievable — from a phrase you don't remember writing, months later.
Later, the overnight cycle may notice this sentence is a claim and queue it as a proposed take. You never had to decide that up front.
Two verbs, two different jobs. Reach for the wrong one and you'll misjudge the tool.
When you want raw material to skim — a quote, a citation, a page you know exists:
gbrain search "consumer AI ideas I dropped"
Returns ranked pages. No LLM cost. Each result carries an evidence tag saying why it matched.
When you want the answer:
gbrain think "which consumer AI ideas did I abandon,
and what was the common reason?"
Same retrieval, then a written answer with citations — and a note on what the brain doesn't know. That last part is the differentiator: it tells you when a page is stale, when two pages contradict, when there's a hole.
When a result surprises you, ask why it ranked where it did:
gbrain search "…" --explain# every boost that firedgbrain search diagnose "…" --target writing/no-way-back
This is where the graph actually grows, and it costs nothing. The trigger is a
line in your CLAUDE.md telling the agent to search first and write
decisions back.
You settle something out loud: “we're dropping the second podcast season, the transcripts are worth more as atoms.”
The agent calls put_page and writes a page containing a wikilink:
# decisions/2026-08-08-podcast-s2.mdDropping season two of [[projects/podcast]]. The dozen existing transcripts are more valuable mined as atoms than extended.
The auto-linker fires on the save. It finds [[projects/podcast]]
and writes a typed link row. Zero LLM calls —
this is pattern matching.
If that page didn't exist, a stub is created. Point at something absent and the graph grows a node for it.
From then on, anything asking about the podcast surfaces this decision through a backlink — not because the words matched, but because the two pages are factually connected.
The longest loop in the system and the one nothing else in a notes stack does. It only works because a take carries a weight and a date.
You wrote an essay months ago containing a real claim — “Hong Kong's tech scene won't recover without unlocking the tools, not the funding.” It is prose. Nothing is tracking it.
Overnight, propose_takes reads your prose and pulls out the
gradeable claims. It does not write them into your brain —
they land in a review queue.
You review, and the ones you accept get written into a ## Takes fence:
gbrain takes propose# accept / reject the queue
Now it is a take: holder self, kind
bet, a weight, a since-date.
Time passes and evidence accumulates in the brain.
grade_takes retrieves what's relevant and asks a judge model to
verdict the claim. Auto-resolve is off by default — it
proposes a verdict, you keep the call.
Across enough resolved takes, calibration_profile writes the
output that matters: two to four sentences describing your standing biases,
plus a Brier score. Not what you believe — how your believing
performs.
gbrain takes scorecard
Transcripts are the worst-shaped content in any vault: enormous, low signal density, impossible to skim. Atoms are the answer, and they're off by default.
Switch to a schema pack that declares the atom phases. The default pack doesn't, so nothing happens until you do this.
gbrain config set schema_pack gbrain-creator
Overnight, extract_atoms reads each transcript and pulls one to
three typed atoms — an anecdote here, a framework there, a
statistic worth reusing. Each becomes its own page. Capped at $0.30 per
source per run.
Then synthesize_concepts groups atoms by theme and tiers them by
recurrence. A theme you circled ten separate times becomes a tier-one
concept page with a written narrative.
The payoff is asymmetric. You can now find the story you half-remember telling without re-listening to an hour of audio — and the tier count tells you which of your themes are actually load-bearing.
The moment the brain stops being one pile. Drafts, client work, and anything half-formed belong behind a boundary.
Register the repo as a source inside the same brain:
gbrain sources add drafts --path ~/brain/sources/drafts gbrain sources add code --path ~/dev/some-project
Turn federation off for the one that should stay quiet:
gbrain sources drafts --federated false
Its pages now answer only when you name the source. Unqualified queries behave as if it isn't there.
Drop a .gbrain-source dotfile in each checkout. Now every command
you run inside that directory is scoped to it automatically — no flags, ever.
Each source keeps its own sync state, so they import independently and a slow one never blocks the rest.
Facts flow to takes, one way, once a night. The consolidate phase groups
your hot facts by entity, deduplicates them against takes that already exist, promotes
the durable ones with proper attribution and weight, and marks the originals consumed.
Nothing flows back. A take is never demoted to a fact — which is why
gbrain recall (this week's conversation memory) and
gbrain takes list (the durable belief record) return genuinely different
things and both are worth asking.
macOS 26.5 Tahoe, Apple Silicon. Two things in the standard quickstart do not apply — both are handled below.
They all begin gbrain init --pglite. PGLite's embedded WASM engine is
incompatible with macOS 26.x on Apple Silicon — it crashes. PGLite is
also single-writer, so gbrain serve and gbrain sync fight over
the write lock. Two independent reasons to go straight to real Postgres.
Only libpq (the client) is installed today. This adds the server.
brew install postgresql@17 pgvector brew services start postgresql@17 createdb gbrain psql gbrain -c 'CREATE EXTENSION IF NOT EXISTS vector;'
From GitHub only. The npm package called gbrain is an unrelated project
that will shadow the real binary on your PATH.
bun install -g github:garrytan/gbrain gbrain --version# expect 0.42.74.0 or later
gbrain init silently applies tokenmax — the most expensive
corner of a 25× spread. Choose deliberately.
| Mode | Haiku 4.5 | Sonnet 4.6 | Opus 4.7 |
|---|---|---|---|
| conservative | $40/mo | $120/mo | $200/mo |
| balanced | $100/mo | $300/mo | $500/mo |
| tokenmax | $200/mo | $600/mo | $1,000/mo |
Figures are per 10,000 queries/month and scale linearly. A personal brain runs a few
hundred — divide by 20 or more. conservative is the right default here.
gbrain init gbrain config set search.mode conservative gbrain doctor
gbrain wants a git repo; iCloud Drive is not one, and git inside iCloud corrupts both. Mirror one way — the vault stays the only thing you ever write to, and you get free version history of your notes as a side effect.
mkdir -p ~/brain/sources/noto && cd ~/brain git init rsync -a --delete \ "$HOME/Library/Mobile Documents/com~apple~CloudDocs/Noto/" \ ~/brain/sources/noto/ git add -A && git commit -m "import vault"
Bulk captured material stays searchable but out of the repo. Write
~/brain/gbrain.yml:
storage:
db_tracked:
- sources/noto/Writings/
- sources/noto/Projects/
- sources/noto/Ideas/
- sources/noto/Daily Notes/
db_only:
- sources/noto/Captures/ # searchable, never in a diff
Ask three questions you genuinely cannot answer today. If think doesn't
beat ten minutes of manual searching, stop here — don't build the rest on faith.
gbrain import ~/brain/ --no-embed
gbrain embed --stale
gbrain think "what have I written about consumer AI ideas,
and which ones did I abandon and why?"
gbrain think "what recurring themes run through my essays?"
gbrain think "what did I save about note-taking systems,
and what's the through-line?"
Then paste the brain-first protocol into your global CLAUDE.md: search
the brain before answering or asking; write decisions back with put_page;
cite the page you used.
claude mcp add gbrain -- gbrain serve
Note: stdio MCP exposes the unfiltered registry — roughly 120 tools, not the "30+" the README advertises.
The auto-linker is pure pattern matching on wikilinks — zero LLM calls. A vault with
almost no links produces almost no graph. Check the size of the prize before spending
on it: doctor reports how many bare links would resolve.
gbrain doctor# look for link_resolution_opportunitygbrain config set link_resolution.global_basename true gbrain extract links --source db --dry-run | head -20 gbrain extract links --source db gbrain stats# links should now be > 0
Only after step 6 proved the value and step 8 measured the delta.
gbrain autopilot --install gbrain autopilot --status
Your existing note CLI stays the only writer to the vault — capture doesn't change at all. gbrain reads a mirror and writes only into its own source. Capture surface and query surface stay cleanly separated, and nothing an agent does can corrupt the notes you actually wrote.