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.