"""What the watcher needs from a CI: the run of a commit and the log of what failed. Vendors live next to it.""" from dataclasses import dataclass from typing import Protocol from app.db.models import PipelineStatus @dataclass(frozen=True) class CIRun: id: str status: PipelineStatus # pending · running · success · failed · skipped url: str failed_step: str | None = None # "job / step" of the first failure class CIProvider(Protocol): async def find(self, sha: str) -> CIRun | None: """The newest run of the uploads workflow for this commit; None — not started (yet).""" ... async def failure_log(self, run: CIRun) -> str: """Log of the failed job, plain text without runner timestamps.""" ... async def aclose(self) -> None: ...