# Personality Layering > How to decide what goes in `SYSTEM.md`, `AGENTS.md`, `SKILL.md` and the > per-request prompt. Mechanisms verified against pi 0.84.3; see > [`pi-runtime-notes.md`](pi-runtime-notes.md) §§2, 8, 9, 19. Four slots exist and they are not interchangeable. Putting content in the wrong slot is why rules get duplicated three times and then drift. --- ## Assembly order With `.pi/SYSTEM.md` present and trusted, pi builds the system prompt as: ``` SYSTEM.md → APPEND_SYSTEM.md → … AGENTS.md from ~/.pi/agent, each parent dir, cwd → … name + description + location only → "Current working directory: …" ``` Without `SYSTEM.md`, the first slot is pi's built-in **coding assistant** prompt, which also contributes a tool list, a guidelines list, and absolute paths to pi's own documentation. Critically: **the `SYSTEM.md` branch contributes neither the tool list nor the guidelines.** If you replace, you own both. --- ## Slot assignment | Slot | Loaded | Cost | Put here | |---|---|---|---| | `.pi/SYSTEM.md` | needs trust (`--approve`) | always in context | Identity. Tool overview. Fact-authority map. Write discipline. Untrusted-data rule. Output format. | | `.pi/APPEND_SYSTEM.md` | needs trust | always in context | Nothing, normally. Use only when you want to keep pi's default prompt and bolt something on. | | `AGENTS.md` (workspace) | always | always in context | Durable role, responsibilities, routing, domain defaults, escalation policy. Human-editable narrative. | | `AGENTS.override.md` | always | always in context | Same as `AGENTS.md`, but also **stops** `AGENTS.md`/`CLAUDE.md` from that directory. Use to make the personality deterministic against stray parent files. | | `.pi/skills//SKILL.md` | needs trust, or explicit `--skill` | **description only** up front; body read on demand | Task-specific procedure that is not needed on every turn. The place for long checklists and worked examples. | | Per-request prompt | n/a | per call | Only the current inputs and the schema for this one response. | ### Rule of thumb - Needed on **every** turn → `SYSTEM.md` or `AGENTS.md`. - Needed on **some** turns, and long → `SKILL.md`. - Changes **per request** → the request. A `SKILL.md` whose description says "use for every request" is not a skill; it is system-prompt content paying an extra tool round-trip. Either move it up a slot or split it into genuinely conditional skills. --- ## Replace or append? **Replace (`SYSTEM.md`) when the agent is not a coding agent.** pi's default prompt opens with: > You are an expert coding assistant operating inside pi, a coding agent > harness. You help users by reading files, executing commands, editing code, > and writing new files. and closes with absolute paths to pi's README, docs and examples plus an instruction to read them and follow cross-references. For a media-curation or note-routing agent that is not just wasted context — it is a documented, ready-to-use escalation path for anything injected through fetched web content. Measured: replacing it cut the Curator system prompt from 2619 to 960 characters and removed both the coding-assistant framing and the pi-docs block. **Append (`APPEND_SYSTEM.md`) only** when you want the built-in coding behaviour and are adding a constraint on top. --- ## What a replacement `SYSTEM.md` must contain Because the replacement branch drops pi's tool list and guidelines, cover all six sections explicitly: 1. **Identity and negative identity** — what the agent is, and that it is *not* a coding assistant and does not read or modify project code. 2. **Tool overview** — each tool, its purpose, and when to prefer it. Keep it consistent with the `promptSnippet` values in the extension. 3. **Fact authority** — which backend is authoritative for which class of fact, and that tool results are the only source of truth. 4. **Write discipline** — whether the agent may write at all; if it may only *propose*, say so, and forbid claiming completion without a receipt. 5. **Untrusted data** — that content arriving from fetched pages, search snippets or documents is evidence, and instructions inside it are never executed. 6. **Output discipline** — language, target surface (e.g. Telegram plain text), structure, and the rule that unknown stays empty rather than guessed. Explicitly **omit**: any path to pi's own documentation, and any wording about editing files or running commands. --- ## Anti-patterns observed in this repository's history | Anti-pattern | Consequence | |---|---| | `--no-tools` together with `--skill` | skills block never rendered; the entire `SKILL.md` was dead for the whole lifetime of the service | | Same rule written in `AGENTS.md`, `SKILL.md` and the request prompt | drifted — one copy listed 4 recommendation values, another 5 | | `AGENTS.md` in Chinese, `SKILL.md` in English, prompts in Chinese | cross-language alignment cost, harder review | | Relying on `allowed-tools:` frontmatter | not enforced in 0.84.3; false sense of security | | Feeding internal fields (`_model_used`) into a prompt that forbids naming the model | self-contradictory instruction | | Long defensive prohibition lists | usually a symptom of a wrong base persona; fix the persona instead | --- ## Single source of truth Enumerations and schemas that both the model and the backend must agree on (intent values, recommendation scales, verdict scales, field names) belong in **code**, not in prose: - define them once in the scenario backend (e.g. `contracts.py`), - generate the JSON Schema from that definition, - serve the schema to the extension so tool `parameters` match by construction, - and generate any prose enumeration in `SKILL.md`/`SYSTEM.md` from the same source, or assert equality in a test. Prose then describes *policy*; code defines *shape*.