Reading the table
POST /observe returns the whole table as JSON: a tick, an entity count, and one object
per entity carrying its components.
| entity | transform.position | velocity.linear | physics_body | health [cur,max] | team |
|---|---|---|---|---|---|
0 (gen 1) | [0.083333336, 1.0, 0.0] | [1.0, 0.0, 0.0] | Kinematic | [100, 100] | 1 |
1 (gen 0) | [5.0, 1.0, 0.0] | — | — | [60, 60] | 2 |
velocity and a physics_body, so a system moved it along +x; entity 1 has
neither, so it stayed put. Nothing about the world is hidden behind accessors — what you read
is the state.
Entities are generational ids
An entity id has two parts: an index and a generation. When an entity is despawned its index can be reused, but the generation bumps — so a stale reference to the old occupant is detected rather than silently pointing at a new one. Thread theid and generation
returned by /spawn through later calls.
The
/observe view is a flattened, readable projection of the underlying components —
for example health is reported as [current, max]. It is built from explicit typed reads,
not reflection: the agent gets plain data, not an opaque handle.Editing the table
Because the world is data, every change is a plain write — no editor, no scripting host:Add a row
POST /spawn creates an entity with the components you give it.Edit a cell
POST /entities/{id}/components adds or updates components on an existing entity.Remove a row
POST /despawn removes an entity by id + generation.Add logic
POST /rule/create adds a rule — logic is data too.The same table, forked and replayed
Because the world is a self-contained table of plain data, the engine can copy it to run a counterfactual, and hash it into a stable digest to verify a replay is exact. The canonical, fully-typed form of this table — used by the evaluation track as the answer key — sorts entities by id and components by name so two runs can be compared byte for byte.Run the loop yourself
Build this table, step it, and fork it in the Quickstart.