Validation

A story-driven game fails in quiet ways: a choice that targets a deleted node, a flag set that nothing ever reads, a quest whose completion condition can never fire, an ending no path reaches. Parlance's position is that these are build errors — findable statically, reportable precisely, and fixable before a playtester ever trips over them.

One validator, three surfaces

The same rule set runs in three places, and they are kept in agreement:

  1. On every save. Each write triggers a re-validation, and the results are pushed over a WebSocket to every open editor window. The pass is incremental: it re-checks the entities that changed and the entities that refer to them, and reuses everything else, so its result is the same one a full pass would give (a change to project-wide configuration such as rules.json still runs everything). The validation bar shows live error/warning counts with per-code filters, and appears only while there is something to report. Clicking an issue row takes you to the place the issue is about, not just the entity (hover a row to see where it will land):

    • a dialogue node or choice — the canvas selects the node and scrolls it into view, and a choice-level issue opens that choice in the inspector;
    • a quest stage, outcome or objective — the quest canvas selects the stage or outcome and scrolls the objective into view;
    • a field on any other entity — its form opens with that field outlined and focused, expanding Metadata first if it is collapsed;
    • FLAG, REP, REL — the variable, faction or character opens with its Flow panel, listing every place that checks it and every place that changes it; each row jumps to the node, which is where a read-but-never-set flag gets fixed.

    Issues are listed entity by entity, errors before warnings; only an issue from a file the editor could not read at all sits above them. You never refresh, and you never validate "later."

    The pass is also scheduled rather than run inside the save itself: a save returns as soon as the bytes are on disk, rapid saves coalesce into one validation instead of one each, and the pass runs on a worker thread off the editor's serving thread. On a small project the difference is invisible; on a large one it is the difference between a save that answers instantly and one that waits on a whole-project pass. See Performance.

  2. In CI. parlance ci-check runs the identical code path headless: exit 1 on errors, --strict to fail on warnings too. See the CI tutorial.

  3. Independently. validate.py is a standalone Python reimplementation of the same rules — usable in pipelines with no Node toolchain, and doubling as a second opinion on the rules themselves. A parity test in the main suite asserts both implementations report the same issues, so they can't drift apart silently.

What gets checked

Thirty check families, spanning shape (SCHEMA), wiring (REF, DUP), flow (FLOW, REACH, GATE), state (FLAG, REP), structure (QUEST, ENDING, COVERAGE, LOC, CUT, OFFER), content (LORE), and progression math (PROG, XP, CHECK). The reference page lists every family with what it scans and how to fix what it finds.

Errors, warnings, and why saves never block

Beyond the checklist: Reports

The Reports panel is validation's exploratory twin:

Validation as a feature of the format

Because the data is schema-first plain JSON with published semantics, deep static analysis is possible at all — you can't statically trace flag flow through a pile of engine-side script. The validator is the payoff of the format's discipline, and via the open spec, third parties can implement the same checks.

Next: wire it into CI in ten minutes · every check family, explained