"""Domain model: run records, forge events, provenance. These are the orchestrator's own dataclasses. `RunRecord` mirrors bot-bottle's `ForgeState` field-for-field so the bootstrap adapter can translate between them with no loss; keeping our own copy is what lets the core stay import-free of bot-bottle. """ from __future__ import annotations from dataclasses import dataclass, field # Run lifecycle. A bottle is launched (running), frozen on the done # signal, and destroyed when the PR closes. STATUS_RUNNING = "running" STATUS_FROZEN = "frozen" STATUS_DESTROYED = "destroyed" @dataclass class RunRecord: """One forge-targeted issue's bottle lifecycle record.""" owner: str repo: str issue_number: int slug: str agent_name: str bottle_names: list[str] = field(default_factory=list) backend_name: str = "" agent_git_user: str = "" pr_number: int | None = None status: str = STATUS_RUNNING last_checkin_at: str = "" # --- Forge events (parsed webhook payloads) -------------------------------- @dataclass(frozen=True) class IssueAssigned: """An issue gained an assignee — the trigger to consider a launch.""" owner: str repo: str issue_number: int title: str body: str assignees: tuple[str, ...] labels: tuple[str, ...] @dataclass(frozen=True) class CommentCreated: """A comment was posted on an issue or PR — a rehydrate trigger.""" owner: str repo: str issue_number: int comment_id: int author: str body: str is_pull: bool @dataclass(frozen=True) class PullRequestClosed: """A PR closed (merged or not) — the teardown trigger.""" owner: str repo: str pr_number: int merged: bool # Union of everything the webhook layer can emit. ForgeEvent = IssueAssigned | CommentCreated | PullRequestClosed # --- Provenance ------------------------------------------------------------ @dataclass(frozen=True) class ForgeOp: """One semantic forge operation the sidecar recorded.""" at: str # ISO timestamp op: str # e.g. "post_comment", "read_pr", "signal_done" target: int | None detail: str @dataclass(frozen=True) class Provenance: """The audit record for one run, served by the provenance API. Never posted into the forge.""" slug: str owner: str repo: str issue_number: int agent_name: str bottle_names: tuple[str, ...] started_at: str finished_at: str exit_code: int | None watchdog_fired: bool ops: tuple[ForgeOp, ...]