Onboard a list
Produce a census, a versioned selection policy, and a private cohort manifest. Import or switch sending only within the taskβs authority.ΒΆ
For routine broadcasts to an onboarded audience, use the projectβs sending procedure.ΒΆ
This procedure defines a migration job. It does not install an importer.ΒΆ
Start with the source and authority
Identify the source account, destination tenant, email topics, available credentials, and approved operations. Reuse an approved cohort choice. Ask only for missing decisions that affect the result or authorize a new live action.ΒΆ
Name the facts and their writer:ΒΆ
- The source owns contact, consent, stop, activity, and automation facts until cutover.ΒΆ
ReadListSnapshotcaptures those facts through a job-shaped port27. Keep provider vocabulary inside adapters27.ΒΆ- The pure planner derives counts, intersections, and cohort selection.ΒΆ
- Destination contact and stop services own imported state. Profile imports cannot lift stops.ΒΆ
- Policy and capture receipts identify the inputs. A private manifest records exact tenant-scoped recipients and matched rule ids.ΒΆ
Read contracts and policy when defining an adapter27 or import policy. Read the count-only example when explaining overlaps.ΒΆ
1. Capture a read-only inventory
Inventory broadcasts, journeys, welcome flows, transactional/API callers, scheduled jobs, and historical link services. Measure actual sends over an explicit window. Enabled does not mean sending. A welcome flow may be the only active sender.ΒΆ
Capture every page of contacts, addresses, topic/channel/global preferences, and permanent stops. Include stopped addresses without profiles.ΒΆ
Reconcile unique keys, terminal cursors, export row totals, and available before/after counts. Record observation times and concurrent-change gaps. Claim a transactional snapshot only when the source provides one.ΒΆ
Keep recipient rows, signed URLs, credentials, and source cursors private. Reports contain aggregates and safe source receipts. Limit read credentials to the source account.ΒΆ
Use read-only export/search jobs only when the task authorizes them. They must not alter customer state.ΒΆ
2. Establish the stop floor
Separate authoritative unsubscribes, complaints, hard-bounce stops, and provider denials from delivery events and heuristics. A bounced delivery alone does not prove a permanent hard-bounce stop.ΒΆ
Resolve identity conservatively. Preserve stops without profiles and their original reasons. Never rewrite typo addresses or collapse plus tags. Use only documented identity normalization. Report ambiguity.ΒΆ
Before enabling destination sends, durably import and read back the complete stop floor. Profile imports and consent defaults must not lift it. Keep later source and legacy-link stops flowing until continuity is proven.ΒΆ
3. Measure each proposed rule
Decode policy data with Effect29 Schema25. Pin external lists by source, revision/date, and digest. Store thresholds, domain/local-part sets, windows, and actions in that policy.ΒΆ
Measure each rule against each original pool. Report independent matches, pairwise overlaps, exclusive matches, the union, and an ordered waterfall. Show newly removed and remaining counts. Never add independent counts.ΒΆ
Keep observations separate from person-level conclusions:ΒΆ
- Disposable-list membership supports a cohort-quarantine proposal. It does not identify a bot person.ΒΆ
- Query DNS once per distinct domain during research. Record resolver view, observation time, and failures. Empty MX can permit address-record fallback. Explicit null MX is different. Revalidate time-sensitive evidence before applying a cut.ΒΆ
- Role mailboxes, typo-like domains, digit runs, entropy, and creation bursts can match real readers. Keep them review-only unless the owner chooses a bounded cohort restriction.ΒΆ
- Search available positive login, purchase, and learning evidence before counting an activity-absence proxy. Missing history remains unknown.ΒΆ
- Distinguish human metrics, raw metrics, and first versus repeat timestamps. Security scans and privacy prefetch are not human engagement. Fast events do not prove bot people.ΒΆ
Do not invent missing evidence or measured precision. Label any analyst precision guess unvalidated. Keep it separate from measurements.ΒΆ
4. Choose and pin the cohort
Present alternatives and one recommendation. Explain exact counts, bounds, gaps, and tradeoffs.ΒΆ
When engagement is required, use positive human evidence. Explain which readers first-event-only history can miss. Do not substitute raw opens when human evidence is unavailable.ΒΆ
Let the owner choose unresolved engagement windows and optional restrictions. Pin source receipts, base manifest, policy digest, cohort digest, and destination tenant.ΒΆ
Dry-run and apply use the same pure planner and policy bytes. Changed policy or widened eligibility needs a new selection. Newly discovered stops always narrow it.ΒΆ
Read verification properties before accepting the planner. Cover the deduplicated waterfall and the weak-signal boundary.ΒΆ
5. Dry-run and prepare the destination
Reuse owned destination contracts. When building automation, expose census, planning, and import as typed capabilities. Project CLI, HTTP, and agent tools from those contracts.ΒΆ
Dry-run exact identities, stop matches, ambiguous records, counts, and value-level readback. Use tenant-scoped idempotency keys9 and durable resume checkpoints.ΒΆ
For an authorized import, write stops first, then profiles. Do not discard rejected stop rows or mark partial work complete.ΒΆ
Prepare sender ownership, authentication, content, tenant-scoped routing, consent basis, unsubscribe handling, and historical-link continuity. Verify real bindings23 with authorized synthetic recipients. Preserve other mail systems on shared domains.ΒΆ
6. Make the authorized switch
For a hard switch, stop every old sending path. Prove that stop before enabling destination sends. Do not invent a parallel sending period.ΒΆ
Capture final contact, consent, and stop deltas through a recorded boundary. Describe and close consistency gaps before claiming completion.ΒΆ
Keep old unsubscribe/tracking links working when needed, with old sending disabled. Persist new stops before acknowledging them. Enforce stops at final dispatch. An eventually consistent cache alone cannot protect that boundary.ΒΆ
Enable only the approved cohort. Reconcile accepted, completed, failed, and unknown provider outcomes. Never blindly retry an uncertain send.ΒΆ
On harm, stop the affected destination path. Restart the old sender only when that recovery action is authorized.ΒΆ
Use a persisted lifecycle: capture β measure β choose β prepare β switch β verify. Put retries, deferrals, cancellation, and resume in XState. Run work through Effect actors under the projectβs pins. A delay proves neither failure nor completion.ΒΆ
7. Prove and report the result
Read back stops, exact profile values, cohort counts, and both dispatch controls. Prove a stopped synthetic address cannot regain eligibility through signup or profile import. Prove historical unsubscribe links durably stop later dispatch.ΒΆ
Verify topic behavior and tenant isolation. Use authorized delivery receipts for real-send claims.ΒΆ
After cutover, measure later eligibility from destination-owned human events. State which human events are recorded. A clock window passing does not justify widening the cohort.ΒΆ
Report analysis-ready, import-verified, and sending-verified separately. Include input digests, exact/bounded sections, overlaps, rejected records, checkpoints, gaps, and the next authorized move.ΒΆ
Keep raw manifests private. Scan shareable artifacts for recipient patterns.ΒΆ
Register an event watch for unfinished external work. Act when its result arrives. Reconciled receipts prove analysis. A report alone does not prove an import or send completed.ΒΆ
Last changed 2026-10-24 in d438f78. Source on GitHub. Change log.