---
version: 0.11.0
feature_pitch:
  headline: "GBrain Minions — durable background agents"
  description: "Turn any long-running agent task into a durable job that survives gateway restarts, streams progress, and can be paused, resumed, or steered mid-flight. Postgres-native, zero infra beyond your existing brain. Replaces flaky subagent spawns for multi-step work, parallel fan-out, and anything the user might ask about later."
  recipe: null
  tiers: null
---

# v0.11.0 Migration: Minions — host-agent instruction manual

**Audience: host agents (OpenClaw deployments, future
hosts) reading this AFTER `gbrain apply-migrations` has run its
mechanical phases.** The orchestrator in
`src/commands/migrations/v0_11_0.ts` is the runtime source of truth for
the mechanical work (schema, smoke, prefs, built-in cron rewrites,
autopilot install). This file tells the host agent how to finish the
host-specific work that can't be done host-agnostically.

## Check what gbrain left you

```bash
cat ~/.gbrain/migrations/pending-host-work.jsonl
```

Empty? You're done. The migration is complete; nothing for you to do.

Non-empty? Each line is a TODO. Each `type` routes to a section below.

## `type: cron-handler-needs-host-registration`

Gbrain rewrites cron entries whose handler name matches a gbrain
builtin (`sync`, `embed`, `lint`, `import`, `extract`, `backlinks`,
`autopilot-cycle`). For host-specific handlers (e.g. `ea-inbox-sweep`,
`frameio-scan`, `x-dm-triage`, `calendar-sync` on your OpenClaw), gbrain
leaves the manifest alone and emits a TODO with shape:

```json
{
  "type": "cron-handler-needs-host-registration",
  "handler": "ea-inbox-sweep",
  "cron_schedule": "0 */30 * * *",
  "manifest_path": "/path/to/cron/jobs.json",
  "current_cmd": "agentTurn ea-inbox-sweep",
  "recommendation": "Add a handler registration for ...",
  "status": "pending"
}
```

**Each TODO is a full skillify, not a drive-by handler add.** A properly
migrated cron meets every one of these gates before its JSONL row gets
marked `complete`:

### (a) Plugin contract

Read `docs/guides/plugin-handlers.md`. Add a `worker.register()` call
in your host's worker bootstrap:

```ts
import { MinionQueue, MinionWorker } from 'gbrain/minions';
const worker = new MinionWorker(engine, { queue: 'default' });
worker.register('ea-inbox-sweep', async (ctx) => {
  // Host-specific agent turn.
});
// ...register every TODO handler...
await worker.start();
```

### (b) Ship the bootstrap in your host repo

Autopilot already spawns `gbrain jobs work` as a child. Configure it to
spawn your custom worker binary (e.g. `your-openclaw-worker`) instead, or
register handlers as a side-effect module that the stock worker loads on
startup. Either path is documented in `plugin-handlers.md`.

### (c) Unit tests for the handler body

Mock the LLM. Assert the happy path, the empty-input path, the
network-timeout path, the LLM-failure path. Every branch of the handler's
deterministic logic has a unit test.

### (d) Integration tests

Submit a real Minion job against your test environment. Assert:
- The job completes (not dead-lettered).
- The side effects happen (brain pages written, external API called).
- The result shape matches what your cron consumers expect.

### (e) LLM evals

Whatever prompt the handler runs, add a case in your eval suite. At
minimum: one happy case, one edge case, one adversarial case. If the
handler's job is "sweep the EA inbox," the eval asserts urgent items
surface and routine items don't.

### (f) Resolver trigger update

The host's AGENTS.md dispatcher used to reference the skill name
(`ea-inbox-sweep`) as an `agentTurn` route. Rewrite that route to
`submit_job` with the same trigger phrases — the user-facing language
doesn't change; the execution path does.

### (g) Resolver trigger eval

Add a test: feed each trigger phrase to the resolver, assert it routes
to the new `submit_job` path, not the old `agentTurn`. Without this
test, your router can silently keep old behavior and nobody notices
until the cron quietly breaks.

### (h) E2E smoke

One test that exercises the full pipeline: scheduler → `gbrain jobs
submit` → worker claim → handler body → side effect. Gated by your
host's E2E DB fixture.

### (i) Brain filing

If the handler writes brain pages, `brain/RESOLVER.md` needs an entry
for the directory. Orphaned brain pages are worse than no brain pages —
nobody can find them on read.

### (j) check-resolvable

Run `gbrain check-resolvable`. It validates reachability (is the skill
mentioned from RESOLVER.md?), MECE overlap, DRY, gap detection. If it
fails, fix the skill or extend an existing one instead of creating a
duplicate.

### Finalize

When all ten gates are green for a handler:

```bash
gbrain apply-migrations --yes
```

The orchestrator detects the newly-registerable handler, rewrites the
cron entry from `agentTurn ea-inbox-sweep` to `gbrain jobs submit ...`
with the right engine-aware form (PGLite uses `--follow`; Postgres uses
fire-and-forget + `--idempotency-key`), and marks the JSONL row
`status: "complete"`.

**Iron rule:** `scripts/skillify-check.ts <handler-code-path>` must
return 10/10 before you mark a handler migrated. No partial-credit
"we'll add tests later." See `skills/skillify/SKILL.md` for the meta.

## `type: agents-md-dispatcher-needs-host-review`

Gbrain injects a marker into each AGENTS.md pointing to
`skills/conventions/subagent-routing.md`. But if your dispatcher has
inline `sessions_spawn` routing logic (custom branching on tool names
or intent patterns), that's not a mechanical rewrite — it involves host
judgment about which routes should flip to `submit_job` and which should
stay on `sessions_spawn` for real-time tasks.

Read `skills/conventions/subagent-routing.md`. Walk your dispatcher's
`sessions_spawn` blocks. For each one, decide:

- **Brain-write, multi-step, user will ask later** → route through
  `submit_job` (Minions).
- **Real-time, user waiting, < 30s** → keep on `sessions_spawn`.
- **Somewhere in between** → `minion_mode: pain_triggered` is the
  right default; let the convention's pain signals decide.

Mark the JSONL row `status: "complete"` by appending a new line with
the same shape + updated status. Or: edit the dispatcher, rerun
`gbrain apply-migrations`, and let gbrain re-detect + update
automatically (it dedupes on file path, so no duplicate rows).

## When every TODO clears

```bash
gbrain apply-migrations --list
```

Should show v0.11.0 as `applied`. Your host is fully migrated to
Minions. Autopilot is dispatching through the queue, cron jobs are
durable, and every handler you registered appears in
`worker.registeredNames` on next worker startup.

## Related

- `skills/migrations/index.ts` (code, not file) — the TS registry the
  runtime consults; `apply-migrations --list` reads from here, not
  from this markdown file.
- `skills/conventions/cron-via-minions.md` — the rewrite pattern
  gbrain applies for builtins + the one your host ships for custom
  handlers.
- `skills/conventions/subagent-routing.md` — runtime dispatcher rules
  (native subagent vs Minion) that AGENTS.md should reference.
- `docs/guides/plugin-handlers.md` — the plugin contract for host
  handler registration.
- `skills/skillify/SKILL.md` — the 10-item checklist every handler
  must pass before its JSONL row clears.
- `scripts/skillify-check.ts` — machine-readable version of the
  checklist; use with `--json` in CI.
- `docs/guides/minions-fix.md` — user-facing troubleshooting for
  broken-v0.11.0 installs that never ran the migration at all.
