Reference Manual contract Feb 2026 v0.1.14

Urd v0.1.14 Reference Manual

Consolidated reference for the Urd declarative schema system. Covers Schema Markdown syntax, the compiled JSON format, six-phase compiler pipeline, all 74 diagnostic codes, FactSet analysis IR, ANALYZE diagnostics, semantic diff engine, and DefinitionIndex.

referenceschemacompilersyntaxjsonfactsetanalyzediff

Key points

  • Complete Schema Markdown syntax guide with structural symbol vocabulary
  • Six-phase compiler pipeline: PARSE → IMPORT → LINK → ANALYZE → VALIDATE → EMIT
  • Full compiled JSON format specification with all eight top-level blocks
  • All 74 diagnostic codes catalogued by compiler phase
  • FactSet analysis IR: six fact types, PropertyDependencyIndex, ANALYZE diagnostics
  • Semantic diff engine with CLI subcommands and snapshot format
  • DefinitionIndex for IDE/LSP integration
  • Two worked examples: Monty Hall and Two Room Key Puzzle

Urd v0.1.14 Reference Manual

Consolidated reference for the Urd declarative schema system

Schema Markdown → Compiler → .urd.json + FactSet IR

February 2026 · Compiler v0.1.14 · 634 tests · Semantic gate closed

Pre-Alpha. Urd is in active early development. The schema language, JSON format, compiler behaviour, and FactSet IR described here are all subject to change. Expect breaking changes. Keep your source worlds under version control. Treat compiled output as disposable.


Table of Contents

  1. Introduction
  2. How Urd Differs
  3. Getting Started
  4. Schema Markdown Syntax
  5. Worked Examples
  6. The Compiled JSON Format
  7. The Compiler
  8. Errors and Warnings
  9. FactSet Analysis IR
  10. Advanced Topics
  11. Quick Reference
  12. Changelog Summary

1. Introduction

Urd is an open, structured world definition format — a declarative schema that describes interactive worlds as typed, validated, engine-agnostic data. Writers author in Schema Markdown (.urd.md), a prose-friendly syntax with a small set of structural symbols. The compiler validates and produces two outputs: a .urd.json contract file that runtimes consume, and a FactSet — a flat, queryable graph of every relationship in the world.

The pipeline:

.urd.md files  →  Compiler  →  .urd.json + FactSet IR  →  Runtime / Tooling / Engine Integrations

There is no intermediate format. Writers author in .urd.md. The compiler produces .urd.json and a FactSet. Runtimes, testing tools, and engine integrations consume the JSON. Tooling queries the FactSet.

Who This Document Is For

This reference covers three audiences:

  • Writers — start at Getting Started and Schema Markdown Syntax. You write prose. A handful of symbols handle structure.
  • Developers — start at The Compiled JSON Format. This is what your runtime consumes. Self-contained, deterministic, versioned, human-inspectable.
  • Architects and tool builders — start at The Compiler and FactSet Analysis IR. The compiler’s six-phase pipeline, diagnostic system, analysis IR, semantic diff engine, and definition index are documented here.

Version Note

This document describes compiler v0.1.14 (February 2026), a pre-alpha milestone. Two gates have closed:

  1. Compiler gate (v0.1.7): 9 compiler requirements (C1–C9), 8 static analysis checks (S1–S8), 8 FactSet verification criteria (F1–F8), 16 test fixtures (7 canonical, 9 negative), JSON Schema validation on all compiled output.
  2. Semantic gate (v0.1.13): 5 novel ANALYZE diagnostics, PropertyDependencyIndex with set-difference queries, semantic diff engine with CLI subcommands, DefinitionIndex for IDE/LSP integration.

634 tests, 100% pass rate. The compiler is functionally complete for v1 scope — it does not mean the schema language or JSON contract are frozen. Syntax, semantics, and output structure may all evolve before a stable release.

How to Read This Manual

  • Required means the compiler rejects it or the runtime cannot function correctly without it.
  • Recommended means it usually avoids bugs or confusion.
  • Optional means you can ignore it safely.

Everything else is explanatory.

First Ten Minutes

Writing a world?

  1. Copy the hello world example and compile it.
  2. Read Structural Symbols — seven symbols cover everything.
  3. Try adding a choice with a condition. See Conditions and Effects.

Building a runtime?

  1. Skim Top-Level Structure — eight JSON blocks, only world is required.
  2. Implement the containment model — it replaces inventory, spatial queries, and object transfer.
  3. Implement the dialogue evaluation loop — sections, choices, exhaustion, jumps.

Building tooling?

  1. Jump to FactSet Analysis IR — six fact types, one secondary index, five ANALYZE diagnostics.
  2. Read the diagnostic shape — every diagnostic includes code, severity, message, and span.
  3. See DefinitionIndex for IDE features (go-to-definition, hover, autocomplete).
  4. See Diff and Snapshots for CI-friendly structural comparison.

2. How Urd Differs

Most interactive narrative tooling — Ink, Yarn Spinner, articy:draft (Flow Player) — treats story as a program to execute: a cursor moves through text, hits choices, and advances. Urd is fundamentally different. The schema describes what the world is, not what it does. The runtime evaluates a system, not a script.

Ink is used here for comparison because a surface-level glance at .urd.md files — markdown, choices, conditions — might suggest the two are similar. They are not. The resemblance is cosmetic. The underlying models, runtime shapes, and design goals are fundamentally different.

InkUrd / Wyrd
Mental modelStory = cursor moving through textWorld = structured state graph + rules + queries
Runtime shapeText stream with pauses (Continue(), ChooseChoiceIndex())State container + rule engine (Query(), Apply(), Step())
Content modelText-first; logic embedded in narrative; loose globalsSchema-first; typed entities, properties, and constraints
ExecutionContinue → text → choice → Continue → …Query → decide → Apply → Step → recompute
ValidationCompiles to JSON; errors can surface at runtimeSix-phase compiler with JSON Schema enforcement and static analysis
Best atBranching dialogue, writer-friendly linear narrativeSystems-driven worlds, simulation, correctness guarantees, multi-agent interaction

The compiler guarantees that every reference resolves, every type checks, and every constraint is satisfied before the runtime sees the output. What it does not guarantee is runtime correctness of game logic — that remains the author’s responsibility.

Ink is an authoring tool. Wyrd is an execution model for worlds. They occupy different spaces. If a higher-level narrative layer is built on top of Wyrd, that would compete with Ink — but the core Urd/Wyrd system is closer to a simulation engine than a dialogue tool.


3. Getting Started

Writing Your First World

