Skip to content

cayu.tasks.TaskStore

Part of cayu.tasks.base. Read the Python API overview and install the required extra.

from cayu.tasks import TaskStore

Persistent store for durable work items.

Worker-lease and recovery-owner transitions use store-authoritative time. Implementations sample, compare, and stamp that time inside the atomic ownership mutation; worker-provided timestamps or cutoffs are not lease authority.

Bases: ABC.

Defaults below are source expressions; Field(...) includes schema/validation settings and default_factory creates a fresh value. Inherited declarations are included. Constructor behavior follows the model or dataclass shown in the bases/decorators.

Name Type Default
service_durability RuntimeStoreDurability RuntimeStoreDurability.UNVERIFIED

list_task_group_reconciliation_candidates (method)

Section titled “list_task_group_reconciliation_candidates (method)”
async TaskStore.list_task_group_reconciliation_candidates(*, after_group_id: str | None=None, limit: int=100) -> list[str]

Read an ordered bounded page of draining groups, without changing them.

async TaskStore.reconcile_task_group(group_id: str) -> TaskGroupSnapshot
async TaskStore.resolve_task_group_quiescence(request: TaskGroupQuiescenceResolution) -> TaskGroupSnapshot
async TaskStore.create_task_group(request: TaskGroupCreate) -> TaskGroupCreationReceipt

Atomically admit a new graph and immutable group, or replay its receipt.

async TaskStore.load_task_group(group_id: str) -> TaskGroupSnapshot | None

Read the group decision and current selected-member states consistently.

async TaskStore.list_task_group_events(group_id: str, *, after_sequence: int=0, limit: int=100) -> list[TaskGroupEvent]

Read bounded, ordered, task-store-owned group evidence.

async TaskStore.create_task_graph(request: TaskGraphCreate) -> TaskGraphCreationReceipt

Atomically admit a bounded graph, or replay its exact creation receipt.

Preserve runtime-prepared invocation authority when copying requests. Expected external parents must match inside the admission transaction; receipts bind both the submitted request and resolved admission digests.

async TaskStore.load_task_graph(graph_id: str) -> TaskGraphSnapshot | None

Read current graph members and retained terminal evidence atomically.

async TaskStore.list_task_graph_events(graph_id: str, *, after_sequence: int=0, limit: int=100) -> list[TaskGraphEvent]

Read one bounded page of durable, task-store-owned graph events.

async TaskStore.publish_work_contract(contract: WorkContract) -> WorkContract

Publish one immutable version or replay its exact canonical content.

async TaskStore.load_work_contract(reference: WorkContractRef) -> WorkContract | None

Load the exact contract named by reference or reject a fingerprint conflict.

load_active_work_contract_task_for_session (method)

Section titled “load_active_work_contract_task_for_session (method)”
async TaskStore.load_active_work_contract_task_for_session(session_id: str) -> Task | None

Load a contracted task whose binding retains authority over a session.

A terminal task does not implicitly release pending session work into the ordinary runtime. Only work-attempt lifecycle settlement retires a binding (retired_contract_binding on its receipt): when the task completes through an accepted decision or is cancelled by group cancellation. Until then the durable session binding remains authoritative and callers must start a new ordinary session.

async TaskStore.admit_ordinary_session_execution(session_id: str) -> None

Atomically admit a session to the ordinary, non-verifier runtime.

Supporting stores must reject admission while a contracted task binding retains authority over the session and must durably prevent later contract attachment to an admitted session. Task terminalization alone is not a release; only the lifecycle settlement that retires the contract binding is. Repeated admission of the same session is idempotent.

async TaskStore.hold_claimed_work_contract_task(task_id: str, *, worker_id: str, lease_expires_at: datetime | None=None, contract: WorkContractRef) -> Task

Claim-fence an unsupported contracted task into operator attention.

async TaskStore.begin_work_attempt(request: WorkAttemptCreate) -> WorkAttempt

Create or replay one bounded execution attempt under a task’s frozen contract.

async TaskStore.enter_work_attempt_execution(request: WorkAttemptExecutionEntryRequest) -> WorkAttemptExecutionEntryResult

Elect one dispatch; an existing entry permits reconciliation only.

async TaskStore.hold_work_attempt_preparation(request: WorkAttemptPreparationHold) -> WorkAttemptPreparationHoldReceipt

Atomically park an exact unattached claim and publish its failure receipt.

record_work_attempt_execution_stop (method)

Section titled “record_work_attempt_execution_stop (method)”
async TaskStore.record_work_attempt_execution_stop(request: WorkAttemptExecutionStopRequest) -> WorkAttemptAdmission

Record immutable stop intent; exact replay never authorizes dispatch.

load_work_attempt_preparation_hold_receipt (method)

Section titled “load_work_attempt_preparation_hold_receipt (method)”
async TaskStore.load_work_attempt_preparation_hold_receipt(hold_id: str) -> WorkAttemptPreparationHoldReceipt | None

Read an advisory original result; exact replay must compare the request.

async TaskStore.prepare_work_attempt_admission(request: WorkAttemptAdmissionPrepare) -> WorkAttemptAdmission

Reserve one exact initial or continuation attempt before session mutation.

async TaskStore.activate_work_attempt_admission(request: WorkAttemptAdmissionActivate) -> WorkAttemptAdmission

Publish a prepared attempt after exact session admission evidence exists.

async TaskStore.load_work_attempt_admission(admission_id: str) -> WorkAttemptAdmission | None

Load one durable admission intent or receipt by stable identity.

load_work_attempt_execution_claim (method)

Section titled “load_work_attempt_execution_claim (method)”
async TaskStore.load_work_attempt_execution_claim(claim_id: str) -> WorkAttemptExecutionClaim | None

Load one immutable execution-claim generation by stable identity.

load_latest_work_attempt_admission (method)

Section titled “load_latest_work_attempt_admission (method)”
async TaskStore.load_latest_work_attempt_admission(task_id: str) -> WorkAttemptAdmission | None

Read the admission with no successor, including pre-dispatch preparation.

This is discovery, not acquisition of execution authority. The caller must still use exact admission/claim operations before dispatch.

async TaskStore.settle_work_attempt_lifecycle(request: WorkAttemptLifecycleSettlement) -> WorkAttemptLifecycleReceipt

Atomically publish or exactly replay final task and release evidence.

list_unsettled_work_attempt_admissions (method)

Section titled “list_unsettled_work_attempt_admissions (method)”
async TaskStore.list_unsettled_work_attempt_admissions(*, task_filter: TaskAggregateFilter | None=None, limit: int=100, after: str | None=None) -> list[WorkAttemptAdmission]

Discover latest unfinished admissions in admission-ID keyset order.

Includes final decision application awaiting lifecycle retirement, even when the task is already completed. Results never acquire authority.

load_work_attempt_lifecycle_receipt (method)

Section titled “load_work_attempt_lifecycle_receipt (method)”
async TaskStore.load_work_attempt_lifecycle_receipt(admission_id: str) -> WorkAttemptLifecycleReceipt | None

Read the original final outcome, including after acknowledgement loss.

async TaskStore.prepare_local_execution_attempt(authority: LocalExecutionAttemptAuthority) -> LocalExecutionAttemptRecord

Prepare or exactly replay one task-claim-bound local execution attempt.

async TaskStore.start_local_execution_attempt(start: LocalExecutionAttemptStart) -> LocalExecutionAttemptRecord

Publish exact supervisor or root launch authority before further work.

async TaskStore.settle_local_execution_attempt(settlement: LocalExecutionAttemptSettlement) -> LocalExecutionAttemptRecord

Commit or replay one exact terminal/quiescence receipt.

async TaskStore.load_local_execution_attempt(attempt_id: str) -> LocalExecutionAttemptRecord | None

Load one local execution attempt by immutable identity.

list_unsettled_local_execution_attempts (method)

Section titled “list_unsettled_local_execution_attempts (method)”
async TaskStore.list_unsettled_local_execution_attempts(*, limit: int=100, after: LocalExecutionAttemptListCursor | None=None) -> tuple[LocalExecutionAttemptRecord, ...]

List one keyset page that still lacks positive containment settlement.

claim_local_execution_attempt_recovery (method)

Section titled “claim_local_execution_attempt_recovery (method)”
async TaskStore.claim_local_execution_attempt_recovery(claim: LocalExecutionAttemptRecoveryClaim) -> LocalExecutionAttemptRecord

Claim one exact recovery generation without releasing task ownership.

renew_work_attempt_execution_claim (method)

