Establishes this repository as the authoritative source for Pi agent configuration across scenarios, starting with the documentation layer. Key verified findings (probe harness included, zero model tokens): - The skills section of the system prompt is emitted only when an active tool named 'read' exists (system-prompt.js:59,113). Therefore --no-tools silently makes every SKILL.md unreachable and --skill a no-op. - registerTool accepts a plain JSON Schema object, so tool definitions can be served from a backend instead of duplicated in TypeScript. - An extension can shadow a built-in tool by name, which is how a dedicated agent gets a path-restricted 'read' while still satisfying the rule above. - .pi/SYSTEM.md replaces pi's coding-assistant prompt, but the replacement branch contributes neither the tool list nor the guidelines. - Without --no-skills/--no-extensions, user-global resources leak into every scenario; probed leak was find-skills, modsearch, summarize. Measured effect of the full baseline: system prompt 2619 -> 960 characters, coding-assistant framing and pi-docs paths removed, skill finally reachable. Secrets are guarded by scripts/verify-no-secrets.sh, installed as a pre-commit hook. Backups deliberately live outside the repository.
5.8 KiB
Personality Layering
How to decide what goes in
SYSTEM.md,AGENTS.md,SKILL.mdand the per-request prompt. Mechanisms verified against pi 0.84.3; seepi-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
→ <project_context> … AGENTS.md from ~/.pi/agent, each parent dir, cwd
→ <available_skills> … 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/<n>/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.mdorAGENTS.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:
- Identity and negative identity — what the agent is, and that it is not a coding assistant and does not read or modify project code.
- Tool overview — each tool, its purpose, and when to prefer it. Keep it
consistent with the
promptSnippetvalues in the extension. - Fact authority — which backend is authoritative for which class of fact, and that tool results are the only source of truth.
- Write discipline — whether the agent may write at all; if it may only propose, say so, and forbid claiming completion without a receipt.
- Untrusted data — that content arriving from fetched pages, search snippets or documents is evidence, and instructions inside it are never executed.
- 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
parametersmatch by construction, - and generate any prose enumeration in
SKILL.md/SYSTEM.mdfrom the same source, or assert equality in a test.
Prose then describes policy; code defines shape.