Files
solo-journaling/AGENTS.md
T
hypercross 3005fd7038 feat(campaigns): separate GM truth from PC knowledge
Hidden truth lives in gm/; state/knowledge.md holds what the PC knows,
believes, and suspects. Everything else in a campaign stays player-facing.
2026-09-13 16:50:18 +08:00

248 lines
11 KiB
Markdown

# 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
1. **Never invent dice results.** All randomness comes from the `journal` CLI.
Run it and use its output verbatim. If you did not run the CLI, you did not
roll.
2. **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.
3. **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.
4. **The player only talks.** Record their intent verbatim in the session log.
Do not paraphrase inside the `Player` block.
5. **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.
6. **Markdown is the source of truth.** Current state lives in YAML
frontmatter; narrative lives in prose. Keep them consistent.
7. **The game package is authoritative.** Its rules override generic
assumptions. A campaign's `house-rules.md` overrides the game, for that
campaign only.
8. **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
1. **Identify the campaign.** The campaign's `campaign.md` frontmatter names its
game. Use `--campaign <name>` on every CLI call. If it is unclear, ask.
2. **Load context.** Read `campaign.md`, the game's `game.md`,
`house-rules.md`, the PC sheet, `state/` (including `knowledge.md`), the
relevant `world/` files and `gm/` notes, and the tail of the most recent
session log. Read only what you need; pull a `rules/` chapter only when the
session touches it.
3. **Read the player's intent.** Treat it as what the PC attempts, not what
happens.
4. **Decide whether to roll.** See "When to roll" below.
5. **Roll.** Call the CLI. Use the result exactly as printed.
6. **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".
7. **Narrate what the PC perceives.** Filter the outcome through the PC's
senses, position, and what `knowledge.md` says they know. Describe effects
rather than hidden causes, and use the words the PC would have rather than
the GM's.
8. **Append to the session log.** Add a new scene using the format below.
9. **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 in `gm/`.
## 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-themes` on `and`/`but` results, or
whenever the fiction needs a twist. Pass `--complicate` to `journal oracle`,
or `journal 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.
```markdown
# 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 `Player` block quotes the player's words verbatim.
- The roll line records exactly what the CLI printed, in backticks.
- `Resolution` is 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 in `gm/`.
- `State` lists 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.
```markdown
---
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
1. If the game does not exist yet, build it under `games/<id>/` following
`docs/games.md` (copy `games/generic/` as a starting point).
2. `journal new-campaign <name> --game <id>`
3. Fill in `campaign.md` with the player.
4. Create the PC sheet in `characters/`, drawing on the game's backgrounds.
5. Open the first session log in `sessions/`.