Section titled “renew_work_attempt_execution_claim (method)”
async TaskStore.renew_work_attempt_execution_claim(request: WorkAttemptExecutionClaimRequest) -> WorkAttemptAdmission

Renew an exact live preparing, active, or recovering claim in place.

async TaskStore.claim_work_attempt_recovery(request: WorkAttemptExecutionClaimRequest) -> WorkAttemptAdmission

Claim recovery after the prior execution generation has expired.

async TaskStore.activate_work_attempt_recovery(request: WorkAttemptRecoveryActivate) -> WorkAttemptAdmission

Activate a replacement only after positive session-settlement evidence.

submit_admitted_completion_proposal (method)

Section titled “submit_admitted_completion_proposal (method)”
async TaskStore.submit_admitted_completion_proposal(request: AdmittedCompletionProposalRequest) -> CompletionProposal

Publish one proposal under the exact current execution claim.

async TaskStore.load_work_attempt(attempt_id: str) -> WorkAttempt | None

Load one work attempt by stable identity.

async TaskStore.submit_completion_proposal(request: CompletionProposalCreate) -> CompletionProposal

Persist a worker proposal without granting completion authority.

async TaskStore.load_completion_proposal(proposal_id: str) -> CompletionProposal | None

Load one completion proposal by stable identity.

load_completion_proposal_for_attempt (method)

Section titled “load_completion_proposal_for_attempt (method)”
async TaskStore.load_completion_proposal_for_attempt(attempt_id: str) -> CompletionProposal | None

Load the unique published proposal for an attempt, without guessing its ID.

prepare_completion_verifier_profile (method)

Section titled “prepare_completion_verifier_profile (method)”
async TaskStore.prepare_completion_verifier_profile(request: CompletionVerifierProfilePreparationRequest) -> CompletionVerifierProfileRecord

Insert or exactly replay immutable verifier-profile authority.

Implementations must atomically bind an adoption idempotency key to at most one proposal within its task. A later proposal cannot reuse that task-scoped transition identity.

async TaskStore.load_completion_verifier_profile(proposal_id: str) -> CompletionVerifierProfileRecord | None

Load the immutable verifier profile prepared for one proposal.

load_prior_completion_verifier_profile (method)

Section titled “load_prior_completion_verifier_profile (method)”
async TaskStore.load_prior_completion_verifier_profile(proposal_id: str) -> CompletionVerifierProfileRecord | None

Load the immediately preceding task attempt’s verifier profile.

async TaskStore.claim_completion_verification(request: CompletionVerificationClaimRequest) -> CompletionVerificationClaim

Claim bounded exclusive authority to verify one undecided proposal.

load_completion_verification_claim (method)

Section titled “load_completion_verification_claim (method)”
async TaskStore.load_completion_verification_claim(proposal_id: str) -> CompletionVerificationClaim | None

Load the latest verification claim, including an expired claim for recovery.

renew_completion_verification_claim (method)

Section titled “renew_completion_verification_claim (method)”
async TaskStore.renew_completion_verification_claim(request: CompletionVerificationClaimRequest) -> CompletionVerificationClaim

Extend one exact live verification claim without changing its owner.

async TaskStore.record_completion_decision(request: CompletionDecisionCreate) -> CompletionDecision

Persist the one authoritative verifier decision for a proposal.

async TaskStore.load_completion_decision(decision_id: str) -> CompletionDecision | None

Load one completion decision by stable identity.

load_completion_decision_for_proposal (method)

Section titled “load_completion_decision_for_proposal (method)”
async TaskStore.load_completion_decision_for_proposal(proposal_id: str) -> CompletionDecision | None

Load the one authoritative decision published for a proposal.

async TaskStore.apply_completion_decision(request: CompletionDecisionApplicationRequest) -> Task

Apply or exactly replay a decision-bound task transition.

load_completion_decision_application_receipt (method)

Section titled “load_completion_decision_application_receipt (method)”
async TaskStore.load_completion_decision_application_receipt(task_id: str, idempotency_key: str) -> CompletionDecisionApplicationReceipt | None

Load exact durable evidence for decision application reconciliation.

async TaskStore.create_task(request: TaskCreate) -> Task

Create a task and its immutable invocation provenance atomically.

Implementations must mint the final task ID, load any requested parent, and call task_invocation_for_create inside the same create boundary.

