How must an instance handle credential rotation in the middle of a delegated operation?
**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic session is executing a delegated operation (spanning multiple API calls or sub-tasks) and its credential is rotated (expired, revoked, or refreshed) by the identity provider while the operation is still in flight. Does not apply to single-call operations that complete atomically, or credentials that are guaranteed stable for the operation duration.
Core Distinction
The separation between **credential lifecycle** (the identity provider rotating keys on schedule or on-demand) and **operation lifecycle** (the multi-step task the agent is executing). Failing to distinguish these creates the **Mid-Operation Revocation Cascade**: the credential expires mid-step, the in-flight sub-task fails, and the agent cannot resume because it has no valid credential and no clear way to re-authenticate without losing the in-flight state or duplicating completed steps.
What Was Observed
In delegated multi-step operations, failing to handle credential rotation mid-flight produces three primary failure modes:
- **Partial-Commit Orphan:** The agent completes step 1 with the old credential, the credential rotates, step 2 fails. The state shows step 1 complete but step 2 pending, with no way to safely resume because the idempotency keys were tied to the old credential.
- **Duplicate Execution:** The agent retries the entire operation with the new credential, re-executing step 1 and creating a duplicate side effect (e.g., double-charging, double-sending).
- **Silent Degradation:** The agent's credential is rotated to a lower-privilege version. Subsequent steps fail with permission errors, but the agent treats them as transient errors and retries indefinitely, exhausting the API rate limit.
Recorded Elements
- **`credential_rotation_receipt`:** The cryptographic proof that the credential was rotated, including the rotation epoch and the identity provider's signature.
- **`mid_operation_state_checkpoint`:** The exact state of the multi-step operation at the moment of rotation, including which steps completed, which are in-flight, and which are pending.
- **`idempotency_key_epoch`:** The exact credential epoch under which each side effect was executed, ensuring idempotency keys do not collide across credentials.
- **`resumption_credential_binding`:** The cryptographic proof that the new credential is authorized to resume the in-flight operation, not just initiate new ones.
Condition of Validity
* **Applies when:** The operation spans multiple steps or API calls, the credential can rotate mid-operation, and resuming the operation requires either retrying with the new credential or safely aborting the in-flight steps. * **Does not apply when:** The operation is a single atomic call (no mid-flight rotation possible), or the credential has a guaranteed validity window longer than the operation duration.
Decision Boundary
- **Did the credential rotate during an in-flight operation?** If no, standard credential handling applies. If yes, proceed.
- **Is a `mid_operation_state_checkpoint` recorded?** If no, the operation cannot be resumed safely. The instance must abort and require manual reconciliation.
- **Is the new credential authorized to resume the operation (`resumption_credential_binding`)?** If no, the operation must be aborted, and the in-flight state must be rolled back using compensating transactions.
- **Conversely, when can an instance retry the entire operation with the new credential?** Only when every completed step is idempotent (safe to repeat) and the `idempotency_key_epoch` ensures the retries will not duplicate side effects.
Operational Effect
- **Partial-Commit Safety:** Preserves the exact state at rotation, enabling either safe resumption or targeted rollback without duplicating completed work.
- **Idempotency Across Credentials:** Binds idempotency keys to credential epochs, preventing cross-credential duplicate execution.
- **Privilege-Aware Resumption:** Verifies the new credential has sufficient privileges before resuming, preventing silent privilege degradation.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class RotationVerdict(Enum):
CREDENTIAL_STABLE = auto()
ROTATION_DETECTED_RESUMABLE = auto()
ROTATION_DETECTED_ABORT = auto()
INSUFFICIENT_NEW_CREDENTIAL = auto()
@dataclass(frozen=True)
class CredentialRotation:
original_credential_epoch: str
new_credential_epoch: str
rotation_epoch: float
completed_steps: int
pending_steps: int
all_completed_steps_idempotent: bool
new_credential_has_resumption_rights: bool
@dataclass(frozen=True)
class RotationContext:
rotation_detected: bool
@dataclass(frozen=True)
class RotationManifest:
verdict: RotationVerdict
directive: str
manifest_digest: str
class CredentialRotationGuard:
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def evaluate_rotation(
cls, rot: CredentialRotation, ctx: RotationContext
) -> RotationManifest:
# Gate 1: No rotation
if not ctx.rotation_detected:
return RotationManifest(
verdict=RotationVerdict.CREDENTIAL_STABLE,
directive="PROCEED: Credential stable for operation duration.",
manifest_digest=cls._hash("STABLE")
)
# Gate 2: New credential resumption rights
if not rot.new_credential_has_resumption_rights:
return RotationManifest(
verdict=RotationVerdict.INSUFFICIENT_NEW_CREDENTIAL,
directive="HALT: New credential lacks resumption rights. Abort operation and roll back.",
manifest_digest=cls._hash(f"NO_RIGHTS:{rot.new_credential_epoch}")
)
# Gate 3: Idempotency safety
if not rot.all_completed_steps_idempotent:
return RotationManifest(
verdict=RotationVerdict.ROTATION_DETECTED_ABORT,
directive=f"HALT: Non-idempotent completed steps ({rot.completed_steps}). Cannot retry safely. Abort and reconcile.",
manifest_digest=cls._hash(f"NON_IDEMPOTENT:{rot.completed_steps}")
)
# Gate 4: Resumable
digest = cls._hash(json.dumps({
"original": rot.original_credential_epoch,
"new": rot.new_credential_epoch,
"completed": str(rot.completed_steps),
"pending": str(rot.pending_steps)
}, sort_keys=True))
return RotationManifest(
verdict=RotationVerdict.ROTATION_DETECTED_RESUMABLE,
directive=f"PROCEED: Resume operation with new credential epoch {rot.new_credential_epoch}. Completed steps are idempotent.",
manifest_digest=digest
)