Schemas.

Fourteen template types, in three layers. Everything the system fills in has a declared shape, and the shape is a file you can read.

This is the part that is portable. None of it is about dungeons. A schema here is a markdown file with YAML front matter and prose underneath — structured enough to validate and query, readable enough that a model with no prior knowledge of the project can act on it without a lookup table.

Counted 28 August 2026, and in flux. Two of these have known gaps, named below rather than tidied away.

What is actually different here

Everyone modelled the mechanical layer. This one models the other half.

Virtual tabletops have had rich schemas for years — hit points, armour class, tokens, initiative order. Nobody built a schema for what a person wants, who knows what, what closure looks like, or what this NPC does instead of fighting, because a human dungeon master held all of that in their head and no file was needed.

The mechanical half is a solved problem you could copy out of an SRD in an afternoon. The wants / knows / visibility / exit half is the part that is not copyable, and it is most of what is on this page.

Commitment × attachment

Three files hold everything the world is doing that the party has not seen. They are separated by whether the thing is committed to and what it is attached to:

FileCommitted?Attached toShape
fronts.mdYesa faction “Agendas, never plot.” wants · doing · clock · if full. The clock advances when the party is elsewhere, and if full must describe what changes in the world — never “the party loses”. Two to four at t=0. “A front with no clock is a rumour.”
secrets.mdYesone person What this character is hiding, and what they want that they have not said. DM-side only — this is the file The Table leaked from once in review and now bans globally.
ideas.mdNonothing idea · seeded · triggers · status: untriggered. “Nothing here is true yet.” A hundred of these cost nothing and none of them constrain the world. The moment one is used it moves to canon with provenance and is deleted from this file in the same breath — an idea left in the pool gets introduced twice.

The third row is the one that does not exist elsewhere. Improv banks and prep lists are old; giving the bank a lifecycle — uncommitted, promoted on use, deleted on promotion — is what stops it becoming a second canon that quietly disagrees with the first.

The same primitive, arrived at three times

grudge_seed is a fact about an antagonist. grudge is that fact made specific to this party, filled in at campaign creation, and the schema states outright that the seed is not a substitute for it. That is the template-and-instance split running inside a single file — the same shape as variant_of on a character sheet, and the same shape as a module versus a played campaign. Three layers of the system independently landed on the same primitive.

Every schema carries its own anti-pattern

This is the part that transfers. Each schema states, in its own text, the failure it exists to prevent:

SchemaThe named failure
Role table20–30 lines and it stays that way. “A role that grows past that is being written as a character, which is the mistake this directory exists to prevent.”
AntagonistSix is “a ceiling, not a starting number. Twenty pre-built nemeses is twenty strangers.”
Character sheetWhere a value is derived rather than copied, say so — “the derivation is auditable, an assertion isn’t.” Write TODO rather than a guess, because an invented modifier becomes a wrong roll in play, silently, forever.
Factsfalse is the one people delete. Don’t.” A party that is confidently wrong is the most playable state in the game, and it is unrecoverable if nobody wrote it down.
Hostile rolesEvery one carries a way out that is not a fight, and plausible_combat says whether this person would even swing. Both exist to stop the model reaching for the resolution it is most fluent at.

These are not shapes. They are arguments about where a model will fail — which is precisely why they work on a model, since it can apply them to cases the schema never anticipated.

The honest part

This is the largest block of unvalidated design in the project, and it is the block that looks most finished.

Almost none of what is above has been exercised. One session, a third-party adventure, other people’s NPCs. The antagonist schema runs to 194 lines and the six nemeses built against it belong to a world that has never been played. There are 42 role templates and the played session plausibly touched two or three.

Nothing tests whether a role template changes the output. The persona format carries a falsifiable prediction — a sentence a player who never read the file could check. Role templates have no equivalent, so “a role biases what the model invents, so the fifteenth villager does not sound like the first” is currently an assertion. The test is cheap and obvious: generate five villagers with the template and five without, and see whether a reader can tell which is which. It either validates 42 files or says the number should have been six.

Until that runs, treat this page as a design that is internally consistent and externally unproven. Which is roughly the same sentence as the one on the overview about what the ledger does and does not settle.

Layer 1 — Rules reference

Rules live on disk because the model’s memory of them is stale and fluent.

A model carries far more of the previous edition in its training than the current one, and it will serve you the wrong rule with total confidence. So the rules are files, and the DM reads them.

article is the type name. element, entry and record were already taken by other parts of the system, which is a small thing that says something about how much of this is naming.

SubtypeCountFields beyond article / name / slug / source
spell301level · school · classes[] · casting_time · range · components · duration · concentration · ritual · summons_stat_block
magic-item229category · subtype · rarity · attunement · attunement_note
rule41conditions and procedures — the high-traffic lookups
feat17category, prerequisites
class12progression tables, levels 1–20
subclass12parent class, features by level
background4ability scores, feat, proficiencies