async TaskStore.reschedule_task(request: TaskRescheduleRequest) -> TaskScheduleReceipt

Atomically replace an exact unadmitted schedule, or replay its receipt.

async TaskStore.cancel_scheduled_task(request: TaskScheduleCancelRequest) -> TaskScheduleReceipt

Cancel an exact schedule without releasing live execution ownership.

async TaskStore.list_task_schedule_events(task_id: str, *, after_sequence: int=0, limit: int=100) -> list[TaskScheduleEvent]

Read bounded task-owned scheduling evidence in durable sequence order.

async TaskStore.next_task_schedule_wakeup(query: TaskQuery | None=None) -> TaskScheduleWakeup

Observe the next matching deadline; the result grants no claim authority.

async TaskStore.create_running_task(request: TaskCreate, *, session_invocation: SessionInvocationBinding) -> Task

Atomically create a running task already attached to its session.

request.session_id is required. This avoids leaving an attached, unclaimable pending task if a process stops between separate create and start operations. session_invocation must describe that exact session so the atomic insert cannot create a contradictory structural attachment and invocation record.

async TaskStore.load_task(task_id: str, *, _access_bounds=None) -> Task | None

Load a task by id.

async TaskStore.load_active_attached_task_worker(task_id: str, worker_id: str, *, session_id: str, session_instance_id: str) -> Task

Load exact active attached-worker authority using store time.

Stores that support worker-owned session resume must override this projection. A stale or mismatched owner raises TaskClaimLost before the session can admit provider or tool work.

async TaskStore.load_direct_attached_task_resume(task_id: str, *, session_id: str, session_instance_id: str) -> Task

Load a direct workerless attachment with no recovery authority.

A committed interrupted-handoff generation is reserved for an elected recovery owner and must reject an ordinary ownerless resume.

async TaskStore.load_invocation_snapshot(task_id: str) -> TaskInvocationSnapshot | None

Load bounded immutable provenance without task payloads or metadata.

async TaskStore.list_tasks(query: TaskQuery | None=None, *, _access_bounds=None) -> list[Task]

List tasks for dashboards, queues, and orchestration.

async TaskStore.load_session_closure_claim(session_id: str) -> TaskSessionClosureClaim | None

Read the retained task-set authority without reopening admission.

async TaskStore.claim_session_closure(claim: TaskSessionClosureClaim) -> TaskSessionClosureClaim

Reserve an exact, complete quiescent task set before independent deletion.

Retain the claim after deletion so future creation/attachment cannot revive the retired session’s task namespace. Exact retries are read-only.

async TaskStore.delete_session_tasks(session_id: str, *, task_ids: tuple[str, ...], policy: Any) -> None

Delete a quiescent session task set owned by this store.

async TaskStore.aggregate_operational_snapshot(filters: TaskAggregateFilter | None=None) -> TaskOperationalSnapshot

Count current task states in one store-local read snapshot.

Default raises NotImplementedError so existing out-of-tree stores remain instantiable when they do not expose this control-plane read model.

async TaskStore.query_task_topology(query: TaskTopologyQuery) -> TaskTopologyStoreResult

Read bounded task/session and direct-child branches.

Default raises NotImplementedError so existing out-of-tree stores remain instantiable while advertising the operation as unsupported.

async TaskStore.start_task(task_id: str, *, session_id: str | None=None, session_invocation: SessionInvocationBinding | None=None) -> Task

Mark a pending task as running, optionally attached to a session.

A session provenance binding is required for attachment. Stores must validate it before changing lifecycle state.

async TaskStore.attach_task(task_id: str, *, session_id: str, session_invocation: SessionInvocationBinding, worker_id: str, lease_expires_at: datetime | None=None) -> Task

Attach a live worker-claimed task to a session and mark it running.

Raise TaskClaimLost unless the worker and exact lease generation still own a live claim.

async TaskStore.complete_task(task_id: str, result: dict[str, Any], *, worker_id: str | None=None, lease_expires_at: datetime | None=None, handoff_id: str | None=None) -> Task

Mark a pending or running task as completed.

If worker_id is given, lease_expires_at must identify the exact active lease generation. The update also requires the exact continuation generation, so a stale worker cannot clobber a task after renewal or reclaim.