Create a file called hello.urd.md:

---
world:
  name: hello-world
  start: room

types:
  NPC [interactable]:
    name: string
    mood: enum(friendly, grumpy) = friendly

entities:
  @greeter: NPC { name: "Ada" }
---

# Room

A quiet room with a single occupant.

[@greeter]

== conversation

@greeter: Hello, traveller. What brings you here?

+ Ask about the room
  @greeter: It's not much, but it's honest.

+ Ask about her name
  @greeter: Ada. And you are...?
  > @greeter.mood = friendly

* Leave
  -> end

Open in Playground

This file defines a world with one location, one NPC, and a short conversation. The frontmatter declares the types and entities. The body is narrative.

Compiling

Native CLI:

urd hello.urd.md > hello.urd.json      # Compile and emit JSON to stdout
urd --help                              # Show usage and all commands
urd --version                           # Print compiler version
urd diff a.urd.md b.urd.md             # Compare two compiled worlds
urd snapshot hello.urd.md              # Create a .urd.snapshot.json

Diagnostics are printed to stderr. The compiled JSON is written to stdout. Exit code 0 on success (or no changes for diff), 1 on errors (or changes detected by diff).

WASM (browser):

The compiler runs client-side via three WASM entry points:

FunctionPurpose
compile_source(source)Full six-phase pipeline
parse_only(source)Phase 1 only (live syntax checking)
compiler_version()Crate version string

Response shapes:

// compile_source(source)
{
  "success": true,
  "world": { ... },         // The compiled .urd.json contract (omitted on failure)
  "diagnostics": [           // Array of { code, severity, message, span }
    { "code": "URD301", "severity": "error", "message": "...", "span": { ... } }
  ],
  "facts": {                  // FactSet IR (see Section 9)
    "reads": [...],
    "writes": [...],
    "exits": [...],
    "jumps": [...],
    "choices": [...],
    "rules": [...]
  },
  "property_index": {         // PropertyDependencyIndex (see Section 9.3)
    "properties": { ... },
    "summary": { ... }
  },
  "definition_index": {       // DefinitionIndex (see Section 7.8)
    "type:TypeName": { ... },
    "entity:@id": { ... },
    ...
  }
}

// parse_only(source)
{
  "success": true,
  "diagnostics": []
}

// compiler_version()
"0.1.14"

Try it live at urd.dev/playground.

What the Compiler Produces

Two outputs:

  1. .urd.json — the compiled world. Self-contained, deterministic, versioned. This is the contract between authoring and execution. See The Compiled JSON Format.
  2. FactSet IR — a flat, queryable graph of every relationship in the world. Used by tooling, not runtimes. See FactSet Analysis IR.

4. Schema Markdown Syntax

4.1 Structural Symbols

SymbolNameWhat It DoesExample
@Entity referenceReferences characters, objects, locations@guard, @door_1
?ConditionGates content on world state? @guard.mood == neutral
>EffectChanges the world> @guard.mood = neutral
*One-shot choiceDisappears after selection* Ask about the ship
+Sticky choiceStays available on revisit+ Ask about the harbour
->JumpNavigates to a section or location-> topics, -> harbour
==Section headerDeclares a dialogue section== topics

Two additional markers appear in source but are secondary:

SymbolWhat It DoesExample
!Blocked message (shown when an exit or action is gated)! The door is locked.
//Comment (ignored by the compiler)// hub prompt

Plain text outside any marker is narrative prose.

4.2 Frontmatter

The block between --- delimiters is Urd frontmatter — YAML-like but not YAML. Parsed by the Urd compiler under strict constraints.

What is allowed:

  • Key-value pairs: key: value
  • Nested blocks via two-space indentation
  • Inline objects: @arina: Barkeep { name: "Arina" }
  • Flow-style lists: [goat, car], [@door_1, @door_2]
  • Comments: # text
  • Quoted strings when ambiguous: "1", "yes", "true"

What is not allowed:

  • Anchors (&name), aliases (*name), merge keys (<<:)
  • Custom tags (!!type)
  • Block-style lists (- item)
  • Implicit type coercion

The world Block

world:
  name: monty-hall
  start: stage
  entry: game
FieldRequiredDescription
nameYesUnique identifier. Lowercase, hyphens allowed.
startNoStarting location.
entryNoStarting sequence.
versionNoAuthor-defined version string.
descriptionNoHuman-readable description.
authorNoAuthor or team name.
seedNoRandom seed for deterministic replay.

The urd version field is set automatically by the compiler to "1". If an author specifies it manually, the compiler warns and overrides.

Imports

---
import: ./world.urd.md
---

Imports are explicit and non-transitive. If file A imports B, and B imports C, A does not see C’s types unless it imports C directly. Circular imports are a compile error.

4.3 Locations and Headings

HeadingCompiles To
# Location NameA location in the locations block
## Sequence NameA sequence in the sequences block
### Phase NameA phase within the enclosing sequence
### Phase Name (auto)An auto-advancing phase (auto: true)

Entity placement uses square brackets after a location heading:

# The Rusty Anchor
A low-ceilinged tavern thick with pipe smoke.
[@arina, @barrel]

Exits connect locations. The name before the colon is the exit name (a direction, label, or identifier). The name after the colon is the destination (a location heading):

-> north: Corridor
  ? @cell_door.locked == false
  ! The iron door is locked.

Here, north is the exit name and Corridor is the destination location. Write the heading title as it appears in the # line — the compiler slugifies it internally (e.g., # The Rusty Anchor becomes the-rusty-anchor). Exits are unidirectional — the destination does not automatically have an exit back.

4.4 Types, Entities, and Properties

Type Definitions

types:
  Guard [interactable, mobile, container]:
    name: string
    mood: enum(hostile, neutral, helpful) = hostile
    ~hint_given: bool = false

The ~ prefix marks a property as visibility: hidden. Traits ([interactable, mobile, container]) inform the runtime about spatial capabilities.

Property Types

TypeValuesExample
bool / booleantrue / falselocked: bool = true
int / integerWhole numbers, optional rangetrust: int(0, 100) = 30
num / numberDecimal numbers, optional rangeweight: num(0.0, 10.0)
str / stringTextname: string
enumOne of a declared setmood: enum(hostile, neutral)
refReference to another entityrequires: ref(Key)
listOrdered listtags: list = []

Short forms (int, num, str, bool) are normalised to canonical names during compilation.

Entity Traits

TraitMeaning
containerCan hold other entities. Locations have this implicitly.
portableCan be moved into another container.
mobileCan move itself between containers.
interactableCan be the target of player actions. Default: true.