617 files, one article each. Every one carries a CC-BY attribution comment above its front matter. Schema: article.md · samples: a spell, a rule.

Homebrew uses a different shape on purposeshape · form · attunement · limit for items, and feat · shape · status · trigger · limit for feats. Note trigger and limit: a homebrew feat declares when it fires and what bounds it, rather than describing itself in prose and hoping. Schema: library.md · sample: an item.

Layer 2 — Templates

The things that get filled in.

A template is a master. A campaign build is a copy of it scaled to a level. A running game is a third thing again. Keeping those three separate is most of the architecture — see architecture.md §2b.

SchemaShape, and why it is shaped that wayFiles
Character sheet name · pronouns · species · class · subclass · level · ruleset · variant_of · variants[] · proficiency_bonus · abilities · saves_proficient · ac · initiative · speed
variant_of is the load-bearing field. A level-5 version of a character is not a separate character; it is a derived variant, and the master says so.
schema
16 instances, unpublished
Adventurer
imported
The character-sheet shape plus provenance: converted_from · dndbeyond_source_id. An imported sheet records where it came from, because an import that loses its origin cannot be re-imported or checked against the source. schema
6 instances, unpublished
Antagonist tier · axis · role · grudge · grudge_seed · exit · escalation · attaches_to · fallback
grudge_seed and exit are the interesting pair. An antagonist carries the reason it is coming for you and a named way it can stop — villains that cannot exit are why campaigns stall.
schema · sample
9 instances
Role table role · stat_block · plausible_combat · domain (+ commands for hostiles)
Filed as town, world and hostile. plausible_combat answers “would this person actually fight you” so a baker does not roll initiative because a table needed a body.
schema · sample
42 roles
Module
an adventure
ruleset · derivation · derived_from · licence · shareable · native_level · party_size · sessions · tone
shareable is a machine-checkable field, not a note. It exists because a derived module was published to a public repository once by mistake.
schema
1 module, unpublished
Module element name · type · module · summary · description · function · rules · constraints
function separates what a thing is from what it is for, so the DM can substitute it when the party does something unplanned.
schema
derived — never ships
Campaign candidate source · url · system · levels · length · licence · redistributable · verified · usable_for_play
verified is separate from licence — one records what the source claims, the other records whether anyone checked.
schema
6 candidates
Persona
the DM itself
Identity · seven dials · triggered moves · a falsifiable prediction · who it is wrong for.
The boundary test for what belongs: if a great DM could sit at either end and still be great, it is a dial; otherwise it is a founding trait. The prediction is a sentence about the DM a player who never read the file could check.
schema
⚠️ no instance on disk
DM module
a behaviour
module · version · title · summary · trigger · auto · updated · canonical
The move carries its own trigger. This solves “anything that happens at a moment needs a named dispatcher” by putting the condition on the behaviour instead of in a router.
1 module
Layer 3 — Runtime state

What a running game writes down.

Two of these are JSON Lines rather than markdown, because they are appended to during play and validated by a script. Everything in this layer is per-campaign and none of it ships.

FileShapeSpec
ledger.jsonl Records inputs, not conclusions. An entry carries roll and mods so total is derivable — and since 28 August, a verdict must match its own arithmetic. Append-only; late entries are written out of order with a note rather than reordered. §2
facts.jsonl Three operations — flip, establish, believe — over four visibility values. true and false are DM-side; the player vocabulary is known and suspected only, so a false belief sits among true ones looking identical. §5
party.md Who is here, where, current status and outcome, initiative, foes, beats. §4
fronts.md What factions want and are doing, each with a clock that advances while the party is elsewhere. Agendas, never plot. §6
ideas.md Uncommitted possibilities. Not canon, not true yet, deleted on promotion so nothing gets introduced twice. campaign-start
secrets.md Per-character, DM-side. Never rendered to any panel, by the global rule on The Table. npc-containers
Canon containers Characters, locations, antagonists. Link-only and empty at t=0 by design — people accrete on contact rather than being populated in advance. schema · detail
Two known gaps

Both were found while writing this page.

There is no persona instance on disk. persona-format.md is the format, and it is real. But the only actual persona in the tree is an archived v1, and the site describes the dungeon master as “a swappable file” on the strength of a format rather than a file. Filed as SRN-54. Until it lands, that claim is a statement about the schema, not a demonstration.

scene.md is specified and does not exist where it is needed. The Table’s Now panel reads it. It exists in the documented example and in all three probe runs — and in neither live campaign directory, which have only party.md, ledger.jsonl and facts.jsonl. Filed as SRN-53. Either the play-test path skipped it or the spec should read party.md; that gets decided before the renderer is built against it.

Both are on this page for the same reason the lost dice rolls are on The Playtest. An inventory that only lists the parts that line up is a brochure.