Vault Conventions¶
File structure and frontmatter standards for campaign vaults.
Overview¶
The vault is an Obsidian-compatible markdown directory that stores all campaign state. It's the source of truth for the DM agent - everything the agent knows comes from vault files.
campaigns/
├── seagate/ # Campaign directory
│ ├── canon/ # Current game state (agent reads this)
│ │ ├── pcs/ # Player characters
│ │ ├── npcs/ # Non-player characters
│ │ ├── locations/ # Places
│ │ ├── items/ # Notable items and artifacts
│ │ ├── factions/ # Organizations
│ │ ├── threads/ # Individual plot threads (detailed)
│ │ ├── arcs/ # Story arcs (narrative structure)
│ │ ├── quests/ # Specific quests/missions
│ │ ├── open-threads.md # Active storylines summary
│ │ ├── timeline.md # Event history
│ │ └── temporal-index.json # Current day/time
│ ├── sessions/ # Session logs
│ └── _gm/ # GM-only secrets (not injected)
└── ironvale/ # Another campaign
Frontmatter Standards¶
All entity files use YAML frontmatter. The frontmatter is parsed for filtering, context building, and RAG retrieval.
Common Fields¶
| Field | Type | Description |
|---|---|---|
type |
string | Entity type: pc, npc, location, item, faction, index |
status |
string | active, inactive, deceased, destroyed |
triggers |
list | Keywords for RAG retrieval |
last_updated |
date | ISO date of last modification |
PC Fields¶
---
type: pc
status: active
triggers: ["Jake", "the wizard", "human mage"]
player: Jake
last_updated: 2025-12-25
# Mutable state (tools update this)
level: 3
class: Wizard
subclass: Abjuration
hp_current: 13
hp_max: 13
gold: 15
location: the-salty-sigil
conditions: [] # e.g., ["exhaustion 1", "poisoned"]
inventory:
equipped: [...]
carried: [...]
stored: [...]
spell_slots:
1st: {current: 4, max: 4}
2nd: {current: 2, max: 2}
allies:
- {name: Elara, relation: "Landlord, ally"}
- {name: Marlena, relation: "Intimate ally"}
---
NPC Fields¶
---
type: npc
status: active
triggers: ["Marlena", "component dealer", "The Salty Sigil"]
disposition: intimate-ally
location: the-salty-sigil
last_updated: 2025-12-25
---
Disposition values:
- intimate-ally - Physical affection, genuine concern, vulnerability
- ally - Trust, cooperation, professional warmth
- neutral - Transactional, cautious
- wary - Suspicious, requires proof of intent
- hostile - Active opposition, may attack/betray
Status values:
- active - Currently in play
- inactive - Exists but not currently relevant
- rescued - Recently saved/recovered
- deceased - Dead
- unknown - Status uncertain
Location Fields¶
---
type: location
status: active
triggers: ["Gilded Quill", "the bookshop", "Elara's shop"]
location_type: building
parent: seagate
last_updated: 2025-12-24
---
Location types:
- city - Major settlement
- district - Area within a city
- building - Structure (shop, inn, tower)
- wilderness - Outdoor area
- dungeon - Underground/enclosed dangerous area
Item Fields¶
---
type: item
status: active
triggers: ["brass token", "V-7 token", "courier token"]
rarity: uncommon
magical: true
owner: jake
location: carried
last_updated: 2025-12-26
---
Index Files¶
Thread Description Convention¶
Critical Pattern: Thread descriptions must include current state, not just the problem.
The DM agent infers missing information. If a thread description is ambiguous, the agent will guess—often incorrectly. Thread descriptions are injected into every session context, making them the primary source of truth for ongoing storylines.
Anti-pattern (causes hallucination):
→ Agent infers Jorah might be missing, hallucinates dialogue about "finding" him.Correct pattern:
Gareth & Jorah Recovery
Description: Gareth recovering at Salty Sigil. Jorah under Elara's ward at Gilded Quill. Both traumatized.
Next step: Bring Jorah to see Gareth
Rule: When calling update_thread, always include:
1. WHO - which entities are involved
2. WHERE - their current locations
3. WHAT - their current state
4. WHY - what's at stake (if not obvious)
Thread next_step = action to take. Thread description = canonical situation.
open-threads.md (Legacy)¶
Note: For DatabaseVault, threads are stored in
rpg.threadstable and injected directly. Theopen-threads.mdfile is used only by file-based vaults.
Summary of active storylines, organized by priority.
---
title: Open Threads
type: index
priority: high
triggers: ["threads", "quests", "priorities"]
---
# Open Threads
## High Priority
### The Fading Muffle
- **Situation:** Token with scrying anchor, muffled for ~36 hours
- **Stakes:** If muffle fades, caster can locate Jake
- **Next step:** Execute infiltration before deadline
## Medium Priority
### Marlena's Favors
- **Situation:** Jake owes TWO unspecified favors
- **Stakes:** Favors will be called in at dramatic moments
- **Next step:** Wait for her to call them in
## Completed/Resolved
### ~~Rescue Gareth~~ (Session 5)
- **Resolved:** Jake found Gareth in Sunfall Hills
timeline.md¶
Chronological event history.
---
title: Campaign Timeline
type: index
priority: high
triggers: ["timeline", "history", "what happened"]
---
# Timeline
## Day 1 (Session 01 - 2025-12-23)
### Morning
- Jake confronted by Elara over rent
- Pip attempts to steal coin purse; caught
### Afternoon
- Alliance seed planted
- Jake accepts mission
## Day 2 (Session 02 - 2025-12-24)
...
temporal-index.json¶
Current temporal state (updated by tools).
Content Sections¶
Entity files follow consistent section patterns.
NPCs¶
# Marlena
## Role
Proprietor of The Salty Sigil curio shop...
## Appearance
- Tall and thin, willowy movements
- Jet-black hair that looks perpetually damp
## Personality
- Speaks in a slow, measured drawl
- Views herself as a "gardener"
- Has a vulnerable side
## What Jake Knows
- Former supplier to Lorcan Mourn
- Knows about the Silent Circle
## Current Relationship
**Intimate alliance** - evolved from transactional...
## Connections
- Lorcan Mourn (former client, deceased)
- The Silent Circle (aware of them)
- **Jake** - intimate ally
Locations¶
# The Gilded Quill
## Overview
Bookshop in Seagate owned by Elara...
## Features
- Main shop floor with books and scrolls
- Back room / office
- Jake's lodging (upstairs)
## Current Occupants
- **Elara** - Proprietor
- **Jake** - Tenant
## Security
- Arcane Lock on doors
- Chest of Security for dangerous items
Tool Integration¶
Reading Entities¶
from lib.vault import Vault
from lib.models import EntityType
vault = Vault("/campaigns/seagate")
# Get specific entity
npc = vault.get_entity(EntityType.NPC, "marlena")
print(npc.frontmatter) # {'type': 'npc', 'disposition': 'intimate-ally', ...}
print(npc.content) # Markdown content after frontmatter
# List entities
npcs = vault.list_entities(EntityType.NPC)
for npc in npcs:
if npc.frontmatter.get("location") == "the-salty-sigil":
print(f"At Salty Sigil: {npc.id}")
Updating PC State¶
The update_pc tool modifies frontmatter fields:
# Tool call from DM agent
update_pc(hp_current=10, location="gilded-quill", conditions=["exhaustion 1"])
# Vault.update_pc() rewrites the frontmatter section
Adding Threads¶
vault.add_thread(
name="New Mystery",
priority="medium",
description="Something strange in the harbor..."
)
Triggers for RAG¶
The triggers field enables keyword-based retrieval:
When a player mentions "the component dealer", the RAG system can retrieve Marlena's full document even if they don't use her name.
GM Secrets¶
The _gm/ directory is never injected into context. Use it for:
- Future plot reveals
- NPC secret motivations
- Encounter plans
- Session notes
_gm/
├── secrets/
│ └── marlena-true-agenda.md
├── encounters/
│ └── salon-confrontation.md
└── notes/
└── session-6-plan.md
Related¶
- Context Injection - How vault data becomes agent context
- Architecture - Overall system design