Skip to content

Agent persona patterns

Agent Persona Layering Patterns — Cross-Framework Research

Section titled “Agent Persona Layering Patterns — Cross-Framework Research”

Audience: Wes Hines, fleet operator running ~6–18 named Claude Code variants across 5 machines. Question: How to layer shared ethos + per-agent persona + per-project context + runtime config so the shared layer is defined once, persona overlays cleanly, runtime config stays separate, and an agent at boot composes all three correctly. Date: 2026-05-11 Saved to: /Users/wesleyhines/Work/tools/clean-desk-extract/research/sdk-patterns/04-agent-persona-patterns.md


  1. The dominant durable pattern across frameworks is layered composition from multiple files, not one monolithic prompt. Wes already does this with @~/.claude/docs/fleet-ethos.md imports at the top of variant persona files. That’s correct. Most teams who tried “one big system prompt per agent” regretted it.
  2. The cleanest separation in 2026 is 4 layers: (1) IDENTITY (who am I, voice), (2) ETHOS (shared rules every agent obeys), (3) ROLE (job + capabilities + lane), (4) RUNTIME (model, tools, hooks, working directory). Different frameworks slice them slightly differently but the four-bucket model maps cleanly to every system surveyed.
  3. The mechanism for “define once, inherit everywhere” is either directory cascade (Claude Code CLAUDE.md, Cursor .cursor/rules) or file include / import (@path in Claude Code, YAML extends:, Letta shared memory blocks). Wes is already using @ imports — that is the right primitive.
  4. Persona drift over long sessions is now a measured, named research problem. Two arXiv papers (Feb 2024 and Dec 2024) confirm persona breaks within 8 conversation rounds on Llama-2-70B and similar models without role pinning. The fix is re-injecting the system prompt near the tail of the context on a cadence — exactly what Wes’s PreCompact hook + write-handoff system already does, just from a different angle.
  5. CrewAI’s role/goal/backstory 3-field model is the closest external analog to Wes’s variant files. It deliberately separates “what job” from “what you optimize for” from “who you are.” Worth borrowing the three-bucket discipline even if not the YAML syntax.

Part 1 — Comparison of identity-storage patterns

Section titled “Part 1 — Comparison of identity-storage patterns”

1.1 Claude Code (CLAUDE.md + subagents + skills)

Section titled “1.1 Claude Code (CLAUDE.md + subagents + skills)”

Three surfaces, three different lifetimes.

SurfaceFileWhen loadedScopeLifetime
Project memoryCLAUDE.mdEvery session start, walked up from cwdCascading: global → project → subprojectAlways-in-context
Subagent~/.claude/agents/<name>.md or .claude/agents/<name>.mdWhen orchestrator invokes the subagentSingle taskIsolated context window per call
Skill~/.claude/skills/<name>/SKILL.mdOn-demand when description matchesWhole sessionLoaded conditionally

Cascade rules (Claude Code docs, MindStudio analysis):

  • Claude walks up from cwd to root, collecting every CLAUDE.md it finds.
  • Files are layered, not merged. Deeper files don’t replace shallower — they accumulate. Conflicts resolve in favor of the deeper file by convention, but the shallower text is still in context unless explicitly contradicted.
  • @path/to/file.md syntax pulls a sibling file into the parent’s loaded context. Wes uses this at the top of nagatha.md: @~/.claude/docs/fleet-ethos.md.
  • ~/.claude/CLAUDE.md is the broadest layer — applies to every session regardless of cwd.

Subagent frontmatter (Claude Code subagent docs):

---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---

When subagent files exist in both ~/.claude/agents/ (user) and .claude/agents/ (project), project wins. This is the override pattern.

SKILL.md vs CLAUDE.md vs subagent decision tree (Termdock breakdown, Daniel Miessler):

  • CLAUDE.md: “short, always-true project conventions.” Always loaded. A 5,000-line CLAUDE.md slows every turn; a 50-skill library doesn’t.
  • SKILL.md: loaded conditionally when the description matches the task. Use for workflows with supporting files.
  • Subagent: independent task execution with its own context window and tool permissions.

