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.
1. **The Ledger** - Rigid material state (Pydantic/SQLite)
2. **The Topology** - Fluid relational state (NetworkX)
3. **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 ``BaseModel`` with constrained types:
.. code-block:: python
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
:py:class:`~babylon.engine.database.DatabaseConnection` class manages
SQLAlchemy sessions.
Entity Collections
^^^^^^^^^^^^^^^^^^
The Ledger contains 18 JSON entity collections in ``src/babylon/data/game/``:
.. list-table:: Entity Collections
:header-rows: 1
:widths: 30 70
* - Collection
- Purpose
* - ``classes.json``
- Class definitions (proletariat, bourgeoisie, etc.)
* - ``locations.json``
- Spatial locations with operational profiles
* - ``relationships.json``
- Initial edge definitions (solidarity, exploitation)
* - ``contradictions.json``
- Tension templates and resolution types
* - ``crises.json``
- Economic and political crisis definitions
* - ``factions.json``
- Political groupings with agendas
* - ``ideologies.json``
- Ideological positions with drift modifiers
* - ``institutions.json``
- State and civil society institutions
* - ``cultures.json``, ``laws.json``, ``movements.json``
- Cultural, legal, and social movement data
* - ``policies.json``, ``resources.json``, ``technologies.json``
- Economic policy, resource, and technology definitions
* - ``revolts.json``, ``sentiments.json``
- 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:**
.. code-block:: text
social_class (C001, C002, ...)
└── Attributes: wealth, organization, ideology, consciousness
territory (T001, T002, ...)
└── Attributes: heat, profile, sector_type, territory_type
**Edge Types:**
.. list-table:: Edge Types
:header-rows: 1
:widths: 20 20 60
* - 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:
.. code-block:: python
# 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:
.. code-block:: python
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:
.. mermaid::
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``:
.. code-block:: python
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:
.. code-block:: python
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 Schema
- ``is_valid_narrative_frame()`` - Boolean validation check
Data Flow Summary
-----------------
.. mermaid::
flowchart TB
subgraph TICK["SIMULATION TICK"]
subgraph LEDGER["LEDGER (Pydantic)"]
WS[WorldState
- classes
- territories
- relationships]
end
subgraph TOPOLOGY["TOPOLOGY (NetworkX)"]
G[nx.DiGraph
- nodes
- edges]
end
subgraph OUTPUT["OUTPUT"]
NS[New State
validated]
ARCHIVE[ARCHIVE
ChromaDB]
end
WS -->|"to_graph()"| G
G -->|"Systems mutate graph"| G
G -->|"from_graph()"| NS
G -->|"Store event narratives"| ARCHIVE
end
Key Design Principles
---------------------
1. **Determinism**
Given the same initial state and configuration, the simulation
produces identical results. Random seeds are explicit.
2. **Immutability at Boundaries**
Pydantic models are frozen. Only graphs are mutable during computation.
3. **Validation on Entry/Exit**
All data is validated when entering or leaving the Ledger.
4. **Graph + Math = History**
Complex emergent behavior arises from simple topological operations
and mathematical formulas.
See Also
--------
- :doc:`/concepts/topology` - Graph structure details
- :doc:`/concepts/simulation-systems` - System architecture explanation
- :doc:`/reference/data-models` - Complete entity and type specifications
- :doc:`/reference/systems` - Systems API reference
- :doc:`/api/engine` - Engine API reference