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

> Explore LoopX data structures: frozen dataclasses, type-safe enums, TypedDict, and Protocols. Learn how LoopX separates immutable contracts from mutable runtime state for robust applications.

- Repository: [huangruiteng/loopx](https://github.com/huangruiteng/loopx)
- Tags: deep-dive
- Published: 2026-08-13

---

**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`](https://github.com/huangruiteng/loopx/blob/main/loopx/presets.py), preset configurations are defined as frozen dataclasses to prevent accidental mutation of global settings. Similarly, [`loopx/global_registry.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/global_registry.py) uses this pattern for immutable registry objects that define available capabilities and agents.

```python

# 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`](https://github.com/huangruiteng/loopx/blob/main/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:

```python
from enum import StrEnum

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

```

*Source:* [`loopx/control_plane/turn_driver/loop_controller.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/turn_driver/loop_controller.py)

Similarly, `ProgressResultClass` and `DeliveryOutcome` in [`loopx/control_plane/work_items/progress_observation.py`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/loopx/experiments/planner_worker/runtime.py) specifies required methods for planning components:

```python
from typing import Protocol

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

```

*Source:* [`loopx/experiments/planner_worker/runtime.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/experiments/planner_worker/runtime.py)

Similarly, [`loopx/extensions/openviking_periodic_report/provider.py`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/loopx/file_lock.py), the `LockAcquisitionPolicy` uses standard dictionaries to manage lock state, while execution contexts in [`loopx/control_plane/scheduler/execution_context.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/scheduler/execution_context.py) utilize lightweight record types (implemented via frozen dataclasses) alongside mutable lists for dynamic task queuing:

```python
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

- **Frozen dataclasses** (`@dataclass(frozen=True, slots=True)`) in [`loopx/presets.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/presets.py) and [`loopx/global_registry.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/global_registry.py) provide immutable configuration objects with reduced memory footprint.
- **StrEnum** classes in [`loopx/control_plane/turn_driver/loop_controller.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/turn_driver/loop_controller.py) enable type-safe string enumerations that serialize to JSON/YAML.
- **TypedDict** structures in [`loopx/control_plane/work_items/interaction_contract.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/control_plane/work_items/interaction_contract.py) define statically-checked dictionary contracts for inter-component messages.
- **Protocol** classes in [`loopx/extensions/openviking_periodic_report/provider.py`](https://github.com/huangruiteng/loopx/blob/main/loopx/extensions/openviking_periodic_report/provider.py) facilitate structural subtyping for pluggable adapters without inheritance coupling.
- **Standard dict/list** containers manage mutable runtime state such as work queues and caches, contrasting with the immutable contract layer.

## 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`](https://github.com/huangruiteng/loopx/blob/main/loopx/presets.py) and [`loopx/control_plane/quota/decision_summary.py`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/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`](https://github.com/huangruiteng/loopx/blob/main/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.