W-039: ecs-prefab instantiate and copy (PREFAB-F02)¶
Type: feature · Size: M · Epic: E-prefab-v0 · Target: packages/ecs-prefab · Breaking: no Depends: W-038 Refs: spec PREFAB-010..PREFAB-021
Goal¶
Add instantiate(world, prefab) that spawns one entity with IsA(prefab) first, then a copy of the prefab's copyable traits. Values are copied once, SoA by value and AoS by structuredClone, and a clone failure throws before any entity exists. The spike (S1) decides how to skip relation-backed traits (spec OQ6) before any copy code is written. Chain walking across variants comes in W-041.
Steps¶
- S1 (spike, OQ6): find out, using only koota's public API, how to tell relation-backed traits from plain traits in
world.traits; confirm by running code (not only reading source) what a backing trait copied by value does (expected:has()true,targetsForempty); record the result in the Log, then STOP for the OQ6 decision (see Decision options), whether or not public detection is found. files: none - S2 (PREFAB-010, PREFAB-011, PREFAB-012, PREFAB-013, PREFAB-019): each call returns a new entity; the instance has the copyable traits and
IsA(prefab)as the first trait, and does not getPrefab,IsExcluded, or copiedIsApairs; copy order isworld.traitsorder; files: packages/ecs-prefab/src/instantiate.ts, packages/ecs-prefab/src/is-a.ts, packages/ecs-prefab/src/copyable-traits.ts, packages/ecs-prefab/tests/instantiate.test.ts - S3 (PREFAB-014, PREFAB-015, PREFAB-016, PREFAB-017, PREFAB-018): copies are independent of the prefab and siblings; SoA values copied by value; later prefab changes do not reach existing instances; AoS values are
structuredClonecopies; an uncloneable AoS value throws naminginstantiatewith no entity left behind; files: packages/ecs-prefab/src/copy-value.ts, packages/ecs-prefab/tests/instantiate-copy.test.ts - S4 (PREFAB-020, PREFAB-021): non-
IsApairs on the prefab are not copied;onQueryAddfires once per instance; files: packages/ecs-prefab/tests/instantiate-queries.test.ts - S5 (changeset): add
.changeset/w-039-ecs-prefab-instantiate.md(patch,@workspace/ecs-prefab)
Decision options¶
User/team picks one after S1. No later step starts before the choice is logged.
- A: package-owned list of plain traits (spec fallback; addToPrefab helper)
- B: explicit opt-in registry
- D: copy pairs via targetsFor for an app-supplied relation list
- E: forbid relations on templates (guard)
- F: IsA only
- G: upstream koota accessor
- C (detect by schema, tag or name): rejected, backing traits look like tags
- Not a decision: the earlier leaning, if S1 finds no public detection, is A with E as guard.
- If S1 finds public detection, options A, B, D and E may be unnecessary.
Log¶
- 2026-11-01 created from the approved spec split of W-005 (Triage)
- 2026-11-01 gate (Triage): S1 is a hard gate. Its result goes in this Log, then work stops until the user/team decide OQ6 (ecs-prefab spec L176, demo-dnd OQ8). The decision is logged before S2 starts. No spec-update item is created now.