Skip to content

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, targetsFor empty); 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 get Prefab, IsExcluded, or copied IsA pairs; copy order is world.traits order; 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 structuredClone copies; an uncloneable AoS value throws naming instantiate with 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-IsA pairs on the prefab are not copied; onQueryAdd fires 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.