Overview

This page explains the internal workings of Ometeotl/ometeotl_core at three depth levels.

For API-level details, use Class Reference.

04/25/26 - major architectural overhaul: Local tests reveal the current architecture is too abstract for any practical implementation. It has been decided to :

  • to keep the current code in a core module ometeotl_core, which is intended to remain abstract;
  • to add a primary layer of specialization ometeotl_foundations, including :
    • spatial: primary layer of spatial implementation of ometeotl_core;
    • networks: primary layer of graph theory implementation of ometeotl_core
  • to add, lastly, an adapter layer ometeotl_adapters, which implements each specialization layer with a reputable library.

Beginner View

Ometeotl/ometeotl_core is a modeling library where everything starts from a generic object, then becomes more specific.

  • ModelObject is the universal base container.
  • GenericObject adds practical metadata such as labels, tags, and profiles.
  • Actor, Resource, Space, and Action represent core domain entities.
  • World is the top-level container that organizes spaces, objects, and their relations.

The library tracks where objects exist with:

Each actor can have a subjective, possibly imperfect view of the world:

Actors may also form explicit composite hierarchies and abstraction layers:

  • Actor supports explicit component relations for composite actors and helper utilities for hierarchy traversal and cycle checks
  • Space can be marked is_abstract to represent conceptual or analytical spaces alongside canonical spaces
  • Perception can carry perceived component links, keeping composition knowledge in the epistemic layer when needed

Candidate actions can then be projected from that perceived state into explicit future-facing strategy artifacts:

Teleology, utility/ranking, and multi-actor game structures are now implemented as model and game extensions:

Model objects can also be generated programmatically from declarative inputs:

For server-authoritative setups, mutations can be routed through:

Intermediate View

The implemented pipeline follows this flow:

  1. Build domain entities from ModelObject-derived classes such as Actor, Resource, Space, and Action.
  2. Register objects in WorldModelRegistry through World.
  3. Place object-space memberships in SpaceObjectGraph.
  4. Add space-to-space edges in SpaceRelationGraph.
  5. Serialize deterministically with to_dict() methods (canonical sorting for stable diffs).
  6. Rebuild canonical objects with from_dict() methods.
  7. Generate actor-relative snapshots via Sensor into Perception.
  8. Derive first-order projection assumptions and projected successor perceived states from candidate actions, one Perception, and available resources through DefaultProjectionTool.
  9. Build a perception-driven Strategy with build_linear_strategy(…) or build_branching_strategy(…).
  10. Validate payloads/objects with the staged validation pipeline in ometeotl_core.validation (syntactic, structural, temporal, spatial, admissibility, epistemic, completeness), using policy profiles (observe_only, enforce_structure, enforce_domain) when needed.
  11. Export a world to canonical JSON or YAML via ometeotl_core.io (world_to_json, world_to_yaml, write_world_json, write_world_yaml).
  12. Re-import a world from JSON or YAML with validated reconstruction via world_from_json / world_from_yaml, which runs syntactic then structural validation before calling World.from_dict.
  13. Export any entity as a language-model-oriented payload via to_llm_view() (LLM view export), which separates ontological reality from epistemic/perception-aware views.
  14. Optionally enforce command gating with AuthorityCommandHandler.
  15. Represent actor objectives with Goal and optionally decompose them with GoalDecompositionTree.
  16. Link Strategy to a goal and evaluate admissibility with GoalAdmissibilityChecker.
  17. Evaluate strategy outcomes with a UtilityFunction implementation and rank with StrategyRanker.
  18. Build a multi-actor game by wrapping players in PlayerProfile objects and grouping them into a GameState, then calling NormalFormGame.from_game_state(…) with an IndependentPayoffFunction to compute the full payoff matrix.
  19. Find best responses with BestResponseCalculator.compute(…) given fixed opponent strategies.
  20. Generate model objects programmatically from declarative GenerationContext inputs using ContextualGenerationPipeline, with optional constraint propagation via the rule engine and optional LLM-assisted context refinement via LLMGenerationAdapter.

Operationally, World composes three independent graphs/registries:

This separation prevents layer mixing and aligns with the architecture constraints in specs_EN.md.

Test Layout

The test suite follows the same layer separation as the source tree:

  • tests/ometeotl_core/model/: tests for ometeotl_core.model.*
  • tests/ometeotl_core/generic/: tests for ometeotl_core.generic.*
  • tests/ometeotl_core/validation/: tests for ometeotl_core.validation.*
  • tests/ometeotl_core/game/: tests for ometeotl_core.game.*
  • tests/ometeotl_core/io/: tests for ometeotl_core.io.*
  • tests/ometeotl_core/generation/: tests for ometeotl_core.generation.*

Within each layer folder, tests are split by module using one file per module (test_<module>.py).

Run the complete suite from the repository root in one command:

  • pytest

Expert View

At expert level, the core implementation can be read as a set of deterministic state-transition boundaries.

1. Canonical object schema boundary

Every serializable entity normalizes to canonical JSON dictionaries:

Deterministic ordering is enforced by canonical sort helpers and sorted list serialization, making snapshots stable for audit/diff workflows.

2. Ontology boundary vs epistemic boundary

The ontology layer is World, SpaceObjectGraph, SpaceRelationGraph, and WorldModelRegistry.

The epistemic layer is Perception, PerceivedSpace, PerceivedMembership, and PerceivedRelation, created by Sensor through composable CoverageRule and NoiseRule.

Perceived actor composition is represented explicitly through PerceivedComponentLink, which lets perceived hierarchies diverge from ontological hierarchies without mixing layers.

