##### Context The "Add Scheduled Gift" flow has been a hardcoded, unwired UI since the beginning (two example cards on the Remembrance Plan tab). ADR-008 stood up the product catalog and explicitly deferred two things: the Ordering/Fulfillment execution context (reserved for ADR-009) and the Remembrance Plan integration that lets a customer attach a real catalog product to a recipient and a schedule. This ADR defines that integration — the **Scheduled Gift** plan-domain aggregate and its financial commitment record. The design is driven by the v7 design handoff (`reference/rempla_flow_scheduled_gift_v7.html`), an 8-screen wizard: 1. **Who is this gift for?** — recipient from the planning tree (or add someone new). 2. **Choose a Gift Category** — Flowers, Gift Card, Curated Gift, or Card. 3. **Choose the arrangement** — a per-category selection; the price shown is the **per-delivery budget**. 4. **When should the first gift arrive?** — start now (while living) or after passing. 5. **When should these gifts be delivered?** — recurrence: cadence (year/quarter/month/week) keyed off a recurring moment (birthday, anniversary, graduation, wedding, a specific date, a milestone, or after passing), over a chosen duration; one-time is also supported. Drives the pricing breakdown and payment options. 6. **Beneficiary notification & plan protection** — when the recipient learns they are a beneficiary, plus plan protectors. 7. **A note to accompany every delivery** — optional personal message / card. 8. **Confirm the plan** — review, delivery address, refund recipient, acknowledgement, and **hold the funds** in the Rempla Gift Reserve. Two forces shape the model. First, the flow is deliberately **very close to the Vault flow** (`CustomerVaultBox` / `VaultTransaction`) in structure, step ordering, trigger semantics, billing modes, and wizard-resume behavior — we mirror it rather than invent a parallel vocabulary. Second, ADR-008's boundary holds: **schedule, recurrence, recipient binding, and personalization live here in the plan domain, never on the catalog.** The catalog is referenced by `ProductVariantId` only, with a frozen promised price and display snapshots. ##### Decision Introduce a **Scheduled Gift** bounded slice in the Remembrance Plan domain, mirroring the Vault aggregate pattern: a resumable customer-facing aggregate plus a separate immutable financial commitment record. **Two aggregates (parallel to Vault)** | Vault | Scheduled Gift | Role | |-------|----------------|------| | `CustomerVaultBox` | `CustomerScheduledGift` | Resumable wizard aggregate; the customer's in-progress / active plan | | `VaultTransaction` | `ScheduledGiftTransaction` | Immutable commitment + pricing snapshot + payment record | **`CustomerScheduledGift`** — fields grouped to match the wizard, mirroring `CustomerVaultBox` where semantics align: - *Identity / ownership:* `Id`, `AccountId`, `RecipientPersonId?`. - *Catalog binding (ADR-008, late-bound):* `ProductCategoryId?` (step 2), `ProductVariantId?` (step 3), `PromisedUnitPrice` (the per-delivery budget, **frozen** at commit), and display snapshots `ProductNameSnapshot` / `ProductDescriptionSnapshot` / `ProductImageUrlSnapshot`. References variant, never `VendorOffer` — vendor is resolved at fulfillment (ADR-009). - *Category-specific configuration* (the gift personalization that is **not** catalog data): - `GiftCardBrand?` and `LetRecipientChooseBrand` (Gift Card branch). - `RecipientInterests` — multi-valued, via child `ScheduledGiftInterest` rows (Curated branch). - `CardPhotoUrl?` (Card branch, Photo Card option). - `FulfillmentMode` (`ShipToAddress` | `RecipientClaimLink`) — curated/gift-card branches can defer the address to a claim link. - *Start trigger (step 4):* `StartTriggerType` (`Now` | `AfterPassing`) — the gift-flow analogue of `DeliveryTriggerTypes`, governing when the first delivery is eligible. - *Recurrence (step 5 — the net-new concept):* - `ScheduleType` (`OneTime` | `Recurring`). - `RecurrenceCadence?` (`Annually` | `Quarterly` | `Monthly` | `Weekly`) — the doc's year/quarter/month/week. - `DurationYears?` — the horizon over which a recurring plan runs; the basis for a computable, fully-funded **Total Guaranteed Price**. - **Anchor**, reusing the Vault trigger vocabulary exactly: `AnchorType` (`ProfileDate` | `SpecificDate` | `Milestone` | `AfterPassing`), `AnchorPersonDateId?` (FK to `PersonDate` — birthday / anniversary / graduation / wedding from the recipient's profile), `AnchorDate?` (`DateOnly`), `AnchorMilestone?` (free text). - *Notification (step 6):* `BeneficiaryNotificationType?` — **reuses the existing `BeneficiaryNotificationTypes` enum** (`Now` | `UponPassing` | `Later`). Plan protectors are a plan-level concern already modeled via `RemembrancePlanRole` (PlanProtector) and are **not** duplicated on the gift. - *Message (step 7):* `PersonalMessage?`. - *Billing & Reserve (step 8):* `BillingMode?` (`PayInFull` | `AnnualInstallments` | `MonthlyInstallments`) — parallel to `StorageBillingModes`. Delivery address (`DeliveryAddressId?` to file, or inline snapshot fields) and refund recipient (`RefundRecipientType` (`Account` | `Person` | `Charity`) with the small set of person/charity fields the doc collects). - *Wizard resume:* `WizardLastStep`, `WizardFurthestStep` (identical pattern to Vault). - *Lifecycle:* `Status` (`CustomerScheduledGiftStatuses`: `Draft` | `Active` | `Cancelled` — mirrors `CustomerVaultBoxStatuses`), `DateCreated`, `DateUpdated`. **`ScheduledGiftTransaction`** — mirrors `VaultTransaction`: a snapshot of the gift configuration at commit, the full pricing breakdown, payment fields, and a unique transaction number. - Snapshot of recipient / category / variant / schedule / message. - Pricing breakdown (mirrors the doc's step-5 panel): `GiftValueTotal` (per-delivery × deliveries), `InflationProtectionCharge`, `ServiceCharge`, `AnnualMaintenanceRate` (per year while living; pay-as-you-go, ceases at activation), `Subtotal`, `DiscountTotal`, `TaxTotal`, `Total` (the **guaranteed** total held in the Reserve). - `DeliveriesPurchased` (count over the horizon), `PerDeliveryAmount`. - Payment: `PaymentProvider?`, `PaymentIntentId?`, `IdempotencyKey?`. - `TransactionNumber` — `GFT-YYYYMMDD-000001` (parallel to Vault's `VLT-`). - Indexes mirroring Vault: unique `TransactionNumber`; unique `CustomerScheduledGiftId` filtered to non-cancelled; unique `IdempotencyKey` filtered to non-null. **The Rempla Gift Reserve.** Funds for the full delivery horizon are held in the Gift Reserve and released only against confirmed deliveries. The quoted `Total` is **guaranteed** — Rempla absorbs cost overruns (same posture as ADR-008's frozen promised price, applied across a multi-delivery horizon). `PayInFull` fully funds the Reserve at signup; installment modes (2 annual / 12 monthly) fund it over time, with the documented under-funding risk if an installment later fails. Reserve accounting beyond what the transaction records (per-delivery release, ledger) is fulfillment-domain work (ADR-009) and is out of scope here. **Domain boundaries (what Scheduled Gift does NOT own).** Per ADR-008 and the ADR-009 reservation: no vendor selection or routing, no `Delivery` rows, no order lifecycle/fulfillment state, no per-delivery execution. The gift records the *commitment and schedule*; turning each scheduled occurrence into a routed, fulfilled delivery is the Ordering/Fulfillment context (ADR-009). The catalog is never mutated or extended with gift fields. ##### Rationale - **Mirror Vault, don't reinvent.** Vault already solved the resumable-wizard aggregate, the immutable transaction record, the trigger/notification model, billing modes, and idempotent submission — all reviewed and tested. Reusing the shapes (and the `BeneficiaryNotificationTypes` enum verbatim, and a `DeliveryTriggerTypes`-shaped anchor) keeps the two flows learnable as one pattern, lets the gift Core services follow the same `IVaultSubmissionService` / `IVaultPricingService` structure, and matches the explicit product direction that the gift flow feel "very much like the Vault flow." - **Recurrence as cadence + duration + anchor.** The doc models a recurring gift as a cadence (year/quarter/month/week) on an anchor moment, run for a duration. Duration-in-years is what makes the Reserve fully fundable and the total *guaranteeable*; an open-ended plan can't be priced or fully funded up front. One-time remains a degenerate case of the same model (`ScheduleType = OneTime`). - **Anchor reuses the Vault trigger vocabulary** because the choices are the same set the customer already meets in Vault (profile date, specific date, milestone, after passing) — a `PersonDate` anchor (birthday/anniversary) is exactly the "recurring moment only the two of you would know." - **Category config lives on the gift, not the catalog.** Gift-card brand choice, "let the recipient choose," curated interests, an uploaded card photo, and claim-link-vs-address are personalization/plan concerns. ADR-008 names these as drift signals if pushed into `Product`/`Variant`. Multi-valued interests get a child table rather than a delimited column. - **Separate immutable transaction.** As with Vault, the customer can keep editing the draft gift, but the moment of commitment freezes a priced snapshot — needed for the guaranteed total, refunds, and audit independent of later edits. ##### Consequences - **New migration** adds `CustomerScheduledGift`, `ScheduledGiftTransaction`, `ScheduledGiftInterest`, and the new enums (`CustomerScheduledGiftStatuses`, `ScheduledGiftTransactionStatuses`, `GiftStartTriggerTypes`, `GiftScheduleTypes`, `GiftRecurrenceCadences`, `GiftBillingModes`, `GiftRefundRecipientTypes`, `GiftFulfillmentModes`). Enums stored as int (house convention). Reuses existing `BeneficiaryNotificationTypes`, `Occasion`, `PersonDate`, `Person`, `ProductCategory`, `ProductVariant`, `Address`. - **Catalog categories already fit** the four branches: Flowers (1), Digital Gift Cards (7), Curated Physical Gifts (8), Direct Mail Cards (9). The flow filters the giftable subset; no catalog change. - **Core services (next ADR-step, mirroring Vault):** `IScheduledGiftService` (load/save the resumable draft per wizard step), `IScheduledGiftPricingService` (the step-5 breakdown, injected `TimeProvider`), `IScheduledGiftSubmissionService` (validate → snapshot → transaction → Reserve hold → `Active`, idempotent). Two-layer tests per ADR-021: pure-unit pricing math + SQLite round-trip for persistence. - **Web wiring (later, per ADR-005):** `RemembrancePlan` gains gift endpoints, a 9-panel wizard view modeled on `Vault/Detail.cshtml`, a `gift` JS module modeled on `vault/detail.js`, a route, and an import-map alias. The hardcoded cards get replaced by live data. - **Deferred to ADR-009 (Ordering/Fulfillment):** per-delivery generation from the schedule, vendor resolution, the `Delivery` entity, Reserve ledger/release accounting, and installment charge execution. - **Open modeling choices to confirm during build:** (a) delivery address as FK-to-`Address` vs inline snapshot columns; (b) whether `RecipientInterests` reuse `ProductTag` or a dedicated interest lookup; (c) representing the Card branch's design selection as a `ProductVariant` (consistent with catalog) vs a gift-domain enum. --- **Decision Date**: June 15, 2026 **Author**: Joshua Angulo