How the lifecycle works¶
The lifecycle is deliberately more precise than “approved” and “done.” Each state answers a different operational question.
| State | Meaning |
|---|---|
awaiting_authority |
A proposal exists, but sufficient bound authority has not been recorded. |
authorized |
Current recorded evidence satisfies the host authority policy. Execution has not started. |
blocked |
A live safety check failed, such as permission being revoked or authority expiring. |
stale |
Material application state differs from the approved snapshot. This proposal is terminal and a fresh proposal is required. |
superseded |
A stale proposal was replaced by a fresh proposal. The old proposal remains terminal. |
executing |
This proposal owns local admission for the semantic effect. The external outcome may still be unknown. |
verification_pending |
The effect may exist, but authoritative completion is not proven yet. |
verified |
The host verifier observed terminal completion at the authoritative target. |
failed_known |
The executor or verifier reported a definite failure. |
failed_unknown |
The call failed without proving whether the effect occurred. Verify; do not blindly resend. |
verification_unresolved |
The bounded verification attempts were exhausted. Human or scheduled follow-up is required. |
partially_succeeded |
An explicitly itemized action produced different item outcomes. |
expired |
The proposal lifetime ended before execution admission. |
denied |
A confirming authority rejected the proposal. |
Normal path¶
async def main() -> None:
demo = build_demo()
prepared = await demo.runtime.prepare(
demo.action,
tenant_reference=TENANT,
command=RefundCommand(order_reference="ORD-42"),
requesting_principal=REQUESTER,
proposing_agent=AGENT,
)
print(prepared.outcome) # prepared
print(prepared.display_preview) # safe to show in a confirmation UI
await demo.approve(prepared.proposal_reference)
accepted = await demo.runtime.execute(
demo.action,
tenant_reference=TENANT,
proposal_reference=prepared.proposal_reference,
)
print(accepted.outcome) # verification_pending: the PSP accepted the request
if accepted.needs_reconciliation:
verified = await demo.runtime.reconcile(
demo.action,
tenant_reference=TENANT,
proposal_reference=prepared.proposal_reference,
)
print(verified.outcome) # verified: the PSP confirms the refund exists
print(verified.safe_result)
view = await demo.runtime.read(
demo.action,
proposal_reference=prepared.proposal_reference,
context=ReadContext(tenant_reference=TENANT, consumer=CONSUMER),
)
print([receipt.receipt_type for receipt in view.receipts])
execute() returning the verification_pending operation outcome is normal.
Keep the proposal reference and schedule reconcile() at or after the
persisted next_verification_at. The runtime bounds concurrent leases and
total attempts.
An executing lifecycle status means an executor still owns an active recovery
lease. An early reconcile() returns the in_progress operation outcome
without changing the record. Crash recovery may move executing to
verification_pending only after the persisted due time, which is set from
verification_lease_duration when execution is admitted.
Lifecycle status versus operation outcome¶
lifecycle_status is the durable state of the proposal. outcome describes
what a particular runtime call observed or did. They are related but are not
the same vocabulary. For example, an early reconciliation call can return the
in_progress outcome while the proposal remains in the executing lifecycle
status.
The lifecycle groups are:
- Positive terminal:
verified. - Negative terminal:
denied,expired,blocked,stale,superseded,failed_known, orverification_unresolved. - Mixed terminal:
partially_succeeded; inspect the item outcomes before compensating or retrying anything. - Needs authoritative reconciliation:
executing,failed_unknown, orverification_pending, subject to the persisted due time and lease. - Still actionable but not reconcilable:
awaiting_authorityorauthorized.
If an operation returns the stale outcome, follow
fresh_proposal_reference when it is present. Otherwise call prepare() again
from current application state. Never execute the stale proposal again.
Outcomes such as authority_pending, in_progress, and conflict describe a
call result; inspect the accompanying lifecycle_status before scheduling the
next operation.
Do not translate accepted, an HTTP 2xx, a queue acknowledgement, or model
text into verified. Only the configured verifier can do that.
Use result.is_terminal instead of copying terminal-state sets into an
application. Use result.needs_reconciliation to decide whether the runtime can
advance the proposal through authoritative reconciliation. Both properties are
derived from the lifecycle state, including replay and conflict results.