Compatibility: Pactmark 0.1.x.

States

Figure 1. The happy path is a straight line. Everything interesting happens when the run has to wait for something that is not the model.

The full transition table

An invalid transition raises KAF_RUNTIME_INVALID_TRANSITION. A transition attempted after the run is terminal raises KAF_RUNTIME_TERMINAL. Neither is retryable.
Notice that verifying can go back to waiting_for_approval. Verification is not a rubber stamp at the end — a failing verifier can send the run back for a human decision.

Events

Validated commands append versioned RunEvent records. Each has a stable eventType, a sequence number and a strict payload.
RunAccepted · PlanningStarted · ExecutionStarted · RunSuspended · RunCompleted · RunFailed · RunCancelled
ModelCallStarted · ModelCallCompleted — carrying reservation ids and request/response digests, never prompt or completion content.
ToolCallRequested · ToolCallCompleted — carrying the tool registration digest and digests of the arguments and result.
RetryScheduled · RetryResumed — with an explicit classification of timed_out, retryable or uncertain, plus a notBefore timestamp.
InputRequested · InputSubmitted · ApprovalRequested · ApprovalRecorded · ApprovalRejected — carrying decision ids and digests, never raw proofs.
EffectPrepared · EffectDispatched · EffectAcknowledged · EffectUncertain · EffectNeedsReconciliation · EffectReconciliationRecorded · EffectAbandoned · CompensationRequested · EffectCompensated
ArtifactProduced · VerificationStarted · VerificationRecorded · VerificationExceptionRecorded
Two payload details worth internalising:
  • EffectNeedsReconciliation and EffectAbandoned both carry effectMayHaveOccurred: true as a literal. The schema will not let you record those states while implying the effect definitely did not happen.
  • RunFailed.errorCode is constrained to /^KAF_[A-Z0-9_]+$/. Failure reasons are machine-readable by construction.

Projections

Figure 2. RunProjection carries status, lastSequence, currentStepId, resumeTarget, waiting ids, artifact and verification ids, and terminalErrorCode. All of it is derived. Because the projection is derived, you can drop it during an incident, rebuild it, and compare — a mismatch tells you something is wrong with your fold, not with the run.

Durability and safe resume

Figure 3. Database-time leases with fencing tokens. A worker that comes back from the dead cannot commit over a newer owner, because its token is stale by definition. Durable Postgres commands bind idempotency records, authority, event mutation and wake-up state into one atomic unit. Protected context is tenant-scoped and purpose-bound, so resume does not become a back door into another tenant’s data.
Recovery starts from persisted truth in a fresh process. If your architecture assumes the original request is still open — background work inferred from a held response, in-memory continuation state, a long function timeout — you do not have durability, you have a long request.

At-least-once, and honest about it

Pactmark uses at-least-once execution with controlled effects. It does not claim global exactly-once delivery. An uncertain effect stays parked until its registered strategy — native, transactional, reconcilable, or none — permits action, or until a separately authorised compensation run is started.

Tools and effects

What each effect strategy actually claims, and what it refuses to claim.

Reliability and recovery

Leases, RPO/RTO, monitoring parked work, and what local crash tests do not prove.