Reservation Lifecycle Side Effect Execution
Reservation Lifecycle Side Effect Execution
Section titled “Reservation Lifecycle Side Effect Execution”ReservationLifecycleSideEffectExecution is the orchestration boundary for
running side effects derived from an accepted reservation lifecycle transition.
It consumes ReservationLifecycleSideEffectPlan work items, enriches them with
execution metadata, persists them first, then dispatches idempotent work through
the neutral workflow engine port. Restate is the current workflow engine
adapter. Supabase remains the business ledger, read model, and repair source of
record.
Provider names, source-era table names, and source status labels must stay in
sourceEvidenceRef, evidence payloads, trace labels, or migration docs. They
must not name target components, target features, command kinds, workflow
kinds, or generated client fields.
Current Plan Boundary
Section titled “Current Plan Boundary”ReservationLifecycleSideEffectPlan belongs to reservation_operations and
persists intent only. Its current work item fields are:
kindtarget_contextdispatch_modeidempotency_keyrequired_revisionreason_code
The plan answers which durable work is required after a lifecycle transition. It does not call payment, notification, settlement, benefit, audit, or workflow providers. Execution must keep that split.
Execution Record
Section titled “Execution Record”Each plan work item becomes one immutable execution record before dispatch. Required execution fields:
executionId: stable id for one plan item execution.reservationLifecycleSideEffectPlanId: accepted plan snapshot id.workItemKind: copied from the plan itemkind.targetContext: copied from the plan itemtarget_context.targetFeature: provider-neutral owning feature, such asreservation_operations.reservation_audit_entry,customer_engagement.communication_dispatch_request,pricing_settlement.payment_refund_closeout,pricing_settlement.payment_refund_completion_barrier,pricing_settlement.reservation_promotion_side_effect,pricing_settlement.branch_settlement_opening, orpricing_settlement.branch_settlement_reversal, orpricing_settlement.financial_lifecycle_closeout.commandKind: provider-neutral command to run inside the owning feature, such asappend_lifecycle_audit,enqueue_lifecycle_notification,request_payment_refund_closeout,record_refund_closeout_completion,plan_reservation_promotion_side_effect,open_branch_settlement,request_branch_settlement_reversal, orclose_financial_lifecycle.sourceEvidenceRef: source-only reference list proving why the work item exists.idempotencyKey: copied from the plan item and reused for target command replay checks.requiredRevision: copied from the plan item and checked before execution.commandPayload: frozen provider-neutral command input. Retry and repair re-use this payload; they do not rebuild payment, settlement, benefit, or notification input from current mutable rows.dependsOnWorkItemKinds: explicit ordering dependency between side effects. Refund completion barriers now gate benefit release and branch settlement reversal.providerEventOrderingStatus: event-ordering checkpoint for provider-backed commands, such asawaiting_provider_command,awaiting_provider_event,provider_event_matched,provider_event_out_of_order,provider_event_conflict, orprovider_event_timeout.providerEventRefs: provider event ledger refs matched to this execution.targetResultRefs: target feature result refs emitted after command handling.preconditions: persisted checks required before dispatch.executionStatus: public execution state.dispatchMode:ledger,outbox, orworkflow.workflowRequestId: workflow request id when Restate dispatch is used.attemptCount,maxAttempts,lastAttemptAt,nextRetryAt.deadLetterReason: terminal failure reason when automatic retry stops.lastErrorCode,lastErrorMessage: latest retry or repair diagnostic.repairCommandKind: backend repair command available to operators.compensation: explicit compensation policy for partial or successful side effects that later need rollback or correction.
Execution rows must be service-role-only Supabase rows. Portal users read filtered backend projections, not execution tables.
refund_paid_payments expands into two execution records: the refund closeout
workflow and a refund completion barrier. The closeout starts in
awaiting_provider_command; the barrier depends on the closeout and starts in
awaiting_provider_event. Benefit release and branch settlement reversal depend
on the barrier. The barrier requires a terminal provider refund event or a
zero-refund fact before benefit release, coupon restore, settlement reversal, or
customer-facing receipt state can be marked complete. This prevents source-era
races where KCP/Kill Bill refund outcomes, coupon restoration, and settlement
rows were mutated from separate controller branches.
Execution Status
Section titled “Execution Status”Use one status vocabulary across ledger, outbox, and workflow-backed side effects:
| Status | Meaning |
|---|---|
accepted | Execution row persisted from a plan item, not dispatched yet. |
blocked_precondition | Required reservation, policy, actor, or target fact is missing. |
dispatch_pending | Preconditions passed and dispatch is waiting. |
running | Worker or workflow invocation accepted the execution snapshot. |
succeeded | Target feature command completed idempotently. |
retry_pending | Retryable failure recorded with next attempt time. |
dead_lettered | Automatic retry limit reached or payload is not executable. |
repair_required | Operator or target-feature repair command must resolve it. |
compensation_pending | Successful or partial side effect needs compensating command. |
compensated | Compensation command completed and original evidence remains linked. |
cancelled | Backend repair command intentionally stopped future dispatch. |
executionStatus is business-visible progress. Internal Restate invocation
status, queue visibility timeout, provider result code, and source status id
are evidence only. The backend can map those internals into this status, but UI
and contracts must not expose them as control flow.
Preconditions
Section titled “Preconditions”Persist preconditions as named checks with pass/fail status, observed revision, and evidence. Required checks:
- Reservation transition was accepted and plan snapshot exists.
- Current reservation revision still satisfies
requiredRevision. - Actor capability snapshot still allows the originating operation.
- Target feature has required facts, such as refund ledger, benefit reservation, settlement participant, communication route, or financial lifecycle request.
idempotencyKeyhas no conflicting successful execution.- Target command can replay same snapshot without recomputing from current rows.
Failed preconditions set blocked_precondition; they do not silently skip work.
Manual repair can refresh facts, suppress work with reason, or requeue the same
execution snapshot.
Retry, Dead Letter, Repair
Section titled “Retry, Dead Letter, Repair”Retry uses the persisted execution snapshot. It must not rebuild command input from current reservation, payment, settlement, benefit, or communication rows.
- Retryable failures set
retry_pending, incrementattemptCount, and storelastErrorCode,lastErrorMessage, andnextRetryAt. - Non-retryable failures or exhausted attempts set
dead_letteredwithdeadLetterReason. - Repair commands must be backend commands with actor, reason, expected execution revision, and idempotency key.
- Repair actions may
retry,suppress,mark_external_result,require_compensation, orcancel. - Every repair writes an append-only audit event and keeps the original
sourceEvidenceRef.
Dead-letter handling belongs to the execution ledger, not to browser state or provider callbacks.
Compensation
Section titled “Compensation”Each target feature declares its own compensation policy before execution:
- Audit entries default to
none; corrections append new audit facts. - Notifications use
suppressor corrected follow-up dispatch, not deletion. - Payment/refund closeout uses target-feature reversal or manual ledger repair.
- Benefit release uses target-feature restore when policy allows it.
- Settlement and financial lifecycle work uses repair or compensating lifecycle command, never direct table mutation.
Compensation commands must link to original executionId, target command
result, actor, reason, and evidence refs. Compensation never erases completed
attempt rows.
Supabase-First Persistence
Section titled “Supabase-First Persistence”Persistence order:
- Persist accepted reservation operation plan and lifecycle side-effect plan.
- Insert one
ReservationLifecycleSideEffectExecutionrow per work item. - Commit execution rows before any workflow, queue, provider, or target-feature dispatch.
- Dispatch through Restate or a queue using the persisted
executionIdandidempotencyKey. - Handler re-reads the Supabase execution snapshot and applies the owning feature command.
- Handler writes attempt, result, retry, dead-letter, repair, or compensation events back to Supabase.
- Backend projections expose public status through
WorkflowStatusProjectionor a reservation-operation read model.
Restate owns replay and handler progress. Supabase owns durable facts, operator repair, read-model filtering, and RLS boundaries.
Restate Dispatch
Section titled “Restate Dispatch”Workflow dispatch must use the neutral WorkflowRequest,
WorkflowInvocation, and WorkflowInvocationPort contract. Target feature
commands receive a provider-neutral snapshot:
executionIdreservationLifecycleSideEffectPlanIdreservationIdtargetFeaturecommandKindcommandPayloaddependsOnWorkItemKindsidempotencyKeyrequiredRevisionsourceEvidenceRefproviderEventOrderingStatusproviderEventRefstargetResultRefs- precondition results
The Restate handler may call target feature application ports, but must not call source adapters or provider-specific clients directly unless the owning feature has already selected that provider behind a port. Restate workflow names and invocation ids are operational evidence only.
Source Evidence
Section titled “Source Evidence”Primary evidence anchors:
source-refs/sanmopia-admin/application/models/Reservation_model.php:1769-1916source-refs/sanmopia-admin/application/models/Reservation_model.php:1980-2140source-refs/sanmopia_web/application/controllers/api/Reservation.php:115-239source-refs/sanmopia_web/application/models/Payment_model.php:101-441
Provider/source names found in those files may appear in evidence refs and evidence payloads only. Target vocabulary stays bounded-context and provider-neutral.