babylon.engine.headless_runner.bridge
WorldState ↔ Postgres bridge for the headless runner.
Spec: 065-engine-bridging (T002 / T040 / T041).
The bridge adapts the in-memory WorldState Pydantic model to the
per-tick Postgres subsystem tables (spec-062 schema + spec-065
additions in migrations 0020-0023). Lifecycle within one run:
WorldStateBridge.hydrate_initial()— one-shot at session init. Reads the tick-0 hex_state + external_node templates that the hex hydrator wrote, constructs per-countySocialClassentity sets tagged withcounty_fips, optionally subscribesEventCaptureto the engine’sEventBus, and returns the initialWorldState.Each tick the runner mutates the
WorldStatein-place viaengine.run_tick(graph, services, context).
WorldStateBridge.persist_tick()— calls the fourbabylon.persistence.county_aggregationhelpers per county to derive the spec-065 subsystem state rows, re-emits the cached hex/external templates with the new tick number, assembles aPerTickTransactionEnvelope, and writes viaruntime.persist_tick_atomic.
The bridge is a derivation/aggregation adapter (research.md §R10),
not a flat field-by-field serializer. See
specs/065-engine-bridging/contracts/engine_bridge_protocol.yaml
for the canonical contract.
Phase 3 first-cut simplification: the engine systems do not yet
mutate hex-resolution state (c/v/s/k per hex are computed by the
hex hydrator at tick 0). The bridge therefore re-emits the tick-0
hex_state values with the current tick on every persist_tick,
keeping the view JOINs satisfied without requiring engine-side
hex disaggregation. When the engine becomes hex-aware in a future
spec, this carry-forward will be replaced by genuine per-tick
hex mutations.
Classes
|
Adapter between in-memory |
- class babylon.engine.headless_runner.bridge.WorldStateBridge(runtime, defines, *, boundary_register=None, event_bus=None, auditor=None, national_phi_reference=0.0)[source]
Bases:
objectAdapter between in-memory
WorldStateand per-tick Postgres state.See
specs/065-engine-bridging/contracts/engine_bridge_protocol.yamlfor the canonical contract.The bridge is bound to a single
(session_id, scope_fips)pair for its lifetime.hydrate_initialis one-shot;persist_tickis called once per tick.- Parameters:
runtime (Any)
defines (GameDefines)
boundary_register (BoundaryFlowRegister | None)
event_bus (EventBus | None)
auditor (ConservationAuditor | None)
national_phi_reference (float)
- __init__(runtime, defines, *, boundary_register=None, event_bus=None, auditor=None, national_phi_reference=0.0)[source]
- Parameters:
runtime (Any)
defines (GameDefines)
boundary_register (BoundaryFlowRegister | None)
event_bus (EventBus | None)
auditor (ConservationAuditor | None)
national_phi_reference (float)
- Return type:
None
- property event_capture: EventCapture | None
- property event_bus: EventBus
The session’s EventBus (T071).
Owned by
runner.run()and injected at __init__ so the engine-side publishers (spec-066) and the bridge-sideEventCapturesubscriber share the same bus.
- property auditor: ConservationAuditor | None
The session’s ConservationAuditor (T049).
Owned by
runner.run()and injected at __init__.persist_tickcallsauditor.audit_end_of_tick(...)after the envelope is built, merging any newly-produced audit rows into the same per-tick transaction.
- property hex_template_size: int
Row count of the cached tick-0 hex frame (read-only observability).
Spec-089 loud gate: the runner compares this against the hydrator’s
report.hex_countright afterhydrate_initial()— a mismatch means spec-088 S3 spatial-map resolution filtered the template (e.g. an emptyhex_spatial_map), in which case delta emission, checkpoint frames, and the conservation auditor would all run silently blind. Returns 0 beforehydrate_initial.
- property population_db_reads: int
count of population reads issued at hydrate time.
Returns 0 before
hydrate_initialis called (percontracts/instrumentation_contract.md§I1).- Type:
Spec-069 SC-002
- property employment_db_reads: int
count of employment-proxy reads issued at hydrate time.
- Type:
Spec-069 SC-002
- property boundary_register: BoundaryFlowRegister
The session’s BoundaryFlowRegister (T055).
Engine systems push BoundaryFlowRegisterRow entries here;
persist_tick()flushes them into the envelope each tick. Spec-065 T055: the register is owned by runner.run() and injected into the bridge at construction time, so it’s always available.
- hydrate_initial(session_id, scope_fips, event_capture=None, *, total_ticks, start_year=2010, sqlite_path=None)[source]
Build the initial
WorldStateand cache persistence templates.Steps (in order):
Query
dynamic_hex_stateat tick 0 for the scope counties — cached as_hex_templatefor re-emission on each subsequent tick.Query
dynamic_external_node_stateat tick 0 — cached as_external_template.For each county in
scope_fips, instantiate one proletariat + one bourgeoisie SocialClass entity tagged with that FIPS via the spec-065 factory updates.(Optional) store the
event_capturereference for later subscription to the engine’s EventBus (US5 / T071 wires the actual subscription).Spec-069: hydrate the per-bridge
ReferenceDataCachewith the full(scope_fips × year_set)covered by the run.year_setis derived from(start_year, total_ticks)under the weekly cadence; the cache then serves all per-tick population / employment-proxy reads from memory.Commit instance state —
_hydratedis set LAST so any failure above is retry-safe.
- Parameters:
session_id (
UUID) – Active session UUID.scope_fips (
frozenset[str]) – 5-digit FIPS codes for the scope counties.event_capture (
EventCapture|None) – Optional EventCapture instance (US5).total_ticks (
int) – Total tick count for the run; drives the spec-069 cache hydrate year-set derivation. Must be>= 0. (Spec-069 FR-001.)start_year (
int) – Calendar year for tick 0 (FR-022; default 2010).sqlite_path (
Path|None) – Optional override for the SQLite reference DB.
- Return type:
- Returns:
Initial
WorldStatewith per-county entities tagged withcounty_fips.- Raises:
RuntimeError – If called twice on the same bridge instance.
ValueError – If
scope_fipsis empty ortotal_ticks < 0.FileNotFoundError – If
sqlite_pathdoes not exist.
- persist_tick(world, tick, determinism_hash, opposition_states=None)[source]
Derive subsystem rows + re-emit hex/external + persist atomically.
Per research.md §R10, the four spec-065 subsystem rows are derivations (engine-state aggregation or reference-data lookups), not flat field reads. The bridge calls the four county_aggregation helpers per county in
self._scope_fips, assembles aPerTickTransactionEnvelopecarrying the three new row-lists plus the cached hex/external rows re-stamped with the current tick, and callsruntime.persist_tick_atomicfor atomic commit.- Parameters:
- Raises:
RuntimeError – If called before
hydrate_initial().ReferenceDataMissingError – If a county’s reference data is outside the SQLite window (FR-022 preflight should catch this earlier).
- Return type:
- set_endgame_detector(dotted_path)[source]
Resolve a dotted import path to an
EndgameDetectorinstance.Phase-2 stub for resolution; final wiring (poll-per-tick into runner) lands in T063.
- poll_endgame(world, tick)[source]
Invoke the configured endgame detector. None if none configured.
- Return type:
- Parameters:
world (WorldState)
tick (int)