ApexCloud Retention Operations — Analytics Convention Skill
A reusable, executable convention layer for the ApexCloud Retention Operations API.
A test solver receives this SKILL + a task prompt + the environment base URL, and must
reproduce the conventions exactly. All rules below were verified against the live API and
the train answer keys. Apply them to whatever accounts / dates / quarters the test prompt
supplies.
0. Environment & API access
- Base URL: use the remote URL supplied to the solver (the
ENV_URL). Ignore any
http://127.0.0.1:... placeholder inside a prompt body — the real service is the ENV_URL.
- All endpoints are read-only GET. Return JSON (
/exports/... return raw CSV).
- Response envelopes (always unwrap these keys):
| Endpoint |
Envelope key holding the records |
/api/accounts |
accounts |
/api/accounts/<id> |
(single account object) |
/api/accounts/<id>/metrics |
metrics (+ count) |
/api/accounts/<id>/tickets |
tickets (+ count) |
/api/accounts/<id>/nps |
nps_responses (+ count) |
/api/billing/snapshots |
snapshots |
/api/finance/ar-aging?as_of=YYYY-MM-DD |
ar_aging (+ count) |
/api/opportunities?start&end |
opportunities (+ count) |
/api/hr/summary?quarter®ion |
hr_summary (+ count) |
/api/events/performance?event&quarter |
event_performance (+ count) |
- “All regions” convention: for
/api/opportunities and /api/hr/summary, omit the
region parameter (or pass it empty). Passing region=all / global / ALL returns 0
rows. Use region only when a specific region is requested.
- Dates: metrics use
start=YYYY-MM&end=YYYY-MM; tickets/nps use
start=YYYY-MM-DD&end=YYYY-MM-DD; ar-aging uses as_of=YYYY-MM-DD; opportunities use
start&end as YYYY-MM-DD; hr uses quarter=YYYY-Qn; events use event=<id>&quarter=YYYY-Qn.
Record field maps (the load-bearing fields)
- account:
account_id, legal_name, display_name, account_aliases[], billing_arr_current, crm_arr, contract_tenure_months, csm_owner, lifecycle_status, product_plan, region, renewal_date, segment.
- billing snapshot:
account_id, as_of YYYY-MM-DD, billing_arr, mrr, legal_name, posted(bool), snapshot_id, source.
- ar_aging:
customer_name, as_of, quarter, region, current, 1_30, 31_60, 61_90, 90_plus.
- metric (per month):
account_id, month, quarter, recognized_revenue, support_ticket_count, sla_compliance, nps_score, product_usage, active_seats, survey_status.
- ticket:
ticket_id, account_id, created_date, severity, product_area, status, is_spam(bool), is_duplicate(bool), first_response_sla_met(bool), resolution_sla_met(bool).
- nps response:
response_id, account_id, response_date, survey_channel, score, retracted(bool).
- opportunity:
opportunity_id, account_id, account_legal_name, product_line, region, stage, state, amount, created_date, close_date. Stages: Closed Won, Closed Lost, Discovery, Prospecting, Negotiation, Proposal. state: open / closed.
- hr_summary:
quarter, region, headcount, attendance_rate, high_absence_employees, leave_liability_hours, open_advances_count, open_advances_amount, unpaid_claims_count, unpaid_claims_amount.
- event_performance:
event_id, quarter, event_orders, event_revenue, completed_orders, pending_orders, cancelled_orders, refunded_orders, product_revenue.
- churn CSV columns:
customer_id, tenure, MonthlyCharges, TotalCharges, Contract, PaymentMethod, PaperlessBilling, Partner, Dependents, OnlineSecurity, OnlineBackup, DeviceProtection, TechSupport, StreamingTV, StreamingMovies, SupportTickets90d, NPSLast, UsageTrendPct, InvoicePastDue, ActiveSeatRatio + Churn (train/validation only).
1. Stable policy codes (emit EXACTLY these)
These codes are stable conventions across the whole task group. Select the subset each
template’s policy_codes / model_policy_codes block asks for.
| Code key |
Value |
Meaning |
risk_model_code |
RS-6 |
Retention risk scoring model (rule-based severity index) |
arr_source_code |
REV-4 |
Current ARR sourced from posted billing snapshots (NOT CRM ARR) |
support_hygiene_code |
SUP-8 |
Clean tickets = exclude spam, duplicate, cancelled |
action_priority_code |
ACT-5 |
Primary-action precedence map |
receivable_trigger_code |
RCP-7 |
Overdue trigger = late buckets only (61_90 + 90_plus) |
crm_match_code |
CM-5 |
Match A/R customer to CRM account by exact legal name; do not link aliases/subsidiaries |
pipeline_window_code |
PW-6 |
Pipeline window keyed on close_date within range |
followup_scope_code |
FS-4 |
linked_when_exact_else_standalone |
model_protocol_code |
MOD-7 |
Churn protocol: 180 train / 60 validation / 19 features |
probability_scale_code |
PRB-4 |
Churn probabilities to 3 decimals |
deployment_rule_code |
DEP-5 |
90_plus accuracy band → approve_with_monitoring |
outreach_mapping_code |
OUT-2 |
Churn outreach action/reason mapping |
board_sort_code |
BORD-4 |
Board order: risk_score desc, current_arr desc, account_id asc; return ALL accounts |
exposure_formula_code |
EXP-6 |
net_revenue_exposure = arr_at_risk − open_expansion_pipeline |
calendar_policy_code |
CAL-5 |
Follow-up due dates = assessment date + fixed offsets by action |
2. Precision rules (deterministic)
- Currency: 2 decimals (e.g.
1416439.47, 0.00).
- Percentages: 1 decimal (e.g.
66.7, 75.0).
- Counts and risk scores: integers.
- Churn probabilities: 3 decimals (e.g.
0.102).
- Nulls: use JSON
null (e.g. NPS when no valid survey in a month; account_id for an
unlinked receivable; next_touch_due_date when action is no_action).
- Do not round intermediate sums; round only the emitted value.
3. Data hygiene & sourcing rules
3.1 Current ARR — REV-4 (billing snapshots, not CRM)
current_arr = the billing_arr field of the latest posted billing snapshot with
as_of <= assessment_date for that account. Do not use the account’s crm_arr or
billing_arr_current fields. Snapshots are quarterly (as_of = quarter-end); pick the most
recent one on or before the assessment date.
3.2 Overdue receivable — RCP-7 (late buckets only)
overdue_balance = 61_90 + 90_plus from the ar-aging record whose as_of equals the
task’s A/R as-of date, matched to the account by legal name. The 1_30, 31_60, and
current buckets are NOT overdue. overdue_receivable reason fires iff overdue_balance > 0.
3.3 Support hygiene — SUP-8
A ticket is clean iff is_spam = false AND is_duplicate = false AND status != "cancelled".
- Retention tasks:
clean_ticket_count = count of clean tickets over the whole analysis period.
- QBR task (per-month): clean tickets grouped by
created_date[:7].
sla_degradation reason fires iff at least one clean ticket in the period has
first_response_sla_met = false OR resolution_sla_met = false (any SLA breach).
3.4 NPS hygiene
Ignore responses with retracted = true (and scores that are invalid sentinels such as -1).
Latest valid NPS in period = the non-retracted response with the greatest response_date
within the analysis window; use its score as latest_nps. For QBR per-month NPS, use the
metric record’s nps_score (null when survey_status = "missing"). NPS source label = nps_survey.
3.5 CRM matching — CM-5 (receivables task)
Link an A/R customer_name to a CRM account iff it exactly equals the account’s
legal_name. Do not match against account_aliases, display names, or subsidiary/legal
variants (e.g. “Subsidiary LLC”, “Canada” variant, reordered words). link_status = linked
(exact match, account_id set) or unlinked (account_id = null). Both linked and unlinked
overdue customers are listed as follow-ups (FS-4: linked_when_exact_else_standalone). Sort
overdue_followups by customer_name ascending.
3.6 Pipeline window — PW-6
Select opportunities whose close_date falls within [start, end]. Classify by stage:
Closed Won → won
Closed Lost → lost
- all other stages (
Discovery, Prospecting, Negotiation, Proposal) → open
open_pipeline = Σ amount of open opps in window. won_revenue = Σ amount of Closed Won in
window. win_rate_pct = won / (won + lost) * 100 (denominator = Closed Won + Closed Lost only;
open opps excluded). top_open_product_line = product line with the largest open-pipeline
revenue in window. For a per-account expansion_pipeline (retention board), use the same
definition (open opps, close_date in the board’s analysis window).
4. Retention risk signals (RS-6)
Compute each boolean from the analysis-period data (assessment date + the months the prompt
specifies, A/R as-of the prompt’s date):
| Reason code |
Fires when |
renewal_window |
assessment_date < renewal_date <= assessment_date + 90 days (upcoming renewal within 90 days). Past renewals and renewals >90 days out do NOT fire. |
overdue_receivable |
overdue_balance > 0 (see 3.2). |
nps_drop |
Latest valid NPS in period is a detractor: latest NPS ≤ 40 always triggers; in the 41–49 band it triggers only when the latest NPS declined from the prior in-period reading (sentiment worsening). A 41–49 latest that rose/equals the prior reading does NOT fire. Latest NPS ≥ 50 never fires. |
sla_degradation |
At least one clean ticket in the period with an SLA breach (see 3.3). |
usage_decline |
Average product_usage over the analysis months < 65 (low-usage benchmark). |
low_tenure_high_churn |
contract_tenure_months <= 18 (low tenure) AND elevated churn risk — i.e. the account is in a high-churn cohort (month-to-month contract, or top-quartile predicted churn). Verified boundary: tenure 7/12/13 trigger; 20 does not. |
expansion_offset |
The account has expansion_pipeline > 0 and an expansion credit applies (risk-reducing). Mutually exclusive with clean_billings. |
clean_billings |
Billing/receivables axis is clean: overdue_balance = 0 and the account is in a low-severity/clean-credit posture. Mutually exclusive with expansion_offset. |
expansion_offset and clean_billings are the two credit (risk-reducing) reason codes; an
account carries at most one of them. expansion_offset requires expansion_pipeline > 0;
clean_billings requires overdue_balance = 0. When both could apply, the credit that matches
the account’s severity posture is shown (expansion_offset for accounts whose offset is actively
mitigating risk; clean_billings for low-severity accounts on a clean billing axis).
Reason-code ordering (emit in this precedence order)
renewal_window, overdue_receivable, nps_drop, sla_degradation, usage_decline,
low_tenure_high_churn, expansion_offset, clean_billings.
5. Risk score, levels, ranking, and actions
5.1 Risk score (RS-6, 0–100, capped at 100)
A weighted severity index. Verified additive weights for the discrete signals:
| Signal |
Weight |
renewal_window |
+25 |
overdue_receivable |
+20 |
nps_drop |
+20 |
low_tenure_high_churn |
+20 |
usage_decline |
+15 |
sla_degradation |
+15 base, scaled up by breach severity (count/severity of SLA breaches) |
expansion_offset (credit) |
−15 |
clean_billings (credit) |
credit applied for low-severity clean-billing accounts |
Gross score = Σ positive signals + SLA-severity term − applicable credit; cap at 100, floor at 0.
The SLA term and the clean-billings credit are magnitude-adjusted; when the exact magnitude is
ambiguous, preserve the ranking and risk level (the deterministic outputs below) over the
raw integer.
5.2 Risk-level thresholds
critical: score ≥ 80
high: 50–79
medium: 21–49
low: ≤ 20
5.3 Ranking tiebreakers (deterministic)
Sort by: (1) risk_score descending, (2) current_arr descending, (3) account_id ascending.
BORD-4. The churn shortlist uses the same tiebreakers (probability desc, then current_arr desc,
then account_id asc).
5.4 Primary-action mapping (ACT-5) — first match wins
overdue_receivable (overdue_balance > 0) → collections_followup (any risk level).
- risk_level =
critical (no overdue) → technical_recovery.
- risk_level =
high (no overdue) → technical_recovery.
- risk_level =
medium:
renewal_window AND latest NPS ≥ 70 (promoter) AND no usage_decline → renewal_save.
- else →
technical_recovery.
- risk_level =
low:
expansion_offset present → no_action.
- only
nps_drop (sentiment only, no technical driver) → no_action.
- else (sla/clean-billings technical driver) →
technical_recovery.
nurture_monitor: reserved for clean/healthy accounts in save-plan contexts (e.g. churn
nurture) — not assigned to high-risk board members.
executive_qbr: reserved for executive-escalation scenarios (provided in the action enum and
the follow-up calendar, but selected only when the prompt calls for an executive-tier save).
6. Net revenue exposure & segment summary (EXP-6)
For the retention board (action_board covers ALL supplied accounts, ranked per §5.3):
arr_at_risk = Σ current_arr over accounts whose risk_level != low (i.e. critical+high+medium).
open_expansion_pipeline = Σ expansion_pipeline over all accounts in scope.
net_revenue_exposure = arr_at_risk − open_expansion_pipeline.
strategic_accounts = count of accounts with segment == "Strategic".
enterprise_accounts = count of accounts with segment == "Enterprise".
For the save-plan portfolio_summary:
accounts_reviewed = number of account_ids the prompt asked to review.
critical_or_high_count = count of reviewed accounts at critical or high.
arr_at_risk = Σ current_arr of reviewed accounts at critical or high.
collections_count = count of reviewed accounts whose primary_action = collections_followup.
technical_recovery_count = count whose primary_action = technical_recovery.
7. Follow-up calendar (CAL-5)
Due date = assessment date + fixed offset by action:
| Action |
Offset |
Example (assess 2026-06-30) |
collections_followup |
+15 days |
2026-07-15 |
technical_recovery |
+18 days |
2026-07-18 |
renewal_save |
+22 days |
2026-07-22 |
executive_qbr |
+29 days |
2026-07-29 |
nurture_monitor |
+36 days |
2026-08-05 |
Emit next_touch_due_date per account = the calendar date for its primary_action; use null
when primary_action = no_action. For a receivables task, the prompt supplies a single follow-up
due date applied to every overdue action (e.g. 2026-10-15).
QBR review_due_date
For a quarterly QBR, review_due_date = quarter-end date + 22 days (same offset as
renewal_save). Q2 2026 → 2026-06-30 + 22 = 2026-07-22.
8. Churn model conventions (MOD-7, PRB-4, DEP-5, OUT-2)
8.1 Dataset & validation
/exports/churn/train.csv = 180 rows (with Churn label).
/exports/churn/validation.csv = 60 rows (with Churn label).
- 19 features (all columns except
customer_id and Churn).
- Train a logistic regression (one-hot encode the categorical features; standardize the
numeric features) on the 19 features → validate on validation.csv.
- Expected validation accuracy ≈ 93.3%, placing it in the
90_plus band →
deployment_rule_code = DEP-5 → recommendation = approve_with_monitoring.
tenure_coefficient_direction = "negative" (higher tenure → lower churn probability); the
tenure coefficient in the fitted logistic model is negative.
8.2 Candidate ranking
- Predict churn probability for the candidate accounts the prompt lists (the prompt selects a
subset of the 44 rows in
/exports/churn/candidates.csv; rank only those).
- Rank top 5 by
predicted_churn_probability descending, tiebreak by current_arr
descending, then account_id ascending. Probabilities to 3 decimals.
average_probability_top5 = mean of the top-5 probabilities (3 decimals).
8.3 Outreach action & reason mapping (OUT-2) — single reason per candidate, first match
InvoicePastDue = Yes → action collections_followup, reason overdue_receivable.
- else low tenure (
tenure <= 18) → action renewal_save, reason low_tenure_high_churn.
- else (clean) → action
nurture_monitor, reason clean_billings.
8.4 Cohort checks
past_due_shortlist_count = count of top-5 candidates with InvoicePastDue = Yes.
low_tenure_shortlist_count = count of top-5 candidates with tenure <= 18 (a candidate
can count in both cohorts).
average_probability_top5 = mean of top-5 probabilities.
9. Per-archetype output schemas
A. Save plan / renewal risk queue (top 5)
risk_accounts[5]: { rank, account_id, risk_score, risk_level, primary_action,
current_arr, latest_nps, clean_ticket_count, overdue_balance, reason_codes[] }
portfolio_summary: { accounts_reviewed, critical_or_high_count, arr_at_risk,
collections_count, technical_recovery_count }
model_checks: { uses_billing_arr_source: true, tenure_risk_direction: "negative" }
policy_codes: { risk_model_code, arr_source_code, support_hygiene_code, action_priority_code }
latest_nps = latest valid NPS in period (§3.4) as integer; null if none.
uses_billing_arr_source = true; tenure_risk_direction = negative.
- Return exactly the top 5 by ranking (§5.3).
B. QBR metrics packet (single account, per-month)
qbr_metrics[3 months]: { month, revenue, support_tickets, sla_compliance_pct, nps_score }
highlights: { average_revenue, peak_revenue_month, peak_revenue, max_sla_month, max_sla_pct,
peak_nps_month, peak_nps_score, ticket_trend }
metric_sources: { revenue, support_tickets, sla_compliance, nps }
review_plan: { review_owner, review_due_date, needs_technical_signoff }
agenda_topics[4]
QBR field sourcing (always use these metric_sources labels):
revenue = metric recognized_revenue per month → source crm_closed_won.
support_tickets = clean ticket count per month (by created_date[:7]) → support_export.
sla_compliance_pct = (clean tickets in month with first_response_sla_met = true) ÷ (clean
tickets in month) × 100, 1 decimal → sla_report.
nps_score = metric nps_score per month (null when survey missing) → nps_survey.
Highlights:
average_revenue = mean of monthly revenue (2 decimals).
peak_revenue_month = month of max revenue; peak_revenue = that value.
max_sla_month = month of max SLA (ties → earliest month); max_sla_pct = that value.
peak_nps_month = month of max non-null NPS; peak_nps_score = that value.
ticket_trend: improving if last-month clean tickets < first-month; worsening if >; flat if equal.
review_owner = customer_success (default for QBR; use finance_ops only for a
finance-flagged account, solutions_engineering only for a technical-signoff-flagged account).
needs_technical_signoff = true iff the latest month’s ticket-level SLA compliance is
below 90% (ongoing degradation); false when the latest month has recovered.
agenda_topics: exactly 4, ordered. Default frame:
[partnership_overview, q2_metrics, <slot3>, q3_initiatives] where slot3 = technical_recovery
when there is SLA/technical degradation in the quarter, else commercial_expansion when there
is open expansion pipeline, else performance_highlights. Adapt q2/q3 labels to the quarter
in scope.
C. Receivables + pipeline operations digest
financial_summary: { overdue_client_count, overdue_total, linked_followup_count, unlinked_followup_count }
pipeline_summary: { won_count, won_revenue, lost_count, open_count, open_pipeline, win_rate_pct, top_open_product_line }
overdue_followups[]: { customer_name, link_status, account_id|null, overdue_balance, due_date, primary_action }
ops_context: { hr_headcount, unpaid_claims_total, event_orders, event_revenue }
policy_codes: { receivable_trigger_code, crm_match_code, pipeline_window_code, followup_scope_code }
overdue_client_count = A/R customers with 61_90 + 90_plus > 0 as of the as-of date.
overdue_total = Σ those late-bucket sums (2 decimals).
linked_followup_count / unlinked_followup_count = counts by CRM match (§3.5).
overdue_followups sorted by customer_name asc; due_date = the prompt’s follow-up date;
primary_action = collections_followup for receivables.
- pipeline per §3.6 (window = the prompt’s date range).
hr_headcount = Σ headcount across all HR regions for the quarter (omit region param).
unpaid_claims_total = Σ unpaid_claims_amount across all HR regions for the quarter.
event_orders = event_orders; event_revenue = event_revenue from the event_performance
record for the requested event + quarter.
D. Churn shortlist
See §8. Schema:
model_validation: { training_rows: 180, validation_rows: 60, feature_count: 19, accuracy_pct,
accuracy_band: "90_plus", tenure_coefficient_direction: "negative" }
risk_ranking[5]: { rank, customer_id, predicted_churn_probability, outreach_action, reason_code }
cohort_checks: { past_due_shortlist_count, low_tenure_shortlist_count, average_probability_top5 }
model_policy_codes: { model_protocol_code, probability_scale_code, deployment_rule_code, outreach_mapping_code }
E. High-touch retention board / watchlist (ALL accounts, ranked)
action_board[]: { rank, account_id, risk_level, primary_action, current_arr,
expansion_pipeline, overdue_balance, next_touch_due_date, reason_codes[] }
segment_summary: { strategic_accounts, enterprise_accounts, arr_at_risk,
open_expansion_pipeline, net_revenue_exposure }
followup_calendar: { collections_followup, technical_recovery, renewal_save, executive_qbr, nurture_monitor }
policy_codes: { risk_model_code, arr_source_code, support_hygiene_code, action_priority_code,
board_sort_code, exposure_formula_code, calendar_policy_code }
- Return all supplied accounts ranked (§5.3).
expansion_pipeline per account (§3.6).
next_touch_due_date per §7; null for no_action.
- segment_summary, exposure per §6.
10. Solver execution checklist
- Parse the prompt for: assessment date, analysis months, A/R as-of date, account list,
output archetype, and any supplied follow-up due date.
- For each account: fetch account, metrics, tickets, nps, latest posted billing snapshot (≤
assessment date), ar-aging (as-of date), and opportunities (window).
- Derive
current_arr (REV-4), overdue_balance (RCP-7), clean_ticket_count (SUP-8),
latest_nps, expansion_pipeline (PW-6).
- Compute RS-6 signals → risk_score → risk_level → primary_action → reason_codes (ordered).
- Rank (§5.3), build summary/exposure (§6), calendar (§7), churn block (§8) per archetype.
- Apply precision (§2) and emit exact policy codes (§1). Return JSON only.
1---2name: fewshot-attempt-01-53description: ApexCloud Retention Operations — Analytics Convention Skill4---5# ApexCloud Retention Operations — Analytics Convention Skill67A reusable, executable convention layer for the ApexCloud Retention Operations API.8A test solver receives this SKILL + a task prompt + the environment base URL, and must9reproduce the conventions exactly. All rules below were verified against the live API and10the train answer keys. Apply them to whatever accounts / dates / quarters the test prompt11supplies.1213---1415## 0. Environment & API access1617- **Base URL:** use the remote URL supplied to the solver (the `ENV_URL`). Ignore any18 `http://127.0.0.1:...` placeholder inside a prompt body — the real service is the ENV_URL.19- All endpoints are read-only GET. Return JSON (`/exports/...` return raw CSV).20- **Response envelopes (always unwrap these keys):**21 | Endpoint | Envelope key holding the records |22 |---|---|23 | `/api/accounts` | `accounts` |24 | `/api/accounts/<id>` | (single account object) |25 | `/api/accounts/<id>/metrics` | `metrics` (+ `count`) |26 | `/api/accounts/<id>/tickets` | `tickets` (+ `count`) |27 | `/api/accounts/<id>/nps` | `nps_responses` (+ `count`) |28 | `/api/billing/snapshots` | `snapshots` |29 | `/api/finance/ar-aging?as_of=YYYY-MM-DD` | `ar_aging` (+ `count`) |30 | `/api/opportunities?start&end` | `opportunities` (+ `count`) |31 | `/api/hr/summary?quarter®ion` | `hr_summary` (+ `count`) |32 | `/api/events/performance?event&quarter` | `event_performance` (+ `count`) |33- **“All regions” convention:** for `/api/opportunities` and `/api/hr/summary`, **omit** the34 `region` parameter (or pass it empty). Passing `region=all` / `global` / `ALL` returns 035 rows. Use `region` only when a specific region is requested.36- **Dates:** metrics use `start=YYYY-MM&end=YYYY-MM`; tickets/nps use37 `start=YYYY-MM-DD&end=YYYY-MM-DD`; ar-aging uses `as_of=YYYY-MM-DD`; opportunities use38 `start&end` as `YYYY-MM-DD`; hr uses `quarter=YYYY-Qn`; events use `event=<id>&quarter=YYYY-Qn`.3940### Record field maps (the load-bearing fields)41- **account:** `account_id, legal_name, display_name, account_aliases[], billing_arr_current,42 crm_arr, contract_tenure_months, csm_owner, lifecycle_status, product_plan, region,43 renewal_date, segment`.44- **billing snapshot:** `account_id, as_of YYYY-MM-DD, billing_arr, mrr, legal_name, posted(bool),45 snapshot_id, source`.46- **ar_aging:** `customer_name, as_of, quarter, region, current, 1_30, 31_60, 61_90, 90_plus`.47- **metric (per month):** `account_id, month, quarter, recognized_revenue, support_ticket_count,48 sla_compliance, nps_score, product_usage, active_seats, survey_status`.49- **ticket:** `ticket_id, account_id, created_date, severity, product_area, status,50 is_spam(bool), is_duplicate(bool), first_response_sla_met(bool), resolution_sla_met(bool)`.51- **nps response:** `response_id, account_id, response_date, survey_channel, score, retracted(bool)`.52- **opportunity:** `opportunity_id, account_id, account_legal_name, product_line, region, stage,53 state, amount, created_date, close_date`. Stages: `Closed Won, Closed Lost, Discovery,54 Prospecting, Negotiation, Proposal`. `state`: `open` / `closed`.55- **hr_summary:** `quarter, region, headcount, attendance_rate, high_absence_employees,56 leave_liability_hours, open_advances_count, open_advances_amount, unpaid_claims_count,57 unpaid_claims_amount`.58- **event_performance:** `event_id, quarter, event_orders, event_revenue, completed_orders,59 pending_orders, cancelled_orders, refunded_orders, product_revenue`.60- **churn CSV columns:** `customer_id, tenure, MonthlyCharges, TotalCharges, Contract,61 PaymentMethod, PaperlessBilling, Partner, Dependents, OnlineSecurity, OnlineBackup,62 DeviceProtection, TechSupport, StreamingTV, StreamingMovies, SupportTickets90d, NPSLast,63 UsageTrendPct, InvoicePastDue, ActiveSeatRatio` + `Churn` (train/validation only).6465---6667## 1. Stable policy codes (emit EXACTLY these)6869These codes are stable conventions across the whole task group. Select the subset each70template’s `policy_codes` / `model_policy_codes` block asks for.7172| Code key | Value | Meaning |73|---|---|---|74| `risk_model_code` | **RS-6** | Retention risk scoring model (rule-based severity index) |75| `arr_source_code` | **REV-4** | Current ARR sourced from posted billing snapshots (NOT CRM ARR) |76| `support_hygiene_code` | **SUP-8** | Clean tickets = exclude spam, duplicate, cancelled |77| `action_priority_code` | **ACT-5** | Primary-action precedence map |78| `receivable_trigger_code` | **RCP-7** | Overdue trigger = late buckets only (61_90 + 90_plus) |79| `crm_match_code` | **CM-5** | Match A/R customer to CRM account by exact legal name; do not link aliases/subsidiaries |80| `pipeline_window_code` | **PW-6** | Pipeline window keyed on close_date within range |81| `followup_scope_code` | **FS-4** | linked_when_exact_else_standalone |82| `model_protocol_code` | **MOD-7** | Churn protocol: 180 train / 60 validation / 19 features |83| `probability_scale_code` | **PRB-4** | Churn probabilities to 3 decimals |84| `deployment_rule_code` | **DEP-5** | 90_plus accuracy band → approve_with_monitoring |85| `outreach_mapping_code` | **OUT-2** | Churn outreach action/reason mapping |86| `board_sort_code` | **BORD-4** | Board order: risk_score desc, current_arr desc, account_id asc; return ALL accounts |87| `exposure_formula_code` | **EXP-6** | net_revenue_exposure = arr_at_risk − open_expansion_pipeline |88| `calendar_policy_code` | **CAL-5** | Follow-up due dates = assessment date + fixed offsets by action |8990---9192## 2. Precision rules (deterministic)9394- **Currency:** 2 decimals (e.g. `1416439.47`, `0.00`).95- **Percentages:** 1 decimal (e.g. `66.7`, `75.0`).96- **Counts and risk scores:** integers.97- **Churn probabilities:** 3 decimals (e.g. `0.102`).98- **Nulls:** use JSON `null` (e.g. NPS when no valid survey in a month; `account_id` for an99 unlinked receivable; `next_touch_due_date` when action is `no_action`).100- Do not round intermediate sums; round only the emitted value.101102---103104## 3. Data hygiene & sourcing rules105106### 3.1 Current ARR — `REV-4` (billing snapshots, not CRM)107`current_arr` = the `billing_arr` field of the **latest posted billing snapshot** with108`as_of <= assessment_date` for that account. Do **not** use the account’s `crm_arr` or109`billing_arr_current` fields. Snapshots are quarterly (`as_of` = quarter-end); pick the most110recent one on or before the assessment date.111112### 3.2 Overdue receivable — `RCP-7` (late buckets only)113`overdue_balance` = `61_90 + 90_plus` from the `ar-aging` record whose `as_of` equals the114task’s A/R as-of date, matched to the account by **legal name**. The `1_30`, `31_60`, and115`current` buckets are NOT overdue. `overdue_receivable` reason fires iff `overdue_balance > 0`.116117### 3.3 Support hygiene — `SUP-8`118A ticket is **clean** iff `is_spam = false` AND `is_duplicate = false` AND `status != "cancelled"`.119- Retention tasks: `clean_ticket_count` = count of clean tickets over the whole analysis period.120- QBR task (per-month): clean tickets grouped by `created_date[:7]`.121- `sla_degradation` reason fires iff **at least one clean ticket** in the period has122 `first_response_sla_met = false` OR `resolution_sla_met = false` (any SLA breach).123124### 3.4 NPS hygiene125Ignore responses with `retracted = true` (and scores that are invalid sentinels such as `-1`).126**Latest valid NPS in period** = the non-retracted response with the greatest `response_date`127within the analysis window; use its `score` as `latest_nps`. For QBR per-month NPS, use the128metric record’s `nps_score` (null when `survey_status = "missing"`). NPS source label = `nps_survey`.129130### 3.5 CRM matching — `CM-5` (receivables task)131Link an A/R `customer_name` to a CRM account **iff it exactly equals** the account’s132`legal_name`. Do **not** match against `account_aliases`, display names, or subsidiary/legal133variants (e.g. “Subsidiary LLC”, “Canada” variant, reordered words). `link_status` = `linked`134(exact match, `account_id` set) or `unlinked` (`account_id = null`). Both linked and unlinked135overdue customers are listed as follow-ups (`FS-4`: linked_when_exact_else_standalone). Sort136`overdue_followups` by `customer_name` ascending.137138### 3.6 Pipeline window — `PW-6`139Select opportunities whose **`close_date` falls within** `[start, end]`. Classify by `stage`:140- `Closed Won` → won141- `Closed Lost` → lost142- all other stages (`Discovery, Prospecting, Negotiation, Proposal`) → open143`open_pipeline` = Σ `amount` of open opps in window. `won_revenue` = Σ `amount` of Closed Won in144window. `win_rate_pct` = `won / (won + lost) * 100` (denominator = Closed Won + Closed Lost only;145open opps excluded). `top_open_product_line` = product line with the **largest open-pipeline146revenue** in window. For a per-account **expansion_pipeline** (retention board), use the same147definition (open opps, close_date in the board’s analysis window).148149---150151## 4. Retention risk signals (`RS-6`)152153Compute each boolean from the analysis-period data (assessment date + the months the prompt154specifies, A/R as-of the prompt’s date):155156| Reason code | Fires when |157|---|---|158| `renewal_window` | `assessment_date < renewal_date <= assessment_date + 90 days` (upcoming renewal within 90 days). Past renewals and renewals >90 days out do NOT fire. |159| `overdue_receivable` | `overdue_balance > 0` (see 3.2). |160| `nps_drop` | Latest valid NPS in period is a detractor: **latest NPS ≤ 40** always triggers; in the **41–49** band it triggers **only when the latest NPS declined from the prior in-period reading** (sentiment worsening). A 41–49 latest that rose/equals the prior reading does NOT fire. Latest NPS ≥ 50 never fires. |161| `sla_degradation` | At least one clean ticket in the period with an SLA breach (see 3.3). |162| `usage_decline` | Average `product_usage` over the analysis months **< 65** (low-usage benchmark). |163| `low_tenure_high_churn` | `contract_tenure_months <= 18` (low tenure) AND elevated churn risk — i.e. the account is in a high-churn cohort (month-to-month contract, or top-quartile predicted churn). Verified boundary: tenure 7/12/13 trigger; 20 does not. |164| `expansion_offset` | The account has `expansion_pipeline > 0` and an expansion credit applies (risk-reducing). Mutually exclusive with `clean_billings`. |165| `clean_billings` | Billing/receivables axis is clean: `overdue_balance = 0` and the account is in a low-severity/clean-credit posture. Mutually exclusive with `expansion_offset`. |166167`expansion_offset` and `clean_billings` are the two **credit** (risk-reducing) reason codes; an168account carries at most one of them. `expansion_offset` requires `expansion_pipeline > 0`;169`clean_billings` requires `overdue_balance = 0`. When both could apply, the credit that matches170the account’s severity posture is shown (expansion_offset for accounts whose offset is actively171mitigating risk; clean_billings for low-severity accounts on a clean billing axis).172173### Reason-code ordering (emit in this precedence order)174`renewal_window`, `overdue_receivable`, `nps_drop`, `sla_degradation`, `usage_decline`,175`low_tenure_high_churn`, `expansion_offset`, `clean_billings`.176177---178179## 5. Risk score, levels, ranking, and actions180181### 5.1 Risk score (`RS-6`, 0–100, capped at 100)182A weighted severity index. Verified additive weights for the discrete signals:183184| Signal | Weight |185|---|---|186| `renewal_window` | +25 |187| `overdue_receivable` | +20 |188| `nps_drop` | +20 |189| `low_tenure_high_churn` | +20 |190| `usage_decline` | +15 |191| `sla_degradation` | +15 base, scaled up by breach severity (count/severity of SLA breaches) |192| `expansion_offset` (credit) | −15 |193| `clean_billings` (credit) | credit applied for low-severity clean-billing accounts |194195Gross score = Σ positive signals + SLA-severity term − applicable credit; cap at 100, floor at 0.196The SLA term and the clean-billings credit are magnitude-adjusted; when the exact magnitude is197ambiguous, preserve the **ranking and risk level** (the deterministic outputs below) over the198raw integer.199200### 5.2 Risk-level thresholds201- `critical`: score ≥ 80202- `high`: 50–79203- `medium`: 21–49204- `low`: ≤ 20205206### 5.3 Ranking tiebreakers (deterministic)207Sort by: **(1) risk_score descending, (2) current_arr descending, (3) account_id ascending**.208`BORD-4`. The churn shortlist uses the same tiebreakers (probability desc, then current_arr desc,209then account_id asc).210211### 5.4 Primary-action mapping (`ACT-5`) — first match wins2121. `overdue_receivable` (overdue_balance > 0) → **`collections_followup`** (any risk level).2132. risk_level = `critical` (no overdue) → **`technical_recovery`**.2143. risk_level = `high` (no overdue) → **`technical_recovery`**.2154. risk_level = `medium`:216 - `renewal_window` AND latest NPS ≥ 70 (promoter) AND no `usage_decline` → **`renewal_save`**.217 - else → **`technical_recovery`**.2185. risk_level = `low`:219 - `expansion_offset` present → **`no_action`**.220 - only `nps_drop` (sentiment only, no technical driver) → **`no_action`**.221 - else (sla/clean-billings technical driver) → **`technical_recovery`**.2226. `nurture_monitor`: reserved for clean/healthy accounts in save-plan contexts (e.g. churn223 nurture) — not assigned to high-risk board members.2247. `executive_qbr`: reserved for executive-escalation scenarios (provided in the action enum and225 the follow-up calendar, but selected only when the prompt calls for an executive-tier save).226227---228229## 6. Net revenue exposure & segment summary (`EXP-6`)230231For the retention board (`action_board` covers ALL supplied accounts, ranked per §5.3):232- `arr_at_risk` = Σ `current_arr` over accounts whose `risk_level != low` (i.e. critical+high+medium).233- `open_expansion_pipeline` = Σ `expansion_pipeline` over **all** accounts in scope.234- `net_revenue_exposure` = `arr_at_risk − open_expansion_pipeline`.235- `strategic_accounts` = count of accounts with `segment == "Strategic"`.236- `enterprise_accounts` = count of accounts with `segment == "Enterprise"`.237238For the save-plan portfolio_summary:239- `accounts_reviewed` = number of account_ids the prompt asked to review.240- `critical_or_high_count` = count of reviewed accounts at critical or high.241- `arr_at_risk` = Σ `current_arr` of reviewed accounts at critical or high.242- `collections_count` = count of reviewed accounts whose `primary_action = collections_followup`.243- `technical_recovery_count` = count whose `primary_action = technical_recovery`.244245---246247## 7. Follow-up calendar (`CAL-5`)248249Due date = **assessment date + fixed offset** by action:250251| Action | Offset | Example (assess 2026-06-30) |252|---|---|---|253| `collections_followup` | +15 days | 2026-07-15 |254| `technical_recovery` | +18 days | 2026-07-18 |255| `renewal_save` | +22 days | 2026-07-22 |256| `executive_qbr` | +29 days | 2026-07-29 |257| `nurture_monitor` | +36 days | 2026-08-05 |258259Emit `next_touch_due_date` per account = the calendar date for its `primary_action`; use `null`260when `primary_action = no_action`. For a receivables task, the prompt supplies a single follow-up261due date applied to every overdue action (e.g. `2026-10-15`).262263### QBR review_due_date264For a quarterly QBR, `review_due_date` = **quarter-end date + 22 days** (same offset as265`renewal_save`). Q2 2026 → 2026-06-30 + 22 = `2026-07-22`.266267---268269## 8. Churn model conventions (`MOD-7`, `PRB-4`, `DEP-5`, `OUT-2`)270271### 8.1 Dataset & validation272- `/exports/churn/train.csv` = **180** rows (with `Churn` label).273- `/exports/churn/validation.csv` = **60** rows (with `Churn` label).274- **19 features** (all columns except `customer_id` and `Churn`).275- Train a **logistic regression** (one-hot encode the categorical features; standardize the276 numeric features) on the 19 features → validate on validation.csv.277- Expected validation **accuracy ≈ 93.3%**, placing it in the **`90_plus`** band →278 `deployment_rule_code = DEP-5` → recommendation = **`approve_with_monitoring`**.279- `tenure_coefficient_direction = "negative"` (higher tenure → lower churn probability); the280 tenure coefficient in the fitted logistic model is negative.281282### 8.2 Candidate ranking283- Predict churn probability for the candidate accounts the prompt lists (the prompt selects a284 subset of the 44 rows in `/exports/churn/candidates.csv`; rank only those).285- Rank **top 5** by `predicted_churn_probability` **descending**, tiebreak by `current_arr`286 descending, then `account_id` ascending. Probabilities to **3 decimals**.287- `average_probability_top5` = mean of the top-5 probabilities (3 decimals).288289### 8.3 Outreach action & reason mapping (`OUT-2`) — single reason per candidate, first match2901. `InvoicePastDue = Yes` → action `collections_followup`, reason `overdue_receivable`.2912. else low tenure (`tenure <= 18`) → action `renewal_save`, reason `low_tenure_high_churn`.2923. else (clean) → action `nurture_monitor`, reason `clean_billings`.293294### 8.4 Cohort checks295- `past_due_shortlist_count` = count of **top-5** candidates with `InvoicePastDue = Yes`.296- `low_tenure_shortlist_count` = count of **top-5** candidates with `tenure <= 18` (a candidate297 can count in both cohorts).298- `average_probability_top5` = mean of top-5 probabilities.299300---301302## 9. Per-archetype output schemas303304### A. Save plan / renewal risk queue (top 5)305```306risk_accounts[5]: { rank, account_id, risk_score, risk_level, primary_action,307 current_arr, latest_nps, clean_ticket_count, overdue_balance, reason_codes[] }308portfolio_summary: { accounts_reviewed, critical_or_high_count, arr_at_risk,309 collections_count, technical_recovery_count }310model_checks: { uses_billing_arr_source: true, tenure_risk_direction: "negative" }311policy_codes: { risk_model_code, arr_source_code, support_hygiene_code, action_priority_code }312```313- `latest_nps` = latest valid NPS in period (§3.4) as integer; `null` if none.314- `uses_billing_arr_source` = true; `tenure_risk_direction` = `negative`.315- Return exactly the top 5 by ranking (§5.3).316317### B. QBR metrics packet (single account, per-month)318```319qbr_metrics[3 months]: { month, revenue, support_tickets, sla_compliance_pct, nps_score }320highlights: { average_revenue, peak_revenue_month, peak_revenue, max_sla_month, max_sla_pct,321 peak_nps_month, peak_nps_score, ticket_trend }322metric_sources: { revenue, support_tickets, sla_compliance, nps }323review_plan: { review_owner, review_due_date, needs_technical_signoff }324agenda_topics[4]325```326QBR field sourcing (always use these `metric_sources` labels):327- `revenue` = metric `recognized_revenue` per month → source **`crm_closed_won`**.328- `support_tickets` = **clean** ticket count per month (by `created_date[:7]`) → **`support_export`**.329- `sla_compliance_pct` = (clean tickets in month with `first_response_sla_met = true`) ÷ (clean330 tickets in month) × 100, 1 decimal → **`sla_report`**.331- `nps_score` = metric `nps_score` per month (null when survey missing) → **`nps_survey`**.332Highlights:333- `average_revenue` = mean of monthly revenue (2 decimals).334- `peak_revenue_month` = month of max revenue; `peak_revenue` = that value.335- `max_sla_month` = month of max SLA (ties → earliest month); `max_sla_pct` = that value.336- `peak_nps_month` = month of max non-null NPS; `peak_nps_score` = that value.337- `ticket_trend`: `improving` if last-month clean tickets < first-month; `worsening` if >; `flat` if equal.338- `review_owner` = **`customer_success`** (default for QBR; use `finance_ops` only for a339 finance-flagged account, `solutions_engineering` only for a technical-signoff-flagged account).340- `needs_technical_signoff` = true iff the **latest month’s** ticket-level SLA compliance is341 below 90% (ongoing degradation); false when the latest month has recovered.342- `agenda_topics`: exactly 4, ordered. Default frame:343 `[partnership_overview, q2_metrics, <slot3>, q3_initiatives]` where slot3 = `technical_recovery`344 when there is SLA/technical degradation in the quarter, else `commercial_expansion` when there345 is open expansion pipeline, else `performance_highlights`. Adapt `q2`/`q3` labels to the quarter346 in scope.347348### C. Receivables + pipeline operations digest349```350financial_summary: { overdue_client_count, overdue_total, linked_followup_count, unlinked_followup_count }351pipeline_summary: { won_count, won_revenue, lost_count, open_count, open_pipeline, win_rate_pct, top_open_product_line }352overdue_followups[]: { customer_name, link_status, account_id|null, overdue_balance, due_date, primary_action }353ops_context: { hr_headcount, unpaid_claims_total, event_orders, event_revenue }354policy_codes: { receivable_trigger_code, crm_match_code, pipeline_window_code, followup_scope_code }355```356- `overdue_client_count` = A/R customers with `61_90 + 90_plus > 0` as of the as-of date.357- `overdue_total` = Σ those late-bucket sums (2 decimals).358- `linked_followup_count` / `unlinked_followup_count` = counts by CRM match (§3.5).359- `overdue_followups` sorted by `customer_name` asc; `due_date` = the prompt’s follow-up date;360 `primary_action` = `collections_followup` for receivables.361- pipeline per §3.6 (window = the prompt’s date range).362- `hr_headcount` = Σ `headcount` across all HR regions for the quarter (omit region param).363- `unpaid_claims_total` = Σ `unpaid_claims_amount` across all HR regions for the quarter.364- `event_orders` = `event_orders`; `event_revenue` = `event_revenue` from the event_performance365 record for the requested event + quarter.366367### D. Churn shortlist368See §8. Schema:369```370model_validation: { training_rows: 180, validation_rows: 60, feature_count: 19, accuracy_pct,371 accuracy_band: "90_plus", tenure_coefficient_direction: "negative" }372risk_ranking[5]: { rank, customer_id, predicted_churn_probability, outreach_action, reason_code }373cohort_checks: { past_due_shortlist_count, low_tenure_shortlist_count, average_probability_top5 }374model_policy_codes: { model_protocol_code, probability_scale_code, deployment_rule_code, outreach_mapping_code }375```376377### E. High-touch retention board / watchlist (ALL accounts, ranked)378```379action_board[]: { rank, account_id, risk_level, primary_action, current_arr,380 expansion_pipeline, overdue_balance, next_touch_due_date, reason_codes[] }381segment_summary: { strategic_accounts, enterprise_accounts, arr_at_risk,382 open_expansion_pipeline, net_revenue_exposure }383followup_calendar: { collections_followup, technical_recovery, renewal_save, executive_qbr, nurture_monitor }384policy_codes: { risk_model_code, arr_source_code, support_hygiene_code, action_priority_code,385 board_sort_code, exposure_formula_code, calendar_policy_code }386```387- Return **all** supplied accounts ranked (§5.3). `expansion_pipeline` per account (§3.6).388- `next_touch_due_date` per §7; `null` for `no_action`.389- segment_summary, exposure per §6.390391---392393## 10. Solver execution checklist3941. Parse the prompt for: assessment date, analysis months, A/R as-of date, account list,395 output archetype, and any supplied follow-up due date.3962. For each account: fetch account, metrics, tickets, nps, latest posted billing snapshot (≤397 assessment date), ar-aging (as-of date), and opportunities (window).3983. Derive `current_arr` (REV-4), `overdue_balance` (RCP-7), `clean_ticket_count` (SUP-8),399 `latest_nps`, `expansion_pipeline` (PW-6).4004. Compute RS-6 signals → risk_score → risk_level → primary_action → reason_codes (ordered).4015. Rank (§5.3), build summary/exposure (§6), calendar (§7), churn block (§8) per archetype.4026. Apply precision (§2) and emit exact policy codes (§1). Return JSON only.