Visibility Model

LevelWho Sees ItSource TokenJSON Support
visibleEveryone (default)(none)Yes
hiddenWorld only. Must be explicitly revealed.~Yes
ownerEntity itself and the world.~~ (reserved — not yet supported in source)Yes
conditionalVisible when a condition is met.(no source syntax)Yes

The owner and conditional levels are part of the v1 JSON format and runtimes are expected to support them. Source syntax for owner (~~) is reserved but not yet implemented in the compiler. conditional visibility is set programmatically in JSON; there is no source shorthand.

Entity Declarations

entities:
  @rusty_key: Key { name: "Rusty Key" }
  @cell_door: LockedDoor { requires: @rusty_key }
  @guard: Guard { name: "Halvard" }

Entity IDs must be globally unique. The @ prefix is used in source; compiled JSON strips it.

Quoting Rules in Frontmatter

Enum values and property overrides in frontmatter follow these rules:

  • Bare tokens are the default. prize: goat, mood: hostile — no quotes needed.
  • Quoted strings are required when the value could be misinterpreted: "true", "false", "1", "yes", "null". Without quotes, these are parsed as booleans, numbers, or null.
  • Consistency. Examples in this document use bare tokens for enum values. Pick one convention per project and apply it throughout.

4.5 Conditions and Effects

Conditions (? lines)

? @arina.trust > 50              // Comparison
? @coin_purse in player          // Containment check
? @coin_purse not in player      // Negated containment
? @key in here                   // "here" = player's current container (see below)

Multiple ? lines are AND-ed. For OR logic, use ? any::

? any:
  @guard.mood == hostile
  @guard.mood == suspicious

A ? any: block counts as a single condition and can be combined with other ? lines. An empty ? any: block (no indented conditions) is accepted by the parser but evaluates to false — avoid it.

Effects (> lines)

> @arina.trust = 75              // Set to value
> @arina.trust + 5               // Increment
> move @coin_purse -> @arina     // Transfer object
> move @key -> here              // Drop in current location
> reveal @door.prize             // Unhide a property
> destroy @rusty_key             // Remove from world

Reserved Keywords: player and here

player and here are reserved runtime bindings, not entity references:

  • player — resolves to the player entity at runtime. Used as a container target: > move @key -> player, ? @key in player.
  • here — resolves to player.container at runtime. It always means the player’s current location, regardless of whether the condition appears inside a dialogue section, a sequence phase, or a location body.

Both are lowered to their runtime equivalents during EMIT. They are not valid entity IDs.

4.6 Dialogue

Choice Types

* marks a one-shot choice — consumed after selection, never shown again. + marks a sticky choice — available every time the section is entered.

== topics
@arina: What'll it be, stranger?

+ Ask about the harbour             // always available
  @arina: Quiet today. Too quiet.
  > @arina.trust + 5
  -> topics

* Ask about the missing ship        // disappears once asked
  ? @arina.trust > 50
  @arina: The Selene didn't sink. She was taken.
  -> topics

* Leave -> harbour

@arina: Suit yourself. I've got glasses to clean.

Targeting

Choices can target specific entities or entity types:

* Pick up the key -> @rusty_key        // target a specific entity
* Pick a door -> any Door              // target any Door the player can reach

-> @entity compiles to an action with a target field. -> any Type compiles to an action with a target_type field — the runtime presents the player with all reachable entities of that type and lets them choose.

Reachable means: inside here (the player’s current location), plus inside open containers within here, recursively. Hidden entities are excluded unless revealed. This rule applies to all target_type resolution.

Exhaustion

When all choices are consumed or gated, the section is exhausted. Content falls through to text after the choice block. Test for exhaustion from another section: ? topics.exhausted.

Exhaustion is a runtime-evaluated predicate, not a stored boolean. The compiled JSON contains an on_exhausted field with fallthrough content, not an exhausted flag.

Exhaustion predicates only resolve within the same file. Cross-file exhaustion checks (e.g., testing ? topics.exhausted from a different file) are rejected by the compiler (URD423).

Sections and Jumps

SyntaxMeaning
== nameDeclare a section. Names: lowercase, digits, underscores. Unique within file.
-> nameJump to a section or exit in this file (see resolution below).
-> exit:nameExplicitly target an exit when a section shadows the name.
-> endExit dialogue mode.

Resolution priority for -> name: The compiler resolves the target as a section first, then as a declared exit. If neither exists, it is a compile error (URD309). -> name can only target exits that are explicitly declared on the current location — it does not create an implicit exit or perform a direct move. Use -> exit:name to disambiguate when a section and exit share a name.

Nesting Rules

Two-space indent per level. Content under a choice is indented one level.

DepthCompiler Behaviour
1–2 levelsNormal.
3 levelsWarning (URD410): “Consider breaking into a labelled section.”
4+ levelsError (URD410). File does not compile.
* Level 1 choice                        // depth 1
  Some response text.
  * Level 2 choice                      // depth 2 — OK
    * Level 3 choice                    // depth 3 — WARNING
      * Level 4 choice                  // depth 4 — ERROR

Entity Speech vs Narration

@arina: What'll it be?         // Dialogue (colon = speech)
@arina leans in close.          // Stage direction (no colon = narration)

4.7 Rules

Rules define constrained NPC behaviour: an actor performs effects when a trigger fires and conditions are met. Rules are typically engineer-authored.

rule monty_reveals:
  actor: @host action reveal
  selects door from [@door_1, @door_2, @door_3]
    where door.prize == goat
  > reveal door.prize

Canonical structure:

LinePurpose
rule name:Declares the rule. Names must be unique within the compilation unit.
actor: @entity action triggerThe entity that performs the rule, and the trigger that activates it.
selects var from [...]Constrained selection from an entity set.
where exprFilter clauses (indented under selects). All must be satisfied.
> effectEffects applied when the rule fires.

The selects block enables constrained choice — from a set of entities, choose one matching all where conditions. If multiple match, the runtime chooses uniformly at random. If none match, the rule does not fire.


5. Worked Examples

5.1 The Monty Hall Problem

A sequence-driven game show demonstrating hidden state, constrained NPC behaviour, and phased progression. No probability is specified anywhere — the 2/3 switching advantage emerges from the structure.

---
world:
  name: monty-hall
  start: stage

types:
  Door [interactable]:
    ~prize: enum(goat, car)
    revealed: bool = false

entities:
  @door_1: Door { prize: "goat" }
  @door_2: Door { prize: "goat" }
  @door_3: Door { prize: "car" }
  @host: Door
