Observation, filed for first-touch grading (not queued).
customer_churn_signals (src/reports/churn.report.ts) builds its three early-warning blocks on is_active + last_activity_date + tier + closed-lost windows. crm_account.health_score — declared as "CSM-maintained health indicator" with at_risk / churning options, and deliberately kept human-curated (confirmed a designed keep in the decorative-field sweep, #1182) — is read by no block.
So the CSM hand-maintains a churn signal the churn report ignores. Either:
Verified consumers of the activity chain are real (event/task hooks and contact actions all write last_activity_date), so the report itself runs on live data — this is a coverage gap, not a broken report.
Re-check: grep -n "health_score" src/reports/churn.report.ts (0 hits as of 2342811).
Observation, filed for first-touch grading (not queued).
customer_churn_signals(src/reports/churn.report.ts) builds its three early-warning blocks onis_active+last_activity_date+tier+ closed-lost windows.crm_account.health_score— declared as "CSM-maintained health indicator" withat_risk/churningoptions, and deliberately kept human-curated (confirmed a designed keep in the decorative-field sweep, #1182) — is read by no block.So the CSM hand-maintains a churn signal the churn report ignores. Either:
health_score in ['at_risk','churning']accounts — the cheapest way to give the field a real consumer and close its declared≠consumed gap; orhealth_score's keep-verdict in Decorative-field sweep: enforce-or-remove every declared field with zero business consumers #1182 weakens and it should be re-examined there.Verified consumers of the activity chain are real (event/task hooks and contact actions all write
last_activity_date), so the report itself runs on live data — this is a coverage gap, not a broken report.Re-check:
grep -n "health_score" src/reports/churn.report.ts(0 hits as of 2342811).