Delivery responds to Round 10 asks
Date: 2026-05-19 From: Delivery To: Platform, Finance
Three corrections and confirmations on the Delivery section of 2026-05-19-platform-round-10-per-domain-asks-after-v2-manifests.
Ask 1 (P3/S) — delivery.customer.status is not a closed enum
Finance's customer-journey-audit.md carve-out assumes delivery.customer.status is a closed enum mapping to {pre_lock, active, dormant, churned, lapsed} and that delivery.customer.last_lesson_at exists as a column. Neither is correct.
Actual schema:
Customer.status is a free-text String @default("active"). It is a CRM workbench field — not a lifecycle enum. Its values are determined by the operator workbench's stage-derivation logic at write time, not by a closed vocabulary enforced at the DB layer.
Customer.last_lesson_at does not exist. The Customer row carries last_touch_at (operator communication-level timestamp) and handoff_at (the Sales-to-Delivery handoff moment). Neither is a lesson-completion recency signal.
How Delivery models customer lifecycle:
Lifecycle stage is org-configurable via CustomerStage (per-org key/name/bucket) and CustomerAttentionPolicy (per-stage rules: lock_state_match, inactive_days, attendance_backlog_hours, lock_drift_fires). The inactive_days threshold on CustomerAttentionPolicy is org-scoped — it is not a global 8-week / 52-week fixed window. Different organizations operate on different inactivity horizons.
Delivery does not have a row-level {pre_lock, active, dormant, churned, lapsed} enum that Finance can map against deterministically. The closest derivable signals are:
customer.handoff_at— anchor for "when did this customer enter Delivery."min(lesson.completed_at)joined via(tenant_id, participant_id = person_id)— first lesson completion (post-v1.0.0 coverage only; see Ask 2 caveat).max(lesson.completed_at)— most recent lesson completion, usable as a recency proxy for the dormancy / churn boundary.customer.closed_at— non-null when the CRM record has been closed, corresponding roughly to churned or lapsed states.
Recommendation for Finance's current_status derivation:
Finance's fixed 8-week / 8-to-52-week / 52+ thresholds are Finance's call to define. Platform can wire the audit endpoint using derived signals rather than mapping a Delivery enum:
pre_lock: customer has no completed lesson (no row matching the join, or all lessons canceled).active: most-recent completed lesson within N weeks ofperiod_end(Finance picks N).dormant: most-recent completed lesson between N and M weeks beforeperiod_end.churned/lapsed: most-recent completed lesson more than M weeks ago, orcustomer.closed_atnon-null.
Finance should file the threshold values and the closed_at interpretation on the customer-journey-audit thread. Platform implements the derivation in the audit endpoint compute; no Delivery schema change is needed.
Ask 2 (P3/L) — time_to_first_delivery_lesson_days: data path confirmed, formula correction
The compute path exists. The proposed formula min(completed_at) - lesson_created_at per customer is not the right anchor. lesson.created_at is the booking-row creation timestamp — it measures booking-to-completion latency on the first lesson, not time from customer acquisition to first completed lesson.
Correct formula:
min(lesson.completed_at) - customer.handoff_at
Join: Customer (tenant_id, person_id) to Lesson (tenant_id, participant_id) filtered to lesson.completed_at IS NOT NULL, grouped per (tenant_id, person_id).
The customer.handoff_at column exists and is non-null on every Customer row. The metric measures days from Sales-to-Delivery handoff to first lesson delivery.
Coverage caveat:
Lesson.participant_id is populated only for lessons created through the sales-scheduling-surface hold flow (post-v1.0.0 contract). Pre-contract lessons carry source_client_profile_id rather than participant_id. The join will have coverage gaps for customers whose first lesson predates v1.0.0 deployment. Platform should document this in the SectionEntityRow registry note. Delivery does not plan to backfill participant_id on pre-contract rows.
The entity-row shape (per-customer grain) works for Delivery's consumers. Platform may proceed.
Ask 3 (P3/L) — cohort_continuation_curve: derivation path confirmed, no separate silver needed
The matrix is derivable directly from Customer + Lesson. No separate Delivery-side cohort silver table is needed.
Derivation path:
- Cohort anchor:
customer.handoff_attruncated to ISO week — this is the customer's intake cohort week. - Continuation signal: for each customer in cohort
w, whether they have a completed lesson (lesson.completed_at IS NOT NULL) in each elapsed weekw + k(k = 0, 1, 2, ...). - Join:
Customer (tenant_id, person_id)toLesson (tenant_id, participant_id)as above. - Matrix cell:
count(distinct customer.person_id with a completed lesson in week w+k) / count(distinct customer.person_id in cohort w)per(org, cohort_week w, elapsed_week k).
Same coverage caveat as Ask 2. For customers whose early lessons predate v1.0.0, the completion signal in the matrix will be sparse. Platform should note this in the metric's registry entry. Cohorts that are entirely pre-v1.0.0 will show artificially low continuation rates; cohorts that are entirely post-v1.0.0 will be accurate.
Platform can wire this as a matrix metric against Customer + Lesson without a Delivery commitment to ship cohort silver.
Summary for Finance
Finance needs to:
- Remove the
delivery.customer.statusenum assumption fromcustomer-journey-audit.mdand replace it with a derived-signal rule usingmax(lesson.completed_at)recency andcustomer.closed_atas described above. - File the threshold values (
NandMweeks for active / dormant / churned boundaries) on thecustomer-journey-auditthread so Platform can encode them.
Delivery's schema does not need to change to support any of these three metrics.
References
- Platform Round 10 asks:
2026-05-19-platform-round-10-per-domain-asks-after-v2-manifests - Finance manifest:
contracts/finance-mart/customer-journey-audit.md - Delivery schema:
delivery.customer,delivery.lesson(Prisma schema, delivery repo)