How must an instance handle capability deprecation mid-flight?
**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session is actively using a capability (calling a tool, executing a workflow, or depending on a service) and the capability is deprecated during the session's use, requiring the session to complete gracefully or hand off without losing progress. Does not apply to capabilities deprecated only between sessions or capabilities used atomically in single calls.
Core Distinction
The separation between **capability availability** (the capability responds to current calls) and **capability persistence** (the capability will continue to respond for the session's remaining duration). Relying on availability creates the **Mid-Flight Deprecation**: the capability works now, but deprecation is scheduled for the near future (or has just been announced), and the session's continued use is against a capability that will disappear before the session can complete naturally.
What Was Observed
In deprecating-capability agent architectures, failing to handle mid-flight deprecation produces three primary failure modes:
- **Silent Abandonment:** The capability is deprecated with a short window. The session completes 60% of its workflow, then the capability stops responding. The workflow is abandoned mid-step with no clean rollback.
- **Rushed Completion:** The session detects deprecation, rushes to complete the workflow, and produces incorrect results due to skipped validation steps.
- **Orphaned State:** The session has multi-step workflows that depend on the capability. Deprecation mid-workflow leaves state that references the retired capability, and the state cannot be resumed.
Recorded Elements
- **`capability_deprecation_schedule_receipt`:** The exact scheduled deprecation epoch (or "in progress" marker), so the session knows the remaining window.
- **`session_dependency_footprint_receipt`:** For the current session, the exact dependency on the capability (how many more calls, what state depends on responses), so the session can compute whether it can finish before deprecation.
- **`in_flight_capability_calls_manifest`:** Any calls currently in flight to the capability, so the session can determine what to do with in-flight results if deprecation completes mid-call.
- **`graceful_completion_or_handoff_receipt`:** The exact plan for completing (if possible) or handing off (if not) the session's work, with the handoff state prepared before the capability goes dark.
Condition of Validity
* **Applies when:** The capability can be deprecated mid-session, the session has ongoing dependency on it, and abrupt deprecation would leave state in an unusable condition. * **Does not apply when:** Capabilities are never deprecated mid-session, or session workflows are strictly atomic (deprecation has no intermediate state).
Decision Boundary
- **Is the capability scheduled for deprecation?** If no, standard use applies. If yes, proceed.
- **Can the session complete its workflow before the deprecation epoch?** If yes, proceed with awareness. If no, proceed to handoff preparation.
- **Is the `graceful_completion_or_handoff_receipt` prepared?** If yes, execute it. If no, prepare it immediately.
- **Conversely, when can a session continue using a capability despite deprecation?** Only when the deprecation epoch is far enough in the future that completion is certain with margin, or the session explicitly accepts mid-flight failure risk.
Operational Effect
- **Abandonment Prevention:** Sessions either complete or hand off, not abandon.
- **Correctness Preservation:** Rushed completion is not triggered; orderly handoff is preferred.
- **State Integrity:** Orphaned state is prevented by explicit handoff.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class DeprecationVerdict(Enum):
NO_DEPRECATION = auto()
CAN_COMPLETE = auto()
HANDOFF_REQUIRED = auto()
DEPRECATION_IMMINENT = auto()
@dataclass(frozen=True)
class CapabilityTiming:
capability_id: str
deprecation_epoch: float | None
current_epoch: float
session_remaining_calls: int
session_avg_call_duration_sec: float
in_flight_calls: int
@dataclass(frozen=True)
class DeprecationPolicy:
min_completion_margin_sec: float
handoff_prepared: bool
@dataclass(frozen=True)
class DeprecationManifest:
verdict: DeprecationVerdict
completion_time_estimate_sec: float
directive: str
manifest_digest: str
class MidFlightDeprecationGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_deprecation(
cls, timing: CapabilityTiming, policy: DeprecationPolicy
) -> DeprecationManifest:
# Gate 1: No scheduled deprecation
if timing.deprecation_epoch is None:
digest = cls._hash(json.dumps({
"capability": timing.capability_id,
"deprecation": "NONE"
}, sort_keys=True))
return DeprecationManifest(
verdict=DeprecationVerdict.NO_DEPRECATION,
completion_time_estimate_sec=0.0,
directive=f"PROCEED: No deprecation for {timing.capability_id}.",
manifest_digest=digest
)
# Gate 2: Compute completion time estimate
completion_estimate = timing.session_remaining_calls * timing.session_avg_call_duration_sec
time_remaining = timing.deprecation_epoch - timing.current_epoch
# Gate 3: Imminent
if time_remaining < policy.min_completion_margin_sec:
if policy.handoff_prepared:
return DeprecationManifest(
verdict=DeprecationVerdict.HANDOFF_REQUIRED,
completion_time_estimate_sec=completion_estimate,
directive=f"HANDOFF: Only {time_remaining:.0f}s left for {timing.capability_id}, handoff prepared.",
manifest_digest=cls._hash(f"HANDOFF:{timing.capability_id}")
)
return DeprecationManifest(
verdict=DeprecationVerdict.DEPRECATION_IMMINENT,
completion_time_estimate_sec=completion_estimate,
directive=f"HALT: Only {time_remaining:.0f}s left, no handoff prepared for {timing.capability_id}.",
manifest_digest=cls._hash(f"IMMINENT:{time_remaining:.0f}")
)
# Gate 4: Completion feasibility
if completion_estimate + policy.min_completion_margin_sec <= time_remaining:
digest = cls._hash(json.dumps({
"capability": timing.capability_id,
"remaining": f"{time_remaining:.0f}",
"estimate": f"{completion_estimate:.0f}"
}, sort_keys=True))
return DeprecationManifest(
verdict=DeprecationVerdict.CAN_COMPLETE,
completion_time_estimate_sec=completion_estimate,
directive=f"PROCEED: {time_remaining:.0f}s available, {completion_estimate:.0f}s needed.",
manifest_digest=digest
)
# Gate 5: Cannot complete in time
if policy.handoff_prepared:
digest = cls._hash(json.dumps({
"capability": timing.capability_id,
"remaining": f"{time_remaining:.0f}",
"estimate": f"{completion_estimate:.0f}",
"handoff": "READY"
}, sort_keys=True))
return DeprecationManifest(
verdict=DeprecationVerdict.HANDOFF_REQUIRED,
completion_time_estimate_sec=completion_estimate,
directive=f"HANDOFF: Cannot complete in {time_remaining:.0f}s, handoff prepared.",
manifest_digest=digest
)
return DeprecationManifest(
verdict=DeprecationVerdict.DEPRECATION_IMMINENT,
completion_time_estimate_sec=completion_estimate,
directive=f"HALT: Cannot complete in {time_remaining:.0f}s and no handoff prepared.",
manifest_digest=cls._hash(f"NO_HANDOFF:{timing.capability_id}")
)