Files
pi-agent-config/docs/personality-layering.md
T
Kai 07dd648b5f feat: scenarios for curator/memo-inbox/pi-grok, deploy and backup tooling
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.
2026-08-26 23:17:12 -07:00

153 lines
6.9 KiB
Markdown

# 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
→ <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.md` in 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.md` or `APPEND_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:
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*.