Tutorial: Build your first schema pack
You’ll fork the bundledgbrain-base pack, add a custom researcher page type, import a handful of placeholder researcher pages, backfill their page.type column with one command, then prove the wiring works by running modusbrain whoknows and seeing your new type surface in results. End state: a forked-and-active pack on disk, ~5 pages typed as researcher, and a query that proves the pack-aware routing fires end-to-end.
Want the WHY before the HOW? Read what-schemas-unlock.md first — 7 concrete use cases (4000 invisible meetings, the founder ops brain, the research brain, the legal brain, the team brain, agent-as-co-curator) plus the structural argument for why types matter at query time. Then come back here for the 5-minute walkthrough.
The whole walkthrough takes about 5 minutes. You’ll see something working by step 3.
What you’ll need
- modusbrain v0.40.7.0 or later (
modusbrain --versionto check) - A brain that’s been initialized (
modusbrain initalready run; either PGLite or Postgres is fine) - A terminal you can paste commands into
Step 1: See what pack is active today
source_tier: "default" means you haven’t customized anything — you’re on the bundled pack. page_types_count: 22 is the universal starter (person, company, meeting, note, etc.).
You can’t mutate bundled packs directly. Step 2 forks it so you have something writable.
Step 2: Fork the bundled pack
Forked 'gbrain-base' → 'mine' at ~/.modusbrain/schema-packs/mine/pack.json.
The fork is a byte-for-byte copy of gbrain-base living at ~/.modusbrain/schema-packs/mine/pack.json. Now you have a writable pack you can mutate.
Step 3: Activate the fork
Pack: mine (json) ... Active.
Run modusbrain schema active --json again to confirm pack_name is now mine and source_tier is home-config (read from ~/.modusbrain/config.json).
You’ve already accomplished something visible — the active pack changed, and any future query will route through your fork. The next four steps add a custom type and prove it works.
Step 4: Add a researcher type
Pack: mine (json) + Sha8: <prev> → <new>.
What just happened:
- The mutation went through
withMutation’s 8-step skeleton: bundled-guard → per-pack lock → read → mutate → file-plane lint validation → atomic write → audit log → cache invalidation. - The pack now declares
researcheras an entity primitive bound topeople/researchers/, markedextractable: true(eligible for facts extraction) andexpert_routing: true(surfaces inwhoknowsqueries). - An audit row landed in
~/.modusbrain/audit/schema-mutations-YYYY-Www.jsonlwith your type name SHA-8-redacted and the prefix’s first segment only (people) for privacy.
Step 5: Import some placeholder researcher pages
You need pages underpeople/researchers/ for the next step to do anything. If your brain repo already has them, skip ahead. If not, drop 3-5 placeholder markdown files into <your-brain-repo>/people/researchers/ and import:
type column will still be empty — the new type was added to the pack AFTER these pages already existed (the typical real-world scenario for an agent walking into an existing brain).
Step 6: See the gap with stats
untyped_pages: 3 (or however many you just imported) and dead_prefixes: [] — your new prefix has 3 matching pages, so it’s not dead.
The 3 researcher pages are “orphaned” by type even though they live in the right directory. The next step backfills them.
Step 7: Backfill with sync --apply
First dry-run to see what would happen:
would_apply: 3 is what you’d touch. sample_slugs is the agent’s drilldown signal — if those slugs look wrong, abort. They look right, so apply:
total_applied: 3. The UPDATE ran in chunks of 1000 (yours fit in one chunk) and never wedged any concurrent writer.
Step 8: Prove the wiring works
researcher, not person or company.
This is the load-bearing demonstration of T1.5 wiring. Pre-v0.40.7.0, whoknows hardcoded ['person', 'company'] as the eligible types and would have ignored your researcher pages entirely. The v0.40.7.0 wiring consults the active pack’s expert_routing: true types via expertTypesFromPack(pack.manifest), so your custom type now routes through expert search.
What you built
You now have:- A fork of
gbrain-basenamedmineat~/.modusbrain/schema-packs/mine/pack.json, active in your brain via~/.modusbrain/config.json. - A
researcherpage type registered in the pack withentityprimitive,people/researchers/prefix,extractable: true,expert_routing: true. - 3 pages typed as
researcher(backfilled from disk viamodusbrain schema sync --apply). - A query path that routes through the new type:
modusbrain whoknowsreads the pack and includesresearcherin its type filter.
Next steps
Add a link verb. Aresearcher can author a paper. To model that:
researcher --(authored)--> paper.
Add aliases for query closure. If you want modusbrain query researcher to also surface person rows (because researchers ARE people):
skills/conventions/schema-evolution.md for the decision tree on when to add types vs aliases vs prefixes. The short version: <20 pages → don’t pack-codify; 20-100 → alias on existing type; 100+ → first-class type.
Lint your pack before shipping. The 11-rule lint surface (with the optional --with-db flag for DB-aware checks) catches dangling references, prefix collisions, and dead-corpus warnings:
~/.modusbrain/schema-packs/mine/ is a git repo, commit pack.json and push. Your pack survives across machines, and the mutation_count_anomaly lint rule will nudge you when you hit >50 mutations in a week (the “you should be committing this” signal).
For agents (MCP): the same operations are reachable over HTTPS MCP via 9 new ops. Register an admin-scope OAuth client and schema_apply_mutations lets a remote agent compose multi-step refactors as one atomic batch. The batched MCP op + per-pack lock + audit log are the load-bearing primitives that make remote schema authoring safe. See skills/schema-author/SKILL.md for the agent dispatcher.
Undo a mistake. Every mutation primitive has an inverse (remove-type, remove-alias, remove-prefix, remove-link-type, set-extractable false, etc.). If you fork twice and want to revert, modusbrain schema downgrade restores the previous active pack from ~/.modusbrain/schema-pack-history.jsonl.
Related docs
- Reference:
modusbrain schema --helpfor the full 22-verb CLI surface; CLAUDE.md’s “Schema Cathedral v3 (v0.40.7.0)” section for the module-by-module architecture. - How-to:
skills/schema-author/SKILL.md— the agent dispatcher with the 7-phase workflow (brain → assess → propose → apply → sync → verify → commit). - Explanation:
skills/conventions/schema-evolution.md— when to add a type vs alias vs prefix. - Plan + decisions: the original design captured 21 decisions including the bundled-pack guard rationale (D6), the empty-filter fallback contract (D4), and the MCP non-localOnly trust posture (D2). Lives in
~/.claude/plans/system-instruction-you-are-working-recursive-thacker.md(private).