Skip to content

Phase 1: Model Additions (PollStrategy + MessageMetadata) #85

Description

@tinkermonkey

Add two new shared models to models.py that all subsequent Phase 2 components depend on: the PollStrategy enum and the MessageMetadata Pydantic model.

Requirements

  • Add PollStrategy(str, Enum) with values PUSH = "push", PULL = "pull", WEBHOOK = "webhook" matching the SQLite schema CHECK constraint: poll_strategy IN ('push', 'pull', 'webhook')
  • Add MessageMetadata(BaseModel) as a frozen Pydantic model with fields: thread_id: str, message_id: str, sender: str, recipients: list[str], timestamp: str, in_reply_to: str | None, subject: str | None, is_thread_root: bool
  • MessageMetadata must use model_config = ConfigDict(frozen=True) consistent with all existing models
  • Add @field_validator("timestamp") on MessageMetadata using the existing validate_iso8601_timestamp function already present in models.py
  • Both additions must be importable from src/context_library/storage/models.py

Design Guidance

File to modify: src/context_library/storage/models.py

Existing patterns to follow:

  • All enums in this file use (str, Enum) pattern (see Domain and ChunkType)
  • All BaseModels use model_config = ConfigDict(frozen=True)
  • Validators use @field_validator with @classmethod
  • The existing validate_iso8601_timestamp utility is already defined in models.py — reuse it for MessageMetadata.timestamp

PollStrategy enum:

class PollStrategy(str, Enum):
    PUSH = "push"
    PULL = "pull"
    WEBHOOK = "webhook"

Values must match exactly the SQLite schema CHECK constraint on sources.poll_strategy.

MessageMetadata model:

class MessageMetadata(BaseModel):
    model_config = ConfigDict(frozen=True)
    thread_id: str
    message_id: str
    sender: str
    recipients: list[str]
    timestamp: str  # ISO 8601
    in_reply_to: str | None
    subject: str | None
    is_thread_root: bool

    @field_validator("timestamp")
    @classmethod
    def validate_timestamp(cls, v: str) -> str:
        return validate_iso8601_timestamp(v)

Where MessageMetadata is used downstream:

  • EmailAdapter populates it and places it in NormalizedContent supplementary metadata
  • MessagesDomain reads it from content and serializes via .model_dump() into Chunk.domain_metadata
  • Chunk.domain_metadata is typed as dict[str, object] | None — so MessageMetadata is always serialized before assignment

Acceptance Criteria

  • PollStrategy.PUSH, PollStrategy.PULL, PollStrategy.WEBHOOK enum values equal "push", "pull", "webhook" respectively
  • PollStrategy is a str subclass (so values are directly usable in SQL queries)
  • MessageMetadata(thread_id="t1", message_id="m1", sender="a@b.com", recipients=["c@d.com"], timestamp="2024-01-01T00:00:00Z", in_reply_to=None, subject=None, is_thread_root=True) instantiates without error
  • MessageMetadata raises ValidationError when timestamp is not ISO 8601
  • MessageMetadata instance raises ValidationError on mutation attempt (frozen)
  • MessageMetadata(...).model_dump() returns a JSON-serializable dict
  • Both PollStrategy and MessageMetadata are importable from context_library.storage.models
  • All existing model tests continue to pass

Parent Issue

Part of #83

Discussion

This work is detailed in discussion 84

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions