Upgrading Downstream Agents
ModusBrain ships skills inskills/. Downstream agents (custom OpenClaw deployments,
agent forks of any kind) often copy these skill files into their own workspace and
diverge over time — adding agent-specific phases, removing irrelevant ones, tightening
language. Once that happens, modusbrain can’t push updates to those forks. The agent has
to apply the diffs by hand.
This doc lists the exact diffs each downstream agent needs to apply when upgrading.
Cross-reference against your fork’s local skill files.
Why this exists
modusbrain upgrade ships the new binary. modusbrain post-upgrade [--execute --yes] runs
the schema migrations and backfills the data. But the skill files themselves
that tell the agent how to behave — those are user-owned. If your ~/git/<your-agent>/workspace/skills/brain-ops/SKILL.md
says # Based on modusbrain v0.10.0 at the top, it doesn’t know about v0.12.0 features.
The agent will keep manually calling modusbrain link after every put_page (now redundant —
auto-link does it), miss out on modusbrain graph-query for relationship questions, and
not know to backfill the structured timeline.
How to apply
- Identify your forked skill files. Typically at
~/git/<your-agent>/workspace/skills/or wherever your agent’s skill directory lives. - For each skill listed below, find the matching phase/section in your fork.
- Apply the diff (paste the new block in the indicated location).
- Update the version banner at the top of your fork (
# Based on modusbrain v0.12.0). - Verify: ask the agent to write a test page and confirm the response includes
auto_links: { created, removed, errors }.
1. brain-ops/SKILL.md
Where: Insert a new### Phase 2.5 section immediately after ### Phase 2: On Every Inbound Signal.
Why: Phase 2.5 declares that auto-link runs automatically. Without this, the
agent’s mental model says it must call modusbrain link after every put_page, which
is now redundant and can cause double-add warnings.
2. meeting-ingestion/SKILL.md
Where: Append to the end of### Phase 3: Attendee enrichment.
Why: Eliminates redundant modusbrain link calls per attendee (auto-link handles them
when the meeting page references attendees as [Name](people/slug)).
### Phase 4: Entity propagation, the line “Back-link from entity page
to meeting page” can be replaced with:
3. signal-detector/SKILL.md
Where: Append to the end of### Phase 2: Entity Detection.
Why: Same logic as brain-ops — eliminates manual modusbrain link after writing
originals/ideas pages that reference people or companies.
4. enrich/SKILL.md
Where: Replace### Step 7: Cross-reference with the v0.12.0 version.
Why: Step 7 used to be primarily about creating links between related entity
pages. With auto-link, that’s automatic. Step 7 is now about content updates,
not link creation.
Old (delete):
After all four diffs are applied
-
Bump the version banner at the top of each forked file:
-
Run the v0.12.0 backfill (this populates the graph for your existing brain):
The v0.12.0 release wires post-upgrade to call
apply-migrations --yesautomatically, which runs the v0_12_0 orchestrator (schema → config check →extract links --source db→extract timeline --source db→ verify). Idempotent; cheap when nothing is pending. -
Verify auto-link works: ask the agent to write a test page that references
[Some Person](people/some-person). Confirm the put_page response includesauto_links: { created: 1, removed: 0, errors: 0 }. -
Verify graph traversal works:
Should return an indented tree of typed edges.
v0.12.2 hotfix (data-correctness, no skill edits)
v0.12.2 is a Postgres data-correctness hotfix. No forked skill files need to change — the skill contracts are unchanged. But you DO need to run the migration, and you should know about one behavior change in markdown parsing.1. Run the migration (Postgres-backed brains)
v0_12_2 orchestrator runs modusbrain repair-jsonb automatically. It rewrites
rows where jsonb_typeof = 'string' across pages.frontmatter, raw_data.data,
ingest_log.pages_updated, files.metadata, and page_versions.frontmatter.
Idempotent, safe to re-run. PGLite brains no-op cleanly.
Verify after upgrade:
2. Recover any truncated wiki articles
If your brain imported wiki-style markdown before v0.12.2, some pages were silently truncated (any standalone--- in body content was treated as a
timeline separator). Re-import from source:
splitBody rebuilds compiled_truth correctly.
3. Know the splitBody contract going forward
splitBody now requires an explicit timeline sentinel. Recognized markers
(priority order):
<!-- timeline -->(preferred — whatserializeMarkdownemits)--- timeline ---(decorated separator)---directly before## Timelineor## Historyheading (backward-compat)
--- in body text is now a markdown horizontal rule, not a timeline
separator. If your agent writes pages with a bare --- delimiter, migrate to
<!-- timeline --> — the serializeMarkdown helper already does this.
4. Wiki subtypes now auto-typed
inferType now auto-detects five additional directory patterns as their own
page types (previously they all defaulted to concept):
If your skills or queries filter by
type=concept and expect wiki content in
that bucket, update them to include the new types.
v0.13.0 — Frontmatter Relationship Indexing
Verdict: no action required for most skills. v0.13 projects YAML frontmatter fields into the graph as typed edges. The ingestion API is unchanged — keep callingput_page with frontmatter the way you do today; the graph auto-populates behind the scenes.
Three skills get an optional new phase if you want to consume the new auto_links.unresolved response field. Without this, unresolvable frontmatter names silently skip (same as v0.12 behavior).
1. meeting-ingestion/SKILL.md (optional)
Where: Add a new section after “Phase 3: Write Meeting Page”.2. enrich/SKILL.md (optional)
Where: Add to the enrichment trigger list.3. idea-ingest/SKILL.md (optional)
Where: Same pattern as meeting-ingestion — checkauto_links.unresolved after put_page, route names to enrichment.
Unchanged skills (no diffs needed)
- brain-ops/SKILL.md — auto-link mechanics are internal; the write path stays the same.
- signal-detector/SKILL.md — signal capture path unchanged.
- query/SKILL.md —
traverse_graphnow returns richer results automatically. - daily-task-manager/SKILL.md, briefing/SKILL.md, citation-fixer/SKILL.md, media-ingest/SKILL.md — unchanged.
New edge types you can filter in graph queries
v0.13 edges carry newlink_type values. If your fork has graph-query skills that filter by type, these are now available:
works_at(person → company) — fromcompany:,companies:, orkey_people:founded(person → company) — fromfounded:invested_in(investor → deal/company) — frominvestors:orlead:led_round(lead → deal) — fromlead:yc_partner(partner → company) — frompartner:attended(person → meeting) — fromattendees:discussed_in(source → page) — fromsources:source(page → source) — fromsource:related_to(page → target) — fromrelated:orsee_also:
Migration timing
modusbrain upgrade takes 2-5 min on a 46K-page brain (one-time). Runs out-of-process via modusbrain post-upgrade. If your agent holds a DB connection during the upgrade, reconnect after; otherwise keep serving.
Type normalization NOT in v0.13
Legacy rows withlink_type='attendee' or link_type='mention' coexist with new 'attended' / 'mentions' rows. Your queries filtering on old type names keep working. A separate opt-in modusbrain normalize-types command in v0.14 handles the rename.
v0.14.0 shell jobs (optional adoption, no skill edits)
Adds ashell job type to Minions so deterministic cron scripts (API fetch, token
refresh, scrape + write) move off the LLM gateway. Zero tokens per fire. ~60%
gateway CPU headroom at typical scale. Feature is off by default, existing
installs keep running exactly as they did before. Nothing breaks.
To adopt, follow skills/migrations/v0.14.0.md. The short version:
- Set
MODUSBRAIN_ALLOW_SHELL_JOBS=1on the worker process, thenmodusbrain jobs work(Postgres). On PGLite, every crontab invocation uses--followfor inline execution; no persistent worker. - Classify each of your host’s cron entries: LLM-requiring (keep on gateway) vs
deterministic (candidate for shell). Typical splits:
- Deterministic → shell:
ycli-token-refresh,x-oauth2-refresh,x-shubham-unified,calendar-sync-to-brain,github-pulse,frameio-scan,flight-tracker,x-raw-json-backfill. - LLM-requiring → stay:
social-radar,content-ideas,adversary-vacuum,ea-inbox-sweep,morning-briefing,brain-maintenance.
- Deterministic → shell:
- For each deterministic cron, rewrite as:
- Watch
modusbrain jobs get <id>for exit_code / stdout_tail / stderr_tail on each fire. Compare against pre-migration behavior before approving the next batch.
modusbrain crontab-to-minions <file> helper is P1 in TODOS.
v0.16.0: durable agent runtime
v0.15 shipsmodusbrain agent run / modusbrain agent logs, a new subagent handler
type in Minions, and a plugin contract for host-repo subagent defs. None of the
existing skills need surgery. The question for downstream agents is how to
adopt the new runtime, not how to patch around a breaking change.
1. Run a worker with an Anthropic key
The subagent handlers (subagent and subagent_aggregator) are always
registered on the worker. No separate opt-in flag — ANTHROPIC_API_KEY is
the natural cost gate (no key, the SDK call fails on the first turn), and
who-can-submit is already protected (PROTECTED_JOB_NAMES + trusted-submit:
MCP callers get permission_denied; only modusbrain agent run can insert
these rows).
2. Ship your subagents as a plugin (OpenClaw + similar)
Move your custom subagent definitions out of your modusbrain fork and into your own repo as a plugin. Concretely:modusbrain.plugin.json:
subagents/*.md is a plain-text agent definition — YAML frontmatter +
body-as-system-prompt. Recognized frontmatter fields: name, model,
max_turns, allowed_tools (must subset the derived brain-tool registry).
Turn it on:
[plugin-loader] loaded '<name>' v<ver> (N subagents)
per plugin; any rejection (bad manifest, unknown tool in allowed_tools,
version mismatch) shows up as a loud warning at startup, not a silent dispatch-
time failure. See docs/guides/plugin-authors.md for the full contract.
3. Replace ephemeral subagent runs with durable ones
If your agent currently spawns ephemeral subagents (OpenClawAgent(), ad-hoc
Anthropic API calls, etc.) for work that should survive crashes, sleeps, or
worker restarts, migrate those to modusbrain agent run. The durability is free:
subagent_messages, every tool call is a two-phase
ledger, and modusbrain agent logs <job> shows where it died + what the last
successful call returned. No more “re-run from scratch because the session
context evaporated.”
4. put_page from subagents writes under an agent namespace
If you adopted the v0.15 subagent runtime, note that put_page calls
originating from a subagent’s tool dispatch MUST target
wiki/agents/<subagent_id>/.... The schema shown to the model enforces this
on first try; a server-side fail-closed check rejects anything else. This
does NOT affect your skill files, CLI put_page calls, or MCP put_page —
only tool-dispatched writes from inside an LLM loop.
Aggregation output (the final “here’s what all N children found” brain page)
goes via a separate trusted CLI path, not through a subagent tool call, so
it can write anywhere you want.
Iron rule: never grant an agent write access beyond its namespace. The
server-side check exists because dispatcher bugs happen; treat it as defense
in depth, not the primary boundary.
v0.22.4 — frontmatter-guard adoption
1. Stop hand-rolling frontmatter validators
If your fork has scripts that calljs-yaml directly to validate brain page
frontmatter, replace them with modusbrain frontmatter validate calls. The CLI
covers the seven canonical error classes and ships a --json envelope that’s
stable across releases.
markdown export instead of duplicating logic:
2. Drop any references to lib/brain-writer.mjs
If your fork’s skills or scripts referenced an aspirational
lib/brain-writer.mjs (it never shipped — the spec was in PR #392 and never
landed), replace those references with the modusbrain CLI. The frontmatter-guard
skill lives at skills/frontmatter-guard/SKILL.md and points at
modusbrain frontmatter validate / audit / install-hook.
3. Wire the doctor subcheck into your health pipeline
modusbrain doctor now reports frontmatter_integrity automatically. If your
fork has a custom health pipeline (e.g. a daily Slack post about brain
health), pull from modusbrain doctor --json and surface the
frontmatter_integrity row counts.
4. (Optional) Install the pre-commit hook on brain repos
For sources backed by git, the v0.22.4 install-hook helper drops a pre-commit script that blocks commits with malformed frontmatter:docs/integrations/pre-commit.md for
the full recipe.
5. Migration ergonomics — read pending-host-work.jsonl
Aftermodusbrain apply-migrations --yes runs the v0.22.4 audit, your agent
should read ~/.modusbrain/migrations/pending-host-work.jsonl (filter to
migration === "0.22.4") and walk each entry’s command field. Each entry
points to a per-source modusbrain frontmatter validate <source_path> --fix
command — surface counts to the user, get explicit consent, then run.
The migration is audit-only. It never mutates brain content during
apply-migrations. Your agent runs the fix command with user consent.
Future versions
When modusbrain ships a new version, this doc will be updated with the diffs for that version. Each new version appends a section; old sections stay so you can catch up multiple versions at once. To check what your fork is missing:v0.36.5.0 — Free-form secret inheritance for shell jobs calling modusbrain CLI
The change. Shell-job params get a new inherit: field. Pass any
snake_case config-key name on it; the worker resolves the value from its
loadConfig() at child-spawn time and injects it into the child env. Names
land in the row; values never persist from inherit:. Validation runs
pre-enqueue in both submit paths (CLI + submit_job op), so a malformed
payload never lands in minion_jobs.data.
Why. Pre-v0.36.5.0, agents that wanted to call modusbrain from shell jobs
had to either write database_url to ~/.modusbrain/config.json plaintext or
pass env: { MODUSBRAIN_DATABASE_URL: "..." } per-job. Both left plaintext
secrets somewhere — disk or DB row. inherit: keeps names in the row and
resolves values at spawn time.
What your agent can do. inherit: is free-form. Pass any config-key:
database_url → MODUSBRAIN_DATABASE_URL, anthropic_api_key →
ANTHROPIC_API_KEY, voyage_api_key → VOYAGE_API_KEY, etc. The validator
does NOT police which config keys you inherit — the agent is in the same
uid as the worker, so it’s the agent’s call.
You can still use env:. v0.36.5.0 does not forbid env:{ ANYTHING }.
If you have a reason to put a value in the row plaintext (a non-secret
correlation token, or a secret you know is OK to persist), pass it via
env:. Prefer inherit: when you want the value out of the row.
Worker setup (one-time, per host):
modusbrain config set database_url postgresql://...(or any other key you want available for inherit)- OR put the key in
~/.modusbrain/config.jsondirectly - OR set
MODUSBRAIN_DATABASE_URL/DATABASE_URL/ per-provider env on the worker process
modusbrain config set <X> hint. No more silent “No database
URL” failures in child stderr minutes after submission.
Also new. A modusbrain doctor check home_dir_in_worktree warns if
~/.modusbrain/ lives inside a git worktree. A retroactive ~/.modusbrain/.gitignore
(single line *) is now laid down by every saveConfig() call AND by
modusbrain post-upgrade, so existing users get coverage without re-running
modusbrain init. Honest scope: the .gitignore covers casual git add but does
NOT cover already-tracked files, screenshots, backups, or git add -f.
Strategy framing. For agent-to-modusbrain calls, the new canonical guide is
docs/guides/agent-to-modusbrain.md. Two distinct surfaces: HTTP MCP via OAuth
for ops with MCP equivalents (search, query, put_page, etc.), and shell
job + inherit: for localOnly admin ops (sync, embed, dream,
doctor, etc.). Not a fallback hierarchy — pick by op.
Errors to handle (your agent submits shell jobs; surface these clearly):