Skip to content

Commit e825cdd

Browse files
committed
Merged comms-email: communications slice 3, two years of bulk email (weekly campus newsletters, church-wide and ministry list emails) with Rock's send path and Mailgun webhooks for delivery, opens, clicks, bounces, and unsubscribes, and calibration part 7 graded in validation (decisions 149 to 151). All three packages pass verify on Rock 21.0.2
2 parents 6cafff7 + 0b87135 commit e825cdd

32 files changed

Lines changed: 2559 additions & 45 deletions

‎calibration/profiles/sample-church.json‎

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,19 @@
125125
"pctNotesWithMention": 6
126126
}
127127
},
128+
{
129+
"path": "communicationDetail.design",
130+
"reason": "Design choice. The measured data records no unsubscribes on recipient rows and too few bounces to shape, so these are designed (decision 131). Each person reads at their own rate by connection status, set so the sample church lands near the measured share of delivered emails opened. Unsubscribes are per 1,000 recipients of each kind of send: a newsletter, another list, and a church-wide send to someone on no list. A few active emails bounce during the window, and the rest of the measured inactive share was inactive before it (pctEmailsInactiveBeforeWindow). Sends with one link get all their clicks on it, so sends with more links give their first link a little less than the measured average.",
131+
"value": {
132+
"openRateByStatus": { "Leading": 0.94, "Engaged": 0.88, "Connected": 0.8, "Participant": 0.64, "Known": 0.62, "Prospect": 0.56 },
133+
"openFactorBySend": { "newsletter": 0.95, "other": 1.05 },
134+
"unsubscribesPerThousandRecipients": { "newsletter": 0.15, "list": 0.5, "churchWide": 40 },
135+
"pctEmailsBouncingIn24Months": 1.8,
136+
"pctEmailsInactiveBeforeWindow": 2.8,
137+
"pctClicksOnTopLinkOfMultiLinkSends": 74
138+
}
139+
},
140+
{ "path": "communicationDetail.saturation.byMedium.Email.mass.00", "value": 47, "reason": "Design choice. A campus newsletter holds every involved adult with an active email plus a third of the others (decisions 133 and 138), so about half of active adults get mass email in a month, where the measured church reached about one in six. The other buckets shift with the month a load is graded in, because church-wide sends cluster around Easter, the fall launch, and Christmas, so only the share with none is graded." },
128141
{ "path": "personNotes.detail.textLengthP10", "value": 45, "reason": "Design choice. Good notes say a little more than the measured ones (decision 78)." },
129142
{ "path": "personNotes.detail.textLengthP50", "value": 110, "reason": "Design choice. Good notes say a little more than the measured ones (decision 78)." },
130143
{ "path": "personNotes.detail.textLengthP90", "value": 260, "reason": "Design choice. Good notes say a little more than the measured ones (decision 78)." },

‎cast/families/dawson.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Kevin Dawson is divorced and has his kids every other weekend. They come when th
3131

3232
- Single dad with every-other-weekend attendance.
3333
- Irregular giver.
34+
- Left the Men list from its newest email: its breakfasts fall on weekends he has the kids (a communication list unsubscribe).
3435

3536
## Activity
3637

‎cast/families/watson.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Brandon and Kristen Watson come about once a month and give once a year, in Dece
2525

2626
- Year-end-only giver.
2727
- Occasional attenders with Known status.
28+
- Brandon is on no communication list and chose No mass emails from a church-wide email about three months ago (a Bulk unsubscribe).
2829

2930
## Activity
3031