async TaskStore.fail_task(task_id: str, error: dict[str, Any], *, worker_id: str | None=None, lease_expires_at: datetime | None=None, handoff_id: str | None=None) -> Task

Mark a pending or running task as failed.

If worker_id is given, lease_expires_at must identify the exact active lease generation. The update also requires the exact continuation generation.

async TaskStore.terminalize_task(request: TaskTerminalizationRequest) -> Task

Atomically terminalize one live claim or replay its exact receipt.

Custom stores opt into this operation by overriding it. The default keeps existing out-of-tree TaskStore implementations instantiable without claiming acknowledgement-loss safety they do not provide.

load_task_terminalization_receipt (method)

Section titled “load_task_terminalization_receipt (method)”
async TaskStore.load_task_terminalization_receipt(task_id: str, idempotency_key: str) -> TaskTerminalizationReceipt | None

Load exact durable commit evidence for receipt reconciliation.

async TaskStore.recover_attached_task_failure(request: TaskTerminalizationRequest, *, session_id: str, session_instance_id: str) -> Task

Fail one exact expired task owner after its attached session is fenced.

Custom stores opt into this operation explicitly. Implementations must atomically validate the task’s session incarnation, worker, lease, and handoff generation before bypassing the ordinary active-lease rule.

async TaskStore.release_interrupted_task_worker(request: TaskInterruptedHandoffRequest) -> TaskInterruptedHandoffReceipt

Release a live exact owner and atomically publish replay evidence.

async TaskStore.recover_interrupted_task_worker(request: TaskInterruptedHandoffRequest) -> TaskInterruptedHandoffReceipt

Release the exact expired owner from reconstructed interruption authority.

load_interrupted_task_handoff_receipt (method)

Section titled “load_interrupted_task_handoff_receipt (method)”
async TaskStore.load_interrupted_task_handoff_receipt(task_id: str, handoff_id: str) -> TaskInterruptedHandoffReceipt | None

Load immutable commit evidence for one exact handoff operation.

list_expired_interrupted_task_handoff_candidates (method)

Section titled “list_expired_interrupted_task_handoff_candidates (method)”
async TaskStore.list_expired_interrupted_task_handoff_candidates(*, after: tuple[datetime, str] | None=None, limit: int=100) -> list[Task]

List one stable page of expired owners using store-authoritative time.

load_expired_interrupted_task_handoff_candidate (method)

Section titled “load_expired_interrupted_task_handoff_candidate (method)”
async TaskStore.load_expired_interrupted_task_handoff_candidate(task_id: str) -> Task | None

Load one exact expired attached owner using store-authoritative time.

claim_interrupted_task_continuation (method)

Section titled “claim_interrupted_task_continuation (method)”
async TaskStore.claim_interrupted_task_continuation(worker_id: str, query: TaskQuery | None=None, *, handoff_id: str, task_id: str | None=None, lease_seconds: int=300, after: tuple[datetime, str] | None=None, scan_limit: int=_TASK_INTERRUPTED_HANDOFF_RECOVERY_MAX_PAGE_SIZE) -> InterruptedTaskContinuationClaimPage

Scan one bounded page and lease its first authentic continuation.

handoff_id is a caller-generated, one-use claim generation. task_id is an optional exact selector available only from stores that advertise supports_exact_interrupted_task_handoffs. Supporting stores must first replay an exact still-live claim with the same worker and generation, making commit-before-ack loss recoverable without transferring authority. Otherwise they select only running attached tasks whose current one-use handoff generation names a fully validated committed receipt and that have no live worker lease. Generation consumption and lease publication are one atomic mutation so competing recovery owners cannot invoke the same continuation concurrently. after and scan_limit bound a stable creation-order scan. The result advances past rejected authority without allowing one malformed candidate to block later independent work.

async TaskStore.reconcile_task_cancellation(request: TaskCancellationReconciliationRequest) -> TaskCancellationReconciliationResult

Settle an owner-lost ordinary cancellation from positive evidence.

Lease expiry establishes only that the original owner is lost. The application must supply positive quiescence or external-effect evidence. Supporting stores commit that evidence with the ordinary cancellation terminalization receipt in one atomic transition.

async TaskStore.settle_task_retry_attempt(request: TaskRetrySettlementRequest) -> TaskRetrySettlementResult

Atomically record one attempt and optionally create its delayed successor.

