Scenarios - memo-inbox: mirrored by copying; the live directory was not moved or modified and the service was not restarted. All four tracked files match byte for byte (pi-diff.sh reports SAME). Marked deploy = "mirror" so deploy-scenario.sh refuses --apply: applying a mirror would invert the direction of truth and could change a service in daily use. - curator: target configuration, not yet deployed. .pi/SYSTEM.md replaces pi's coding-assistant prompt; durable role text is in .pi/APPEND_SYSTEM.md; profile.toml is the single source of truth for the launch contract. - pi-grok: registered only. It is genuinely a coding agent, so the isolation baseline does not apply in full. Corrections to the documentation, found by testing rather than by reading - AGENTS.override.md does NOT block parent-directory context files; it only shadows its own directory. Verified: with an override file in the workspace, a marker in /tmp/AGENTS.md still reached the system prompt. The only effective switch is --no-context-files, so durable role text must live in .pi/APPEND_SYSTEM.md, which is a system-prompt file and unaffected by -nc. Verified end state: no coding-assistant framing, no pi-docs block, own identity and role text present, no parent pollution, only own skills/tools. - PI_CODING_AGENT_DIR isolates settings/models/auth/trust/extensions/skills/ prompts/themes under the agent directory -- stronger than the --no-* flags because it also repoints credentials -- but does NOT cover ~/.agents/skills. Measured: find-skills, modsearch and summarize still leak. So it complements --no-skills rather than replacing it. - --append-system-prompt accepts a file path, which pi-grok relies on. - cwd is what anchors .pi discovery: a probe that forgot cwd silently lost .pi/SYSTEM.md and kept the coding-assistant persona. Tooling (all dry-run by default; none of them restarts a service) - pi-diff.sh: compares tracked config against the live install in both directions, with a key-redacted comparison for models.json - deploy-scenario.sh: installs a workspace and renders profile.toml into .pi/launch.json, then checks that every referenced path exists - deploy-runtime.sh: renders models.json from its template, refusing placeholder or missing keys. Verified byte-identical to the live file - pi-backup.sh / pi-restore.sh: archives outside the repo, sha256 manifest verified before any restore, live paths preserved rather than overwritten Fixed while testing: pi-backup.sh compared the destination against the repo root literally, so a relative --dest ./backups wrote credential archives into the work tree. Now canonicalised with realpath; ./backups, an absolute in-repo path and ./docs/../backups are all refused.
6.9 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 shadows AGENTS.md/CLAUDE.md in its own directory only. It does not stop parent directories — verified. Rarely the right tool. |
.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. |
Deterministic personality requires -nc
Context files layer from ~/.pi/agent/AGENTS.md and from every parent
directory of the working directory. AGENTS.override.md shadows only its own
directory, so it cannot protect you: with an override file present in the
workspace, a marker placed in /tmp/AGENTS.md still reached the system prompt.
The only switch that stops the upward walk is --no-context-files (-nc), and it
drops the workspace's own file too. So for an agent whose personality must be
reproducible:
- pass
-nc; - put identity in
.pi/SYSTEM.md; - put durable role text in
.pi/APPEND_SYSTEM.md; - keep no
AGENTS.mdin the workspace at all.
Both SYSTEM.md and APPEND_SYSTEM.md are system-prompt files, not context
files, so -nc does not affect them. Verified: identity and role text present,
parent marker absent.
AGENTS.md remains the right slot for a shared, layered convention — for
example a repository-wide instruction that every agent working in a source tree
should honour. It is the wrong slot for a single-purpose service agent.
Rule of thumb
- Needed on every turn →
SYSTEM.mdorAPPEND_SYSTEM.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.