mlua-swarm-cli 0.11.0

Command line interface for mlua-swarm (mse binary with serve / mcp subcommands).
Documentation
# mse — Agent (agent.md) authoring guide

An **agent.md** file is the durable system prompt for a Claude Code SubAgent
(built-in or custom). It is what gets fetched and pushed into the SubAgent's
context window every time the agent is invoked. Because that context window is
finite and shared with the runtime task payload (fetched Task IF, `Read`
results, `tool_result` bodies, MCP resource fetches, `PreOut` file contents),
**an oversized agent.md leaves no room to actually do the work**.

This guide states the canonical shape, size targets, and anti-patterns for
SubAgent prompts used from mse — the Operator wrapper agents, the mse-worker
family, and any custom SubAgent that participates in a Blueprint dispatch. It
follows the Claude Code SubAgent conventions; see the References section for
primary sources.

## Canonical shape (4 sections)

The Anthropic official examples (`code-reviewer`, `debugger`, `data-scientist`,
`db-reader`) all converge on the same 4-layer structure. Reproduce it:

1. **Role — 1 sentence.** What this agent is and what it excels at. No
   preamble, no background, no history. If it takes more than one sentence, the
   agent is doing too much and should be split.

2. **`When invoked:` — numbered workflow.** The exact steps this agent runs,
   in order. Prefer 3–8 steps. Each step is imperative ("Read X", "Post to
   /v1/…", "Return Y"). No decision trees, no exceptional paths described in
   prose — put those into the step body as sub-bullets if truly needed.

3. **Tool guidance / key practices — checklist.** Which tools to reach for at
   which step, plus non-obvious constraints (auth headers, ordering rules,
   idempotency). Keep it to items that *change what the agent does*; do not
   describe every tool from scratch (the tool schemas are already loaded).

4. **Output format — explicit.** What the caller expects back: the exact
   `return` literal shape, the JSON schema, or the human-readable format. If
   the caller parses the output, put a fenced example. If the agent hands off
   to another SubAgent, state the handoff artifact path.

**Input is not a section.** The delegation prompt (Task message) carries the
input. Do not enumerate example inputs in the durable prompt — that content is
per-invocation and belongs in the caller's `Task` dispatch, not in agent.md.

## Output contract: inline body vs `@file:` sentinel

`POST /v1/worker/submit` accepts two body forms. Declare **exactly one** in
the agent's Output format section — never present both as alternatives in a
single agent.md (an LLM given a choice tends to defensively do both, writing
the file *and* re-emitting the full body inline):

- **Inline body (the default).** The submit body is the output itself — a
  short literal (`DONE ...`, `PASS|BLOCKED`) or a markdown report. Use this
  unless the payload is genuinely too large to re-emit.
- **`@file:<abs-path>` sentinel.** The worker `Write`s the payload to a file
  under its task `work_dir` and submits the single line
  `@file:<abs-path>`; the server resolves the file into the body. The step
  must be opted in on the Blueprint side — see
  `mse://guides/operator-execution-model` § `allow_file_submit` opt-in for
  the declaration form and precedence. Without the opt-in the server
  rejects the sentinel with `400`. Path guards apply either way (absolute
  path, under `work_dir`, ≤ 2 MiB).

**Self-report convention.** If the agent uses a tool outside the set its
prompt intends, or falls back to another tool after a failure, it should
report that via `POST /v1/worker/degradation` (`{tool, error, fallback,
note}`) instead of silently working around it. Reports surface in the run
record and in `mse_doctor`'s degradations section, which is the standard
post-run checkpoint for contract deviations.

## Size targets

There is **no official byte limit** in the Claude Code SubAgent docs, but
neighboring numbers make the working range obvious:

- Official example agents: **20–40 lines** each (measured across the 4
  reference agents in `code.claude.com/docs/en/sub-agents §Example subagents`).
- `MEMORY.md` injection threshold: **first 200 lines or 25 KB, whichever is
  first**; beyond that the runtime injects a curation instruction instead of
  the content ([sub-agents §Enable persistent memory][mem]).
- Windows delegation hard cap: **8191 characters** in the delegation prompt
  string; longer prompts fail to dispatch ([agent-sdk/subagents §Long prompt
  failures on Windows][winlim]).

Target: **≤ 200 lines / ≤ 25 KB** for any agent.md. Treat 500+ lines / 50 KB+
as a defect — at that size the fetched system_prompt alone will consume a
significant fraction of a 200 K context window, leaving no headroom for the
task payload, and the SubAgent will fail with *Prompt is too long* the first
time it hits a real tool_result of any size.

These thresholds are observed by the `bp_doctor` MCP tool, which fetches a
Blueprint from `GET /v1/blueprints/:id/head`, measures every agent's
`profile.system_prompt` bytes/lines, and returns per-agent severity
(`OK` / `WARN` / `BLOCK`) plus an aggregate verdict. The default check is:

- **WARN** if `system_prompt` bytes ≥ 25 KB **or** lines ≥ 200.
- **BLOCK** if `system_prompt` bytes ≥ 50 KB **or** lines ≥ 500.

The verdict is a report label only — `bp_doctor` never prevents any
dispatch. **BLOCK is disabled by default**: modern Claude models (Opus-tier
and long-window Fable variants) tolerate large system prompts, so an
over-block-threshold agent falls back to WARN. Callers running against a
strict 200 K-window model can pass `disable_block=false` to opt into the
BLOCK band. Any of the four thresholds (`warn_bytes` / `warn_lines` /
`block_bytes` / `block_lines`) can also be overridden per call to fit the
model actually in play.

Agents whose backend does not carry an `agent.md` body (`RustFn` and other
spec-only agents where `profile` is `None`) are reported with size 0 and
severity `OK`.

## What to remove (anti-patterns)

Everything in this list is already available to the SubAgent from another
channel; re-stating it in agent.md is dead weight that costs tokens on every
invocation.

- **Content of `CLAUDE.md` / `~/.claude/CLAUDE.md` / project rules / managed
  policy files.** The full memory hierarchy is auto-loaded into every SubAgent
  except the built-in `Explore` / `Plan` ([sub-agents §What loads at
  startup][load]). Re-quoting a rule your agent is expected to follow just
  duplicates it.
- **Accident logs, retrospectives, "why this exists" narratives.** SubAgents
  do not inherit the parent's conversation history or tool results ([agent-sdk
  §What subagents inherit][inh]). A long historical explanation gives the
  SubAgent no leverage it can use at runtime; it only bloats the fetch.
