# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a2a-task-revocation-as-incomplete-until-compensation-is-acknowledged-by-the-delegated-org.md
# Remnant Warrant:    https://corpusfetch.com/c/under-what-conditions-must-an-instance-treat-a2a-task-revocation-as-incomplete-until-compensation-is-acknowledged-by-the-delegated-org.warrant.json
# Markdown SHA-256:   48a1d46c24aa195e5d8d18dd70a5cf1c6799b92c0efc80df7a2961da389f4368
# Code AST SHA-256:   89405631aaee1c57c2dc430f59ca5e7850654938766a723b671d831d30ef9b32
# Verified Classes:   CompensationUnconfirmedError, DelegationRecord
# Defensive Guards:   5 fail-closed invariants (zero external deps)

class CompensationUnconfirmedError(RuntimeError):
    """Raised when a delegation is released or archived while compensation
    for committed external effects has not been acknowledged."""
    pass


TERMINAL_TASK_STATES = {"completed", "failed", "canceled"}
EFFECT_CAPABLE_TASK_CLASSES = {"procurement", "payment", "provisioning", "shipment"}


class DelegationRecord:
    def __init__(self, task_id, task_class, remote_org, has_shared_coordinator):
        self.task_id = task_id
        self.task_class = task_class
        self.remote_org = remote_org
        self.has_shared_coordinator = has_shared_coordinator
        self.task_status = "working"
        self.compensation_ack = None          # set only by a real compensation receipt
        self.no_compensation_declaration = None  # signed "no effects" declaration
        self.budget_hold_active = True

    def requires_compensation_gate(self) -> bool:
        return (
            self.task_class in EFFECT_CAPABLE_TASK_CLASSES
            and not self.has_shared_coordinator
        )

    def on_terminal_status(self, status: str):
        if status not in TERMINAL_TASK_STATES:
            raise ValueError(f"non-terminal status passed as terminal: {status}")
        self.task_status = status
        if status == "canceled" and self.requires_compensation_gate():
            # Do NOT release the hold. Enter pending-compensation state.
            self._enter_pending_compensation()

    def _enter_pending_compensation(self):
        # budget_hold_active stays True; delegation is not resolved.
        self.pending_compensation = True

    def record_compensation_ack(self, receipt):
        if receipt.task_id != self.task_id:
            raise CompensationUnconfirmedError(
                f"compensation receipt for {receipt.task_id} does not match {self.task_id}"
            )
        self.compensation_ack = receipt
        self.budget_hold_active = False

    def record_no_compensation_declaration(self, declaration):
        if declaration.signer_org != self.remote_org:
            raise CompensationUnconfirmedError(
                "no-compensation declaration not signed by delegated org"
            )
        self.no_compensation_declaration = declaration
        self.budget_hold_active = False

    def release(self):
        if self.task_status not in TERMINAL_TASK_STATES:
            raise CompensationUnconfirmedError(
                f"cannot release non-terminal delegation {self.task_id}"
            )
        if self.requires_compensation_gate() and not (
            self.compensation_ack is not None
            or self.no_compensation_declaration is not None
        ):
            raise CompensationUnconfirmedError(
                f"delegation {self.task_id} canceled but compensation unconfirmed; "
                f"budget hold must remain active"
            )
        self.budget_hold_active = False
        return "released"
