///|
/// A concrete, explainable retention action reviewed by operators.
pub struct Playbook {
code : String
priority : Int
max_recency : Int
min_frequency : Int
action : String
rationale : String
}
///|
/// The policy catalog is data-driven so adding a policy does not duplicate code.
pub fn retention_playbooks() -> Array[Playbook] {
[
{
code: "winback-urgent-00",
priority: 25,
max_recency: 14,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-01",
priority: 30,
max_recency: 14,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-02",
priority: 35,
max_recency: 14,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-03",
priority: 25,
max_recency: 14,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-04",
priority: 30,
max_recency: 14,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-05",
priority: 35,
max_recency: 14,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-06",
priority: 25,
max_recency: 14,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-07",
priority: 30,
max_recency: 14,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-08",
priority: 45,
max_recency: 21,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-09",
priority: 35,
max_recency: 21,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-10",
priority: 40,
max_recency: 21,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-11",
priority: 45,
max_recency: 21,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-12",
priority: 35,
max_recency: 21,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-13",
priority: 40,
max_recency: 21,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-14",
priority: 45,
max_recency: 21,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-15",
priority: 35,
max_recency: 21,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-16",
priority: 50,
max_recency: 28,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-17",
priority: 55,
max_recency: 28,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-18",
priority: 45,
max_recency: 28,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-19",
priority: 50,
max_recency: 28,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-20",
priority: 55,
max_recency: 28,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-21",
priority: 45,
max_recency: 28,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-22",
priority: 50,
max_recency: 28,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-23",
priority: 55,
max_recency: 28,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-24",
priority: 55,
max_recency: 35,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-25",
priority: 60,
max_recency: 35,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-26",
priority: 65,
max_recency: 35,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-27",
priority: 55,
max_recency: 35,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-28",
priority: 60,
max_recency: 35,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-29",
priority: 65,
max_recency: 35,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-30",
priority: 55,
max_recency: 35,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-31",
priority: 60,
max_recency: 35,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-32",
priority: 75,
max_recency: 42,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-33",
priority: 65,
max_recency: 42,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-34",
priority: 70,
max_recency: 42,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-35",
priority: 75,
max_recency: 42,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-36",
priority: 65,
max_recency: 42,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-37",
priority: 70,
max_recency: 42,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-38",
priority: 75,
max_recency: 42,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-39",
priority: 65,
max_recency: 42,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-40",
priority: 80,
max_recency: 49,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-41",
priority: 85,
max_recency: 49,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-42",
priority: 75,
max_recency: 49,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-43",
priority: 80,
max_recency: 49,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-44",
priority: 85,
max_recency: 49,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-45",
priority: 75,
max_recency: 49,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-46",
priority: 80,
max_recency: 49,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-47",
priority: 85,
max_recency: 49,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-48",
priority: 85,
max_recency: 56,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-49",
priority: 90,
max_recency: 56,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-50",
priority: 95,
max_recency: 56,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-51",
priority: 85,
max_recency: 56,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-52",
priority: 90,
max_recency: 56,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-53",
priority: 95,
max_recency: 56,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-54",
priority: 85,
max_recency: 56,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-55",
priority: 90,
max_recency: 56,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
{
code: "winback-urgent-56",
priority: 105,
max_recency: 63,
min_frequency: 1,
action: "send a personal win-back message",
rationale: "high churn risk with a recent repeat history",
},
{
code: "winback-value-57",
priority: 95,
max_recency: 63,
min_frequency: 2,
action: "offer a targeted value bundle",
rationale: "high churn risk with meaningful spend",
},
{
code: "service-review-58",
priority: 100,
max_recency: 63,
min_frequency: 3,
action: "assign a retention owner",
rationale: "high risk requires human follow-up",
},
{
code: "feedback-loop-59",
priority: 105,
max_recency: 63,
min_frequency: 4,
action: "request feedback before the next offer",
rationale: "low frequency needs diagnosis",
},
{
code: "loyalty-60",
priority: 95,
max_recency: 63,
min_frequency: 1,
action: "invite to the referral program",
rationale: "healthy repeat behavior can create referrals",
},
{
code: "early-access-61",
priority: 100,
max_recency: 63,
min_frequency: 2,
action: "offer early access to a relevant release",
rationale: "high-value loyal behavior",
},
{
code: "onboarding-62",
priority: 105,
max_recency: 63,
min_frequency: 3,
action: "complete the onboarding sequence",
rationale: "new customers need activation",
},
{
code: "education-63",
priority: 95,
max_recency: 63,
min_frequency: 4,
action: "send a product education sequence",
rationale: "low activity can be addressed with guidance",
},
]
}
///|
/// Select policies applicable to one customer insight.
pub fn applicable_playbooks(
item : CustomerInsight,
catalog : Array[Playbook],
) -> Array[Playbook] {
catalog.filter(policy => {
item.churn.score >= policy.priority &&
item.scores.recency <= policy.max_recency &&
item.scores.frequency >= policy.min_frequency
})
}
///|
/// Choose the highest-priority policy for a customer, if one applies.
pub fn best_playbook(
item : CustomerInsight,
catalog : Array[Playbook],
) -> Playbook? {
let applicable = applicable_playbooks(item, catalog)
if applicable.is_empty() {
None
} else {
let ranked = applicable.copy()
ranked.sort_by((left, right) => right.priority - left.priority)
Some(ranked[0])
}
}