- **Full copies of upstream doc pages.** Link to `mse://guides/<slug>` or the
  canonical URL and let the agent fetch on demand if truly needed.
- **Input examples / fixture blobs.** These belong in the delegation prompt
  (Task message), which is per-invocation and correctly sized to the specific
  work.
- **Exhaustive tool schemas.** Tool schemas are loaded automatically; the
  agent only needs *which tool to reach for and why*, not the JSON Schema.
- **Persona / register decoration for agents that are pure workers.** Voice
  and register belong to persona-facing agents; worker SubAgents that just
  return structured output do not benefit from them.

## Fetch-vs-embed policy (mse SubAgent specifics)

When an mse SubAgent needs data at runtime, use the right channel for each
kind of payload:

| Payload kind | Delivery channel | Why |
|---|---|---|
| **System prompt (agent role definition)** | `fetch` (mse-worker `fetch` MCP tool) | Literal, hard guarantee. FilePath-only delivery fails on cwd drift / relative-path ambiguity ~ 100% of the time in some environments; embed as literal to eliminate that class. |
| **Lightweight context (FilePath, short config, env-derived values)** | Embed in system prompt OR pass via Task IF from MainAI | Small enough to co-locate; passing through Task IF keeps agent.md invocation-agnostic. |
| **PreOut (upstream phase artifacts, large fixtures)** | Pass the path in Task IF; SubAgent runs `Read` itself | Keeps agent.md size bounded and lets the SubAgent decide what to load. |
| **Anything else** | Assume it will fail. | These three channels cover the reliable delivery paths for mse SubAgents. |

The size discipline in the previous section exists to preserve headroom for
the "PreOut via `Read`" case: if agent.md consumes 77 K of the 200 K window
just for the system prompt, `Read` results have nowhere to go and the agent
crashes on the first non-trivial file.

## Verifying how your agent materializes (`bp_explain_agent`)

Once an agent.md is wired into a Blueprint (`profile.worker_binding` set,
or reachable via `$agent_md`), do not guess whether the definition
survived the compile/spawn pipeline intact — check it. Two faces expose
the same read-only, dry-run view, resolved statically from the
Blueprint's head commit alone (no engine state touched, nothing
dispatched):

- **HTTP**: `GET /v1/blueprints/:bp_id/agents/:agent/explain` on
  `mse serve` — the source of truth. Same unauthenticated trust tier and
  404 mapping as `GET /v1/agents/:name/render-size`.
- **MCP**: `bp_explain_agent {bp_id, agent, bind?, wrapper_dir?}`  proxies the HTTP response, then adds the one check the server cannot do
  itself: when the agent has a `worker_binding`, it reads the worker
  wrapper `.claude/agents/<variant>.md` (override with `wrapper_dir`,
  default `.claude/agents`) and diffs its frontmatter `tools` against
  `declared_tools.tools`, returned as `tool_drift: {matched,
  declared_only, wrapper_only, wrapper_only_contract,
  wrapper_only_meaningful}`. `wrapper_only_contract` is the mse-worker
  fetch/submit tools (`mse_worker_fetch` / `mse_worker_submit`) every
  wrapper carries regardless of author intent — noise, not signal.
  `wrapper_only_meaningful` is everything else in `wrapper_only`: tools
  the wrapper actually grants that the agent.md never declared.
  `wrapper_only` (flat, unsplit) is retained for one release cycle for
  backward compat and may be removed after that; `declared_only` stays
  the primary drift signal either way (see point 1 below).

