mud_server.axis.engine

Axis resolution engine.

AxisEngine is the orchestrator that ties together the resolution grammar, the resolver functions, the JSONL ledger, and the SQLite materialized view. One instance is created per World at startup and remains live for the server’s lifetime.

Resolution sequence (resolve_chat_interaction):

  1. Resolve character IDs from names (world-scoped, raises CharacterNotFoundError on miss).

  2. Acquire per-character threading locks in ascending ID order (deadlock prevention).

  3. Read current axis scores from the SQLite DB.

  4. Compute ipc_hash via compute_payload_hash() over the pre-interaction snapshot.

  5. Compute axis deltas for every axis in the chat grammar.

  6. Write chat.mechanical_resolution to the JSONL ledger — the authoritative act that makes the interaction permanent.

  7. Clamp deltas to [0.0, 1.0] and apply to the DB via apply_axis_event().

  8. Release locks.

  9. Return AxisResolutionResult.

Locking strategy:

Each character has a threading.Lock stored in a dict keyed by character_id. Locks are always acquired in ascending ID order to prevent deadlocks when two interactions share a character. The dict itself is protected by a separate _locks_mutex.

Note on ipc_hash computation (deviation from plan):

compute_ipc_id() requires system_prompt_hash: str — a concept that has no meaning in a purely mechanical resolution (no LLM call). compute_payload_hash() is used directly on the resolution payload dict instead. When the translation service subsequently uses this ipc_hash for deterministic Ollama seeding it calls compute_ipc_id() with this hash as input_hash, which is the intended design.

Attributes

logger

Exceptions

CharacterNotFoundError

Raised when a character name cannot be resolved within a world.

Classes

AxisEngine

General-purpose resolver registry for axis mutations.

Module Contents

mud_server.axis.engine.logger
exception mud_server.axis.engine.CharacterNotFoundError(character_name, world_id)[source]

Bases: Exception

Raised when a character name cannot be resolved within a world.

character_name

The name that could not be resolved.

world_id

The world in which the lookup was attempted.

Initialize self. See help(type(self)) for accurate signature.

character_name
world_id
class mud_server.axis.engine.AxisEngine(*, world_id, grammar)[source]

General-purpose resolver registry for axis mutations.

Instantiated once per World (same lifecycle as the translation service). The chat resolver is the first concrete implementation; future stimulus types (environmental, physical, economic) will add new resolve_* methods following the same pattern.

Parameters:
resolve_chat_interaction(*, speaker_name, listener_name, channel, world_id)[source]

Resolve a chat interaction between two characters.

This is the primary entry point. All ten steps of the resolution sequence (see module docstring) are executed here atomically under per-character locks.

Parameters:
  • speaker_name (str) – Display name of the character who sent the message.

  • listener_name (str) – Display name of the character who received it.

  • channel (str) – Chat channel — "say", "yell", or "whisper". Governs the channel multiplier applied to every axis delta.

  • world_id (str) – World in which the interaction occurs. Must match self._world_id (passed explicitly to make the call site readable).

Returns:

AxisResolutionResult containing the ipc_hash, per-character deltas (only axes with non-zero actual change), and the pre-interaction axis snapshot.

Raises:

CharacterNotFoundError – If either speaker_name or listener_name is not registered in world_id.

Return type:

mud_server.axis.types.AxisResolutionResult