Phase-A.1 implementation per sporel-meta/docs/superpowers/specs/2026-06-09-phase-A-... Template-Only-Subset; slots/container/quality/condition produce loud-errors with explicit re-entry-phase hints. Position-Format revised to two flat scalar properties (position.x, position.y) per engine reality: no PROPERTY_TABLE exists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
lib-core.composition
Template + Instantiation + Tag-Index over engine.entity. Templates define
property defaults; create spawns an engine.entity with template
defaults + per-instance overrides; entities are indexed by template-id
and tag for cheap reverse-lookup.
Version: 0.1.0
Lib-ID: lib-core.composition
Requires: (none — uses engine entity.*, domain.contribute only)
Tags: composition, entity, template, tag-index
Topology
graph LR
this["lib-core.composition"]
engine["engine.*"]
this --> engine
Scope (v0.1.0 — Spine)
Template-Only-Subset per Phase-A Spec.
Supported:
define_template{id, properties, tags}create{template, properties}list_by_template(id),list_by_tag(tag)destroy(entity)
Deferred (Loud-Error if attempted):
slotsblock → Phase D trigger (Composite Items with Sub-Items)containerblock → Phase B trigger (Items + Inventory)quality,condition→ Phase J trigger (Damage / Wear)parent:template-inheritance → no consumer yet
API
composition.define_template(def)
Syntax: composition.define_template({id: string, properties?: table, tags?: array}) -> void
Example:
composition.define_template{
id = "sign",
properties = {
sprite_path = "sprites/sign.png",
text = "default sign text",
position = {x = 0, y = 0}, -- auto-flattened to position.x + position.y
},
tags = {"renderable"},
}
Description: Registers a template under id. All properties are
auto-declared as inert via domain.contribute with type inferred
from the default-value. Nested tables in properties are flattened
to dotted scalar keys (e.g. position = {x, y} → declares position.x
and position.y as separate number properties). Tag-list registers
the template in the tag-index used by list_by_tag.
Loud error(...) on: missing/non-string id, duplicate id,
non-table properties, malformed tags, attempted slots /
container / quality / condition / parent: block (each pointing
to its re-entry phase).
composition.create(spec)
Syntax: composition.create({template: string, properties?: table}) -> entity
Example:
local sign = composition.create{
template = "sign",
properties = {
position = {x = 144, y = 200},
text = "Hier steht: Willkommen im Spine.",
},
}
Description: Spawns an engine.entity, applies template-defaults
shallow-overridden by per-instance properties. Nested-table
overrides (like position = {x, y}) are flattened the same way as in
define_template. Per-instance properties that weren't declared by
the template get declared on-the-fly as inert. Entity is registered
in the template-index + tag-index of the template. Returns the
engine.entity handle.
Loud error(...) on: non-table spec, missing/unknown template,
non-table properties.
composition.list_by_template(id)
Syntax: composition.list_by_template(id: string) -> {entity, ...}
Example:
for _, sign in ipairs(composition.list_by_template("sign")) do
engine.print(sign:get_property("text"))
end
Description: Returns a shallow-copied list of all currently-alive entities created with the given template-id. Empty list if template is unknown or has no instances.
composition.list_by_tag(tag)
Syntax: composition.list_by_tag(tag: string) -> {entity, ...}
Example:
for _, e in ipairs(composition.list_by_tag("renderable")) do
local x = e:get_property("position.x")
local y = e:get_property("position.y")
-- ... render
end
Description: Returns all currently-alive entities whose template
declared tag in its tags array. Used by lib-core.render
draw_entities{tag="..."} (see A.2). Empty list if tag has no entities.
composition.destroy(entity)
Syntax: composition.destroy(entity) -> void
Description: Removes entity from template-index + tag-index +
destroys via engine.entity.destroy. Idempotent. If entity wasn't
composition-created, falls through to entity.destroy.
composition.list_templates()
Syntax: composition.list_templates() -> {id, ...}
Description: Debug/introspection — returns set of declared template-ids.
Conventions
- Property-Value-Types: number, string, boolean (engine §PROPERTY_*
catalog). Tables are flattened to dotted scalar keys; arrays inside
properties→ loud error. - Position-Format: stored as two scalar properties
position.xandposition.y(per Phase-A Spec §5 A-Q5 revision 2026-06-09 — engine has no PROPERTY_TABLE). - Tag-Index: maintained in lib, not in engine. Tag-lookup is O(1) by tag, list iteration is O(n) per tag.
composition.reg_id: internal book-keeping property on each created entity; used bydestroyto find the meta-record. Do not set or read manually.
Future Phases
| Phase | Triggers in Template | Spec |
|---|---|---|
| B (Items) | container = true |
inventory-model.md |
| D (Composites) | slots = {...} |
composition-model.md (full Slot-System) |
| J (Damage/Wear) | quality, condition |
damage-model.md |