AirPlan V2 initial release — unified scheduler with 12 sub-modes

Merges 8 V1 plugins into 1 unified plugin, adds 4 new components (dep, tst, sec, rvr).
12 sub-modes: arc, eng, do, dbg, xdb, sdb, ndb, ctx, dep, tst, sec, rvr.
L1 code-level guarantees: atomic writes, file locks, evidence gating, forced debug routing,
3-phase arc gate, evidence-first debug gate, Chinese language lock, scheduler boundary.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
AirPlan Team
2026-06-10 16:24:26 +08:00
commit 2c4b3340bf
81 changed files with 6005 additions and 0 deletions

View File

@@ -0,0 +1,109 @@
"""
数据契约 — 保持 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