World state has separate read and write facades in core, while the service layer adds a gate for external access.
The important boundary is:
- Below the gate, services such as
iQueryService,iMutationService, andiWorldServicedo not know aboutActorCtx. - At the boundary,
iCommandServiceauthorizes, delegates, audits, and returns user-safe results.
Core Read/Write Split¶
AsyncWorld does not touch storage directly:
AsyncWorld
/ \
QueryManager UpdateManager
| |
AsyncStore AsyncStore
reads appends
QueryManager owns reads. UpdateManager owns writes. This split is independent of auth.
External Direct Path¶
Most runtime and API calls use a direct gated path:
Runtime / API / caller
|
iCommandService.<method>(ctx, ...)
|
guardrail_allow(command, ctx)
|
delegate to one service
|
iAuditLog.record(row)
|
return result
Examples:
create_worlddelegates toiWorldServiceand returnsWorldInfo.create_entitydelegates toiMutationServiceand returnsentity_id.rundelegates toiSimulationServiceand returnsRunResult.query_archetypedelegates toiQueryServiceand returns a DataFrame.
Reads are gated at this external boundary. The internal iQueryService remains ActorCtx-free.
Tick-Deferred Path¶
When a caller wants work applied at a tick boundary, the gate enqueues a command:
Runtime / API / caller
|
iCommandService.submit(ctx, world_id, cmd)
|
guardrail_allow(command, ctx)
|
iCommandBroker.enqueue(world_id, cmd)
|
SimulationService.step()
|
iCommandService.drain_and_apply(world_id, tick)
|
MutationService / WorldService
|
AsyncWorld internal mutation
drain_and_apply has no ActorCtx argument because submitted commands were validated before entering the queue.
Commands are ordered by (tick, priority, seq) within the broker queue.
Internal Writes¶
Processors are trusted internal code. During a tick, processors transform DataFrames and the world persists the result through the updater:
AsyncSystem.execute(resources, tick)
|
processor.process(df, resources=resources, tick=tick)
|
AsyncWorld._update_archetype(sig, df, run_config)
|
UpdateManager.update(df, sig, tick, world_id, run_id)
These writes are not individually command-gated. The trust boundary is processor registration, which is an operator/admin operation through the gate.
Lifecycle Flow¶
World lifecycle operations are direct gated methods:
create_world: admin only; returnsWorldInfo.fork_world: operator/admin; returnsWorldInfo.destroy_world: operator/admin; removes the live world only.
Destroy does not delete storage or audit rows. See World Lifecycle.
Audit Flow¶
Every gated call emits one audit row. Broker queue history is not the audit log.
RuntimeWorld.history(...) and API history reads use iCommandService.get_audit_history(...), which authorizes the read and delegates to iAuditLog.query(...).
See Audit Log.
Source Reference¶
- Command service:
src/archetype/app/command_service.py - Command broker:
src/archetype/app/broker.py - Simulation service:
src/archetype/app/simulation_service.py - Query service:
src/archetype/app/query_service.py - RBAC guard:
src/archetype/app/auth/guard.py - Querier:
src/archetype/core/aio/async_querier.py - Updater:
src/archetype/core/aio/async_updater.py