---

# Stage

[@door_1, @door_2, @door_3]

## The Game

### Choose

* Pick a door -> any Door

### Reveal (auto)

rule monty_reveals:
  actor: @host action reveal
  selects door from [@door_1, @door_2, @door_3]
    where door.prize == goat
  > reveal door.prize

### Switch

== switch

* Switch doors -> any Door
  ? @door_1.revealed == false
* Stay with your choice

The host opens the final door.

Open in Playground

What to notice:

  • @host is typed as Door — this is a deliberate shortcut for the example. The host is not spatially important here; it only exists as the actor: in the rule. A production world would declare a separate Host type (e.g., Host [interactable]: name: string).
  • The selects constraint filters out the car door and the player’s chosen door, forcing the host to reveal a goat. The 2/3 switching advantage is an emergent property of a correct world definition, not an authored probability.

5.2 The Two Room Key Puzzle

A freeform spatial puzzle with no sequences. Progression emerges from action conditions and the containment model.

---
world:
  name: two-room-key
  start: cell

types:
  Key [portable]:
    name: string

  LockedDoor [interactable]:
    locked: bool = true
    requires: ref(Key)

  Guard [interactable, mobile, container]:
    name: string
    mood: enum(hostile, neutral, helpful) = hostile
    ~hint_given: bool = false

entities:
  @rusty_key: Key { name: "Rusty Key" }
  @cell_door: LockedDoor { requires: @rusty_key }
  @guard: Guard { name: "Halvard" }
---

# Cell

A dim stone cell. A guard watches from the corner.

[@rusty_key, @guard, @cell_door]

* Wait quietly and show respect -> @guard
  ? @guard.mood == hostile
  > @guard.mood = neutral

* Talk to the guard -> @guard
  ? @guard.mood == neutral
  ? @guard.hint_given == false
  @guard glances at the loose stone in the wall.
  > @guard.hint_given = true

* Pick up the rusty key -> @rusty_key
  ? @rusty_key in here
  > move @rusty_key -> player

* Use the key on the door -> @cell_door
  ? @rusty_key in player
  ? @cell_door.locked == true
  The lock clicks. The door swings open.
  > @cell_door.locked = false
  > destroy @rusty_key

-> north: Corridor
  ? @cell_door.locked == false
  ! The iron door is locked.

# Corridor

A long corridor. Daylight leaks from the far end.

-> south: Cell

Open in Playground

What to notice: Containment replaces inventory throughout. “Pick up” is > move @rusty_key -> player. “Does the player have the key?” is ? @rusty_key in player. The guard’s interaction arc (hostile → neutral → hint) emerges from conditions gating what’s available. No sequence block needed.

Schema Coverage Matrix

ConceptMonty HallKey Puzzle
world metadata
Types and properties
visibility: hidden (~)✓ (~prize)✓ (~hint_given)
traits: portable
traits: container✓ (Guard)
Containment as inventory
Locations (multi)
Exits with conditions
Rules with select
Sequences (phases)
Freeform (no sequence)
effects: set
effects: reveal
effects: move
effects: destroy

Together, the two examples cover most v1 primitives. Not covered: owner visibility, conditional visibility, and spawn effects.


6. The Compiled JSON Format

A compiled .urd.json file is self-contained, deterministic (same source → byte-identical output), versioned, and human-inspectable. It is the single interchange format between authoring and execution.

Here is a minimal compiled output for a world with one type, one entity, one location, and one dialogue section:

{
  "world": { "name": "hello-world", "urd": "1", "start": "room" },
  "types": {
    "NPC": {
      "traits": ["interactable"],
      "properties": {
        "name": { "type": "string" },
        "mood": { "type": "enum", "values": ["friendly", "grumpy"], "default": "friendly" }
      }
    }
  },
  "entities": {
    "greeter": { "type": "NPC", "properties": { "name": "Ada" } }
  },
  "locations": {
    "room": {
      "description": "A quiet room with a single occupant.",
      "contains": ["greeter"]
    }
  },
  "dialogue": {
    "hello-world/conversation": {
      "id": "hello-world/conversation",
      "prompt": { "speaker": "greeter", "text": "Hello, traveller. What brings you here?" },
      "choices": [
        { "id": "hello-world/conversation/ask-about-the-room", "label": "Ask about the room", "sticky": true },
        { "id": "hello-world/conversation/ask-about-her-name", "label": "Ask about her name", "sticky": true },
        { "id": "hello-world/conversation/leave", "label": "Leave", "sticky": false }
      ]
    }
  }
}

The sections below describe each block in detail.

6.1 Top-Level Structure

All blocks are optional except world. Blocks can appear in any order.

BlockPurpose
worldRequired. Metadata: name, version, start location, entry sequence.
typesEntity type definitions with property schemas.
entitiesInstances of defined types with property overrides.
locationsSpatial containers with exits and connections.
rulesNPC behavioural constraints: trigger → condition → select → effect.
actionsPlayer-performable interactions: target, prerequisites, effects.
sequencesOrdered phase flows: game show rounds, tutorial steps.
dialogueFlat map of dialogue sections, choices, jumps, and on_exhausted content.

6.2 The world Block

{
  "world": {
    "name": "monty-hall",
    "urd": "1",
    "version": "1.0",
    "description": "The classic Monty Hall problem",
    "start": "stage",
    "entry": "game"
  }
}

The urd field is always "1" for v1, set by the compiler. The name field is slugified during emit.

6.3 Types, Entities, Properties

{
  "types": {
    "Door": {
      "traits": ["interactable"],
      "properties": {
        "prize": { "type": "enum", "values": ["goat", "car"], "visibility": "hidden" },
        "state": { "type": "enum", "values": ["closed", "open"], "default": "closed" }
      }
    }
  },
  "entities": {
    "door_1": { "type": "Door", "properties": { "prize": "car" } }
  }
}

Property types in compiled JSON always use canonical long forms (boolean, integer, number, string, enum, ref, list).

6.4 Locations and Exits

{
  "locations": {
    "cell": {
      "description": "A dim stone cell.",
      "contains": ["rusty_key", "guard"],
      "exits": {
        "north": {
          "to": "corridor",
          "condition": "cell_door.locked == false",
          "blocked_message": "The iron door is locked."
        }
      }
    }
  }
}

Exits are unidirectional. The field name is contains, consistent with the containment model.

6.5 Actions, Rules, Sequences

Actions are player-performable interactions:

{
  "pick_up_key": {
    "target": "rusty_key",
    "conditions": ["rusty_key.container == player.container"],
    "effects": [{ "move": "rusty_key", "to": "player" }]
  }
}

