Smol¶
| Distribution | archetype-smol |
| Import package | archetype.smol |
| Public surface | Component, Processor, World, RunResult |
Dependency on archetype-ecs |
No |
Smol is a tiny synchronous, in-memory DataFrame ECS for learning the core
Archetype model. It is a separate engine, not a compatibility facade, storage
backend, or world library for archetype-ecs.
Install¶
Learn the loop¶
from daft import col
from archetype.smol import Component, Processor, World
class Position(Component):
x: float = 0.0
class Move(Processor):
components = (Position,)
def process(self, df, *, tick):
return df.with_column("position__x", col("position__x") + 1)
world = World(processors=[Move()])
entity_id = world.spawn(Position(x=2))
world.step()
assert world.query(Position).to_pylist() == [
{
"entity_id": entity_id,
"tick": 1,
"is_active": True,
"position__x": 3.0,
}
]
The loop is deliberately visible:
- component types define an archetype table;
- matching processors transform its Daft DataFrame in priority order;
- Smol materializes each table at the in-memory boundary;
- the world publishes every successful table together as the next tick; and
query()andhistory()return reviewable DataFrames.
Surface¶
| API | Contract |
|---|---|
Component |
Frozen, validated Pydantic value whose fields become prefixed DataFrame columns |
Processor.process(df, *, tick) |
Synchronous lazy transform for every archetype containing the declared components |
World(processors=...) |
Create one single-threaded in-memory world |
add_processor() / remove_processor() |
Change the priority-ordered processor set between steps |
spawn() / update() / despawn() |
Mutate current state immediately without changing an entity's component signature |
step() |
Compute every active archetype and publish all successful results at the next tick |
run(steps) |
Execute zero or more steps and return RunResult |
query(*component_types) |
Return active current rows containing the requested component subset |
history(*component_types) |
Return the retained per-tick snapshots, including despawn tombstones |
There is at most one snapshot for an entity at a tick. Multiple immediate
mutations before the next step() update that current snapshot; a successful
step publishes the next one. Passing no component types to query() or
history() selects metadata for every matching entity.
Deliberate limits¶
| Smol includes | Smol omits |
|---|---|
| Frozen typed Component records | Persistent or remote storage |
| Synchronous DataFrame Processors | Async execution and per-table concurrency |
| Priority and component-subset matching | Commands, permissions, and audit |
| Immediate spawn, update, and despawn | Activities and provider recovery |
| Atomic in-memory steps | Hooks and Resources |
| Current and historical snapshots | Runtime/API/CLI hosting |
Processors must preserve input columns, metadata, and each entity exactly once. A failed transform publishes no state, history, or tick. Python side effects inside a processor cannot be rolled back, so processors should remain pure.
Component fields must resolve to None, bool, int, float, str,
bytes, or nested lists of those scalar values. Smol rejects mappings,
tuples, enums, nested Pydantic models, and other values whose Python identity
would be erased by DataFrame materialization. Encode structured teaching state
as a JSON string in a _json field. This keeps the small engine's validation
boundary exact instead of reproducing the production framework's Arrow
serialization policy.
Choose the production framework when¶
Use archetype-ecs when work needs durable worlds,
append-only storage, crash recovery, concurrent execution, commands,
Activities, artifacts, evaluation, API hosting, or separately installed world
libraries. There is no migration or alias promise between the two engines;
their shared vocabulary exists to make the production architecture easier to
understand.