docs(v0.11.1): cron-via-minions convention, plugin-handlers guide, minions-fix, skill updates

New reference docs:
  - skills/conventions/cron-via-minions.md — the rewrite convention for
    cron manifests. Shows the Postgres (fire-and-forget + idempotency-
    key) vs PGLite (--follow inline) branch; explains why builtin-only
    auto-rewrite is safe + how host-specific handlers get the plugin
    contract.
  - docs/guides/plugin-handlers.md — the plugin contract for host-
    specific Minion handlers. Code-level registration via import +
    worker.register(), not a data file (Codex D: handlers.json was an
    RCE surface). Concrete TypeScript skeleton + handler contract
    (ctx.data, ctx.signal, ctx.inbox) + full migration flow from TODO
    JSONL to a rewritten cron entry.
  - docs/guides/minions-fix.md — user-facing troubleshooting for
    half-migrated v0.11.0 installs. Paste-one-liner for the stopgap,
    gbrain apply-migrations path for v0.11.1+, verification commands,
    failure-mode recipes.

Rewrites + updates:
  - skills/migrations/v0.11.0.md — body restored as the host-agent
    instruction manual. Audience is the host agent reading
    ~/.gbrain/migrations/pending-host-work.jsonl after the CLI
    orchestrator has done the mechanical phases. Walks each TODO type
    through the 10-item skillify checklist (plugin contract, ship
    bootstrap, unit tests, integration tests, LLM evals, resolver
    trigger, trigger eval, E2E smoke, brain filing, check-resolvable).
    Reverses the earlier "delete the body" decision (1B) because the
    body serves a different audience now — host-agent, not CLI
    documentation.
  - skills/cron-scheduler/SKILL.md — Phase 4 ("Register with host
    scheduler") now references cron-via-minions + plugin-handlers.
  - skills/maintain/SKILL.md — new "Fix a half-migrated install"
    section with the apply-migrations recipe.
  - skills/setup/SKILL.md — new Phase C.5 "One-step autopilot +
    Minions install (v0.11.1+)" explaining the four install targets
    + the OpenClaw auto-injection default.
  - docs/GBRAIN_SKILLPACK.md — Operations section adds the three new
    guides + the subagent-routing and cron-routing SKILLPACK notes
    (v0.11.0+).

All 167 related tests (conformance + resolver + skillify-check + v0_11_0
orchestrator) stay green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-18 13:58:56 +08:00
co-authored by Claude Opus 4.7
parent f9b71de10d
commit 5c55ab86b5
8 changed files with 589 additions and 128 deletions
+13
View File
@@ -49,11 +49,24 @@ Running a production brain.
| Guide | What It Covers |
|-------|---------------|
| [Reference Cron Schedule](guides/cron-schedule.md) | 20+ recurring jobs, quiet hours, dream cycle |
| [Cron via Minions](../skills/conventions/cron-via-minions.md) | Why scheduled work runs as Minion jobs, not `agentTurn`. Auto-applied by v0.11.0 migration for built-in handlers; host-specific handlers use the plugin contract below. |
| [Plugin Handlers](guides/plugin-handlers.md) | Registering host-specific Minion handlers via code (no data-file exec surface). |
| [Minions fix](guides/minions-fix.md) | Repairing a half-migrated v0.11.0 install. |
| [Quiet Hours & Timezone](guides/quiet-hours.md) | Hold notifications during sleep, timezone-aware delivery |
| [Executive Assistant Pattern](guides/executive-assistant.md) | Email triage, meeting prep, scheduling |
| [Operational Disciplines](guides/operational-disciplines.md) | Signal detection, brain-first, sync-after-write, heartbeat, dream cycle |
| [Skill Development Cycle](guides/skill-development.md) | 5-step cycle: concept, prototype, evaluate, codify, cron |
**Subagent routing (v0.11.0+):** agents that dispatch background work should route through
`skills/conventions/subagent-routing.md` — it reads `~/.gbrain/preferences.json#minion_mode`
and branches between native subagents and Minion jobs. The v0.11.0 migration auto-injects
a marker into AGENTS.md pointing at this convention.
**Cron routing (v0.11.0+):** scheduled work goes through Minions, not OpenClaw's `agentTurn`.
See `skills/conventions/cron-via-minions.md` for the rewrite pattern. The v0.11.0 migration
auto-rewrites entries whose handler is a gbrain builtin; host-specific handlers (e.g.
`ea-inbox-sweep`) need a code-level registration per `docs/guides/plugin-handlers.md`.
## Architecture
How to structure your system.
+125
View File
@@ -0,0 +1,125 @@
# Minions fix — repairing a half-migrated v0.11.0 install
**tl;dr:** if your gbrain upgrade to v0.11.0 left Minions half-wired
(no preferences, autopilot still inline, or cron jobs still on
`agentTurn`), run:
```bash
gbrain upgrade && gbrain apply-migrations --yes
```
If you're stuck on a v0.11.0 binary that predates `apply-migrations`,
paste the stopgap:
```bash
curl -fsSL https://raw.githubusercontent.com/garrytan/gbrain/v0.11.1/scripts/fix-v0.11.0.sh | bash
```
Then upgrade + run `apply-migrations` once v0.11.1 is installed.
## What went wrong
The v0.11.0 release shipped the Minions schema, worker, queue, and
migration skill at `skills/migrations/v0.11.0.md`. But `gbrain upgrade`'s
`runPostUpgrade()` only printed the feature pitch — it never executed
the migration steps. Users ended up with:
- Schema migrated to v7 (thanks to `gbrain init` on upgrade).
- Minions table created.
- Worker handlers registered (but no worker daemon running).
- No `~/.gbrain/preferences.json` (so `minion_mode` was unset).
- Autopilot still running sync/extract/embed inline, not dispatching
through Minions.
- AGENTS.md + cron manifests still referencing the old `sessions_spawn`
+ `agentTurn` routes.
## The fix (v0.11.1 binary or later)
`gbrain apply-migrations` is the canonical repair. It reads
`~/.gbrain/migrations/completed.jsonl`, sees v0.11.0 is pending (or
stopgap-partial), and runs the orchestrator's seven phases:
```
A. Schema gbrain init --migrate-only
B. Smoke gbrain jobs smoke
C. Mode prompt (or --yes default pain_triggered)
D. Prefs write ~/.gbrain/preferences.json
E. Host AGENTS.md marker injection + cron rewrites for gbrain
builtins; JSONL TODOs for host-specific handlers
F. Install gbrain autopilot --install (env-aware)
G. Record append completed.jsonl status:"complete"
```
If Phase E emits TODOs for host-specific handlers (Wintermute's
~29 non-gbrain crons, for example), the migration finishes with
`status: "partial"`. Your host agent walks the TODOs using
`skills/migrations/v0.11.0.md` + `docs/guides/plugin-handlers.md`, ships
handler registrations in the host repo, then you re-run
`gbrain apply-migrations --yes` — the newly-registerable cron entries
get rewritten and the JSONL rows mark `status: "complete"`.
## The stopgap (v0.11.0 binary, no apply-migrations yet)
`scripts/fix-v0.11.0.sh` is a shell script that does what apply-migrations
does from a bash environment without depending on any new CLI. It:
1. Runs `gbrain init --migrate-only` to ensure schema v7.
2. Runs `gbrain jobs smoke`.
3. Prompts for `minion_mode` (pain_triggered default on non-TTY).
4. Writes `~/.gbrain/preferences.json` atomically.
5. Appends `~/.gbrain/migrations/completed.jsonl` with
`status: "partial"` + `apply_migrations_pending: true` so a later
v0.11.1 `apply-migrations` run picks up where it left off.
6. Detects host agent repos and **prints** rewrite instructions (never
auto-edits from a curl-piped script — too high blast radius).
7. Tells the user to run `gbrain autopilot --install` as the one-stop
finisher (autopilot forks the Minions worker as a child; no separate
daemon to manage).
Once v0.11.1 is installed, the stopgap retires: the canonical fix
becomes `gbrain upgrade && gbrain apply-migrations`.
## Verify the fix landed
```bash
# 1. Preferences exist and are readable
cat ~/.gbrain/preferences.json
# 2. Migration recorded
cat ~/.gbrain/migrations/completed.jsonl
# 3. Autopilot is supervising a Minions worker child
gbrain autopilot --status
ps aux | grep 'jobs work'
# 4. Jobs show up in the queue
gbrain jobs list
# 5. Any host-specific TODOs still pending
cat ~/.gbrain/migrations/pending-host-work.jsonl 2>/dev/null || echo "(none — all host work is done)"
```
## If the fix fails
Each phase is idempotent. Re-running is safe. Common failure modes:
- **Phase B smoke fails:** the schema didn't apply. Check
`~/.gbrain/config.json` has a valid `database_url` (or `database_path`
for PGLite). Run `gbrain init --migrate-only` directly and look at
the error.
- **Phase F install fails:** your host environment doesn't match any
detected target. Pass `--target <macos|linux-systemd|ephemeral-container|linux-cron>`
explicitly.
- **Pending host work never clears:** your host agent hasn't shipped
handler registrations yet. Read
`~/.gbrain/migrations/pending-host-work.jsonl`, open
`skills/migrations/v0.11.0.md`, and follow the host-agent instruction
manual.
## Related
- `skills/migrations/v0.11.0.md` — full migration skill for host agents.
- `docs/guides/plugin-handlers.md` — plugin contract for host-specific
handlers.
- `skills/conventions/cron-via-minions.md` — the canonical cron rewrite
pattern.
+137
View File
@@ -0,0 +1,137 @@
# Plugin handlers — registering host-specific Minion handlers
GBrain's Minion worker ships with seven built-in handlers: `sync`,
`embed`, `lint`, `import`, `extract`, `backlinks`, `autopilot-cycle`.
These cover every background operation the gbrain CLI itself performs.
Host platforms (Wintermute, other OpenClaw deployments, future hosts)
register their own handlers via a plugin bootstrap that imports
`gbrain/minions`. No `handlers.json`-style data file — handlers are
code, loaded by the worker, with the same trust model as any other
code in the host's repo.
## Why code, not data
An earlier design draft shipped `~/.claude/gbrain-handlers.json` where
each entry was a shell command the worker would exec on job claim.
Codex flagged this as a durable RCE surface: an agent-writable data
file that spawns arbitrary shell. We dropped the data-file approach;
handlers are code that the host imports explicitly and ships through
code review.
## The plugin contract
A host worker bootstrap looks like this (TypeScript):
```ts
import { MinionQueue, MinionWorker } from 'gbrain/minions';
import type { BrainEngine } from 'gbrain/engine';
async function main() {
const engine: BrainEngine = /* your engine setup */;
await engine.connect({});
const worker = new MinionWorker(engine, { queue: 'default' });
// Register every host-specific handler the host's cron manifest references.
// Each handler returns a plain object (serialized as the job result).
// Throw on failure — the worker catches and retries per max_attempts.
worker.register('ea-inbox-sweep', async (ctx) => {
const slot = ctx.data.slot ?? new Date().toISOString();
// Host-specific agent turn: call your LLM, scan the inbox, write
// brain pages, return a summary. ctx.signal.aborted indicates the
// worker wants you to cooperate with shutdown — honor it.
return { swept: true, slot };
});
worker.register('morning-briefing', async (ctx) => {
/* host logic */
return { briefed: true };
});
// Call start() AFTER every handler is registered. The worker's
// stall-detector ignores jobs whose name is not in the registered set.
await worker.start();
}
main().catch(err => { console.error(err); process.exit(1); });
```
Ship this as a separate binary in the host repo (e.g. `wintermute-worker`)
or as a side-effect module that the stock `gbrain jobs work` command
auto-loads on startup (configurable via a host-provided entry point).
## Handler contract
Every handler receives a `MinionJobContext`:
```ts
interface MinionJobContext {
data: Record<string, unknown>; // job params (whatever the cron submit passed)
job: MinionJob; // full job row (id, queue, attempts, etc.)
signal: AbortSignal; // set to aborted when the worker is shutting down
inbox: MinionInbox; // read messages sent to this job while it runs
}
```
Return a serializable object on success. Throw on failure (the worker
will log + retry per `max_attempts`).
**Abort cooperation.** When `ctx.signal.aborted` becomes true, finish
gracefully. The worker will wait 30s for you to return before SIGKILL.
Long-running LLM calls should pass the signal through to whatever
network library they use.
**Idempotency.** The queue enforces unique `idempotency_key` at the DB
layer, so you don't need to worry about double-submits from a cron that
fires while the previous invocation is still running.
## Gbrain's migration flow
The v0.11.0 migration orchestrator (run by `gbrain apply-migrations`)
detects cron entries whose handler name is NOT in GBrain's builtin set
and emits a structured TODO to `~/.gbrain/migrations/pending-host-work.jsonl`.
Each TODO has 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 `ea-inbox-sweep` in your host worker bootstrap per docs/guides/plugin-handlers.md. Once registered, re-run `gbrain apply-migrations` to auto-rewrite this entry.",
"status": "pending"
}
```
The host agent walks these entries using `skills/migrations/v0.11.0.md`:
1. Read `~/.gbrain/migrations/pending-host-work.jsonl`.
2. For each `cron-handler-needs-host-registration` row, ship a handler
registration in the host's worker bootstrap following the pattern
above.
3. Deploy the updated worker.
4. Re-run `gbrain apply-migrations --yes`. The orchestrator now
recognizes the newly-registerable handler (worker writes the
registered names to a discovery file on startup) and rewrites the
cron entry to use `gbrain jobs submit`. The JSONL row is marked
`status: "complete"`.
## Trust boundary
Handler code runs inside the worker process with the same privileges
as the rest of the host binary. There is no elevation. But there is
also no runtime sandbox — handlers can read + write anywhere the
worker user can. Review handler PRs the same way you review any other
code that touches production data.
## Related
- `skills/conventions/cron-via-minions.md` — the rewrite convention
for cron manifests.
- `skills/migrations/v0.11.0.md` — how the migration orchestrator
drives the host agent through this work.
- `skills/minion-orchestrator/SKILL.md` — patterns for submitting,
monitoring, steering, and replaying jobs once the handler is live.
+93
View File
@@ -0,0 +1,93 @@
# Cron via Minions Convention
How cron-scheduled agent work is dispatched in a GBrain-backed install.
## Rule: scheduled work runs as Minion jobs, not `agentTurn`
When a cron fires, it should submit a Minion job. Not call OpenClaw's
native `agentTurn` (300s timeout, no durability, no transcript). Not
start an isolated session that races the gateway for resources.
```
# Bad: agentTurn with a fixed timeout, no durability.
{ "schedule": "*/30 * * * *", "kind": "agentTurn", "skill": "ea-inbox-sweep" }
# Good (Postgres): fire-and-forget submit with an idempotency key per
# cycle slot. The queue dedupes long-running overlaps at the DB layer.
{
"schedule": "*/30 * * * *",
"kind": "shell",
"cmd": "gbrain jobs submit ea-inbox-sweep --params '{\"slot\":\"$(date -u +%Y-%m-%dT%H:%M)\"}' --idempotency-key ea-inbox-sweep:$(date -u +%Y-%m-%dT%H:%M)"
}
# Good (PGLite): inline execution with --follow. PGLite's exclusive file
# lock blocks a separate worker daemon, so the cron runs the job directly.
{
"schedule": "*/30 * * * *",
"kind": "shell",
"cmd": "gbrain jobs submit ea-inbox-sweep --params '{}' --follow"
}
```
## Why
- **Durability.** Gateway restart mid-task? Worker picks the job up on
boot. No lost state.
- **Observability.** `gbrain jobs list` + `gbrain jobs get <id>` show
every run, its duration, its transcript, its token accounting.
- **Steering.** Running jobs accept inbox messages. "Skip the
newsletter thread, focus on the urgent DMs" lands as context on the
next iteration.
- **Concurrency safety.** Idempotency-key on the cycle slot means a cron
that fires during a still-running previous invocation produces a noop
at the queue layer. Without this, a 5-min cron running 8-min jobs
stacks 4 overlapping copies at steady state.
## Who registers the handler?
**GBrain only rewrites cron entries whose handler name matches a
gbrain builtin** (`sync`, `embed`, `lint`, `import`, `extract`,
`backlinks`, `autopilot-cycle`). For host-specific handlers
(`ea-inbox-sweep`, `morning-briefing`, whatever your deployment runs
on cron), the host platform ships the handler as code.
See `docs/guides/plugin-handlers.md` for the plugin contract. In short:
```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. Call whatever LLM + tools the host has.
// ctx.data contains the cron slot payload; return a result object.
});
await worker.start();
```
Ship the bootstrap in the host repo. Autopilot spawns the worker as a
child; the host's custom worker binary (or a side-effect module the
stock worker auto-loads on startup) registers handlers before `start()`.
## Off mode
Users who set `minion_mode: off` in `~/.gbrain/preferences.json` keep
using `agentTurn`. Respect that. No auto-rewrite.
## Forward note (v0.12.0)
GBrain v0.12.0 ships `gbrain cron`: a scheduler loop inside
`gbrain jobs work` that owns cron expressions natively — no more
handing off to host schedulers. Until v0.12.0 lands, the host
scheduler keeps firing on schedule; v0.11.1 only replaces the execution
layer (what the cron trigger *does*), not the scheduling layer.
## Related
- `skills/conventions/subagent-routing.md` — native subagents vs
Minions for ad-hoc (not cron-scheduled) work.
- `skills/minion-orchestrator/SKILL.md` — patterns for managing jobs
once they're in the queue.
- `skills/cron-scheduler/SKILL.md` — scheduling guidance (quiet hours,
staggering, idempotency). Now references this convention.
- `skills/migrations/v0.11.0.md` — how GBrain migrates an existing host
cron manifest to this convention.
+1 -1
View File
@@ -39,7 +39,7 @@ This skill guarantees:
- Override: user-awake flag (if user is active, quiet hours suspended)
- During quiet hours: save output to held queue
- Morning contact releases the backlog
4. **Register with host scheduler.** OpenClaw cron, Railway cron, crontab, or process manager.
4. **Register with host scheduler.** OpenClaw cron, Railway cron, crontab, or process manager. **Each registered entry should execute via Minions, not `agentTurn`.** See `skills/conventions/cron-via-minions.md` for the rewrite pattern (PGLite uses `--follow`, Postgres uses fire-and-forget + `--idempotency-key` on the cycle slot). GBrain's v0.11.0 migration auto-rewrites entries for built-in handlers; host-specific handlers need a code-level registration per `docs/guides/plugin-handlers.md`.
5. **Write thin prompt.** Job prompt is one line: "Read skills/{name}/SKILL.md and run it."
## Idempotency Requirement
+23
View File
@@ -85,6 +85,29 @@ If not running, install it:
gbrain autopilot --install --repo ~/brain
```
Autopilot runs sync, extract, and embed in a continuous loop with adaptive scheduling.
In v0.11.1+, autopilot dispatches each cycle as a single `autopilot-cycle`
Minion job and supervises the worker child — one install step gives you
sync + extract + embed + backlinks + durable job processing.
### Fix a half-migrated install
A v0.11.0 install where the migration skill never fired leaves Minions
partially set up: schema is applied, but `~/.gbrain/preferences.json`
doesn't exist, autopilot runs inline, host manifests still reference
`agentTurn`. Repair:
```bash
# Check migration status
gbrain apply-migrations --list
# Apply pending migrations (idempotent; safe on healthy installs)
gbrain apply-migrations --yes
# If host-specific handlers are flagged in ~/.gbrain/migrations/pending-host-work.jsonl:
# walk them per skills/migrations/v0.11.0.md + docs/guides/plugin-handlers.md,
# ship handler registrations in the host repo, then re-run apply-migrations.
```
Full troubleshooting guide: `docs/guides/minions-fix.md`.
### Back-link enforcement
Check that the back-linking iron law is being followed:
+163 -127
View File
@@ -7,156 +7,192 @@ feature_pitch:
tiers: null
---
# v0.11.0 Migration: Minions
# v0.11.0 Migration: Minions — host-agent instruction manual
Minions is a job queue for agent work. Submit a task, get an id back. Check
progress whenever. The job keeps running even if the gateway restarts. Parent
jobs fan out to children, children post results back to the parent's inbox,
fan-in just works.
**Audience: host agents (Wintermute, other 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.
This migration adds the Minions schema (additive, safe to re-run), runs a
smoke test, and asks the user how they want their agent to use it.
## What's New
Minions primitives landed in this release:
- **Durability.** Jobs live in Postgres. Gateway crash = agent picks up where
it left off on restart. No lost state.
- **Observability.** Every job has structured progress, token accounting, and
a transcript. `gbrain jobs stats` is the health dashboard.
- **Steering.** Running jobs can receive inbox messages. "Focus on revenue,
skip headcount" lands as context on the next iteration.
- **Parallel fan-out.** Parent job spawns N children with depth + child-cap
guardrails (depth ≤ 5, max_children per parent). Children post `child_done`
into parent's inbox on completion. Parent polls and aggregates.
- **Per-job timeout.** `timeout_ms` + cooperative AbortSignal + DB-enforced
dead-letter. Agents that hang don't hold resources forever.
- **Cascade cancel.** Kill a parent → all descendants cancel via recursive CTE.
- **Idempotency.** Same key = same job. Safe to submit the same task twice.
Postgres only for the worker daemon (`gbrain jobs work`). PGLite supports
inline execution via `gbrain jobs submit --follow` but not background.
## Agent Instructions
### Step 1: Apply the schema
Minions needs schema version 7. If the user's brain is below that, bump it.
## Check what gbrain left you
```bash
gbrain init
cat ~/.gbrain/migrations/pending-host-work.jsonl
```
On existing Postgres brains this is additive (new columns via `ALTER TABLE
... ADD COLUMN IF NOT EXISTS`). On PGLite brains, the DDL bundle is re-applied
and the new columns appear.
Empty? You're done. The migration is complete; nothing for you to do.
If `gbrain init` errors, read the output and fix before continuing. Common
causes: stale lock file, wrong DATABASE_URL, PGLite directory permissions.
Non-empty? Each line is a TODO. Each `type` routes to a section below.
### Step 2: Smoke test
## `type: cron-handler-needs-host-registration`
```bash
gbrain jobs smoke
```
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 Wintermute), gbrain
leaves the manifest alone and emits a TODO with shape:
Expected output on success:
```
SMOKE PASS — Minions healthy in 0.5s (engine: postgres)
```
On PGLite:
```
SMOKE PASS — Minions healthy in 0.3s (engine: pglite)
Note: the `gbrain jobs work` daemon requires Postgres. PGLite
supports inline execution only (`submit --follow`).
```
If SMOKE FAIL: report the exact error to the user, do not proceed. The most
common failure is "table not found, schema version 1 need 7" which means
Step 1 didn't actually run. Re-run `gbrain init` and try again.
### Step 3: Ask the user how they want to use Minions
Use AskUserQuestion. Re-ground first (project + branch + what Minions is),
then ask with these three options:
> GBrain Minions is a durable, observable job queue for background agent
> work. It survives restarts, tracks progress, and lets agents fan out
> parallel work with parent-child DAGs. How should your agent use it?
Options:
- **A) Switch to Minions for everything (recommended).** All background
work goes through Minions. Most durable, most observable. Adds some
overhead (Postgres queries per job) but pays off the first time the
gateway restarts mid-task. `Completeness: 10/10`
- **B) Use only when pain signals fire.** Default to native subagents
(lower overhead, zero extra setup). Auto-switch to Minions when the
gateway drops subagent state, the user asks "what happened to the agent,"
or > 3 parallel streams are needed. Agent watches for these signals and
proposes the switch in-session. `Completeness: 8/10`
- **C) Don't use Minions.** Keep subagents only. Turn this on later with
`gbrain config set minion_mode always` or `pain_triggered`.
`Completeness: 5/10`
RECOMMENDATION: Choose A for production brains and research-heavy workflows
(most durability, best transcripts). Choose B if the user is exploring
or on PGLite (lower friction, opt-in on demand). Choose C only if the
user explicitly says they don't want it.
### Step 4: Persist the choice
Write the user's answer to `~/.gbrain/preferences.json`:
```bash
mkdir -p ~/.gbrain
MODE="always" # or "pain_triggered" or "off" — substitute the user's pick
cat > ~/.gbrain/preferences.json <<EOF
```json
{
"minion_mode": "$MODE",
"set_at": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"set_in_version": "0.11.0"
"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"
}
EOF
```
The subagent-routing convention (`skills/conventions/subagent-routing.md`)
reads this file at runtime. If the file is missing, the convention defaults
to `pain_triggered`.
**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`:
### Step 5: Sanity-check the registered handlers
### (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. `wintermute-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 jobs work --concurrency 1 &
WORKER_PID=$!
sleep 2
gbrain jobs submit sync --params '{"dry":true}' --follow
kill $WORKER_PID 2>/dev/null
gbrain apply-migrations --yes
```
This proves the built-in handlers (`sync`, `embed`, `lint`, `import`) are
wired up. Skip on PGLite (use `--follow` instead of daemon).
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"`.
### Step 6: Done
**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
mkdir -p ~/.gbrain/migrations
echo '{"version":"0.11.0","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","status":"complete","minion_mode":"'$MODE'"}' >> ~/.gbrain/migrations/completed.jsonl
gbrain apply-migrations --list
```
## For the user
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.
If mode = `always`: "Your agent now runs every multi-step task through
Minions. `gbrain jobs stats` shows what's running. `gbrain jobs list` shows
everything. Nothing to configure."
## Related
If mode = `pain_triggered`: "Your agent keeps using subagents by default.
If the gateway drops state or you ask 'what happened to the agent,' it'll
offer to switch that task to Minions. You can flip modes any time with
`gbrain config set minion_mode always`."
If mode = `off`: "Minions is installed but dormant. Turn it on later with
`gbrain config set minion_mode always` or `pain_triggered`."
- `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.
+34
View File
@@ -168,6 +168,40 @@ echo "=== Discovery Complete ==="
If no markdown repos are found, create a starter brain with a few template pages
(a person page, a company page, a concept page) from docs/GBRAIN_RECOMMENDED_SCHEMA.md.
## Phase C.5: One-step autopilot + Minions install (v0.11.1+)
Run the migration runner once, then install autopilot. Two commands, done:
```bash
gbrain apply-migrations --yes # applies any pending migrations; idempotent on healthy installs
gbrain autopilot --install # supervises itself + forks the Minions worker; env-aware
```
What `gbrain autopilot --install` does:
- On **macOS**: writes a launchd plist at `~/Library/LaunchAgents/com.gbrain.autopilot.plist`.
- On **Linux with systemd**: writes `~/.config/systemd/user/gbrain-autopilot.service`
with `Restart=on-failure`.
- On **ephemeral containers** (Render / Railway / Fly / Docker): writes
`~/.gbrain/start-autopilot.sh` and prints the one-line your agent's
bootstrap should source to launch autopilot on every container start.
Auto-injects into OpenClaw's `hooks/bootstrap/ensure-services.sh` if
detected (use `--no-inject` to opt out).
- On **Linux without systemd**: installs a crontab entry (every 5 min).
Autopilot then supervises the Minions worker as a child process. Users get
sync + extract + embed + backlinks + durable Postgres-backed job processing
from ONE install step. No separate `gbrain jobs work` daemon to manage.
On PGLite, autopilot runs inline (PGLite's exclusive file lock blocks a
separate worker process). Everything else still works.
If `apply-migrations` prints "N host-specific items need your agent's
attention," read `~/.gbrain/migrations/pending-host-work.jsonl` + walk
`skills/migrations/v0.11.0.md` + `docs/guides/plugin-handlers.md` to
register host-specific handlers. Re-run `apply-migrations` after each
batch.
## Phase D: Brain-First Lookup Protocol
Inject the brain-first lookup protocol into the project's AGENTS.md (or equivalent).