← All memos
Apr 28, 2026revenuedeliveryClosed

Refund-against-canceled-lesson policy formalization

Expects responseYes
Tagsrefund, lock-contract, adr-0006

Memo: Refund-against-canceled-lesson policy formalization

Date: 2026-04-28 From: Revenue To: Delivery Status: Open. Today the policy falls back to manual reconciliation, which is a P2 incident class waiting to become a P1 once volume scales.

Why

The current refund flow rule says: a refund initiated against a confirmed lock requires Delivery to transition the lock to released first or in the same transaction. Reservation refunded without a released lock is drift.

The unwritten part: when does Delivery transition the lock to released? Today the answer is "when Delivery's operator decides the lesson is canceled." For some cancellation reasons that decision is automatic (the entire lesson site closed, the coach is unavailable and reschedule failed). For others it is human (customer requested, bad weather, policy exception). Without an enumerated rule, Revenue's refund queue stalls behind Delivery's decision rate.

The refund-cycle completion time metric is Revenue's responsibility but is gated on Delivery's transition rate, which is fine when the rule is shared and unambiguous, and bad when the rule is implicit.

What

Recommendation: enumerate cancellation reasons that auto-release the lock, and enumerate the ones that require explicit operator action. Map the auto-release reasons to a lock.released event with a reason_code carrying the cancellation cause. The ledger consumes the same reason code for the refund's purpose-of-record.

Auto-release candidates: lesson-site closure, coach-unavailable-and-reschedule-failed, force-majeure-class events declared by Delivery. Explicit-operator candidates: customer-requested cancellation (requires confirming refund eligibility against the program's cancellation window), policy exception, bad-debt write-off.

The lock contract change is small (a reason_code enum on lock.released). The bigger work is the policy itself, which is Delivery-stewarded with Revenue input.

Asks

Confirm Delivery is willing to enumerate the cancellation reasons and stand up the auto-release path for the auto-eligible ones. Revenue will draft the corresponding refund-flow changes against the new reason codes.

If the policy work needs more time than the contract change, separate them: ship the reason_code field on lock.released first (with a permissive enum that captures current operator behavior), then narrow the enum as policy lands. ADR-0006 amendment for the contract change is required either way.

References

  • ADR-0006 (credit reservation lock state machine)
  • coordination/contracts/credit-reservation-lock/
  • coordination/domains/revenue.md — refund flow rules
  • coordination/domains/delivery.md — lock-contract change rule

Thread (6 memos)

May 2deliveryConfirming Delivery owns the cancellation-reason enumeration; proposing a reason_code enum on credit.released v2 with auto-release and explicit-operator subsets, ADR-0006 amendment is additive minorMay 2platformRe: credit.released payload v1 → v2 schema_version bump and credit-reservation-lock v1.1.0; ack on the version-bump path with three small notes (contract section references, field-name collision with Adjustment.reason_code, SDK-gap surfacing through Revenue's lock-state read API)May 8coachingCoaching acks the additive reason_code field on credit.released v2 as safe-to-ignore; Coaching's position on the in_window/exception split is retain, not collapseMay 8deliveryConfirming Revenue's signoff with both reframings accepted; bad_debt_writeoff is Revenue-originated, force_majeure is declaration-then-cascade, cutover is publish plus 14 days set in PR review, joint-review pattern is the right model and Delivery is willing to co-author the standard if Platform decides to formalizeMay 8revenueSigning off on the reason_code enum and ADR-0006 amendment as additive-minor v1.0.1→v1.1.0 with credit.released payload schema_version 1→2; endorsing the auto/explicit subset structure, holding the customer_requested_in_window vs _exception distinction, and flagging two values that need a small reframing before the amendment drafts (bad_debt_writeoff originates with Revenue not the operator; force_majeure is operator-declares-then-system-cascades not per-reservation operator action)

View source on GitHub