Declare either target (specific entity) or target_type (any matching entity), not both.

Rules use the select block for constrained random choice:

{
  "monty_reveals": {
    "actor": "monty",
    "trigger": "phase_is reveal",
    "select": {
      "from": ["door_1", "door_2", "door_3"],
      "as": "target",
      "where": ["target.prize != car", "target.chosen == false"]
    },
    "effects": [{ "set": "target.state", "to": "open" }]
  }
}

Sequences define ordered phase flows. Worlds without sequences are freeform.

Advance ModeBehaviour
on_actionAdvance after the player completes a listed action.
on_ruleAdvance after the phase’s rule fires.
on_condition <expr>Advance when the expression becomes true.
autoAdvance immediately after phase effects.
manualRemains active until explicitly advanced. Default.
endThe sequence ends.

6.6 Dialogue

The dialogue block is a flat map of sections keyed by world-unique ID.

{
  "dialogue": {
    "tavern/topics": {
      "id": "tavern/topics",
      "prompt": { "speaker": "arina", "text": "What'll it be, stranger?" },
      "choices": [{
        "id": "tavern/topics/ask-about-the-harbour",
        "label": "Ask about the harbour",
        "sticky": true,
        "response": { "speaker": "arina", "text": "Quiet today." },
        "effects": [{ "set": "arina.trust", "to": "arina.trust + 5" }],
        "goto": "tavern/topics"
      }],
      "on_exhausted": { "text": "Suit yourself." }
    }
  }
}

Stable IDs:

  • Section: file_stem/section_name (e.g., tavern/topics)
  • Choice: section_id/slugified-label (e.g., tavern/topics/ask-about-the-harbour)
  • Entity: declared @name (e.g., rusty_key)

Exhaustion is never stored. It is recomputed on every evaluation: if every choice is consumed or gated, the section is exhausted. The on_exhausted field is content, not a boolean.

6.7 Replay and Randomness

How do I get repeatable runs? Set world.seed in the frontmatter. The runtime uses this seed for every random selection. Same seed + same player choices = same outcome every time.

What if I don’t set a seed? The runtime generates one. It should record the seed in your save data so the session can be replayed later.

Where does randomness happen? Only inside selects blocks. When multiple entities match the where conditions, one is chosen uniformly at random. Declaration order does not influence probability.

What breaks replay? Reordering rules in source changes which random values get consumed where. Runs are still repeatable, but old saves may no longer replay the same way after recompiling. Any change to declarations, conditions, effects, or structure is a semantic change and may produce different output — including different IDs and different evaluation order.

What stays stable? The compiler produces byte-identical output for the same source. Adding whitespace, blank lines, or comments does not change the output.

6.8 Effects

EffectJSON StructureDescription
set{ "set": "entity.prop", "to": value }Change a property value.
move{ "move": "entity", "to": "container" }Move entity into a container.
reveal{ "reveal": "entity.prop" }Change hidden → visible.
destroy{ "destroy": "entity" }Remove entity from world.
spawn{ "spawn": { "id": "new_id", "type": "Type", "in": "container" } }Create entity at runtime.

7. The Compiler

7.1 Six-Phase Pipeline

.urd.md files

  1. PARSE       Source text → per-file ASTs

  2. IMPORT      Resolve imports, build dependency graph

  3. LINK        Merge scopes, resolve cross-file references

  4. ANALYZE     FactSet-derived diagnostics (URD600–URD699)

  5. VALIDATE    Type-check properties, conditions, effects, static analysis

  6. EMIT        Produce .urd.json output

7.2 What Each Compiler Phase Does

PhaseInputOutputGuarantee
PARSESource textPer-file ASTEvery token classified. Unrecognised syntax produces URD111/URD112.
IMPORTFile ASTsDependency graphCycles detected (URD202). Topological order established.
LINKGraph + ASTsSymbol table + FactSetEvery reference resolved. Duplicates rejected.
ANALYZEFactSet + PropertyDependencyIndexDiagnosticsCross-property relationship analysis. Five novel checks (URD601–URD605).
VALIDATESymbol tableDiagnosticsType safety. Constraint satisfaction. Static analysis (S1–S8).
EMITValidated symbol table.urd.jsonConforms to JSON Schema. urd: "1" injected.

ANALYZE runs between LINK and VALIDATE. Its diagnostics are warnings — they never block compilation on their own.

7.3 Import Resolution

  • Imports are explicit and non-transitive.
  • Paths are relative to the importing file. Absolute paths rejected.
  • Circular imports produce URD202 with the full cycle path.
  • Duplicate entity IDs (URD302) and type names (URD303) across files are compile errors.
  • File stem collisions (URD203) are rejected since section IDs would collide.

7.4 ID Derivation

ElementDerivationExample
EntityDeclared @name@rusty_keyrusty_key
LocationSlugified heading# The Rusty Anchorthe-rusty-anchor
Sectionfile_stem/section_name== topics in tavern.urd.mdtavern/topics
Choicesection_id/slugified-label”Ask about the harbour” → tavern/topics/ask-about-the-harbour

IDs are stable across recompiles. If a writer adds a line, IDs do not change. This is critical for save files, testing assertions, and LSP references.

7.5 Input Limits

LimitValue
Maximum file size1 MB (1,048,576 bytes)
Maximum files per compilation unit256
Maximum import depth64 levels
Maximum frontmatter nesting8 levels
Choice nesting: warning3 levels
Choice nesting: error4+ levels

7.6 Error Recovery

The compiler does not stop at the first error. Each phase collects diagnostics and continues where possible. EMIT runs only when zero errors exist. Warnings do not block compilation.

Diagnostics include file path, line/column span, actionable message, and error code. Edit-distance suggestions are offered for unresolved references (e.g., “@guard.trust is not a property on type Guard. Did you mean @guard.mood?“).

7.7 Diff and Snapshots

The compiler includes a semantic diff engine that compares two compiled worlds at the structural level — not a text diff or JSON diff, but a typed change report that understands the difference between “an exit was added” and “a property write was removed.”

Six change categories: entity, location/exit, dialogue (section/choice), property dependency, rule, and reachability.

CLI commands:

urd diff a.urd.md b.urd.md                    # Compare two source files
urd diff before.snapshot.json after.urd.md     # Compare snapshot to source
urd diff a.urd.md b.urd.md --format summary    # Human-readable summary
urd snapshot hello.urd.md                      # Create .urd.snapshot.json
urd snapshot hello.urd.md -o out.json          # Custom output path

