World build document authoring compile apply loop
Use this for
Not for
Pairs with: Blank game quickstart cold start recipe empty project, Game loop assembly entities scripts HUD win shippable loop, Playtest verification loop proving gameplay executed evidence, Collectibles pickups scoring and win condition
When to use
On a blank game whose user asked for a playable genre, do NOT rebuild a player, camera, or controller here first: start from a certified starter template (see blank-game-quickstart, template plus delta), then use this document to author only the delta on top of it.
The three tools
world_build_get_operation_catalogreturns the high level operation catalog (intent level operation ids, not raw component payloads). Read it once so you author with catalog operations instead of endpoint shaped payloads.world_build_compile_semantic_operationsis the DRY RUN twin: it validates the document and returns diagnostics and the lowered operations without writing.world_build_apply_semantic_operationsis the WRITE twin: it compiles through the SAME validator, then lands the result as one atomic, idempotent, undoable transaction. A replay of the same document is a no op.
The loop
- Ensure a target world exists (a blank game ships a start world). Name it with
context.world_idORcontext.world_key(either coordinate resolves); a single-world game needs neither - apply binds the only world. Apply does NOT create the game or world. - Read the operation catalog once with
world_build_get_operation_catalog. - Write the whole intent as one
operationsarray. Each operation is{ "operationId": <catalog id>, "payload": { ... } }. - If you are confident, call
world_build_apply_semantic_operationsdirectly. If you are uncertain, callworld_build_compile_semantic_operationsfirst, read the diagnostics, fix the document, then apply. - Read the result:
createdEntityCount,createdKeys,revision, and the compilerdiagnostics. Then verify by playing (see playtest-verification-loop).
What apply lands (the seam)
Apply lands, in ONE atomic idempotent commit: lowered project.entity.create (entities into the target world, honoring EXPLICIT per-item key and tags - required exact keys are spec, write them on the items), project.component.create and project.component.update, and - as receipted post-commit stages under the same idempotency umbrella - script.semantic_patch.apply (creates the script shell if missing, fills version and fingerprint from its own read, patches, then attaches the ScriptComponent to every entity named in the cluster's entityKeys), typed_state.store.create (resolve-or-replay), and ui.hud.bind/HUD recipes. EVERY lane is wired: the whole playable game - bodies, behavior loop, per-entity script attachments, typed-state store, HUD - lands in ONE document. gameplay.collectible_cluster.create is the loop carrier: give it tag, entityKeys, scriptKey, score, victory and it composes the touch-pickup trigger loop, attaches the script to every listed collectible, completes a static trigger collider on every collectible body this same document creates (an item that declares its own collider wins; do NOT add RigidBody to a static sensor), and auto-creates the referenced store. prefab.create defines a named reusable entity subtree (components + scripts per entity) that scripts spawn with spawnEntityFromTemplate prefabKey; the default gameplay loop's projectile prefab is compiler-ensured, and an explicitly named prefab is yours to declare (same document or precision tool). Package and proof operations are compile-visible but never applied here (proofs run via world_playtest_scenario).
Worked example: the arena shell in one document
A blank game already has a start world; read its id from project_get_graph_snapshot. Then apply the shell (floor, four walls, a ring of coin bodies, camera, sky) as ONE transaction with real operation ids:
world_build_apply_semantic_operations({
game_id: <gameId>,
context: { world_id: <worldId> },
operations: [
{ operationId: "entity.role_batch.create", payload: { items: [
{ name: "Arena Floor", primitive: "box", position: {x:0,y:0,z:0}, scale: {x:20,y:1,z:20}, role: "boundary", materialTint: "#3f3f46" },
{ name: "Wall North", primitive: "box", position: {x:0,y:1,z:-10}, scale: {x:20,y:2,z:1}, role: "boundary" },
{ name: "Wall South", primitive: "box", position: {x:0,y:1,z:10}, scale: {x:20,y:2,z:1}, role: "boundary" }
/* Wall East, Wall West ... */
] } },
{ operationId: "entity.primitive_batch.create", payload: { items: [
{ key: "arena_coin_1", name: "Coin 1", tags: ["coin"], primitive: "sphere", position: {x:6,y:1.2,z:0}, scale: {x:0.6,y:0.6,z:0.6}, materialTint: "#facc15" },
{ key: "arena_coin_2", name: "Coin 2", tags: ["coin"], primitive: "sphere", position: {x:4.24,y:1.2,z:4.24}, scale: {x:0.6,y:0.6,z:0.6}, materialTint: "#facc15" }
/* arena_coin_3..8 around a radius 6 ring ... */
] } },
{ operationId: "gameplay.collectible_cluster.create", payload: {
tag: "coin", clusterKey: "arena_coins", scriptKey: "script.arena.coin_pickup",
entityKeys: ["arena_coin_1", "arena_coin_2" /* ..._8 */], removeOnPickup: true,
score: { field: "score", increment: 1 },
victory: { message: "All coins collected!" } } },
{ operationId: "ui.hud_recipe.apply", payload: { panelKey: "arena_hud_score", name: "Score HUD" } },
{ operationId: "camera.hero_frame.set", payload: { position: {x:0,y:14,z:16} } },
{ operationId: "environment.sky_atmosphere.apply", payload: {} }
]
})
The cluster operation composes the whole gameplay loop: an onTriggerEnter handler that despawns the touched collectible and increments the score, an onTick handler that re-scans the tag, writes the won flag, and declares the win condition, a HUD wire when the document carries a panel, the ScriptComponent attached to every entity in entityKeys, a static TRIGGER COLLIDER completed onto every collectible body created in the same document (no RigidBody - a static sensor plus the KCC player is the proven trigger pair), and the referenced typed-state store auto-created. Explicit item keys make required exact keys land exactly.
One call, one commit, one undo unit. entity.primitive_batch.create and entity.role_batch.create each fan a payload.items array (max 32 per batch) out to one entity per item; the compiler owns the TransformComponent, RenderableComponent, and material defaults. Apply returns the created keys.
Compile diagnostics repair loop
When a document is rejected, world_build_apply_semantic_operations returns { ok: false, stage, code, message, diagnostics } and writes NOTHING. Common stages: compile (a payload the validator refused), budget (the document lowers to more than the apply cap; split it), owner (a post-commit stage failed or is owner_unavailable - the graph part COMMITTED and the result names what landed; finish the unavailable lane with its precision tool), apply (a transaction conflict). Read diagnostics, edit the one offending operation, and re apply. Prefer world_build_compile_semantic_operations to inspect diagnostics without a write when you are iterating.
The precision tier (per mutation is the repair tool, not the default)
The document carries the WHOLE game: bodies with exact keys and tags, the behavior loop with per-entity attachments (the cluster operation), the typed-state store, and the HUD. Do not follow a document with a string of project_add_component / project_create_ui_panel / project_create_data_store calls to "finish" it - the document already landed those lanes. Reach for a single project_create_entity, project_add_component, or a field update only to REPAIR or precisely target ONE entity after verification. The document is the write path; the per mutation call is the scalpel.
Verify
project_get_graph_snapshotto confirm the created entities and the newrevisionlanded.world_playtest_scenarioto PLAY the result (see playtest-verification-loop): an after state that is identical to the before state is not proof of gameplay.