# Reply Rate Engine ## Problem Current MVP replies to **every** matched warmup email (~100%). Real humans and reputable warmup tools reply to a **fraction** of mail (typically 30%, max recommended 45%). ## Configuration | Parameter | Default | Min | Max recommended | |-----------|---------|-----|-----------------| | `replyRatePercent` | 30 | 10 | 45 | Set per `WarmupCampaign`. Hard cap at 45% in validation. ## Semantics **Reply rate** = of warmup emails received by the primary (or sent to primary from satellites), the percentage that receive an AI-generated reply that day. Not the same as cold-email response rate — this is warmup engagement simulation only. ## Algorithm ### On rescuer cycle (per received warmup message) ``` function shouldReply(campaign, message, statsToday): target = campaign.replyRatePercent actual = statsToday.replies / max(statsToday.received, 1) * 100 if actual >= target: return false // quota met for today // Probabilistic: bias toward hitting target by end of day remaining = statsToday.received - statsToday.replies needed = ceil(statsToday.received * target/100) - statsToday.replies probability = needed / max(remaining, 1) return random() < probability ``` ### Properties - Deterministic enough to converge on daily target - Stochastic enough to avoid fixed patterns - Never exceeds 45% even if configured wrong (clamp) ## What still happens at 0% reply (for a given message) Even when not replying: 1. Spam rescue still runs (move to inbox, flag) 2. Mark as read 3. Log status may be `complete` without `replied` — new status: `engaged_no_reply` ### Extended WarmupStatus enum | Status | Meaning | |--------|---------| | sent | Delivered from satellite | | delivered | Confirmed in primary inbox (optional) | | rescued_from_spam | Moved from spam folder | | engaged_no_reply | Read/flagged, no reply sent | | replied | Reply sent | | complete | Thread closed | ## Reply direction | Step | From | To | |------|------|-----| | 1. Warmup send | Satellite | Primary | | 2. Reply (if sampled) | Primary | Satellite | Maintains realistic thread: primary appears engaged with incoming mail. ## Daily reply budget ``` plannedReplies = ceil(plannedSends * replyRatePercent / 100) ``` Shown on dashboard: `replies today: 8/12 planned` ## Edge cases | Case | Handling | |------|----------| | All messages arrive at 23:59 | Spread replies across rescuer cycles | | Reply SMTP fails | Retry once; log error; don't count toward rate | | Primary in error | Pause replies; satellites may still send | | Satellite in error | Exclude from pool; rebalance | ## ESP rationale 100% reply rate on automated mail is a detection signal. Variable reply rate + rescue without reply on some messages mimics real mailbox behavior more closely.