‎cast/households/dawson.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"story": "Kevin Dawson is divorced and has his kids every other weekend. They come when the kids are with him.",
1111
"testPurposes": [
1212
"Single dad with every-other-weekend attendance.",
13-
"Irregular giver."
13+
"Irregular giver.",
14+
"Left the Men list from its newest email: its breakfasts fall on weekends he has the kids (a communication list unsubscribe)."
1415
],
1516
"address": {
1617
"type": "home",

‎cast/households/watson.json‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
"story": "Brandon and Kristen Watson come about once a month and give once a year, in December.",
1111
"testPurposes": [
1212
"Year-end-only giver.",
13-
"Occasional attenders with Known status."
13+
"Occasional attenders with Known status.",
14+
"Brandon is on no communication list and chose No mass emails from a church-wide email about three months ago (a Bulk unsubscribe)."
1415
],
1516
"address": {
1617
"type": "home",

‎configuration/communications.json‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,5 +89,21 @@
8989
"fallGroupLaunch": { "month": 9, "weekday": "Sunday", "week": 2 },
9090
"springGroupLaunch": { "month": 1, "weekday": "Sunday", "week": 3 },
9191
"serveSunday": { "month": 8, "weekday": "Sunday", "week": 4 }
92-
}
92+
},
93+
"$emailClientsComment": "The email clients people read on, as Mailgun reports them, with their shares. Rock stores each as an interaction device type, and Communication Detail charts opens by client. Each person reads mostly on one client.",
94+
"emailClients": [
95+
{ "name": "Apple Mail", "operatingSystem": "iOS", "clientType": "email client", "deviceType": "mobile", "weight": 38 },
96+
{ "name": "Gmail", "operatingSystem": "Android", "clientType": "mobile browser", "deviceType": "mobile", "weight": 18 },
97+
{ "name": "Outlook", "operatingSystem": "Windows", "clientType": "email client", "deviceType": "desktop", "weight": 12 },
98+
{ "name": "Apple Mail", "operatingSystem": "macOS", "clientType": "email client", "deviceType": "desktop", "weight": 9 },
99+
{ "name": "Chrome", "operatingSystem": "Windows", "clientType": "browser", "deviceType": "desktop", "weight": 9 },
100+
{ "name": "Outlook", "operatingSystem": "iOS", "clientType": "email client", "deviceType": "mobile", "weight": 5 },
101+
{ "name": "Apple Mail", "operatingSystem": "iPadOS", "clientType": "email client", "deviceType": "tablet", "weight": 5 },
102+
{ "name": "Yahoo Mail", "operatingSystem": "Android", "clientType": "mobile browser", "deviceType": "mobile", "weight": 4 }
103+
],
104+
"$castUnsubscribesComment": "Cast people whose stories include an unsubscribe, so tests can find a known case (decision 131). With a list, the person is on it and leaves it from the newest email sent to it at least daysAgo days before install day. With no list, the person is on no list and chooses No mass emails from the newest church-wide email at least daysAgo days before install day.",
105+
"castUnsubscribes": [
106+
{ "person": "DE3E4484-6818-4919-978A-A2A2EBEFC64A", "name": "Kevin Dawson", "list": "men", "daysAgo": 0 },
107+
{ "person": "21FA1DBE-3DA0-4ED0-A7A5-97D447F517CA", "name": "Brandon Watson", "daysAgo": 100 }
108+
]
93109
}

‎docs/communications.md‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Communications
22

3-
How email, text, and push history will look in the sample data, and why. The setup is built: the texting numbers, the communication lists, the branded template, and the message library. The sends are still a plan. The decisions are 90 to 96, 131 to 133, and 137 to 141 in [Decisions](decisions.md).
3+
How email, text, and push history will look in the sample data, and why. The setup and bulk email are built: the texting numbers, the communication lists, the branded template, the message library, and two years of mass email. Texts, one-to-one messages, push, and step messages are still a plan. The decisions are 90 to 96, 131 to 133, 137 to 141, and 149 to 151 in [Decisions](decisions.md).
44

55
## The goal
66

