packages/world/prefabs.ts compiles saved prefab source into ordinary world-format-2 content, the existing primitive manifest, a dependency index and source mappings. Simulation.create(build.world) consumes the result without a prefab interpreter or source files at runtime.
The checked-in environment kit contains a three-piece stone arch, an inherited lantern arch and a planter. It instantiates the kit in two places, including an explicit lamp-material override. These are primitive environment pieces; GLB, textures, skeletal animation and external asset processing remain separate work.
#File workflow
mkdir -p authoring-output
npm run author -- build-prefabs games/engine-zoo/prefab-kit.json authoring-output/kit-build.json
npm run author -- expand-prefabs games/engine-zoo/prefab-kit.json authoring-output/kit-world.json
npm run author -- import authoring-output/kit-world.json authoring-output/kit-document.jsonBoth compiler commands run through the isolated build worker and validate before creating an output and refuse to overwrite an existing file. The build contains its expanded world and primitive assets; it has no runtime filesystem references. expand-prefabs writes just that world for the normal authoring CLI. Importing expanded content bakes the prefab instances into ordinary objects; subsequent document edits do not write overrides back to prefab source. Keep the original source for linked prefab editing and rebuild it deliberately. Neither command deploys or changes a running room.
#Source contract
A source has format: 1, a persisted world-2 world, prefabs and instances. The world's existing objects are inline content. Definitions and instances have distinct source IDs. Arrays of definitions, places, members, bindings and instances may be reordered without changing the semantic build hash. Ordered gameplay arrays, such as lights and interactions, retain their source ordering.
Each prefab has id, optional extends and members. A member has a stable key and a patch containing ordinary scene-object properties. Identity fields are forbidden in templates. A child inherits its parent's resolved members and applies its own patches. Properties replace whole values: replacing a material replaces the whole material rather than merging individual channels. null removes an optional property; a null member patch removes that member. Mesh, position, scale and material cannot be removed. New members must supply every required property. Definitions are validated even when unused.
Each instance records id, prefab, destination place, offset, explicit bindings and overrides. Bindings map every surviving member key to its place-local source name and persisted entity ID. Save those IDs once at authoring ingress; the compiler never allocates them. Renaming an instance/member display source or moving its destination preserves the bound identity. Missing, duplicate or stale bindings fail. When a prefab adds or removes a member, update the bindings explicitly; an old override targeting a removed member reports a migration conflict rather than disappearing silently.
Instance overrides use the same whole-property patch rules but may only target existing prefab members. They can remove a member if its binding is also removed. Inherited changes preserve explicit instance overrides. offset translates positions and hinge pivots into the destination place's coordinates. Instance rotation/scaling, nested instance placement and hierarchy composition are not yet implemented; ordinary member rotation/scale are supported. Interactions and light definitions remain in the containing place: attaching an interaction string to a member does not create a new interaction or rewrite references.
#Reproducibility and dependencies
The prefab-world.1 compiler emits nodes for each prefab, instance, place and the world. Keys include both compiler versions, settings, explicit override intent and dependency hashes. Inheritance and instance references form directed edges. changedBuildDependencies(before, after) compares node hashes and includes deleted nodes. An edit to the standalone planter invalidates its instance, containing place and world; the arch definitions, arch instances and annex remain unchanged.
The manifest records the exact resolved prefab hash and entity IDs for each instance. sha256 covers the semantic payload, including world, primitive manifest and dependency index. The sources table is excluded from that hash because its JSON-pointer offsets change when source arrays are reordered; it is diagnostic data, not a signed provenance record. PrefabBuildError carries a JSON-pointer path, stable error code and a readable message, including the inheritance chain for a cycle.
This implementation computes a complete build and supplies content-addressed invalidation. The local build coordinator now schedules bounded child processes and fences superseded results while preserving the last successful build. It does not yet reuse cached build outputs. Durable/authenticated job ownership, multi-coordinator fencing, source-file line maps, script metadata, asset imports and live activation remain open.
#Bounds and verification
Input and output JSON are bounded to 16 MiB. Each resolved member is capped at 16 KiB; cumulative resolved-definition and expanded-world bytes are checked before retaining additional copies, so repeated instances cannot bypass the byte budget. Defaults allow 256 definitions, 32 inheritance levels, 256 members per resolved prefab, 16,384 total resolved definition members, 4,096 instances and 65,535 expanded objects. maxExpandedObjects can lower the last bound and participates in every dependency key. Existing primitive-compiler limits still apply, including 4,096 objects per place. Exceeding any bound rejects the build without publishing a partial world.
The headless suite exercises inheritance/overrides, persistent identity, translation, source reordering, selective invalidation, explicit removals, cached-depth rejection, cycles, malformed fields, collisions, budgets, caller-input isolation and CLI output preservation. A complete golden fixture runs the built world through the real native Simulation and exact checkpoint restore. Observatory compares that fixture in a browser worker against Node.
The rendered worker also builds the kit through Simulation and ECS presentation, changes an inherited lintel material, verifies changed pixels, rejects a cyclic source, and verifies that the last valid frame remains byte-identical. Its independent preview releases native and GPU resources. This is a compiler preview, not evidence that live gameplay state can migrate between content releases.
Clean-source acceptance records the complete 23-scenario browser run on ec34043, then 145 headless checks and the final rendered contract recheck on byte-limit follow-up 4224472. The worker report and preview capture identify that final source. The prefab feature subsequently passed all CI jobs. The byte-limit follow-up passed core and Docker; its browser CI was still running at the latest check.
The current runtime evidence is prefab-world.2, which records simulation profile 5. The prefab compiler remains prefab-world.1; a regression compares all compiler/content evidence against the retained version-1 runtime fixture, allowing only fixture metadata and checkpoint identity to change.
#Source 2: models and nested arrangements
Source format: 2 uses persisted world format 5 and the separately versioned prefab-world.2 compiler. Source 1 retains its exact primitive compiler identity and pinned results. Both pass through the isolated build coordinator; input hashes and reply checks bind the appropriate source/compiler profile.
Modern members accept model, parent and group: true. A group has only position, scale, optional rotation and optional parent. Template parents name another member key; expansion resolves that key to the instance's persisted group binding. Every object and group needs a binding. The instance offset applies only to roots, so nested local transforms are preserved. A selected branch captured from inside an external group keeps its world pose by converting its root frame; unsupported shear is rejected. Cycles, missing/non-group parents, duplicate identities/names and the existing group budgets reject the whole source.
Model artifacts live in the source world's embedded catalog. Model dependency nodes feed the definitions, instances and places that actually use them, including model overrides. An asset change therefore invalidates dependent prefabs even when their model name remains unchanged. The world depends on the complete catalog. Inherited changes retain explicit instance overrides and bound identities.
Save arrangement… in the atelier downloads this portable source for a saved selection. It captures complete selected branches, their local hierarchy, appearance/collision properties and only the imported models they reference. Gameplay bindings remain on the originals. The file includes one example instance with the captured bindings and offset; it can be built with the existing build-prefabs / expand-prefabs CLI. The selection limit is 64 roots / 256 total members. The authoring capture layer depends on the world compiler to validate the complete exported source before download.
The browser arrangement library imports these files, previews definitions, places fresh linked instances, and accepts explicit source revisions while retaining local overrides. Export itself does not attach original objects to a definition. Whole-instance transform authoring uses ordinary root/group transforms; nested prefab instances and richer membership migration tooling remain in the full plan.
#Saved authoring links
Authoring documents optionally carry a checksummed prefabs library containing owned source snapshots and instance bindings. Legacy documents without this field retain their previous hash profile. Explicit world commands materialize every placement/update; the source validator rejects a document whose linked geometry, model artifacts or source checksums disagree. Players consume the expanded ordinary world and need no source interpreter.
Ordinary member edits record whole-property overrides, root positions relative to the instance offset, and parent references as member keys. Deletion removes the binding and records an explicit removed-member patch. Undo restores exact metadata after restoring geometry, so reload and replay preserve override intent. Source changes never silently discard edited members. The authoring preparation API compares resolved member keys, retains surviving bindings, allocates fresh IDs only for additions and emits explicit creation/deletion commands before replacing the source link. Removing a locally edited member or changing its object/group kind reports a migration conflict. Deletion overrides remain while their key exists; removing an already-deleted source member drops its obsolete marker. Durable undo restores exact geometry, metadata and source ordering.
Merge history protects source reads against replacement and records a whole-instance write dependency for member edits. This deliberately conflicts concurrent edits inside one instance to prevent inverse metadata from erasing another builder's override; finer per-member metadata merging remains open. The library is bounded to 16 sources, 64 instances and 6 MiB, independently of compiler limits.
Detaching a linked instance removes only its authoring link. Runtime geometry, imported assets and other copies remain unchanged. Undo restores its exact metadata, with entity/source read dependencies protecting against later foreign edits. Source-only updates and empty-instance migrations remain valid authoring transactions. Rich per-member conflict resolution and key-renaming migrations remain future work.