Files
aios/active-runtime/app/task_plane/autonomous_resume_aios_adapter.py
T

973 lines
32 KiB
Python

"""AIOS-native binding layer for the unattended resume controller.
This module intentionally contains no HTTP client, Unix-socket client, database,
systemd integration, scheduler loop, secret handling, or privileged identity
mutation.
It converts existing AIOS authorities into the generic callbacks defined by
``autonomous_resume_integration``. Field code supplies the current authoritative
implementations for:
- durable Owner authorization/grant projection;
- canonical governed-worker activation/status;
- governed task admission;
- task/claim/TaskLease/TaskPrincipal/WriterLease/CAS ownership;
- bounded source-lane execution and integration;
- Gate0;
- ReleaseReady/deployability;
- PromotionController;
- final production acceptance;
- mission resume lease/checkpoint/effect reconciliation.
The adapter is deterministic and fail-closed. It never fabricates any credential,
principal, lease, fence, task, commit, receipt, approval, or deployment result.
"""
from __future__ import annotations
from dataclasses import dataclass
from enum import Enum
import hashlib
import json
from typing import Mapping, Protocol, Sequence
from .autonomous_resume_controller import (
EffectState,
MissionCheckpoint,
MissionObservation,
MissionPhase,
ResumeLease,
StepResult,
StepStatus,
)
from .autonomous_resume_integration import (
AuthorityCallbacks,
CallableAutonomousExecutionPort,
PhaseCallbacks,
)
def _canonical(value) -> bytes:
return json.dumps(
value,
sort_keys=True,
separators=(",", ":"),
ensure_ascii=False,
allow_nan=False,
).encode("utf-8")
def _digest(value) -> str:
return hashlib.sha256(_canonical(value)).hexdigest()
class WorkerState(str, Enum):
ACTIVE = "ACTIVE"
STARTING = "STARTING"
INACTIVE = "INACTIVE"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
class WorkState(str, Enum):
PENDING = "PENDING"
RUNNING = "RUNNING"
COMPLETE = "COMPLETE"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
class GateState(str, Enum):
PASS = "PASS"
HOLD = "HOLD"
UNKNOWN = "UNKNOWN"
class ReadyState(str, Enum):
READY = "READY"
NOT_READY = "NOT_READY"
UNKNOWN = "UNKNOWN"
class PromotionState(str, Enum):
IN_PROGRESS = "IN_PROGRESS"
VERIFIED = "VERIFIED"
ROLLED_BACK = "ROLLED_BACK"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
class AcceptanceState(str, Enum):
PASS = "PASS"
FAIL_ROLLED_BACK = "FAIL_ROLLED_BACK"
BLOCKED = "BLOCKED"
UNKNOWN = "UNKNOWN"
@dataclass(frozen=True)
class DurableGrantView:
grant_id: str
active: bool
repository: str
allowed_target_prefixes: tuple[str, ...]
max_task_risk: str
expires_at: float | None = None
owner_action_required: bool = False
evidence_refs: tuple[str, ...] = ()
def permits(self, *, repository: str, branch: str, risk: str) -> bool:
if not self.active:
return False
if self.repository != repository:
return False
if not any(branch.startswith(prefix) for prefix in self.allowed_target_prefixes):
return False
rank = {"low": 0, "medium": 1, "engineering": 2, "high": 3}
if risk not in rank or self.max_task_risk not in rank:
return False
return rank[risk] <= rank[self.max_task_risk]
@dataclass(frozen=True)
class TaskLaneSpec:
lane: str
task_id: str
repository: str
target_branch: str
risk: str
description: str
write_set: tuple[str, ...]
def __post_init__(self) -> None:
if not self.lane or not self.task_id or not self.repository:
raise ValueError("lane_task_repository_required")
if not self.target_branch.startswith("agent/"):
raise ValueError("target_branch_must_use_agent_prefix")
if not self.write_set:
raise ValueError("write_set_required")
@dataclass(frozen=True)
class TaskAdmissionRequest:
task_id: str
idempotency_key: str
repository: str
base_oid: str
target_branch: str
expected_branch_oid: str
risk: str
description: str
@dataclass(frozen=True)
class TaskAdmissionReceipt:
task_id: str
generation: int
target_branch: str
persisted: bool
claimable: bool
receipt_ref: str
def __post_init__(self) -> None:
if self.generation < 1:
raise ValueError("task_generation_must_be_positive")
if not self.receipt_ref:
raise ValueError("task_admission_receipt_required")
@dataclass(frozen=True)
class WorkerActivationReceipt:
state: WorkerState
receipt_ref: str | None = None
retry_after_seconds: int | None = None
owner_action_required: bool = False
code: str = "WORKER_STATE"
@dataclass(frozen=True)
class LaneExecutionView:
lane: str
state: WorkState
condition_fingerprint: str
receipt_refs: tuple[str, ...] = ()
retry_after_seconds: int | None = None
code: str = "LANE_STATE"
@dataclass(frozen=True)
class IntegrationView:
state: WorkState
condition_fingerprint: str
receipt_refs: tuple[str, ...] = ()
retry_after_seconds: int | None = None
code: str = "INTEGRATION_STATE"
@dataclass(frozen=True)
class GateView:
state: GateState
condition_fingerprint: str
receipt_refs: tuple[str, ...] = ()
retry_after_seconds: int | None = None
code: str = "GATE_STATE"
@dataclass(frozen=True)
class ReleaseReadyView:
state: ReadyState
condition_fingerprint: str
material_digest: str | None
receipt_refs: tuple[str, ...] = ()
retry_after_seconds: int | None = None
code: str = "READY_STATE"
@dataclass(frozen=True)
class PromotionView:
state: PromotionState
condition_fingerprint: str
material_digest: str | None
receipt_refs: tuple[str, ...] = ()
retry_after_seconds: int | None = None
code: str = "PROMOTION_STATE"
@dataclass(frozen=True)
class AcceptanceView:
state: AcceptanceState
condition_fingerprint: str
receipt_refs: tuple[str, ...] = ()
code: str = "ACCEPTANCE_STATE"
@dataclass(frozen=True)
class MissionEffectView:
effect_state: EffectState
condition_fingerprint: str
requires_reconcile: bool
receipt_refs: tuple[str, ...] = ()
class AiosMissionAuthorities(Protocol):
"""Existing AIOS authority surface consumed by this adapter."""
# Live truth.
def read_head_oid(self, repository: str) -> str: ...
def read_material_digest(self, mission_id: str) -> str: ...
def read_generation(self, mission_id: str) -> int: ...
def read_effect_view(self, mission_id: str) -> MissionEffectView: ...
def owner_action_resolved(self, mission_id: str) -> bool: ...
# Durable mission state / one-winner resume authority.
def load_checkpoint(self, mission_id: str) -> MissionCheckpoint | None: ...
def acquire_resume_lease(
self,
mission_id: str,
generation: int,
idempotency_key: str,
lease_seconds: int,
) -> ResumeLease | None: ...
def persist_checkpoint(
self,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> None: ...
def persist_owner_action_required(
self,
payload: Mapping[str, object],
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> str: ...
def release_resume_lease(
self,
lease: ResumeLease,
outcome_code: str,
) -> None: ...
def reconcile_effects(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult: ...
# Admission.
def read_durable_grant(self, mission_id: str) -> DurableGrantView: ...
def ensure_canonical_worker(
self,
mission_id: str,
generation: int,
idempotency_key: str,
) -> WorkerActivationReceipt: ...
def create_or_resume_task(
self,
request: TaskAdmissionRequest,
) -> TaskAdmissionReceipt: ...
def read_task_admission(
self,
lane: TaskLaneSpec,
) -> TaskAdmissionReceipt | None: ...
# Phase execution.
def read_lane_execution(
self,
lane: TaskLaneSpec,
task: TaskAdmissionReceipt,
) -> LaneExecutionView: ...
def drive_integration(
self,
lanes: Sequence[TaskLaneSpec],
tasks: Sequence[TaskAdmissionReceipt],
) -> IntegrationView: ...
def run_gate0(self, mission_id: str) -> GateView: ...
def build_release_ready(self, mission_id: str) -> ReleaseReadyView: ...
def run_promotion(self, mission_id: str) -> PromotionView: ...
def run_final_acceptance(self, mission_id: str) -> AcceptanceView: ...
@dataclass(frozen=True)
class AiosUnattendedMissionConfig:
mission_id: str
repository: str
lanes: tuple[TaskLaneSpec, ...]
admission_risk: str = "medium"
def __post_init__(self) -> None:
if not self.mission_id or not self.repository:
raise ValueError("mission_and_repository_required")
if not self.lanes:
raise ValueError("at_least_one_lane_required")
names = [lane.lane for lane in self.lanes]
task_ids = [lane.task_id for lane in self.lanes]
branches = [lane.target_branch for lane in self.lanes]
if len(names) != len(set(names)):
raise ValueError("duplicate_lane")
if len(task_ids) != len(set(task_ids)):
raise ValueError("duplicate_task_id")
if len(branches) != len(set(branches)):
raise ValueError("duplicate_target_branch")
owners: dict[str, str] = {}
for lane in self.lanes:
if lane.repository != self.repository:
raise ValueError("lane_repository_mismatch")
for resource in lane.write_set:
if resource in owners:
raise ValueError(
f"overlapping_lane_resource:{resource}:"
f"{owners[resource]}:{lane.lane}"
)
owners[resource] = lane.lane
class AiosUnattendedCallbackFactory:
"""Construct generic controller callbacks from current AIOS authorities."""
def __init__(
self,
authority: AiosMissionAuthorities,
config: AiosUnattendedMissionConfig,
) -> None:
self.authority = authority
self.config = config
self._admissions: dict[str, TaskAdmissionReceipt] = {}
def build_port(self) -> CallableAutonomousExecutionPort:
return CallableAutonomousExecutionPort(
AuthorityCallbacks(
read_observation=self.read_observation,
load_checkpoint=self.authority.load_checkpoint,
acquire_resume_lease=self.authority.acquire_resume_lease,
persist_checkpoint=self.authority.persist_checkpoint,
persist_owner_action_required=self.authority.persist_owner_action_required,
release_resume_lease=self.authority.release_resume_lease,
reconcile=self.authority.reconcile_effects,
),
PhaseCallbacks(
admission=self.admission,
source_execution=self.source_execution,
gate0=self.gate0,
release_ready=self.release_ready,
promotion=self.promotion,
final_acceptance=self.final_acceptance,
),
)
def read_observation(self, mission_id: str) -> MissionObservation:
if mission_id != self.config.mission_id:
raise ValueError("mission_id_mismatch")
generation = self.authority.read_generation(mission_id)
head_oid = self.authority.read_head_oid(self.config.repository)
material = self.authority.read_material_digest(mission_id)
effect = self.authority.read_effect_view(mission_id)
fingerprint = _digest({
"schema": "aios.unattended-condition.v1",
"mission_id": mission_id,
"generation": generation,
"head_oid": head_oid,
"material_digest": material,
"effect_state": effect.effect_state.value,
"effect_condition": effect.condition_fingerprint,
"requires_reconcile": effect.requires_reconcile,
})
return MissionObservation(
mission_id=mission_id,
generation=generation,
head_oid=head_oid,
material_digest=material,
condition_fingerprint=fingerprint,
effect_state=effect.effect_state,
requires_reconcile=effect.requires_reconcile,
owner_action_resolved=self.authority.owner_action_resolved(mission_id),
evidence_refs=effect.receipt_refs,
)
def admission(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
grant = self.authority.read_durable_grant(observation.mission_id)
grant_refs = tuple(grant.evidence_refs)
if not grant.active:
return StepResult(
StepStatus.BLOCKED_OWNER if grant.owner_action_required
else StepStatus.BLOCKED_DETERMINISTIC,
"DURABLE_GRANT_NOT_ACTIVE",
self._phase_fingerprint(
"admission", observation, {"grant_id": grant.grant_id, "active": False}
),
receipt_refs=grant_refs,
owner_action_payload=(
{
"boundary": "DURABLE_GRANT_NOT_ACTIVE",
"grant_id": grant.grant_id,
}
if grant.owner_action_required else None
),
)
for lane in self.config.lanes:
if not grant.permits(
repository=lane.repository,
branch=lane.target_branch,
risk=lane.risk,
):
return StepResult(
StepStatus.BLOCKED_OWNER if grant.owner_action_required
else StepStatus.BLOCKED_DETERMINISTIC,
"DURABLE_GRANT_SCOPE_MISMATCH",
self._phase_fingerprint(
"admission",
observation,
{
"grant_id": grant.grant_id,
"lane": lane.lane,
"branch": lane.target_branch,
"risk": lane.risk,
},
),
receipt_refs=grant_refs,
owner_action_payload=(
{
"boundary": "DURABLE_GRANT_SCOPE_MISMATCH",
"grant_id": grant.grant_id,
"lane": lane.lane,
}
if grant.owner_action_required else None
),
)
worker = self.authority.ensure_canonical_worker(
observation.mission_id,
observation.generation,
self._idempotency(
"worker",
observation.mission_id,
observation.generation,
),
)
worker_refs = self._refs(grant_refs, (worker.receipt_ref or "",))
if worker.state is WorkerState.UNKNOWN:
return StepResult(
StepStatus.RECONCILE,
worker.code,
self._phase_fingerprint(
"worker", observation, {"state": worker.state.value}
),
receipt_refs=worker_refs,
)
if worker.state is WorkerState.BLOCKED:
return StepResult(
StepStatus.BLOCKED_OWNER if worker.owner_action_required
else StepStatus.BLOCKED_DETERMINISTIC,
worker.code,
self._phase_fingerprint(
"worker", observation, {"state": worker.state.value}
),
receipt_refs=worker_refs,
owner_action_payload=(
{"boundary": worker.code}
if worker.owner_action_required else None
),
)
if worker.state in {WorkerState.STARTING, WorkerState.INACTIVE}:
return StepResult(
StepStatus.IN_PROGRESS,
worker.code,
self._phase_fingerprint(
"worker", observation, {"state": worker.state.value}
),
receipt_refs=worker_refs,
retry_after_seconds=worker.retry_after_seconds,
)
if worker.state is WorkerState.ACTIVE and not worker.receipt_ref:
return StepResult(
StepStatus.RECONCILE,
"WORKER_ACTIVE_WITHOUT_RECEIPT",
self._phase_fingerprint(
"worker", observation, {"state": worker.state.value}
),
receipt_refs=worker_refs,
)
task_refs: list[str] = list(worker_refs)
admissions: dict[str, TaskAdmissionReceipt] = {}
for lane in self.config.lanes:
request = TaskAdmissionRequest(
task_id=lane.task_id,
idempotency_key=self._idempotency(
"task",
observation.generation,
lane.task_id,
observation.head_oid,
),
repository=lane.repository,
base_oid=observation.head_oid,
target_branch=lane.target_branch,
expected_branch_oid=observation.head_oid,
risk=lane.risk,
description=lane.description,
)
receipt = self.authority.create_or_resume_task(request)
task_refs.append(receipt.receipt_ref)
receipt_error = self._admission_receipt_error(
lane,
receipt,
observation=observation,
)
if receipt_error is not None:
return StepResult(
StepStatus.RECONCILE,
receipt_error,
self._phase_fingerprint(
"task",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(task_refs),
)
if not receipt.persisted:
return StepResult(
StepStatus.RECONCILE,
"TASK_PERSISTENCE_UNKNOWN",
self._phase_fingerprint(
"task",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(task_refs),
)
if not receipt.claimable:
return StepResult(
StepStatus.IN_PROGRESS,
"TASK_NOT_CLAIMABLE_YET",
self._phase_fingerprint(
"task",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(task_refs),
)
admissions[lane.lane] = receipt
self._admissions = admissions
return StepResult(
StepStatus.SUCCEEDED,
"GOVERNED_TASKS_ADMITTED",
self._phase_fingerprint(
"admission",
observation,
{"tasks": sorted(r.task_id for r in admissions.values())},
),
receipt_refs=tuple(task_refs),
next_phase=MissionPhase.S2_EXECUTION,
)
def source_execution(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
recovery_refs: list[str] = []
for lane in self.config.lanes:
if lane.lane in self._admissions:
continue
receipt = self.authority.read_task_admission(lane)
if receipt is None:
return StepResult(
StepStatus.RECONCILE,
"TASK_ADMISSION_NOT_RESOLVED",
self._phase_fingerprint(
"source",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(recovery_refs),
)
recovery_refs.append(receipt.receipt_ref)
receipt_error = self._admission_receipt_error(
lane,
receipt,
observation=observation,
)
if receipt_error is not None:
return StepResult(
StepStatus.RECONCILE,
receipt_error,
self._phase_fingerprint(
"source",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(recovery_refs),
)
if not receipt.persisted:
return StepResult(
StepStatus.RECONCILE,
"TASK_PERSISTENCE_UNKNOWN",
self._phase_fingerprint(
"source",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(recovery_refs),
)
if not receipt.claimable:
return StepResult(
StepStatus.IN_PROGRESS,
"TASK_NOT_CLAIMABLE_YET",
self._phase_fingerprint(
"source",
observation,
{"lane": lane.lane, "task_id": lane.task_id},
),
receipt_refs=tuple(recovery_refs),
)
self._admissions[lane.lane] = receipt
views: list[LaneExecutionView] = []
refs: list[str] = list(recovery_refs)
for lane in self.config.lanes:
view = self.authority.read_lane_execution(
lane,
self._admissions[lane.lane],
)
if view.lane != lane.lane:
return StepResult(
StepStatus.RECONCILE,
"LANE_IDENTITY_MISMATCH",
self._phase_fingerprint(
"source",
observation,
{"expected": lane.lane, "actual": view.lane},
),
receipt_refs=tuple(refs),
)
if view.state is WorkState.COMPLETE and not view.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"LANE_COMPLETE_WITHOUT_RECEIPT",
view.condition_fingerprint,
receipt_refs=tuple(refs),
)
views.append(view)
refs.extend(view.receipt_refs)
unknown = [v for v in views if v.state is WorkState.UNKNOWN]
if unknown:
return StepResult(
StepStatus.RECONCILE,
unknown[0].code,
self._phase_fingerprint(
"source",
observation,
{"states": [(v.lane, v.state.value) for v in views]},
),
receipt_refs=tuple(refs),
)
blocked = [v for v in views if v.state is WorkState.BLOCKED]
if blocked:
return StepResult(
StepStatus.BLOCKED_DETERMINISTIC,
blocked[0].code,
self._phase_fingerprint(
"source",
observation,
{"states": [(v.lane, v.state.value) for v in views]},
),
receipt_refs=tuple(refs),
)
pending = [
v for v in views
if v.state in {WorkState.PENDING, WorkState.RUNNING}
]
if pending:
retry_values = [
v.retry_after_seconds
for v in pending
if v.retry_after_seconds is not None
]
return StepResult(
StepStatus.IN_PROGRESS,
"SOURCE_LANES_IN_PROGRESS",
self._phase_fingerprint(
"source",
observation,
{"states": [(v.lane, v.state.value) for v in views]},
),
receipt_refs=tuple(refs),
retry_after_seconds=min(retry_values) if retry_values else None,
)
integration = self.authority.drive_integration(
self.config.lanes,
tuple(self._admissions[lane.lane] for lane in self.config.lanes),
)
refs.extend(integration.receipt_refs)
if integration.state is WorkState.UNKNOWN:
return StepResult(
StepStatus.RECONCILE,
integration.code,
integration.condition_fingerprint,
receipt_refs=tuple(refs),
)
if integration.state is WorkState.BLOCKED:
return StepResult(
StepStatus.BLOCKED_DETERMINISTIC,
integration.code,
integration.condition_fingerprint,
receipt_refs=tuple(refs),
)
if integration.state in {WorkState.PENDING, WorkState.RUNNING}:
return StepResult(
StepStatus.IN_PROGRESS,
integration.code,
integration.condition_fingerprint,
receipt_refs=tuple(refs),
retry_after_seconds=integration.retry_after_seconds,
)
if integration.state is WorkState.COMPLETE and not integration.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"INTEGRATION_COMPLETE_WITHOUT_RECEIPT",
integration.condition_fingerprint,
receipt_refs=tuple(refs),
)
return StepResult(
StepStatus.SUCCEEDED,
"SOURCE_INTEGRATION_COMPLETE",
integration.condition_fingerprint,
receipt_refs=tuple(refs),
next_phase=MissionPhase.GATE0,
)
def gate0(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
view = self.authority.run_gate0(observation.mission_id)
if view.state is GateState.UNKNOWN:
status = StepStatus.RECONCILE
elif view.state is GateState.HOLD:
status = StepStatus.BLOCKED_DETERMINISTIC
else:
if not view.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"GATE0_PASS_WITHOUT_RECEIPT",
view.condition_fingerprint,
)
status = StepStatus.SUCCEEDED
return StepResult(
status,
view.code,
view.condition_fingerprint,
receipt_refs=view.receipt_refs,
retry_after_seconds=view.retry_after_seconds,
next_phase=(
MissionPhase.RELEASE_READY
if status is StepStatus.SUCCEEDED else None
),
)
def release_ready(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
view = self.authority.build_release_ready(observation.mission_id)
if view.state is ReadyState.UNKNOWN:
status = StepStatus.RECONCILE
elif view.state is ReadyState.NOT_READY:
status = StepStatus.BLOCKED_DETERMINISTIC
else:
if not view.material_digest:
return StepResult(
StepStatus.RECONCILE,
"READY_WITHOUT_MATERIAL_DIGEST",
view.condition_fingerprint,
receipt_refs=view.receipt_refs,
)
if not view.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"READY_WITHOUT_RECEIPT",
view.condition_fingerprint,
)
status = StepStatus.SUCCEEDED
return StepResult(
status,
view.code,
view.condition_fingerprint,
receipt_refs=view.receipt_refs,
retry_after_seconds=view.retry_after_seconds,
next_phase=(
MissionPhase.PROMOTION
if status is StepStatus.SUCCEEDED else None
),
material_digest=view.material_digest,
)
def promotion(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
view = self.authority.run_promotion(observation.mission_id)
if view.state is PromotionState.UNKNOWN:
status = StepStatus.RECONCILE
elif view.state is PromotionState.IN_PROGRESS:
status = StepStatus.IN_PROGRESS
elif view.state is PromotionState.VERIFIED:
if not view.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"PROMOTION_VERIFIED_WITHOUT_RECEIPT",
view.condition_fingerprint,
material_digest=view.material_digest,
)
status = StepStatus.SUCCEEDED
elif view.state is PromotionState.ROLLED_BACK:
status = StepStatus.BLOCKED_DETERMINISTIC
else:
status = StepStatus.BLOCKED_DETERMINISTIC
return StepResult(
status,
view.code,
view.condition_fingerprint,
receipt_refs=view.receipt_refs,
retry_after_seconds=view.retry_after_seconds,
next_phase=(
MissionPhase.FINAL_ACCEPTANCE
if status is StepStatus.SUCCEEDED else None
),
material_digest=view.material_digest,
)
def final_acceptance(
self,
observation: MissionObservation,
checkpoint: MissionCheckpoint,
lease: ResumeLease,
) -> StepResult:
view = self.authority.run_final_acceptance(observation.mission_id)
if view.state is AcceptanceState.UNKNOWN:
status = StepStatus.RECONCILE
elif view.state is AcceptanceState.PASS:
if not view.receipt_refs:
return StepResult(
StepStatus.RECONCILE,
"FINAL_ACCEPTANCE_PASS_WITHOUT_RECEIPT",
view.condition_fingerprint,
)
status = StepStatus.SUCCEEDED
else:
status = StepStatus.BLOCKED_DETERMINISTIC
return StepResult(
status,
view.code,
view.condition_fingerprint,
receipt_refs=view.receipt_refs,
next_phase=(
MissionPhase.COMPLETE
if status is StepStatus.SUCCEEDED else None
),
)
@staticmethod
def _admission_receipt_error(
lane: TaskLaneSpec,
receipt: TaskAdmissionReceipt,
*,
observation: MissionObservation,
) -> str | None:
if receipt.task_id != lane.task_id:
return "TASK_IDENTITY_MISMATCH"
if receipt.target_branch != lane.target_branch:
return "TASK_BRANCH_MISMATCH"
if receipt.generation != observation.generation:
return "TASK_GENERATION_MISMATCH"
return None
@staticmethod
def _refs(*groups: Sequence[str]) -> tuple[str, ...]:
seen: set[str] = set()
values: list[str] = []
for group in groups:
for item in group:
if item and item not in seen:
seen.add(item)
values.append(item)
return tuple(values)
@staticmethod
def _idempotency(*parts: object) -> str:
return "aios:auto:" + _digest(
{"schema": "aios.unattended-idempotency.v1", "parts": list(parts)}
)
@staticmethod
def _phase_fingerprint(
phase: str,
observation: MissionObservation,
details: Mapping[str, object],
) -> str:
return _digest({
"schema": "aios.unattended-phase-condition.v1",
"phase": phase,
"mission_id": observation.mission_id,
"generation": observation.generation,
"head_oid": observation.head_oid,
"material_digest": observation.material_digest,
"details": details,
})