"""Data schema for the three-phase loop. Each phase passes a ContextPayload forward. The schema is intentionally minimal — Timmy decides what fields matter as the loop matures. """ from __future__ import annotations import logging from dataclasses import dataclass, field from datetime import UTC, datetime logger = logging.getLogger(__name__) @dataclass class ContextPayload: """Immutable context packet passed between loop phases. Attributes: source: Where this payload originated (e.g. "user", "timer", "event"). content: The raw content string to process. timestamp: When the payload was created. token_count: Estimated token count for budget tracking. -1 = unknown. metadata: Arbitrary key-value pairs for phase-specific data. """ source: str content: str timestamp: datetime = field(default_factory=lambda: datetime.now(UTC)) token_count: int = -1 metadata: dict = field(default_factory=dict) def with_metadata(self, **kwargs: object) -> ContextPayload: """Return a new payload with additional metadata merged in.""" merged = {**self.metadata, **kwargs} return ContextPayload( source=self.source, content=self.content, timestamp=self.timestamp, token_count=self.token_count, metadata=merged, )