Structural notation describes tracked YAML source rather than normalized JSON:? permits omission, ? ... = value identifies an omission default, and unmarked fields are required. Collection-qualified references in record YAML use bare IDs; registry/owner-qualified references retain their explicit identity. Numbers are finite. Semantic examples below are checked normalized JSON fragments, not copy-ready source YAML; use the Agent Kit's checked YAML examples for authoring. Run the read-onlynoveltea validate for contextual and cross-record constraints.
Project workspace manifest
Raw JSON SchemaShape and constraints
ProjectWorkspaceManifest = {
schema: "noveltea.project.workspace"
schemaVersion: 1
project: {
id: EntityId
name: string
version?: string = "0.1.0"
author?: string = ""
description?: string = ""
}
settings: {
distributionNotices?: ({
path: string
displayName?: string length >= 1 length <= 256
})[] = []
ui?: {
systemLayouts?: {
title?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"game-hud"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"pause-menu"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"save-menu"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"load-menu"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"settings-menu"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"text-log"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
modal?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"debug-overlay"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"command-builder"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"scene-text"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
"scene-choice"?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
licenses?: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
} = {}
} = {"systemLayouts":{}}
inventory?: {
defaultLayout: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
} = {"defaultLayout":null}
interaction?: {
defaultVerbMenuLayout: {
$ref: {
collection: "layouts"
id: string length >= 1
}
} | null
} = {"defaultVerbMenuLayout":null}
text?: {
defaultFont?: {
$ref: {
collection: "assets"
id: string length >= 1
}
} | null = null
fontStack: ({
$ref: {
collection: "assets"
id: string length >= 1
}
})[]
} = {"defaultFont":null,"fontStack":[]}
titleScreen?: {
titleImage?: {
$ref: {
collection: "assets"
id: string length >= 1
}
} | null = null
showProjectTitle?: boolean = true
showAuthor?: boolean = false
subtitle?: string = ""
startLabel?: string length >= 1 = "Start"
} = {"titleImage":null,"showProjectTitle":true,"showAuthor":false,"subtitle":"","startLabel":"Start"}
app?: {
displayName: string length >= 1
shortName?: string length >= 1
publisher?: string length >= 1
copyright?: string length >= 1
description?: string
defaultLocale?: string length >= 1
localized?: {
[key: string]: {
displayName?: string length >= 1
shortName?: string length >= 1
description?: string length >= 1
}
} = {}
applicationId: string pattern "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9-]*)+$"
saveNamespace: string length >= 1
versionName: string length >= 1
buildNumber?: integer >= 1 <= 9007199254740991
icon?: {
$ref: {
collection: "assets"
id: string length >= 1
}
} | null = null
iconBackgroundColor?: string pattern "^#[0-9a-fA-F]{6}$"
accentColor?: string pattern "^#[0-9a-fA-F]{6}$"
themeColor?: string pattern "^#[0-9a-fA-F]{6}$"
launchImage?: {
$ref: {
collection: "assets"
id: string length >= 1
}
} | null = null
launchBackgroundColor?: string pattern "^#[0-9a-fA-F]{6}$"
desktop?: {
appleBundleId?: string pattern "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9-]*)+$"
linuxDesktopId?: string pattern "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9-]*)+$"
windowsIdentity?: string length >= 1
buildNumber?: integer >= 1 <= 9007199254740991
} = {}
web?: {
manifestId?: string length >= 1
buildNumber?: integer >= 1 <= 9007199254740991
} = {}
android?: {
applicationId?: string pattern "^[a-z][a-z0-9]*(?:\\.[a-z][a-z0-9-]*)+$"
versionCode?: integer >= 1 <= 9007199254740991
allowBackup?: boolean
isGame?: boolean
} = {}
}
display?: {
referenceResolution: {
width: integer >= 1 <= 10000
height: integer >= 1 <= 10000
}
worldRasterPolicy: "capped" | "native"
barColor: string pattern "^#[0-9a-fA-F]{6}$"
} = {"referenceResolution":{"width":1920,"height":1080},"worldRasterPolicy":"capped","barColor":"#000000"}
accessibility?: {
uiScale?: {
enabled: boolean
minimum: number > 0
maximum: number > 0
} = {"enabled":true,"minimum":1,"maximum":2}
textScale?: {
enabled: boolean
minimum: number > 0
maximum: number > 0
} = {"enabled":true,"minimum":1,"maximum":2}
} = {"uiScale":{"enabled":true,"minimum":1,"maximum":2},"textScale":{"enabled":true,"minimum":1,"maximum":2}}
audio?: {
purposes: {
music: {
volume: number >= 0 <= 1
muted: boolean
}
ambience: {
volume: number >= 0 <= 1
muted: boolean
}
voice: {
volume: number >= 0 <= 1
muted: boolean
}
"sound-effect": {
volume: number >= 0 <= 1
muted: boolean
}
"ui-sound": {
volume: number >= 0 <= 1
muted: boolean
}
}
voiceDucking: {
enabled: boolean
musicGain: number >= 0 <= 1
ambienceGain: number >= 0 <= 1
}
} = {"purposes":{"music":{"volume":1,"muted":false},"ambience":{"volume":1,"muted":false},"voice":{"volume":1,"muted":false},"sound-effect":{"volume":1,"muted":false},"ui-sound":{"volume":1,"muted":false}},"voiceDucking":{"enabled":false,"musicGain":0.5,"ambienceGain":0.5}}
cursors?: {
defaults: {
default: CursorTarget
pointer: CursorTarget
hotspot: CursorTarget | {
kind: "inherit"
semantic: "pointer"
}
}
named: ({
id: CursorId
image: {
$ref: {
collection: "assets"
id: string length >= 1
}
}
hotspotX: integer >= 0 <= 9007199254740991
hotspotY: integer >= 0 <= 9007199254740991
})[]
} = {"defaults":{"default":{"kind":"system","cursor":"default"},"pointer":{"kind":"system","cursor":"pointer"},"hotspot":{"kind":"inherit","semantic":"pointer"}},"named":[]}
presentation?: {
roomNavigationTransition: {
kind: "cut" | "fade" | "dissolve"
durationMs: integer >= 0 <= 9007199254740991
color: string | null
skippable: boolean
}
} = {"roomNavigationTransition":{"kind":"cut","durationMs":0,"color":null,"skippable":true}}
}
export: {
runtime: {
id?: string length >= 1 = "runtime-default"
label?: string length >= 1 = "Runtime Package"
kind?: "runtime" | "editable" = "runtime"
outputPath?: string = ""
includeChecksums?: boolean = true
stripEditorData?: boolean = true
stripShaderSources?: boolean = true
compileShadersBeforeExport?: boolean = true
shaderVariants?: ("glsl-330" | "essl-300" | "metal")[] = ["glsl-330","essl-300","metal"]
excludeUnusedAssets?: boolean = true
includeShaderSources?: boolean = false
includeTests?: boolean = false
previewAfterExport?: boolean = false
localization?: {
locales: (string length >= 1)[] items >= 1
defaultLocale: string length >= 1
quality: "development" | "release" | "reviewed-release"
} = {"locales":["en"],"defaultLocale":"en","quality":"development"}
}
profiles?: (({
format: "noveltea.platform-export-profile"
id: string length >= 1
label: string length >= 1
buildFlavor: "debug" | "release"
playerVariant?: "standard" | "devtools"
compression?: "default" | "store" | "maximum" = "default"
includeDebugSymbols?: boolean = false
excludeUnusedAssets?: boolean = true
includeShaderSources?: boolean = false
assetMemory?: ({
kind: "builtin"
preset: "low" | "balanced" | "high"
} | {
kind: "policy"
policyId: string length >= 1
}) exactly one variant = {"kind":"builtin","preset":"balanced"}
localization?: {
locales: (string length >= 1)[] items >= 1
defaultLocale: string length >= 1
quality: "development" | "release" | "reviewed-release"
} = {"locales":["en"],"defaultLocale":"en","quality":"release"}
target: "windows" | "linux" | "macos"
architecture: "x64" | "arm64"
packageAccess: "sidecar" | "bundle-resource"
desktop: {
artifact: "zip" | "tar" | "appimage" | "app-bundle"
executableName: string length >= 1
}
} | {
format: "noveltea.platform-export-profile"
id: string length >= 1
label: string length >= 1
buildFlavor: "debug" | "release"
playerVariant?: "standard" | "devtools"
compression?: "default" | "store" | "maximum" = "default"
includeDebugSymbols?: boolean = false
excludeUnusedAssets?: boolean = true
includeShaderSources?: boolean = false
assetMemory?: ({
kind: "builtin"
preset: "low" | "balanced" | "high"
} | {
kind: "policy"
policyId: string length >= 1
}) exactly one variant = {"kind":"builtin","preset":"balanced"}
localization?: {
locales: (string length >= 1)[] items >= 1
defaultLocale: string length >= 1
quality: "development" | "release" | "reviewed-release"
} = {"locales":["en"],"defaultLocale":"en","quality":"release"}
target: "web"
architecture: "wasm32"
packageAccess: "web-fetch"
web: {
artifact: "directory-zip"
threaded?: boolean = false
pwa?: boolean = false
display?: "fullscreen" | "standalone" | "minimal-ui" | "browser" = "standalone"
basePath?: string pattern "^\\/$|^\\/[a-zA-Z0-9._~!$&'()*+,;=:@%/-]*\\/$" = "/"
serviceWorker?: "disabled" | "offline" = "disabled"
}
} | {
format: "noveltea.platform-export-profile"
id: string length >= 1
label: string length >= 1
buildFlavor: "debug" | "release"
playerVariant?: "standard" | "devtools"
compression?: "default" | "store" | "maximum" = "default"
includeDebugSymbols?: boolean = false
excludeUnusedAssets?: boolean = true
includeShaderSources?: boolean = false
assetMemory?: ({
kind: "builtin"
preset: "low" | "balanced" | "high"
} | {
kind: "policy"
policyId: string length >= 1
}) exactly one variant = {"kind":"builtin","preset":"balanced"}
localization?: {
locales: (string length >= 1)[] items >= 1
defaultLocale: string length >= 1
quality: "development" | "release" | "reviewed-release"
} = {"locales":["en"],"defaultLocale":"en","quality":"release"}
target: "android"
architecture: "arm64" | "x86_64"
packageAccess: "android-asset" | "android-private-copy"
android: {
artifact: "apk" | "aab" | "both"
abi: "arm64-v8a" | "x86_64"
minSdk: integer >= 24 <= 9007199254740991
}
}) exactly one variant)[] = []
assetMemoryPolicies?: ({
id: string length >= 1
label: string length >= 1
basePreset: "low" | "balanced" | "high"
overrides?: {
preparedCpuBytes?: integer >= 1 <= 9007199254740991
gpuBytes?: integer >= 1 <= 9007199254740991
audioBytes?: integer >= 1 <= 9007199254740991
temporaryBytes?: integer >= 1048576 <= 9007199254740991
warmPreparedCpuBytes?: integer >= 0 <= 9007199254740991
warmGpuBytes?: integer >= 0 <= 9007199254740991
warmAudioBytes?: integer >= 0 <= 9007199254740991
} = {}
})[] = []
}
bootstrapModule: ScriptRef
undefinedInteractionProgram?: {
instructions: GameplayCommand[]
completion: FlowTarget
outcome: "handled" | "unhandled"
} | null = null
entrypoint?: ({
kind: "room"
id: EntityId
} | {
kind: "scene"
id: EntityId
} | {
kind: "dialogue"
id: EntityId
}) exactly one variant | null = null
inventories: [{
id: EntityId
label: string length >= 1
}] items >= 1 items <= 1
interactableInstances: {
[key: EntityId]: InteractableInstance
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
CursorId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
CursorTarget = ({
kind: "system"
cursor: "default" | "pointer" | "text" | "wait" | "progress" | "crosshair" | "move" | "not-allowed" | "ns-resize" | "ew-resize" | "nesw-resize" | "nwse-resize"
} | {
kind: "named"
id: CursorId
} | {
kind: "none"
}) exactly one variant
ScriptRef = {
$ref: {
collection: "scripts"
id: EntityId
}
}
VariableRef = {
$ref: {
collection: "variables"
id: EntityId
}
}
RuntimeScalar = null | boolean | number | string
RoomRef = {
$ref: {
collection: "rooms"
id: EntityId
}
}
CharacterRef = {
$ref: {
collection: "characters"
id: EntityId
}
}
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: RoomRef
} | {
kind: "character"
character: CharacterRef
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: RoomRef
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
TraitRef = {
$ref: {
collection: "traits"
id: EntityId
}
}
LocationSubjectOperand = ({
kind: "character"
character: CharacterRef
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: RoomRef
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: CharacterRef
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: RoomRef
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: CharacterRef
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: RoomRef
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
ArchetypeRef = {
$ref: {
collection: "archetypes"
id: EntityId
}
}
GameplayConfigurationSource = ({
kind: "archetype"
archetype: ArchetypeRef
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
InteractableRef = {
$ref: {
collection: "interactables"
id: EntityId
}
}
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: InteractableRef
traits?: TraitRef[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
LayoutRef = {
$ref: {
collection: "layouts"
id: EntityId
}
}
SceneRef = {
$ref: {
collection: "scenes"
id: EntityId
}
}
DialogueRef = {
$ref: {
collection: "dialogues"
id: EntityId
}
}
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
Condition = ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: VariableRef
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: TraitRef
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: VariableRef
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: VariableRef
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: TraitRef
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: TraitRef
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: InteractableRef
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: InteractableRef
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: LayoutRef
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: SceneRef
} | {
id: EntityId
kind: "call-dialogue"
dialogue: DialogueRef
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
FlowTarget = ({
kind: "scene"
id: EntityId
} | {
kind: "dialogue"
id: EntityId
} | {
kind: "room"
id: EntityId
} | {
kind: "return"
} | {
kind: "end"
}) exactly one variant
InteractableLocation = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomRef
} | {
kind: "inventory"
inventory: InventoryRef
}) exactly one variant
MaterialRef = {
$ref: {
collection: "materials"
id: EntityId
}
}
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: {
$ref: {
collection: "assets"
id: string length >= 1
}
}
}
MaterialApplicationSpecialization = {
material: MaterialRef | null
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}
InteractableInstance = {
id: EntityId
definition: {
$ref: {
collection: "interactables"
id: EntityId
}
}
editorLabel?: string length >= 1
location: InteractableLocation
enabled: boolean
visible: boolean
quantity: integer >= 1 <= 9007199254740991
traits: {
add: EntityId[]
remove: EntityId[]
}
localProperties: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
value: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
materialApplication: MaterialApplicationSpecialization
featureOverrides: ({
featureId: EntityId
traits: {
add: EntityId[]
remove: EntityId[]
}
properties: ({
propertyId: EntityId
value: null | boolean | number | string | {
$message: NamedMessageKey
}
})[]
})[]
}ProjectWorkspaceManifest.settings.distributionNotices
Unconditional Project-wide distribution notices. Asset attachment notices are included only when their physical Asset is packaged.
ProjectWorkspaceManifest.settings.distributionNotices[item].path
Safe Project-relative path to an original UTF-8 plain-text notice file; .txt and .md only, at most 1 MiB.
ProjectWorkspaceManifest.settings.distributionNotices[item].displayName
Optional notice viewer name; shared paths must agree on explicit names.
ProjectWorkspaceManifest.settings.accessibility.uiScale
- Constraint: minimum must be less than or equal to maximum.
- Constraint: When enabled is true, the inclusive range [minimum, maximum] must contain the default scale 1.
ProjectWorkspaceManifest.settings.accessibility.textScale
- Constraint: minimum must be less than or equal to maximum.
- Constraint: When enabled is true, the inclusive range [minimum, maximum] must contain the default scale 1.
ProjectWorkspaceManifest.undefinedInteractionProgram
Project-owned fallback in project.yaml, tried after the selected Verb defaultProgram returns Unhandled. Null skips to the engine undefined-interaction response; a handled Project program stops the chain.
Project fallback that declines to the engine response
{
"instructions": [],
"completion": {
"kind": "return"
},
"outcome": "unhandled"
}ProjectWorkspaceManifest.undefinedInteractionProgram[1].outcome
Handled stops fallback. Unhandled requires empty behavior with no committed work and advances to the next fallback stage for the host of this program. The complete chain is selected rule → Verb defaultProgram → Project undefinedInteractionProgram → engine response. Runtime failure aborts without fallback and does not rewind earlier observable boundaries.
InteractableLocation
- Location is authoritative semantic membership. Room visual occurrences do not change it; Unplaced Instances still exist.
- Related: Room, Inventory, Interactable Instance
Unplaced
{
"kind": "unplaced"
}In a Room
{
"kind": "room",
"room": {
"$ref": {
"collection": "rooms",
"id": "hall"
}
}
}InteractableInstance
One exact live identity, not an aggregate count of its Definition.
Unplaced key Instance
Source: editor/src/shared/project-schema/examples/interactable-instance.json
{
"id": "brass-key-1",
"definition": {
"$ref": {
"collection": "interactables",
"id": "brass-key"
}
},
"location": {
"kind": "unplaced"
},
"enabled": true,
"visible": true,
"quantity": 1,
"traits": {
"add": [],
"remove": []
},
"localProperties": [],
"materialApplication": {
"material": null,
"parameters": {},
"textures": {}
},
"featureOverrides": []
}InteractableInstance.quantity
- Constraint: A declared non-stackable Instance must have quantity 1. A stackable Instance must not exceed its Definition stackLimit when non-null.
Traits
Raw JSON SchemaShape and constraints
Traits = {
[key: EntityId]: {
id: EntityId
label: string length >= 1
description?: string
ownerKinds: ("room" | "character" | "interactable" | "feature")[] items >= 1
properties: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
defaultValue?: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
}
} = {}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"Traits[key]
- Constraint: When multiple attached Traits contribute the same Property ID, their Property schemas must be compatible.
- Constraint: When multiple attached Traits provide a Default for the same Property ID, those Defaults must agree exactly.
- Constraint: Reusable definitions and Archetypes may leave no-Default Trait requirements unresolved; concrete gameplay owners and Interactable Instances must resolve every required Property before publication.
Traits[key].properties[item]
- Omitting defaultValue leaves a Property requirement. Reusable configuration may leave that requirement unresolved, but a concrete gameplay owner or Interactable Instance must resolve an effective compatible value before publication.
Localization
Raw JSON SchemaShape and constraints
Localization = {
sourceLocale: string length >= 1
sourceLocaleLock?: string length >= 1 | null = null
defaultLocale: string length >= 1
locales: {
[key: string length >= 1]: {
supported: boolean
parentLocale: string length >= 1 | null
fontStack: ({
$ref: {
collection: "assets"
id: string length >= 1
}
})[] | null
displayName?: string length >= 1
}
}
messages: {
[key: MessageId]: ({
kind: "local"
source: string
pattern?: MessagePattern
context?: string
translatorNote?: string
arguments?: {
[key: MessageArgumentName]: "printable" | "string" | "number" | "integer" | "plural-number"
}
} | {
kind: "named"
key: NamedMessageKey
source: string
pattern?: MessagePattern
context?: string
translatorNote?: string
arguments?: {
[key: MessageArgumentName]: "printable" | "string" | "number" | "integer" | "plural-number"
}
}) exactly one variant
}
structuredMessageIds: {
[key: string length >= 1]: MessageId
}
usageNotes: {
[key: string length >= 1]: string
}
sourceMessageTracking: {
[key: string length >= 1]: {
family: "lua" | "rml"
ownerKey: string length >= 1
sourcePath: string length >= 1
sourceSnapshotFingerprint: LocalizationSourceTrackingFingerprint
occurrences: ({
messageId: MessageId
ordinal: integer >= 0 <= 9007199254740991
structuralFingerprint: LocalizationSourceTrackingFingerprint
anchorFingerprint: LocalizationSourceTrackingFingerprint
sourceFingerprint: LocalizationSourceTrackingFingerprint
sourceSnapshot: string
contextSnapshot?: string
translatorNoteSnapshot?: string
})[]
}
}
orphanedMessages: {
[key: MessageId]: {
family: "lua" | "rml"
ownerKey: string length >= 1
sourcePath: string length >= 1
sourceSnapshotFingerprint: LocalizationSourceTrackingFingerprint
occurrence: {
messageId: MessageId
ordinal: integer >= 0 <= 9007199254740991
structuralFingerprint: LocalizationSourceTrackingFingerprint
anchorFingerprint: LocalizationSourceTrackingFingerprint
sourceFingerprint: LocalizationSourceTrackingFingerprint
sourceSnapshot: string
contextSnapshot?: string
translatorNoteSnapshot?: string
}
translations: {
[key: string length >= 1]: {
text: string
pattern?: MessagePattern
dialogueCues?: ({
id: string length >= 1
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
})[]
sourceFingerprint: LocalizationWorkflowFingerprint
origin: "human" | "ai" | "imported" | "unknown"
review: "needs-review" | "reviewed"
provider?: string length >= 1
model?: string length >= 1
acknowledgedPresentationFingerprint?: LocalizationWorkflowFingerprint
acknowledgedGuidanceFingerprint?: LocalizationWorkflowFingerprint
useSource?: true
}
}
}
}
translations: {
[key: string length >= 1]: {
[key: MessageId]: {
text: string
pattern?: MessagePattern
dialogueCues?: ({
id: string length >= 1
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
})[]
sourceFingerprint: LocalizationWorkflowFingerprint
origin: "human" | "ai" | "imported" | "unknown"
review: "needs-review" | "reviewed"
provider?: string length >= 1
model?: string length >= 1
acknowledgedPresentationFingerprint?: LocalizationWorkflowFingerprint
acknowledgedGuidanceFingerprint?: LocalizationWorkflowFingerprint
useSource?: true
}
}
}
assets: {
[key: string length >= 1]: {
[key: string length >= 1]: {
useSource: true
} | {
asset: {
$ref: {
collection: "assets"
id: string length >= 1
}
}
sourceFingerprint: LocalizationWorkflowFingerprint
origin: "human" | "ai" | "imported" | "unknown"
review: "needs-review" | "reviewed"
provider?: string length >= 1
model?: string length >= 1
}
}
}
}
MessageId = string pattern "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$" format uuid
MessageSelectorArgumentName = string pattern "^[A-Za-z_][A-Za-z0-9_-]*$"
MessagePattern = ({
kind: "text"
text: string
} | {
kind: "plural"
argument: MessageSelectorArgumentName
cases: {
[key: "zero" | "one" | "two" | "few" | "many" | "other"]: MessagePattern
}
} | {
kind: "select"
argument: MessageSelectorArgumentName
cases: {
[key: string length >= 1]: MessagePattern
}
}) exactly one variant
MessageArgumentName = string pattern "^[A-Za-z_][A-Za-z0-9_-]*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
LocalizationSourceTrackingFingerprint = string pattern "^fnv1a:[0-9a-f]{32}$"
LocalizationWorkflowFingerprint = string pattern "^fnv1a:[0-9a-f]{32}$"MessagePattern
- Constraint: Every plural pattern must contain an 'other' case.
- Constraint: Every select pattern must contain an 'other' fallback case.
Plural with nested select fallback
{
"kind": "plural",
"argument": "count",
"cases": {
"one": {
"kind": "text",
"text": "One item"
},
"other": {
"kind": "select",
"argument": "audience",
"cases": {
"formal": {
"kind": "text",
"text": "{count} items"
},
"other": {
"kind": "text",
"text": "{count} things"
}
}
}
}
}Tracked editor metadata
Raw JSON SchemaShape and constraints
TrackedEditorMetadata = {
chapters: {
records?: {
[key: string]: {
id: string length >= 1
label: string length >= 1
color?: string | null
sortKey?: string | null
}
} = {}
assignments?: {
[key: string]: string[]
} = {}
}
tags: {
records?: {
[key: string]: {
name: string length >= 1
color: string length >= 1
sortKey?: string | null
}
} = {}
}
recordMetadata: {
[key: string]: {
[key: string]: {
tags?: string[] = []
color?: string | null
sortKey?: string | null
}
}
}
}Assets record
Raw JSON SchemaShape and constraints
AssetsRecord = {
id: EntityId
label: string length >= 1
description?: string
data: {
kind: "image" | "font" | "audio" | "video" | "text" | "data" | "binary"
source: {
type: "project-file"
path: string length >= 1
}
attachments?: ({
path: string
purpose: "distribution-notice" | "authoring-source" | "reference" | "other"
displayName?: string length >= 1 length <= 256
sourceBaselineHash?: string pattern "^sha256:[0-9a-f]{64}$"
assetBaselineHash?: string pattern "^sha256:[0-9a-f]{64}$"
})[]
aliases?: string[] = []
sampling?: "linear" | "nearest"
mimeType?: string
extension?: string
byteSize?: number >= 0
contentHash?: string
importedAt?: string
originalName?: string
originalPath?: string
imageMetadata: {
width: integer >= 1 <= 65535
height: integer >= 1 <= 65535
hasAlpha: boolean
orientation: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
} | null
preview?: {
thumbnailRevision?: string
durationSeconds?: number >= 0
}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"AssetsRecord.data
- Constraint: Image Assets require a non-null imageMetadata object.
- Constraint: Every non-image Asset requires imageMetadata to be null.
- Constraint: attachments is an optional collection of explicit Project-relative file relationships, not registered Assets.
- Constraint: Each attachment has one purpose: distribution-notice, authoring-source, reference, or other; displayName is optional.
- Constraint: Each Asset may associate a physical attachment path only once; paths must exclude local, generated, and VCS directories.
Animations record
Raw JSON SchemaShape and constraints
AnimationsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: Animation
import?: {
format: "image-sequence" | "gif" | "apng"
sources: ({
path: string length >= 1
contentHash: string length >= 1
originalName: string length >= 1
})[] items >= 1
frameDurationMs: integer >= 1 <= 9007199254740991
importedAt: string length >= 1
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
AnimationCanvas = {
width: integer >= 1 <= 10000
height: integer >= 1 <= 10000
}
Animation = {
canvas: AnimationCanvas
defaultMotionId: EntityId
motions: (({
id: EntityId
kind: "sprite-sequence"
frames: ({
image: EntityId
durationMs: integer >= 1 <= 9007199254740991
})[]
markers: ({
id: EntityId
timeMs: integer >= 0 <= 9007199254740991
})[]
} | {
id: EntityId
kind: "video"
video: EntityId
sourceRange?: {
startMs: integer >= 0 <= 9007199254740991
endMs: integer >= 1 <= 9007199254740991
}
frameSampling?: ({
mode: "source"
} | {
mode: "target-fps"
fps: {
numerator: integer >= 1 <= 9007199254740991
denominator: integer >= 1 <= 9007199254740991
}
} | {
mode: "retain-percent"
percent: integer >= 1 <= 100
}) exactly one variant
markers: ({
id: EntityId
timeMs: integer >= 0 <= 9007199254740991
})[]
}) exactly one variant)[] items >= 1
}AnimationCanvas
- The logical canvas is stable across every motion and frame. Source image dimensions do not change world placement.
Animation
- Animation is immutable reusable raster presentation content. Playback phase belongs to presentation realization, not this resource.
- Motion IDs are scoped to the Animation. Sprite frames name Image Assets with explicit positive durations; video motions name semantic Video Assets and may select an in/out source range.
Variables record
Raw JSON SchemaShape and constraints
VariablesRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable?: boolean = false
value: null | boolean | number | string | {
$message: NamedMessageKey
}
scope?: "global" = "global"
enumValues?: string[]
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"VariablesRecord.data
- Constraint: value must match the declared type and nullability.
- Constraint: Enum Variables require at least one non-empty enumValues entry; entries must be unique. Non-null values must equal one declared entry; null is permitted when nullable is true.
Materials record
Raw JSON SchemaShape and constraints
MaterialsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
base: ({
kind: "preset"
preset: "engine-2d" | "active-text" | "rmlui-decorator" | "postprocess-tint" | "hotspot-overlay-alpha" | "hotspot-overlay-custom"
} | {
kind: "material"
material: EntityId
}) exactly one variant
displayName?: string
shader?: {
vertex?: {
kind: "project"
path: string pattern "^shaders\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\)(?!.*\\/\\/)[^/].*$"
} | {
kind: "engine"
path: string pattern "^engine:\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\).+$"
}
fragment?: {
kind: "project"
path: string pattern "^shaders\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\)(?!.*\\/\\/)[^/].*$"
} | {
kind: "engine"
path: string pattern "^engine:\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\).+$"
}
varying?: {
kind: "project"
path: string pattern "^shaders\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\)(?!.*\\/\\/)[^/].*$"
} | {
kind: "engine"
path: string pattern "^engine:\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\).+$"
}
}
parameters?: {
[key: string length >= 1]: {
type?: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
value?: ShaderUniformValue
binding?: "engine.time" | "engine.paint_dimensions" | "engine.reference_to_world_raster_scale" | "engine.context_logical_to_raster_scale" | "engine.viewport_pixel_dimensions" | "engine.pointer_position" | "engine.pointer_valid" | "rmlui.media_query_resolution" | "engine.hotspot_bounds" | "engine.hotspot_hovered" | "engine.hotspot_pressed" | "engine.hotspot_image_dimensions" | "engine.hotspot_mask_dimensions" | null
editor?: {
label?: string
range?: [number, number] items >= 2 items <= 2
control?: "number" | "slider" | "color" | "toggle" | "vector"
}
}
} = {}
textures?: {
[key: string length >= 1]: {
source?: string length >= 1 | {
alias: string length >= 1
} | {
uri: string length >= 1
}
address?: "clamp" | "repeat"
filter?: "inherit" | "nearest" | "linear"
binding?: "engine.hotspot_image" | "engine.hotspot_mask" | null
editor?: {
label?: string
}
}
} = {}
preview?: {
geometry?: "quad" | "rounded-rect" | "sprite" | "glyphs"
background?: "transparent" | "checker" | "dark" | "light"
}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}MaterialsRecord.data.shader
- Constraint: A custom shader override must set at least one of vertex, fragment, or varying.
MaterialsRecord.data.parameters[key]
- Constraint: A Material parameter override must set at least one override field.
MaterialsRecord.data.textures[key]
- Constraint: A Material texture override must set at least one override field.
Layouts record
Raw JSON SchemaShape and constraints
LayoutsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
layoutKind?: "document" | "fragment" = "document"
displayName?: string
target?: "default-ui" | "dialogue-ui" | "scene-overlay" | "room-overlay" | "menu-ui" | "custom-overlay" = "default-ui"
scalePolicy?: {
ui: "inherit" | "ignore"
text: "inherit" | "ignore"
}
contract?: {
inputs?: {
[key: LayoutContractId]: {
type: "boolean" | "integer" | "number" | "string"
nullable?: boolean = false
defaultValue?: null | boolean | number | string
}
} = {}
signals?: {
[key: LayoutContractId]: {
fields?: {
[key: LayoutContractId]: {
type: "boolean" | "integer" | "number" | "string"
nullable?: boolean = false
required?: boolean = true
}
} = {}
}
} = {}
state?: LayoutStateShape
} = {"inputs":{},"signals":{}}
rml: ({
sourceMode: "file"
} | {
sourceMode: "asset"
sourceAsset: string length >= 1
} | {
sourceMode: "none"
}) exactly one variant
rcss: ({
sourceMode: "file"
} | {
sourceMode: "asset"
sourceAsset: string length >= 1
} | {
sourceMode: "none"
}) exactly one variant
lua: ({
sourceMode: "file"
} | {
sourceMode: "none"
}) exactly one variant
script?: {
enabled?: boolean = true
namespace?: string
additionalDependencies?: LuaExplicitDependencies
} = {"enabled":true}
mount?: {
defaultParent?: string
scopedStyles?: boolean = true
} = {"scopedStyles":true}
dependencies?: {
images?: (string length >= 1)[] = []
fonts?: (string length >= 1)[] = []
stylesheets?: (string length >= 1)[] = []
materials?: MaterialApplication[] = []
scripts?: (string pattern "^scripts\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\)(?!.*\\/\\/)[^/].*\\.lua$")[] = []
templates?: (string length >= 1)[] = []
data?: (string length >= 1)[]
} = {"images":[],"fonts":[],"stylesheets":[],"materials":[],"scripts":[],"templates":[]}
sampleState?: {
[key: string]: JsonValue
} = {}
preview?: {
background?: "transparent" | "checker" | "dark" | "light" = "dark"
} = {"background":"dark"}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
LayoutContractId = string pattern "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$"
LayoutPersistableValue = null | boolean | number | string | LayoutPersistableValue[] | {
[key: string length >= 1]: LayoutPersistableValue
}
LayoutStateShape = ({
type: "boolean" | "integer" | "number" | "string"
nullable?: boolean = false
defaultValue?: LayoutPersistableValue
} | {
type: "array"
nullable?: boolean = false
items: LayoutStateShape
defaultValue?: LayoutPersistableValue
} | {
type: "object"
nullable?: boolean = false
fields?: {
[key: LayoutContractId]: {
required?: boolean = true
shape: LayoutStateShape
}
} = {}
defaultValue?: LayoutPersistableValue
}) exactly one variant
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: string length >= 1
}
MaterialApplication = {
material: EntityId
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}
JsonValue = null | boolean | number | string | JsonValue[] | {
[key: string]: JsonValue
}LayoutsRecord.data.contract.inputs[key]
- Constraint: Layout input defaultValue must match the declared type and nullability.
LayoutsRecord.data.rml
- Constraint: For RML/RCSS, sourceMode 'file' owns the companion records/layouts/<layout-id>/layout.rml or layout.rcss file respectively.
- Constraint: When an RML/RCSS selector is not 'file', the corresponding companion file must be absent.
LayoutsRecord.data.rcss
- Constraint: For RML/RCSS, sourceMode 'file' owns the companion records/layouts/<layout-id>/layout.rml or layout.rcss file respectively.
- Constraint: When an RML/RCSS selector is not 'file', the corresponding companion file must be absent.
LayoutsRecord.data.lua
- Constraint: For Lua, sourceMode 'file' owns records/layouts/<layout-id>/layout.lua; sourceMode 'none' requires that companion file to be absent.
LayoutStateShape
- Constraint: Every State Shape defaultValue must recursively match the declared shape, required fields, and nullability.
Archetypes record
Raw JSON SchemaShape and constraints
ArchetypesRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
instanceKind: "room" | "character" | "interactable"
base?: EntityId | null = null
overrides?: {
[key: string]: JsonValue
} = {}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
JsonValue = null | boolean | number | string | JsonValue[] | {
[key: string]: JsonValue
}ArchetypesRecord.data
- Constraint: Each overrides key is a JSON Pointer into the inherited configuration and must be rooted under /data, /traits, or /defaultProperties.
- Constraint: Character /data/initialWorldState is instance-local and cannot be inherited or changed through Archetype overrides.
Override inherited Room configuration
{
"kind": "archetype",
"instanceKind": "room",
"base": null,
"overrides": {
"/data/displayName": "Shared Hall",
"/traits": [
"inspectable"
]
}
}Characters record
Raw JSON SchemaShape and constraints
CharactersRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
archetype?: EntityId | null
archetypeOverrides?: {
[key: string]: JsonValue
}
traits?: EntityId[]
localProperties?: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
value: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
data: {
displayName?: string = ""
dialogue?: {
name?: string = ""
nameColor?: string | null = null
textColor?: string | null = null
styleClass?: string = ""
} = {"name":"","nameColor":null,"textColor":null,"styleClass":""}
defaults: {
profileId: EntityId
expressionId: EntityId
appearanceId?: EntityId | null = null
idleId?: EntityId | null = null
}
profiles?: ({
id: EntityId
label: string length >= 1
layers?: ({
id: EntityId
label: string length >= 1
role?: string length >= 1 | null = null
})[] = []
defaultPoseId: EntityId
poses?: ({
id: EntityId
label: string length >= 1
layers?: ({
layerId: EntityId
visual?: Visual | null = null
materialApplication?: MaterialApplication | null = null
offset?: {
x: number
y: number
} = {"x":0,"y":0}
scale?: number > 0 = 1
anchor?: {
x: number
y: number
} = {"x":0.5,"y":1}
visible?: boolean = true
})[] = []
})[] = []
animationClips?: ({
id: EntityId
label: string length >= 1
clock: "gameplay" | "unscaled-presentation"
frames: ({
durationMs: integer >= 1 <= 9007199254740991
layers?: ({
layerId: EntityId
visual?: Visual | null
materialApplication?: MaterialApplication | null
offset?: {
x: number
y: number
}
scale?: number > 0
anchor?: {
x: number
y: number
}
visible?: boolean
})[] = []
})[] items >= 1
})[] = []
automaticAnimations?: {
blink?: {
clipId: EntityId
role: string length >= 1
intervalMs: integer >= 1 <= 9007199254740991
} | null = null
speaking?: {
clipId: EntityId
role: string length >= 1
} | null = null
} = {"blink":null,"speaking":null}
})[] = []
expressions?: ({
id: EntityId
label: string length >= 1
profiles?: ({
profileId: EntityId
layers?: ({
layerId: EntityId
visual?: Visual | null
materialApplication?: MaterialApplication | null
visible?: boolean
})[] = []
})[] = []
})[] = []
appearances?: ({
id: EntityId
label: string length >= 1
profiles?: ({
profileId: EntityId
layers?: ({
layerId: EntityId
visual?: Visual | null
materialApplication?: MaterialApplication | null
visible?: boolean
})[] = []
})[] = []
})[] = []
gestures?: ({
id: EntityId
label: string length >= 1
profiles?: ({
profileId: EntityId
clipId: EntityId
cues?: (({
kind: "presentation"
id: EntityId
atMs: integer >= 0 <= 9007199254740991
event: EntityId
} | {
kind: "audio"
id: EntityId
atMs: integer >= 0 <= 9007199254740991
asset: string length >= 1
gain?: number >= 0 <= 1 = 1
pan?: number >= -1 <= 1 = 0
}) exactly one variant)[] = []
})[] = []
})[] = []
idles?: ({
id: EntityId
label: string length >= 1
kind: "bob" | "sway" | "pulse"
amplitude: number >= 0
periodMs: integer >= 1 <= 9007199254740991
clock: "gameplay" | "unscaled-presentation"
})[] = []
inventories?: ({
id: EntityId
label: string length >= 1
})[] = []
initialWorldState?: {
location?: ({
kind?: "unplaced" = "unplaced"
} | {
kind?: "room" = "unplaced"
room: EntityId
}) exactly one variant = {"kind":"unplaced"}
enabled?: boolean = true
visible?: boolean = true
} = {"location":{"kind":"unplaced"},"enabled":true,"visible":true}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
JsonValue = null | boolean | number | string | JsonValue[] | {
[key: string]: JsonValue
}
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
Visual = ({
kind: "image"
image: EntityId
} | {
kind: "animation"
animation: EntityId
motionId?: EntityId | null = null
playback: {
repeat: "once" | "loop"
rate: number > 0
clock: "gameplay" | "unscaled-presentation"
initialMarker: EntityId | null
loopRange?: {
start: EntityId
end: EntityId
}
} | null
}) exactly one variant
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: string length >= 1
}
MaterialApplication = {
material: EntityId
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}CharactersRecord.data
- Constraint: A Character must contain at least one presentation Profile and at least one Expression, even though omitted fields normalize to empty arrays during editing.
CharactersRecord.data.profiles[item]
- Constraint: A Character presentation Profile must contain at least one layer and at least one pose, even though omitted fields normalize to empty arrays during editing.
Visual
- A Visual is initially either a static Image Asset or a raster Animation. An Animation Visual with no motionId uses the Animation default motion.
Rooms record
Raw JSON SchemaShape and constraints
RoomsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
archetype?: EntityId | null
archetypeOverrides?: {
[key: string]: JsonValue
}
traits?: EntityId[]
localProperties?: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
value: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
data: {
displayName: string
background?: {
asset?: EntityId | null = null
materialApplication?: MaterialApplication | null = null
fit?: "cover" | "contain" | "stretch" | "center" = "cover"
color?: string | null = null
} = {"asset":null,"materialApplication":null,"fit":"cover","color":null}
description?: TextContent = {"source":{"kind":"inline","text":""},"markup":"active-text"}
presentationSpace: {
size: {
width: number > 0
height: number > 0
}
bounds?: {
x: number
y: number
width: number > 0
height: number > 0
} | null = null
edgePolicy?: "contain" | "overscan" = "contain"
defaultView: {
center: {
x: number
y: number
}
zoom?: number > 0 = 1
rotationDegrees?: number = 0
}
views?: ({
id: EntityId
view: {
center: {
x: number
y: number
}
zoom: number > 0
rotationDegrees: number
}
})[] = []
}
anchors?: ({
id: EntityId
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
})[] = []
overlays?: ({
id: EntityId
layout: EntityId
condition?: Condition = "always"
visible?: boolean = true
order?: integer >= -2147483648 <= 2147483647 = 0
})[] = []
cast?: ({
id: EntityId
character: EntityId
condition?: Condition = "always"
placementId: EntityId
profileId?: EntityId | null
poseId?: EntityId | null = null
expressionId?: EntityId | null = null
appearanceId?: EntityId | null
idleId?: EntityId | null = null
visible?: boolean = true
order?: integer >= -2147483648 <= 2147483647 = 0
})[] = []
props?: ({
id: EntityId
condition?: Condition = "always"
placementId: EntityId
asset?: EntityId | null = null
materialApplication?: MaterialApplication | null = null
visible?: boolean = true
order?: integer >= -2147483648 <= 2147483647 = 0
})[] = []
interactables?: ({
id: EntityId
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
condition?: Condition = "always"
placementId: EntityId
visible?: boolean = true
order?: integer >= -2147483648 <= 2147483647 = 0
})[] = []
environments?: ({
id: EntityId
condition?: Condition = "always"
visual: Visual | null
materialApplication: MaterialApplication
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
plane?: "world-background" | "world-content" | "world-overlay" = "world-content"
order?: integer >= -2147483648 <= 2147483647 = 0
clock?: "gameplay" | "unscaled-presentation" = "gameplay"
scrollPerSecond?: {
x?: number = 0
y?: number = 0
} = {"x":0,"y":0}
opacity?: number >= 0 <= 1 = 1
visible?: boolean = true
})[] = []
scriptHooks?: ({
hook: "can-enter" | "can-leave" | "reject-enter" | "reject-leave" | "before-enter" | "after-enter" | "before-leave" | "after-leave" | "compose"
handler: {
module: EntityId
export: string length >= 1
}
})[] = []
lifecycle?: {
canEnter?: Condition = "always"
canLeave?: Condition = "always"
beforeEnter?: GameplayCommand[] = []
afterEnter?: GameplayCommand[] = []
beforeLeave?: GameplayCommand[] = []
afterLeave?: GameplayCommand[] = []
onEnterRejected?: GameplayCommand[] = []
onLeaveRejected?: GameplayCommand[] = []
} = {"canEnter":"always","canLeave":"always","beforeEnter":[],"afterEnter":[],"beforeLeave":[],"afterLeave":[],"onEnterRejected":[],"onLeaveRejected":[]}
exits?: ({
id: EntityId
label: string length >= 1
direction: "northwest" | "north" | "northeast" | "west" | "east" | "southwest" | "south" | "southeast" | "custom"
target: EntityId
condition?: Condition = "always"
transition?: {
kind: "cut" | "fade" | "dissolve"
durationMs: integer >= 0 <= 9007199254740991
color: string | null
skippable: boolean
} | null
onRejected?: GameplayCommand[] = []
})[] = []
placements?: ({
id: EntityId
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
presentation: {
label: TextContent | null
layout: null
} | {
label: TextContent | null
layout: EntityId
layoutOrder: integer >= -2147483648 <= 2147483647
}
})[] = []
fallbackInteractablePlacementId?: EntityId | null = null
features?: ({
id: EntityId
label: string length >= 1
traits: EntityId[]
localProperties: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
value: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
defaultProperties: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
defaultValue?: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
inventories: ({
id: EntityId
label: string length >= 1
})[]
})[] = []
hotspots?: ({
id: EntityId
label: string length >= 1
condition: Condition
inputOrder: integer >= -2147483648 <= 2147483647
highlight: ({
kind: "default"
} | {
kind: "material"
materialApplication: MaterialApplication
} | {
kind: "none"
}) exactly one variant
cursor?: CursorTarget | null
shape: {
kind: "rect"
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
}
target: ({
kind: "none"
} | {
kind: "owner-feature"
featureId: EntityId
} | {
kind: "subject"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
} | {
kind: "exit"
exitId: EntityId
}) exactly one variant
})[] = []
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
JsonValue = null | boolean | number | string | JsonValue[] | {
[key: string]: JsonValue
}
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: string length >= 1
}
MaterialApplication = {
material: EntityId
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
Visual = ({
kind: "image"
image: EntityId
} | {
kind: "animation"
animation: EntityId
motionId?: EntityId | null = null
playback: {
repeat: "once" | "loop"
rate: number > 0
clock: "gameplay" | "unscaled-presentation"
initialMarker: EntityId | null
loopRange?: {
start: EntityId
end: EntityId
}
} | null
}) exactly one variant
GameplayConfigurationSource = ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: EntityId
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: EntityId
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: EntityId
} | {
id: EntityId
kind: "call-dialogue"
dialogue: EntityId
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
CursorId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
CursorTarget = ({
kind: "system"
cursor: "default" | "pointer" | "text" | "wait" | "progress" | "crosshair" | "move" | "not-allowed" | "ns-resize" | "ew-resize" | "nesw-resize" | "nwse-resize"
} | {
kind: "named"
id: CursorId
} | {
kind: "none"
}) exactly one variantRoomsRecord.data.interactables[item]
- This is Room-local presentation linkage for one exact declared Interactable Instance. The referenced Instance owns Location, quantity, enabled/visible state, Traits, and Properties.
- Related: InteractableInstance, InteractableLocation, Room placement
RoomsRecord.data.lifecycle
- Lifecycle: canEnter/canLeave are Exploration guards.
- Lifecycle: beforeEnter/beforeLeave run before the Room switch commits and therefore admit only immediate commands.
- Lifecycle: Pre-commit eligibility is checked recursively through if branches. call-scene, call-dialogue, notify, and run-lua are Flow-capable and forbidden here regardless of Lua source; use an immediate before-enter/before-leave Script Hook for Lua pre-commit work.
- Lifecycle: afterEnter/afterLeave run after the Room switch commits and may use Flow-capable commands.
- Lifecycle: onEnterRejected runs when target canEnter rejects; onLeaveRejected runs when source canLeave rejects and is also the fallback for an Exit rejection without its own onRejected commands.
- Lifecycle: Exploration rejection keeps the Current Room unchanged while rejection commands execute.
RoomsRecord.data.placements[item]
- Placement bounds are normalized to the Room presentation-space reference frame. They do not change when the Room background uses cover, contain, stretch, or center fitting.
RoomsRecord.data.features[item]
- Constraint: Room Features are concrete gameplay identities: defaultProperties must be empty and Property values belong in localProperties.
- Constraint: Interactable-definition Features are reusable configuration: localProperties must be empty and Property defaults belong in defaultProperties.
- Related: Room Feature, Interactable-definition Feature, Property
RoomsRecord.data.hotspots[item]
- Rect bounds are normalized to the complete Room background source image in image/UV space, not to the fitted on-screen background rectangle.
- A Hotspot selects a semantic target; it does not own the target's Verb or Interaction behavior.
RoomsRecord.data.hotspots[item].target[3].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
Visual
- A Visual is initially either a static Image Asset or a raster Animation. An Animation Visual with no motionId uses the Animation default motion.
Interactables record
Raw JSON SchemaShape and constraints
InteractablesRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
archetype?: EntityId | null
archetypeOverrides?: {
[key: string]: JsonValue
}
traits?: EntityId[]
defaultProperties?: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
defaultValue?: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[]
data: {
displayName: string
stackable: boolean
stackLimit: integer >= 1 <= 9007199254740991 | null
presentation?: {
visual?: Visual | null = null
materialApplication?: MaterialApplication | null = null
cursor?: CursorTarget | null
hotspots?: ({
kind: "none"
} | {
kind: "visual-alpha"
hotspot: {
id: EntityId
label: string length >= 1
condition: Condition
inputOrder: integer >= -2147483648 <= 2147483647
highlight: ({
kind: "default"
} | {
kind: "material"
materialApplication: MaterialApplication
} | {
kind: "none"
}) exactly one variant
target: ({
kind: "none"
} | {
kind: "owner"
} | {
kind: "owner-feature"
featureId: EntityId
} | {
kind: "subject"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
}) exactly one variant
}
} | {
kind: "custom"
hotspots: ({
id: EntityId
label: string length >= 1
condition: Condition
inputOrder: integer >= -2147483648 <= 2147483647
highlight: ({
kind: "default"
} | {
kind: "material"
materialApplication: MaterialApplication
} | {
kind: "none"
}) exactly one variant
cursor?: CursorTarget | null
target: ({
kind: "none"
} | {
kind: "owner"
} | {
kind: "owner-feature"
featureId: EntityId
} | {
kind: "subject"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
}) exactly one variant
shape: {
kind: "rect"
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
motionTracks?: ({
motionId: EntityId
keyframes: ({
timeMs: integer >= 0 <= 9007199254740991
interpolation: "hold" | "linear"
active: boolean
bounds: {
x: number >= 0 <= 1
y: number >= 0 <= 1
width: number > 0 <= 1
height: number > 0 <= 1
}
})[] items >= 1
})[]
}
})[]
}) exactly one variant = {"kind":"none"}
} = {"visual":null,"materialApplication":null,"hotspots":{"kind":"none"}}
features?: ({
id: EntityId
label: string length >= 1
traits?: EntityId[] = []
localProperties?: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
value: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[] = []
defaultProperties?: ({
id: EntityId
label?: string length >= 1
description?: string
type: "boolean" | "integer" | "number" | "string" | "enum" | "message"
nullable: boolean
defaultValue?: null | boolean | number | string | {
$message: NamedMessageKey
}
enumValues?: (string length >= 1)[]
})[] = []
inventories?: ({
id: EntityId
label: string length >= 1
})[] = []
})[] = []
inventories?: ({
id: EntityId
label: string length >= 1
})[] = []
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
JsonValue = null | boolean | number | string | JsonValue[] | {
[key: string]: JsonValue
}
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
Visual = ({
kind: "image"
image: EntityId
} | {
kind: "animation"
animation: EntityId
motionId?: EntityId | null = null
playback: {
repeat: "once" | "loop"
rate: number > 0
clock: "gameplay" | "unscaled-presentation"
initialMarker: EntityId | null
loopRange?: {
start: EntityId
end: EntityId
}
} | null
}) exactly one variant
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: string length >= 1
}
MaterialApplication = {
material: EntityId
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}
CursorId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
CursorTarget = ({
kind: "system"
cursor: "default" | "pointer" | "text" | "wait" | "progress" | "crosshair" | "move" | "not-allowed" | "ns-resize" | "ew-resize" | "nesw-resize" | "nwse-resize"
} | {
kind: "named"
id: CursorId
} | {
kind: "none"
}) exactly one variant
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variantInteractablesRecord.data.stackable
- When false, aggregate creation of quantity N creates N distinct quantity-one Instances rather than rejecting the creation.
- Constraint: Stackable Definitions cannot own identity-bearing Features or Inventories.
- Lifecycle: Changing stackability does not rewrite authored Instances; incompatible quantities remain blocking validation diagnostics.
InteractablesRecord.data.stackLimit
- Constraint: Must be null when stackable is false. Null means the portable safe-integer ceiling when stackable is true.
InteractablesRecord.data.presentation.hotspots
- visual-alpha samples the current Visual frame CPU alpha coverage. Custom rectangular Hotspot bounds are normalized to the complete Visual canvas.
- Constraint: visual-alpha and non-empty custom Hotspots require an image or raster Animation Visual.
InteractablesRecord.data.presentation.hotspots[2].hotspot.target[4].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
InteractablesRecord.data.presentation.hotspots[3].hotspots[item].target[4].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
InteractablesRecord.data.features[item]
- Constraint: Room Features are concrete gameplay identities: defaultProperties must be empty and Property values belong in localProperties.
- Constraint: Interactable-definition Features are reusable configuration: localProperties must be empty and Property defaults belong in defaultProperties.
- Related: Room Feature, Interactable-definition Feature, Property
Visual
- A Visual is initially either a static Image Asset or a raster Animation. An Animation Visual with no motionId uses the Animation default motion.
Verbs record
Raw JSON SchemaShape and constraints
VerbsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
slots?: ({
id: EntityId
label: TextContent
prompt: TextContent
selectors: (({
kind: "any-subject"
} | {
kind: "family"
family: "character" | "interactable" | "feature"
} | {
kind: "trait"
trait: EntityId
} | {
kind: "interactable-definition"
interactableDefinition: EntityId
} | {
kind: "interactable-feature"
interactableDefinition: EntityId
featureId: EntityId
} | {
kind: "qualified-pattern"
family: "character" | "interactable" | "feature"
pattern: string length >= 2
} | {
kind: "exact"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
}) exactly one variant)[] items >= 1
})[] = []
bindingOrder?: EntityId[] = []
actionText: TextContent
completedCommandText: TextContent
offers?: ({
id: EntityId
slotId: EntityId
selectors: (({
kind: "any-subject"
} | {
kind: "family"
family: "character" | "interactable" | "feature"
} | {
kind: "trait"
trait: EntityId
} | {
kind: "interactable-definition"
interactableDefinition: EntityId
} | {
kind: "interactable-feature"
interactableDefinition: EntityId
featureId: EntityId
} | {
kind: "qualified-pattern"
family: "character" | "interactable" | "feature"
pattern: string length >= 2
} | {
kind: "exact"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
}) exactly one variant)[] items >= 1
condition?: Condition
rank?: integer >= -9007199254740991 <= 9007199254740991 = 0
primary?: boolean = false
})[] = []
availability?: Condition = "always"
defaultProgram?: {
instructions?: GameplayCommand[] = []
completion?: FlowTarget
outcome?: "handled" | "unhandled" = "handled"
} = {"instructions":[],"completion":{"kind":"return"},"outcome":"handled"}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
GameplayConfigurationSource = ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: EntityId
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: EntityId
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: EntityId
} | {
id: EntityId
kind: "call-dialogue"
dialogue: EntityId
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
FlowTarget = ({
kind: "scene"
id: EntityId
} | {
kind: "dialogue"
id: EntityId
} | {
kind: "room"
id: EntityId
} | {
kind: "return"
} | {
kind: "end"
}) exactly one variant = {"kind":"return"}VerbsRecord.data
- Constraint: qualified-pattern selectors require exactly one wildcard and it must be the final character.
- Constraint: bindingOrder must contain every declared Verb slot exactly once.
- Constraint: Inline slot labels/prompts and completed-command text use placeholders of the form {slot-id}; every placeholder must name a declared Verb slot.
Named slots with progressive order different from serialized slot order
{
"kind": "verb",
"slots": [
{
"id": "recipient",
"label": {
"source": {
"kind": "inline",
"text": "recipient"
},
"markup": "plain"
},
"prompt": {
"source": {
"kind": "inline",
"text": "Select recipient"
},
"markup": "plain"
},
"selectors": [
{
"kind": "any-subject"
}
]
},
{
"id": "object",
"label": {
"source": {
"kind": "inline",
"text": "object"
},
"markup": "plain"
},
"prompt": {
"source": {
"kind": "inline",
"text": "Select object"
},
"markup": "plain"
},
"selectors": [
{
"kind": "any-subject"
}
]
}
],
"bindingOrder": [
"object",
"recipient"
],
"actionText": {
"source": {
"kind": "inline",
"text": "Show"
},
"markup": "plain"
},
"completedCommandText": {
"source": {
"kind": "inline",
"text": "Show {object} to {recipient}"
},
"markup": "plain"
},
"offers": [],
"availability": {
"kind": "always"
},
"defaultProgram": {
"instructions": [],
"completion": {
"kind": "return"
},
"outcome": "unhandled"
}
}VerbsRecord.data.slots[item].selectors[item][7].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
VerbsRecord.data.bindingOrder
Every slot once, in progressive selection order. Final bindings are named by slotId, not array position; the same live subject may fill multiple slots.
VerbsRecord.data.offers
Explicit subject-first discovery declarations with their own selectors; rule-derived Offers instead reuse their starting rule slot selectors. Missing published Offers do not prohibit direct complete-command submission.
Exact false Offer suppresses the broad Offer for the same Verb
[
{
"id": "broad",
"slotId": "object",
"selectors": [
{
"kind": "any-subject"
}
],
"rank": 0,
"primary": false
},
{
"id": "suppressed",
"slotId": "object",
"selectors": [
{
"kind": "exact",
"subject": {
"kind": "interactable",
"interactable": {
"$ref": {
"registry": "interactableInstances",
"id": "sealed-box"
}
}
}
}
],
"condition": {
"kind": "not",
"condition": {
"kind": "always"
}
},
"rank": 10,
"primary": false
}
]VerbsRecord.data.offers[item].selectors[item][7].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
VerbsRecord.data.offers[item].condition
Independent discovery predicate for the starting slot only. A false winning Offer Condition suppresses this Verb, without falling back to a broader Offer.
VerbsRecord.data.offers[item].rank
Lower rank wins within equal Offer specificity; published Offers sort by rank then stable Verb ID. Rank never overrides structural specificity.
VerbsRecord.data.offers[item].primary
Primary Activate executes a unique immediately-complete primary Offer; otherwise it opens the menu. Multiple primary candidates produce an ambiguity diagnostic. Open Verb Menu never auto-selects.
VerbsRecord.data.defaultProgram.outcome
Handled stops fallback. Unhandled requires empty behavior with no committed work and advances to the next fallback stage for the host of this program. The complete chain is selected rule → Verb defaultProgram → Project undefinedInteractionProgram → engine response. Runtime failure aborts without fallback and does not rewind earlier observable boundaries.
Interactions record
Raw JSON SchemaShape and constraints
InteractionsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
rules?: ({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
verb: EntityId
slots: ({
slotId: EntityId
selectors: (({
kind: "any-subject"
} | {
kind: "family"
family: "character" | "interactable" | "feature"
} | {
kind: "trait"
trait: EntityId
} | {
kind: "interactable-definition"
interactableDefinition: EntityId
} | {
kind: "interactable-feature"
interactableDefinition: EntityId
featureId: EntityId
} | {
kind: "qualified-pattern"
family: "character" | "interactable" | "feature"
pattern: string length >= 2
} | {
kind: "exact"
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
}) exactly one variant)[] items >= 1
})[]
offer?: {
slotId: EntityId
condition?: Condition
rank?: integer >= -9007199254740991 <= 9007199254740991 = 0
primary?: boolean = false
} | null = null
guard?: Condition = "always"
priority?: integer >= -9007199254740991 <= 9007199254740991 = 0
program?: {
instructions?: GameplayCommand[] = []
completion?: FlowTarget
outcome?: "handled" | "unhandled" = "handled"
} = {"instructions":[],"completion":{"kind":"return"},"outcome":"handled"}
})[] = []
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
GameplayConfigurationSource = ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: EntityId
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: EntityId
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: EntityId
} | {
id: EntityId
kind: "call-dialogue"
dialogue: EntityId
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
FlowTarget = ({
kind: "scene"
id: EntityId
} | {
kind: "dialogue"
id: EntityId
} | {
kind: "room"
id: EntityId
} | {
kind: "return"
} | {
kind: "end"
}) exactly one variant = {"kind":"return"}InteractionsRecord.data
- Constraint: Interaction resolution considers the structurally most-specific matching Rule tier before comparing priority.
- Constraint: Within the winning structural tier, the highest passing priority wins; multiple passing Rules at the same winning priority are an ambiguity error, not an arbitrary tie-break.
False narrow Guard falls through to a broad rule; discovery opt-in is independent
{
"kind": "interaction",
"rules": [
{
"id": "narrow",
"verb": {
"$ref": {
"collection": "verbs",
"id": "inspect"
}
},
"slots": [
{
"slotId": "object",
"selectors": [
{
"kind": "exact",
"subject": {
"kind": "interactable",
"interactable": {
"$ref": {
"registry": "interactableInstances",
"id": "sealed-box"
}
}
}
}
]
}
],
"offer": null,
"guard": {
"kind": "not",
"condition": {
"kind": "always"
}
},
"priority": 100,
"program": {
"instructions": [],
"completion": {
"kind": "return"
},
"outcome": "handled"
}
},
{
"id": "broad",
"verb": {
"$ref": {
"collection": "verbs",
"id": "inspect"
}
},
"slots": [
{
"slotId": "object",
"selectors": [
{
"kind": "any-subject"
}
]
}
],
"offer": {
"slotId": "object",
"rank": 0,
"primary": false
},
"guard": {
"kind": "always"
},
"priority": 0,
"program": {
"instructions": [],
"completion": {
"kind": "return"
},
"outcome": "handled"
}
}
]
}InteractionsRecord.data.rules[item]
- guard is a pure execution predicate with access to all bound slots, independent of Offer discovery. If no Guard passes in this structural tier, try the next broader tier; evaluation errors fault before behavior.
InteractionsRecord.data.rules[item].slots
Bind every Verb slot exactly once by slotId. Rule slot array ordering is non-semantic.
InteractionsRecord.data.rules[item].slots[item].selectors[item][7].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
InteractionsRecord.data.rules[item].offer
Rule-derived discovery uses the named starting slot selectors. offer: null disables subject-first discovery only, not complete-command execution.
InteractionsRecord.data.rules[item].offer[1].condition
Independent discovery predicate for the starting slot only. A false winning Offer Condition suppresses this Verb, without falling back to a broader Offer.
InteractionsRecord.data.rules[item].offer[1].rank
Lower rank wins within equal Offer specificity; published Offers sort by rank then stable Verb ID. Rank never overrides structural specificity.
InteractionsRecord.data.rules[item].offer[1].primary
Primary Activate executes a unique immediately-complete primary Offer; otherwise it opens the menu. Multiple primary candidates produce an ambiguity diagnostic. Open Verb Menu never auto-selects.
InteractionsRecord.data.rules[item].priority
Greatest priority wins among passing rules within one structural tier, never across tiers. Equal winning priority is an ambiguity fault that executes nothing; declaration order is not a tie-break.
InteractionsRecord.data.rules[item].program.outcome
Handled stops fallback. Unhandled requires empty behavior with no committed work and advances to the next fallback stage for the host of this program. The complete chain is selected rule → Verb defaultProgram → Project undefinedInteractionProgram → engine response. Runtime failure aborts without fallback and does not rewind earlier observable boundaries.
Dialogues record
Raw JSON SchemaShape and constraints
DialoguesRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
displayName?: string = ""
defaultSpeaker?: EntityId | null = null
stageSlots?: ({
id: EntityId
label: string length >= 1
speakerSync?: boolean = true
initial?: {
character: EntityId
profileId: EntityId
poseId: EntityId
expressionId: EntityId
appearanceId: EntityId | null
position: "left" | "center" | "right"
offset: {
x: number
y: number
}
scale: number > 0
visible: boolean
} | null = null
})[] = []
mediaSlots?: ({
id: EntityId
label: string length >= 1
initial?: ({
kind: "character"
character: EntityId
profileId: EntityId
poseId: EntityId
expressionId: EntityId
appearanceId: EntityId | null
} | {
kind: "image"
asset: EntityId
}) exactly one variant | null = null
visible?: boolean = true
})[] = []
settings?: {
showDisabledChoices?: boolean = true
logMode?: "everything" | "nothing" | "only-choices" | "only-lines" = "everything"
} = {"showDisabledChoices":true,"logMode":"everything"}
entryBlockId: EntityId
blocks: (({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "sequence"
label: string length >= 1
defaultSpeaker?: EntityId | null = null
segments: (({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "line"
speaker?: EntityId | null = null
text: TextContent
cues?: (({
id: EntityId
kind: "active-text"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
token: string length >= 1
} | {
id: EntityId
kind: "invalid-markup"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
token: string length >= 1
message: string length >= 1
} | {
id: EntityId
kind: "speaker-expression"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
expressionId: EntityId
} | {
id: EntityId
kind: "stage"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
mutation: {
slotId: EntityId
action: "update" | "show" | "hide" | "clear"
character?: EntityId
profileId?: EntityId
poseId?: EntityId
expressionId?: EntityId
appearanceId?: EntityId | null
position?: "left" | "center" | "right"
offset?: {
x: number
y: number
}
scale?: number > 0
}
} | {
id: EntityId
kind: "media"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
mutation: {
slotId: EntityId
action: "update" | "show" | "hide" | "clear"
content?: ({
kind: "character"
character: EntityId
profileId: EntityId
poseId: EntityId
expressionId: EntityId
appearanceId: EntityId | null
} | {
kind: "image"
asset: EntityId
}) exactly one variant
}
} | {
id: EntityId
kind: "gesture"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
slotId: EntityId
gestureId: EntityId
waitForCompletion?: boolean = false
skippable?: boolean = true
} | {
id: EntityId
kind: "voice"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
asset: EntityId
pausePolicy: "gameplay" | "owner" | "unscaled"
gain: number >= 0 <= 1
pan: number >= -1 <= 1
waitForCompletion?: boolean = false
skipBehavior: "stop" | "suppress" | "play"
} | {
id: EntityId
kind: "sound-effect"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
asset: EntityId
pausePolicy: "gameplay" | "owner" | "unscaled"
gain: number >= 0 <= 1
pan: number >= -1 <= 1
waitForCompletion?: boolean = false
causality: "causal" | "disposable"
synchronized: boolean
skipBehavior: "stop" | "suppress" | "play"
} | {
id: EntityId
kind: "camera"
position: {
offset: integer >= 0 <= 9007199254740991
order: integer >= 0 <= 9007199254740991
}
emphasis: ({
kind: "shake"
amplitude: {
x: number
y: number
}
frequencyHz: number > 0
durationMs: integer >= 1 <= 9007199254740991
skippable: boolean
waitForCompletion: boolean
} | {
kind: "punch"
translation: {
x: number
y: number
}
zoomDelta: number
rotationDegrees: number
durationMs: integer >= 1 <= 9007199254740991
skippable: boolean
waitForCompletion: boolean
} | {
kind: "flash"
color: string length >= 1
opacity: number >= 0 <= 1
durationMs: integer >= 1 <= 9007199254740991
skippable: boolean
waitForCompletion: boolean
}) exactly one variant
}) exactly one variant)[] = []
condition?: Condition
effects?: GameplayCommand[] = []
showOnce?: boolean = false
logged?: boolean = true
autosaveSafePoint?: boolean = false
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "run-lua"
condition?: Condition
source: string length >= 1
mayYield?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "call-scene"
condition?: Condition
scene: EntityId
inputs?: ({
inputId: EntityId
value: RuntimeScalar
})[] = []
uiPolicy?: "preserve" | "conceal" = "conceal"
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "handoff"
condition?: Condition
payload?: RuntimeScalar
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "comment"
text: string
}) exactly one variant)[]
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "choice"
label: string length >= 1
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "redirect"
label: string length >= 1
targetBlockId: EntityId
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
type: "comment"
label: string length >= 1
text: string
}) exactly one variant)[] items >= 1
edges?: (({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
kind: "next"
fromBlockId: EntityId
toBlockId: EntityId
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
kind: "choice"
fromBlockId: EntityId
toBlockId: EntityId
label: TextContent
condition?: Condition
effects?: GameplayCommand[] = []
logged?: boolean = true
autosaveSafePoint?: boolean = false
}) exactly one variant)[] = []
completion?: FlowTarget = {"kind":"end"}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
GameplayConfigurationSource = ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: EntityId
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: EntityId
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: EntityId
} | {
id: EntityId
kind: "call-dialogue"
dialogue: EntityId
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
FlowTarget = ({
kind: "scene"
id: EntityId
} | {
kind: "dialogue"
id: EntityId
} | {
kind: "room"
id: EntityId
} | {
kind: "return"
} | {
kind: "end"
}) exactly one variantDialoguesRecord.data
- Constraint: Dialogue cue position.offset counts Unicode code points in the inline source text, not UTF-8 bytes or UTF-16 code units.
- Constraint: Positioned Dialogue cues currently require an inline text source, and each (offset, order) pair must be unique within one line segment.
- Constraint: Sequence blocks admit at most one outgoing next edge and no choice edges; Choice blocks require at least one outgoing choice edge and admit no next edges; Redirect and Comment blocks cannot own outgoing edges.
- Constraint: Edges cannot target Comment blocks, and Redirect blocks cannot target Comment blocks.
Scenes record
Raw JSON SchemaShape and constraints
ScenesRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
displayName?: string = ""
stage: ({
kind: "inherited"
} | {
kind: "staged-room"
room: EntityId
} | {
kind: "blank"
background: {
asset: EntityId | null
materialApplication: MaterialApplication | null
color: string | null
fit: "cover" | "contain" | "stretch" | "center"
}
layout: EntityId | null
}) exactly one variant
inputs?: ({
id: EntityId
label: string length >= 1
type: "boolean" | "integer" | "number" | "string"
nullable: boolean
defaultValue?: RuntimeScalar
})[] = []
outcomes?: ({
id: EntityId
label: string length >= 1
})[] = []
events: (({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "set-background"
owner: "invocation" | "active-room" | "runtime-session"
asset: EntityId | null
materialApplication: MaterialApplication | null
color: string | null
fit: "cover" | "contain" | "stretch" | "center"
transition: "none" | "fade" | "cut"
durationMs: integer >= 0 <= 9007199254740991
waitForCompletion?: boolean = false
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "actor-cue"
owner: "invocation" | "active-room" | "runtime-session"
slotId: EntityId
character: EntityId
action: "show" | "hide" | "move" | "profile" | "pose" | "expression" | "appearance"
profileId?: EntityId | null
poseId: EntityId | null
expressionId: EntityId | null
appearanceId?: EntityId | null
position: "left" | "center" | "right" | "custom"
offset: {
x: number
y: number
}
scale: number > 0
transition: "none" | "fade" | "slide"
durationMs: integer >= 0 <= 9007199254740991
waitForCompletion?: boolean = false
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "call-scene"
scene: EntityId
inputs?: ({
inputId: EntityId
value: RuntimeScalar
})[] = []
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "start-detached-scene"
scene: EntityId
inputs?: ({
inputId: EntityId
value: RuntimeScalar
})[] = []
owner: "flow" | "active-room" | "runtime-session"
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "call-dialogue"
dialogue: EntityId
startBlockId: EntityId | null
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "resume-dialogue"
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "show-text"
text: TextContent
speaker: EntityId | null
wait: "input" | "immediate"
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "audio-cue"
owner: "invocation" | "active-room" | "runtime-session"
asset: EntityId | null
purpose: "music" | "ambience" | "voice" | "sound-effect" | "ui-sound"
action: "play" | "stop" | "fade-in" | "fade-out"
lifetime: "desired-loop" | "one-shot"
pausePolicy: "gameplay" | "owner" | "unscaled"
gain: number >= 0 <= 1
pan: number >= -1 <= 1
panSource: ({
kind: "scene-actor"
slotId: EntityId
} | {
kind: "room-anchor"
room: EntityId
anchorId: EntityId
}) exactly one variant | null
fadeMs: integer >= 0 <= 9007199254740991
waitForCompletion?: boolean = false
causality: "causal" | "disposable"
synchronized: boolean
skipBehavior: "stop" | "suppress" | "play"
instanceId: EntityId | null
replacementGroup: EntityId | null
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "set-variable"
variable: EntityId
value: RuntimeScalar
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "gameplay-effect-batch"
operations: GameplayCommand[] items >= 1
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "runtime-world-transaction"
operations: (({
kind: "create-room"
source: ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
} | {
kind: "effective-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
}) exactly one variant
} | {
kind: "create-character"
source: ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
} | {
kind: "effective-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
}) exactly one variant
location: ({
kind: "unplaced"
} | {
kind: "room"
room: EntityId
}) exactly one variant
enabled: boolean
visible: boolean
} | {
kind: "create-interactable"
definition: EntityId
quantity?: integer >= 1 <= 9007199254740991 = 1
location: InteractableLocation
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none" = "resolve"
} | {
kind: "replace-configuration"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
source: ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
} | {
kind: "effective-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
}) exactly one variant
} | {
kind: "clear-configuration"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
} | {
kind: "retarget-room-exit"
room: EntityId
exitId: EntityId
target: EntityId
} | {
kind: "destroy-instance"
instance: ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: EntityId
}) exactly one variant
}) exactly one variant)[] items >= 1
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "directed-room-change"
room: EntityId
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "navigation-attempt"
room: EntityId
exitId: EntityId
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "call-interaction"
verb: EntityId
bindings: ({
slotId: EntityId
subject: ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
})[]
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "run-lua"
source: string length >= 1
mayYield?: boolean = true
} | ({
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "duration"
durationMs: integer >= 0 <= 9007199254740991
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "input"
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "condition"
waitCondition: Condition
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "operation"
eventId: EntityId
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "audio"
eventId: EntityId
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "wait"
waitKind: "layout-signal"
owner: "invocation" | "active-room" | "runtime-session"
slot: "hud" | "dialogue-box" | "overlay" | "custom"
signalId: LayoutContractId
skippable?: boolean = true
}) exactly one variant | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "conditional-branch"
branches: ({
id: EntityId
condition: Condition
targetStepId: EntityId
})[]
fallbackStepId: EntityId
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
autosaveSafePoint?: boolean = false
type: "choice"
prompt: TextContent | null
options: ({
id: EntityId
label: TextContent
condition?: Condition
effects?: GameplayCommand[] = []
targetStepId: EntityId
})[] items >= 1
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "set-layout"
owner: "invocation" | "active-room" | "runtime-session"
layout: EntityId | null
scaleOverrides?: {
ui?: "inherit" | "ignore"
text?: "inherit" | "ignore"
}
action: "show" | "hide" | "swap"
slot: "hud" | "dialogue-box" | "overlay" | "custom"
transition: "none" | "fade"
durationMs: integer >= 0 <= 9007199254740991
waitForCompletion?: boolean = false
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "material-parameter"
owner: "invocation" | "active-room" | "runtime-session"
target: ({
kind: "background"
} | {
kind: "actor"
slotId: EntityId
layerId: EntityId
} | {
kind: "layout"
slot: "hud" | "dialogue-box" | "overlay" | "custom"
} | {
kind: "postprocess"
instanceId: EntityId
}) exactly one variant
material: EntityId
parameter: string length >= 1
value: ShaderUniformValue
transition: "none" | "tween"
durationMs: integer >= 0 <= 9007199254740991
easing: "linear" | "ease-in" | "ease-out" | "ease-in-out"
clock: "gameplay" | "unscaled-presentation"
waitForCompletion?: boolean = false
skippable?: boolean = true
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "postprocess-effect"
owner: "invocation" | "active-room" | "runtime-session"
action: "upsert" | "remove"
instanceId: EntityId
materialApplication: MaterialApplication | null
scope: "world" | "full-game-viewport"
order: integer >= -9007199254740991 <= 9007199254740991
clock: "gameplay" | "unscaled-presentation"
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
enabled?: boolean = true
condition?: Condition
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
completionDependencies?: EntityId[] = []
type: "transition-group"
owner: "invocation" | "active-room" | "runtime-session"
transitionKind: "fade" | "cut" | "dissolve"
durationMs: integer >= 0 <= 9007199254740991
color: string | null
waitForCompletion?: boolean = false
skippable?: boolean = true
children: (({
id: EntityId
type: "set-background"
asset: EntityId | null
materialApplication: MaterialApplication | null
color: string | null
fit: "cover" | "contain" | "stretch" | "center"
} | {
id: EntityId
type: "clear-background"
} | {
id: EntityId
type: "actor-cue"
slotId: EntityId
character: EntityId
action: "show" | "hide" | "move" | "profile" | "pose" | "expression" | "appearance"
profileId?: EntityId | null
poseId: EntityId | null
expressionId: EntityId | null
appearanceId?: EntityId | null
position: "left" | "center" | "right" | "custom"
offset: {
x: number
y: number
}
scale: number > 0
} | {
id: EntityId
type: "set-layout"
layout: EntityId | null
scaleOverrides?: {
ui?: "inherit" | "ignore"
text?: "inherit" | "ignore"
}
action: "show" | "hide" | "swap"
slot: "hud" | "dialogue-box" | "overlay" | "custom"
}) exactly one variant)[] items >= 1
} | {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
type: "comment"
text: string
timeline: {
trackId: EntityId
startMs: integer >= 0 <= 9007199254740991
durationMs: integer >= 0 <= 9007199254740991
}
}) exactly one variant)[] items >= 1
terminal: ({
kind: "return"
outcome: EntityId | null
} | {
kind: "continue-scene"
scene: EntityId
inputs: ({
inputId: EntityId
value: RuntimeScalar
})[]
} | {
kind: "continue-dialogue"
dialogue: EntityId
} | {
kind: "release-to-exploration"
} | {
kind: "complete-game"
}) exactly one variant
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
ShaderUniformValue = null | number | boolean | [number, number] items >= 2 items <= 2 | [number, number, number] items >= 3 items <= 3 | [number, number, number, number] items >= 4 items <= 4 | {
r: number
g: number
b: number
a: number
}
MaterialParameterSource = ({
kind: "literal"
value: ShaderUniformValue
} | {
kind: "property"
property: EntityId
} | {
kind: "standard-facet"
facet: "occurrence-time" | "paint-width" | "paint-height" | "viewport-width" | "viewport-height" | "camera-zoom"
}) exactly one variant
MaterialParameterOverride = {
type: "float" | "vec2" | "vec3" | "vec4" | "color" | "int" | "bool"
source: MaterialParameterSource
}
MaterialTextureOverride = {
source: string length >= 1
}
MaterialApplication = {
material: EntityId
parameters?: {
[key: string length >= 1]: MaterialParameterOverride
} = {}
textures?: {
[key: string length >= 1]: MaterialTextureOverride
} = {}
}
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
GameplayConfigurationSource = ({
kind: "archetype"
archetype: EntityId
} | {
kind: "compiled-instance"
instance: GameplayIdentityOperand
} | {
kind: "effective-instance"
instance: GameplayIdentityOperand
}) exactly one variant
GameplayCommand = {
id: EntityId
kind: "set-global-property"
variable: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-global-property"
variable: EntityId
} | {
id: EntityId
kind: "set-property"
owner: GameplayIdentityOperand
propertyId: EntityId
value: RuntimeScalar
} | {
id: EntityId
kind: "unset-property"
owner: GameplayIdentityOperand
propertyId: EntityId
} | {
id: EntityId
kind: "add-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "remove-trait"
owner: GameplayIdentityOperand
trait: EntityId
} | {
id: EntityId
kind: "set-enabled"
subject: LocationSubjectOperand
enabled: boolean
} | {
id: EntityId
kind: "set-visible"
subject: LocationSubjectOperand
visible: boolean
} | {
id: EntityId
kind: "move-instance"
subject: LocationSubjectOperand
location: LocationOperand
roomPresentation?: "resolve" | "none"
} | {
id: EntityId
kind: "create-room"
source: GameplayConfigurationSource
result?: EntityId
} | {
id: EntityId
kind: "create-character"
source: GameplayConfigurationSource
location: LocationOperand
enabled: boolean
visible: boolean
result?: EntityId
} | {
id: EntityId
kind: "create-interactable"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
enabled: boolean
visible: boolean
roomPresentation?: "resolve" | "none"
result?: EntityId
} | {
id: EntityId
kind: "destroy-instance"
instance: GameplayIdentityOperand
} | {
id: EntityId
kind: "split-quantity"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
result?: EntityId
} | {
id: EntityId
kind: "merge-quantity"
receiver: InteractableOperand
donor: InteractableOperand
} | {
id: EntityId
kind: "transfer-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
result?: EntityId
} | {
id: EntityId
kind: "transfer-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "add-quantity"
definition: EntityId
quantity: integer >= 1 <= 9007199254740991
location: LocationOperand
} | {
id: EntityId
kind: "consume-quantity"
mode: "exact"
source: InteractableOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "consume-quantity"
mode: "aggregate"
matcher: InteractableMatcher
sourceInventory?: InventoryOperand
quantity: integer >= 1 <= 9007199254740991
} | {
id: EntityId
kind: "present-inventory"
inventory: InventoryOperand
layout?: EntityId
useTriggerAnchor?: boolean
parentToTriggeringLayout?: boolean
coexist?: boolean
} | {
id: EntityId
kind: "navigate-exit"
exitId: EntityId
} | {
id: EntityId
kind: "change-room"
room: RoomOperand
} | {
id: EntityId
kind: "call-scene"
scene: EntityId
} | {
id: EntityId
kind: "call-dialogue"
dialogue: EntityId
} | {
id: EntityId
kind: "notify"
message: TextContent
} | {
id: EntityId
kind: "run-lua"
source: string length >= 1
} | {
id: EntityId
kind: "if"
condition: Condition
then: GameplayCommand[]
else: GameplayCommand[]
}
InteractableLocation = ({
kind: "unplaced"
} | {
kind: "room"
room: EntityId
} | {
kind: "inventory"
inventory: InventoryRef
}) exactly one variant
LayoutContractId = string pattern "^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$"ScenesRecord.data
- Constraint: Scene Event completionDependencies may name only earlier enabled non-comment Events.
- Constraint: gameplay-effect-batch operations use GameplayCommand shape but do not admit call-scene, call-dialogue, present-inventory, navigate-exit, change-room, notify, or run-lua commands.
- Constraint: Choice option effects use GameplayCommand shape but do not admit call-scene, call-dialogue, present-inventory, navigate-exit, change-room, or notify; nested run-lua is forbidden, and commands that create/split/exact-transfer identities cannot bind command results across the yielding Choice boundary.
ScenesRecord.data.events[item][14].bindings[item].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
InteractableLocation
- Location is authoritative semantic membership. Room visual occurrences do not change it; Unplaced Instances still exist.
- Related: Room, Inventory, Interactable Instance
Unplaced
{
"kind": "unplaced"
}In a Room
{
"kind": "room",
"room": {
"$ref": {
"collection": "rooms",
"id": "hall"
}
}
}Maps record
Raw JSON SchemaShape and constraints
MapsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
presentation?: {
title?: TextContent | null = null
background?: EntityId | null = null
layout?: EntityId | null = null
initialMode?: "minimap" | "full-map" = "full-map"
} = {"title":null,"background":null,"layout":null,"initialMode":"full-map"}
locations?: ({
id: EntityId
room: EntityId
regions?: ({
points: ({
x: number >= 0 <= 1
y: number >= 0 <= 1
})[] items >= 3
})[] = []
label?: TextContent | null = null
icon?: EntityId | null = null
style?: string length >= 1 | null = null
labelAnchor?: {
x: number >= 0 <= 1
y: number >= 0 <= 1
} | null = null
connectionAnchor?: {
x: number >= 0 <= 1
y: number >= 0 <= 1
} | null = null
visibility?: Condition = "always"
pickOrder?: integer >= -9007199254740991 <= 9007199254740991 = 0
logicalOrder?: integer >= -9007199254740991 <= 9007199254740991 = 0
})[] = []
connections?: ({
id: EntityId
exits: ({
room: EntityId
exit: EntityId
})[] items >= 1 items <= 2
label?: TextContent | null = null
icon?: EntityId | null = null
style?: string length >= 1 | null = null
visibility?: Condition = "always"
logicalOrder?: integer >= -9007199254740991 <= 9007199254740991 = 0
path?: ({
x: number >= 0 <= 1
y: number >= 0 <= 1
})[] = []
hitRegions?: ({
points: ({
x: number >= 0 <= 1
y: number >= 0 <= 1
})[] items >= 3
})[] = []
})[] = []
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
NamedMessageKey = string pattern "^[A-Za-z][A-Za-z0-9_-]*(?:\\.[A-Za-z][A-Za-z0-9_-]*)+$"
LuaDependencyTarget = ({
kind: "record"
collection: "assets" | "animations" | "variables" | "materials" | "layouts" | "archetypes" | "characters" | "rooms" | "interactables" | "verbs" | "interactions" | "dialogues" | "scenes" | "maps" | "scripts" | "tests"
id: string length >= 1
} | {
kind: "property-value"
owner: {
kind: "room" | "character" | "interactable"
id: string length >= 1
}
propertyId: string length >= 1
} | {
kind: "room-placement"
roomId: string length >= 1
placementId: string length >= 1
} | {
kind: "room-exit"
roomId: string length >= 1
exitId: string length >= 1
}) exactly one variant
LuaExplicitDependencies = {
targets?: LuaDependencyTarget[] = []
}
TextSource = ({
kind: "inline"
text: string
} | {
kind: "localized"
key: NamedMessageKey
} | {
kind: "lua-expression"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variant
TextContent = {
source: TextSource
markup: "plain" | "active-text"
}
RuntimeScalar = null | boolean | number | string
InteractableInstanceRef = {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
InteractionSlotOperand = {
kind: "interaction-slot"
slotId: EntityId
}
CommandResultOperand = {
kind: "command-result"
bindingId: EntityId
}
GameplayIdentityOperand = ({
kind: "room"
room: EntityId
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | {
kind: "current-room"
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
LocationSubjectOperand = ({
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
RoomOperand = ({
kind: "room"
room: EntityId
} | {
kind: "current-room"
} | CommandResultOperand) exactly one variant
InventoryOwner = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
}) exactly one variant
InventoryRef = {
owner: InventoryOwner
inventoryId: EntityId
}
InventoryOwnerOperand = ({
kind: "project"
} | {
kind: "character"
character: EntityId
} | {
kind: "interactable"
interactable: InteractableInstanceRef
} | {
kind: "room-feature"
room: EntityId
featureId: EntityId
} | {
kind: "interactable-feature"
interactable: InteractableInstanceRef
featureId: EntityId
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InventoryOperand = ({
kind: "inventory"
inventory: InventoryRef
} | {
kind: "player-inventory"
} | {
kind: "owner-inventory"
owner: InventoryOwnerOperand
inventoryId: EntityId
} | CommandResultOperand) exactly one variant
LocationOperand = ({
kind: "unplaced"
} | {
kind: "room"
room: RoomOperand
} | {
kind: "inventory"
inventory: InventoryOperand
}) exactly one variant
InteractableOperand = ({
kind: "interactable"
interactable: InteractableInstanceRef
} | InteractionSlotOperand | CommandResultOperand) exactly one variant
InteractableMatcher = {
definition?: EntityId
traits?: EntityId[] = []
properties?: ({
propertyId: EntityId
value: RuntimeScalar
})[] = []
exact?: InteractableOperand
}
Condition = "always" | ({
kind: "always"
} | {
kind: "all"
conditions: Condition[]
} | {
kind: "any"
conditions: Condition[]
} | {
kind: "not"
condition: Condition
} | {
kind: "variable-comparison"
variable: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "property-comparison"
owner: GameplayIdentityOperand
propertyId: EntityId
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal" | "truthy" | "falsy"
value?: RuntimeScalar
} | {
kind: "trait-presence"
owner: GameplayIdentityOperand
trait: EntityId
present: boolean
} | {
kind: "location-comparison"
subject: LocationSubjectOperand
operator: "equal" | "not-equal"
location: LocationOperand
} | {
kind: "inventory-quantity-comparison"
inventory: InventoryOperand
matcher: InteractableMatcher
operator: "equal" | "not-equal" | "less" | "less-equal" | "greater" | "greater-equal"
quantity: integer >= 0 <= 9007199254740991
} | {
kind: "lua-predicate"
source: string length >= 1
additionalDependencies?: LuaExplicitDependencies
}) exactly one variantMapsRecord.data.connections[item]
- Constraint: A two-exit Map Connection must reference reciprocal Room Exits connecting the same two distinct Rooms.
Scripts record
Raw JSON SchemaShape and constraints
ScriptsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
source: ({
kind: "inline-lua"
source: string
} | {
kind: "project-file"
path: string pattern "^scripts\\/(?!.*(?:^|\\/)\\.\\.(?:\\/|$))(?!.*\\\\)(?!.*\\/\\/)[^/].*\\.lua$"
}) exactly one variant
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"Tests record
Raw JSON SchemaShape and constraints
TestsRecord = {
narrativeRefs?: (string pattern "^(?:creative-direction|arc|milestone|event|character|fact|world-entity|relationship|revelation|choice-point|puzzle|authored-content|narrative-position)\\.[a-z][a-z0-9]*(?:-[a-z0-9]+)*$")[]
id: EntityId
label: string length >= 1
description?: string
data: {
displayName?: string = ""
steps?: ({
id: EntityId
input: "tick" | "continue" | "fast-forward" | "dialogue-choice" | "scene-choice" | "navigate" | "select-subjects" | "primary-activate" | "open-verb-menu" | "clear-subject-selection" | "run-interaction" | "save" | "load" | "ui-click"
label: string length >= 1
enabled?: boolean = true
expectations?: ({
id: EntityId
type: "property" | "current-room" | "location" | "quantity" | "trait" | "entity-state" | "active-flow" | "layout" | "ui-element" | "event" | "diagnostic"
operator: "eq" | "ne" | "present" | "absent" | "gt" | "gte" | "lt" | "lte"
property?: {
scope?: "global" | "room" | "character" | "interactable"
ownerId?: string = ""
propertyId?: EntityId = "property"
value?: null | boolean | number | string = null
}
currentRoom?: {
roomId?: string
}
location?: {
entityKind?: "character" | "interactable"
entityId?: string
locationKind?: "unplaced" | "room" | "inventory"
roomId?: string = ""
inventoryOwnerKind?: "project" | "character" | "interactable" = "project"
inventoryOwnerId?: string = ""
inventoryId?: string = ""
}
quantity?: {
interactableId?: string
value?: number = 0
}
trait?: {
ownerKind?: "room" | "character" | "interactable"
ownerId?: string
traitId?: EntityId = "trait"
}
entityState?: {
entityKind?: "character" | "interactable"
entityId?: string
field?: "enabled" | "visible"
value?: boolean = true
}
activeFlow?: {
kind?: "scene" | "dialogue"
flowId?: string
}
layout?: {
layoutId?: string = ""
field?: "mounted" | "state" = "mounted"
value?: LayoutPersistableValue = null
}
uiElement?: {
documentId?: string | null = "runtime_game"
mountInstanceId?: EntityId | null = null
selector?: string
field?: "present" | "visible"
value?: boolean = true
}
event?: {
kind?: "notification" | "save-outcome"
value?: string = ""
}
diagnostic?: {
code?: string
}
})[] = []
tick?: {
deltaSeconds?: number >= 0 = 0
} = {"deltaSeconds":0}
dialogueChoice?: {
edgeId?: EntityId = "choice"
}
sceneChoice?: {
optionId?: EntityId = "choice"
}
navigate?: {
exitId?: EntityId = "exit"
}
selectSubjects?: {
subjects?: (({
kind: "character"
character: string length >= 1
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: string length >= 1
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant)[] = []
} = {"subjects":[]}
subjectAction?: {
subject?: ({
kind: "character"
character: string length >= 1
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: string length >= 1
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant | null
}
runInteraction?: {
verb?: string length >= 1 | null
bindings?: ({
slotId: EntityId
subject: ({
kind: "character"
character: string length >= 1
} | {
kind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: string length >= 1
}
}
} | {
kind: "feature"
feature: ({
ownerKind: "room"
room: EntityId
featureId: EntityId
} | {
ownerKind: "interactable"
interactable: {
$ref: {
registry: "interactableInstances"
id: EntityId
}
}
featureId: EntityId
}) exactly one variant
}) exactly one variant
})[]
}
saveSlot?: {
slotId?: string
}
uiClick?: {
documentId?: string | null = "runtime_game"
mountInstanceId?: EntityId | null = null
selector?: string
}
})[] = []
finalExpectations?: ({
id: EntityId
type: "property" | "current-room" | "location" | "quantity" | "trait" | "entity-state" | "active-flow" | "layout" | "ui-element" | "event" | "diagnostic"
operator: "eq" | "ne" | "present" | "absent" | "gt" | "gte" | "lt" | "lte"
property?: {
scope?: "global" | "room" | "character" | "interactable"
ownerId?: string = ""
propertyId?: EntityId = "property"
value?: null | boolean | number | string = null
}
currentRoom?: {
roomId?: string
}
location?: {
entityKind?: "character" | "interactable"
entityId?: string
locationKind?: "unplaced" | "room" | "inventory"
roomId?: string = ""
inventoryOwnerKind?: "project" | "character" | "interactable" = "project"
inventoryOwnerId?: string = ""
inventoryId?: string = ""
}
quantity?: {
interactableId?: string
value?: number = 0
}
trait?: {
ownerKind?: "room" | "character" | "interactable"
ownerId?: string
traitId?: EntityId = "trait"
}
entityState?: {
entityKind?: "character" | "interactable"
entityId?: string
field?: "enabled" | "visible"
value?: boolean = true
}
activeFlow?: {
kind?: "scene" | "dialogue"
flowId?: string
}
layout?: {
layoutId?: string = ""
field?: "mounted" | "state" = "mounted"
value?: LayoutPersistableValue = null
}
uiElement?: {
documentId?: string | null = "runtime_game"
mountInstanceId?: EntityId | null = null
selector?: string
field?: "present" | "visible"
value?: boolean = true
}
event?: {
kind?: "notification" | "save-outcome"
value?: string = ""
}
diagnostic?: {
code?: string
}
})[] = []
preview?: {
selectedStepId?: EntityId | null = null
selectedObservationIndex?: integer >= 0 <= 9007199254740991 | null = null
autoOpenReport?: boolean = true
} = {"selectedStepId":"start","selectedObservationIndex":null,"autoOpenReport":true}
}
}
EntityId = string pattern "^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$"
LayoutPersistableValue = null | boolean | number | string | LayoutPersistableValue[] | {
[key: string length >= 1]: LayoutPersistableValue
}TestsRecord.data.steps[item]
An authored Step requires input. Active input payloads and their identifying fields are required; ui-click must specify a documentId or mountInstanceId.
TestsRecord.data.steps[item].input
Use semantic inputs for behavior: select-subjects changes selection, primary-activate uses Primary activation (or active Builder capture), open-verb-menu requests discovery without auto-selection, and run-interaction submits complete named bindings without proving Offer discovery. ui-click exercises real RuntimeUI presentation with exactly one stable document address: documentId for fixed/system documents or mountInstanceId for a uniquely realized custom Mount, plus a stable semantic selector; see technical/LAYOUTS.md. Runtime error diagnostics make playback an error even when a diagnostic expectation matches.
TestsRecord.data.steps[item].expectations[item]
An authored Expectation requires type, operator, and the matching active payload with its identifying fields. Comparisons require explicit values; owner/location/UI addressing depends on the selected mode.
TestsRecord.data.steps[item].expectations[item].layout.layoutId
Layout definition ID observed by this expectation. Mounted presence is definition-wide; state observation requires exactly one live state Slot for this Layout definition.
TestsRecord.data.steps[item].expectations[item].layout.field
Use mounted for definition-wide presence. Use state only when exactly one live state Slot exists for the Layout; authored semantic expectations do not select a Slot by owner/scope/occurrence.
TestsRecord.data.steps[item].expectations[item].layout.value
Expected complete state value when field is state. If several owner/scope Slots for the same Layout are live, verify the exact occurrence through ui-click.mountInstanceId and observable UI/events, or use a focused native runtime test.
TestsRecord.data.steps[item].selectSubjects.subjects[item][3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
TestsRecord.data.steps[item].subjectAction.subject[1][3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
TestsRecord.data.steps[item].runInteraction.bindings[item].subject[3].feature
Owner-qualified Feature identity: the Feature must exist on the effective/live owner configuration. Room Features require their active Room; Interactable Features follow the exact owner Instance eligibility, including visible/enabled Inventory-held owners.
TestsRecord.data.steps[item].saveSlot.slotId
Authored Test save-slot identity. Use 'autosave' for the reserved autosave slot or 'slot-N' for manual slot N; bare numbers and runtime transport names such as 'manual-N' are not authored forms.
TestsRecord.data.steps[item].uiClick.documentId
Stable RmlUi document identity for built-in/system documents. Set null when mountInstanceId addresses a custom Mount.
TestsRecord.data.steps[item].uiClick.mountInstanceId
Semantic custom-Mount instance ID. The UI runner resolves its unique current realization, so generated realization document IDs must not be authored. Set documentId to null when using this field.
TestsRecord.data.finalExpectations[item]
An authored Expectation requires type, operator, and the matching active payload with its identifying fields. Comparisons require explicit values; owner/location/UI addressing depends on the selected mode.
TestsRecord.data.finalExpectations[item].layout.layoutId
Layout definition ID observed by this expectation. Mounted presence is definition-wide; state observation requires exactly one live state Slot for this Layout definition.
TestsRecord.data.finalExpectations[item].layout.field
Use mounted for definition-wide presence. Use state only when exactly one live state Slot exists for the Layout; authored semantic expectations do not select a Slot by owner/scope/occurrence.
TestsRecord.data.finalExpectations[item].layout.value
Expected complete state value when field is state. If several owner/scope Slots for the same Layout are live, verify the exact occurrence through ui-click.mountInstanceId and observable UI/events, or use a focused native runtime test.