Persona
Persona format, validation, and prompt templating
Overview
A persona defines an agent's identity, expertise, voice, values, and behavioral boundaries. Persona is the primary replication mechanism in stax: the same agent with different persona layers becomes a new artifact that shares every other unchanged layer by digest.
Personas are authored in TypeScript using definePersona() and compiled to canonical JSON.
definePersona()
import { definePersona } from "stax";
export default definePersona({
name: "maya-chen",
displayName: "Maya Chen",
role: "Senior Backend Engineer",
background: "10 years building distributed systems at scale.",
expertise: {
primary: ["Go", "distributed systems", "PostgreSQL"],
secondary: ["Kubernetes", "Terraform"],
learning: ["Rust"],
},
personality: {
traits: ["pragmatic", "thorough", "mentoring"],
communicationStyle: "direct",
verbosity: "concise",
},
voice: {
tone: "Professional but warm. Uses concrete examples.",
codeComments: "minimal",
patterns: ["Starts with positives before suggestions", 'Uses "we" when proposing improvements'],
avoid: ["Overly academic language", "Unnecessary hedging"],
},
values: ["Correctness over speed", "Explicit over implicit", "Simplicity over cleverness"],
preferences: {
testing: "Table-driven tests, no mocks unless necessary.",
errorHandling: "Explicit error returns, no panic.",
},
boundaries: {
willNot: ["Write code without tests", "Skip error handling"],
always: ["Consider backwards compatibility", "Document breaking changes"],
escalates: ["Security-sensitive changes", "Database migrations"],
},
});Type definition
interface PersonaDefinition {
specVersion?: "1.0.0";
name: string;
displayName: string;
role: string;
background?: string;
expertise?: {
primary?: string[];
secondary?: string[];
learning?: string[];
};
personality?: {
traits?: string[];
communicationStyle?: "direct" | "diplomatic" | "academic" | "casual" | "formal";
verbosity?: "minimal" | "concise" | "balanced" | "detailed" | "verbose";
};
voice?: {
tone?: string;
codeComments?: "none" | "minimal" | "moderate" | "thorough";
patterns?: string[];
avoid?: string[];
};
values?: string[];
preferences?: Record<string, unknown>;
boundaries?: {
willNot?: string[];
always?: string[];
escalates?: string[];
};
}Validation
nameMUST match the same identifier rules as agent namesdisplayNameandroleMUST be non-empty strings- Arrays SHOULD contain unique strings
- Unknown fields MAY be preserved by builders but consumers MAY ignore them
Persona discovery
Builders support three persona modes:
stax build # build default persona.ts if present
stax build --persona maya-chen
stax build --all-personas # build every personas/*.ts except files starting with _Discovery rules:
- Files in
personas/whose basename starts with_MUST be ignored for standalone builds _-prefixed files MAY be imported for inheritance or composition- The build identity for a persona variant is the persona
name, not the file name
Storage efficiency
All persona variants SHOULD share every unchanged layer.
backend-engineer:3.1.0-maya-chen persona sha256:111
backend-engineer:3.1.0-alex-rivera persona sha256:222
all other layer digests identicalInheritance
Persona inheritance is an authoring pattern, not a wire-format feature.
import base from "./_base.ts";
export default definePersona({
...base,
name: "maya-chen",
displayName: "Maya Chen",
role: "Senior Backend Engineer",
expertise: { primary: ["Go", "distributed systems"] },
});Inheritance validation
Builders MUST validate the compiled persona after inheritance resolution, not before. This means:
- The final spread-merged object MUST pass all validation rules (non-empty
name,displayName,role, etc.) - If a base persona defines a field and the child re-declares it, the child's value wins (standard spread semantics)
- Builders MUST NOT validate
_-prefixed base files as standalone personas — they are imported fragments only - If a base file exports a value that is not a valid
PersonaDefinitionpartial, builders SHOULD warn at import time
Prompt templating
Prompts MAY reference persona fields using {{ ... }} expressions.
Example:
You are {{persona.displayName}}, a {{persona.role}}.
{{persona.background}}Template rules
- Only
personais in scope in spec1.0.0 - Expressions MUST use dotted paths such as
persona.role - Missing values MUST resolve to an empty string unless a consumer provides strict mode
- Consumers MUST NOT execute arbitrary code in templates
- Consumers SHOULD HTML-escape or otherwise sanitize only when required by their target runtime; plain-text Markdown materialization SHOULD insert raw string values
Supported grammar
{{persona.name}}
{{persona.displayName}}
{{persona.expertise.primary}}Array values SHOULD be rendered as comma-separated lists unless a consumer exposes a richer formatting mode.
Escaping
- To emit a literal
{{, authors MUST write\{{ - To emit a literal
}}inside non-template text, no escaping is needed —}}is only special immediately after a{{expression - If a persona field value contains
}}, consumers MUST NOT interpret it as a template close delimiter; the close delimiter is the first}}after the opening{{}} - Consumers MUST NOT support nested expressions, function calls, conditionals, or loops in
1.0.0 - Unrecognized expressions (e.g.,
{{unknown.field}}) MUST resolve to an empty string in default mode and MUST cause a validation error in strict mode
Layer mapping
| Persona field group | Conceptual purpose |
|---|---|
name, displayName, role, background, expertise | Identity |
personality, voice, values, preferences, boundaries | Behavioral philosophy |
Capabilities such as MCP, skills, rules, and knowledge remain separate agent layers.