The response covers `blueprint` (id/version), `agent` (name/kind),
`worker_binding`/`binding_note`, `declared_tools`, `system_prompt`
(bytes/lines/`template_variables`/`template_syntax_error`),
`effective_ctx`, and `output`
(`projection_name`/`naming_warnings`/`parts_note`). Three things matter
most when reading it:

1. **`declared_tools` never grants anything.** It is `profile.tools`,
   verbatim, marked `informational: true` — the effective tool surface an
   Operator-dispatched SubAgent actually gets is whatever the worker
   wrapper's frontmatter declares. `bp_explain_agent`'s
   `tool_drift.declared_only` is the single most useful field this whole
   view produces: a tool your agent.md lists but the wrapper does not
   grant is silently unusable, and neither a successful compile nor a
   successful spawn tells you that on its own. `wrapper_missing: true`
   (+ `wrapper_error`) means the drift check could not run at all —
   treat that result as "unverified", not "clean".
2. **`effective_ctx` only resolves the 3 static tiers**   `AgentInline > MetaRef > BpGlobal` (`AgentMeta.ctx` beats
   `AgentMeta.meta_ref` beats `Blueprint.default_agent_ctx`), reusing the
   exact merge `AgentContextMiddleware` runs at spawn time rather than
   re-implementing it. The full runtime cascade is
   `Run > Task > Step > Agent > BP-global`; the Run/Task/Step tiers only
   exist once a launch or a dispatched Step supplies them, so this static
   view has nothing to show for them (`effective_ctx.note` states this).
3. **`output.projection_name` and the part-staging shape change.** If the
   agent's step ever stages named output parts
   (`mse_worker_submit` with `name`), the step's OUTPUT changes shape to
   `{"out", "parts"}` — see `output.parts_note`. A downstream `Step.in`
   written against the plain (non-parts) shape breaks the moment staging
   is introduced for that step; check this before wiring a consumer.

**Phase 2 carry**: this view does not analyze flow expr consumers — which
`$.step` references read this agent's output, and in what shape. That
requires walking the `mlua-flow-ir` `flow` tree and is out of scope for
the current implementation; cross-reference `output.projection_name`
against the Blueprint's `flow` manually until that lands.

### Sweeping a whole Blueprint (`bp_explain_agents`)

Checking one agent at a time does not scale once a Blueprint has more
than a handful of agents. `bp_explain_agents {bp_id, bind?,
wrapper_dir?}` sweeps every agent in one call and reports a compact
per-agent row — counts, not full lists, since a whole-Blueprint response
has to stay small:

```json
{
  "blueprint": { "id": "...", "version": "..." },
  "agents": [
    {
      "name": "...",
      "variant": "...",
      "wrapper_missing": false,
      "wrapper_error": null,
      "declared_only_count": 2,
      "wrapper_only_contract_count": 2,
      "wrapper_only_meaningful_count": 1
    }
  ]
}
```

Agents without a `worker_binding` get `variant: null` and every
wrapper-side field `null` — there is nothing to diff. A missing or
unparsable wrapper file sets `wrapper_missing: true` + `wrapper_error`,
with every count at `0`. Once a row's `declared_only_count` or
`wrapper_only_meaningful_count` is non-zero, drill down with
`bp_explain_agent {bp_id, agent}` for the full `tool_drift` detail
(which tools, not just how many).

## Quick self-check before you commit an agent.md

1. Is the file **≤ 200 lines / ≤ 25 KB**?
2. Does it have exactly the **4 canonical sections** (Role / When invoked /
   Tool guidance / Output format)?
3. Does it **avoid re-stating** anything already in `CLAUDE.md`, user-global
   rules, or tool schemas?
4. If the agent takes runtime input, does the prompt describe **the shape**
   (not example values)?
5. Does it declare its **Output format** concretely enough that the caller
   can parse or verify the return?
6. Does it commit to **one submit form** (inline body *or* `@file:` sentinel,
   never both) — and if `@file:`, does the Blueprint declare
   `allow_file_submit: true` for the step?

Six yeses → ship it. Any no → shrink and re-check.

## References

- [Create custom subagents — Claude Code Docs][sub-agents]
- [Subagents in the SDK — Claude Agent SDK Docs][sdk-subagents]
- [Effective context engineering for AI agents — Anthropic Engineering][context]
- [Writing effective tools for AI agents — Anthropic Engineering][tools]
- `mse://guides/operator-execution-model` — Operator-kind 3-hop execution model this SubAgent is dispatched under (Spawn.directive rendering, supply tiers, `allow_file_submit` opt-in).
- `mse://guides/blueprint-authoring` — Blueprint document shape that names this agent.

[sub-agents]: https://code.claude.com/docs/en/sub-agents
[sdk-subagents]: https://code.claude.com/docs/en/agent-sdk/subagents
[mem]: https://code.claude.com/docs/en/sub-agents#enable-persistent-memory
[winlim]: https://code.claude.com/docs/en/agent-sdk/subagents
[load]: https://code.claude.com/docs/en/sub-agents
[inh]: https://code.claude.com/docs/en/agent-sdk/subagents
[context]: https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents
[tools]: https://www.anthropic.com/engineering/writing-tools-for-agents