async TaskStore.load_task_retry_settlement(task_id: str, idempotency_key: str) -> TaskRetrySettlementResult | None

Load an exact retry-attempt settlement receipt for acknowledgement recovery.

reconcile_task_retry_cancellation (method)

Section titled “reconcile_task_retry_cancellation (method)”
async TaskStore.reconcile_task_retry_cancellation(request: TaskRetryCancellationReconciliationRequest) -> TaskRetrySettlementResult

Settle an exact owner-lost cancellation from positive application evidence.

Lease expiry establishes only that the original owner is lost. The request’s typed evidence remains the application’s positive quiescence/effect proof. Custom retry-capable stores must implement the transition atomically with the ordinary retry settlement receipt.

async TaskStore.enforce_task_retry_deadline(task_id: str, worker_id: str, *, lease_expires_at: datetime, token_count: int=0, estimated_cost: Decimal=Decimal(0)) -> TaskRetrySettlementResult | None

Atomically terminalize an owned attempt only when store time exhausted it.

Returning None is positive store evidence that the cumulative deadline had not elapsed at this check. Retry-capable custom stores must override this operation so workers never substitute their own wall clock.

async TaskStore.task_retry_deadline_elapsed(task_id: str, worker_id: str, *, lease_expires_at: datetime) -> bool

Return store-authoritative elapsed evidence without releasing ownership.

async TaskStore.cancel_task(task_id: str, error: dict[str, Any] | None=None) -> Task

Cancel idle work or durably request cancellation from its live owner.

Tasks with a live worker remain fenced until that worker proves its dispatched work quiescent and commits the cancellation receipt.

request_claimed_task_cancellation (method)

Section titled “request_claimed_task_cancellation (method)”
async TaskStore.request_claimed_task_cancellation(task_id: str, worker_id: str, lease_expires_at: datetime, error: dict[str, Any] | None=None) -> Task

Request cancellation only for one exact live worker lease.

A delayed stale owner must not place a cancellation marker on a claim acquired by a replacement worker.

mark_claimed_task_execution_started (method)

Section titled “mark_claimed_task_execution_started (method)”
async TaskStore.mark_claimed_task_execution_started(task_id: str, worker_id: str, lease_expires_at: datetime) -> Task

Fence an exact claim before its worker can dispatch opaque work.

Supporting stores persist started_at while the exact worker lease is still live. Exact replay is idempotent. Once this marker exists, expiry reclamation must enter cancellation reconciliation instead of making the task claimable while the original effect may still be running.

async TaskStore.pause_task(task_id: str, *, reason: str | None=None, payload: dict[str, Any] | None=None) -> Task

Pause a pending or unattached running task until app code resumes it.

async TaskStore.block_task(task_id: str, *, reason: str | None=None, payload: dict[str, Any] | None=None) -> Task

Mark a pending or unattached running task as blocked on an external dependency.

async TaskStore.mark_task_needs_attention(task_id: str, *, reason: str | None=None, payload: dict[str, Any] | None=None) -> Task

Mark a pending or unattached running task as waiting for human/operator input.

async TaskStore.resume_task(task_id: str) -> Task

Return a paused, blocked, or attention-needed task to the pending queue.

async TaskStore.claim_task(worker_id: str, query: TaskQuery | None=None, *, lease_seconds: int=300) -> Task | None

Atomically claim the next pending task matching query.

async TaskStore.heartbeat(task_id: str, worker_id: str, *, lease_expires_at: datetime, handoff_id: str | None=None, extend_seconds: int=300) -> Task

Extend the exact worker-owned lease identified by its expiry.

Raise TaskClaimLost if the worker and lease expiry no longer identify the live claim.

async TaskStore.release_task(task_id: str, worker_id: str, *, lease_expires_at: datetime) -> Task

Release the exact claimed task lease back to pending.

Raise TaskClaimLost if the worker and lease expiry no longer identify the live claim.

async TaskStore.release_attached_task_worker(task_id: str, worker_id: str, *, lease_expires_at: datetime) -> Task

Release an exact lease while preserving a running task’s session link.

Raise TaskClaimLost if the worker and lease expiry no longer identify the live claim.

async TaskStore.reclaim_expired(*, query: TaskQuery | None=None, max_reclaims: int=100) -> list[Task]

Return expired claimed task leases to pending.

Release source · Runtime guarantees · Capability guides