"""The action vocabulary. Agents — honest or adversarial — can express moves *only* in this vocabulary, and every move passes through the legality engine before it touches the state. Red-team agents win by finding legal sequences with illegitimate outcomes, never by stepping outside the rules. """ from __future__ import annotations from dataclasses import dataclass, field from typing import Any __all__ = [ "Action", "Pass", "Propose", "Vote", "Delegate", "RevokeDelegation", "Transfer", "PROPOSAL_KINDS", "VOTE_CHOICES", "describe", ] PROPOSAL_KINDS = ("spend", "amend", "expel", "emergency_declare", "emergency_renew") VOTE_CHOICES = ("yes", "no", "abstain") @dataclass(frozen=True) class Action: actor: str @dataclass(frozen=True) class Pass(Action): """Do nothing this slot.""" @dataclass(frozen=True) class Propose(Action): kind: str = "spend" payload: dict = field(default_factory=dict) window_turns: int | None = None # None -> constitutional default @dataclass(frozen=True) class Vote(Action): proposal_id: str = "" choice: str = "abstain" @dataclass(frozen=True) class Delegate(Action): to: str = "" @dataclass(frozen=True) class RevokeDelegation(Action): pass @dataclass(frozen=True) class Transfer(Action): """Private balance transfer between agents (not the treasury).""" to: str = "" amount: float = 0.0 def describe(action: Action) -> str: """One-line human-readable rendering for the ledger and reports.""" if isinstance(action, Pass): return f"{action.actor}: pass" if isinstance(action, Propose): return f"{action.actor}: propose {action.kind} {_short(action.payload)} window={action.window_turns}" if isinstance(action, Vote): return f"{action.actor}: vote {action.choice} on {action.proposal_id}" if isinstance(action, Delegate): return f"{action.actor}: delegate -> {action.to}" if isinstance(action, RevokeDelegation): return f"{action.actor}: revoke delegation" if isinstance(action, Transfer): return f"{action.actor}: transfer {action.amount} -> {action.to}" return f"{action.actor}: {action!r}" def _short(payload: Any, limit: int = 90) -> str: text = repr(payload) return text if len(text) <= limit else text[: limit - 3] + "..."