Skip to content

W-061: ecs-relations symmetric relations (REL-F01)

Type: feature · Size: S · Epic: E-relations-v0 · Target: packages/ecs-relations · Breaking: no Depends: W-060 Refs: spec REL-001..REL-016 (group REL-F01; KL1, KL2, KL4, KL5)

Goal

Implement symmetric(world, rel) in packages/ecs-relations: per-world onAdd and onRemove hooks that mirror every pair of rel, with the re-entrancy guard, install-time mirroring, and the unsubscribe function. Headless, no transitive or traversal code yet.

Steps

  • S1 (REL-001, REL-002, REL-003, REL-008, REL-015): symmetric mirrors add and remove through per-world hooks; a re-added pair is a no-op; a remove for an absent pair is ignored; files: packages/ecs-relations/src/symmetric.ts, packages/ecs-relations/src/index.ts, packages/ecs-relations/tests/symmetric.test.ts
  • S2 (REL-004, REL-005, REL-006, REL-007, REL-010, REL-013, REL-016): destroy of either side does not throw; existing pairs are mirrored at install; exclusive replacement and self pairs; every removal route terminates; repeated install gives the same state; an uninitialized world throws naming symmetric and installs no hook; files: packages/ecs-relations/src/symmetric.ts, packages/ecs-relations/tests/symmetric-edge.test.ts
  • S3 (REL-009, REL-011, REL-012, REL-014; changeset): user hooks fire once per direction; pair data gets store defaults and is not synced; unsubscribe removes the hooks, is idempotent, and is safe after world.reset() or world.destroy(); installs are isolated per relation and per world; files: packages/ecs-relations/tests/symmetric-hooks.test.ts, packages/ecs-relations/src/symmetric.ts, .changeset/w-061-ecs-relations-symmetric.md (patch, @workspace/ecs-relations)

Log

  • 2026-11-01 created (Triage), split from the approved spec W-008
  • 2026-11-01 placed in Later (Depends W-060 not done; ready once the scaffold is done)
  • 2026-11-02 moved to Next (Triage): W-060 done, size S, target and Goal filled
  • 2026-11-02 moved down in Next behind the demo-critical items (Triage)