Files
gbrain/skills/minion-orchestrator/SKILL.md
T
11abb24ddd v0.20.4 feat: merge gbrain-jobs into minion-orchestrator — single unified minions skill (#381)
* feat: merge gbrain-jobs into minion-orchestrator — single unified minions skill

* fix(skill/minion-orchestrator): correct MCP boundary, real handler names, PGLite path

The initial merge commit a51c737 documented `submit_job name="shell"` as
agent-callable, but src/core/operations.ts:1106 rejects protected names
from MCP callers (shell is in src/core/minions/protected-names.ts:16) —
shell-job submission is CLI-only. Subagent examples referenced non-existent
handler names (`research`, `orchestrate`) instead of the real `subagent` /
`subagent_aggregator` handlers. PGLite section wrongly told users to
migrate to Supabase when `gbrain jobs submit ... --follow` inline mode
works per docs/guides/minions-shell-jobs.md:15. Contract section canonized
"every task through Minions" against the `pain_triggered` default in
skills/conventions/subagent-routing.md:16,27.

Rewrite addresses all four:
- Shell Jobs section is explicit about CLI-only submission; agents observe
  via get_job / list_jobs / get_job_progress (non-protected).
- Subagent examples route through `gbrain agent run` (user-facing CLI)
  with raw handler names documented as the power-user path.
- PGLite gets --follow inline execution, not migration friction.
- Contract softened to point at subagent-routing.md convention.

Also adds a Preconditions block for Shell Jobs (env gate, RCE warning,
execution-mode choice, verification command), narrows the frontmatter
"gbrain jobs" trigger to "gbrain jobs submit" + "submit a gbrain job"
(bare was too broad — CLI namespace covers 9 subcommands), inlines a
"replaces older gbrain-jobs routing intent" note in the description, and
removes non-existent `get_job_stats` from the tools list (CLI is
`gbrain jobs stats`; no MCP equivalent).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(resolver): narrow "gbrain jobs" trigger to specific intents

Replace bare "gbrain jobs" in the routing table with "gbrain jobs submit"
+ "submit a gbrain job". The bare phrase was too broad — the CLI namespace
covers 9 subcommands (submit, list, get, retry, delete, prune, stats,
smoke, work). Users asking about stats/prune/retry now fall through to
`gbrain --help` instead of getting misrouted to minion-orchestrator, which
only documents shell execution and subagent orchestration.

Matches the frontmatter trigger narrow in minion-orchestrator/SKILL.md.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(resolver): add round-trip + skill-example-name validator

Two new assertion blocks in test/resolver.test.ts:

1. RESOLVER.md trigger round-trip: every quoted phrase in a routing-table
   row has a fuzzy match in the target skill's frontmatter `triggers:` list.
   Catches RESOLVER ↔ frontmatter drift that checkResolvable's reachability
   check doesn't. Fuzzy match is case-insensitive, trailing-punctuation-
   insensitive, and splits on "/" for compound phrases like
   "pause/resume agent" — accommodates RESOLVER.md's natural-language
   summary style without allowing real drift through.

2. Skill example-name validator: every `name="<word>"` reference in any
   SKILL.md body must resolve to either a declared operation in
   src/core/operations.ts or a known Minions handler in
   PROTECTED_JOB_NAMES. Would have caught the `name="research"` /
   `name="orchestrate"` drift that slipped through the first review
   — nothing in CI caught those handler names referencing non-existent
   handlers until a Codex cold-read found them. This test closes that
   class of regression gap.

51 / 51 tests pass locally. Full E2E suite (bun run test:e2e) still
passes 197 / 197 across 19 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(e2e): PGLite shell-job --follow inline path

Closes the T4 coverage gap surfaced during PR #381 eng review. The sibling
test/e2e/minions-shell.test.ts covers Postgres + persistent-daemon; this
file covers the PGLite + --follow path the minion-orchestrator skill now
documents.

Two assertions:

1. submit → registerBuiltinHandlers → worker.start → shell runs → completes
   with exit_code 0 and stdout_tail "hello\n". Exercises the exact dispatch
   path src/commands/jobs.ts:207 takes when --follow is set, including the
   GBRAIN_ALLOW_SHELL_JOBS=1 gate.

2. With GBRAIN_ALLOW_SHELL_JOBS unset, registerBuiltinHandlers leaves the
   shell handler unregistered. Confirms the env gate from
   src/commands/jobs.ts:611 works.

Runs in-memory against PGLiteEngine — no DATABASE_URL, no Docker, runs in
CI unconditionally. Completes in ~1.2s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: pre-landing review fixes

Pre-landing review caught 4 doc bugs + 2 test fragilities + 2 pre-existing
drift cases. All auto-fix category (clear correct answer, single obvious fix).

minion-orchestrator/SKILL.md:
- Shell submit examples used nonexistent `--cmd`/`--argv`/`--cwd` flags. Real
  CLI takes `--params '{"cmd":"...","cwd":"..."}'` (src/commands/jobs.ts:55-85).
  Examples now match `gbrain jobs submit --help` output.
- `--tools "search,web_search"` referenced `web_search` which isn't in
  BRAIN_TOOL_ALLOWLIST (src/core/minions/tools/brain-allowlist.ts:47-59).
  Swapped to `search,query`. Added a full allowlist enumeration so
  readers don't have to grep.
- `gbrain agent run` flags section listed `--queue`, `--priority`,
  `--max-attempts`, `--delay` — none of these exist on that command
  (src/commands/agent.ts:105-129). Replaced with the real flag set
  (`--subagent-def`, `--model`, `--max-turns`, `--tools`, `--timeout-ms`,
  `--fanout-manifest`, `--follow`, `--no-follow`, `--detach`) and a note
  about using `gbrain jobs submit` for queue tuning.
- MCP boundary claim "returns permission_denied" was imprecise. Reworded:
  throws an OperationError with code permission_denied.

test/resolver.test.ts:
- D5/C row regex required the backtick-quoted skill path to be followed
  immediately by `|`, silently skipping rows with trailing parentheticals
  (e.g., `` `skills/maintain/SKILL.md` (extraction sections) |``). Broadened
  to `[^|]*\|` so every row gets audited.

test/e2e/minions-shell-pglite.test.ts:
- Shared engine across both tests with no per-test reset. Future test
  additions would hit order-dependency. Added beforeEach TRUNCATE on
  minion_jobs / minion_inbox / minion_attachments, matching the Postgres
  sibling at test/e2e/minions-shell.test.ts:55-58.

skills/query/SKILL.md:
- Added 4 triggers RESOLVER.md routes to this skill but the frontmatter
  never declared: "who knows who", "relationship between", "connections",
  "graph query". Pre-existing drift — the broadened D5/C regex surfaced it.

skills/maintain/SKILL.md:
- Added 6 triggers with the same pre-existing drift: "extract links",
  "build link graph", "populate timeline", "populate links", "backfill graph",
  "extract timeline entries".

57/57 tests pass on the fixed tree.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: second-pass review fixes — stale CLI flag + handler name

Two more stale references caught by specialist re-dispatch on the fixed tree:

skills/minion-orchestrator/SKILL.md:72 — Routing table row described shell
  jobs as taking `--cmd` or `--argv` as CLI flags. Same class of bug as M1
  from the prior fix commit but in a different location. Now says `--params`
  with `cmd` or `argv`, matching the corrected submit examples (lines 112-120).

skills/conventions/subagent-routing.md:82 — "Check `get_job_stats`
  queue_health.active" referenced an MCP operation that doesn't exist in
  src/core/operations.ts. The new minion-orchestrator skill cross-references
  this convention file, so agents following the routing pointer would hit a
  non-existent op. Replaced with the real ops: `list_jobs --status active`
  (MCP) or `gbrain jobs stats` (CLI).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix: adversarial pass cleanups — manifest.json + anti-pattern scope

Claude adversarial subagent caught two last consistency gaps:

skills/manifest.json:135 — Skill description still read "Manage background
  agents via Minions job queue" (subagent-only framing), out of sync with
  the reframed SKILL.md frontmatter. Manifest is what the skill registry
  indexes; leaving this stale meant shell-job-intent routers would miss it.
  Updated to match the unified wording.

skills/minion-orchestrator/SKILL.md:288 — Anti-pattern line "Don't use
  sessions_spawn with runtime: subagent when Minions is available" was
  subagent-lane-specific inside the now-consolidated skill, reading like
  the one rule in the skill but only addressing one lane. Scoped to
  "For subagent work" and pointed at `gbrain agent run` so the rule
  doesn't confuse shell-job readers.

Two investigate-class items deferred to follow-up:
- D13 regex could false-positive on future skills with unrelated `name="..."`
  usage. Today clean; scope to backtick-fenced snippets if it bites.
- PGLite E2E env-var race if bun:test ever goes file-parallel. Today isolated
  per file; add helper + comment when needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore: bump version and changelog (v0.19.2)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* docs: update README + CLAUDE.md for v0.19.2 Minions consolidation

- Skill count 28 -> 29 across README and CLAUDE.md (adds smoke-test from
  v0.19.1 to the Skills section, closes a prior drift).
- README minion-orchestrator row rewritten to name both lanes (shell jobs
  via `gbrain jobs submit shell`, LLM subagents via `gbrain agent run`)
  so the surface matches the consolidated skill file.
- README Operational table gains a smoke-test row.
- CLAUDE.md key-files entry for minion-orchestrator now describes the
  v0.19.2 consolidation, trust boundary (MCP permission_denied on
  protected names), and the narrowed trigger set.
- CLAUDE.md Skills section notes the consolidation and the new v0.19.1
  smoke-test skill.
- CLAUDE.md test inventory picks up `test/e2e/minions-shell-pglite.test.ts`
  and the v0.19.2 round-trip + name-validator additions in
  `test/resolver.test.ts`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(ci): update PGLite test for new env-gate behavior + regenerate llms-full.txt

CI caught two issues:

1. `test/e2e/minions-shell-pglite.test.ts` — the "GBRAIN_ALLOW_SHELL_JOBS
   unset → shell handler not registered" test was written against pre-v0.20.3
   `registerBuiltinHandlers` behavior (env gate at registration time). Master's
   queue-resilience merge moved the gate from registration to execution:
   shell handler is now always registered so claimed jobs emit a clear rejection
   log, and `shellHandler` itself throws UnrecoverableError when
   GBRAIN_ALLOW_SHELL_JOBS != '1' (see src/core/minions/handlers/shell.ts:210).
   Updated the test to invoke shellHandler directly with a minimal ctx and
   assert the throw. Preserves the test's intent (prove the guard works) under
   the new control flow.

2. `llms-full.txt` drift — README.md + CLAUDE.md updates in v0.19.2 and v0.20.4
   updated the skill count to 29 and rewrote the minion-orchestrator
   description, but the committed `llms-full.txt` bundle still reflected the
   pre-consolidation content. Regenerated via `bun run build:llms`.

The third CI failure (`planInstall + applyInstall D-CX-11`) passes cleanly
locally (26/26 in test/skillpack-install.test.ts). The 1ms runtime in CI
suggests a filesystem-mtime flake, not a real regression from this branch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(skillpack): treat future-mtime lock as stale (CI race fix)

D-CX-11 ("--force-unlock overrides a stale lock") flaked in CI with a 1ms
runtime. Root cause: on fast CI filesystems (ext4 with high-resolution
mtimes on GitHub runners), `writeFileSync` can set a lock's mtime a few
microseconds ahead of the subsequent `Date.now()`, making `age` negative.

Old logic:
  const stale = age >= staleMs;

With `staleMs: 0` and `age = -0.3ms`: `-0.3 >= 0` is false → NOT stale →
the `!stale` branch throws `lock_held` before reaching the force-unlock
path. Test failed at the first ms, never exercised the actual unlock logic.

Fix (src/core/skillpack/installer.ts:189):
  const stale = age < 0 || age >= staleMs;

Treats negative age (future mtime) as stale. Safe: if the lock's mtime is
in the future, either the filesystem clock just jumped forward or the
lock was written by a racing process; either way it's not a live,
healthy lock and the stale path is the correct branch.

Passes locally (26/26 in test/skillpack-install.test.ts).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: root <root@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 01:39:58 -07:00

11 KiB

name, version, description, triggers, tools, mutating
name version description triggers tools mutating
minion-orchestrator 1.0.0 Unified Minions skill for both deterministic shell jobs and LLM subagent orchestration. Replaces the older `gbrain-jobs` routing intent. Use when: submitting gbrain jobs, shell/background tasks, spawning subagents, checking progress, steering running work, pausing/resuming, parallel fan-out. One durable, observable, steerable queue interface.
gbrain jobs submit
submit a gbrain job
submit a shell job
shell job
run shell command in background
deterministic background task
spawn agent
background task
run in background
check on agent
agent progress
what's running
steer agent
change direction
tell the agent
pause agent
stop agent
resume agent
parallel tasks
fan out
do these in parallel
submit_job
get_job
list_jobs
cancel_job
pause_job
resume_job
replay_job
send_job_message
get_job_progress
true

Minion Orchestrator

Contract

Minions is a Postgres-native job queue for durable, observable background work. This single skill handles two lanes:

  • Deterministic shell jobs (gbrain jobs submit shell ...)
  • LLM subagent jobs (gbrain agent run ...)

When to route to Minions: durable, observable work that must survive restarts, fan out across many parallel tasks, or persist across sessions. Routing policy is defined in skills/conventions/subagent-routing.md — the project default is pain_triggered (native subagents first, Minions after specific pain signals fire); Mode A (all-through-Minions) is opt-in.

Guarantees:

  • Jobs survive gateway restart (Postgres-backed)
  • Every job has structured progress, token accounting, and session transcripts
  • Running agents can be steered mid-flight via inbox messages
  • Jobs can be paused, resumed, or cancelled at any time
  • Parent-child DAGs with configurable failure policies

Route the Request: Shell Job vs Subagent

Condition Action
User asks for deterministic command/script run Shell job (CLI: gbrain jobs submit shell ...)
User asks to "run in minions" + explicit command/argv Shell job (CLI, --params with cmd or argv)
User asks for research/reasoning/iterative agent Subagent job (CLI: gbrain agent run)
User asks to steer/pause/resume an agent Subagent job lifecycle tools (MCP-callable)
Single simple operation under ~30s Consider inline execution first
Needs restart durability/observability Submit as Minion job
Parallel work (2+ streams) gbrain agent run --fanout-manifest or parent + child subagents

If intent is ambiguous, ask one clarification: "Do you want a deterministic shell command job, or an LLM agent job?"

Shell Jobs (Deterministic Scripts)

Use for reproducible command execution, ETL steps, cron work, and scriptable tasks where no LLM reasoning loop is needed.

Preconditions (read before submitting your first shell job)

  • GBRAIN_ALLOW_SHELL_JOBS=1 must be set on the worker environment. Without it, the shell handler refuses to register and submissions sit in waiting silently. Gate lives in src/core/minions/handlers/shell.ts.
  • Security: flipping GBRAIN_ALLOW_SHELL_JOBS=1 authorizes arbitrary command execution on the worker. On a shared queue, this is a remote code execution surface. Treat as privileged infrastructure authorization.
  • Execution mode — pick one:
    • Postgres + daemon: gbrain jobs work runs a persistent worker that claims and executes jobs from the queue.
    • PGLite + --follow: gbrain jobs submit ... --follow runs inline. The daemon mode is not available on PGLite (exclusive file lock). See docs/guides/minions-shell-jobs.md.
  • MCP boundary: shell-job submission is CLI-only. submit_job name="shell" over MCP throws an OperationError with code permission_denied ("'shell' jobs cannot be submitted over MCP") because shell is in PROTECTED_JOB_NAMES. Agents CAN observe shell jobs via get_job / list_jobs / get_job_progress (not protected), but cannot submit them. Operator or autopilot submits; agent observes.
  • Verify setup: after configuration, run gbrain jobs stats (CLI) to confirm the worker is registered and consuming the queue.

Submit (CLI, operator or autopilot)

Shell jobs take their command via --params as a JSON object with cmd (string) or argv (array), plus cwd and optional env.

Command string form:

gbrain jobs submit shell --params '{"cmd":"echo hello","cwd":"/abs/path"}'

Argv form (no shell expansion):

gbrain jobs submit shell --params '{"argv":["bash","-lc","echo hello"],"cwd":"/abs/path"}'

Inline execution on PGLite or any one-shot deployment:

gbrain jobs submit shell --params '{"cmd":"echo hello","cwd":"/tmp"}' --follow

Queue/lifecycle flags exposed by gbrain jobs submit --help: --queue, --priority, --delay, --max-attempts, --max-stalled, --backoff-type, --backoff-delay, --backoff-jitter, --timeout-ms, --idempotency-key, --dry-run.

Monitor (agents or operator)

These operations are MCP-callable and safe for agent use:

list_jobs --name shell --status active
get_job ID
get_job_progress ID

Check structured result fields (exit code, stdout/stderr tails, attempts, timings) from get_job. Use gbrain jobs stats (CLI) for worker/queue health dashboard.

Control (MCP-callable)

cancel_job id=ID
replay_job id=ID

replay_job is not protected — only shell submission is. Agents can cancel or replay a shell job without CLI access.

Use idempotency keys for recurring shell workloads to avoid duplicate runs.

Subagent Jobs (LLM Orchestration)

Use for open-ended reasoning, tool-using research, and fan-out synthesis.

User-facing entrypoint: gbrain agent run <prompt> is the canonical way to submit subagent work. It handles the elevated-trust plumbing — subagent and subagent_aggregator are both in PROTECTED_JOB_NAMES, so direct MCP submission requires {allowProtectedSubmit: true}, which gbrain agent run supplies.

Phase 1: Submit

gbrain agent run "Research Acme Corp revenue" --tools "search,query"

--tools accepts a comma-separated subset of BRAIN_TOOL_ALLOWLIST (see src/core/minions/tools/brain-allowlist.ts): query, search, get_page, list_pages, file_list, file_url, get_backlinks, traverse_graph, resolve_slugs, get_ingest_log, put_page. Anything outside the allow-list is rejected at submit time with allowed_tools references unknown tool.

For parallel work with a fan-out manifest:

gbrain agent run --fanout-manifest companies.json

The manifest describes N children + 1 aggregator. Each child runs name="subagent" under the hood; the aggregator runs name="subagent_aggregator" and claims AFTER every child terminates. See src/core/minions/handlers/subagent.ts and src/core/minions/handlers/subagent-aggregator.ts.

Flags (from src/commands/agent.ts):

  • --subagent-def <name> — named subagent definition
  • --model <id> — override model
  • --max-turns <N> — cap the LLM loop
  • --tools <csv> — allow-listed brain tools (see above)
  • --timeout-ms <N> — hard timeout per job
  • --fanout-manifest <file> — N children + 1 aggregator
  • --follow / --no-follow — stream logs + wait (default on TTY)
  • --detach — submit and return immediately

Queue/priority/retry tuning is not exposed by gbrain agent run; submit the raw subagent handler via gbrain jobs submit (requires CLI trust) if you need those knobs.

Phase 2: Monitor

list_jobs --status active          # MCP — what's running?
get_job ID                         # MCP — full details + logs + tokens
get_job_progress ID                # MCP — structured progress snapshot
gbrain jobs stats                  # CLI — queue health dashboard
gbrain agent logs ID --follow      # CLI — streaming transcript + heartbeat

Progress includes: step count, total steps, message, token usage, last tool called.

Phase 3: Steer

Send a message to redirect a running agent:

send_job_message id=ID payload={"directive":"focus on revenue, skip headcount"}

The agent handler reads inbox messages on each iteration and injects them as context. Messages are acknowledged (read receipts tracked).

Only the parent job or admin can send messages (sender validation).

Phase 4: Lifecycle

pause_job id=ID                    # freeze without losing state
resume_job id=ID                   # pick up where it left off
cancel_job id=ID                   # hard stop
replay_job id=ID                   # re-run with same or modified params
replay_job id=ID data_overrides={"depth":"deep"}  # replay with changes

All lifecycle ops are MCP-callable.

Phase 5: Review Results

get_job ID                         # result, token counts, transcript

Token accounting: every job tracks tokens_input, tokens_output, tokens_cache_read. Child tokens roll up to parent automatically on completion.

Output Format

When reporting job status to the user:

Job #ID (name) — status
Progress: step/total — last action
Tokens: input_count in / output_count out (+ cache_read cached)
Runtime: Xs
Children: N pending, M completed

When reporting completion:

Job #ID completed in Xs
Tokens used: input / output / cache_read
Result: <summary>

When reporting batch status (parent with children):

Parent #ID — waiting-children
  #A subagent(Acme) — active, 3/5 steps, 2.5k tokens
  #B subagent(Beta) — completed, 1.8k tokens
  #C subagent(Gamma) — paused
Total tokens so far: 4.3k

Anti-Patterns

  • Don't spawn a Minion for a single search query (use search tool directly)
  • Don't fire-and-forget without checking results
  • Don't spawn > 5 concurrent agents without checking gbrain jobs stats first
  • For subagent work, don't use sessions_spawn with runtime: "subagent" when Minions is available (use gbrain agent run instead)
  • Don't poll get_job in a tight loop (use get_job_progress for lightweight checks)

Tools Used

  • Submit a background job — submit_job (MCP, non-protected names only; shell jobs are CLI-only, subagent jobs via gbrain agent run)
  • Get job details — get_job (MCP)
  • List jobs with filters — list_jobs (MCP)
  • Cancel a job — cancel_job (MCP)
  • Pause a job — pause_job (MCP)
  • Resume a paused job — resume_job (MCP)
  • Replay a completed/failed job — replay_job (MCP)
  • Send sidechannel message — send_job_message (MCP)
  • Get structured progress — get_job_progress (MCP)
  • Queue stats — gbrain jobs stats (CLI; no MCP equivalent)