@@ -77,15 +77,17 @@ Messages come from a hand-written library in [content/communications/](../conten
7777

7878
## Bulk email
7979

80-
Bulk email is where the very large sends come from.
80+
Bulk email is where the very large sends come from (decision 149). On Full 7 it is about 1,500 sends and 1.3 million recipient rows over two years. Core 1 has about 330 sends and 20,000 recipients.
8181

82-
- **Who sends what.** Each campus sends its weekly newsletter to its list. The church sends seasonal emails (Easter, Christmas, year-end giving) to everyone, and ministries send to their own lists.
82+
- **Who sends what.** Each campus sends its weekly newsletter to its list every Thursday at 6 a.m. The church sends seasonal emails (Easter, Christmas, year-end giving, the campaign, baptism Sunday, group launches) once per campus to that campus's involved adults with an email, the segment a church's active adults data view gives. Ministries send to their own lists. Student Camp and mission trip emails go to students and their parents.
83+
- **The calendar.** Each library email names an occasion, and `loader/generation/CommunicationCalendar.cs` dates it from the install day with the same rules the rest of the data uses: baptism Sundays, camp, and the mission trips from the steps, Commitment Sunday from the campaign, and the class schedules.
8384
- **Bulk flag and medium.** Mass sends are flagged `IsBulkCommunication` and have a concrete type (Email, SMS, or Push), so the Saturation report counts them by default (decision 132). A few leader sends to small lists stay unflagged, so the report's bulk-only toggle shows a difference, and a small share of sends use recipient preference. The measured data flags only about 10% of mass sends; the sample church shows the better practice.
84-
- **Sender, reviewer, topic.** Each send has a Cast staff sender, a reviewer when Rock's approval would require one, and a topic.
85+
- **Sender, reviewer, topic.** Daniel Peak sends and approves the church's emails. Pete Foster's and Isaac Silva's are approved by Daniel. Every send has its topic, the branded template, and the from line in configuration/communications.json.
8586
- **Recipients.** Each recipient row has its own `SendDateTime`, which the Unsubscribe report reads.
86-
- **Delivery.** Most recipients are Delivered or Opened. People whose email is already inactive in the people data are the ones whose sends fail, so the two always agree.
87-
- **Opens and clicks.** Opens follow the measured open rate and come mostly within a day of the send. Clicks come only from people who opened, at the measured click-through rate.
88-
- **Unsubscribes and spam.** See Unsubscribes. Spam complaints are rare.
87+
- **Delivery.** Recipients follow Rock's send path. Rock refuses an inactive email, "Do not email", and "No mass emails" on a bulk send, with its own status notes and no send time. Everyone else is sent through Mailgun and confirmed delivered, and Rock's "Sent" line goes on their History. Email recipients keep no `SentMessage`, because Rock's email transport never stores one.
88+
- **Bounces.** About 1.8% of active emails bounce once during the window. That send fails with the bounce, Rock makes the email inactive with its bounce note and the History lines, and later sends fail as not active.
89+
- **Opens and clicks.** Each person reads at their own rate, higher for Leading and Engaged people, so opens land near the measured rate and mostly within a day. Clicks come only from people who opened, at the measured rate, mostly on the email's first link. Each open and click is an interaction with its own session and an email client's device type, as Rock's Mailgun webhook writes them, so Communication Detail charts opens by client.
90+
- **Unsubscribes.** See Unsubscribes. There are no spam complaints on recipients, because Rock 21's email transports never record one there.
8991

9092
## Unsubscribes
9193

@@ -95,7 +97,8 @@ Unsubscribes are designed, not measured (decision 131). The measured data record
9597
- **When.** Most come the same day as the email or the next, and some come in the last 6 months, so the report's default window has data.
9698
- **Which sends.** Large newsletters draw more unsubscribes than small ministry sends.
9799
- **Texted STOP.** A few people text STOP. Their newest text gets a level All unsubscribe, and texting is turned off on their phone, so later texts leave them out.
98-
- **The Cast.** A couple of Cast people unsubscribe in ways their stories explain, such as leaving the Men's list after moving to a Saturday group, so tests can find a known case.
100+
- **Who can reach which level** (decision 150). A list email unsubscribe leaves the person Inactive in that list. A church-wide email can set "No mass emails" only for someone on no active list, so nobody with that preference is ever left on a list. Rock steps a preference down one level at a time, and a bulk email never reaches someone on "No mass emails", so email unsubscribes almost never reach "Do not email". Generated people therefore start the window with the measured share of "Do not email", and of inactive emails.
101+
- **The Cast.** Kevin Dawson leaves the Men list from its newest email, because its breakfasts fall on the weekends he has his kids. Brandon Watson is on no list and chose "No mass emails" from a church-wide email about three months ago. Every load checks both.
99102
- **Calibration.** Part 7 also counts a whole year of unsubscribes, with their levels and timing, where Rock 19's unsubscribe index exists. In the measured data, recipient rows hold texted STOPs (about 0.34 per 1,000 mass-text recipients, mostly the same day) but no email unsubscribes, so the STOP rate and the timing are measured, and the email levels and rates are designed.
100103

101104
## Message load per person
@@ -166,10 +169,11 @@ Volume is designed, as for prayer and notes: the sample church shows what good c
166169

167170
## How it is checked
168171

172+
- **Bulk email invariants (built):** every communication is Approved with its send time and date key, no recipient is Pending or Sending, recipient statuses agree with their dates and notes, every open and click belongs to an opened recipient after its first open, every unsubscribe matches the person and the list, and every sent email has its "Sent" History line.
169173
- **Setup invariants (built):** no list member chose "No mass emails" or "Do not email", a newsletter holds only its campus's adults with an active email, the Volunteers and Small Group Leaders lists match the teams and groups, and every texting number is reserved, active, and no loaded person's number. The Cast check confirms each staff texting number is assigned to its staff member.
170174
- **Invariants:** no Pending or Sending recipient, no communication without a `SendDateTime`, no active flow instance, every email recipient address on a reserved domain, and every outbound SMS conversation row Delivered with `SentMessage` set. Every unsubscribed recipient matches the person: a list unsubscribe leaves them Inactive in that list, a Bulk or All unsubscribe matches their email preference, and a texted STOP has texting turned off on their phone.
171175
- **Cast expectations:** each Cast thread shows in SMS Conversations in its intended state, and the recent threads are unread.
172-
- **Validation** grades the generated data against calibration part 7.
176+
- **Validation** grades the generated email data against calibration part 7 (decision 151): the open rate, engagement, open delays, top link share, the shares of inactive emails, "No mass emails", and "Do not email", and the designed share of active adults with no mass email in a month.
173177
- **The verify tool** re-records the contract for the new tables and runs Rock Cleanup as the referee.
174178

175179
## Build order
@@ -178,14 +182,16 @@ Each slice lands on its own, passes verify, and goes to the integrator.
178182

179183
1. **Design and calibration.** This page, the decisions, the calibration part 7 script, and the church app stub. Done. Source-1 has run part 7, and its shapes are in the profile.
180184
2. **Setup.** System phone numbers, communication lists with membership that follows involvement, topics, the sample church template, and the message library. Done.
181-
3. **Bulk email.** Sends flagged bulk with a sender, reviewer, and topic, recipients with their own send time, opens, clicks, failures, and unsubscribes that follow Rock's rules with matching History. Validation starts grading saturation.
185+
3. **Bulk email.** Sends flagged bulk with a sender, reviewer, and topic, recipients with their own send time, opens, clicks, failures, and unsubscribes that follow Rock's rules with matching History. Validation starts grading saturation. Done.
182186
4. **Texts and SMS conversations.** Bulk texts, replies, the Cast's threads, generated threads, and STOP opt-outs.
183187
5. **One-to-one messages.** Serving, connections, prayer, and staff messages.
184188
6. **Push.** The church app's site and layout, personal devices, pushes, and push opens.
185189
7. **Steps.** Step messages and the finished Communication Flow, after steps land.
186190

187191
## Open questions
188192

189-
1. **Email message copies.** Each recipient row can hold a copy of the sent message in `SentMessage`, which makes the table large. Slice 3 checks which screens read it for email. If none do, email recipients leave it empty.
190-
2. **`SendDateKey` dates.** Prayer interactions already write date keys, so `AnalyticsSourceDate` probably covers the 2-year window. Slice 3 confirms it.
191-
3. **The app's pages.** The stub plans five tabs. Which pages and data each tab gets is later work.
193+
1. **The app's pages.** The stub plans five tabs. Which pages and data each tab gets is later work.
194+
195+
Answered: email recipients leave `SentMessage` empty, because Rock's email transport never stores it (decision 149), and `AnalyticsSourceDate` covers 1876 to 2126, so every send's date key exists.
196+
197+
2. **The app's pages.** The stub plans five tabs. Which pages and data each tab gets is later work.

0 commit comments

Comments
 (0)