Assertion reference

Every assertion kind the playtest validator accepts, generated from the registry the validator itself reads, so this page cannot disagree with the runtime. Assertions fail closed: a malformed assertion throws at load, and a scenario whose assertions all hold trivially fails TN_PLAYTEST_SCENARIO_ASSERTS_NOTHING.

On this page

deviceMetrics

Reports the device's thermal, power and battery state around the run and judges whether the run is comparable with a cool one. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: device.metrics
Field Type Required
maxTemperatureRiseC finite non-negative number no
maxThermalStatus non-negative integer 0..6 no
notThermallyConfounded true no
json
{
  "deviceMetrics": {
    "maxTemperatureRiseC": 5,
    "notThermallyConfounded": true
  }
}

framebufferCoverage

Samples the framebuffer on every render frame inside a labeled loading window and requires every coarse-grid RGB sample to match the declared backdrop. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: browser.screenshot
Field Type Required
backdrop [integer 0..255, integer 0..255, integer 0..255] yes
grid { columns: positive integer <= 256, rows: positive integer <= 256 } no
tolerance integer 0..255 yes
window { startStep: string, endStep: string } yes
json
{
  "framebufferCoverage": {
    "backdrop": [
      5,
      7,
      11
    ],
    "grid": {
      "columns": 32,
      "rows": 18
    },
    "tolerance": 8,
    "window": {
      "endStep": "loading-end",
      "startStep": "loading-start"
    }
  }
}

reachability

Checks every consecutive platform against a measured static movement-envelope fit; it does not simulate traversal, walls, ceilings, run-up, or air control. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: entity.observe
Field Type Required
artifact string yes
entities string[] (minimum 2) yes
json
{
  "reachability": {
    "artifact": "artifacts/character-envelope/player.json",
    "entities": [
      "platform.a",
      "platform.b"
    ]
  }
}

aerodynamics

Proves aerodynamic force telemetry and signed control-surface delivery for a flight entity. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.fixedStep, runtime.physics
Field Type Required
entity string yes
minForceSamples positive integer no
controls Array<{ surface: string, sign: 'negative' 'positive', minAbs?: number }>
torques Array<{ label: string, relativeToLabel?: string, axis: 'x' 'y'
json
{
  "aerodynamics": [
    {
      "controls": [
        {
          "sign": "negative",
          "surface": "elevator"
        }
      ],
      "entity": "aircraft",
      "minForceSamples": 4
    }
  ]
}

visual

Proves screenshot change, populated coordinate or DOM element-bound regions, dark-pixel bounds, and sustained projected entity visibility. Use when that is the thing the scenario must prove.

  • Supported on: web · Requires: browser.screenshot
Field Type Required
frameDiff { baselineImage?: project-relative PNG, minChangedPixelRatio?: number, maxChangedPixelRatio?: number } no
region static bounds { x: number, y: number, width: number, height: number, minNonblankPixelRatio?: number, maxDarkPixelRatio?: number, minDarkPixelRatio?: number, maxLuminance?: number } or element bounds { element: { id?: string, selector?: string }, minNonblankPixelRatio?: number, maxDarkPixelRatio?: number, minDarkPixelRatio?: number, maxLuminance?: number } no
entityVisible { entity: string, minProjectedPixels: number, throughoutFrames?: boolean } no
json
{
  "visual": [
    {
      "frameDiff": {
        "baselineImage": "artifacts/baseline.png",
        "minChangedPixelRatio": 0.01
      },
      "entityVisible": {
        "entity": "board.e4",
        "minProjectedPixels": 20,
        "throughoutFrames": true
      },
      "region": {
        "element": {
          "id": "error"
        },
        "minNonblankPixelRatio": 0.002
      }
    }
  ]
}

movement

Proves the subject moved, reached a minimum velocity, or changed rotation during held input. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: entity.observe
Field Type Required
entity string no
closesDistanceToPosition { position: [number, number, number], min: number } no
facesMovementWithinDegrees number no
axis string no
minAxisDelta { axis: string, min: number } no
minResolvedAxisDelta { axis: string, min: number } no
maxTiltDegrees number in [0, 180] no
minDistance number no
maxDistance number no
minVelocity number no
pathLength number no
notFacing { entity: string, minDegrees: number } no
notFacingPosition { position: [number, number, number], minDegrees: number } no
reachesPositionWithin { position: [number, number, number], maxDistance: number, atStep?: string } no
rotationChanged boolean no
json
{
  "movement": {
    "entity": "player",
    "minDistance": 0.5,
    "minVelocity": 0.01,
    "rotationChanged": true
  }
}

camera

Proves a camera follows an entity or keeps a target in view. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: camera.observe, entity.observe
Field Type Required
entity string no
follows string no
within number no
targetInViewport boolean no
json
{
  "camera": {
    "entity": "camera.main",
    "follows": "player",
    "within": 10,
    "targetInViewport": true
  }
}

components

Proves a live entity component value after the scenario or at named steps. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.components
Field Type Required
entity string yes
component string yes
path string no
equals json no
gte number no
lte number no
changed boolean no
atSteps Array<{ label: string, equals: json }> no
allowTrivial triviality reason no
json
{
  "components": [
    {
      "component": "Camera",
      "entity": "camera.main",
      "path": "fovY",
      "equals": 22,
      "changed": true
    }
  ]
}

resources

Proves resource state after the scenario through equals, gte, lte, textIncludes, or changed checks. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.resources
Field Type Required
id string yes
path string no
equals json no
gte number no
lte number no
textIncludes string no
changed boolean no
throughoutSteps boolean no
atSteps Array<{ label: string, equals?: json, textIncludes?: string }> no
allowTrivial triviality reason no
anyOf Array<{ path: string, equals?: json, gte?: number, lte?: number, textIncludes?: string, changed?: boolean }> no
json
{
  "resources": [
    {
      "id": "GameState",
      "path": "score",
      "gte": 1,
      "changed": true
    }
  ]
}

tags

Proves the final count of entities carrying a bounded runtime tag. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.tags
Field Type Required
tag string yes
count non-negative integer no
gte non-negative integer no
lte non-negative integer no
allowTrivial triviality reason no
json
{
  "tags": [
    {
      "tag": "coin",
      "count": 10
    }
  ]
}

signals

Proves a named Godot signal was emitted by the application during the run or at a labeled step. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.events
Field Type Required
atStep string no
entity string no
maxCount non-negative integer no
minCount non-negative integer no
name string yes
json
{
  "signals": [
    {
      "name": "collected",
      "entity": "player",
      "minCount": 3,
      "atStep": "last-coin"
    }
  ]
}

states

Proves an observed entity's final runtime-owned state-machine state. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.state
Field Type Required
entity string no
equals string yes
allowTrivial triviality reason no
json
{
  "states": [
    {
      "equals": "completed"
    }
  ]
}

hud

Proves retained UI/HUD text or values after the scenario. Use when that is the thing the scenario must prove.

  • Supported on: web · Requires: browser.dom
Field Type Required
id string yes
path string no
equals json no
gte number no
lte number no
textIncludes string no
changed boolean no
allowTrivial triviality reason no
visible boolean no
json
{
  "hud": [
    {
      "id": "score-label",
      "textIncludes": "Score"
    }
  ]
}

overlayNodes

Proves DOM state inside a same-origin webview overlay iframe. Use when that is the thing the scenario must prove.

  • Supported on: web · Requires: browser.dom
Field Type Required
overlayId string yes
selector string yes
attribute string no
equals json no
textIncludes string no
visible boolean no
json
{
  "overlayNodes": [
    {
      "attribute": "data-aiming",
      "equals": "true",
      "overlayId": "game-ui",
      "selector": "[data-testid=fps-crosshair]",
      "visible": false
    }
  ]
}

diagnostics

Proves console, network, runtime, and readiness diagnostics stayed clean. Use when that is the thing the scenario must prove.

  • Supported on: web · Requires: browser.console, browser.network, runtime.diagnostics
Field Type Required
noConsoleErrors boolean no
noNetworkErrors boolean no
noRuntimeDiagnostics boolean no
consoleErrorsOptOutReason non-empty string no
networkErrorsOptOutReason non-empty string no
runtimeDiagnosticsOptOutReason non-empty string no
runtimeReady boolean no
json
{
  "diagnostics": {
    "noConsoleErrors": true,
    "noNetworkErrors": true,
    "noRuntimeDiagnostics": true,
    "runtimeReady": true
  }
}

performance

Proves a live render sample exists and optionally bounds frame time, an fps floor, per-phase frame budget, whole-frame or per-pass draw calls and triangles. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.performance
Field Type Required
maxFrameMsP95 number no
minFps number no
maxPhaseMsP95 { [phase]: number } no
maxDrawCalls number no
maxPassDrawCalls { [pass: 'main' 'shadow'
maxPassTriangles { [pass: 'main' 'shadow'
maxTriangles number no
json
{
  "performance": {
    "maxPassDrawCalls": {
      "shadow": 400
    },
    "maxPhaseMsP95": {
      "render": 12
    },
    "minFps": 30
  }
}

parity

PRD-222 Tier 2: proves the fps ratio of a parity pair — the same scene on the same device, once in the browser and once native. Lives in the second run and names the first run's saved report; refuses a pair whose halves ran on different devices, a thermally confounded half, or a missing series on either side. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.performance
Field Type Required
minFpsRatio positive number no
minRenderParity positive number no
referenceReport non-empty string no
referenceSide 'browser' or 'native' no
reference object no
json
{
  "parity": {
    "minFpsRatio": 0.85,
    "referenceReport": "reports/native.json",
    "referenceSide": "native"
  }
}

visibility

Proves projected entity visibility in the viewport. Use when that is the thing the scenario must prove.

  • Supported on: web · Requires: entity.bounds
Field Type Required
entity string no
minProjectedPixels number no
maxOffscreenRatio number no
present boolean no
allowTrivial triviality reason no
json
{
  "visibility": [
    {
      "entity": "player",
      "minProjectedPixels": 1200,
      "maxOffscreenRatio": 0.05
    }
  ]
}

world

Proves runtime world metadata exposed by the application bridge. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.world
Field Type Required
seed json yes
runtime object no
json
{
  "world": {
    "seed": 90210
  }
}

contacts

Proves contact or trigger evidence appeared in the effect log. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.contacts
Field Type Required
atStep string no
entity string no
with string no
kind string no
minCount number no
maxCount non-negative integer no
requiredOn Array<'web' 'desktop'
json
{
  "contacts": [
    {
      "entity": "player",
      "with": "pickup",
      "kind": "trigger",
      "minCount": 1
    }
  ]
}

settled

Proves an observed cohort of matching physics bodies is asleep in a retained physics-debug sample. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.physics
Field Type Required
entity string no
atStep string no
minBodies positive integer no
compareToStep string no
minMeanPoseDistance positive number no
requiredOn Array<'web' 'desktop'
allowTrivial triviality reason no
json
{
  "settled": [
    {
      "atStep": "fall-and-settle",
      "minBodies": 15
    }
  ]
}

occluded

Proves rendered scene geometry occludes the segment between an origin entity and target. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.physics
Field Type Required
entity string no
target string no
allowTrivial triviality reason no
json
{
  "occluded": [
    {
      "entity": "listener",
      "target": "emitter"
    }
  ]
}

animation

Proves animation evidence appeared in the effect log or runtime observation. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.animation
Field Type Required
entity string no
clip string no
entered boolean no
advancedFrames number no
finished boolean no
maxFootSlide number no
strideSynced boolean no
allowTrivial triviality reason no
json
{
  "animation": [
    {
      "entity": "player",
      "clip": "run",
      "entered": true,
      "advancedFrames": 5,
      "finished": false
    }
  ]
}

audio

Proves which labelled audio cues the game actually played, and how often. Every other audio check is about the file — it exists, it decodes, it is inside its budget — and all of them stay green while a one-shot line sounds a second time mid-match. Use when that is the thing the scenario must prove.

Label a cue by passing a cue name to the audio bus when the game plays it; unlabelled sounds are never counted. When it fails, observations.json/runtimeObservations/gameplay/audio/recentCues says when each play happened.

  • Supported on: web, desktop · Requires: runtime.audio
Field Type Required
cue string yes
minPlays non-negative integer (defaults to 1) no
maxPlays non-negative integer; 0 proves silence no
minGapMs non-negative integer; fewest ms since the line before it no
json
{
  "audio": [
    {
      "cue": "speech:p01",
      "minPlays": 1,
      "maxPlays": 1,
      "minGapMs": 500
    }
  ]
}

scene

Bounds the room the game is played in — the lights, materials, fog and camera framing the renderer was handed. Fails closed on a bridge that does not report the scene. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: scene.observe
Field Type Required
cameraClearsScene boolean no
fogClearsScene boolean no
litMaterialsAreLit boolean no
minVisibleLights number no
allowTrivial triviality reason no
json
{
  "scene": {
    "litMaterialsAreLit": true,
    "minVisibleLights": 1
  }
}

sceneNodes

Bounds on named nodes of the scene graph — where an object is, whether it is on screen, whether its textures loaded. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: scene.nodes
Field Type Required
select object no
visible boolean no
inFrustum boolean no
texturesLoaded boolean no
animated boolean no
minCount number no
maxCount number no
minTriangles number no
json
{
  "sceneNodes": [
    {
      "inFrustum": true,
      "select": {
        "nameContains": "crate"
      },
      "texturesLoaded": true,
      "visible": true
    }
  ]
}

causedBy

Relates a tick-stamped effect to the tick-stamped cause that must precede it — the 'because' a terminal state needs. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.transitions
Field Type Required
cause object no
effect object no
neverBefore boolean no
withinTicks number no
json
{
  "causedBy": [
    {
      "cause": {
        "contact": {
          "entity": "player",
          "kind": "trigger",
          "with": "seal"
        }
      },
      "effect": {
        "becomes": "won",
        "path": "state.status"
      },
      "neverBefore": true,
      "withinTicks": 4
    }
  ]
}

startup

Bounds when the application's startup milestones happened: the world entered, first-use compilation settled, readiness reached — in milliseconds since navigation, from the runtime's own startup timeline. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.startup
Field Type Required
maxEnteredMs number no
maxCompileSettledMs number no
maxReadyMs number no
json
{
  "startup": {
    "maxEnteredMs": 2500,
    "maxReadyMs": 8000
  }
}

renderChain

Proves the render chain reports its quality tier, authored stage ids/order, graph-output changes for named stages, and, when asserted, a bounded temporal-history rejection fraction. Use when that is the thing the scenario must prove.

  • Supported on: web, desktop, bevy · Requires: runtime.renderChain
Field Type Required
tier string no
stages object no
contributions object no
velocity object no
json
{
  "renderChain": {
    "tier": "high",
    "stages": {
      "includes": [
        "outline",
        "kuwahara",
        "watercolor"
      ],
      "order": [
        "outline",
        "kuwahara",
        "watercolor"
      ]
    },
    "contributions": {
      "graphOutputChanged": [
        "outline",
        "kuwahara",
        "watercolor"
      ]
    }
  }
}

Captured tone

Use tone to gate exposure from decoded frame pixels. Each row takes an optional atStep (named step; omitted means final frame) and at least one metric bound. mean, p1, p50, p99 use display luminance 0..255; clipFraction and blackFraction use 0..1. Bounds take inclusive min/max numbers. Empty, invalid and contradictory bounds throw at load; a missing capture fails.

json
{ "tone": [{ "atStep": "landed", "mean": { "min": 60, "max": 140 }, "p99": { "min": 150 }, "clipFraction": { "max": 0.005 } }] }

The host uses one rounded 256-bin Rec.709 luminance histogram; fully transparent pixels are excluded. The CLI command threenative-playtest tone shot.png other.png reports the same six metrics plus an unweighted frame-average row without starting a browser. Tone is an exposure gate, not an aesthetic verdict. Retain and inspect the actual runtime screenshots.

Edit this page on GitHub ↗