It is reasonable to assume this is a clever prompt. Here is what it actually runs on.
Most “AI dungeon master” projects are a long instruction and a good model. That is a fair first guess about this one, and the only useful answer is the files rather than an argument about them. So: what the system is made of, what each part does, and what it still cannot do.
This is not published out of virtue. Anyone can copy a schema in an afternoon — the believe/truth pattern is a dozen lines. What is harder to copy is a session where the schema caught something, and harder still is the habit of publishing the session where it did not.
Counted 28 August 2026. These move; the files are the record, not this box.
Thirty thousand lines of structured markdown, and one Python file. That is deliberate and it is written down as a constraint: the runtime is the model, so anything that can be a document should be a document, and code is reserved for the one job a document cannot do — refusing.
So it is not a prompt, because a prompt has no schema, no validator, no separation between what is true and what is known, and nothing that can tell it no. And it is not an application, because there is no engine here doing the reasoning. It is a set of documents strict enough to be checkable, and one script that does the checking.
gate.py, 556 lines, standard library only. It validates every entry before it is written and rejects the ones that do not add up. It knows nothing about D&D — it cannot tell you whether a ruling was fair. It knows that a d20 stops at twenty and that addition is not optional.
Its roll command produces the dice and the ledger line in one operation, so an unlogged roll is an unrolled roll. That exists because the first played session lost two rolls to a narrate-then-log habit, and an instruction to remember is not a control.
state-formats.md defines two append-only files. The ledger records what happened, with inputs rather than conclusions — the raw die and each modifier, so the total is derivable rather than asserted.
The facts file records what is true and who knows it, in four values: true, known, suspected, false. A fact the party believes and is wrong about has a place to live. That is the whole fog-of-war mechanism, and it is a field.
library/ exists for one reason, stated in its own first paragraph: the model has far more 2014 D&D in training than 2024, and left to recall rules it will serve the old ones fluently. Fluent wrong does not announce itself.
So the rules are on disk as 617 individual articles — one file per spell, item, condition, feat, class — plus progression tables for all twelve classes. In the first session that caught a module-breaking interaction before a die was rolled.
architecture.md splits everything three ways: the master (a character at their canonical level), the campaign build (the same character expressed at this campaign’s level), and runtime state.
A played campaign lives in its own directory that never ships. Modules are portable; playthroughs are not. That is a filesystem fact rather than a policy, which is the only kind that holds.
npc-containers.md is the rule that people are created on contact — a town does not ship with a census, it gains one person at a time as the party meets them, each with a file and an interaction log. A fact moves from suspected to known by changing a field, not by walking a path.
And persona-format.md makes the dungeon master itself a swappable file: seven dials, a set of triggered moves, and a falsifiable prediction — a sentence a player who never read the file could check. Same contract, different persona, different game.
The module schema converts a written adventure into structure. Its central rule is that a beat records a state change that must happen — “the party learns X” — never a scene. Every beat also carries a dm_owns field naming what is explicitly free.
That is what stops a module becoming a railroad, and it is why the first session’s ending was none of the four the adventure lists while still hitting every beat the adventure requires.
campaign-start.md is the procedure that turns a module in that shape into a playable campaign folder.
The adventure played in the first session was not written for this system. It is A Wild Sheep Chase by R.M. Jansen-Parkes, published by Winghorn Press in 2016 — a real, widely-played, free third-party module for a different edition.
It was read and converted into the schema by hand: beats as required state changes, a cast with wants and tells, fronts with clocks, elements carrying their own constraints, and a fill list naming every question the source leaves to the table. Converting somebody else’s adventure is the only honest test of a format — a schema that only fits adventures written for it has proved nothing.
The conversion also found things the source does not say out loud. It has one specific way a well-meaning DM ruins it, one moment the text requires be telegraphed heavily, and an assumption in its opening scene that any druid breaks. Those became fields, because the schema has somewhere to put them.
⚠️ The converted file is not published, and will not be. The source is DMs Guild Community Content, and that agreement commits distribution to DMs Guild. Our schema is ours and is here in full. The adventure poured into it is somebody else’s. Go and get it from them — it is free, and it is good.
Each row is a claim first and a file second, so you can go straight from the thing you doubt to the thing that settles it.
| The claim | The file that settles it |
|---|---|
| “It lost two rolls and says so.” | ledger.jsonl — entries e017 and e018, appended out of order with notes recording their own lateness |
| “No recorded result contradicts its own inputs.” | gate.py + ledger.jsonl — run check: every total rebuilds from its own die and modifiers, and every verdict matches its total against its own DC |
| “…so nothing was softened.” | That does not follow, and no file on this page closes the gap. The dungeon master sets the DC. A total of 7 against a DC quietly set at 5 is honest arithmetic and a softened scene, and the ledger cannot tell the difference. What check rules out is a result that contradicts its own numbers — never a number chosen to be kind |
| “The player was wrong about something and it was recorded.” | facts.jsonl — the two believe entries, both truth: false |
| “The DM is bound by something written down.” | DM.md — 276 lines, every clause marked earned or guessed |
| “It looks rules up instead of recalling them.” | library/ and core.md — and one of the 617 articles to show the shape |
| “Truth and knowledge are different things here.” | state-formats.md §5 — four visibility values, one of them for being wrong |
| “A played campaign is separate from the templates.” | architecture.md §2b and LIVE/ |
| “People are created when you meet them, not in advance.” | npc-containers.md |
| “The dungeon master is a swappable file.” | persona-format.md — seven dials, triggered moves, and a prediction a player can check |
| “An adventure is data, not a script.” | the module schema — beats record a state change, never a scene |
| “There is a procedure that builds a campaign.” | campaign-start.md |
| “The player’s own settings are load-bearing and conveniently unpublished.” | table-agreement-sample.md — every mechanically-cited section in full; one section’s answers cut and its shape kept |
| “The validator would catch a softened result.” | CHANGELOG.md — it would not, until 28 August. A reader found the hole by running the published commands against the published files. Dated, with what it was and was not checking |
| “A session actually happened.” | transcript.md and FINDING.md, which the DM wrote about its own run |
Download the validator and the session’s files and check the arithmetic without taking anyone’s word for it. Python 3, no dependencies.
If you are pointing an AI assistant at this site, give it /seren/llms.txt — a plain-text map with a direct URL for every page, every evidence file and every source file on this page. Several assistants can fetch a URL handed to them but will not follow links they find, and then report the rest of the site as inaccessible. Nothing here is restricted; every URL returns 200 to any user agent.
curl -O https://seren-dm.lovable.app/source/gate.py curl -O https://seren-dm.lovable.app/evidence/ledger.jsonl curl -O https://seren-dm.lovable.app/evidence/facts.jsonl python gate.py check ledger.jsonl → 18 entries, all valid. python gate.py check facts.jsonl → 54 entries, all valid. python gate.py count ledger.jsonl → entries by type, and how many involved a die
What that proves: every total rebuilds from its own die and modifiers, every verdict agrees with its own arithmetic — a stated pass against a DC it did not meet is refused — no id is reused or out of order, and no entry carries a field its type does not define.
What it does not prove: that the file is complete. That distinction is the session’s main finding, and it is written into the evidence folder beside the files rather than only here.
⚠️ And the verdict check is younger than the ledger it validates. Until 28 August the gate confirmed that totals rebuilt and never confirmed that pass agreed with them — a reader found that by running exactly these commands, which is the point of publishing them. Every existing file passes the stricter check, so the hole was real and nothing had gone through it. The changelog says what the tool was checking on which day, because a guarantee without a date is not one.
The playtest page carries the other half — a session that actually ran on these, with its ledger, its facts file and its transcript.
A pile of documents can look like more than it is, so here is the honest boundary.
| It has been played once. | One session, one player, and the player wrote the system. Most of the contract is still marked as a guess rather than something a session has proved. |
| There is no product. | No install, no interface, no account. It is a repository a capable model reads. The panel used at the table was built during the session and thrown together from what the game needed. |
| The validator is deliberately shallow. | It checks arithmetic and field names. It cannot tell you a difficulty was unfair or a ruling was wrong, and it cannot tell you an entry is missing — which is exactly how the first session lost two rolls while passing validation. |
| The guarantees have dates. | The validator did not check that a stated pass agreed with its own arithmetic until 28 August, after the session it validates. A reader found that by downloading it and trying. Every existing file passes the stricter check — the hole was real and nothing had gone through it — but the promises are versioned and the changelog says which were in force when. |
| The batch commitment is untested. | The command that writes a sequence of dice to the ledger before any of it is spent exists and works. The session on the playtest page predates it and does not use it — its eight dice were disclosed to the player but recorded inside a note rather than as a committed batch. The mechanism is real; the evidence for it is not there yet. |
| Several pieces are specified and unbuilt. | The session ceremonies are a placeholder inside the contract. The campaign generator has never generated a campaign — the one that was played was assembled by hand on purpose, so that a real folder exists before anything tries to produce one automatically. |
None of that is in the files unless you read them closely, so it is here instead. The claim is that this is a system rather than a prompt, and that the system is early. Both halves are meant.
One file is not published: the table agreement, which records what a specific player does not want in their game. It is inherited by every campaign and ships with none of them, because a folder you hand to someone else must not carry another person’s boundaries.
⚠️ A reader pointed out the problem with that. The withheld file also happens to carry the sections that back the weakest claim on this site — how hard the dungeon master argues, and whether the companions running beside you are allowed to refuse. The one unpublished file propping up the softest argument is a coincidence worth not asking anyone to swallow.
So here is the file with one section redacted. Sections two through six are reproduced in full, because they are table craft rather than personal disclosure. Section one keeps its shape — the four questions it answers — and loses its answers, which are four sentences belonging to one named person.
⭐ It is more useful that way than the original would have been, because the shape is the transferable part. Anyone can see what the file is for and write their own.
The first release is easy. The failure in it is a bookkeeping miss, caught by the system’s own close ceremony, on a session that otherwise went well. That is a comfortable thing to publish and it proves less than it appears to.
At some point there will be a run where the model actually softens a result, or a combat session where the ledger falls apart under per-round state, or a persona that turns out to do nothing at all. Publishing that one is what would make this a practice rather than a launch tactic, so the rule is written down now, while it is cheap to commit to:
Every session that runs against this system gets a findings file, and every findings file gets published, whatever it says. The dungeon master writes it before anyone reads the transcript. If a run produces a result that undermines a claim made on this site, that run goes up and the claim comes down.
There is no way to prove that commitment in advance. It is here so that it is on the record before there is anything uncomfortable to test it with.
If you build systems, the interesting conversation is about the parts that are wrong — the shallow validator, the unbuilt ceremonies, the contract clauses still marked as guesses. That is the conversation I want to be having.