""" 数据契约 — 保持 V1 契约完整性,新增 DeploymentRecord、AirRvr 审查报告等结构。 """ from __future__ import annotations import re from dataclasses import dataclass, field from datetime import datetime, timezone from typing import Any def now_iso() -> str: return datetime.now(timezone.utc).isoformat() @dataclass class WorkerResult: task_id: str status: str # done | blocked | failed summary: str = "" files_changed: list[str] = field(default_factory=list) validations: list[dict] = field(default_factory=list) document_updates: list[dict] = field(default_factory=list) evidence: list[dict] = field(default_factory=list) risks: list[str] = field(default_factory=list) blockers: list[str] = field(default_factory=list) deploy_required: bool = False deploy_info: dict | None = None def validate_for_finalize(self, brief: dict | None = None) -> None: if not re.fullmatch(r"[A-Za-z0-9_\-\.]+", self.task_id): raise ValidationError(f"invalid task_id: {self.task_id!r}") if self.status not in ("done", "blocked", "failed"): raise ValidationError(f"invalid status: {self.status}") if self.status == "done" and not self.validations and not self.files_changed: raise ValidationError("done without validations or file changes") if self.deploy_required: deploy_validations = [ v for v in self.validations if v.get("kind") in ("remote-deploy-verify", "remote-binary-md5") ] if not deploy_validations: raise ValidationError("deploy_required but no deploy verification in validations") def to_dict(self) -> dict[str, Any]: return { "taskId": self.task_id, "status": self.status, "summary": self.summary, "filesChanged": self.files_changed, "validations": self.validations, "documentUpdates": self.document_updates, "evidence": self.evidence, "risks": self.risks, "blockers": self.blockers, "deployRequired": self.deploy_required, "deployInfo": self.deploy_info, } @classmethod def from_dict(cls, data: dict) -> WorkerResult: return cls( task_id=data.get("taskId", ""), status=data.get("status", ""), summary=data.get("summary", ""), files_changed=data.get("filesChanged", []), validations=data.get("validations", []), document_updates=data.get("documentUpdates", []), evidence=data.get("evidence", []), risks=data.get("risks", []), blockers=data.get("blockers", []), deploy_required=data.get("deployRequired", False), deploy_info=data.get("deployInfo"), ) @dataclass class TaskRecord: task_id: str task: str = "" files_dirs: str = "" done_when: str = "" status: str = "TODO" validations: str = "" adr: str = "" @property def text_for_classification(self) -> str: return f"{self.task} {self.files_dirs} {self.done_when}" @dataclass class DeploymentRecord: task_id: str host: str binary_path: str md5: str = "" service_name: str = "" service_status: str = "" deploy_at: str = field(default_factory=now_iso) smoke_test_passed: bool | None = None class ValidationError(Exception): pass