Skip to main content
Grounded source for training page 2.0.4 “Filing work in Pi’s VFS”. Everything here is cited to code in apps/pi and packages/db. Where the PLAN’s premise didn’t match the code, it’s flagged with ❓ [needs Raymond: …] — do not paper over these in the published page.
Premise correction up front: the SOP/PLAN reference to a /reports/ folder does not exist in code or skills. The real canonical persistence directory is /memory/. references/… is skill-relative, not an operator-facing org folder. Details below.

1. What the VFS actually is

Pi’s per-org “file system” is database-backed, not a real disk. Each file is a row in the VirtualFile Prisma model.
  • Model: packages/db/prisma/schema.prisma:1196-1208 — fields path, content (@db.Text), isDirectory, scoped by organizationId with @@unique([organizationId, path]). Every file belongs to exactly one org; a directory is just a row with isDirectory = true.
  • Class: apps/pi/src/lib/virtual-fs.ts:22class VirtualFS(db, organizationId), constructed per request/org (e.g. apps/pi/src/chat.ts:25, apps/pi/src/lib/run-agent.ts:148). Paths are normalized POSIX; the agent’s working directory is the VFS root / (run-agent.ts DEFAULT_CWD = '/').
Two-layer design (matters for operators). There’s a shared core layer (CORE_ORG_ID = '__core__', virtual-fs.ts:11) that always wins on read and holds system files — including the skills tree at /skills/**/SKILL.md. When an operator/Pi tries to write over a core file, the write is intercepted and saved as a ProposedEdit for review, with the message “Core file ’…’ is read-only. Your proposed changes have been saved for review.” (virtual-fs.ts:110-126). So org work lives in the org layer; the shared skills/system files are read-only.

The tools Pi uses on the VFS

Registered in apps/pi/src/tools/core/virtual-fs/create-virtual-fs-tools.ts; spread into the agent toolset via create-core-tools.ts:
ToolDoesFile
readread a file (text + images), paged.../virtual-fs/read.ts
writecreate/overwrite a file.../virtual-fs/write.ts
editexact find-and-replace.../virtual-fs/edit.ts
lslist a directory.../virtual-fs/ls.ts
findglob search by path pattern.../virtual-fs/find.ts
grepregex/literal content search.../virtual-fs/virtual-grep.ts
deleteremove (recursive flag).../virtual-fs/delete.ts
For the training page: operators don’t call these directly — they ask Pi in plain language (“save this to…”, “what did we decide about…”) and Pi uses these tools.

2. Standard folders — where files live

Confirmed conventions from code + skills:

Root context files — apps/pi/src/lib/context-files.ts:10-15 (CONTEXT_PATHS)

PathWhat it holds
/SOUL.mdPer-org persona / voice / values overlay (see §3)
/IDENTITY.mdPi’s self-identity for this org — name, creature, vibe, emoji
/MEMORY.mdThe long-term memory index — injected into the prompt every turn
/BOOTSTRAP.mdOne-time onboarding script; deleted after onboarding
These four are auto-loaded into the system prompt each turn by loadContextFiles (context-files.ts:17-35, wired in apps/pi/src/systemPrompt/index.ts:117).

/memory/ — the canonical persistence directory (the real “reports”)

This is where durable artifacts go. The pattern across skills: write the canonical copy to a deterministic /memory/… path; chat is only the working copy. Confirmed uses:
  • Sub-agent large outputs → /memory/<task_id>/<name>.md, returning a ref (systemPrompt/index.ts:194, 217).
  • Product Bible → /memory/{account}-product-bible.md (apps/pi/core-skills/product-bible/SKILL.md:90).
  • Keyword-eval reports/followups → /memory/keyword-eval-report-<orgId>-<YYYY-MM-DD>.md / -followups-… (apps/pi/core-skills/keyword-evaluation/SKILL.md:159, 184, 204).
  • SEO-debug reports → /memory/debug-<slug>-<orgId>-<YYYY-MM-DD>.md, described as “the canonical artifact… Chat is the working copy” (apps/pi/core-skills/seo-debug/SKILL.md:242, 273, 288).
  • Analytics setup notes → /memory/posthog.md, /memory/mixpanel.md (apps/pi/core-skills/posthog-setup/SKILL.md:48, mixpanel-analytics/SKILL.md:17).

/account-management/ — the structured operator workspace (AM cadence)

Fully specified in apps/pi/core-skills/account-management/SKILL.md:22-56:
/account-management/
├── memory.md              ← index: current month + links to notes (keep < 20 lines)
├── notes/
│   ├── style-preferences.md
│   ├── legal-compliance.md
│   └── client-preferences.md
├── 2026-04/               ← monthly dir, YYYY-MM by billing-cycle start
│   ├── plan.md            ← themes, goals, metric targets, weekly refs
│   ├── week-1.md … week-4.md
│   └── ad-hoc.md          ← unplanned tasks
Navigation rule: always start at /account-management/memory.md (SKILL.md:45-48, 271). Note this is a different file from root /MEMORY.md — don’t conflate the two in the docs.

references/… is skill-relative — NOT an org folder

