fix(hc warmup): only mail slightly-overdue providers (deliverability)

Mailing heavily-overdue NPIs (months/years past due) risks hitting practices
that have closed, merged, or abandoned the inbox -> hard bounces, which are the
fastest way to wreck a warming IP's reputation. The warmup now restricts the
reval_overdue selector to an inclusive [HC_OVERDUE_MIN, HC_OVERDUE_MAX] window
(default 1-90 days) and the OIG 'any' selector likewise excludes heavily-overdue
and dropped-off-list rows. On the current cohort this trims the overdue audience
178->96 and the OIG audience 399->317, holding out the stale long tail
(181-365d + 366d+). upcoming/active providers are unaffected.
This commit is contained in:
justin 2026-06-08 03:27:22 -05:00
parent c79a7715e1
commit feb677f6ce
2 changed files with 37 additions and 2 deletions

View file

@ -4,4 +4,7 @@
# America/Chicago). Delivery is throttled by pw-hc-rampcap's sliding-window
# cap, sent ONLY via the hc HOT stream (.107-.109). Segment audiences are
# source-grounded: a segment with no matching providers simply imports nobody.
# Deliverability guard: warmup only mails SLIGHTLY-overdue providers (1-90 days
# by default, HC_OVERDUE_MIN/MAX) -- recently-lapsed practices still have live
# inboxes; heavily-overdue ones likely bounce and burn the warming IPs.
0 7 * * 1-5 deploy cd /opt/performancewest && HC_VERIFIED_CSV=/opt/performancewest/data/hc_warmup_nongoogle.csv python3 scripts/build_healthcare_campaigns_cron.py --start-campaign >> /var/log/pw-hc-campaign.log 2>&1

View file

@ -67,6 +67,22 @@ ACTIVE_SEGMENTS = os.getenv(
"revalidation_overdue,oig_screening,nppes_outdated,npi_reactivation,compliance_bundle",
).split(",")
# Warmup deliverability guard: only mail SLIGHTLY-overdue providers. A practice
# that lapsed recently is almost certainly still operating with a live inbox; one
# that is many months/years overdue has likely closed, merged, or abandoned the
# address, so its mail bounces -- and bounces are the fastest way to wreck a
# warming IP's reputation. Window is inclusive [MIN, MAX] days overdue.
WARMUP_OVERDUE_MIN = int(os.getenv("HC_OVERDUE_MIN", "1"))
WARMUP_OVERDUE_MAX = int(os.getenv("HC_OVERDUE_MAX", "90"))
def _overdue_days(r: dict):
v = (r.get("days_overdue") or "").strip()
try:
return int(v)
except ValueError:
return None
def warmup_day() -> int:
try:
@ -197,14 +213,30 @@ def row_matches(seg_key: str, r: dict) -> bool:
status = (r.get("reval_status") or "").strip().lower()
excluded = (r.get("leie_excluded") or "").strip() not in ("", "0", "false")
optout = (r.get("optout_ending") or "").strip() != ""
if sel == "reval_overdue": return status == "overdue"
if sel == "reval_overdue":
# Only SLIGHTLY-overdue: recently-lapsed practices are still active with
# deliverable inboxes. Heavily-overdue ones likely bounce and burn the
# warming IP's reputation, so we hold them out of the warmup window.
if status != "overdue":
return False
od = _overdue_days(r)
return od is not None and WARMUP_OVERDUE_MIN <= od <= WARMUP_OVERDUE_MAX
if sel == "reval_upcoming": return status == "upcoming"
if sel == "leie_or_deactivated":
# Reactivation targets: flagged excluded, OR no longer on the reval list
# (a strong deactivation proxy once revalidation lapses).
return excluded or status in ("not_on_list", "no_reval_flag")
if sel == "optout_ending": return optout
if sel == "any": return True
if sel == "any":
# OIG screening applies to any billing practice, but for warmup we still
# exclude the likely-undeliverable: providers heavily overdue (stale) or
# already dropped off the reval list. Recently-lapsed and upcoming stay.
if status in ("not_on_list", "no_reval_flag"):
return False
od = _overdue_days(r)
if status == "overdue" and (od is None or od > WARMUP_OVERDUE_MAX):
return False
return True
return False