---
title: "Test module replay format (version 1)"
url: https://recursiveai.org/onescope/docs/test-module/
description: "The test module is a small integer simulation: up to 64 bodies bouncing in a 10 m box (lengths in millimetres), with a stage axis (apply input, gravity…"
version: "1.0.0"
source: https://github.com/recursive-ai-oss/onescope-csharp/blob/d681be56420836ea0d8874416367bc8181235813/docs/test-module.md
---

# Test module replay format (version 1)

The test module is a small integer simulation: up to 64 bodies bouncing in a 10 m box (lengths in millimetres), with a stage axis (apply input, gravity, move, bookkeeping). All integers are little endian.

```
magic          4 bytes   "OSTM"
version        u16       1
flags          u16       bit 0: hash rows present; bit 1: a start snapshot present; bit 2: a link block follows the setup; other bits must be 0
setup length   u32       6, or 10 when flag bit 2 is set
setup          6 bytes   u8 dimensions (2 or 3), u8 body count (1..64), u32 seed
link block     4 bytes   (only with flag bit 2) u8 role: 1 sends a body away, 2 receives it, 3 neither (declares the density and occupancy fields only); u8 body index (roles 1 and 2); u16 transfer step
step count     u32       at most 16 777 216
start snapshot u32 length, then the world snapshot, only if flag bit 1: the state a live attach began from; its first 8 bytes are the host's step at that moment, which is the recording's start step
hash rows      u64 x (step count + 1), only if flag bit 0; row i is the world hash at the start step + i
per step       u16 input length, then that many bytes
```

A step input is a sequence of operations: `1 body dx dy dz` (impulse), `2 body x y z` (teleport), `3 body` (remove), `4 gravity`, `5 x y z` (spawn a body at rest; it takes the next free index, and does nothing when the world already holds 64 bodies). `body` is a u8 index, the rest are i32. Files written before spawn existed stay valid.

Every length is checked against the bytes that remain before anything is allocated; trailing bytes are an error. Errors are `ScopeFormatException`, naming the input and what was expected at which offset.

A world with a link block also declares the two scalar fields (`density`, per grid vertex, and `occupancy`, per triangle, both over an 8 by 8 floor grid) and their overlays. Role 1 removes the body at the transfer step and role 2 holds it from that step on (it is absent before, and appears at the middle of the box with a fixed velocity); the sender declares the link as `ContinuedBy` and the receiver as `Continues`, with link keys `tm-left` and `tm-right`. `TestGhost` writes a pose cache from a run, with an offset, as a stand-in for a reference engine's dump.

`testdata/test-10000.ostm` (3D, 24 bodies, 10 000 steps) and `testdata/test-2d-600.ostm` are produced by `tools/GenTestData`, as are `test-field-2d.ostm`, `test-field-3d.ostm`, the linked pair `test-link-left.ostm` and `test-link-right.ostm` (body 3 moves at step 150) and the pose cache `test-2d-ghost.ospc` (the 2D replay's bodies for 40 frames from step 200, drawn 400 mm to the right).

## Live stream

The test module is also a live module. Its probe snapshot is the 6-byte setup followed by the world snapshot (a 64-bit host step, gravity, then per body (as many as the world holds, up to 64, spawned ones included) alive, still count, position and velocity as 32-bit integers). Each record is one step's input. A viewer's world starts at the snapshot at the host's step, so the viewer's step numbers are the host's and hashes compare by step number. A live source written as a replay sets flag bit 1 and holds the snapshot, so reopening it reproduces the same hashes.
