v0.18.0
skills/migrations/v0.18.0.md
version: 0.18.0
feature_pitch:
headline: "Multi-source brains: one DB, many repos. Federated and isolated sources coexist."
description: |
v0.17.0 introduces sources as a first-class primitive. A single
gbrain backend can now hold multiple knowledge repos (wiki, gstack,
yc-media, garrys-list, etc.) with clean scoping. Every page, file,
and ingest_log row is scoped to a sources(id) row. Slugs are
unique PER source, not globally — so two sources can both have
topics/ai and they're different pages.
Per-source federation controls whether a source participates in
unqualified default search. `federated=true` (the default source
post-upgrade) joins the cross-source recall pool. `federated=false`
is isolation — only searched when explicitly named via `--source`.
This supports both "unified knowledge brain" (wiki + gstack, both
federated) and "purpose-separated brains" (yc-media + garrys-list,
both isolated) at the same time.
Per-directory default via `.gbrain-source` dotfile walk-up +
`GBRAIN_SOURCE` env var. Matches how kubectl / terraform / git
scope context. `cd ~/yc-media && gbrain query "X"` just works.
recipe: docs/guides/multi-source-brains.md tiers: null
v0.17.0 Migration: Multi-source brains
Audience: host agents reading this after gbrain apply-migrations
has run. v0.17.0 installs a schema primitive for multi-source and
exposes a sources CLI subcommand. Existing single-source brains
keep working unchanged — they live under a seeded default source
that preserves all prior behavior.
Mechanical migration: automatic, no action required
gbrain upgrade chains to gbrain apply-migrations --yes, which
runs:
- migration v16 — creates the
sourcestable, seedsdefaultwith{"federated": true}config, inherits your pre-v0.17sync.repo_pathandsync.last_commitinto the default row. - migration v17 — adds
pages.source_id TEXT NOT NULL DEFAULT 'default' REFERENCES sources(id). Swaps the globalUNIQUE(slug)constraint for compositeUNIQUE(source_id, slug). Engine upserts simultaneously re-targetON CONFLICT (source_id, slug)so the constraint swap and the write path land atomically.
Both migrations are idempotent. Safe to re-run.
Later point releases (v0.17.1 and v0.18.0) will layer:
- v0.17.1: ACL enforcement via a caller-identity primitive (the
JSONB slot for
access_policyships now; enforcement waits for identity to be designed). - v0.18.0: Session ingest (
.jsonltranscripts, raised size cap, session PageType) AND per-source retention/TTL at the same time.
What's new for agents
sources CLI subcommand
gbrain sources add <id> --path <p> [--name <n>] [--federated|--no-federated]
gbrain sources list [--json]
gbrain sources remove <id> [--yes] [--dry-run] [--keep-storage]
gbrain sources rename <id> <new-display-name>
gbrain sources default <id>
gbrain sources attach <id> # write .gbrain-source in CWD
gbrain sources detach # remove .gbrain-source
gbrain sources federate <id>
gbrain sources unfederate <id>
Source id rules: [a-z0-9](?:[a-z0-9-]{0,30}[a-z0-9])? — start + end
with alnum, optional interior hyphens, max 32 chars. Immutable after
creation (rename only changes the display name). Used as the stable
citation key in [source:slug] references.
Per-directory default
Running gbrain sources attach gstack inside ~/.gstack/ writes a
.gbrain-source file containing the single word gstack. Any
gbrain command run from that directory (or any subdirectory) auto-
selects gstack as the default source. gbrain sources detach
removes the dotfile.
Resolution priority for the source a command targets:
- Explicit
--source <id>flag. GBRAIN_SOURCEenv var..gbrain-sourcedotfile in CWD or any ancestor.- Registered source whose
local_pathcontains CWD (longest prefix wins — nested~/gstack+~/gstack/plansresolves toplanswhen deeper). - Brain-level default set via
gbrain sources default <id>. - Literal
default(backward-compat fallback).
Federation semantics
federated=true(only thedefaultsource has this out of the box, by migration): appears in unqualifiedgbrain search "X"results.federated=false(new sources default to this): only appears when--source <id>is passed.
Interactive gbrain sources add prompts for federation; non-
interactive uses --federated / --no-federated. Flip later with
gbrain sources federate <id> / unfederate <id>.
Citation contract (for agents)
When agents get multi-source search results they MUST cite pages
in [source-id:slug] form. Example:
You told me about the distillation protocol — see [wiki:topics/ai] and [gstack:plans/multi-repo] for where this came from.
Citations are keyed on sources.id (immutable), never
sources.name (mutable display). If a user renames a source via
gbrain sources rename, existing citations stay valid.
What's NOT in v0.17.0 yet
The following land in later Steps of this release cycle (already on the branch but gated until the matching code ships):
ingest_log.source_id— lands with Step 5 sync rewrite.links.resolution_type+ qualified[[source:slug]]wikilink parsing — lands with Step 4 link-extraction rewrite.files.page_slug → page_idFK rewrite +file_migration_ledger- storage object prefixing — lands with Step 7 storage backfill.
- Source-aware search dedup — lands with Step 3.
gbrain sources import-from-github <url>— deferred to a patch release after the plumbing stabilizes.
Existing callers continue to work against the default source. No
agent behavioral change is required; the new capabilities are
opt-in via the new sources CLI surface.
Host-repo actions
None required. If your host agent manages the brain via the
standard gbrain sync flow, it continues to target the default
source and sees no behavioral change. To start using multi-source:
# Register a new source
gbrain sources add gstack --path ~/.gstack --no-federated
# Pin that directory to it so no --source flag is needed
cd ~/.gstack
gbrain sources attach gstack
# Ingest
gbrain sync --source gstack
Or see docs/guides/multi-source-brains.md for the full three
canonical scenarios (unified, purpose-separated, mixed).