How to author an agent (without writing Mustache)¶
This guide is about the authoring side of a DNA scope: creating an agent and the soul that gives it a voice, and ordering the two in the composed prompt — all without hand-writing a template. If you want to read the composed prompt back out at boot, see How to consume prompts.
There are three moving parts, each a small convenience over the raw kernel:
dna new— scaffold a valid skeleton so you never remember the envelope.- named layouts — order the persona and the instruction by name, not by copy-pasting Mustache section tags.
- single-file souls — a soul is one
SOUL.md; the two-file soulspec.org bundle stays available but is no longer the price of entry.
Scaffold with dna new¶
dna new writes a correct, minimal skeleton into a scope through the kernel
(every write guard and schema check runs — it is not a text template), leaving
you with a valid document whose only empty part is the prose you came to write.
dna new soul warm-host -d "Patient, warm, concise concierge voice"
dna new agent concierge --soul warm-host --layout persona-first
dna new guardrail no-pii --severity error --guard-scope output
dna new agent <name>createsagents/<name>/AGENT.mdwith a placeholder instruction body and any--soul/--guardrails/--layout/--modelwiring pre-filled.dna new soul <name>creates a singlesouls/<name>/SOUL.md— nosoul.json(see below).dna new guardrail <name>createsguardrails/<name>/GUARDRAIL.mdwith a starter rule plus a severity and scope.
dna new is idempotent: it refuses to overwrite an existing document unless you
pass --force. Add --json for machine-readable output, and --scope to
target a scope other than the auto-detected one.
Named layouts — order the persona by name¶
An agent's system prompt is composed from several pieces: the agent's own
instruction, the soul (persona/voice), and any guardrails. The order of those
pieces used to be a raw promptTemplate full of internal section names
({{{soul_content}}}, {{#guardrails-guardrail}}) — a cliff you fell off the
moment you wanted the persona to speak first.
A layout: field on the agent picks the order by name:
| Layout | Order |
|---|---|
instruction-first (a.k.a. default, or absent) |
instruction → soul → guardrails |
persona-first |
soul → instruction → guardrails |
# agents/concierge/AGENT.md
---
name: concierge
soul: warm-host
layout: persona-first
---
# concierge
You help guests check in...
Guardrails always compose last — they are hard policy enforced every turn,
regardless of the layout. The kernel resolves the named layout to an embedded
template, so the common case never authors a single {{ }}.
A misspelled layout fails loud rather than silently falling back to the default order:
UnknownLayout: Unknown layout 'persona_first' on agent 'concierge' — available: default, instruction-first, persona-first
The 20% escape hatch¶
A raw promptTemplate still wins over layout when both are present — the
poweruser who genuinely needs a bespoke composition keeps full control:
Single-file souls¶
A soul following the soulspec.org standard is a
two-file bundle: SOUL.md plus a soul.json manifest (specVersion, a files
map, tags). DNA reads SOUL.md directly, so soul.json is optional — a
soul authored as a single file reads and composes exactly the same:
<!-- souls/warm-host/SOUL.md -->
# warm-host
A patient, warm concierge. Speaks in short, reassuring sentences...
That is all a soul needs. The two-file form is still fully supported — author it
(or install a market bundle) with a soul.json present and DNA preserves it
byte-faithfully on the round-trip. Single-file authoring is the convenience;
soulspec.org fidelity is intact for the marketplace.
When you omit frontmatter entirely, the soul's name is inferred from its bundle
directory (souls/warm-host/ → warm-host).
Putting it together¶
Three commands take you from an empty scope to a composing, persona-first agent with no Mustache anywhere: