Skip to content

Variables

Variables are named constants declared in a top-level variables: section. They are substituted into definitions and operations during preprocessing, before YAML deserialization.

variables:
BASE_HP: 1000
BASE_MP: 500
ENCHANT_STEPS: [10, 11, 12]

Variable names must match [A-Za-z_][A-Za-z0-9_]*. Reserved names (extends, remove, with, params, loop, repeat) are not allowed.

TypeExampleSubstitution result
Scalar (string, number, bool)BASE_HP: 1000$BASE_HP1000
Scalar listSTEPS: [10, 11, 12]$STEPS[10, 11, 12]
Forbidden typeExampleError
MappingSTATS: { hp: 100, mp: 50 }E534
List of mappingsITEMS: [{ id: 1 }, { id: 2 }]E534
List of listsMATRIX: [[1,2],[3,4]]E534

Variables are referenced with a $ prefix. Only whole-value replacement is supported — the entire scalar value must be the variable reference.

variables:
DEFAULT_LEVEL: 60
creatures:
create:
- id: 1
level: $DEFAULT_LEVEL
name: goblin

After resolution: level: 60. The variables: section is stripped from output.

Variables declared in variables: are available inside definition bodies. Substitution runs after $extends merge:

variables:
BASE_HP: 1000
definitions:
warrior:
hp: $BASE_HP
mp: 500
warriors:
create:
- $extends: warrior
name: fighter

After resolution: hp: 1000, mp: 500.

Declare variables and list them in exports.variables:

shared/constants.yaml
variables:
BASE_HP: 1000
BASE_MP: 500
exports:
variables:
- BASE_HP
- BASE_MP

All exported names must exist in the module’s variables: section or its effective imported scope (E535 if not). See Re-exporting for details.

Import variables with use.variables in the import directive:

imports:
- from: shared/constants.yaml
use:
variables:
- BASE_HP
- BASE_MP
warriors:
create:
- id: 1
hp: $BASE_HP
mp: $BASE_MP

Variables require explicit opt-in via use.variables. An import without use.variables does not import any variables, even if the source module exports them.

Requesting a variable not listed in the source module’s exports.variables triggers E536.

A module can re-export variables it has imported via use.variables. The re-exported variable becomes part of the module’s public export surface, allowing downstream consumers to import it without depending on the original source:

# package-a/index.yml — declares and exports BASE_HP
variables:
BASE_HP: 1000
exports:
variables:
- BASE_HP
# package-b/index.yml — imports and re-exports BASE_HP
imports:
- from: package-a
use:
variables:
- BASE_HP
exports:
variables:
- BASE_HP # Re-export: allowed because BASE_HP is in imported scope
# consumer.yaml — imports from package-b, not package-a
imports:
- from: package-b
use:
variables:
- BASE_HP # Works: package-b re-exports it
items:
create:
- id: 1
hp: $BASE_HP # Resolves to 1000

A module can only re-export a variable that it explicitly imported via use.variables. Attempting to re-export a variable that was not imported triggers E535:

# INVALID: package-b did not import INTERNAL via use.variables
imports:
- from: package-a # No use.variables — no variables imported
exports:
variables:
- INTERNAL # E535: not in local variables or effective imported scope

When a module declares a local variable with the same name as an imported one, the local value is exported:

imports:
- from: package-a
use:
variables:
- HP # HP: 100 from package-a
variables:
HP: 999 # Local declaration shadows the import
exports:
variables:
- HP # Exports 999 (local value wins)

Variables can be imported from registered packages:

imports:
- from: shared
use:
variables:
- GLOBAL_HP

The package’s index.yml must export the variable.

Local variables shadow imported variables of the same name:

imports:
- from: shared/constants.yaml
use:
variables:
- BASE_HP
variables:
BASE_HP: 9999
warriors:
create:
- id: 1
hp: $BASE_HP # Resolves to 9999, not the imported 1000

Variables and definitions can be imported from the same module:

imports:
- from: shared/base.yaml
use:
variables:
- DEFAULT_LEVEL
creatures:
create:
- $extends: baseCreature
id: 1
name: goblin

Definitions import automatically (no opt-in needed). Variables require use.variables.

ContextResolved?Notes
Definition bodiesYesAfter $extends merge
Spec body (create, update, upsert)YesDirect substitution
$with valuesYes$with: { X: $VAR } resolves $VAR before binding
$extends value (with $with)Yes$extends: $PARAM resolves via $with bindings
$extends value (without $with)NoDirect $extends: $VAR is not supported
ConstraintDetail
No inline interpolation"prefix_${VAR}_suffix" does not work. Only full-value replacement.
No direct $extends: $VAR$extends requires a literal definition name — unless $with bindings provide the value. See Definitions.
No expressions${ BASE_HP + 100 } is not supported.
No namespacesVariable names are flat — no dot-separated paths.
Flat names onlyCasing is not enforced; [A-Za-z_][A-Za-z0-9_]* is the only constraint.

For parameterized templates, use definitions with $extends and $with.

CodeMeaning
E520Unknown variable reference ($MISSING with no binding in scope)
E532Invalid variable name (bad pattern or reserved name)
E533Duplicate variable declaration in the same module
E534Invalid variable type (mapping or non-scalar list)
E535Exported variable not found in module’s variables: section or effective imported scope
E536Imported variable not listed in source module’s exports.variables
variables:
HP: 100
creatures:
create:
- id: 1
hp: $MISSING # E520: No variable 'MISSING' in scope
variables:
extends: foo # E532: 'extends' is a reserved name
variables:
123bad: foo # E532: Does not match [A-Za-z_][A-Za-z0-9_]*
variables:
STATS:
hp: 100
mp: 50 # E534: Mapping values are not allowed

Triggers when an exported variable name is neither declared locally in variables: nor available in the module’s effective imported scope (via use.variables):

variables:
BASE_HP: 1000
exports:
variables:
- BASE_HP
- NONEXISTENT # E535: not in local variables or effective imported scope
imports:
- from: shared/constants.yaml
use:
variables:
- SECRET # E536: 'SECRET' is not in source module's exports.variables
  • Definitions$extends inheritance, $with parameter binding, $params validation
  • Import System — packages, exports, and module resolution
  • Error Codes — complete error code reference