references/onboarding-weeks-1-4.md etc. are relative to a skill’s own directory in the /skills tree (e.g. account-management/SKILL.md:91, 118, 243-250), read by Pi from within a loaded skill. Do not document references/ as an operator-facing VFS directory.

3. How memory & soul work

Both are plain VFS files loaded into the system prompt each turn (not DB columns), via formatContextFilesPrompt (context-files.ts:49-62):
  • SOUL → injected in <soul> with “Embody this persona and tone.”
  • IDENTITY → injected in <identity>.
  • MEMORY → injected in <memory> with “Use this memory to maintain continuity across sessions.”
Memory rules operators should know (context-files.ts:64-72):
  • Before answering about prior work/decisions/people/preferences, Pi searches MEMORY.md + memory/*.md, then reads only the needed lines (the “Memory Recall” rule).
  • MEMORY.md is truncated after line 200 in the prompt, so it’s kept as a concise index — detailed notes go in separate files under /memory/ and are linked from MEMORY.md. Organized by topic, not chronologically; stale entries get pruned.
Important nuance on SOUL. Pi’s core working stance / values are hardcoded in the system prompt, not in /SOUL.md — the code comment says this is deliberate so “every org gets it on day one without depending on a per-org VFS file being seeded” (systemPrompt/index.ts:46-53, workingStance at :77-97). So /SOUL.md is an optional per-org overlay on top of an always-present stance — an org with no SOUL.md still behaves correctly. The docs should not claim SOUL.md is required for Pi to have a personality. Onboarding / how it’s set up (apps/pi/src/lib/bootstrap.ts):
  • BOOTSTRAP_TEMPLATES seeds default /SOUL.md, /IDENTITY.md, /MEMORY.md, /BOOTSTRAP.md (bootstrap.ts:3-115); bootstrapContextFiles writes each only if absent (idempotent).
  • Triggered by POST /api/v1/orgs/:orgId/bootstrap (apps/pi/src/routes/bootstrap.ts:7), auth-gated so the JWT org claim must equal the path org.
  • The seeded /BOOTSTRAP.md scripts the wake-up conversation: Pi talks with the operator to set name/nature/vibe/emoji, updates IDENTITY.md, discusses and writes SOUL.md, then deletes /BOOTSTRAP.md when done (bootstrap.ts:60-114). This is the code behind PLAN page 2.1.1 “Create the org & initialize Pi (memory + soul)“.

4. How an operator files work into the VFS

There is no dedicated “file this” tool and no magic phrase — filing goes through the ordinary write/edit tools, and where a thing lands is governed by the active skill’s convention. Three patterns to teach:
  1. Persist-to-/memory/ (the dominant pattern). Skills tell Pi to write the canonical artifact to a deterministic /memory/… filename and treat chat as the working copy. Real phrasing from code:
    • seo-debug/SKILL.md:288: “Persist the report. The MD in /memory/ is the canonical artifact. Chat is the working copy.”
    • keyword-evaluation/SKILL.md:204: “Persist every run… must be written to /memory/. The chat output is not the canonical record.”
    • product-bible/SKILL.md:90: “Write the bible to /memory/{account}-product-bible.md. Add a reference link in MEMORY.md.”
  2. Append-to-notes (account-management). Told to record persistent knowledge, Pi reads the right /account-management/notes/*.md, appends with a date stamp, one line per fact (account-management/SKILL.md:70, 168-172, 276).
  3. Honor an explicit path (fallback). slide-deck/SKILL.md:141: “Save to wherever the user asked. If they didn’t specify, default to [the skill default].” This is the closest to a generic “file this under X” — Pi writes to a path you name, otherwise falls back to the skill’s convention.
Example operator phrasing to put on the page (grounded in the patterns above; anonymized):
save this competitor breakdown to /memory/ so we have it for next month.
Pi writes it to a /memory/… file and (per the product-bible/memory pattern) can add an index line to MEMORY.md so it’s findable later.
note in the client's preferences that they don't want first-person copy.
Pi appends a dated line to /account-management/notes/client-preferences.md. Not the same thing — the report tool. “File a report about a bug/gap” routes to the DB FeedbackItem inbox via the report tool (systemPrompt/index.ts:127-157), not the VFS. Keep this distinct on the page: save/file a document/memory/ or /account-management/notes/; report a problem with the platform → the feedback inbox.

Flags for Raymond (unconfirmed / premise corrections)

  1. ❓ [needs Raymond: the SOP "/reports/" folder — code uses "/memory/" instead. Confirm the docs should teach "/memory/" and drop "/reports/", or tell me where "/reports/" lives.]
  2. ❓ [needs Raymond: "references/…" is skill-relative in code, not an operator VFS folder — confirm we should NOT teach it as a place operators file work.]
  3. ❓ [needs Raymond: USER.md is referenced only inside the seeded BOOTSTRAP template and is NOT in CONTEXT_PATHS / not auto-loaded — is it an intentional context file or just onboarding scratch?]
  4. Core working stance is hardcoded in the prompt (not SOUL.md) — documented above as fact from systemPrompt/index.ts:46-53; flag only if the framing should change.