39 lines
1.4 KiB
Markdown
39 lines
1.4 KiB
Markdown
# Sprint 1.4 — Reply Rate Engine
|
|
|
|
Probabilistic reply sampling so warmup mimics real mailbox engagement (~30% default, max 45%).
|
|
|
|
## What shipped
|
|
|
|
- **`engaged_no_reply`** status on `WarmupStatus` enum
|
|
- **`reply-rate.service.ts`** — `shouldReply()`, `getTodayReplyStats()`, simulation helper
|
|
- **Campaign rescuer** samples replies; non-replied messages still get read/flagged/spam-rescue
|
|
- **Hard daily cap** — skips reply when `actualReplies >= plannedReplies`
|
|
- **Spam counters** — `spamDetected` / `spamRescued` increment on rescue
|
|
- **`WarmupLog.placement`** — `inbox` or `spam`
|
|
- **`POST /api/jobs/rescuer/run`** — manual rescuer trigger
|
|
|
|
## Algorithm
|
|
|
|
Per [`docs/07-reply-rate-engine.md`](../07-reply-rate-engine.md):
|
|
|
|
1. If daily reply quota met → skip
|
|
2. If actual rate ≥ target → skip
|
|
3. Else probabilistic bias: `probability = needed / remaining`
|
|
|
|
## Manual QA
|
|
|
|
```bash
|
|
# Start campaign, dispatch sends, then run rescuer repeatedly
|
|
curl -X POST http://localhost:3000/api/jobs/dispatcher/run -H "x-api-key: $API_KEY"
|
|
curl -X POST http://localhost:3000/api/jobs/rescuer/run -H "x-api-key: $API_KEY"
|
|
|
|
# Check mix of complete vs replied logs
|
|
curl "http://localhost:3000/api/logs?campaignId=<id>" -H "x-api-key: $API_KEY"
|
|
```
|
|
|
|
Expect some logs with `engaged_no_reply` → `complete` and reply rate trending toward `replyRatePercent`.
|
|
|
|
## Key files
|
|
|
|
- `src/services/reply-rate.service.ts`
|
|
- `src/jobs/rescuer.job.ts`
|