Architecture: The Embedded Trinity
Babylon’s architecture is built on three interconnected data layers that work together to simulate class struggle as a deterministic output of material conditions.
Overview
The simulation runs locally without external servers, using what we call the Embedded Trinity:
Note
Architecture diagram (The Embedded Trinity) planned for future addition.
The Ledger - Rigid material state (Pydantic/SQLite)
The Topology - Fluid relational state (NetworkX)
The Archive - Semantic history (ChromaDB)
This architecture separates concerns:
State is pure data - Pydantic models with strict validation
Engine is pure transformation - Stateless functions on graphs
They never mix - Clean separation enables testing and reasoning
The Ledger: Material State
The Ledger stores rigid, quantitative state that changes discretely:
Economic values (wealth, wages, tribute)
Political attributes (repression, organization)
Class positions (proletariat, bourgeoisie, lumpen)
Territorial properties (heat, operational profile)
Data Storage
The Ledger uses two complementary systems:
- Pydantic Models
In-memory state with strict validation. All game entities derive from Pydantic
BaseModelwith constrained types:from babylon.models import SocialClass, Probability, Currency class SocialClass(BaseModel): id: str = Field(pattern=r"^C[0-9]{3}$") role: SocialRole wealth: Currency = Field(ge=0.0) organization: Probability # Constrained to [0, 1]
- SQLite Database
Persistent storage for history and checkpoints. The
DatabaseConnectionclass manages SQLAlchemy sessions.
Entity Collections
The Ledger contains 18 JSON entity collections in src/babylon/data/game/:
Collection |
Purpose |
|---|---|
|
Class definitions (proletariat, bourgeoisie, etc.) |
|
Spatial locations with operational profiles |
|
Initial edge definitions (solidarity, exploitation) |
|
Tension templates and resolution types |
|
Economic and political crisis definitions |
|
Political groupings with agendas |
|
Ideological positions with drift modifiers |
|
State and civil society institutions |
|
Cultural, legal, and social movement data |
|
Economic policy, resource, and technology definitions |
|
Uprising conditions and public sentiment data |
The Topology: Relational State
The Topology stores fluid, relational state that changes continuously:
Class solidarity networks
Economic extraction flows
Territorial adjacency
Imperial tribute chains
Graph Structure
Babylon uses a NetworkX DiGraph (directed graph) with two node types
and multiple edge types:
Node Types:
social_class (C001, C002, ...)
└── Attributes: wealth, organization, ideology, consciousness
territory (T001, T002, ...)
└── Attributes: heat, profile, sector_type, territory_type
Edge Types:
Edge Type |
Direction |
Meaning |
|---|---|---|
EXPLOITATION |
bourgeoisie → proletariat |
Economic extraction relationship |
SOLIDARITY |
bidirectional |
Class consciousness connection |
WAGES |
employer → worker |
Labor-wage payment flow |
TRIBUTE |
periphery → core |
Imperial value transfer |
TENANCY |
class → territory |
Spatial occupation |
ADJACENCY |
territory → territory |
Spatial proximity (spillover routes) |
State Transformation
The simulation transforms between Pydantic and graph representations:
# Pydantic → Graph (for computation)
graph: nx.DiGraph = world_state.to_graph()
# Graph operations (mutation allowed)
engine.run_tick(graph, services, context)
# Graph → Pydantic (for validation)
new_state = WorldState.from_graph(graph, old_state.tick + 1)
This pattern allows:
Flexible graph algorithms during simulation
Strict validation on state boundaries
Clear separation of concerns
The Archive: Semantic History
The Archive stores semantic, narrative state for AI integration:
Event narratives as embeddings
Historical patterns for retrieval
Theory corpus for RAG queries
ChromaDB Integration
Babylon uses ChromaDB as a vector database:
from babylon.rag.retrieval import VectorStore, Retriever
from babylon.rag.chunker import DocumentChunk
# Initialize store
store = VectorStore(collection_name="events")
# Store document chunks
chunks = [
DocumentChunk(
content="The workers seized the factory...",
metadata={"tick": 42, "class_id": "C001"}
)
]
store.add_chunks(chunks)
# Query similar content
retriever = Retriever(store)
results = retriever.query(query="factory occupation", k=5)
The Archive enables:
AI narrative generation from simulation state
Pattern matching for event prediction
Theory-grounded responses via RAG
Engine Architecture
The simulation engine orchestrates the three layers:
flowchart TB
subgraph Input
WS[WorldState]
SC[SimulationConfig]
end
WS --> step["step()"]
SC --> step
step -->|"to_graph()"| G[NetworkX DiGraph]
subgraph Engine["SimulationEngine.run_tick()"]
G --> S1[1. ImperialRentSystem]
S1 --> S2[2. SolidaritySystem]
S2 --> S3[3. ConsciousnessSystem]
S3 --> S4[4. SurvivalSystem]
S4 --> S5[5. StruggleSystem]
S5 --> S6[6. ContradictionSystem]
S6 --> S7[7. TerritorySystem]
end
S7 --> OBS[Observers]
OBS -->|"from_graph()"| WS2[New WorldState]
Dependency Injection
The engine uses dependency injection via ServiceContainer:
from babylon.engine import ServiceContainer, EventBus
from babylon.engine.formula_registry import FormulaRegistry
from babylon.engine.database import DatabaseConnection
from babylon.config.defines import GameDefines
from babylon.models import SimulationConfig
services = ServiceContainer(
config=SimulationConfig(),
database=DatabaseConnection(":memory:"),
event_bus=EventBus(),
formulas=FormulaRegistry(),
defines=GameDefines(),
)
This enables:
Easy testing with mock services
Formula hot-swapping for experimentation
Clean separation of infrastructure concerns
Observer Pattern
Observers implement the SimulationObserver protocol to receive state
change notifications without modifying state:
from babylon.engine.observer import SimulationObserver
class MyObserver(SimulationObserver):
@property
def name(self) -> str:
return "MyObserver"
def on_simulation_start(self, initial_state, config): ...
def on_tick(self, previous_state, new_state): ...
def on_simulation_end(self, final_state): ...
Current observers:
TopologyMonitor - Tracks solidarity network condensation via percolation theory
EconomyMonitor - Detects economic crises (>20% imperial rent pool drops)
CausalChainObserver - Detects Shock Doctrine pattern (crash → austerity → radicalization)
Validation utilities (in babylon.engine.observers):
validate_narrative_frame()- Validate NarrativeFrame against JSON Schemais_valid_narrative_frame()- Boolean validation check
Data Flow Summary
flowchart TB
subgraph TICK["SIMULATION TICK"]
subgraph LEDGER["LEDGER (Pydantic)"]
WS[WorldState<br/>- classes<br/>- territories<br/>- relationships]
end
subgraph TOPOLOGY["TOPOLOGY (NetworkX)"]
G[nx.DiGraph<br/>- nodes<br/>- edges]
end
subgraph OUTPUT["OUTPUT"]
NS[New State<br/>validated]
ARCHIVE[ARCHIVE<br/>ChromaDB]
end
WS -->|"to_graph()"| G
G -->|"Systems mutate graph"| G
G -->|"from_graph()"| NS
G -->|"Store event narratives"| ARCHIVE
end
Key Design Principles
Determinism Given the same initial state and configuration, the simulation produces identical results. Random seeds are explicit.
Immutability at Boundaries Pydantic models are frozen. Only graphs are mutable during computation.
Validation on Entry/Exit All data is validated when entering or leaving the Ledger.
Graph + Math = History Complex emergent behavior arises from simple topological operations and mathematical formulas.
See Also
Topology - Graph structure details
Simulation Systems Architecture - System architecture explanation
Data Models Reference - Complete entity and type specifications
Simulation Systems Reference - Systems API reference
Engine - Engine API reference