Data Structures in LoopX: Immutable Contracts and Type-Safe Runtime Collections

LoopX utilizes a hybrid architecture of frozen dataclasses, type-safe enums, TypedDict payloads, and Protocol-based interfaces to enforce strict separation between immutable configuration contracts and mutable runtime state.

The huangruiteng/loopx repository implements a sophisticated agent runtime built on Python's modern typing primitives. By leveraging specific data structures for distinct architectural layers—immutable value objects for domain contracts and standard collections for transient state—LoopX achieves strong static guarantees while maintaining runtime flexibility.

Frozen Dataclasses for Immutable Domain Contracts

LoopX defines its core configuration, capability contracts, and state snapshots using @dataclass with frozen=True and slots=True. This pattern ensures that once a configuration object is instantiated, it cannot be mutated, while slots=True reduces memory overhead by eliminating the per-instance __dict__.

In loopx/presets.py, preset configurations are defined as frozen dataclasses to prevent accidental mutation of global settings. Similarly, loopx/global_registry.py uses this pattern for immutable registry objects that define available capabilities and agents.


# Quota decision packet combining TypedDict with frozen dataclass

from dataclasses import dataclass
from typing import TypedDict

class QuotaDecisionPacket(TypedDict):
    task_id: str
    allowed: bool
    reason: str

@dataclass(frozen=True, slots=True)
class QuotaDecision:
    packet: QuotaDecisionPacket
    timestamp: float

Source: loopx/control_plane/quota/decision_summary.py

Type-Safe Enumerations with Enum and StrEnum

For categorical values that must interoperate with JSON and YAML payloads, LoopX employs Enum and StrEnum to create enumerated vocabularies. These data structures provide static type checking while ensuring valid string serialization.

The LoopXTurnResultKind enum in the turn driver defines discrete outcomes for agent execution cycles:

from enum import StrEnum

class LoopXTurnResultKind(StrEnum):
    SUCCESS = "success"
    FAILURE = "failure"
    REPLAN = "replan"

Source: loopx/control_plane/turn_driver/loop_controller.py

Similarly, ProgressResultClass and DeliveryOutcome in loopx/control_plane/work_items/progress_observation.py use StrEnum to type progress observations with string-based values that serialize cleanly to configuration files.

Structured Contracts via TypedDict

LoopX uses TypedDict to define dictionary contracts for inter-component messaging where flexibility of a standard dict is required, but static type safety is desired. These structures define the shape of payloads exchanged between schedulers, quota managers, and work items.

The InteractionContractPacket and WorkspaceChange definitions in loopx/control_plane/work_items/interaction_contract.py demonstrate this pattern, providing clear field expectations for interaction payloads without requiring full class instantiation overhead.

Structural Subtyping with Protocol

To enable pluggable adapters without forcing inheritance hierarchies, LoopX implements Protocol classes for structural subtyping. This allows the framework to define expected behaviors for external integrations while remaining agnostic to concrete implementations.

The PlannerAdapter protocol in loopx/experiments/planner_worker/runtime.py specifies required methods for planning components:

from typing import Protocol

class PlannerAdapter(Protocol):
    def plan(self, goal: str) -> dict: ...
    def status(self) -> dict: ...

Source: loopx/experiments/planner_worker/runtime.py

Similarly, loopx/extensions/openviking_periodic_report/provider.py defines the OpenVikingResourceClient protocol to abstract external resource interactions, allowing different client implementations to satisfy the interface through duck typing.

Mutable Collections for Runtime State

While contracts use immutable structures, LoopX relies on standard Python dict and list containers for mutable runtime state such as work-item queues, projection caches, and execution contexts. These are typically wrapped in thin helper classes that manage access patterns.

In loopx/file_lock.py, the LockAcquisitionPolicy uses standard dictionaries to manage lock state, while execution contexts in loopx/control_plane/scheduler/execution_context.py utilize lightweight record types (implemented via frozen dataclasses) alongside mutable lists for dynamic task queuing:

class WorkQueue:
    def __init__(self) -> None:
        self._queue: list[dict] = []

    def push(self, item: dict) -> None:
        self._queue.append(item)

    def pop(self) -> dict | None:
        return self._queue.pop(0) if self._queue else None

Summary

Frequently Asked Questions

Does LoopX use traditional Python classes or dataclasses?

LoopX predominantly uses frozen dataclasses rather than traditional classes for domain models. According to the source code in loopx/presets.py and loopx/control_plane/quota/decision_summary.py, the framework relies on @dataclass(frozen=True, slots=True) to enforce immutability and memory efficiency for configuration and contract objects.

Why does LoopX use frozen dataclasses with slots?

The combination of frozen=True and slots=True serves two purposes: immutability guarantees that configuration objects cannot be accidentally modified after creation, while slots=True eliminates the per-instance __dict__, significantly reducing memory overhead for the numerous small objects created during runtime. This pattern appears throughout loopx/global_registry.py for capability definitions.

How does LoopX ensure type safety for dictionary-based payloads?

LoopX employs TypedDict from the standard library to define explicit field requirements for dictionary structures used in inter-component communication. As implemented in loopx/control_plane/work_items/interaction_contract.py, this approach provides static type checking through mypy or similar tools while maintaining the runtime flexibility and serialization compatibility of standard dictionaries.

What role do Protocol classes play in the LoopX architecture?

Protocol classes enable structural subtyping, allowing LoopX to define expected interfaces for external components without forcing those components to inherit from a specific base class. The PlannerAdapter protocol in loopx/experiments/planner_worker/runtime.py demonstrates how the framework supports pluggable planners—the runtime checks for method presence rather than class inheritance, facilitating integration of third-party adapters.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →