# Drip — Demo Status & Production TODO

**This is a working DEMO, not a production app.** The architecture, data model, UI,
and orchestration logic are real and tested (88 automated tests). What is *simulated*
is everything that would touch the outside world: sending email, tracking opens, and
receiving form events. Those are deliberately isolated behind clean "seams" so going
live is a set of adapter swaps, not a rewrite.

This doc is the honest map of **what's real**, **what's faked for the demo**, and the
**to-do list** to make it production-ready.

---

## ✅ Real & working (safe to show)

- **Data model + store** — Campaign / Journey / Step / Email / Contact / Enrollment /
  Send / Event, persisted in SQLite (default) or **DynamoDB** (`DRIP_BACKEND=dynamo`,
  real boto3 code, needs AWS creds + `drip dynamo-init`).
- **Campaign structure** — any # of tracks / emails / per-email day-offset; add/remove/
  edit; the authoritative save + live-edit guard.
- **Copywriter** — on-brand copy from a brief (**LLM-authored via Claude
  `claude-opus-4-8` by default** when a key is set, with a deterministic composer
  fallback offline), compliance linting, per-email edits, subject ideas,
  borrowing winning copy from past campaigns.
- **The Manager / orchestration** — portfolio view, launch/pause, run-all, clone/re-roll.
- **Frequency governor** — the global "1 email/person/day" cap with per-campaign
  defer/suppress. This is real logic and fully tested.
- **Analytics + learnings + export** — computed from the event stream; CSV/JSON export.
- **The assistant** — **Claude-driven** (`claude-opus-4-8`) when a key is present:
  a Tool Runner that understands, plans, and calls the same handlers as tools,
  with multi-turn memory. Falls back to the deterministic NL agent (and that's
  the whole engine offline / `DRIP_LLM=0`).
- **Customer categories & tracks** — contacts carry a category (prospect / in-process /
  closed-loan / declined); enrollment routes each to the right journey; the Customers
  page groups/filters by category.
- **Customer history** — a per-person timeline (enrolled, sent, opened, clicked, form
  completed, promoted, exited) with a "simulate a form/CRM event" demo control.
- **Journey branching** — Incomplete → *complete the form* → exit + promote to In
  Process; In Process → *"my refi is complete"* → exit. This is the reference PDF's
  core loop, now actually exercised in the demo via the form-event simulator.

## ⚠️ Simulated for the demo (needs wiring for production)

| Area | What the demo does | What production needs |
|---|---|---|
| **Sending email** | `MockProvider` records the message; **no email is sent**. | Implement `EmailProvider` for SendGrid/SES/Resend (`dripcore/providers/`). |
| **Opens / clicks** | Fabricated by a decay curve (`providers/mock.py`) so analytics look realistic. | The ESP's tracking pixel + click-wrap + an inbound **webhook** calling `dripcore.events.ingest`. |
| **Form events** (started / completed) | **Simulated.** Contacts have a **category** (prospect / in-process / closed-loan / declined) and are routed to the matching **track**. Completing the form is simulated — manually per-customer (Customers → click a person → *Completed the form*) or probabilistically during a run — which **stops their Incomplete emails and promotes them to In Process**, exactly like the PDF. Each customer has a full **history** timeline. | Replace the simulator with a real **webhook** from your website's form/CRM that calls `apply_form_event()` / `enroll_by_segment()` on the same events. |
| **Scheduling** | `run` / `run all` fast-forward a **virtual clock** (14 days in seconds). | Call `Runtime.step_campaign()` from a real daily cron (e.g. 9am), not the virtual loop. |
| **Copy generation** | **Wired (not simulated).** Claude (`claude-opus-4-8`) writes the copy by default when `ANTHROPIC_API_KEY` is set (`LLMEngine`, structured outputs); the deterministic composer is the automatic offline fallback. | Nothing to wire — it's live. Optionally add rate-limit/cost controls; `DRIP_LLM=0` forces the composer. |
| **Tracking / unsubscribe links** | Point at a fake host (`track.drip.local`), never served; **Unsubscribe does nothing**. | Real tracking + redirect host, and an unsubscribe handler that sets `contact.unsubscribed`. |
| **Sender identity / design** | Stored + shown in the preview. | Passed to the ESP on real sends (already on `RenderedMessage.from_name/from_email`). |
| **Disclaimer text** | The current legal block is a **placeholder** (ends `0626-5038350`). | Confirm final copy with Legal. |
| **"People" counts, engagement** | Derived from the simulated run. | Become real once sending + webhooks are live. |

## 🚫 Not started (out of scope for the demo)

- Auth / users / roles / multi-tenant isolation beyond the `owner` field.
- A/B testing splits, send-time optimization, deliverability/bounce handling.
- Real audience segmentation & querying (segments are a free-text field today).
- Rate limiting, retries, idempotency on real sends; audit logs.
- Image hosting / a drag-and-drop block email builder (copy is markdown today).

---

## Production TODO (rough order)

1. **Real ESP provider** — implement `EmailProvider.send` for your ESP; wire the
   inbound webhook to `events.ingest`. (Biggest unlock: real sends + real analytics.)
2. **Form/event ingestion** — webhook endpoints that `enroll()` on form-start and
   `mark_form_complete()` on submit, so triggers/branches/conversions become real.
3. **Real scheduler** — a daily cron calling `Runtime.step_campaign()` per campaign
   (replace the virtual-clock `run`/`run_all` loops for live sending).
4. **LLM copy — DONE.** `LLMEngine._call` is wired to Claude (`claude-opus-4-8`)
   and is the **default** engine whenever a key is present; the composer is the
   offline fallback (`DRIP_LLM=0` forces it). See the README "LLM copywriting".
5. **Unsubscribe + suppression** — serve the unsubscribe link; honor `unsubscribed`;
   global suppression list.
6. **Auth & multi-tenant** — real accounts, not just an `owner` string.
7. **Deliverability** — bounces, complaints, ret/backoff, per-domain throttling.
8. **Finalize the disclaimer** with Legal; per-brand disclaimers.
9. **DynamoDB hardening** — provisioned throughput/on-demand, IAM least-privilege,
   backups; load-test the single-table access patterns.
10. **Segmentation & audience querying**; A/B testing; image/block editor.

---

## Running the demo

```bash
python3 -m dashboard.server          # http://127.0.0.1:8787
python3 cli.py demo-data             # clear + seed a full sample portfolio
```
In the UI, open **Ask Drip** and try: *"load the demo"*, *"new drip"* (it asks how to
shape it), *"run all campaigns"*, *"what worked?"*, *"who's over-emailed?"*.
