Files
aios/services/aios-autonomous-release-candidate/aios_autonomous_release_candidate/api.py
T

174 lines
6.0 KiB
Python

"""Dependency-light candidate API facade.
The production transport is intentionally external. This module exposes the
application boundary without opening listeners or changing production routes.
"""
from __future__ import annotations
from typing import Any, Mapping
from .evidence import EvidenceChain
from .orchestrator import AutonomousReleaseOrchestrator
from .release import ReleaseController
from .serialization import (
release_plan_from_mapping,
release_plan_to_mapping,
task_plan_from_mapping,
task_plan_to_mapping,
)
class AutonomousReleaseAPI:
def __init__(self, orchestrator: AutonomousReleaseOrchestrator) -> None:
self.orchestrator = orchestrator
self.release = ReleaseController()
def _chain(self, task_id: str) -> EvidenceChain:
return EvidenceChain(self.orchestrator.workflow.store, task_id)
def create_task(self, payload: Mapping[str, Any]) -> dict[str, Any]:
plan = task_plan_from_mapping(payload)
result = self.orchestrator.create(plan)
self.orchestrator.workflow.next_steps(plan)
return {
"task_id": result.task_id,
"plan_digest": result.plan_digest,
"production_side_effects": result.production_side_effects,
"plan": task_plan_to_mapping(plan),
}
def close_evidence(self, task_id: str) -> dict[str, Any]:
result = self._chain(task_id).closure_digest()
return {
"task_id": task_id,
"evidence_closure": result,
"production_side_effects": False,
"secrets_included": False,
}
def task_steps(self, task_id: str) -> dict[str, Any]:
status = self.task_status(task_id)
return {
"task_id": task_id,
"steps": status["steps"],
"production_side_effects": False,
"secrets_included": False,
}
def claim(self, task_id: str, payload: Mapping[str, Any]) -> dict[str, Any]:
step_id = str(payload["step_id"])
worker_id = str(payload["worker_id"])
session_id = str(payload["session_id"])
workflow = self.orchestrator.workflow
workflow.claim(task_id, step_id, worker_id, session_id)
workflow.start(task_id, step_id, worker_id, session_id)
self._chain(task_id).append(
"STEP_CLAIMED",
task_id,
{
"step_id": step_id,
"worker_id": worker_id,
"session_id": session_id,
"secrets_included": False,
},
)
return {
"task_id": task_id,
"step_id": step_id,
"state": "RUNNING",
"production_side_effects": False,
"secrets_included": False,
}
def heartbeat(self, task_id: str, payload: Mapping[str, Any]) -> dict[str, Any]:
step_id = str(payload["step_id"])
worker_id = str(payload["worker_id"])
session_id = str(payload["session_id"])
self.orchestrator.workflow.heartbeat(task_id, step_id, worker_id, session_id)
return {
"task_id": task_id,
"step_id": step_id,
"state": "RUNNING",
"production_side_effects": False,
"secrets_included": False,
}
def result(self, task_id: str, payload: Mapping[str, Any]) -> dict[str, Any]:
step_id = str(payload["step_id"])
result_digest = str(payload["result_digest"])
self.orchestrator.workflow.complete(task_id, step_id, result_digest=result_digest)
self._chain(task_id).append(
"STEP_RESULT",
task_id,
{
"step_id": step_id,
"result_digest": result_digest,
"secrets_included": False,
},
)
return {
"task_id": task_id,
"step_id": step_id,
"state": "COMPLETED",
"production_side_effects": False,
"secrets_included": False,
}
def evidence(self, task_id: str) -> dict[str, Any]:
chain = self._chain(task_id)
return {
"task_id": task_id,
"events": chain.export(),
"verified": chain.verify(),
"production_side_effects": False,
"secrets_included": False,
}
def create_release_plan(self, payload: Mapping[str, Any]) -> dict[str, Any]:
plan = release_plan_from_mapping(payload)
self.release = ReleaseController()
self.release.attach_plan(plan)
self.release.mark_built()
return {
"plan": release_plan_to_mapping(plan),
"release_state": self.release.status().state.value,
"production_side_effects": False,
"secrets_included": False,
}
def verify_release(self, payload: Mapping[str, Any]) -> dict[str, Any]:
self.release.verify(
artifact_ok=payload.get("artifact_ok") is True,
evidence_ok=payload.get("evidence_ok") is True,
)
self.release.request_owner_approval()
return {
"release_state": self.release.status().state.value,
"production_release": "BLOCKED_EXTERNAL_OWNER_SIGNATURE",
"production_side_effects": False,
"secrets_included": False,
}
def task_status(self, task_id: str) -> dict[str, Any]:
store = self.orchestrator.workflow.store
state, revision = store.get_task_state(task_id)
steps = store.list_steps(task_id)
return {
"task_id": task_id,
"state": state.value,
"revision": revision,
"steps": {
step_id: {
"state": item.state.value,
"attempt": item.attempt,
"max_attempts": item.max_attempts,
"worker_id": item.worker_id,
"session_id": item.session_id,
}
for step_id, item in steps.items()
},
"production_side_effects": False,
"secrets_included": False,
}