Role prompts — what to put in a role body, and what not to

A role file (*.agent.md, or a brain copy of role-*.md) is frontmatter plus a body. The body is the role’s own section of the system prompt. This page is about that body: how big it should be, what belongs in it, and where everything else goes.

Related: contract.md (what a role declares and what it is delivered), features.md § Skills, state.md (structured data an agent keeps), memory.md (prose memory), contexts.md (what a feature adds to the turns linked to it).


1. A role body is paid on every turn

Every agent of the role carries the body in its system prompt on every turn — a heartbeat tick, a chat message, a sub-agent. A cached prefix makes it cheaper, never free: it still fills the context window and competes for the model’s attention with the task at hand. A paragraph needed once a week costs the same as a rule needed every turn.

2. Persona and rules only

The body is who the agent is and the 5–10 rules that hold on every turn. A procedure needed for one kind of request — “how to run the weekly report”, “how to triage an incident” — is a skill: write it as skills/<name>/SKILL.md, give it a trigger-first description, and list it by name in the role’s skills:. The catalog line is the pointer; do not name the skill again in the body.

A rule that belongs to one feature — how the invoice chase phrases a reminder, what a ticket’s SLA is — is not the role’s either: it goes into that feature’s context (features/<feature>/<name>.context.md, contexts.md), which reaches the role only on the turns linked to the feature — a run of its heartbeat, a context the agent opened — and switches off with the feature. The engine’s feature-contexts skill walks an agent through that move.

3. Pointers, not paragraphs

When the agent needs to know where things are, give one line per thing: path — when to read it. A file map is a list of pointers, not a summary of each file’s contents.

4. No data in the prompt

Lists, current values, statuses and SQL do not belong in a role body: they go stale the moment they change, and the body is re-sent unchanged. Keep them in tables and expose the current slice through a state view (state.md) — the agent sees it every turn and it is always current.

5. Don’t restate the engine

The engine injects its own modules: memory, asking questions, chat delivery, the turn lifecycle, state, the skills catalog. Repeating them in the body pays twice and invites contradictions. Say only what differs for this role.

The same goes for tools: their definitions reach the agent on their own, so a body that lists them pays for them twice. If a wide role keeps reaching for the same few tools, keep those in its toolLoading.eager instead of describing them — they then ride every turn while the rest wait behind a search (contract.md § 9.6).

6. Skill descriptions are triggers

A catalog entry is one line: - <skill>: <description>, capped at 250 chars. The description’s job is to make the agent pick the skill at the right moment — “Use when …” first. The skill’s body costs nothing until it is read.

7. Specialist skills

metadata.omnislash.listed: false in a skill’s frontmatter keeps it out of every skills: ['*'] catalog. A role that names the skill in skills: still lists it. Use it for a skill only a few roles need, so the '*' roles do not carry it.

---
name: incident-postmortem
description: "Use when writing a postmortem after an incident is closed."
metadata:
  omnislash:
    listed: false
---

8. Overriding an engine skill and building on it

A brain skill with the same name as an engine skill replaces it. To build on the engine’s version instead of copying it, say so in your body: “read the engine’s version at engine/SKILL.md, then apply these rules”. The catalog marks the entry (builds on …) so the agent can reach the engine copy in both modes — a path on CLI, the engine/SKILL.md resource of skill_read_resource on API. The self-evolving loop never edits skills/engine/ and never writes a skill that shadows an engine skill by name.

9. State roles and the modelling guide

A role with state: gets a short state module plus one pointer to the engine’s agent-state skill — the long modelling guide (tables vs views, deterministic views, blast radius). The pointer appears only when the agent can load the skill: on API the role needs skill:read (or '*'). A brain that opted out of engine skills (skills/.engine-skills.override) vendors a skill named agent-state into skills/agent-state/; doctor Skills warns when a state role would run without it.

10. The budget: promptBudget.roleChars

How large a role body may be before the engine reports it — engine default 6000 chars, a brain-wide value in constitution/MANIFEST.yaml, and a per-role value in the role’s frontmatter. 0 turns the report off.

# constitution/MANIFEST.yaml — every role in this brain (requires an engine that knows
# the section; an older one rejects the manifest)
promptBudget:
  roleChars: 8000

# features/team/nova.agent.md — this one is deliberately large
promptBudget:
  roleChars: 20000

A body over its budget raises the role-heavy finding on /system/context (with the three largest ## sections) and makes doctor Context budget a warning naming the role. It is observation only: nothing is trimmed, nothing fails, the role loads as written. The one hard limit is the engine’s, not this budget: a Codex step fails (and the chain fails over) when the whole system prompt is above 128 KiB serialized — the most one command-line argument can carry.

Screens from Sixty Seconds Studio, a fictional demo studio, running on the real dashboard.