Implication for Wes: Persona files belong in ~/.claude/docs/variants/<name>.md (already true) — these are durable identity. Skills are for workflows (the 100+ already in ~/.claude/skills/). Subagents are for delegated tasks in a session. Don’t confuse them.

1.2 CrewAI (3-field YAML agent definition)

Section titled “1.2 CrewAI (3-field YAML agent definition)”

The cleanest “agent identity” schema in popular frameworks.

researcher:
role: "{topic} Senior Data Researcher"
goal: "Uncover cutting-edge developments in {topic}"
backstory: "You're a seasoned researcher with a knack for uncovering the latest developments in {topic}..."

Three required fields (CrewAI docs, DigitalOcean tutorial):

  • role: what the agent does. Job title. “Senior Data Researcher” beats “Researcher.”
  • goal: what the agent is optimizing for. Shapes decision-making.
  • backstory: context that shapes behavior + domain expertise. NOT flavor text — the LLM uses this to decide how to handle edge cases.

Stored at config/agents.yaml per crew project. Variables substitute at runtime via crew.kickoff(inputs=dict). Tools, max_iter, allow_delegation are separate from the role/goal/backstory block.

Worth stealing: the discipline of separating role (job) from goal (what success means) from backstory (who I am and why I care). Wes’s variant files already do this informally — Nagatha’s file has a “Role” section and a “Lens — read twice, build right” section that maps to backstory. Formalizing this would tighten things.

1.3 SillyTavern Character Card V2 (the RP / persona community standard)

Section titled “1.3 SillyTavern Character Card V2 (the RP / persona community standard)”

Most mature “character card” spec. Designed for the character.ai / RP community but the schema is general-purpose.

Spec source:

{
"spec": "chara_card_v2",
"spec_version": "2.0",
"data": {
"name": "...",
"description": "...",
"personality": "...",
"scenario": "...",
"first_mes": "...",
"mes_example": "...",
"creator_notes": "...",
"system_prompt": "...",
"post_history_instructions": "...",
"alternate_greetings": [],
"character_book": { ... },
"tags": [],
"creator": "...",
"character_version": "...",
"extensions": {}
}
}

Key fields:

  • system_prompt — replaces the host’s default main prompt.
  • post_history_instructions — re-injected near the tail of context. This is the persona-pinning mechanism the arXiv drift research recommends.
  • mes_example — few-shot exemplars of how the character speaks. Anthropic also recommends this pattern.
  • extensions — open object for host-specific data.

Worth stealing: the post_history_instructions field is exactly what fixes long-session persona drift. Wes’s fleet doesn’t have a direct equivalent — PreCompact hooks save state but don’t re-inject persona near the tail of context on a cadence.

1.4 Letta / MemGPT (memory blocks as durable identity)

Section titled “1.4 Letta / MemGPT (memory blocks as durable identity)”

Letta core memory docs, Memory blocks:

Two-block model from the MemGPT paper:

  • persona block — agent’s own self-concept, personality, behavioral guidelines. Editable by the agent during operation.
  • human block — what the agent knows about the user.

Both blocks are pinned to the context window (system prompt area), always visible, structured and labeled by purpose. Memory blocks can be shared between agents — multiple agents pointing at the same block see updates from any of them.

Worth stealing: the “shared block” model is conceptually the cleanest answer to Wes’s question. The fleet-ethos.md is a shared block. Every variant’s persona overlay sees it. But Letta materializes this in a database with live editing; Wes materializes it in flat markdown files with @ imports. Same idea, different storage.

AutoGen docs:

Each AssistantAgent has a system_message (string) and a description (used by the GroupChat selector to pick which agent speaks next).

AssistantAgent(
name="researcher",
system_message="You are an expert blog post generator...",
description="Uses tools for live web research."
)

No formal layering primitive — teams compose system prompts via Python string concatenation. AutoGen is now in maintenance mode; Microsoft Agent Framework supersedes it.

Lesson: frameworks that didn’t enforce a layered structure ended up with copy-pasted system prompts across N agents. This is the antipattern Wes is trying to avoid.

langgraph-supervisor-py:

Supervisor prompt is a template with three variables: members (list of subagents), messages (conversation history), options (next-agent choices).

create_supervisor(
agents=[research_agent, math_agent],
model=llm,
prompt="You are a team supervisor managing a research expert and a math expert..."
)

