"""The situation layer: who is acting, and in what state of the world. Actor isolation is structural. An Actor holds its own credentials and private memory and has no reference to the Cast or to any sibling. The orchestrator may know the whole world; actors must not learn anything merely because they happen to be executed by the same process. """ from __future__ import annotations from dataclasses import dataclass, field from typing import Any, Iterator class ActorIsolationError(Exception): """Raised when one actor is offered another actor's private state.""" @dataclass(slots=True) class Actor: """An independent execution entity. Deliberately holds no back-reference to the Cast or the World. An actor that can enumerate its siblings can leak knowledge it was never given, and no later check can reliably detect that it did. """ id: str display_name: str credentials: dict[str, str] = field(default_factory=dict) session: dict[str, Any] = field(default_factory=dict) _memory: dict[str, Any] = field(default_factory=dict, repr=False) def remember(self, key: str, value: Any) -> None: self._memory[key] = value def recall(self, key: str, default: Any = None) -> Any: return self._memory.get(key, default) def known_keys(self) -> tuple[str, ...]: return tuple(sorted(self._memory)) @dataclass(slots=True) class Cast: """The set of actors in a scenario. Known to the orchestrator, not to actors.""" actors: dict[str, Actor] = field(default_factory=dict) def add(self, actor: Actor) -> Actor: if actor.id in self.actors: raise ValueError(f"duplicate actor id {actor.id!r}") self.actors[actor.id] = actor return actor def __getitem__(self, actor_id: str) -> Actor: return self.actors[actor_id] def __iter__(self) -> Iterator[Actor]: return iter(self.actors.values()) def __len__(self) -> int: return len(self.actors) @dataclass(slots=True) class World: """Initial state plus the handle to the system under test. `seed` is everything needed to rebuild the initial state, so that a run can be replayed from a known starting point rather than from wherever the previous run happened to leave things. """ id: str sut: Any sut_version: str seed: dict[str, Any] = field(default_factory=dict) cast: Cast = field(default_factory=Cast)