Every example on this page runs end-to-end with a single command. The
recommended pattern is ArchetypeRuntime for scripts. A small number of
examples intentionally drop lower when the read path is still below the
runtime API, most notably the time-travel example.
1. World Mutations¶
Demonstrates every mutation type: spawn entities with components, inject processors at runtime, RBAC permission checks, fork a world, and query the full command audit trail.
uv run python examples/01_world_mutations.py
Source: examples/01_world_mutations.py
This example uses ArchetypeRuntime plus world.as_actor(...) to show
multiple ActorCtx roles on one logical world without dropping to the
service layer.
What it demonstrates:
- SPAWN / DESPAWN / UPDATE through the brokered runtime surface
- ADD_COMPONENT / REMOVE_COMPONENT with archetype migration at tick boundaries
- ADD_PROCESSOR to inject a
MovementProcessorat runtime - RBAC checks through actor-bound handles: viewer denied spawn, player denied add_processor
- FORK from an actor-bound handle while keeping the same actor binding on the branch
- Command history through
world.command_history()
Output:
1. SPAWN + RBAC
viewer: SPAWN denied (correct)
player: spawned scout=1, dummy=2
2. UPDATE + COMPONENT MUTATIONS
scout after update/add_components: pos=(2.0, 1.0), vel=(1.5, 0.5), hp=80
3. PROCESSOR MUTATIONS
player: ADD_PROCESSOR denied (correct)
Command types (15 total, including run_rollout and run_episode for MCTS):
| Command | Payload | Who Can Run It |
|---|---|---|
spawn |
{"components": [...]} |
player, maintainer, admin |
despawn |
{"entity_id": int} |
player, maintainer, admin |
update |
{"entity_id": int, "components": [...]} |
player, coder, maintainer, admin |
add_component |
{"entity_id": int, "components": [...]} |
coder, maintainer, admin |
remove_component |
{"entity_id": int, "component_types": [...]} |
coder, maintainer, admin |
add_processor |
{"processor": ...} |
maintainer, admin |
remove_processor |
{"processor_type": str} |
maintainer, admin |
create_world |
{"config": {"name": str}} |
admin |
destroy_world |
{"world_id": str} |
admin |
fork_world |
{"source_world_id": str, "name": str} |
admin |
message |
{"sender_id", "receiver_id", "content"} |
player, admin |
custom |
{...} |
player, admin |
query_world |
{} |
viewer, operator, admin |
2. Fork for Counterfactuals¶
Fork a world three times with different parameters, run each branch, compare results.
uv run python examples/02_fork_counterfactual.py
Source: examples/02_fork_counterfactual.py
import asyncio
from dataclasses import dataclass
from archetype import ArchetypeRuntime, Component, StorageConfig
@dataclass
class PhysicsConfig:
gravity: float = 9.8
drag: float = 0.1
class Probe(Component):
label: str = ""
async def main():
storage = StorageConfig(uri="./archetype_data", namespace="counterfactuals")
async with ArchetypeRuntime() as runtime:
base = runtime.world("base", storage=storage)
await base.spawn(Probe(label="seed"))
await base.run(steps=1)
for gravity in [1.0, 9.8, 25.0]:
fork = await base.fork(f"gravity-{gravity}", storage=storage)
fork.resources.insert(PhysicsConfig(gravity=gravity))
result = await fork.run(steps=10)
rows = (await fork.query(Probe)).collect().to_pylist()
print(f"gravity={gravity:>5.1f}: tick={result.final_tick}, entities={len(rows)}")
asyncio.run(main())
Output:
gravity= 1.0: tick=11
gravity= 9.8: tick=11
gravity= 25.0: tick=11
All three branches start from the same base state and diverge independently.
3. Time-Travel Queries¶
Run 10 ticks, then query any point in history. Every tick is preserved.
uv run python examples/03_time_travel.py
Source: examples/03_time_travel.py
This example intentionally uses the lower-level QueryService because
historical snapshot reads are part of the read path rather than the current
top-level runtime sugar. Current-state runtime mutation helpers exist; history
snapshot helpers do not yet.
import asyncio
from uuid_utils import uuid7
from archetype.app.auth.models import ActorCtx
from archetype.app.container import ServiceContainer
from archetype.app.models import Command, CommandType
from archetype.core.config import RunConfig, StorageConfig, WorldConfig
async def main():
container = ServiceContainer()
ctx = ActorCtx(id=uuid7(), roles={"admin"})
world = await container.world_service.create_world(
WorldConfig(name="time-travel-demo"), StorageConfig(),
)
# Spawn 3 entities and run 5 ticks
for _ in range(3):
cmd = Command(type=CommandType.SPAWN, payload={"components": []})
await container.command_service.submit(world.world_id, cmd, ctx)
await container.simulation_service.run(world.world_id, RunConfig(num_steps=5))
# Spawn 2 more and run 5 more ticks
for _ in range(2):
cmd = Command(type=CommandType.SPAWN, payload={"components": []})
await container.command_service.submit(world.world_id, cmd, ctx)
await container.simulation_service.run(world.world_id, RunConfig(num_steps=5))
# Query state at different ticks
for tick in [1, 5, 10]:
state = await container.query_service.get_world_state(world.world_id, tick=tick)
print(f"tick {tick:>2}: {len(state.entities)} entities")
# Full command audit trail
history = await container.query_service.get_command_history(world.world_id)
for cmd in history:
print(f" tick={cmd.tick}: {cmd.type.value}")
await container.shutdown()
asyncio.run(main())
4. Agent Messaging¶
Three agents send greetings to each other via the CommandBroker. Messages are enqueued as MESSAGE commands and delivered at tick boundaries. Mood and energy update based on messages received.
uv run python examples/04_messaging.py
Source: examples/04_messaging.py
What it demonstrates:
- Components:
AgentState(name, mood, energy),Inbox,Outbox - Resources:
SimConfigfor shared parameters,CommandBrokerfor message routing - Processors:
GreetingProcessor(sends messages),MessageRealizationProcessor(drains broker into inboxes),MoodProcessor(updates mood based on inbox) - Hooks:
PreTickandPostTicklifecycle callbacks
Output:
Archetype Messaging Demo: Resources + MESSAGE + Hooks
-> Pre-tick 0: Starting processing...
<- Post-tick 1: Completed!
Messages pending in broker: 6
Final State:
Alice: mood=happy, energy=130.0, 2 messages received
Bob: mood=happy, energy=130.0, 2 messages received
Charlie: mood=happy, energy=130.0, 2 messages received
5. LLM-Powered Agents¶
Three agents with different personalities, each calling an LLM every tick via daft.functions.prompt. The ECS handles batching automatically — all entities get LLM calls in parallel because world state is a DataFrame.
export OPENAI_API_KEY=sk-...
uv run python examples/05_llm_agents.py
Source: examples/05_llm_agents.py
What it demonstrates:
- Component:
Agentwith name, role, and a JSON journal of thoughts - Processor:
ThinkProcessorusesdaft.functions.promptto call an LLM for every agent entity in a single DataFrame operation - Pattern:
ArchetypeRuntimekeeps the script surface toworld.spawn(...),world.run(...), andworld.query(...)
Requires an OpenAI API key (or any provider via daft.set_provider()).
6. Trajectory Analysis¶
Ingest agent trajectories, label them with LLM-based evaluation, and compare techniques via world forking. Demonstrates the full ECS pattern: components, processors, resources, and forking in a single script.
uv run python examples/06_trajectory_analysis.py
Source: examples/06_trajectory_analysis.py
What it demonstrates:
- Components:
Trajectory(JSON-encoded turns),Label(evaluation result) - Processors:
SamplingProcessor(filter),LabelingProcessor(LLM eval),ScoringProcessor(normalize) - Resources:
SamplingConfig,LabelingConfigstaged onruntime.world(..., resources=[...]) - Fork-based comparison: Clone a world with
world.fork(...), swap config, run independently
7. Lifecycle Hooks¶
Record lifecycle audit events, measure tick duration, and publish per-tick metrics without putting side effects inside processors.
uv run python examples/07_hooks.py
Source: examples/07_hooks.py
What it demonstrates:
- Mutation audit:
OnSpawn,OnDespawn,OnComponentAdded, andOnComponentRemoved - Tick telemetry:
PreTickstarts a timer andPostTickcomputes metrics fromevent.results - Hook handles: unregister a temporary debug hook with
world.remove_hook(handle) - Boundary discipline: hooks emit side effects; processors keep the simulation state deterministic