Files
aios/active-runtime/app/task_plane/receipts.py
T
AIOS Governed Continuation f557f18620 feat(governed): ANNEX-2.3 R2 agent auto-claim chain (SchedulabilityProof→recheck→claim→pull→receipt)
Implement governed auto-claim loop on task_plane:
- SchedulabilityProof model (5-segment persisted diagnostic snapshot) + ProofBuilder
- authoritative_claim_recheck (A23: cross-tenant/task/generation/lease/capacity)
- SchedulingService wiring TaskScheduler to HTTP long_poll/poll/schedule
- governed_claim single-transaction (proof→recheck→claim→receipt→event)
- HttpRunnerTransport (real transport, renew with claim_id self-resolution)
- AgentPullRunner discover_once handles idle/no-task (REQ-03c)
- receipt/readback endpoints (/proofs, /receipts, /tasks/{id}/receipts)

Pure stdlib, no new deps; 25 tests pass (22 R2 + 2 heartbeat + 1 idle). Base: 3c320ae.
2026-09-14 14:37:42 +08:00

79 lines
2.0 KiB
Python

"""Claim/proof receipts for ANNEX-2.3 R2 governed claim audit read-back.
A receipt is a minimal, tamper-evident record generated alongside a governed
claim. Its digest is sha256(canonical_json(payload)) so the payload can be
re-verified on read-back, consistent with the evidence and event chain.
"""
from __future__ import annotations
import time
import uuid
from dataclasses import dataclass
from typing import Any
from .models import compute_sha256_hex
def receipt_digest(payload: dict[str, Any]) -> str:
return compute_sha256_hex(payload)
@dataclass
class ClaimReceipt:
receipt_id: str
kind: str
task_id: str
worker_id: str
claim_id: str | None
proof_id: str | None
digest: str
payload: dict[str, Any]
created_at: float
def to_dict(self) -> dict[str, Any]:
return {
"receipt_id": self.receipt_id,
"kind": self.kind,
"task_id": self.task_id,
"worker_id": self.worker_id,
"claim_id": self.claim_id,
"proof_id": self.proof_id,
"digest": self.digest,
"payload": self.payload,
"created_at": self.created_at,
}
def build_receipt(
*,
kind: str,
task_id: str,
worker_id: str,
claim_id: str | None = None,
proof_id: str | None = None,
extra: dict[str, Any] | None = None,
created_at: float | None = None,
) -> ClaimReceipt:
"""Build a ClaimReceipt whose digest covers the full digestable payload."""
extra = extra or {}
payload = {
"kind": kind,
"task_id": task_id,
"worker_id": worker_id,
"claim_id": claim_id,
"proof_id": proof_id,
**extra,
}
return ClaimReceipt(
receipt_id=f"rct-{uuid.uuid4()}",
kind=kind,
task_id=task_id,
worker_id=worker_id,
claim_id=claim_id,
proof_id=proof_id,
digest=receipt_digest(payload),
payload=payload,
created_at=created_at if created_at is not None else time.time(),
)