Exit codes: 0 if no changes, 1 if changes detected.

Snapshot format: .urd.snapshot.json files capture a normalised, comparable representation of a compiled world — entities, locations, exits, sections, choices, rules, properties, and diagnostics. Version marker: urd_snapshot: "1". Snapshots can be committed to version control and used as regression baselines in CI.

7.8 DefinitionIndex

The DefinitionIndex maps identifiers to their declaration spans — the source location where each type, entity, property, section, choice, location, exit, or rule was defined. Built from the SymbolTable after LINK, it provides go-to-definition and hover data for IDE consumers.

Seven namespaces:

Key FormatKindMetadata
type:TypeNameTypeTraits
prop:TypeName.propNamePropertyType, default value
entity:@entity_idEntityType name
section:file_stem/nameSectionLocal name, file stem
choice:section_id/slugChoiceSection ID, label
location:slugLocationDisplay name
exit:location_id/directionExitFrom location, destination
rule:nameRule

Each entry includes the declaration span (file, line, column range) and kind-specific metadata. The index is serialised in WASM output for playground integration and is available on CompilationResult whenever LINK succeeds.

7.9 CLI Reference

urd 0.1.14 — Schema Markdown compiler

Compiles .urd.md files through a six-phase pipeline
(PARSE → IMPORT → LINK → ANALYZE → VALIDATE → EMIT) to produce .urd.json.

USAGE:
  urd <file.urd.md>
  urd diff <a> <b> [OPTIONS]
  urd snapshot <file.urd.md> [OPTIONS]
  urd --help | -h
  urd --version | -V

COMMANDS:
  <file.urd.md>    Compile a .urd.md file and emit .urd.json to stdout.
                   Diagnostics are printed to stderr.
                   Exit code 0 on success, 1 on errors.

  diff <a> <b>     Compare two compilations and report changes.
                   Each argument can be a .urd.md file (compiled on the
                   fly) or a .urd.snapshot.json file.
                   Exit code 0 if no changes, 1 if changes detected.

      --format <FORMAT>   Output format: json (default) or summary.

  snapshot <file>  Create a .urd.snapshot.json from a .urd.md file.
                   Snapshots capture entities, locations, exits, sections,
                   choices, rules, properties, and diagnostics for use
                   with the diff command.

      -o <path>           Output path. Defaults to <file>.urd.snapshot.json.

OPTIONS:
  -h, --help       Print help message and exit.
  -V, --version    Print the compiler version and exit.

8. Errors and Warnings

Reading a Diagnostic

Every diagnostic the compiler emits has four parts:

FieldExampleDescription
codeURD301Stable numeric code. Look it up in the tables below.
severityerrorOne of error, warning, or info.
messageUnresolved reference: @gaurd. Did you mean @guard?Human-readable explanation, often with a suggestion.
spantavern.urd.md:14:3–14:9File path, start line:column, end line:column.

Errors block compilation — the compiler will not produce JSON output. Warnings are informational — output is still produced, but the warning usually points to something worth fixing. The compiler collects all diagnostics across all phases before reporting; it does not stop at the first error.

Code Ranges

PhaseRangeErrorsWarningsTotal
PARSEURD100–URD19911011
IMPORTURD200–URD29913114
LINKURD300–URD39913114
VALIDATEURD400–URD49922830
ANALYZEURD600–URD699055
EMITURD500–URD599000
Total591574

PARSE Phase (URD100–URD199)

CodeSeverityTrigger
URD101ErrorUnclosed frontmatter block.
URD102ErrorTab character in source. Urd requires spaces.
URD103ErrorFile exceeds 1 MB size limit.
URD104ErrorFrontmatter nesting exceeds 8 levels.
URD105ErrorYAML anchor (&name) rejected.
URD106ErrorYAML alias (*name) rejected.
URD107ErrorYAML merge key (<<:) rejected.
URD108ErrorYAML custom tag (!!type) rejected.
URD109ErrorBlock-style list (- item) rejected. Use [item1, item2].
URD111ErrorUnrecognised frontmatter syntax.
URD112ErrorUnrecognised content syntax.

IMPORT Phase (URD200–URD299)

CodeSeverityTrigger
URD201ErrorImported file not found.
URD202ErrorCircular import detected. Full cycle path reported.
URD203ErrorFile stem collision across compilation unit.
URD204ErrorImport depth exceeds 64 levels.
URD205ErrorCompilation unit exceeds 256 files.
URD206WarningFilename casing mismatch (case-insensitive filesystem).
URD207ErrorSelf-import.
URD208ErrorImport path escapes project root.
URD209ErrorAbsolute import path.
URD210ErrorMissing .urd.md extension.
URD211ErrorEmpty import path.
URD212ErrorInvalid UTF-8 in imported file.
URD213ErrorPermission denied reading imported file.
URD214ErrorI/O error reading imported file.
CodeSeverityTrigger
URD301ErrorUnresolved reference (@entity, type, location, property). Suggestions via edit distance.
URD302ErrorDuplicate entity or rule ID.
URD303ErrorDuplicate type name.
URD304ErrorDuplicate location ID (slugified heading collision).
URD305ErrorDuplicate section name within a file.
URD306ErrorDuplicate choice ID within a section.
URD307ErrorUnknown entity type. Suggestions via edit distance.
URD308ErrorUnknown property on type.
URD309ErrorUnresolved jump target or section.
URD310WarningSection shadows exit name. Use -> exit:name.
URD311ErrorUnresolved -> exit:name jump.
URD312ErrorUnresolved exit destination.
URD313ErrorEmpty slugified ID.
URD314ErrorConstruct outside location context.

VALIDATE Phase (URD400–URD499)

CodeSeverityTrigger
URD401ErrorType mismatch in condition or effect.
URD402ErrorInvalid enum override value.
URD404ErrorInvalid world.start location.
URD405ErrorInvalid world.entry sequence.
URD406ErrorBoth target and target_type declared.
URD407ErrorUnknown action in sequence phase.
URD408ErrorUnknown rule in sequence phase.
URD409ErrorInvalid advance mode.
URD410Error/WarnChoice nesting depth (warn at 3, error at 4+).
URD411WarningAuthor manually set urd: field.
URD412ErrorPlayer entity missing mobile/container traits.
URD413ErrorInvalid property default value.
URD414ErrorEmpty enum values list.
URD415ErrorUnknown ref target type.
URD416ErrorRange min > max.
URD417ErrorRange on non-numeric type.
URD418ErrorValue outside declared range.
URD419ErrorRef type mismatch.
URD420ErrorOrdering operator on non-numeric type.
URD422ErrorMissing container trait.
URD423ErrorCross-file exhaustion check.
URD424ErrorArithmetic on non-numeric property.
URD425ErrorMove without portable trait.
URD426WarningReveal on non-hidden property.
URD427WarningAuto phase with player actions.
URD428ErrorEmpty sequence.
URD429WarningUnrecognised property type string.
URD430WarningUnreachable location (no path from world.start).
URD431WarningSection named end shadows built-in terminal.
URD432WarningOrphaned choice (condition impossible).
URD433WarningMissing fallthrough (all one-shot, no exit).
URD434WarningSection-exit shadowing.

