
Status: Draft (AS85-001)
Date: 2026-03-05
Canonical schemaVersion: aurajs.game-state.v1
Schema file: docs/schemas/game-state-v1.schema.json
This document defines the canonical game-state-v1 contract for:
aura state export)aura state patch / aura state apply)aura state apply response artifact)Top-level export payload is validated against the schema root object.
| Field | Type | Required | Notes |
|---|---|---|---|
schemaVersion |
string |
yes | Must be aurajs.game-state.v1. |
export |
object |
yes | Export metadata (mode, seed, frameIndex, fingerprint). |
state |
object |
yes | Canonical mutable state sections. |
State sections:
| Section | Required | Notes |
|---|---|---|
state.globals |
yes | Game-defined values; JSON-compatible. |
state.camera |
no | Canonical 2D camera state snapshot. |
state.scene3d |
no | Scene hierarchy and clip snapshots. |
state.physics |
no | Physics frame + body snapshots. |
state.ecs |
no | ECS entities/systems snapshot. |
state.tilemap |
no | Tilemap/layer snapshot. |
Schema rejects unknown top-level/state-section keys to keep contract drift-sensitive.
Mutation payload and result payload are defined in schema defs:
#/$defs/mutationRequest#/$defs/mutationOperation#/$defs/mutationResult{
"schemaVersion": "aurajs.game-state.v1",
"baseFingerprint": "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
"mutations": [
{
"order": 10,
"op": "set",
"path": "/state/globals/score",
"value": 1200
},
{
"order": 20,
"op": "increment",
"path": "/state/camera/zoom",
"by": 0.1
},
{
"order": 30,
"op": "array_insert",
"path": "/state/ecs/entities/0/tags",
"index": 1,
"value": "armed"
}
],
"options": {
"dryRun": false,
"verify": true,
"rollbackOnFail": true,
"maxMutations": 128,
"timeoutMs": 200
}
}
op |
Required fields | Semantics |
|---|---|---|
set |
order, path, value |
Set/replace target value at JSON Pointer path. |
delete |
order, path |
Remove target key/entry. |
increment |
order, path, by |
Numeric increment; fails on non-number targets. |
array_insert |
order, path, index, value |
Insert one array entry at index. |
array_remove |
order, path, index (count optional) |
Remove one or more array entries from index. |
Mutations MUST reject writes to:
/schemaVersion/export/export/*Mutations are allowed only under /state/*.
schemaVersion, export, stateexport: mode, seed, frameIndex, elapsedSeconds, fingerprint, capturedAtstate: globals, camera, scene3d, physics, ecs, tilemapscene3d.nodes: ascending idscene3d.clips: ascending clipIdphysics.bodies: ascending idecs.entities: ascending idecs.systems: ascending order, then nametilemap.maps: ascending idtilemap.layers: ascending order, then idorder ascending, then original array index.rollbackOnFail=true, any failed row must restore pre-apply state and return a rollback reason code.NaN, Infinity, -Infinity) are invalid and must fail validation.| Reason code | Phase | Trigger |
|---|---|---|
state_export_ok |
export | Export completed and payload matches schema. |
state_dry_run_ok |
apply | Dry-run succeeded; no mutation committed. |
state_apply_ok |
apply | Apply + optional verify succeeded. |
schema_version_mismatch |
validate | schemaVersion is not aurajs.game-state.v1. |
invalid_schema_payload |
validate | JSON shape/type mismatch vs schema. |
missing_required_field |
validate | Required key absent from contract payload. |
unknown_top_level_key |
validate | Unexpected root key present. |
unknown_state_section |
validate | Unexpected key under state. |
invalid_json_pointer |
apply | Mutation path is not valid JSON Pointer. |
immutable_path |
apply | Mutation targets immutable contract path (/schemaVersion, /export/*). |
unsupported_mutation_op |
apply | op is not one of canonical operations. |
type_mismatch |
apply | Target type incompatible with operation (for example increment on string). |
entity_not_found |
apply | Target ECS entity/path is absent. |
tilemap_layer_not_found |
apply | Target tilemap layer/path is absent. |
mutation_conflict |
apply | Mutations conflict under same base snapshot/fingerprint. |
mutation_budget_exceeded |
apply | Mutation row count exceeded options.maxMutations. |
state_payload_too_large |
apply | Payload exceeds deterministic size limit. |
state_apply_timeout |
apply | Apply exceeded deterministic runtime budget. |
verify_failed |
apply | Post-apply verification mismatch. |
rollback_unavailable |
apply | Rollback requested but runtime cannot rollback. |
rollback_failed |
apply | Rollback attempted but did not restore baseline. |
reasonCode must be stable and machine-readable. Human-readable message text is additive and non-canonical.
{
"schemaVersion": "aurajs.game-state.v1",
"export": {
"mode": "headless",
"seed": 12345,
"frameIndex": 0,
"fingerprint": "0000000000000000000000000000000000000000000000000000000000000000"
},
"state": {
"globals": {
"level": "intro",
"score": 0
}
}
}
{
"schemaVersion": "aurajs.game-state.v1",
"export": {
"mode": "native",
"seed": 777,
"frameIndex": 1440,
"elapsedSeconds": 24,
"fingerprint": "1111111111111111111111111111111111111111111111111111111111111111",
"capturedAt": null
},
"state": {
"globals": {
"level": "forest-03",
"score": 1200,
"flags": {
"bossUnlocked": true
}
},
"camera": {
"x": 10,
"y": -4,
"zoom": 1.25,
"rotation": 0,
"following": true,
"activeEffects": 0
},
"scene3d": {
"nodes": [
{
"id": 1,
"parentId": null,
"position": { "x": 0, "y": 0, "z": 0 },
"rotation": { "x": 0, "y": 0, "z": 0 },
"scale": { "x": 1, "y": 1, "z": 1 },
"visible": true,
"layer": 0
}
],
"clips": [
{
"clipId": 5,
"nodeId": 1,
"time": 0.5,
"playing": true,
"weight": 1
}
]
},
"physics": {
"frameIndex": 1440,
"snapshot": null,
"bodies": [
{
"id": 1,
"kind": "dynamic",
"position": { "x": 3, "y": 2 },
"velocity": { "x": 0, "y": -1 },
"angle": 0,
"angularVelocity": 0,
"sleeping": false
}
],
"lastReasonCode": null
},
"ecs": {
"entities": [
{
"id": 100,
"components": {
"transform": { "x": 3, "y": 2 },
"health": 95
},
"tags": ["player", "armed"]
}
],
"systems": [
{
"name": "movement",
"enabled": true,
"order": 0
}
]
},
"tilemap": {
"maps": [
{
"id": "overworld",
"layers": [
{
"id": 0,
"name": "ground",
"visible": true,
"opacity": 1,
"order": 0,
"collisionEnabled": false
}
]
}
]
}
}
}
{
"ok": true,
"reasonCode": "state_apply_ok",
"appliedMutations": 3,
"failedMutationIndex": null,
"fingerprint": "2222222222222222222222222222222222222222222222222222222222222222",
"warnings": []
}
{
"schemaVersion": "aurajs.game-state.v0",
"export": {
"mode": "headless",
"seed": 1,
"frameIndex": 0,
"fingerprint": "0000000000000000000000000000000000000000000000000000000000000000"
},
"state": {
"globals": {}
}
}
Expected reason code: schema_version_mismatch
{
"schemaVersion": "aurajs.game-state.v1",
"baseFingerprint": "3333333333333333333333333333333333333333333333333333333333333333",
"mutations": [
{
"order": 1,
"op": "set",
"path": "/export/seed",
"value": 999
}
],
"options": {
"dryRun": false,
"verify": true,
"rollbackOnFail": true
}
}
Expected reason code: immutable_path
{
"schemaVersion": "aurajs.game-state.v1",
"baseFingerprint": "4444444444444444444444444444444444444444444444444444444444444444",
"mutations": [
{
"order": 1,
"op": "set",
"path": "state/globals/score",
"value": 7
}
]
}
Expected reason code: invalid_json_pointer