This realizes reality/perception dissociation from the spec: decisions can be based on perceived states, not only ontological states.

3. Authority boundary

When authority mode is enabled in World, direct mutations require a token.

AuthorityCommandHandler provides a single command path that enforces:

  • command allowlisting
  • command id deduplication
  • actor existence checks
  • strictly increasing sequence checks per actor
  • bounded in-memory tracking for processed ids and sequence history
  • immutable audit traces with AuditEntry
  • staged validation with configurable hardening profiles (observe_only, enforce_structure, enforce_domain)
  • structured validation summaries attached to command results and audit entries

4. LLM export boundary

to_llm_view() is defined on ModelObject and dispatches to LLMViewBuilder by object type.

The export path (F-5) keeps ownership clean:

  • model owns canonical state (to_dict and domain classes)
  • io owns formatting and projection for external consumers

Output always exposes explicit epistemic distinctions: reality, perception, belief, hypothesis, projection. Collections and epistemic groups are produced deterministically (sorted keys).

5. Generation boundary

The generation layer (F-16 to F-22) converts declarative GenerationContext inputs into model objects through a deterministic pipeline:

GenerationContext
    → GenerationRuleSet.apply()   # constraint propagation, normalization
    → build_from_context()        # kind dispatch to builder functions
    → registration / validation   # optional; driven by context flags
    → GenerationResult

Key design properties:

  • Rule application is purely functional: each rule receives a context and returns a new one via copy_with.
  • RuleRegistry enables pluggable policy selection at call-site without modifying the pipeline.
  • LLMGenerationAdapter is explicitly chained by callers — the pipeline never calls it automatically.
  • Constraint propagation rules use setdefault semantics, making the pipeline non-destructive: existing metadata survives rule application.

4. Extensibility seam

Primary extension seams are intentionally abstract and composable:

5. Runtime wiring

RuntimeContext plus build_runtime(...) provide explicit mode selection:

  • local mode: direct world mutation APIs
  • server-authoritative mode: command-gated mutation via AuthorityCommandHandler

When server-authoritative mode is enabled, runtime also wires validation policy options (validation_soft_gate, validation_policy_profile, validation_stage_mode_overrides, validation_block_on_error, validation_completeness_level) through to the authority boundary.

This keeps local testing ergonomics while preserving an enforceable server boundary for multi-client systems.

6. First-order projection seam

DefaultProjectionTool is intentionally separate from the strategy model layer.

It consumes Action, Perception, and Resource inputs and emits ProjectionAssumption collections grouped as ActionProjection or ProjectionBatch, together with a ProjectedPerceptionState representing the successor perceived state for that action.

This keeps first-order projection focused on assumption building plus successor-state derivation, while leaving later strategy-node construction and branching as a separate concern.

7. Strategy chaining seam

The strategy layer is implemented in Strategy, StrategyNode, StrategyOutcomeBranch, and StrategyBuildStep.

The important rule is that a strategy node is anchored to:

  • one action id
  • one source perception id (the perceived state the action is applied from)
  • an ordered list of StrategyOutcomeBranch links, each carrying its own projected successor perceived state

validate_tree() enforces two invariants per branch:

  1. if a branch’s projected_state is present, its generating_action_id must match the parent node’s action_id
  2. if a branch links to a child node and carries a projected_state, the child node’s source_perception_id must equal the branch’s projected perception id

This makes strategy hops explicitly perception-driven and allows one action to produce several distinct outcomes across sibling branches.

8. Builder seam

Two minimal builders exist today in src/ometeotl_core/model/strategies.py:

  • build_linear_strategy(...) for ordered action sequences
  • build_branching_strategy(...) for recursive action trees built from StrategyBuildStep

Both builders project each action from the currently active perceived state and attach the resulting successor perceived state to the outgoing branches of each node.

build_branching_strategy(...) allows one action to branch into several sibling child steps: all sibling branches from a node share the same parent action’s projected state, while each child subtree is projected from that shared successor perception onward.

9. Teleology and game utility seam

The teleology seam is now explicit in the model layer through Goal and GoalDecompositionTree, while goal evaluation remains domain-neutral via GoalFeasibilityTool and GoalAdmissibilityChecker.

The game utility seam is explicit in UtilityFunction plus concrete game-layer combinators WeightedSumUtility and LexicographicUtility.

StrategyRanker evaluates terminal projected states and aggregates branch probabilities in a deterministic way, including directed acyclic strategy graphs with merged terminal paths.

The multi-actor game seam is implemented as three composable layers:

  1. Game statePlayerProfile binds an actor to its admissible strategies and utility function. GameState groups players into a snapshot with a world_id reference, realizing G-1 (actors → players) and G-3 (world state → game state).

  2. Normal formNormalFormGame.from_game_state(…) enumerates the Cartesian product of all players’ strategy lists and evaluates each profile through a PayoffFunction. The V1 concrete implementation, IndependentPayoffFunction, delegates per-player evaluation to StrategyRanker — no logic is duplicated. The abstraction keeps the door open for joint-projection payoff functions without breaking callers.

  3. Best responseBestResponseCalculator.compute(…) operates on a prebuilt NormalFormGame, filters payoff vectors to rows where opponent strategies match, and returns the BestResponseResult with the dominant strategy and a ranked list of all options. Tie-breaking is deterministic (descending utility, then ascending strategy id), consistent with the rank_key convention in RankedStrategy.

Ometeotl

A Python library to build complex multi-agent simulations, wargames, and AI-driven strategies


Three-level walkthrough of how Ometeotl/ometeotl_core works internally