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 |
{
"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 |
{
"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 |
{
"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' |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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' |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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 |
{
"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.
{ "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.