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.
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.
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:
| File | Committed? | Attached to | Shape |
|---|---|---|---|
fronts.md | Yes | a 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.md | Yes | one 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.md | No | nothing | 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.
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.
This is the part that transfers. Each schema states, in its own text, the failure it exists to prevent:
| Schema | The named failure |
|---|---|
| Role table | 20–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.” |
| Antagonist | Six is “a ceiling, not a starting number. Twenty pre-built nemeses is twenty strangers.” |
| Character sheet | Where 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. |
| Facts | “false 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 roles | Every 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.
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.
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.
| Subtype | Count | Fields beyond article / name / slug / source |
|---|---|---|
spell | 301 | level · school · classes[] · casting_time · range · components · duration · concentration · ritual · summons_stat_block |
magic-item | 229 | category · subtype · rarity · attunement · attunement_note |
rule | 41 | conditions and procedures — the high-traffic lookups |
feat | 17 | category, prerequisites |
class | 12 | progression tables, levels 1–20 |
subclass | 12 | parent class, features by level |
background | 4 | ability 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 purpose — shape · 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.
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.
| Schema | Shape, and why it is shaped that way | Files |
|---|---|---|
| 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 · fallbackgrudge_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 · toneshareable 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 · constraintsfunction 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_playverified 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 · canonicalThe 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 |
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.
| File | Shape | Spec |
|---|---|---|
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 |
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.