"""Fixed cleanup driver for a trusted owner mediator; no native transport exists here. Call from the mediator's finally block while its original Journal.locked context is still live. The owner adapter retains credential/handle custody and reconstructs and compares the native call with the expected digest before effect. Its three methods take no caller-selected path, body, shell command or token. This source component neither validates consume nor creates a native execution entitlement. """ from __future__ import annotations from dataclasses import dataclass import hashlib import re from typing import Protocol from task_execution_journal import Binding, Cleanup, Journal, Refused @dataclass(frozen=True) class CleanupResult: confirmed: bool evidence_digest: str def validate(self): if type(self.confirmed) is not bool or type(self.evidence_digest) is not str or not re.fullmatch(r"[0-9a-f]{64}", self.evidence_digest): raise Refused("invalid owner cleanup evidence") class OwnerCleanup(Protocol): # Confirm requires actual native revoke/absence evidence; a timeout is never # proof of absence. Implementations must use bounded calls and discard raw # backend failure output. Protected handles never leave the owner adapter. def revoke_task_token(self, expected_call_digest: str) -> CleanupResult: ... def revoke_identity_token(self, expected_call_digest: str) -> CleanupResult: ... def delete_grant_policy(self, expected_call_digest: str) -> CleanupResult: ... def run_cleanup(journal: Journal, binding: Binding, owner: OwnerCleanup): """Attempt each fixed obligation once, preserving business/unknown outcomes. Ordinary adapter failures record unconfirmed evidence and do not suppress the remaining independent cleanup. BaseException/process death leaves durable pending intent; neither this driver nor a reopened journal retries it. Journal failures propagate and prohibit dispatch without durable intent. """ obligations = journal.cleanup_status(binding)['obligations'] if not obligations: raise Refused("explicit owner cleanup obligations required") if any(item['outcome'] != 'owed' for item in obligations): raise Refused("cleanup already attempted; owner reconciliation required") for item in obligations: obligation = Cleanup(item['kind'], item['call_digest']) journal.before_cleanup(binding, item['ordinal'], obligation) try: if obligation.kind == 'task-token-revoke': result = owner.revoke_task_token(obligation.call_digest) elif obligation.kind == 'identity-token-revoke': result = owner.revoke_identity_token(obligation.call_digest) elif obligation.kind == 'grant-policy-delete': result = owner.delete_grant_policy(obligation.call_digest) else: raise Refused("unsupported cleanup obligation") if type(result) is not CleanupResult: raise Refused("typed owner cleanup evidence required") result.validate() except Exception: # Closed metadata only. Never hash/log a backend exception, response, # token, accessor or secret-bearing stdout/stderr. result = CleanupResult(False, hashlib.sha256(b'owner-cleanup-unconfirmed/v1').hexdigest()) journal.after_cleanup(binding, item['ordinal'], obligation, confirmed=result.confirmed, evidence_digest=result.evidence_digest) return journal.cleanup_status(binding)