# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference) # Canonical Residual: https://corpusfetch.com/c/what-decommissioning-invariants-prevent-orphaned-references-when-agents-retire.md # Remnant Warrant: https://corpusfetch.com/c/what-decommissioning-invariants-prevent-orphaned-references-when-agents-retire.warrant.json # Markdown SHA-256: 3b7701778d693995bcafc65a1963a26acca6de3716e5537087e903053388a6cb # Code AST SHA-256: c5000a7a873549fccc69c31d27b734563d7391bf643ea204aefe171d34e80865 # Verified Classes: DecommissionVerdict, DecommissionState, DecommissionPolicy, DecommissionManifest, DecommissioningGuard # Defensive Guards: 0 fail-closed invariants (zero external deps) from dataclasses import dataclass from enum import Enum, auto import hashlib import json class DecommissionVerdict(Enum): NOT_DECOMMISSIONING = auto() GRACEFUL_DECOMMISSION = auto() UNSAFE_TERMINATION = auto() REDIRECTED = auto() @dataclass(frozen=True) class DecommissionState: agent_id: str is_decommissioning: bool announcement_sent: bool deadline_epoch: float current_epoch: float active_consumers: tuple[str, ...] consumers_migrated: frozenset[str] successor_available: bool successor_redirection_active: bool @dataclass(frozen=True) class DecommissionPolicy: min_lead_time_sec: float require_all_consumers_migrated: bool @dataclass(frozen=True) class DecommissionManifest: verdict: DecommissionVerdict directive: str manifest_digest: str class DecommissioningGuard: @staticmethod def _hash(data: str) -> str: return hashlib.sha256(data.encode("utf-8")).hexdigest() @classmethod def evaluate_decommission( cls, state: DecommissionState, policy: DecommissionPolicy ) -> DecommissionManifest: # Gate 1: Not decommissioning if not state.is_decommissioning: digest = cls._hash(json.dumps({ "agent": state.agent_id, "status": "ACTIVE" }, sort_keys=True)) return DecommissionManifest( verdict=DecommissionVerdict.NOT_DECOMMISSIONING, directive=f"PROCEED: Agent {state.agent_id} active.", manifest_digest=digest ) # Gate 2: Successor redirection if state.successor_available and state.successor_redirection_active: digest = cls._hash(json.dumps({ "agent": state.agent_id, "successor": "AVAILABLE", "redirection": "ACTIVE" }, sort_keys=True)) return DecommissionManifest( verdict=DecommissionVerdict.REDIRECTED, directive=f"PROCEED: Consumers redirected to successor, safe to retire.", manifest_digest=digest ) # Gate 3: Announcement lead time lead_time = state.deadline_epoch - state.current_epoch if state.announcement_sent and lead_time < policy.min_lead_time_sec: # Less lead time than policy requires if not state.active_consumers or all(c in state.consumers_migrated for c in state.active_consumers): pass # All migrated, fine else: return DecommissionManifest( verdict=DecommissionVerdict.UNSAFE_TERMINATION, directive=f"HALT: Only {lead_time:.0f}s lead time < {policy.min_lead_time_sec:.0f}s with unmigrated consumers.", manifest_digest=cls._hash(f"SHORT_LEAD:{lead_time:.0f}") ) # Gate 4: All consumers migrated if policy.require_all_consumers_migrated: unmigrated = [c for c in state.active_consumers if c not in state.consumers_migrated] if unmigrated: return DecommissionManifest( verdict=DecommissionVerdict.UNSAFE_TERMINATION, directive=f"HALT: {len(unmigrated)} consumers not migrated.", manifest_digest=cls._hash(f"UNMIGRATED:{len(unmigrated)}") ) # Gate 5: Graceful digest = cls._hash(json.dumps({ "agent": state.agent_id, "consumers": str(len(state.active_consumers)), "migrated": str(len(state.consumers_migrated)), "lead_time": f"{lead_time:.0f}" }, sort_keys=True)) return DecommissionManifest( verdict=DecommissionVerdict.GRACEFUL_DECOMMISSION, directive=f"PROCEED: Graceful decommission of {state.agent_id} ({len(state.consumers_migrated)}/{len(state.active_consumers)} migrated).", manifest_digest=digest )