feat: real email and sequences, delivery sync, schedules, notifications, collaboration features #12

Merged
KofTwentyTwo merged 5 commits from feature/platform-reuse into develop 2026-10-14 18:34:58 +00:00
KofTwentyTwo commented 2026-10-14 18:32:25 +00:00 (Migrated from github.com)

Summary

P4 part A (platform reuse) of the qbit-crm 0.2.0 design:

  • Send Email (contact action):
    • Merge fields: contact, company, owner and the unsubscribe link.
    • Sends through CrmEmailTransport (qbit-email first, then core messaging) with an idempotency key.
    • Records a crm_email_message row and an EMAIL activity.
    • Skips, with a warning line, contacts marked do-not-email, contacts with no address, and sends when no transport is configured.
  • Sequences send for real:
    • Only active sequences run, so Pause is honored.
    • EMAIL steps go through the MARKETING category, and each enrollment is gated on checkEmailEligibility.
    • The crm.sequence.failing and crm.email.unavailable alerts raise and resolve.
  • New background processes:
    • crm_sync_email_delivery pulls qbit-email status. Bounces and complaints set do-not-email and record a withdrawn consent.
    • crm_expire_consents expires unconfirmed double opt-ins after 14 days.
  • Schedules: sequences every 5 min, scoring hourly, delivery sync every 15 min, and consent expiry daily. Hosts control them with withSchedulerName and withSchedulesEnabled.
  • Lead scoring:
    • The DAYS_AGO_GT and DAYS_AGO_LT decay operators are implemented.
    • Recalculation is incremental since the last run, with a full pass daily.
    • Company reads are batched, removing the N+1 queries.
  • Auto-assign runs on contact and deal insert and sends a crm.lead.assigned notification.
  • Deal notifications: crm.deal.won and crm.deal.rotting, deduplicated per stage.
  • Collaboration: core RecordFeaturesMetaData declarations (tags, comments, attachments, follow, timeline, recents, favorites, tasks, assignment) and ownership on ownerUserId.

Not in RC.6, so deferred to RC.7: the DurableProcessMetaData and DurableJobsMetaData declarations. The scheduled runs stay plain processes until then. qbit-email RC.3 exposes no open, click or bounce timestamps, so those dates stay empty.

Codex implemented this; Claude reviewed it.

Test plan

  • ./mvnw verify: 430 tests, 0 failures; JaCoCo 91.5%; checkstyle and guardrails pass

🤖 Generated with Claude Code

## Summary P4 part A (platform reuse) of the qbit-crm 0.2.0 design: - **Send Email (contact action):** - Merge fields: contact, company, owner and the unsubscribe link. - Sends through `CrmEmailTransport` (qbit-email first, then core messaging) with an idempotency key. - Records a crm_email_message row and an EMAIL activity. - Skips, with a warning line, contacts marked do-not-email, contacts with no address, and sends when no transport is configured. - **Sequences send for real:** - Only active sequences run, so Pause is honored. - EMAIL steps go through the MARKETING category, and each enrollment is gated on `checkEmailEligibility`. - The `crm.sequence.failing` and `crm.email.unavailable` alerts raise and resolve. - **New background processes:** - `crm_sync_email_delivery` pulls qbit-email status. Bounces and complaints set do-not-email and record a withdrawn consent. - `crm_expire_consents` expires unconfirmed double opt-ins after 14 days. - **Schedules:** sequences every 5 min, scoring hourly, delivery sync every 15 min, and consent expiry daily. Hosts control them with `withSchedulerName` and `withSchedulesEnabled`. - **Lead scoring:** - The `DAYS_AGO_GT` and `DAYS_AGO_LT` decay operators are implemented. - Recalculation is incremental since the last run, with a full pass daily. - Company reads are batched, removing the N+1 queries. - **Auto-assign** runs on contact and deal insert and sends a `crm.lead.assigned` notification. - **Deal notifications:** `crm.deal.won` and `crm.deal.rotting`, deduplicated per stage. - **Collaboration:** core `RecordFeaturesMetaData` declarations (tags, comments, attachments, follow, timeline, recents, favorites, tasks, assignment) and ownership on `ownerUserId`. Not in RC.6, so deferred to RC.7: the `DurableProcessMetaData` and `DurableJobsMetaData` declarations. The scheduled runs stay plain processes until then. qbit-email RC.3 exposes no open, click or bounce timestamps, so those dates stay empty. Codex implemented this; Claude reviewed it. ## Test plan - [x] `./mvnw verify`: 430 tests, 0 failures; JaCoCo 91.5%; checkstyle and guardrails pass 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.