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:

  1. WorldStateBridge.hydrate_initial() — one-shot at session init. Reads the tick-0 hex_state + external_node templates that the hex hydrator wrote, constructs per-county SocialClass entity sets tagged with county_fips, optionally subscribes EventCapture to the engine’s EventBus, and returns the initial WorldState.

  2. Each tick the runner mutates the WorldState in-place via engine.run_tick(graph, services, context).

  3. WorldStateBridge.persist_tick() — calls the four babylon.persistence.county_aggregation helpers per county to derive the spec-065 subsystem state rows, re-emits the cached hex/external templates with the new tick number, assembles a PerTickTransactionEnvelope, and writes via runtime.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

WorldStateBridge(runtime, defines, *[, ...])

Adapter between in-memory WorldState and per-tick Postgres state.

class babylon.engine.headless_runner.bridge.WorldStateBridge(runtime, defines, *, boundary_register=None, event_bus=None, auditor=None, national_phi_reference=0.0)[source]

Bases: object

Adapter between in-memory WorldState and per-tick Postgres state.

See specs/065-engine-bridging/contracts/engine_bridge_protocol.yaml for the canonical contract.

The bridge is bound to a single (session_id, scope_fips) pair for its lifetime. hydrate_initial is one-shot; persist_tick is 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 runtime: Any
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-side EventCapture subscriber share the same bus.

property auditor: ConservationAuditor | None

The session’s ConservationAuditor (T049).

Owned by runner.run() and injected at __init__. persist_tick calls auditor.audit_end_of_tick(...) after the envelope is built, merging any newly-produced audit rows into the same per-tick transaction.

property hydrated: bool
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_count right after hydrate_initial() — a mismatch means spec-088 S3 spatial-map resolution filtered the template (e.g. an empty hex_spatial_map), in which case delta emission, checkpoint frames, and the conservation auditor would all run silently blind. Returns 0 before hydrate_initial.

property population_db_reads: int

count of population reads issued at hydrate time.

Returns 0 before hydrate_initial is called (per contracts/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 total_db_reads: int

population_db_reads + employment_db_reads.

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 WorldState and cache persistence templates.

Steps (in order):

  1. Query dynamic_hex_state at tick 0 for the scope counties — cached as _hex_template for re-emission on each subsequent tick.

  2. Query dynamic_external_node_state at tick 0 — cached as _external_template.

  3. For each county in scope_fips, instantiate one proletariat + one bourgeoisie SocialClass entity tagged with that FIPS via the spec-065 factory updates.

  4. (Optional) store the event_capture reference for later subscription to the engine’s EventBus (US5 / T071 wires the actual subscription).

  5. Spec-069: hydrate the per-bridge ReferenceDataCache with the full (scope_fips × year_set) covered by the run. year_set is derived from (start_year, total_ticks) under the weekly cadence; the cache then serves all per-tick population / employment-proxy reads from memory.

  6. Commit instance state — _hydrated is 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:

WorldState

Returns:

Initial WorldState with per-county entities tagged with county_fips.

Raises:
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 a PerTickTransactionEnvelope carrying the three new row-lists plus the cached hex/external rows re-stamped with the current tick, and calls runtime.persist_tick_atomic for atomic commit.

Parameters:
  • world (WorldState) – Current in-memory WorldState (post-engine-run).

  • tick (int) – Tick number being persisted.

  • determinism_hash (str) – 64-char SHA-256 of the canonical envelope payload (Constitution III.7).

  • opposition_states (dict[str, Any] | None)

Raises:
Return type:

None

refresh_event_log()[source]

Drain accumulated engine events for summary.json.events.

Return type:

tuple[EngineEvent, ...]

set_endgame_detector(dotted_path)[source]

Resolve a dotted import path to an EndgameDetector instance.

Phase-2 stub for resolution; final wiring (poll-per-tick into runner) lands in T063.

Return type:

None

Parameters:

dotted_path (str)

poll_endgame(world, tick)[source]

Invoke the configured endgame detector. None if none configured.

Return type:

Any

Parameters: