Home / Ops / More in this area

Affirmology - Automated Mirror Drip (spec v1)

Updated Jul 16, 2026 · Affirmology_MirrorDrip_Spec_v1.md

Summary. Goal. A few days after a person receives their Origin Soul Song (in the normal demo flow), the system automatically renders and delivers their Mirror reading - no manual batch, no one remembering to press a button. It is the built-in re-engagement beat: the fi

Affirmology - Automated Mirror Drip (spec v1)

Goal. A few days after a person receives their Origin Soul Song (in the normal demo flow), the system automatically renders and delivers their Mirror reading - no manual batch, no one remembering to press a button. It is the built-in re-engagement beat: the first "how did it know that?" gift arrives, then a deeper one lands unprompted while they are still thinking about the first.

Applies to the DEMO process (every real demo-taker), not just beta.


1. Behavior in one line

On Origin Soul Song delivery → schedule the Mirror for ~3 days later → a scheduled worker renders it from the person's stored Profile and emails it (link to their page), once, idempotently, honoring holds and opt-outs.

2. The trigger

3. The delay

4. Durable queue (survives restart/redeploy)

A new table (or reuse the consumer DB), e.g. mirror_drip: | field | meaning | |---|---| | id | pk | | email / profile_id | who | | origin_job_id | the Origin render it followed | | created_at | when scheduled | | due_at | created_at + DELAY_DAYS | | status | pendingsent / held / skipped / error | | mirror_job_id | the render, once made | | attempts, last_error | for self-heal |

Durable so a "drip opened days later" survives restarts (mirrors the pattern already noted in db_models.py). No in-memory scheduling.

5. The worker (scheduled, like growth_cron)

A Render Cron Job (or a boot poll loop, like chant_runner) runs on a cadence (hourly is plenty): 1. Select status=pending AND due_at <= now. 2. For each, run the gate checks (§6). If gated → mark held/skipped with reason, move on. 3. Render the Mirror from the stored Profile (reuse build_mirror_brief + the existing Mirror render path; length target 12 - 15 min / ~2200 words per the reading-render length rule). One at a time / paced (Fish + Sonnet cost). 4. On success: attach to the person's page, send the Mirror email (§7), mark sent. 5. On failure: increment attempts, store last_error, leave pending for retry with backoff; after N attempts mark error and surface it (no silent drops - self-healing loop principle).

6. Gate checks (do NOT send if…)

7. Delivery

8. Cost + safety

9. Rollout

  1. Build the table + the schedule-on-delivery write + the worker, with MIRROR_DRIP_ENABLED=false.
  2. Verify end-to-end on a test person (schedule with a 5-minute delay via a debug override, confirm one send, confirm dedupe + gates).
  3. Flip MIRROR_DRIP_ENABLED=true when the Mirror rebuild is approved and .mirror_hold is lifted.
  4. Backfill (optional): for people who got their Origin in the last few days but no Mirror, enqueue them.

10. Open choices for Jeff