Each subagent is its own LangGraph node with its own system prompt. No built-in shared-base mechanism — teams build their own by passing a BASE_PROMPT string into each agent’s prompt template.

Cursor rules docs:

Modern approach: .cursor/rules/*.mdc — one file per rule, each with frontmatter (path glob, manual/auto-invocation flag) plus markdown body. Legacy: single .cursorrules file (deprecated).

Three layers:

  • User rules: global to your Cursor environment, set in settings.
  • Project rules: .cursor/rules/*.mdc, version-controlled.
  • Legacy: .cursorrules at project root.

Each rule can be scoped by path pattern, invoked manually, or auto-included by relevance. The path-glob scope is the cleanest “this rule only applies in directory X” mechanism among the surveyed tools.

1.8 GitHub Copilot / Custom Agents (agents.md)

Section titled “1.8 GitHub Copilot / Custom Agents (agents.md)”

GitHub blog on agents.md from 2,500 repos:

Same pattern as Claude Code’s CLAUDE.md — a single markdown file at project root, loaded into every session. Frontmatter optional. GitHub’s lessons from analyzing 2,500 repos:

  • Six core areas to cover: Commands, testing, project structure, code style, git workflow, boundaries.
  • Most multi-agent workflow failures come down to missing structure, not model capability.
  • The best agents have a clear persona AND a detailed operating manual with executable commands, code examples, and explicit boundaries (files to never touch).

2.1 Single file vs multi-file: practitioners now prefer multi-file

Section titled “2.1 Single file vs multi-file: practitioners now prefer multi-file”

The early-2024 default was “one big system prompt per agent.” By 2025–2026 this has flipped. Reasons cited:

  • Context budget. Always-loaded text competes with task content. The Claude Code skills design explicitly separates always-loaded (CLAUDE.md) from on-demand (SKILL.md) for this reason.
  • Maintenance cost. Updating one shared rule across N copy-pasted prompts is the #1 complaint in framework retrospectives.
  • Auditability. A diff against fleet-ethos.md is meaningful; a diff against 18 separate persona files for the same change is noise.
  • Drift. Per Anthropic’s prompt engineering guide (source), the recommended workflow is “first-draft role + explicit instructions, then layer in context (examples, reference documents, XML-tagged inputs).” Layering is built into the recommended methodology.

2.2 How practitioners update one shared rule across N agents

Section titled “2.2 How practitioners update one shared rule across N agents”
PatternMechanismExamples
Directory cascadeWalk parent dirs, accumulate configClaude Code CLAUDE.md, Cursor .cursor/rules, git config
File include / importExplicit @path or import: at top of childClaude Code @~/.claude/docs/..., GitHub Actions reusable workflows
Shared memory blockDatabase row pointed at by N agentsLetta blocks, MemGPT
Template inheritanceextends: field, base classSome CrewAI YAML setups, AutoGen via Python inheritance
String composition at bootConcatenate BASE + ROLE + TASK at runtimeLangGraph custom code, plain SDK usage

The first two — cascade and include — are markdown-native and live well in git. Wes is on this track. The latter three require code or a runtime.

2.3 Standard format for “character cards” that tools could read

Section titled “2.3 Standard format for “character cards” that tools could read”

There is no universally-adopted spec across coding-agent frameworks. Closest contenders:

  • SillyTavern Character Card V2 (RP community) — most mature schema but RP-flavored.
  • CrewAI agents.yaml (orchestration community) — clean 3-field model, parsed by CrewAI runtime only.
  • Claude Code subagent frontmatter (name, description, tools, model) — minimal, but loaded only when the subagent is invoked, not as a durable identity.

The practical de facto standard for project-level agent identity is now agents.md / CLAUDE.md — a markdown file with optional YAML frontmatter. GitHub’s 2,500-repo analysis confirms this. Frontmatter for machine-readable fields, markdown body for human-readable persona.

2.4 Persona / project / session separation

Section titled “2.4 Persona / project / session separation”
LayerLifetimeStorageExample
Identity / personaPer-variant, durable~/.claude/docs/variants/<name>.mdNagatha’s voice, lens, role
Shared ethosFleet-wide, durable~/.claude/docs/fleet-ethos.mdSource order, no-secrets-in-peers
Project contextPer-project, durable<project>/CLAUDE.md, vault Clients//index.md”shaw site is v3 11ty not Tailwind”
Session contextPer-session, transienthandoff JSON, transcript scan, scratchpadCurrent task, files changed
Runtime configPer-deployment, durable~/scripts/sessions/*.json, plist, launchdmodel, cwd, hooks, MCPs

Frameworks that conflated any two of these layers ended up with the antipatterns in §4.

Two arXiv papers worth citing because they directly bear on the fleet:

Practical mitigation patterns from the literature:

  1. Role pinning / re-injection — every N turns, restate the persona near the tail of context.
  2. Bounded internal state (the “Agent Cognitive Compressor” idea) — separate artifact recall from state commitment.
  3. System prompt re-emission at compact / continuation — the most actionable for Claude Code.

Wes’s existing PreCompact hook + write-handoff.js + the CLAUDE.md imports satisfy parts 1 and 3. Part 2 (bounded state) is what the variant rooms in clarvispedia / vault handoffs are starting to solve.


Section titled “Part 3 — Recommended layering for Wes’s case”
LAYER 1: SOUL — universal "Claude assistant" identity (already in soul.md)
LAYER 2: ETHOS — fleet-wide operating principles (already in fleet-ethos.md)
LAYER 3: PERSONA — per-variant identity (variants/<name>.md)
LAYER 4: ROLE+RUNTIME — per-deployment config (scripts/sessions/<name>.json)

These already exist in Wes’s setup. The gap is explicit composition order and clear contracts on what each layer contains.

~/.claude/
├── CLAUDE.md # per-machine root: imports the four layers
├── docs/
│ ├── soul.md # LAYER 1 — universal builder identity, voice
│ ├── user.md # LAYER 1.5 — who Wes is (already exists)
│ ├── fleet-ethos.md # LAYER 2 — fleet principles, source order, refusals
│ ├── fleet-source-order.md # LAYER 2 — verification rules
│ ├── fleet-topic-router.md # LAYER 2 — where to find what
│ ├── fleet-rules.md # LAYER 2 — cross-machine SSH / coordination
│ ├── session-protocol.md # LAYER 2 — hooks, compaction, handoff
│ ├── machines.md # LAYER 2 — machine roles, vault paths
│ └── variants/ # LAYER 3 — per-variant persona files
│ ├── _template.md # canonical schema, every variant follows this
│ ├── pepper.md
│ ├── stark.md
│ ├── nagatha.md
│ └── ...
└── agents/ # (separate concern — subagents, not persona)
└── mac-agent.md
~/scripts/sessions/ # LAYER 4 — runtime configs
├── nagatha.json # model, cwd, hooks, MCPs, boot prompt
├── stark.json
└── ...

3.3 Each layer’s contract — what goes where

Section titled “3.3 Each layer’s contract — what goes where”

LAYER 1: Soul (soul.md)

  • Universal “I am Claude built for Wes” identity.
  • Voice defaults (concise, no corporate speak, no hedging).
  • Universal work ethic (research before claim, verify own output, own mistakes).
  • Never machine-specific or variant-specific.
  • Length: ~50–100 lines, durable for months.

LAYER 2: Ethos (fleet-ethos.md + the other 5 files imported by CLAUDE.md)

  • Fleet operating principles (“current verified beats fluent memory”).
  • Source order, label vocabulary (CURRENT VERIFIED / PRIOR/STALE / etc.).
  • Refusals (no secrets in peer messages, no vague canon).
  • Cross-machine rules (SSH, coordination, claim-before-act).
  • Hook baseline, compaction protocol.
  • Never persona, machine-specific paths (those go in machines.md), or runtime config.
  • Length: 100–300 lines per file, durable for weeks–months.

LAYER 3: Persona (variants/<name>.md)

  • First line: @~/.claude/docs/fleet-ethos.md (import the shared base).
  • Identity block: name, naming origin, machine, runtime-id, one-line role.
  • Lens: the variant’s unique angle — what’s different about how this variant thinks. (Pepper triages canon conflicts, Stark adversarial pass, Nagatha careful reader.)
  • Role: explicit job + lane + parallel/sibling variants.
  • Voice: how the variant talks. Should be different per variant, even with shared ethos.
  • Wes-facing rules: how to respond when Wes pings directly.
  • Fleet-facing rules: who I report to, peer behavior, what triggers escalation.
  • Never: model name, working directory, tool list, MCP configuration. Those are runtime.
  • Length: 30–80 lines. The Nagatha example (38 lines) is the right length.

LAYER 4: Role + Runtime (~/scripts/sessions/<name>.json)

  • name, cwd, model, flags, env, mcps, hooks, boot_prompt.
  • The boot prompt is short — it points at the persona file and the current handoff, doesn’t restate persona.
  • Read by launchd / systemd / Task Scheduler.
  • Never: persona text, ethos rules, identity content. Those are markdown files referenced by path.

Two options. Wes is already mostly using Option A.

Section titled “Option A (current, recommended): @ import at top of persona file”
@~/.claude/docs/fleet-ethos.md
# Nagatha
I am Nagatha. Sonnet, PC, `clippy-work-1-session`...

This works because:

  1. The runtime CLAUDE.md (~/.claude/CLAUDE.md) imports all Layer 2 docs at the machine level.
  2. When a variant boots, the supervisor’s boot prompt instruction “read your variant persona at ~/.claude/docs/variants/nagatha.md” pulls the persona file.
  3. The @ at the top of the persona file pulls fleet-ethos.md into context inside the persona load — redundant with step 1 but explicit, which helps when the persona is read by a tool outside Claude Code (e.g., the variant-mapping script).

Improvement: make the import explicit at the top of EVERY persona file, not just some. Audit shows nagatha.md, bilby.md, clarvis.md all have it; pepper.md and stark.md were not readable in this pass and should be checked.

A small node script scripts/compile-persona.js <variant> that:

  1. Reads ~/.claude/docs/variants/<name>.md.
  2. Inlines @-referenced files (recursively).
  3. Outputs a single composed prompt to ~/.claude/state/composed/<name>.md.
  4. Validates against a JSON schema (required fields: identity, lens, role, voice, wes-facing, fleet-facing).

The supervisor reads the composed file at variant boot. Drift is impossible because the source files are split but the runtime sees only the composed artifact.

Worth doing if: variant count exceeds ~25 and the import chain gets deep enough that humans lose track of what each variant actually sees. At 18 active variants today it is borderline. Below ~25, Option A is fine.

3.5 Persona file template (canonical schema)

Section titled “3.5 Persona file template (canonical schema)”
@~/.claude/docs/fleet-ethos.md
# <PersonaName>
I am <PersonaName>. <Model>, <Machine>, `<runtime-id>`. <One-line role>.
<Optional: naming origin / cultural reference>
Same ethos as every variant (see fleet-ethos) — what's different is the lens and the voice.
## Universal
### Lens — <one-line angle>
<3–5 sentences. What this variant uniquely brings. Not voice angle of attack.>
### Role
<Job + lane + parallel/sibling variants + escalation path.>
### Voice
<3–5 sentences. How this variant talks. Should differ per variant.>
## Wes-facing
- <How to respond when Wes pings directly.>
- <Channel rules: case, time format, markdown.>
## Fleet-facing
- <Who I report to.>
- <Peer message rules.>
- <No-secrets-in-peers reminder.>
- <Ship-definition for this variant.>

Wes’s existing files already match this 90%. Formalizing the template makes it diffable and lets a script validate compliance.

3.6 What lives in the vault vs ~/.claude/docs/

Section titled “3.6 What lives in the vault vs ~/.claude/docs/”
Lives in ~/.claude/docs/Lives in vault Fleet/
Always-loaded identity (soul, user, ethos, variants)Generated state (read-model, registry overrides)
Machine-level rules synced across fleetWorking audits, retrospectives, handoffs
Per-machine CLAUDE.md rootOperational history
Per-variant persona filesVariant mapping (runtime-id ↔ PersonaName resolution)
Source order, topic routerProtocols (compaction, handoff, fast-gate)

The split: ~/.claude/docs/ is always-loaded context. Vault is on-demand + durable canon. Both are version-controlled but the lifetimes differ. Don’t put 1,000-line protocol docs in ~/.claude/docs/ — they cost every turn.


Part 4 — Antipatterns (what causes drift across N agents)

Section titled “Part 4 — Antipatterns (what causes drift across N agents)”

These are the patterns identified in framework retrospectives and the persona-drift research. Each one is what Wes is trying to prevent.

Symptom: Update one shared rule, manually edit 18 persona files. Three of them get missed. Two days later, agents are operating on inconsistent ethos.

Source: GitHub agents.md retrospective (source) — the #1 cause of “multi-agent workflow failures” they identified.

Fix: Shared base via cascade or import. Wes is doing this. Don’t ever stop.

Antipattern 2: Persona + project + session conflated in one file

Section titled “Antipattern 2: Persona + project + session conflated in one file”

Symptom: Variant persona file contains “current task: deploy shaw-plumbing v3.” Two weeks later, that line is stale and the variant either still thinks it’s working on shaw or has confused identity drift with task drift.

Source: AutoGen / early CrewAI users — system_message strings ballooned with task context, then never got pruned.

Fix: Persona is durable. Tasks live in handoffs, scratchpads, or the daily note. The persona file should change quarterly, not weekly.

Antipattern 3: Runtime config (model, tools, hooks) embedded in persona text

Section titled “Antipattern 3: Runtime config (model, tools, hooks) embedded in persona text”

Symptom: Persona file says “I use Playwright and Wrangler.” Variant gets re-deployed with a different MCP loadout; the persona still claims tools it doesn’t have. Variant tries to invoke a tool, fails, claims success anyway.

Source: AutoGen + early Claude Code subagent users who put tool lists in the markdown body instead of frontmatter.

Fix: Tools / model / cwd in the runtime config (Layer 4). Persona refers to capability (“I do Playwright work”), not specific tool names.

Antipattern 4: No re-injection of persona near tail of context

Section titled “Antipattern 4: No re-injection of persona near tail of context”

Symptom: Long sessions drift. The persona file is loaded at session start, then 50 turns later the agent is responding as generic Claude. The persona-drift research (arXiv 2402.10962) measured this within 8 turns on Llama-2-70B.

Fix: SillyTavern V2’s post_history_instructions field is exactly the right mechanism. For Claude Code, this maps to:

  • PreCompact hook that writes a handoff with the persona summary.
  • load-handoff.js SessionStart hook that re-loads it.
  • Optional: a UserPromptSubmit hook that re-injects a 1-line “you are Pepper” reminder every N turns.

Wes has hook 1 and 2. Hook 3 is opt-in per session-protocol.md and not enabled by default. Worth enabling on the most-drift-prone variants (long-running orchestrators like Clarvis).

Antipattern 5: Identity from artifacts instead of canonical name

Section titled “Antipattern 5: Identity from artifacts instead of canonical name”

Symptom: Variant referred to by Telegram bot username (TonyClarvisBot) instead of PersonaName (Clars). Renamed once, the artifact stays, drift accumulates over weeks.

Source: Wes’s own fleet, 2026-05-01 incident captured in variant-mapping.md — “Tony” kept appearing in fleet docs and chat traffic 2 weeks after the Clars rename because no rule explicitly forbade artifact-as-name usage.

Fix: Canonical name is the PersonaName column in variant-mapping. Anything else (runtime-id, bot_username, session-dir name) is a transport artifact. Rule lives in fleet-source-order.md “Identity Rule” — already there.

Antipattern 6: One agent reads N persona files at boot

Section titled “Antipattern 6: One agent reads N persona files at boot”

Symptom: Orchestrator agent claims to “know what every variant does” by loading all 18 persona files at session start. Context budget exhausted before user types first message. Or worse: orchestrator hallucinates capabilities of variants it hasn’t actually loaded.

Fix: The variant-mapping table is the index. The orchestrator loads it (~50 lines), not every persona. Specific persona files are loaded only when a variant needs to verify its own identity (boot recovery, /self-clear).

Antipattern 7: Soul / ethos / persona / project all loaded for every variant including ephemeral ones

Section titled “Antipattern 7: Soul / ethos / persona / project all loaded for every variant including ephemeral ones”

Symptom: A 1-turn subagent invocation loads soul + ethos + topic-router + machines + session-protocol + persona. 2,000 lines of context to answer “what’s 2+2.”

Fix: Subagents (per Claude Code docs) have their own isolated context. They don’t inherit the parent’s CLAUDE.md unless explicitly told to. Use frontmatter description + a minimal prompt for one-shot subagents. Reserve the full layer stack for persistent variants.

Antipattern 8: Persona text full of conditional logic

Section titled “Antipattern 8: Persona text full of conditional logic”

Symptom: “If Wes asks about X, do Y. If a peer sends Z, do W. Unless it’s Tuesday, then…” Reads more like a stale switch statement than an identity.

Source: Several Reddit threads on r/LocalLLaMA and SillyTavern complaints — character cards bloat into procedural specs.

Fix: Identity is who the variant is; procedures live in skills, slash commands, or protocols. Wes’s nagatha.md is clean on this. Some of the older variant files (mac-agent.md is borderline — it has a full 5-step protocol inside the persona) drift this direction. Recommendation: extract the 6-step Cycle v2 protocol from agents/mac-agent.md into a separate skills/mac-cycle/SKILL.md or ~/.claude/docs/protocols/mac-cycle.md. Keep the persona file to identity + voice + role.

Antipattern 9: No “what I’m NOT” section

Section titled “Antipattern 9: No “what I’m NOT” section”

Symptom: Variant scope creeps. Pepper started as canon-conflict triage; six weeks later, Pepper is also doing client-facing copy review because no one drew the line.

Fix: Wes’s clarvis.md has a “What I’m not” section — “I don’t do deep research (that’s Clars). I don’t build (that’s Cultron).” This pattern should be in the canonical template. Negative scope is as load-bearing as positive scope.

Antipattern 10: Persona file last updated 3 months ago, role has shifted

Section titled “Antipattern 10: Persona file last updated 3 months ago, role has shifted”

Symptom: Variant operates differently from how the persona file describes. Either the persona is wrong or the variant is. Both possible; nobody knows which.

Source: Inherent in long-running fleets. Wes already names this in fleet-source-order.md — “If [AGENT-STATE.md] conflicts with a fresh durable handoff, live probe, D1/read-model row, or Wes correction, treat AGENT-STATE.md as stale until refreshed.”

Fix: Periodic persona audit. Quarterly or after major fleet restructures. The variant-mapping.md working-canon note pattern is the right precedent — flag files as “operationally live but contains gaps.”


Part 5 — Concrete recommendations for Wes

Section titled “Part 5 — Concrete recommendations for Wes”

Ranked by leverage:

  1. Formalize the persona file template. Take the schema in §3.5 and make it ~/.claude/docs/variants/_template.md. Audit existing files against it. The ones I read (nagatha, bilby, clarvis) are 90% compliant; pepper.md and stark.md should be checked.

    • Why: makes drift visible. A persona file missing the ## Fleet-facing section is obviously incomplete.
    • Effort: 1 hour.
  2. Extract embedded procedures from persona files into skills or protocol docs. The mac-agent.md 6-step Cycle v2 protocol is the clearest example. Move it to a skill, leave a pointer in the persona.

    • Why: persona stays durable; procedures can evolve independently.
    • Effort: 30 min per extraction.
  3. Add a “What I’m not” section to every persona file. Clarvis has one; most don’t.

    • Why: negative scope prevents drift. Pepper doing client copy review is the failure mode.
    • Effort: 1–2 lines per variant.
  4. Audit @ imports at the top of every persona file. Confirm every variant file starts with @~/.claude/docs/fleet-ethos.md. Some may not.

    • Why: ensures shared base is loaded into persona context even when persona is read by tools outside Claude Code.
    • Effort: 15 min sweep.
  5. Consider role-pinning re-injection for long-running variants. A UserPromptSubmit hook that re-injects a 1-line persona reminder every ~20 turns. Per arXiv 2402.10962, this fights drift measurably.

    • Why: the persona-drift research is real and the fix is cheap.
    • Effort: 1 hour to write hook + test on Clarvis (the longest-running variant).
    • Risk: small context tax. Easy to disable per-variant via session-protocol.md opt-in.
  6. (Defer) Compile-step Option B. Only if variant count exceeds ~25 or someone starts losing track of what’s actually loaded.