ANALYZE Phase (URD600–URD699)

The ANALYZE phase runs five FactSet-derived diagnostics that operate solely on the FactSet and PropertyDependencyIndex — no AST traversal, no source text parsing. All are warnings. They detect structural issues that require cross-referencing reads against writes across the entire world.

CodeSeverityTrigger
URD601WarningProperty read in conditions but never written by any effect.
URD602WarningProperty written by effects but never read in any condition.
URD603WarningEffect sets enum property to a variant that no condition ever tests.
URD604WarningCondition tests a numeric threshold unreachable by any effect.
URD605WarningCircular property dependency: every write is guarded by a read of the same property, with no unguarded bootstrap path.

These diagnostics answer questions that AST-walking checks cannot. They require cross-referencing reads against writes across the entire world. The FactSet makes each one a single-pass query.

Gate Cross-Reference

Gate CheckPrimary Codes
C1: Constrained frontmatterURD104–URD109, URD111
C2: Import resolutionURD201, URD209–URD211
C3: Circular import detectionURD202
C4: Duplicate entity IDsURD302
C5: Duplicate type namesURD303
C6: Reference resolutionURD301, URD307–URD309, URD311–URD312
C7: Property validationURD401, URD402, URD413–URD420
C8: urd: "1" injectionURD411
C9: Nesting depthURD410
S1–S2: Entity/type checksURD301, URD401
S3: Unreachable locationURD430
S4: Orphaned choiceURD432
S5: Duplicate IDsURD302–URD306
S6: Missing fallthroughURD433
S7: Circular importsURD202
S8: Shadowed exitURD434

9. FactSet Analysis IR

The FactSet is a normalised, immutable, deterministic set of typed tuples extracted after the LINK phase. Each tuple represents one atomic relationship — an exit connecting two locations, a condition reading a property, an effect writing a property. The facts are direct translations of what LINK resolved. No inference, no transitive closure, no runtime simulation.

9.1 Fact Types

Fact TypeWhat It RepresentsKey Fields
ExitEdgeAn exit connecting two locationsfrom_location, to_location, exit_name, is_conditional
JumpEdgeA jump between dialogue sectionsfrom_section, target (section, exit, or end)
ChoiceFactA choice within a sectionsection, choice_id, label, sticky, condition/effect indices, jump_indices
RuleFactA behavioural rulerule_id, condition/effect indices
PropertyReadA condition reading a propertysite, entity_type, property, operator, value_literal
PropertyWriteAn effect writing a propertysite, entity_type, property, operator, value_expr

Every fact carries a span (file, line, column range) for source attribution.

ChoiceFact.jump_indices tracks which JumpEdge indices the choice owns, enabling dialogue graph edge-to-choice correlation. This follows the same index-reference pattern as condition_reads and effect_writes.

9.2 FactSite

Every PropertyRead and PropertyWrite is tagged with a FactSite — the construct that owns the read or write:

Site KindMeaning
Choice(id)A dialogue choice’s condition or effect
Exit(id)An exit’s guard condition
Rule(id)A rule’s where-clause or effect

This enables queries like “which choices read Guard.mood?” or “which rules write Door.state?“

9.3 PropertyDependencyIndex

A derived secondary index built from the FactSet in a single pass. Maps (entity_type, property) pairs to their read and write site indices.

MethodReturns
reads_of(key)All read indices for a property
writes_of(key)All write indices for a property
read_properties()All properties read anywhere
written_properties()All properties written anywhere
read_but_never_written()Properties with reads but no writes (orphaned reads)
written_but_never_read()Properties with writes but no reads (orphaned writes)
to_json()Deterministic JSON with per-property read/write counts, orphaned flags, and summary

Use cases: dead property detection (written but never read), impact analysis (what changes if a property is removed), dependency graphs for incremental revalidation, and the ANALYZE diagnostics (URD601–URD605).

The PropertyDependencyIndex is serialised in the WASM pipeline as property_index alongside facts in the compile_source() response.

9.4 WASM Serialisation

The WASM compile_source() response includes:

  • facts — the serialised FactSet with six arrays (reads, writes, exits, jumps, choices, rules) corresponding to the six fact types.
  • property_index — the PropertyDependencyIndex with per-property read/write counts, orphaned flags, and summary statistics.
  • definition_index — the DefinitionIndex mapping identifiers to declaration spans (see Section 7.8).

The browser playground’s Analysis panel renders facts by type, the Properties tab renders the PropertyDependencyIndex, and the editor uses the DefinitionIndex for hover and go-to-definition.

9.5 ANALYZE Diagnostics

The ANALYZE phase (URD600–URD699) is the direct consumer of the FactSet and PropertyDependencyIndex. It runs five checks, all producing warnings:

  1. URD601 — Read but never written. A property appears in conditions but no effect in the world ever writes to it. The condition can only evaluate against the default value, which is probably not the author’s intent.

  2. URD602 — Written but never read. A property is changed by effects but no condition ever tests it. The writes have no observable consequence.

  3. URD603 — Untested enum variant. An effect sets an enum property to a variant that no condition anywhere tests. The runtime will set the value, but nothing reacts to it.

  4. URD604 — Unreachable threshold. A condition compares a numeric property against a threshold that no combination of effects can produce. Conservative: skips the check if any Add/Sub writes exist (incremental effects could potentially reach any threshold).

  5. URD605 — Circular dependency. Every write to a property is guarded by a condition that reads the same property, and there is no unguarded “bootstrap” write. The property can only change if it has already changed — a deadlock.

These diagnostics answer questions that AST-walking cannot. Each requires cross-referencing the full read and write sets across every choice, exit, and rule in the world. The FactSet makes each one a single-pass query.


10. Advanced Topics

10.1 The Containment Model

