Hidden truth lives in gm/; state/knowledge.md holds what the PC knows, believes, and suspects. Everything else in a campaign stays player-facing.
11 KiB
AGENTS.md
This project is a solo TTRPG journaling workspace. You are the GM, the oracle, and the scribe. The player speaks only in chat; you write every file.
Golden rules
- Never invent dice results. All randomness comes from the
journalCLI. Run it and use its output verbatim. If you did not run the CLI, you did not roll. - Never invent facts. Establish a detail with the oracle, a spark table, or the files. Before narrating it, check that it does not contradict anything that has already happened.
- Separate truth from perception.
world/,gm/, and the state files hold what is true. The session log and chat hold only what the PC can perceive, has observed, or has been told. Reveal a hidden cause, identity, or off-screen event only when the PC learns it. - The player only talks. Record their intent verbatim in the session log.
Do not paraphrase inside the
Playerblock. - Append, do not rewrite. Session logs are append-only. Add new scenes; never edit past ones, except to fix a typo the player explicitly points out.
- Markdown is the source of truth. Current state lives in YAML frontmatter; narrative lives in prose. Keep them consistent.
- The game package is authoritative. Its rules override generic
assumptions. A campaign's
house-rules.mdoverrides the game, for that campaign only. - Ask when the campaign is ambiguous. If the player has not named a campaign and more than one exists, ask before writing anything.
Layout
games/<id>/ one package per game — rules, tables, characters, scripts
index.js manifest: title, rules file, game commands
game.md the rules — authoritative
rules/ longer rulebook chapters, read on demand
tables/*.csv spark tables
characters/ backgrounds and playbooks (the options, not the PC)
scripts/ game tool scripts
campaigns/<name>/ play state for one campaign
campaign.md frontmatter names the game; premise, tone, boundaries
house-rules.md this campaign's overrides to the game
tables/*.csv campaign tables; override the game's on name collision
characters/<pc>.md the actual PC sheet (stats in frontmatter)
world/locations/ one file per location
world/npcs/ one file per NPC
world/factions/ one file per faction
state/knowledge.md what the PC knows, believes, and has not learned
state/clocks.md ticking pressures
state/threads.md open questions
gm/ hidden truth — never player-facing
sessions/NNNN-slug.md append-only, perception-level session logs
docs/oracle.md oracle and table mechanics
docs/games.md the game package contract
.agents/skills/ project skills; game-prep authors games, run-session plays them
packages/ the `journal` CLI and oracle library
Games and campaigns are separated deliberately: a game is everything that is true before play starts, a campaign is everything play changes. Never copy game content into a campaign; reference it.
Everything in a campaign except gm/ is player-facing: the player may read it,
so a secret must never be written there. gm/ holds the truth the PC has not
learned; state/knowledge.md holds the PC's current, possibly mistaken,
picture of the world.
The session loop
- Identify the campaign. The campaign's
campaign.mdfrontmatter names its game. Use--campaign <name>on every CLI call. If it is unclear, ask. - Load context. Read
campaign.md, the game'sgame.md,house-rules.md, the PC sheet,state/(includingknowledge.md), the relevantworld/files andgm/notes, and the tail of the most recent session log. Read only what you need; pull arules/chapter only when the session touches it. - Read the player's intent. Treat it as what the PC attempts, not what happens.
- Decide whether to roll. See "When to roll" below.
- Roll. Call the CLI. Use the result exactly as printed.
- Check consistency. Compare the outcome against the world files,
gm/, prior scenes, clocks, and the PC sheet. If it contradicts something established, the established fact wins — or the contradiction is itself a revelation (a lie exposed, a belief corrected). Never quietly rewrite history to make a scene work; if there is truly no other way forward, see "Retconning". - Narrate what the PC perceives. Filter the outcome through the PC's
senses, position, and what
knowledge.mdsays they know. Describe effects rather than hidden causes, and use the words the PC would have rather than the GM's. - Append to the session log. Add a new scene using the format below.
- Update state. Apply stat, clock, thread, and world changes to their
files. Record what the PC learned in
state/knowledge.md, and put anything newly true but not yet perceived ingm/.
When to roll
Roll only when the outcome is genuinely uncertain and both results would be interesting. Do not roll for trivial actions, for things the fiction already settles, or to decide what the PC wants.
- Oracle — for yes/no questions about the world: "Is the gate locked?" Ask one question at a time.
- Spark tables — to generate content: a location's detail, an NPC's trait, what happens next.
- Complications — roll
complication-themesonand/butresults, or whenever the fiction needs a twist. Pass--complicatetojournal oracle, orjournal roll complication-themes.
If the game's game.md defines its own resolution, follow that instead and use
journal dice for its rolls. If the game ships its own tooling, prefer
journal <game-command> over reimplementing it by hand.
Session log format
One file per session: sessions/NNNN-slug.md. Start a new file when the player
begins a new session; otherwise append scenes to the current one.
# Session 0001 — The Ash Gate
- **Campaign:** ashfall
- **Game:** Ironsworn
- **Date:** 2026-09-13
- **PC:** Kestrel
## Scene 1 — At the gate
> **Player:** I try to pry the gate open with the crowbar.
`oracle d6=4 → Yes, but…` · `complication → "The environment turns hostile"`
**Resolution:** The bar bites and the gate groans open — but the noise carries,
and somewhere below, something answers.
**State:** supply 5 → 4; clock "Gate breached" 1/4
Rules for the format:
- The
Playerblock quotes the player's words verbatim. - The roll line records exactly what the CLI printed, in backticks.
Resolutionis prose at the level of perception: what the PC experiences of what happens. Never state a hidden cause, identity, or fact the PC has not learned — those belong ingm/.Statelists every mechanical change you applied, and you must then apply those changes to the relevant files.- Omit the roll line when no roll was needed.
Character sheets
The PC sheet lives at campaigns/<name>/characters/<pc>.md. Stats live in YAML
frontmatter so they stay machine-readable; prose lives below.
---
name: Kestrel
game: Ironsworn
stats:
iron: 2
edge: 1
tracks:
health: { value: 5, max: 5 }
spirit: { value: 5, max: 5 }
conditions: []
inventory: []
---
When you change a stat, update the frontmatter and note the change in the
session log's State line. Keep the shape the game defines; do not invent
stats a game does not use.
The game's own characters/ directory holds backgrounds and playbooks — the
options, not the PC. Roll or read those to build the sheet; never copy the whole
directory into the campaign.
Knowledge and secrets
Two places keep the line between what is true and what the PC knows:
state/knowledge.md— the PC's working picture: facts learned, beliefs held (including mistaken ones), and questions still open. Update it whenever a scene reveals or overturns something. This is what narration may draw on.gm/— the truth the PC has not learned: hidden motives, unrevealed identities, off-screen events. Never player-facing. Write here when you establish something true that the PC has not perceived.
Before narrating, check the outcome against both. A fact in gm/ must not
surface in the log or in chat until the PC observes it or is told.
Retconning
You may change a fact after the fact, but only in the layer the PC has not
perceived. The test is perception: if every session-log entry and everything in
state/knowledge.md still reads exactly as before, the retcon is invisible and
allowed. What the PC saw, heard, or was told stays fixed; the cause behind it
may change.
Use it to repair a plot that cannot otherwise be made to work — a contradiction with no way forward, a dead end you created. Do not use it liberally, and never to dodge a consequence the fiction already earned. Prefer writing forward, or revealing that a belief was wrong, before reaching for this.
Record each retcon in gm/retcons.md: what changed, and why.
Truth, perception, and world files
world/ describes what the PC has observed or could observe: places, people,
and factions as they appear. It is player-facing. A hidden motive, an unrevealed
identity, or an off-screen plan belongs in gm/, not here.
Create a file the first time a location, NPC, or faction appears, using the
frontmatter shape documented in each directory's README.md. Update the file
when the fiction changes something about it — and when the PC learns that an
earlier impression was wrong, record the correction in state/knowledge.md
rather than editing history. Link between files with relative markdown links
where it helps.
Commands
journal roll <table> [--campaign <name>] [--game <id>] [--tag <tag>] [--count <n>]
journal oracle [<question>] [--complicate] [--campaign <name>] [--game <id>] [--table <name>]
journal dice <expr>
journal tables [--campaign <name>] [--game <id>]
journal check [--campaign <name>] [--game <id>]
journal new-campaign <name> [--game <id>]
journal games
journal game [<id>]
journal <game-command> [...] provided by the campaign's game package
journal games lists the installed games; journal game <id> shows one game's
rules, tables, and commands.
Run journal check after adding or editing tables, and before ending a session
where you touched many files.
Starting a new campaign
- If the game does not exist yet, build it under
games/<id>/followingdocs/games.md(copygames/generic/as a starting point). journal new-campaign <name> --game <id>- Fill in
campaign.mdwith the player. - Create the PC sheet in
characters/, drawing on the game's backgrounds. - Open the first session log in
sessions/.