Urd uses a single spatial primitive: containment. Every entity exists inside exactly one container at any given time. A room holds a sword. A chest holds a sword. A player holds a sword. Same mechanism.

  • “Pick up” is > move @key -> player.
  • “Drop” is > move @key -> here.
  • “Give to NPC” is > move @key -> @guard.
  • “Does the player have it?” is ? @key in player.
  • “Is it in the same room?” is ? @key in here.

Player entity rule. If a @player entity is explicitly declared in frontmatter, its type must include the mobile and container traits (URD412 if missing). If no @player entity is declared, the runtime should synthesise one with both traits, placed at the world.start location. In either case, the player is a mobile container — no special inventory system is needed.

10.2 Multi-File Projects

tavern-game/
  world.urd.md      # Types, entities, config (engineer)
  rules.urd.md      # NPC behaviour rules (engineer)
  tavern.urd.md     # Tavern location + dialogue (writer)
  harbour.urd.md    # Harbour location + events (writer)
  • Writers’ frontmatter is one line: import: ./world.urd.md
  • Cross-location movement uses exits (-> harbour), not section jumps.
  • Entity state (properties set via >) communicates across files. Section exhaustion does not.
  • Each file’s sections track their own state independently.

10.3 Source Is Not YAML

Urd frontmatter uses --- delimiters and key: value pairs, which look like YAML. They are not. The parser is a purpose-built subset with strict constraints. Key differences:

FeatureYAMLUrd Frontmatter
Anchors and aliases (&name, *name)SupportedRejected (URD105, URD106)
Merge keys (<<:)SupportedRejected (URD107)
Custom tags (!!type)SupportedRejected (URD108)
Block-style lists (- item)SupportedRejected (URD109). Use [item1, item2].
Implicit type coercion (yestrue)SupportedRejected. true/false only.
TabsAllowedRejected (URD102). Spaces only.
NestingUnlimited8 levels maximum (URD104)

If you paste YAML from another tool and the compiler rejects it, check the diagnostic code — it will tell you exactly which construct is unsupported.

10.4 What You Can Rely On

These things are stable as of v0.1.14. We intend to keep them stable:

  • Diagnostic code numbers. URD101–URD605 are assigned and will not be reassigned to different meanings.
  • ID derivation rules. Entity IDs, section IDs, choice IDs, and location IDs are derived deterministically. The algorithm will not change.
  • JSON Schema version field. urd: "1" is injected by the compiler. The value only changes for a new schema version.
  • Compiler output determinism. Same source produces byte-identical JSON.
  • FactSet structure. The six fact types and their core fields have proven stable across seven releases. The PropertyDependencyIndex API is stable.

These things will change as the project matures:

  • Source syntax (new symbols, revised grammar)
  • JSON output structure (new blocks, renamed fields)
  • WASM API function signatures and response shapes
  • Diagnostic message text (codes are stable; wording is not)
  • DefinitionIndex key formats and metadata fields

10.5 v1 Boundaries

v1 is the complete foundation. A runtime that fully supports v1 implements: all eight JSON blocks, all property types, all four visibility levels (including owner and conditional which have JSON support but no source syntax yet), all entity traits, the containment model, all five effect types, all five trigger types, the select block with deterministic random selection, all six advance modes, dialogue with sticky/one-shot choices, any: OR conditions, and event sourcing.

Deferred to future versions:

Featurev1 Workaround
Cross-file section jumpsUse exits for cross-file movement. Bridge sections for shared context.
Lambda functionsExpress logic declaratively using rules, conditions, effects.
Owner visibility transferUse owner for static ownership only.
Cross-file exhaustionAccept independent counters in duplicated sections.

10.6 Permanent Exclusions

The following are permanently excluded from the schema (not deferred — they will never be part of Urd):

  • Verb synonyms and natural language parsing
  • Conditional text rendering (presentation layer concern)
  • Failure experience design (UX layer concern)
  • Time and pacing mechanics (future extension, not core schema)
  • Persistence format specification
  • On-attempt rules (rules fire on success, not attempt)

11. Quick Reference

Writer Pattern Reference

I want to…Pattern
Create a hub conversation== topics with choices that -> topics
Gate a choice on state? @entity.prop == value before the choice
Change the world on selection> @entity.prop = value after the choice
Make a choice one-time* choice text
Make a choice repeatable+ choice text
Show fallthrough textPlain text after the choice block
Check if player has an item? @item in player
Check if item is in the room? @item in here
Transfer an object> move @item -> @npc
End the conversation-> end
Move to another location-> location_name (requires a declared exit)
Branch on NPC mood? @npc.mood == hostile / ? @npc.mood == neutral as separate blocks
Gate on any of several conditions? any: + indented conditions
Drop an item> move @item -> here
Compare two worldsurd diff before.urd.md after.urd.md
Create a regression snapshoturd snapshot world.urd.md

Compiler Mapping Table

SyntaxCompiles To
------world, types, entities blocks
import: pathResolved types/entities merged into scope
# NameEntry in locations block
[@entity, ...]contains field of enclosing location
## NameEntry in sequences block
### NamePhase within enclosing sequence
(auto)auto: true on the phase
* labelOne-shot choice (sticky: false)
+ labelSticky choice (sticky: true)
* label -> @targetChoice targeting a specific entity (compiles to action with target)
* label -> any TypeChoice targeting any entity of a type the player can reach (compiles to action with target_type)
? exprEntry in conditions list (AND-ed)
? any:any: block in conditions
> entity.prop = valueset effect
> move @entity -> containermove effect
> reveal @entity.propreveal effect
> destroy @entitydestroy effect
== nameSection in dialogue block
-> namegoto field
-> endDialogue exit (no goto)
! textblocked_message on exit or action
rule name:Entry in rules block
// textStripped. Not in JSON.

12. Changelog Summary (v0.1.8–v0.1.14)

VersionDateHighlightsTests
0.1.82026-02-24ANALYZE phase: URD601–URD605. Five FactSet-derived diagnostics.569
0.1.92026-02-24PropertyDependencyIndex: set-difference queries, JSON serialisation, WASM property_index.580
0.1.102026-02-24ChoiceFact.jump_indices. Playground graph visualisation (location + dialogue).584
0.1.122026-02-24Semantic diff engine. CLI urd diff and urd snapshot. .urd.snapshot.json format.612
0.1.132026-02-24DefinitionIndex: seven namespaces, go-to-definition, hover data. Semantic gate closed.626
0.1.142026-02-26SymbolTable + DependencyGraph on CompilationResult. CLI --help/--version.634

For full details, see the compiler CHANGELOG.


This manual is the practical entry point. If you need formal definitions or design rationale, the individual specifications and briefs contain the deeper detail.

End of Reference Manual